package graphql import ( "context" "fmt" "github.com/vektah/gqlgen/neelance/common" "github.com/vektah/gqlgen/neelance/query" "github.com/vektah/gqlgen/neelance/schema" ) type ExecutableSchema interface { Schema() *schema.Schema Query(ctx context.Context, op *query.Operation) *Response Mutation(ctx context.Context, op *query.Operation) *Response Subscription(ctx context.Context, op *query.Operation) func() *Response } func CollectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}) []CollectedField { return collectFields(doc, selSet, satisfies, variables, map[string]bool{}) } func collectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}, visited map[string]bool) []CollectedField { var groupedFields []CollectedField for _, sel := range selSet { switch sel := sel.(type) { case *query.Field: f := getOrCreateField(&groupedFields, sel.Alias.Name, func() CollectedField { f := CollectedField{ Alias: sel.Alias.Name, Name: sel.Name.Name, } if len(sel.Arguments) > 0 { f.Args = map[string]interface{}{} for _, arg := range sel.Arguments { if variable, ok := arg.Value.(*common.Variable); ok { if val, ok := variables[variable.Name]; ok { f.Args[arg.Name.Name] = val } } else { f.Args[arg.Name.Name] = arg.Value.Value(variables) } } } return f }) f.Selections = append(f.Selections, sel.Selections...) case *query.InlineFragment: if !instanceOf(sel.On.Ident.Name, satisfies) { continue } for _, childField := range collectFields(doc, sel.Selections, satisfies, variables, visited) { f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField }) f.Selections = append(f.Selections, childField.Selections...) } case *query.FragmentSpread: fragmentName := sel.Name.Name if _, seen := visited[fragmentName]; seen { continue } visited[fragmentName] = true fragment := doc.Fragments.Get(fragmentName) if fragment == nil { // should never happen, validator has already run panic(fmt.Errorf("missing fragment %s", fragmentName)) } if !instanceOf(fragment.On.Ident.Name, satisfies) { continue } for _, childField := range collectFields(doc, fragment.Selections, satisfies, variables, visited) { f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField }) f.Selections = append(f.Selections, childField.Selections...) } default: panic(fmt.Errorf("unsupported %T", sel)) } } return groupedFields } type CollectedField struct { Alias string Name string Args map[string]interface{} Selections []query.Selection } func instanceOf(val string, satisfies []string) bool { for _, s := range satisfies { if val == s { return true } } return false } func getOrCreateField(c *[]CollectedField, name string, creator func() CollectedField) *CollectedField { for i, cf := range *c { if cf.Alias == name { return &(*c)[i] } } f := creator() *c = append(*c, f) return &(*c)[len(*c)-1] }