Apply deferred update mutations in parallel for gh issue edit
The Issues 2.0 mutations (issue type, parent set/remove, sub-issues, blocked-by, blocking) are deferred until after the main UpdateIssue because they target IDs that the standard mutation does not handle. Move them behind a shared api.DeferredUpdateIssue orchestrator that fans them out in parallel and joins all errors so a single failure does not abort the rest. editRun no longer carries its own applyEditParent / applyEditSubIssues / applyEditRelationships helpers; the per-issue goroutine resolves refs to node IDs via a small deferredUpdateIssueOptions builder, then hands the populated DeferredUpdateIssueOptions to api.DeferredUpdateIssue. Also moves ResolveIssueRef and ResolveIssueTypeName from the deleted resolve.go into lookup.go. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
315dafbf74
commit
02457482a5
5 changed files with 261 additions and 182 deletions
|
|
@ -2,7 +2,9 @@ package edit
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -1052,34 +1054,31 @@ func Test_editRun(t *testing.T) {
|
|||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
mockIssueNumberGet(t, reg, 100)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "SUB_123_ID" } } } }
|
||||
`),
|
||||
issueNodeIDByNumberMatcher(123),
|
||||
httpmock.StringResponse(`{ "data": { "repository": { "issue": { "id": "SUB_123_ID" } } } }`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation AddSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addSubIssue": { "issue": { "id": "100" } } } }`,
|
||||
issueNodeIDByNumberMatcher(124),
|
||||
httpmock.StringResponse(`{ "data": { "repository": { "issue": { "id": "SUB_124_ID" } } } }`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQLMutationMatcher(`mutation AddSubIssue\b`, func(input map[string]interface{}) bool {
|
||||
return input["subIssueId"] == "SUB_123_ID"
|
||||
}),
|
||||
httpmock.GraphQLMutation(`{ "data": { "addSubIssue": { "issue": { "id": "100" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "100", inputs["issueId"])
|
||||
assert.Equal(t, "SUB_123_ID", inputs["subIssueId"])
|
||||
assert.Equal(t, false, inputs["replaceParent"])
|
||||
}),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`query IssueNodeID\b`),
|
||||
httpmock.StringResponse(`
|
||||
{ "data": { "repository": { "issue": { "id": "SUB_124_ID" } } } }
|
||||
`),
|
||||
)
|
||||
reg.Register(
|
||||
httpmock.GraphQL(`mutation AddSubIssue\b`),
|
||||
httpmock.GraphQLMutation(`
|
||||
{ "data": { "addSubIssue": { "issue": { "id": "100" } } } }`,
|
||||
httpmock.GraphQLMutationMatcher(`mutation AddSubIssue\b`, func(input map[string]interface{}) bool {
|
||||
return input["subIssueId"] == "SUB_124_ID"
|
||||
}),
|
||||
httpmock.GraphQLMutation(`{ "data": { "addSubIssue": { "issue": { "id": "100" } } } }`,
|
||||
func(inputs map[string]interface{}) {
|
||||
assert.Equal(t, "100", inputs["issueId"])
|
||||
assert.Equal(t, "SUB_124_ID", inputs["subIssueId"])
|
||||
assert.Equal(t, false, inputs["replaceParent"])
|
||||
}),
|
||||
)
|
||||
},
|
||||
|
|
@ -1732,3 +1731,30 @@ func TestProjectsV1Deprecation(t *testing.T) {
|
|||
reg.Verify(t)
|
||||
})
|
||||
}
|
||||
|
||||
// issueNodeIDByNumberMatcher matches an IssueNodeID GraphQL query whose
|
||||
// number variable equals the given value. Used by tests that issue
|
||||
// multiple IssueNodeID lookups and need stubs to route by issue number
|
||||
// rather than by registration order.
|
||||
func issueNodeIDByNumberMatcher(number int) httpmock.Matcher {
|
||||
queryMatcher := httpmock.GraphQL(`query IssueNodeID\b`)
|
||||
return func(req *http.Request) bool {
|
||||
if !queryMatcher(req) {
|
||||
return false
|
||||
}
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
req.Body = io.NopCloser(bytes.NewReader(body))
|
||||
var b struct {
|
||||
Variables struct {
|
||||
Number int `json:"number"`
|
||||
} `json:"variables"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &b); err != nil {
|
||||
return false
|
||||
}
|
||||
return b.Variables.Number == number
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue