data.go

  1package codegen
  2
  3import (
  4	"fmt"
  5	"sort"
  6
  7	"github.com/99designs/gqlgen/codegen/config"
  8	"github.com/pkg/errors"
  9	"github.com/vektah/gqlparser/ast"
 10)
 11
 12// Data is a unified model of the code to be generated. Plugins may modify this structure to do things like implement
 13// resolvers or directives automatically (eg grpc, validation)
 14type Data struct {
 15	Config          *config.Config
 16	Schema          *ast.Schema
 17	SchemaStr       map[string]string
 18	Directives      map[string]*Directive
 19	Objects         Objects
 20	Inputs          Objects
 21	Interfaces      map[string]*Interface
 22	ReferencedTypes map[string]*config.TypeReference
 23	ComplexityRoots map[string]*Object
 24
 25	QueryRoot        *Object
 26	MutationRoot     *Object
 27	SubscriptionRoot *Object
 28}
 29
 30type builder struct {
 31	Config     *config.Config
 32	Schema     *ast.Schema
 33	SchemaStr  map[string]string
 34	Binder     *config.Binder
 35	Directives map[string]*Directive
 36}
 37
 38func BuildData(cfg *config.Config) (*Data, error) {
 39	b := builder{
 40		Config: cfg,
 41	}
 42
 43	var err error
 44	b.Schema, b.SchemaStr, err = cfg.LoadSchema()
 45	if err != nil {
 46		return nil, err
 47	}
 48
 49	err = cfg.Check()
 50	if err != nil {
 51		return nil, err
 52	}
 53
 54	cfg.InjectBuiltins(b.Schema)
 55
 56	b.Binder, err = b.Config.NewBinder(b.Schema)
 57	if err != nil {
 58		return nil, err
 59	}
 60
 61	b.Directives, err = b.buildDirectives()
 62	if err != nil {
 63		return nil, err
 64	}
 65
 66	dataDirectives := make(map[string]*Directive)
 67	for name, d := range b.Directives {
 68		if !d.Builtin {
 69			dataDirectives[name] = d
 70		}
 71	}
 72
 73	s := Data{
 74		Config:     cfg,
 75		Directives: dataDirectives,
 76		Schema:     b.Schema,
 77		SchemaStr:  b.SchemaStr,
 78		Interfaces: map[string]*Interface{},
 79	}
 80
 81	for _, schemaType := range b.Schema.Types {
 82		switch schemaType.Kind {
 83		case ast.Object:
 84			obj, err := b.buildObject(schemaType)
 85			if err != nil {
 86				return nil, errors.Wrap(err, "unable to build object definition")
 87			}
 88
 89			s.Objects = append(s.Objects, obj)
 90		case ast.InputObject:
 91			input, err := b.buildObject(schemaType)
 92			if err != nil {
 93				return nil, errors.Wrap(err, "unable to build input definition")
 94			}
 95
 96			s.Inputs = append(s.Inputs, input)
 97
 98		case ast.Union, ast.Interface:
 99			s.Interfaces[schemaType.Name] = b.buildInterface(schemaType)
100		}
101	}
102
103	if s.Schema.Query != nil {
104		s.QueryRoot = s.Objects.ByName(s.Schema.Query.Name)
105	} else {
106		return nil, fmt.Errorf("query entry point missing")
107	}
108
109	if s.Schema.Mutation != nil {
110		s.MutationRoot = s.Objects.ByName(s.Schema.Mutation.Name)
111	}
112
113	if s.Schema.Subscription != nil {
114		s.SubscriptionRoot = s.Objects.ByName(s.Schema.Subscription.Name)
115	}
116
117	if err := b.injectIntrospectionRoots(&s); err != nil {
118		return nil, err
119	}
120
121	s.ReferencedTypes, err = b.buildTypes()
122	if err != nil {
123		return nil, err
124	}
125
126	sort.Slice(s.Objects, func(i, j int) bool {
127		return s.Objects[i].Definition.Name < s.Objects[j].Definition.Name
128	})
129
130	sort.Slice(s.Inputs, func(i, j int) bool {
131		return s.Inputs[i].Definition.Name < s.Inputs[j].Definition.Name
132	})
133
134	return &s, nil
135}
136
137func (b *builder) injectIntrospectionRoots(s *Data) error {
138	obj := s.Objects.ByName(b.Schema.Query.Name)
139	if obj == nil {
140		return fmt.Errorf("root query type must be defined")
141	}
142
143	__type, err := b.buildField(obj, &ast.FieldDefinition{
144		Name: "__type",
145		Type: ast.NamedType("__Type", nil),
146		Arguments: []*ast.ArgumentDefinition{
147			{
148				Name: "name",
149				Type: ast.NonNullNamedType("String", nil),
150			},
151		},
152	})
153	if err != nil {
154		return err
155	}
156
157	__schema, err := b.buildField(obj, &ast.FieldDefinition{
158		Name: "__schema",
159		Type: ast.NamedType("__Schema", nil),
160	})
161	if err != nil {
162		return err
163	}
164
165	obj.Fields = append(obj.Fields, __type, __schema)
166
167	return nil
168}