exec.go

  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}