schema.go

  1//go:generate go run ./inliner/inliner.go
  2
  3package validator
  4
  5import (
  6	"strconv"
  7	"strings"
  8
  9	. "github.com/vektah/gqlparser/ast"
 10	"github.com/vektah/gqlparser/gqlerror"
 11	"github.com/vektah/gqlparser/parser"
 12)
 13
 14func LoadSchema(inputs ...*Source) (*Schema, *gqlerror.Error) {
 15	ast, err := parser.ParseSchemas(inputs...)
 16	if err != nil {
 17		return nil, err
 18	}
 19	return ValidateSchemaDocument(ast)
 20}
 21
 22func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, *gqlerror.Error) {
 23	schema := Schema{
 24		Types:         map[string]*Definition{},
 25		Directives:    map[string]*DirectiveDefinition{},
 26		PossibleTypes: map[string][]*Definition{},
 27		Implements:    map[string][]*Definition{},
 28	}
 29
 30	for i, def := range ast.Definitions {
 31		if schema.Types[def.Name] != nil {
 32			return nil, gqlerror.ErrorPosf(def.Position, "Cannot redeclare type %s.", def.Name)
 33		}
 34		schema.Types[def.Name] = ast.Definitions[i]
 35	}
 36
 37	for _, ext := range ast.Extensions {
 38		def := schema.Types[ext.Name]
 39		if def == nil {
 40			return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because it does not exist.", ext.Name)
 41		}
 42
 43		if def.Kind != ext.Kind {
 44			return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because the base type is a %s, not %s.", ext.Name, def.Kind, ext.Kind)
 45		}
 46
 47		def.Directives = append(def.Directives, ext.Directives...)
 48		def.Interfaces = append(def.Interfaces, ext.Interfaces...)
 49		def.Fields = append(def.Fields, ext.Fields...)
 50		def.Types = append(def.Types, ext.Types...)
 51		def.EnumValues = append(def.EnumValues, ext.EnumValues...)
 52	}
 53
 54	for _, def := range ast.Definitions {
 55		switch def.Kind {
 56		case Union:
 57			for _, t := range def.Types {
 58				schema.AddPossibleType(def.Name, schema.Types[t])
 59				schema.AddImplements(t, def)
 60			}
 61		case InputObject, Object:
 62			for _, intf := range def.Interfaces {
 63				schema.AddPossibleType(intf, def)
 64				schema.AddImplements(def.Name, schema.Types[intf])
 65			}
 66			schema.AddPossibleType(def.Name, def)
 67		}
 68	}
 69
 70	for i, dir := range ast.Directives {
 71		if schema.Directives[dir.Name] != nil {
 72			return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name)
 73		}
 74		schema.Directives[dir.Name] = ast.Directives[i]
 75	}
 76
 77	if len(ast.Schema) > 1 {
 78		return nil, gqlerror.ErrorPosf(ast.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.")
 79	}
 80
 81	if len(ast.Schema) == 1 {
 82		for _, entrypoint := range ast.Schema[0].OperationTypes {
 83			def := schema.Types[entrypoint.Type]
 84			if def == nil {
 85				return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
 86			}
 87			switch entrypoint.Operation {
 88			case Query:
 89				schema.Query = def
 90			case Mutation:
 91				schema.Mutation = def
 92			case Subscription:
 93				schema.Subscription = def
 94			}
 95		}
 96	}
 97
 98	for _, ext := range ast.SchemaExtension {
 99		for _, entrypoint := range ext.OperationTypes {
100			def := schema.Types[entrypoint.Type]
101			if def == nil {
102				return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
103			}
104			switch entrypoint.Operation {
105			case Query:
106				schema.Query = def
107			case Mutation:
108				schema.Mutation = def
109			case Subscription:
110				schema.Subscription = def
111			}
112		}
113	}
114
115	for _, typ := range schema.Types {
116		err := validateDefinition(&schema, typ)
117		if err != nil {
118			return nil, err
119		}
120	}
121
122	for _, dir := range schema.Directives {
123		err := validateDirective(&schema, dir)
124		if err != nil {
125			return nil, err
126		}
127	}
128
129	if schema.Query == nil && schema.Types["Query"] != nil {
130		schema.Query = schema.Types["Query"]
131	}
132
133	if schema.Mutation == nil && schema.Types["Mutation"] != nil {
134		schema.Mutation = schema.Types["Mutation"]
135	}
136
137	if schema.Subscription == nil && schema.Types["Subscription"] != nil {
138		schema.Subscription = schema.Types["Subscription"]
139	}
140
141	if schema.Query != nil {
142		schema.Query.Fields = append(
143			schema.Query.Fields,
144			&FieldDefinition{
145				Name: "__schema",
146				Type: NonNullNamedType("__Schema", nil),
147			},
148			&FieldDefinition{
149				Name: "__type",
150				Type: NonNullNamedType("__Type", nil),
151				Arguments: ArgumentDefinitionList{
152					{Name: "name", Type: NamedType("String", nil)},
153				},
154			},
155		)
156	}
157
158	return &schema, nil
159}
160
161func validateDirective(schema *Schema, def *DirectiveDefinition) *gqlerror.Error {
162	if err := validateName(def.Position, def.Name); err != nil {
163		// now, GraphQL spec doesn't have reserved directive name
164		return err
165	}
166
167	return validateArgs(schema, def.Arguments, def)
168}
169
170func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
171	for _, field := range def.Fields {
172		if err := validateName(field.Position, field.Name); err != nil {
173			// now, GraphQL spec doesn't have reserved field name
174			return err
175		}
176		if err := validateTypeRef(schema, field.Type); err != nil {
177			return err
178		}
179		if err := validateArgs(schema, field.Arguments, nil); err != nil {
180			return err
181		}
182		if err := validateDirectives(schema, field.Directives, nil); err != nil {
183			return err
184		}
185	}
186
187	for _, intf := range def.Interfaces {
188		intDef := schema.Types[intf]
189		if intDef == nil {
190			return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intf))
191		}
192		if intDef.Kind != Interface {
193			return gqlerror.ErrorPosf(def.Position, "%s is a non interface type %s.", strconv.Quote(intf), intDef.Kind)
194		}
195	}
196
197	switch def.Kind {
198	case Object, Interface:
199		if len(def.Fields) == 0 {
200			return gqlerror.ErrorPosf(def.Position, "%s must define one or more fields.", def.Kind)
201		}
202	case Enum:
203		if len(def.EnumValues) == 0 {
204			return gqlerror.ErrorPosf(def.Position, "%s must define one or more unique enum values.", def.Kind)
205		}
206	case InputObject:
207		if len(def.Fields) == 0 {
208			return gqlerror.ErrorPosf(def.Position, "%s must define one or more input fields.", def.Kind)
209		}
210	}
211
212	for idx, field1 := range def.Fields {
213		for _, field2 := range def.Fields[idx+1:] {
214			if field1.Name == field2.Name {
215				return gqlerror.ErrorPosf(field2.Position, "Field %s.%s can only be defined once.", def.Name, field2.Name)
216			}
217		}
218	}
219
220	if !def.BuiltIn {
221		// GraphQL spec has reserved type names a lot!
222		err := validateName(def.Position, def.Name)
223		if err != nil {
224			return err
225		}
226	}
227
228	return validateDirectives(schema, def.Directives, nil)
229}
230
231func validateTypeRef(schema *Schema, typ *Type) *gqlerror.Error {
232	if schema.Types[typ.Name()] == nil {
233		return gqlerror.ErrorPosf(typ.Position, "Undefined type %s.", typ.Name())
234	}
235	return nil
236}
237
238func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective *DirectiveDefinition) *gqlerror.Error {
239	for _, arg := range args {
240		if err := validateName(arg.Position, arg.Name); err != nil {
241			// now, GraphQL spec doesn't have reserved argument name
242			return err
243		}
244		if err := validateTypeRef(schema, arg.Type); err != nil {
245			return err
246		}
247		if err := validateDirectives(schema, arg.Directives, currentDirective); err != nil {
248			return err
249		}
250	}
251	return nil
252}
253
254func validateDirectives(schema *Schema, dirs DirectiveList, currentDirective *DirectiveDefinition) *gqlerror.Error {
255	for _, dir := range dirs {
256		if err := validateName(dir.Position, dir.Name); err != nil {
257			// now, GraphQL spec doesn't have reserved directive name
258			return err
259		}
260		if currentDirective != nil && dir.Name == currentDirective.Name {
261			return gqlerror.ErrorPosf(dir.Position, "Directive %s cannot refer to itself.", currentDirective.Name)
262		}
263		if schema.Directives[dir.Name] == nil {
264			return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
265		}
266		dir.Definition = schema.Directives[dir.Name]
267	}
268	return nil
269}
270
271func validateName(pos *Position, name string) *gqlerror.Error {
272	if strings.HasPrefix(name, "__") {
273		return gqlerror.ErrorPosf(pos, `Name "%s" must not begin with "__", which is reserved by GraphQL introspection.`, name)
274	}
275	return nil
276}