Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 39 additions & 156 deletions pkg/github/granular_tools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,27 @@ func TestGranularUnresolveReviewThread(t *testing.T) {
}

func TestGranularSetIssueFields(t *testing.T) {
t.Run("mutation selects only issue identity", func(t *testing.T) {
transport := &sequencedGraphQLTransport{
t: t,
responses: []func(capturedGraphQLRequest) (int, string){
func(req capturedGraphQLRequest) (int, string) {
assert.Contains(t, req.Query, "issue{id,url}")
assert.NotContains(t, req.Query, "issueFieldValues")
assert.NotContains(t, req.Query, "number")
return http.StatusOK, `{"data":{"setIssueFieldValue":{"issue":{"id":"ISSUE_123","url":"https://github.com/owner/repo/issues/5"}}}}`
},
},
}
_, err := SetIssueFieldValues(context.Background(), githubv4.NewClient(&http.Client{Transport: transport}), SetIssueFieldValueInput{
IssueID: githubv4.ID("ISSUE_123"),
IssueFields: []IssueFieldCreateOrUpdateInput{{
FieldID: githubv4.ID("FIELD_1"), TextValue: githubv4.NewString("hello"),
}},
})
require.NoError(t, err)
})

t.Run("successful set with text value", func(t *testing.T) {
matchers := []githubv4mock.Matcher{
// Mock the issue ID query
Expand All @@ -1770,29 +1791,7 @@ func TestGranularSetIssueFields(t *testing.T) {
),
// Mock the setIssueFieldValue mutation
githubv4mock.NewMutationMatcher(
struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
Number githubv4.Int
URL githubv4.String
}
IssueFieldValues []struct {
TextValue struct {
Value string
} `graphql:"... on IssueFieldTextValue"`
SingleSelectValue struct {
Name string
} `graphql:"... on IssueFieldSingleSelectValue"`
DateValue struct {
Value string
} `graphql:"... on IssueFieldDateValue"`
NumberValue struct {
Value float64
} `graphql:"... on IssueFieldNumberValue"`
}
} `graphql:"setIssueFieldValue(input: $input)"`
}{},
setIssueFieldValueMutation{},
SetIssueFieldValueInput{
IssueID: githubv4.ID("ISSUE_123"),
IssueFields: []IssueFieldCreateOrUpdateInput{
Expand All @@ -1806,9 +1805,8 @@ func TestGranularSetIssueFields(t *testing.T) {
githubv4mock.DataResponse(map[string]any{
"setIssueFieldValue": map[string]any{
"issue": map[string]any{
"id": "ISSUE_123",
"number": 5,
"url": "https://github.com/owner/repo/issues/5",
"id": "ISSUE_123",
"url": "https://github.com/owner/repo/issues/5",
},
},
}),
Expand Down Expand Up @@ -1945,29 +1943,7 @@ func TestGranularSetIssueFields(t *testing.T) {
}),
),
githubv4mock.NewMutationMatcher(
struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
Number githubv4.Int
URL githubv4.String
}
IssueFieldValues []struct {
TextValue struct {
Value string
} `graphql:"... on IssueFieldTextValue"`
SingleSelectValue struct {
Name string
} `graphql:"... on IssueFieldSingleSelectValue"`
DateValue struct {
Value string
} `graphql:"... on IssueFieldDateValue"`
NumberValue struct {
Value float64
} `graphql:"... on IssueFieldNumberValue"`
}
} `graphql:"setIssueFieldValue(input: $input)"`
}{},
setIssueFieldValueMutation{},
SetIssueFieldValueInput{
IssueID: githubv4.ID("ISSUE_123"),
IssueFields: []IssueFieldCreateOrUpdateInput{
Expand All @@ -1982,9 +1958,8 @@ func TestGranularSetIssueFields(t *testing.T) {
githubv4mock.DataResponse(map[string]any{
"setIssueFieldValue": map[string]any{
"issue": map[string]any{
"id": "ISSUE_123",
"number": 5,
"url": "https://github.com/owner/repo/issues/5",
"id": "ISSUE_123",
"url": "https://github.com/owner/repo/issues/5",
},
},
}),
Expand Down Expand Up @@ -2059,29 +2034,7 @@ func TestGranularSetIssueFields(t *testing.T) {
}),
),
githubv4mock.NewMutationMatcher(
struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
Number githubv4.Int
URL githubv4.String
}
IssueFieldValues []struct {
TextValue struct {
Value string
} `graphql:"... on IssueFieldTextValue"`
SingleSelectValue struct {
Name string
} `graphql:"... on IssueFieldSingleSelectValue"`
DateValue struct {
Value string
} `graphql:"... on IssueFieldDateValue"`
NumberValue struct {
Value float64
} `graphql:"... on IssueFieldNumberValue"`
}
} `graphql:"setIssueFieldValue(input: $input)"`
}{},
setIssueFieldValueMutation{},
SetIssueFieldValueInput{
IssueID: githubv4.ID("ISSUE_123"),
IssueFields: []IssueFieldCreateOrUpdateInput{
Expand All @@ -2096,9 +2049,8 @@ func TestGranularSetIssueFields(t *testing.T) {
githubv4mock.DataResponse(map[string]any{
"setIssueFieldValue": map[string]any{
"issue": map[string]any{
"id": "ISSUE_123",
"number": 5,
"url": "https://github.com/owner/repo/issues/5",
"id": "ISSUE_123",
"url": "https://github.com/owner/repo/issues/5",
},
},
}),
Expand Down Expand Up @@ -2173,29 +2125,7 @@ func TestGranularSetIssueFields(t *testing.T) {
}),
),
githubv4mock.NewMutationMatcher(
struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
Number githubv4.Int
URL githubv4.String
}
IssueFieldValues []struct {
TextValue struct {
Value string
} `graphql:"... on IssueFieldTextValue"`
SingleSelectValue struct {
Name string
} `graphql:"... on IssueFieldSingleSelectValue"`
DateValue struct {
Value string
} `graphql:"... on IssueFieldDateValue"`
NumberValue struct {
Value float64
} `graphql:"... on IssueFieldNumberValue"`
}
} `graphql:"setIssueFieldValue(input: $input)"`
}{},
setIssueFieldValueMutation{},
SetIssueFieldValueInput{
IssueID: githubv4.ID("ISSUE_123"),
IssueFields: []IssueFieldCreateOrUpdateInput{
Expand All @@ -2210,9 +2140,8 @@ func TestGranularSetIssueFields(t *testing.T) {
githubv4mock.DataResponse(map[string]any{
"setIssueFieldValue": map[string]any{
"issue": map[string]any{
"id": "ISSUE_123",
"number": 5,
"url": "https://github.com/owner/repo/issues/5",
"id": "ISSUE_123",
"url": "https://github.com/owner/repo/issues/5",
},
},
}),
Expand Down Expand Up @@ -2264,29 +2193,7 @@ func TestGranularSetIssueFields(t *testing.T) {
}),
),
githubv4mock.NewMutationMatcher(
struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
Number githubv4.Int
URL githubv4.String
}
IssueFieldValues []struct {
TextValue struct {
Value string
} `graphql:"... on IssueFieldTextValue"`
SingleSelectValue struct {
Name string
} `graphql:"... on IssueFieldSingleSelectValue"`
DateValue struct {
Value string
} `graphql:"... on IssueFieldDateValue"`
NumberValue struct {
Value float64
} `graphql:"... on IssueFieldNumberValue"`
}
} `graphql:"setIssueFieldValue(input: $input)"`
}{},
setIssueFieldValueMutation{},
SetIssueFieldValueInput{
IssueID: githubv4.ID("ISSUE_123"),
IssueFields: []IssueFieldCreateOrUpdateInput{
Expand All @@ -2302,9 +2209,8 @@ func TestGranularSetIssueFields(t *testing.T) {
githubv4mock.DataResponse(map[string]any{
"setIssueFieldValue": map[string]any{
"issue": map[string]any{
"id": "ISSUE_123",
"number": 5,
"url": "https://github.com/owner/repo/issues/5",
"id": "ISSUE_123",
"url": "https://github.com/owner/repo/issues/5",
},
},
}),
Expand Down Expand Up @@ -2356,29 +2262,7 @@ func TestGranularSetIssueFields(t *testing.T) {
}),
),
githubv4mock.NewMutationMatcher(
struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
Number githubv4.Int
URL githubv4.String
}
IssueFieldValues []struct {
TextValue struct {
Value string
} `graphql:"... on IssueFieldTextValue"`
SingleSelectValue struct {
Name string
} `graphql:"... on IssueFieldSingleSelectValue"`
DateValue struct {
Value string
} `graphql:"... on IssueFieldDateValue"`
NumberValue struct {
Value float64
} `graphql:"... on IssueFieldNumberValue"`
}
} `graphql:"setIssueFieldValue(input: $input)"`
}{},
setIssueFieldValueMutation{},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only id/url are used. SetIssueFieldValues returns MinimalResponse{ID, URL}, so the extra IssueFieldValues selections were dead weight. Replacing it with a simpler type.

SetIssueFieldValueInput{
IssueID: githubv4.ID("ISSUE_123"),
IssueFields: []IssueFieldCreateOrUpdateInput{
Expand All @@ -2392,9 +2276,8 @@ func TestGranularSetIssueFields(t *testing.T) {
githubv4mock.DataResponse(map[string]any{
"setIssueFieldValue": map[string]any{
"issue": map[string]any{
"id": "ISSUE_123",
"number": 5,
"url": "https://github.com/owner/repo/issues/5",
"id": "ISSUE_123",
"url": "https://github.com/owner/repo/issues/5",
},
},
}),
Expand Down
54 changes: 24 additions & 30 deletions pkg/github/issues_granular.go
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,27 @@ type IssueFieldCreateOrUpdateInput struct {
Suggest *githubv4.Boolean `json:"suggest,omitempty"`
}

type setIssueFieldValueMutation struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
URL githubv4.String
}
} `graphql:"setIssueFieldValue(input: $input)"`
}

// SetIssueFieldValues updates Issue Field values and returns the updated issue.
func SetIssueFieldValues(ctx context.Context, gqlClient *githubv4.Client, input SetIssueFieldValueInput) (MinimalResponse, error) {
var mutation setIssueFieldValueMutation
if err := gqlClient.Mutate(ctx, &mutation, input, nil); err != nil {
return MinimalResponse{}, err
}
return MinimalResponse{
ID: fmt.Sprintf("%v", mutation.SetIssueFieldValue.Issue.ID),
URL: string(mutation.SetIssueFieldValue.Issue.URL),
}, nil
}

// GranularSetIssueFields creates a tool to set issue field values on an issue using GraphQL.
func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.ServerTool {
st := NewTool(
Expand Down Expand Up @@ -1486,31 +1507,6 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to get issue", err), nil, nil
}

// Execute the setIssueFieldValue mutation
var mutation struct {
SetIssueFieldValue struct {
Issue struct {
ID githubv4.ID
Number githubv4.Int
URL githubv4.String
}
IssueFieldValues []struct {
TextValue struct {
Value string
} `graphql:"... on IssueFieldTextValue"`
SingleSelectValue struct {
Name string
} `graphql:"... on IssueFieldSingleSelectValue"`
DateValue struct {
Value string
} `graphql:"... on IssueFieldDateValue"`
NumberValue struct {
Value float64
} `graphql:"... on IssueFieldNumberValue"`
}
} `graphql:"setIssueFieldValue(input: $input)"`
}

mutationInput := SetIssueFieldValueInput{
IssueID: issueID,
IssueFields: issueFields,
Expand All @@ -1519,14 +1515,12 @@ func GranularSetIssueFields(t translations.TranslationHelperFunc) inventory.Serv
// The rationale and suggest input fields on IssueFieldCreateOrUpdateInput
// are gated behind the update_issue_suggestions GraphQL feature flag.
ctxWithFeatures := ghcontext.WithGraphQLFeatures(ctx, "update_issue_suggestions")
if err := gqlClient.Mutate(ctxWithFeatures, &mutation, mutationInput, nil); err != nil {
response, err := SetIssueFieldValues(ctxWithFeatures, gqlClient, mutationInput)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetIssueFieldValues is exported so projects, calls it directly. Also the mutation call is extracted into SetIssueFieldValues so both set_issue_fields (issues) and update_project_item (projects) can use it, with the flagged ctx.

if err != nil {
return ghErrors.NewGitHubGraphQLErrorResponse(ctx, "failed to set issue field values", err), nil, nil
}

r, err := json.Marshal(MinimalResponse{
ID: fmt.Sprintf("%v", mutation.SetIssueFieldValue.Issue.ID),
URL: string(mutation.SetIssueFieldValue.Issue.URL),
})
r, err := json.Marshal(response)
if err != nil {
return utils.NewToolResultErrorFromErr("failed to marshal response", err), nil, nil
}
Expand Down
Loading
Loading