Skip to content

fix(issues): resolve names to node IDs in createIssue/updateIssue - #27

Open
netztaucher wants to merge 1 commit into
kunwarVivek:mainfrom
netztaucher:fix/resolve-node-ids-for-labels-and-assignees
Open

fix(issues): resolve names to node IDs in createIssue/updateIssue#27
netztaucher wants to merge 1 commit into
kunwarVivek:mainfrom
netztaucher:fix/resolve-node-ids-for-labels-and-assignees

Conversation

@netztaucher

Copy link
Copy Markdown

Fixes #26.

createIssue and updateIssue pass human-readable names into GraphQL ID fields, so the API rejects the mutation:

Could not resolve to a node with the global id of 'priority:medium'

Three fields in the same input are affected:

repositoryId: this.repo,        // repo name, not a node ID
assigneeIds:  data.assignees,   // logins, not node IDs
labelIds:     data.labels,      // label names, not node IDs

Since ToolSchemas gives priority and type zod .default()s and IssueService appends them as priority:<x> / type:<y> labels, even a call that passes no labels carries two — so create_issue fails unconditionally, for every repository, and no argument combination avoids it.

What this changes

Three resolvers on BaseGitHubRepository, next to the existing resolveIssueNodeId():

cached why
resolveRepositoryNodeId() yes a repository's node ID is stable
resolveLabelIds() no labels change while the server runs
resolveAssigneeIds() no collaborators change while the server runs

create() resolves all three in parallel; update() resolves the two it needs.

Unknown names are skipped with a warning, not created. Creating them would silently alter a repository's label taxonomy as a side effect of writing an issue. This also makes the synthetic priority: / type: labels harmless in repos that don't define them.

update() keeps undefined distinct from [] — an empty array means "remove all", undefined means "leave unchanged", so an omitted field must not be resolved into [].

Tests

Two regression tests asserting the mutation input. They fail on main:

- "repositoryId": "R_repo123",
+ "repositoryId": "test-repo",

The existing suite mocked graphql: jest.fn() and never inspected its arguments, which is why this was invisible. The new tests route the mock by query name so the assertions can look at what actually reaches createIssue.

✓ should create an instance correctly
✓ passes node IDs — not names — to createIssue
✓ skips labels that do not exist instead of failing

npm run build is clean.

Verified end-to-end

Built from this branch and driven over stdio against a real repository:

  • before: create_issueCould not resolve to a node with the global id of '<repo>'
  • after: issue created, labels backlog + doc applied, a deliberately bogus label skipped with Skipping labels that do not exist in <owner>/<repo>: …

Deliberately left alone

The priority: / type: label injection in IssueService.createIssue() (and its zod defaults) still happens — with this fix it is merely skipped when the labels don't exist. It's a separate design question: those look like project-board field values rather than labels, and today they cannot be turned off (z.enum rejects ""). Happy to follow up in a second PR whichever way you prefer — drop the injection, or remove the .default()s so it becomes opt-in.

One related bug is also still open, same class: data.labels || [] returns the caller's array and is then mutated with .push. Left out to keep this diff focused.

createIssue and updateIssue pass human-readable names into GraphQL ID
fields, so every call is rejected by the API:

  Could not resolve to a node with the global id of 'priority:medium'

Three fields are affected in the same mutation input:

  repositoryId: this.repo        // repo name, not a node ID
  assigneeIds:  data.assignees   // logins, not node IDs
  labelIds:     data.labels      // label names, not node IDs

Because ToolSchemas gives `priority` and `type` zod defaults and
IssueService appends them as `priority:<x>` / `type:<y>` labels, even a
call that passes no labels at all carries two — so create_issue fails
unconditionally, for every repository.

Adds three resolvers to BaseGitHubRepository, alongside the existing
resolveIssueNodeId():

- resolveRepositoryNodeId() — cached, a repository's node ID is stable
- resolveLabelIds()         — not cached, labels change at runtime
- resolveAssigneeIds()      — not cached, collaborators change at runtime

Names that do not exist are skipped with a warning rather than created:
creating them would silently alter the repository's label taxonomy as a
side effect of writing an issue. This also makes the synthetic
priority:/type: labels harmless when the repo does not define them.

In update(), an omitted field stays undefined instead of resolving to an
empty array — [] means "remove all", undefined means "leave unchanged".

Adds regression tests asserting the mutation input. The existing suite
mocked `graphql` wholesale and never checked its arguments, which is why
this went unnoticed; the new tests fail on the current code with
`repositoryId: "test-repo"`.

Fixes kunwarVivek#26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

create_issue always fails: names passed where GraphQL expects node IDs (repositoryId, assigneeIds, labelIds)

1 participant