From 0a8763f23750279d6db2745fc55573a4ec7a212b Mon Sep 17 00:00:00 2001 From: Oliver Braun Date: Sat, 18 Jul 2026 13:53:24 +0200 Subject: [PATCH] feat(web): mergeRequest approvals (settings + rules) in the editor schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The most nested block: approval settings (tri-state) plus an approval-rules list, all in the same flat draft under approvals.*. - approvalSettingsSchema: the four prevent/require toggles as tri-state ENUMs (empty option = unset → *bool nil) plus whenCommitAdded as an ENUM with the three GitLab-valid values. - approvalRuleSchema: one rule row (name, requiredApprovals int, usernames / groups / branches comma-lists, multiMemberGroupsOnly bool). - assignmentOwn emits approvals.settings.* (tri-state) + approvals.rules.count + approvals.rules..*; applyMergeRequestDraft now also rebuilds mr.Approvals from those keys (new struct, never mutating the original; nil when neither settings nor rules remain), preserving the MR when only approvals change. Verified: go test ./web/... (tri-state set/unset, rules, clear-all), go vet (both tags), golangci-lint, and an isolated live check — the persisted YAML nests mergeRequest.approvals.settings/rules correctly. Co-Authored-By: Claude Opus 4.8 (1M context) --- web/app/assignment_schema.go | 102 +++++++++++++++++++++ web/app/assignments.go | 137 +++++++++++++++++++++++++++-- web/app/assignments_test.go | 69 +++++++++++++++ web/graph/assignments.graphqls | 4 + web/graph/assignments.resolvers.go | 10 +++ web/graph/generated/generated.go | 128 +++++++++++++++++++++++++++ 6 files changed, 445 insertions(+), 5 deletions(-) diff --git a/web/app/assignment_schema.go b/web/app/assignment_schema.go index d71ab78..ccb1a09 100644 --- a/web/app/assignment_schema.go +++ b/web/app/assignment_schema.go @@ -55,6 +55,108 @@ func AssignmentBranchSchema() []FieldMeta { return branchFields } +// AssignmentApprovalSettingsSchema returns the metadata for the mergeRequest +// approval settings. These are tri-state: an ENUM whose empty option means +// "not set" (inherit), so `an`/`aus` map to *bool true/false and "" to nil. +func AssignmentApprovalSettingsSchema() []FieldMeta { + return approvalSettingsFields +} + +// AssignmentApprovalRuleSchema returns the metadata for one approval rule — a row +// of the repeat-group `approvals.rules` list. +func AssignmentApprovalRuleSchema() []FieldMeta { + return approvalRuleFields +} + +var triStateOptions = []FieldOption{ + {Value: "true", Label: "an", Description: "aktiviert"}, + {Value: "false", Label: "aus", Description: "deaktiviert"}, +} + +var approvalSettingsFields = []FieldMeta{ + { + Key: "preventApprovalByMergeRequestCreator", + Label: "Ersteller darf nicht selbst freigeben", + Description: "Die MR-Erstellerin/der Ersteller kann den eigenen MR nicht approven.", + Kind: KindEnum, + Options: triStateOptions, + }, + { + Key: "preventApprovalsByUsersWhoAddCommits", + Label: "Committer dürfen nicht freigeben", + Description: "Wer Commits hinzufügt, kann den MR nicht approven.", + Kind: KindEnum, + Options: triStateOptions, + }, + { + Key: "preventEditingApprovalRulesInMergeRequests", + Label: "Approval-Regeln im MR sperren", + Description: "Approval-Regeln können im einzelnen MR nicht geändert werden.", + Kind: KindEnum, + Options: triStateOptions, + }, + { + Key: "requireUserReauthenticationToApprove", + Label: "Re-Authentifizierung zum Freigeben", + Description: "Approven erfordert erneute Authentifizierung.", + Kind: KindEnum, + Options: triStateOptions, + }, + { + Key: "whenCommitAdded", + Label: "Bei neuem Commit", + Description: "Was mit bestehenden Approvals passiert, wenn ein Commit hinzukommt (leer = GitLab-Default).", + Kind: KindEnum, + Options: []FieldOption{ + {Value: "keepApprovals", Label: "Behalten", Description: "Approvals bleiben bestehen."}, + {Value: "removeAllApprovals", Label: "Alle entfernen", Description: "Alle Approvals werden zurückgesetzt."}, + {Value: "removeCodeOwnerApprovalsIfTheirFilesChanged", Label: "Code-Owner (geänderte Dateien)", Description: "Nur Code-Owner-Approvals entfernen, wenn deren Dateien geändert wurden."}, + }, + }, +} + +var approvalRuleFields = []FieldMeta{ + { + Key: "name", + Label: "Regelname", + Description: "Name der Approval-Regel.", + Kind: KindString, + Required: true, + Example: "Tutoren", + }, + { + Key: "requiredApprovals", + Label: "Erforderliche Freigaben", + Description: "Anzahl nötiger Approvals für diese Regel.", + Kind: KindInt, + Example: "1", + }, + { + Key: "usernames", + Label: "Usernames", + Description: "GitLab-Usernames, die freigeben dürfen (kommagetrennt).", + Kind: KindStringList, + }, + { + Key: "groups", + Label: "Gruppen", + Description: "GitLab-Gruppen(pfade), die freigeben dürfen (kommagetrennt).", + Kind: KindStringList, + }, + { + Key: "branches", + Label: "Branches", + Description: "Branches, für die die Regel gilt (kommagetrennt; leer = alle).", + Kind: KindStringList, + }, + { + Key: "multiMemberGroupsOnly", + Label: "Nur mehrköpfige Gruppen", + Description: "Nur Gruppen mit mehr als einem Mitglied zählen.", + Kind: KindBool, + }, +} + var branchFields = []FieldMeta{ { Key: "name", diff --git a/web/app/assignments.go b/web/app/assignments.go index 832888b..c74f7fe 100644 --- a/web/app/assignments.go +++ b/web/app/assignments.go @@ -79,9 +79,61 @@ func assignmentOwn(src *config.AssignmentSource) []FieldValue { FieldValue{Key: p + "codeOwnerApprovalRequired", Value: strconv.FormatBool(b.CodeOwnerApprovalRequired)}, ) } + + // mergeRequest.approvals (settings tri-state + rules repeat group), keyed + // flat as approvals.* so they render as their own section. + var appr *config.ApprovalsSource + if src.MergeRequest != nil { + appr = src.MergeRequest.Approvals + } + var settings *config.ApprovalSettingsSource + if appr != nil { + settings = appr.Settings + } + own = append(own, + FieldValue{Key: "approvals.settings.preventApprovalByMergeRequestCreator", Value: triBool(settings, func(s *config.ApprovalSettingsSource) *bool { return s.PreventApprovalByMergeRequestCreator })}, + FieldValue{Key: "approvals.settings.preventApprovalsByUsersWhoAddCommits", Value: triBool(settings, func(s *config.ApprovalSettingsSource) *bool { return s.PreventApprovalsByUsersWhoAddCommits })}, + FieldValue{Key: "approvals.settings.preventEditingApprovalRulesInMergeRequests", Value: triBool(settings, func(s *config.ApprovalSettingsSource) *bool { return s.PreventEditingApprovalRulesInMergeRequests })}, + FieldValue{Key: "approvals.settings.requireUserReauthenticationToApprove", Value: triBool(settings, func(s *config.ApprovalSettingsSource) *bool { return s.RequireUserReauthenticationToApprove })}, + FieldValue{Key: "approvals.settings.whenCommitAdded", Value: optStr(settings)}, + ) + var rules []config.ApprovalRuleSource + if appr != nil { + rules = appr.Rules + } + own = append(own, FieldValue{Key: "approvals.rules.count", Value: strconv.Itoa(len(rules))}) + for i, r := range rules { + p := fmt.Sprintf("approvals.rules.%d.", i) + own = append(own, + FieldValue{Key: p + "name", Value: r.Name}, + FieldValue{Key: p + "requiredApprovals", Value: strconv.Itoa(r.RequiredApprovals)}, + FieldValue{Key: p + "usernames", Value: strings.Join(r.Usernames, ", ")}, + FieldValue{Key: p + "groups", Value: strings.Join(r.Groups, ", ")}, + FieldValue{Key: p + "branches", Value: strings.Join(r.Branches, ", ")}, + FieldValue{Key: p + "multiMemberGroupsOnly", Value: strconv.FormatBool(r.MultiMemberGroupsOnly)}, + ) + } return own } +func triBool(s *config.ApprovalSettingsSource, f func(*config.ApprovalSettingsSource) *bool) string { + if s == nil { + return "" + } + b := f(s) + if b == nil { + return "" + } + return strconv.FormatBool(*b) +} + +func optStr(s *config.ApprovalSettingsSource) string { + if s == nil || s.WhenCommitAdded == nil { + return "" + } + return *s.WhenCommitAdded +} + func issuesBool(i *config.IssuesSource, f func(*config.IssuesSource) bool) string { if i == nil { return "false" @@ -292,14 +344,13 @@ func splitIntList(s string) []int { return out } -// applyMergeRequestDraft rebuilds the mergeRequest block from the draft's -// mergeRequest.* keys into a NEW struct (never mutating the shared original) and -// nils it when every editable field is empty AND there is no approvals block. -// The (not-yet-editable) approvals block is carried over untouched. +// applyMergeRequestDraft rebuilds the mergeRequest block (scalars + approvals) +// from the draft into a NEW struct (never mutating the shared original) and nils +// it when every field is empty and there is no approvals block. func applyMergeRequestDraft(orig *config.MergeRequestSource, draft map[string]string) *config.MergeRequestSource { touched := false for k := range draft { - if strings.HasPrefix(k, "mergeRequest.") { + if strings.HasPrefix(k, "mergeRequest.") || strings.HasPrefix(k, "approvals.") { touched = true break } @@ -328,6 +379,8 @@ func applyMergeRequestDraft(orig *config.MergeRequestSource, draft map[string]st mr.StatusChecksMustSucceed = v == "true" } } + mr.Approvals = buildApprovals(mr.Approvals, draft) + if mr.MergeMethod == "" && mr.SquashOption == "" && !mr.Pipeline && !mr.SkippedPipelinesAreSuccessful && !mr.AllThreadsMustBeResolved && !mr.StatusChecksMustSucceed && mr.Approvals == nil { @@ -336,6 +389,80 @@ func applyMergeRequestDraft(orig *config.MergeRequestSource, draft map[string]st return &mr } +// buildApprovals rebuilds the approvals block from the draft's approvals.* keys +// into a NEW struct; returns the original when the draft does not address +// approvals, and nil when there are neither settings nor rules. +func buildApprovals(orig *config.ApprovalsSource, draft map[string]string) *config.ApprovalsSource { + touched := false + for k := range draft { + if strings.HasPrefix(k, "approvals.") { + touched = true + break + } + } + if !touched { + return orig + } + settings := buildApprovalSettings(draft) + rules := buildApprovalRules(draft) + if settings == nil && len(rules) == 0 { + return nil + } + return &config.ApprovalsSource{Settings: settings, Rules: rules} +} + +func buildApprovalSettings(draft map[string]string) *config.ApprovalSettingsSource { + s := config.ApprovalSettingsSource{} + any := false + setTri := func(key string, dst **bool) { + if v, ok := draft[key]; ok && v != "" { + b := v == "true" + *dst = &b + any = true + } + } + setTri("approvals.settings.preventApprovalByMergeRequestCreator", &s.PreventApprovalByMergeRequestCreator) + setTri("approvals.settings.preventApprovalsByUsersWhoAddCommits", &s.PreventApprovalsByUsersWhoAddCommits) + setTri("approvals.settings.preventEditingApprovalRulesInMergeRequests", &s.PreventEditingApprovalRulesInMergeRequests) + setTri("approvals.settings.requireUserReauthenticationToApprove", &s.RequireUserReauthenticationToApprove) + if v, ok := draft["approvals.settings.whenCommitAdded"]; ok { + if w := strings.TrimSpace(v); w != "" { + s.WhenCommitAdded = &w + any = true + } + } + if !any { + return nil + } + return &s +} + +func buildApprovalRules(draft map[string]string) []config.ApprovalRuleSource { + countStr, ok := draft["approvals.rules.count"] + if !ok { + return nil + } + n, _ := strconv.Atoi(countStr) + var rules []config.ApprovalRuleSource + for i := 0; i < n; i++ { + p := fmt.Sprintf("approvals.rules.%d.", i) + name := strings.TrimSpace(draft[p+"name"]) + if name == "" { + continue + } + ra, _ := strconv.Atoi(strings.TrimSpace(draft[p+"requiredApprovals"])) + rules = append(rules, config.ApprovalRuleSource{ + Name: name, + Branches: splitList(draft[p+"branches"]), + Usernames: splitList(draft[p+"usernames"]), + Groups: splitList(draft[p+"groups"]), + MultiMemberGroupsOnly: draft[p+"multiMemberGroupsOnly"] == "true", + RequiredApprovals: ra, + }) + } + return rules +} + // applyStartercodeDraft rebuilds the startercode block from the draft's // startercode.* keys. It always returns a NEW struct (never mutates the shared // original) when the draft touches startercode, and nil when every startercode diff --git a/web/app/assignments_test.go b/web/app/assignments_test.go index 598be35..4c7dd80 100644 --- a/web/app/assignments_test.go +++ b/web/app/assignments_test.go @@ -576,6 +576,75 @@ func TestSetAssignment_branchesRepeatGroup(t *testing.T) { } } +func TestSetAssignment_approvals(t *testing.T) { + const owner = "prof@hm.edu" + fs := newFakeStore() + fs.courses[owner+"/tc"] = storedCourse(t, owner, tcCourse) + a := &App{db: fs, gitlabHost: "https://gl"} + ctx := ctxAs(owner) + + view, err := a.SetAssignment(ctx, "tc", "blatt1", map[string]string{ + "mergeRequest.mergeMethod": "merge", + "approvals.settings.preventApprovalByMergeRequestCreator": "true", + "approvals.settings.requireUserReauthenticationToApprove": "false", + "approvals.settings.whenCommitAdded": "keepApprovals", + "approvals.rules.count": "1", + "approvals.rules.0.name": "Tutoren", + "approvals.rules.0.requiredApprovals": "2", + "approvals.rules.0.usernames": "a, b", + "approvals.rules.0.multiMemberGroupsOnly": "true", + }) + if err != nil { + t.Fatalf("set: %v", err) + } + mr := fs.saved.Source.Assignments["blatt1"].MergeRequest + if mr == nil || mr.Approvals == nil { + t.Fatalf("approvals missing: %+v", mr) + } + s := mr.Approvals.Settings + if s == nil || s.PreventApprovalByMergeRequestCreator == nil || !*s.PreventApprovalByMergeRequestCreator { + t.Errorf("prevent-creator tri-state should be true: %+v", s) + } + if s.RequireUserReauthenticationToApprove == nil || *s.RequireUserReauthenticationToApprove { + t.Errorf("reauth tri-state should be false") + } + if s.PreventApprovalsByUsersWhoAddCommits != nil { + t.Errorf("unset tri-state should stay nil") + } + if s.WhenCommitAdded == nil || *s.WhenCommitAdded != "keepApprovals" { + t.Errorf("whenCommitAdded = %v", s.WhenCommitAdded) + } + if len(mr.Approvals.Rules) != 1 { + t.Fatalf("want 1 rule, got %+v", mr.Approvals.Rules) + } + r := mr.Approvals.Rules[0] + if r.Name != "Tutoren" || r.RequiredApprovals != 2 || len(r.Usernames) != 2 || !r.MultiMemberGroupsOnly { + t.Errorf("rule = %+v", r) + } + + own := ownMap(view.Own) + if own["approvals.settings.preventApprovalByMergeRequestCreator"] != "true" || + own["approvals.settings.preventApprovalsByUsersWhoAddCommits"] != "" || + own["approvals.rules.count"] != "1" || own["approvals.rules.0.usernames"] != "a, b" { + t.Errorf("own approval keys wrong: %v", own) + } + + // Clearing settings + rules removes the approvals block; the MR itself stays. + if _, err := a.SetAssignment(ctx, "tc", "blatt1", map[string]string{ + "mergeRequest.mergeMethod": "merge", + "approvals.settings.preventApprovalByMergeRequestCreator": "", + "approvals.settings.requireUserReauthenticationToApprove": "", + "approvals.settings.whenCommitAdded": "", + "approvals.rules.count": "0", + }); err != nil { + t.Fatalf("set (clear): %v", err) + } + mr2 := fs.saved.Source.Assignments["blatt1"].MergeRequest + if mr2 == nil || mr2.Approvals != nil { + t.Errorf("approvals should be cleared while the MR is kept: %+v", mr2) + } +} + func TestSetAssignment_rejectsUnresolvable(t *testing.T) { const owner = "prof@hm.edu" fs := newFakeStore() diff --git a/web/graph/assignments.graphqls b/web/graph/assignments.graphqls index 61c1d7b..774cb49 100644 --- a/web/graph/assignments.graphqls +++ b/web/graph/assignments.graphqls @@ -95,6 +95,10 @@ extend type Query { assignmentSchema: [FieldMeta!]! "Field metadata for one branch rule — a row of the repeat-group `branches` list." branchRuleSchema: [FieldMeta!]! + "Field metadata for the mergeRequest approval settings (tri-state; empty ENUM option means unset)." + approvalSettingsSchema: [FieldMeta!]! + "Field metadata for one approval rule — a row of the repeat-group `approvals.rules` list." + approvalRuleSchema: [FieldMeta!]! "One assignment of one of the caller's courses: source values, resolved preview. Null when there is no such assignment." assignment(course: String!, name: String!): AssignmentView "Validate a draft assignment against the real resolver without saving." diff --git a/web/graph/assignments.resolvers.go b/web/graph/assignments.resolvers.go index 2906fb7..7832cec 100644 --- a/web/graph/assignments.resolvers.go +++ b/web/graph/assignments.resolvers.go @@ -38,6 +38,16 @@ func (r *queryResolver) BranchRuleSchema(ctx context.Context) ([]*model.FieldMet return toGraphAssignmentSchema(app.AssignmentBranchSchema()), nil } +// ApprovalSettingsSchema is the resolver for the approvalSettingsSchema field. +func (r *queryResolver) ApprovalSettingsSchema(ctx context.Context) ([]*model.FieldMeta, error) { + return toGraphAssignmentSchema(app.AssignmentApprovalSettingsSchema()), nil +} + +// ApprovalRuleSchema is the resolver for the approvalRuleSchema field. +func (r *queryResolver) ApprovalRuleSchema(ctx context.Context) ([]*model.FieldMeta, error) { + return toGraphAssignmentSchema(app.AssignmentApprovalRuleSchema()), nil +} + // Assignment is the resolver for the assignment field. func (r *queryResolver) Assignment(ctx context.Context, course string, name string) (*model.AssignmentView, error) { view, err := r.app.Assignment(ctx, course, name) diff --git a/web/graph/generated/generated.go b/web/graph/generated/generated.go index 7ba2868..18146d2 100644 --- a/web/graph/generated/generated.go +++ b/web/graph/generated/generated.go @@ -115,6 +115,8 @@ type ComplexityRoot struct { } Query struct { + ApprovalRuleSchema func(childComplexity int) int + ApprovalSettingsSchema func(childComplexity int) int Assignment func(childComplexity int, course string, name string) int AssignmentSchema func(childComplexity int) int BranchRuleSchema func(childComplexity int) int @@ -168,6 +170,8 @@ type QueryResolver interface { ServerInfo(ctx context.Context) (*model.ServerInfo, error) AssignmentSchema(ctx context.Context) ([]*model.FieldMeta, error) BranchRuleSchema(ctx context.Context) ([]*model.FieldMeta, error) + ApprovalSettingsSchema(ctx context.Context) ([]*model.FieldMeta, error) + ApprovalRuleSchema(ctx context.Context) ([]*model.FieldMeta, error) Assignment(ctx context.Context, course string, name string) (*model.AssignmentView, error) ValidateAssignmentDraft(ctx context.Context, course string, name string, draft []*model.FieldValueInput) (*model.ValidationResult, error) Courses(ctx context.Context) ([]*model.Course, error) @@ -549,6 +553,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Mutation.SetGitlabToken(childComplexity, args["token"].(string)), true + case "Query.approvalRuleSchema": + if e.ComplexityRoot.Query.ApprovalRuleSchema == nil { + break + } + + return e.ComplexityRoot.Query.ApprovalRuleSchema(childComplexity), true + case "Query.approvalSettingsSchema": + if e.ComplexityRoot.Query.ApprovalSettingsSchema == nil { + break + } + + return e.ComplexityRoot.Query.ApprovalSettingsSchema(childComplexity), true case "Query.assignment": if e.ComplexityRoot.Query.Assignment == nil { break @@ -881,6 +897,10 @@ extend type Query { assignmentSchema: [FieldMeta!]! "Field metadata for one branch rule — a row of the repeat-group ` + "`" + `branches` + "`" + ` list." branchRuleSchema: [FieldMeta!]! + "Field metadata for the mergeRequest approval settings (tri-state; empty ENUM option means unset)." + approvalSettingsSchema: [FieldMeta!]! + "Field metadata for one approval rule — a row of the repeat-group ` + "`" + `approvals.rules` + "`" + ` list." + approvalRuleSchema: [FieldMeta!]! "One assignment of one of the caller's courses: source values, resolved preview. Null when there is no such assignment." assignment(course: String!, name: String!): AssignmentView "Validate a draft assignment against the real resolver without saving." @@ -3223,6 +3243,70 @@ func (ec *executionContext) fieldContext_Query_branchRuleSchema(_ context.Contex return fc, nil } +func (ec *executionContext) _Query_approvalSettingsSchema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query_approvalSettingsSchema(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.Query().ApprovalSettingsSchema(ctx) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []*model.FieldMeta) graphql.Marshaler { + return ec.marshalNFieldMeta2ᚕᚖgithubᚗcomᚋobcodeᚋglabsᚋv3ᚋwebᚋgraphᚋmodelᚐFieldMetaᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Query_approvalSettingsSchema(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_FieldMeta(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _Query_approvalRuleSchema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query_approvalRuleSchema(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.Query().ApprovalRuleSchema(ctx) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []*model.FieldMeta) graphql.Marshaler { + return ec.marshalNFieldMeta2ᚕᚖgithubᚗcomᚋobcodeᚋglabsᚋv3ᚋwebᚋgraphᚋmodelᚐFieldMetaᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Query_approvalRuleSchema(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_FieldMeta(ctx, field) + }, + } + return fc, nil +} + func (ec *executionContext) _Query_assignment(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -5613,6 +5697,50 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "approvalSettingsSchema": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_approvalSettingsSchema(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "approvalRuleSchema": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_approvalRuleSchema(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "assignment": field := field