@@ -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.
2527type 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.
268274func 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" ,
0 commit comments