1package graphql
2
3import (
4 "context"
5 "fmt"
6
7 "github.com/vektah/gqlgen/neelance/common"
8 "github.com/vektah/gqlgen/neelance/query"
9 "github.com/vektah/gqlgen/neelance/schema"
10)
11
12type ExecutableSchema interface {
13 Schema() *schema.Schema
14
15 Query(ctx context.Context, op *query.Operation) *Response
16 Mutation(ctx context.Context, op *query.Operation) *Response
17 Subscription(ctx context.Context, op *query.Operation) func() *Response
18}
19
20func CollectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}) []CollectedField {
21 return collectFields(doc, selSet, satisfies, variables, map[string]bool{})
22}
23
24func collectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}, visited map[string]bool) []CollectedField {
25 var groupedFields []CollectedField
26
27 for _, sel := range selSet {
28 switch sel := sel.(type) {
29 case *query.Field:
30 f := getOrCreateField(&groupedFields, sel.Alias.Name, func() CollectedField {
31 f := CollectedField{
32 Alias: sel.Alias.Name,
33 Name: sel.Name.Name,
34 }
35 if len(sel.Arguments) > 0 {
36 f.Args = map[string]interface{}{}
37 for _, arg := range sel.Arguments {
38 if variable, ok := arg.Value.(*common.Variable); ok {
39 if val, ok := variables[variable.Name]; ok {
40 f.Args[arg.Name.Name] = val
41 }
42 } else {
43 f.Args[arg.Name.Name] = arg.Value.Value(variables)
44 }
45 }
46 }
47 return f
48 })
49
50 f.Selections = append(f.Selections, sel.Selections...)
51 case *query.InlineFragment:
52 if !instanceOf(sel.On.Ident.Name, satisfies) {
53 continue
54 }
55
56 for _, childField := range collectFields(doc, sel.Selections, satisfies, variables, visited) {
57 f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField })
58 f.Selections = append(f.Selections, childField.Selections...)
59 }
60
61 case *query.FragmentSpread:
62 fragmentName := sel.Name.Name
63 if _, seen := visited[fragmentName]; seen {
64 continue
65 }
66 visited[fragmentName] = true
67
68 fragment := doc.Fragments.Get(fragmentName)
69 if fragment == nil {
70 // should never happen, validator has already run
71 panic(fmt.Errorf("missing fragment %s", fragmentName))
72 }
73
74 if !instanceOf(fragment.On.Ident.Name, satisfies) {
75 continue
76 }
77
78 for _, childField := range collectFields(doc, fragment.Selections, satisfies, variables, visited) {
79 f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField })
80 f.Selections = append(f.Selections, childField.Selections...)
81 }
82
83 default:
84 panic(fmt.Errorf("unsupported %T", sel))
85 }
86 }
87
88 return groupedFields
89}
90
91type CollectedField struct {
92 Alias string
93 Name string
94 Args map[string]interface{}
95 Selections []query.Selection
96}
97
98func instanceOf(val string, satisfies []string) bool {
99 for _, s := range satisfies {
100 if val == s {
101 return true
102 }
103 }
104 return false
105}
106
107func getOrCreateField(c *[]CollectedField, name string, creator func() CollectedField) *CollectedField {
108 for i, cf := range *c {
109 if cf.Alias == name {
110 return &(*c)[i]
111 }
112 }
113
114 f := creator()
115
116 *c = append(*c, f)
117 return &(*c)[len(*c)-1]
118}