...

Source file src/github.com/vektah/gqlparser/validator/schema.go

Documentation: github.com/vektah/gqlparser/validator

     1  //go:generate go run ./inliner/inliner.go
     2  
     3  package validator
     4  
     5  import (
     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  
    14  func 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  
    22  func 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  	defs := append(DefinitionList{}, ast.Definitions...)
    38  
    39  	for _, ext := range ast.Extensions {
    40  		def := schema.Types[ext.Name]
    41  		if def == nil {
    42  			schema.Types[ext.Name] = &Definition{
    43  				Kind:        ext.Kind,
    44  				Name:        ext.Name,
    45  				Position: ext.Position,
    46  			}
    47  			def = schema.Types[ext.Name]
    48  			defs = append(defs, def)
    49  		}
    50  
    51  		if def.Kind != ext.Kind {
    52  			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)
    53  		}
    54  
    55  		def.Directives = append(def.Directives, ext.Directives...)
    56  		def.Interfaces = append(def.Interfaces, ext.Interfaces...)
    57  		def.Fields = append(def.Fields, ext.Fields...)
    58  		def.Types = append(def.Types, ext.Types...)
    59  		def.EnumValues = append(def.EnumValues, ext.EnumValues...)
    60  	}
    61  
    62  	for _, def := range defs {
    63  		switch def.Kind {
    64  		case Union:
    65  			for _, t := range def.Types {
    66  				schema.AddPossibleType(def.Name, schema.Types[t])
    67  				schema.AddImplements(t, def)
    68  			}
    69  		case InputObject, Object:
    70  			for _, intf := range def.Interfaces {
    71  				schema.AddPossibleType(intf, def)
    72  				schema.AddImplements(def.Name, schema.Types[intf])
    73  			}
    74  			schema.AddPossibleType(def.Name, def)
    75  		}
    76  	}
    77  
    78  	for i, dir := range ast.Directives {
    79  		if schema.Directives[dir.Name] != nil {
    80  			return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name)
    81  		}
    82  		schema.Directives[dir.Name] = ast.Directives[i]
    83  	}
    84  
    85  	if len(ast.Schema) > 1 {
    86  		return nil, gqlerror.ErrorPosf(ast.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.")
    87  	}
    88  
    89  	if len(ast.Schema) == 1 {
    90  		for _, entrypoint := range ast.Schema[0].OperationTypes {
    91  			def := schema.Types[entrypoint.Type]
    92  			if def == nil {
    93  				return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
    94  			}
    95  			switch entrypoint.Operation {
    96  			case Query:
    97  				schema.Query = def
    98  			case Mutation:
    99  				schema.Mutation = def
   100  			case Subscription:
   101  				schema.Subscription = def
   102  			}
   103  		}
   104  	}
   105  
   106  	for _, ext := range ast.SchemaExtension {
   107  		for _, entrypoint := range ext.OperationTypes {
   108  			def := schema.Types[entrypoint.Type]
   109  			if def == nil {
   110  				return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
   111  			}
   112  			switch entrypoint.Operation {
   113  			case Query:
   114  				schema.Query = def
   115  			case Mutation:
   116  				schema.Mutation = def
   117  			case Subscription:
   118  				schema.Subscription = def
   119  			}
   120  		}
   121  	}
   122  
   123  	for _, typ := range schema.Types {
   124  		err := validateDefinition(&schema, typ)
   125  		if err != nil {
   126  			return nil, err
   127  		}
   128  	}
   129  
   130  	for _, dir := range schema.Directives {
   131  		err := validateDirective(&schema, dir)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  	}
   136  
   137  	if schema.Query == nil && schema.Types["Query"] != nil {
   138  		schema.Query = schema.Types["Query"]
   139  	}
   140  
   141  	if schema.Mutation == nil && schema.Types["Mutation"] != nil {
   142  		schema.Mutation = schema.Types["Mutation"]
   143  	}
   144  
   145  	if schema.Subscription == nil && schema.Types["Subscription"] != nil {
   146  		schema.Subscription = schema.Types["Subscription"]
   147  	}
   148  
   149  	if schema.Query != nil {
   150  		schema.Query.Fields = append(
   151  			schema.Query.Fields,
   152  			&FieldDefinition{
   153  				Name: "__schema",
   154  				Type: NonNullNamedType("__Schema", nil),
   155  			},
   156  			&FieldDefinition{
   157  				Name: "__type",
   158  				Type: NonNullNamedType("__Type", nil),
   159  				Arguments: ArgumentDefinitionList{
   160  					{Name: "name", Type: NamedType("String", nil)},
   161  				},
   162  			},
   163  		)
   164  	}
   165  
   166  	return &schema, nil
   167  }
   168  
   169  func validateDirective(schema *Schema, def *DirectiveDefinition) *gqlerror.Error {
   170  	if err := validateName(def.Position, def.Name); err != nil {
   171  		// now, GraphQL spec doesn't have reserved directive name
   172  		return err
   173  	}
   174  
   175  	return validateArgs(schema, def.Arguments, def)
   176  }
   177  
   178  func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
   179  	for _, field := range def.Fields {
   180  		if err := validateName(field.Position, field.Name); err != nil {
   181  			// now, GraphQL spec doesn't have reserved field name
   182  			return err
   183  		}
   184  		if err := validateTypeRef(schema, field.Type); err != nil {
   185  			return err
   186  		}
   187  		if err := validateArgs(schema, field.Arguments, nil); err != nil {
   188  			return err
   189  		}
   190  		if err := validateDirectives(schema, field.Directives, nil); err != nil {
   191  			return err
   192  		}
   193  	}
   194  
   195  	for _, typ := range def.Types {
   196  		typDef := schema.Types[typ]
   197  		if typDef == nil {
   198  			return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(typ))
   199  		}
   200  		if !isValidKind(typDef.Kind, Object) {
   201  			return gqlerror.ErrorPosf(def.Position, "%s type %s must be %s.", def.Kind, strconv.Quote(typ), kindList(Object))
   202  		}
   203  	}
   204  
   205  	for _, intf := range def.Interfaces {
   206  		if err := validateImplements(schema, def, intf); err != nil {
   207  			return err
   208  		}
   209  	}
   210  
   211  	switch def.Kind {
   212  	case Object, Interface:
   213  		if len(def.Fields) == 0 {
   214  			return gqlerror.ErrorPosf(def.Position, "%s must define one or more fields.", def.Kind)
   215  		}
   216  		for _, field := range def.Fields {
   217  			if typ, ok := schema.Types[field.Type.Name()]; ok {
   218  				if !isValidKind(typ.Kind, Scalar, Object, Interface, Union, Enum) {
   219  					return gqlerror.ErrorPosf(field.Position, "%s field must be one of %s.", def.Kind, kindList(Scalar, Object, Interface, Union, Enum))
   220  				}
   221  			}
   222  		}
   223  	case Enum:
   224  		if len(def.EnumValues) == 0 {
   225  			return gqlerror.ErrorPosf(def.Position, "%s must define one or more unique enum values.", def.Kind)
   226  		}
   227  	case InputObject:
   228  		if len(def.Fields) == 0 {
   229  			return gqlerror.ErrorPosf(def.Position, "%s must define one or more input fields.", def.Kind)
   230  		}
   231  		for _, field := range def.Fields {
   232  			if typ, ok := schema.Types[field.Type.Name()]; ok {
   233  				if !isValidKind(typ.Kind, Scalar, Enum, InputObject) {
   234  					return gqlerror.ErrorPosf(field.Position, "%s field must be one of %s.", def.Kind, kindList(Scalar, Enum, InputObject))
   235  				}
   236  			}
   237  		}
   238  	}
   239  
   240  	for idx, field1 := range def.Fields {
   241  		for _, field2 := range def.Fields[idx+1:] {
   242  			if field1.Name == field2.Name {
   243  				return gqlerror.ErrorPosf(field2.Position, "Field %s.%s can only be defined once.", def.Name, field2.Name)
   244  			}
   245  		}
   246  	}
   247  
   248  	if !def.BuiltIn {
   249  		// GraphQL spec has reserved type names a lot!
   250  		err := validateName(def.Position, def.Name)
   251  		if err != nil {
   252  			return err
   253  		}
   254  	}
   255  
   256  	return validateDirectives(schema, def.Directives, nil)
   257  }
   258  
   259  func validateTypeRef(schema *Schema, typ *Type) *gqlerror.Error {
   260  	if schema.Types[typ.Name()] == nil {
   261  		return gqlerror.ErrorPosf(typ.Position, "Undefined type %s.", typ.Name())
   262  	}
   263  	return nil
   264  }
   265  
   266  func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective *DirectiveDefinition) *gqlerror.Error {
   267  	for _, arg := range args {
   268  		if err := validateName(arg.Position, arg.Name); err != nil {
   269  			// now, GraphQL spec doesn't have reserved argument name
   270  			return err
   271  		}
   272  		if err := validateTypeRef(schema, arg.Type); err != nil {
   273  			return err
   274  		}
   275  		def := schema.Types[arg.Type.Name()]
   276  		if !def.IsInputType() {
   277  			return gqlerror.ErrorPosf(
   278  				arg.Position,
   279  				"cannot use %s as argument %s because %s is not a valid input type",
   280  				arg.Type.String(),
   281  				arg.Name,
   282  				def.Kind,
   283  			)
   284  		}
   285  		if err := validateDirectives(schema, arg.Directives, currentDirective); err != nil {
   286  			return err
   287  		}
   288  	}
   289  	return nil
   290  }
   291  
   292  func validateDirectives(schema *Schema, dirs DirectiveList, currentDirective *DirectiveDefinition) *gqlerror.Error {
   293  	for _, dir := range dirs {
   294  		if err := validateName(dir.Position, dir.Name); err != nil {
   295  			// now, GraphQL spec doesn't have reserved directive name
   296  			return err
   297  		}
   298  		if currentDirective != nil && dir.Name == currentDirective.Name {
   299  			return gqlerror.ErrorPosf(dir.Position, "Directive %s cannot refer to itself.", currentDirective.Name)
   300  		}
   301  		if schema.Directives[dir.Name] == nil {
   302  			return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
   303  		}
   304  		dir.Definition = schema.Directives[dir.Name]
   305  	}
   306  	return nil
   307  }
   308  
   309  func validateImplements(schema *Schema, def *Definition, intfName string) *gqlerror.Error {
   310  	// see validation rules at the bottom of
   311  	// https://facebook.github.io/graphql/June2018/#sec-Objects
   312  	intf := schema.Types[intfName]
   313  	if intf == nil {
   314  		return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
   315  	}
   316  	if intf.Kind != Interface {
   317  		return gqlerror.ErrorPosf(def.Position, "%s is a non interface type %s.", strconv.Quote(intfName), intf.Kind)
   318  	}
   319  	for _, requiredField := range intf.Fields {
   320  		foundField := def.Fields.ForName(requiredField.Name)
   321  		if foundField == nil {
   322  			return gqlerror.ErrorPosf(def.Position,
   323  				`For %s to implement %s it must have a field called %s.`,
   324  				def.Name, intf.Name, requiredField.Name,
   325  			)
   326  		}
   327  
   328  		if !isCovariant(schema, requiredField.Type, foundField.Type) {
   329  			return gqlerror.ErrorPosf(foundField.Position,
   330  				`For %s to implement %s the field %s must have type %s.`,
   331  				def.Name, intf.Name, requiredField.Name, requiredField.Type.String(),
   332  			)
   333  		}
   334  
   335  		for _, requiredArg := range requiredField.Arguments {
   336  			foundArg := foundField.Arguments.ForName(requiredArg.Name)
   337  			if foundArg == nil {
   338  				return gqlerror.ErrorPosf(foundField.Position,
   339  					`For %s to implement %s the field %s must have the same arguments but it is missing %s.`,
   340  					def.Name, intf.Name, requiredField.Name, requiredArg.Name,
   341  				)
   342  			}
   343  
   344  			if !requiredArg.Type.IsCompatible(foundArg.Type) {
   345  				return gqlerror.ErrorPosf(foundArg.Position,
   346  					`For %s to implement %s the field %s must have the same arguments but %s has the wrong type.`,
   347  					def.Name, intf.Name, requiredField.Name, requiredArg.Name,
   348  				)
   349  			}
   350  		}
   351  		for _, foundArgs := range foundField.Arguments {
   352  			if requiredField.Arguments.ForName(foundArgs.Name) == nil && foundArgs.Type.NonNull && foundArgs.DefaultValue == nil {
   353  				return gqlerror.ErrorPosf(foundArgs.Position,
   354  					`For %s to implement %s any additional arguments on %s must be optional or have a default value but %s is required.`,
   355  					def.Name, intf.Name, foundField.Name, foundArgs.Name,
   356  				)
   357  			}
   358  		}
   359  	}
   360  	return nil
   361  }
   362  
   363  func isCovariant(schema *Schema, required *Type, actual *Type) bool {
   364  	if required.NonNull && !actual.NonNull {
   365  		return false
   366  	}
   367  
   368  	if required.NamedType != "" {
   369  		if required.NamedType == actual.NamedType {
   370  			return true
   371  		}
   372  		for _, pt := range schema.PossibleTypes[required.NamedType] {
   373  			if pt.Name == actual.NamedType {
   374  				return true
   375  			}
   376  		}
   377  		return false
   378  	}
   379  
   380  	if required.Elem != nil && actual.Elem == nil {
   381  		return false
   382  	}
   383  
   384  	return isCovariant(schema, required.Elem, actual.Elem)
   385  }
   386  
   387  func validateName(pos *Position, name string) *gqlerror.Error {
   388  	if strings.HasPrefix(name, "__") {
   389  		return gqlerror.ErrorPosf(pos, `Name "%s" must not begin with "__", which is reserved by GraphQL introspection.`, name)
   390  	}
   391  	return nil
   392  }
   393  
   394  func isValidKind(kind DefinitionKind, valid ...DefinitionKind) bool {
   395  	for _, k := range valid {
   396  		if kind == k {
   397  			return true
   398  		}
   399  	}
   400  	return false
   401  }
   402  
   403  func kindList(kinds ...DefinitionKind) string {
   404  	s := make([]string, len(kinds))
   405  	for i, k := range kinds {
   406  		s[i] = string(k)
   407  	}
   408  	return strings.Join(s, ", ")
   409  }
   410  

View as plain text