Skip to content

Commit 30de7cd

Browse files
zwickCopilot
andcommitted
Resolve project fields and items to GraphQL node IDs
Extend ResolvedField with the GraphQL node ID alongside the existing numeric databaseId, and extend the #2914 paginated issue-item resolver to also return the item's node ID (resolveProjectItemByIssueNumber), keeping resolveProjectItemIDByIssueNumber as a numeric-only wrapper for existing callers. These node IDs are what the upcoming aliased GraphQL mutations need as ItemID/FieldID inputs, in place of the numeric IDs the REST API takes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: d7dc302d-e6f2-41e9-a2c8-ed598de47067
1 parent cf2d9e2 commit 30de7cd

2 files changed

Lines changed: 27 additions & 9 deletions

File tree

pkg/github/projects_resolver.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@ type ResolvedFieldOption struct {
2020
Name string
2121
}
2222

23-
// ResolvedField is a project field resolved by name; Options is only set when
24-
// DataType == "SINGLE_SELECT".
23+
// ResolvedField is a project field resolved by name or numeric ID; Options is
24+
// only set when DataType == "SINGLE_SELECT". ID is the field's numeric
25+
// databaseId; NodeID is the GraphQL node ID required by aliased
26+
// updateProjectV2ItemFieldValue / clearProjectV2ItemFieldValue mutations.
2527
type ResolvedField struct {
2628
ID string
29+
NodeID string
2730
Name string
2831
DataType string
2932
Options []ResolvedFieldOption
@@ -117,19 +120,22 @@ func listAllProjectFields(ctx context.Context, gqlClient *githubv4.Client, owner
117120
}
118121
all = append(all, ResolvedField{
119122
ID: fmt.Sprintf("%d", n.ProjectV2SingleSelectField.DatabaseID),
123+
NodeID: fmt.Sprintf("%v", n.ProjectV2SingleSelectField.ID),
120124
Name: string(n.ProjectV2SingleSelectField.Name),
121125
DataType: string(n.ProjectV2SingleSelectField.DataType),
122126
Options: opts,
123127
})
124128
case n.ProjectV2IterationField.ID != nil:
125129
all = append(all, ResolvedField{
126130
ID: fmt.Sprintf("%d", n.ProjectV2IterationField.DatabaseID),
131+
NodeID: fmt.Sprintf("%v", n.ProjectV2IterationField.ID),
127132
Name: string(n.ProjectV2IterationField.Name),
128133
DataType: string(n.ProjectV2IterationField.DataType),
129134
})
130135
case n.ProjectV2Field.ID != nil:
131136
all = append(all, ResolvedField{
132137
ID: fmt.Sprintf("%d", n.ProjectV2Field.DatabaseID),
138+
NodeID: fmt.Sprintf("%v", n.ProjectV2Field.ID),
133139
Name: string(n.ProjectV2Field.Name),
134140
DataType: string(n.ProjectV2Field.DataType),
135141
})
@@ -266,13 +272,23 @@ func resolveSingleSelectOptionByName(field *ResolvedField, optionName string) (s
266272
// project item's full database ID in one GraphQL hop. Returns a structured
267273
// error if the issue is not an item on the project.
268274
func resolveProjectItemIDByIssueNumber(ctx context.Context, gqlClient *githubv4.Client, owner, ownerType string, projectNumber int, issueOwner, issueRepo string, issueNumber int) (int64, error) {
275+
_, itemID, err := resolveProjectItemByIssueNumber(ctx, gqlClient, owner, ownerType, projectNumber, issueOwner, issueRepo, issueNumber)
276+
return itemID, err
277+
}
278+
279+
// resolveProjectItemByIssueNumber resolves a (project, issue) pair to the
280+
// project item's GraphQL node ID and full database ID in one paginated
281+
// GraphQL traversal. Returns a structured error if the issue is not an item
282+
// on the project.
283+
func resolveProjectItemByIssueNumber(ctx context.Context, gqlClient *githubv4.Client, owner, ownerType string, projectNumber int, issueOwner, issueRepo string, issueNumber int) (nodeID string, itemID int64, err error) {
269284
projectID, err := resolveProjectNodeID(ctx, gqlClient, owner, ownerType, projectNumber)
270285
if err != nil {
271-
return 0, err
286+
return "", 0, err
272287
}
273288

274289
type projectItemsConnection struct {
275290
Nodes []struct {
291+
ID githubv4.ID
276292
FullDatabaseID githubv4.String `graphql:"fullDatabaseId"`
277293
Project struct {
278294
ID githubv4.ID
@@ -296,18 +312,18 @@ func resolveProjectItemIDByIssueNumber(ctx context.Context, gqlClient *githubv4.
296312
}
297313

298314
if err := gqlClient.Query(ctx, &firstPageQuery, vars); err != nil {
299-
return 0, fmt.Errorf("failed to resolve project item for %s/%s#%d: %w", issueOwner, issueRepo, issueNumber, err)
315+
return "", 0, fmt.Errorf("failed to resolve project item for %s/%s#%d: %w", issueOwner, issueRepo, issueNumber, err)
300316
}
301317

302318
projectItems := firstPageQuery.Repository.Issue.ProjectItems
303319
for {
304320
for _, item := range projectItems.Nodes {
305321
if item.Project.ID == projectID {
306-
itemID, parseErr := parseInt64(string(item.FullDatabaseID))
322+
parsedItemID, parseErr := parseInt64(string(item.FullDatabaseID))
307323
if parseErr != nil {
308-
return 0, fmt.Errorf("project item ID %q is not an integer: %w", string(item.FullDatabaseID), parseErr)
324+
return "", 0, fmt.Errorf("project item ID %q is not an integer: %w", string(item.FullDatabaseID), parseErr)
309325
}
310-
return itemID, nil
326+
return fmt.Sprintf("%v", item.ID), parsedItemID, nil
311327
}
312328
}
313329

@@ -324,12 +340,12 @@ func resolveProjectItemIDByIssueNumber(ctx context.Context, gqlClient *githubv4.
324340
}
325341
vars["after"] = projectItems.PageInfo.EndCursor
326342
if err := gqlClient.Query(ctx, &nextPageQuery, vars); err != nil {
327-
return 0, fmt.Errorf("failed to resolve project item for %s/%s#%d: %w", issueOwner, issueRepo, issueNumber, err)
343+
return "", 0, fmt.Errorf("failed to resolve project item for %s/%s#%d: %w", issueOwner, issueRepo, issueNumber, err)
328344
}
329345
projectItems = nextPageQuery.Repository.Issue.ProjectItems
330346
}
331347

332-
return 0, ghErrors.NewStructuredResolutionError(
348+
return "", 0, ghErrors.NewStructuredResolutionError(
333349
"item_not_in_project",
334350
fmt.Sprintf("%s/%s#%d", issueOwner, issueRepo, issueNumber),
335351
"the issue exists but is not an item on the named project; add it first via add_project_item",

pkg/github/projects_resolver_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ type resolveItemByIssueQuery struct {
204204
Issue struct {
205205
ProjectItems struct {
206206
Nodes []struct {
207+
ID githubv4.ID
207208
FullDatabaseID githubv4.String `graphql:"fullDatabaseId"`
208209
Project struct {
209210
ID githubv4.ID
@@ -220,6 +221,7 @@ type resolveItemByIssuePageQuery struct {
220221
Issue struct {
221222
ProjectItems struct {
222223
Nodes []struct {
224+
ID githubv4.ID
223225
FullDatabaseID githubv4.String `graphql:"fullDatabaseId"`
224226
Project struct {
225227
ID githubv4.ID

0 commit comments

Comments
 (0)