...

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

Documentation: github.com/vektah/gqlparser/v2/validator

     1  package validator
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  	"strings"
     7  
     8  	//nolint:revive
     9  	. "github.com/vektah/gqlparser/v2/ast"
    10  	"github.com/vektah/gqlparser/v2/gqlerror"
    11  	"github.com/vektah/gqlparser/v2/parser"
    12  )
    13  
    14  func LoadSchema(inputs ...*Source) (*Schema, error) {
    15  	sd, err := parser.ParseSchemas(inputs...)
    16  
    17  	if err != nil {
    18  		return nil, gqlerror.WrapIfUnwrapped(err)
    19  	}
    20  	return ValidateSchemaDocument(sd)
    21  }
    22  
    23  func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) {
    24  	schema := Schema{
    25  		Types:         map[string]*Definition{},
    26  		Directives:    map[string]*DirectiveDefinition{},
    27  		PossibleTypes: map[string][]*Definition{},
    28  		Implements:    map[string][]*Definition{},
    29  	}
    30  
    31  	for i, def := range sd.Definitions {
    32  		if schema.Types[def.Name] != nil {
    33  			return nil, gqlerror.ErrorPosf(def.Position, "Cannot redeclare type %s.", def.Name)
    34  		}
    35  		schema.Types[def.Name] = sd.Definitions[i]
    36  	}
    37  
    38  	defs := append(DefinitionList{}, sd.Definitions...)
    39  
    40  	for _, ext := range sd.Extensions {
    41  		def := schema.Types[ext.Name]
    42  		if def == nil {
    43  			schema.Types[ext.Name] = &Definition{
    44  				Kind:     ext.Kind,
    45  				Name:     ext.Name,
    46  				Position: ext.Position,
    47  			}
    48  			def = schema.Types[ext.Name]
    49  			defs = append(defs, def)
    50  		}
    51  
    52  		if def.Kind != ext.Kind {
    53  			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)
    54  		}
    55  
    56  		def.Directives = append(def.Directives, ext.Directives...)
    57  		def.Interfaces = append(def.Interfaces, ext.Interfaces...)
    58  		def.Fields = append(def.Fields, ext.Fields...)
    59  		def.Types = append(def.Types, ext.Types...)
    60  		def.EnumValues = append(def.EnumValues, ext.EnumValues...)
    61  	}
    62  
    63  	for _, def := range defs {
    64  		switch def.Kind {
    65  		case Union:
    66  			for _, t := range def.Types {
    67  				schema.AddPossibleType(def.Name, schema.Types[t])
    68  				schema.AddImplements(t, def)
    69  			}
    70  		case InputObject, Object:
    71  			for _, intf := range def.Interfaces {
    72  				schema.AddPossibleType(intf, def)
    73  				schema.AddImplements(def.Name, schema.Types[intf])
    74  			}
    75  			schema.AddPossibleType(def.Name, def)
    76  		case Interface:
    77  			for _, intf := range def.Interfaces {
    78  				schema.AddPossibleType(intf, def)
    79  				schema.AddImplements(def.Name, schema.Types[intf])
    80  			}
    81  		}
    82  	}
    83  
    84  	for i, dir := range sd.Directives {
    85  		if schema.Directives[dir.Name] != nil {
    86  			// While the spec says SDL must not (§3.5) explicitly define builtin
    87  			// scalars, it may (§3.13) define builtin directives. Here we check for
    88  			// that, and reject doubly-defined directives otherwise.
    89  			switch dir.Name {
    90  			case "include", "skip", "deprecated", "specifiedBy", "defer": // the builtins
    91  				// In principle here we might want to validate that the
    92  				// directives are the same. But they might not be, if the
    93  				// server has an older spec than we do. (Plus, validating this
    94  				// is a lot of work.) So we just keep the first one we saw.
    95  				// That's an arbitrary choice, but in theory the only way it
    96  				// fails is if the server is using features newer than this
    97  				// version of gqlparser, in which case they're in trouble
    98  				// anyway.
    99  			default:
   100  				return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name)
   101  			}
   102  		}
   103  		schema.Directives[dir.Name] = sd.Directives[i]
   104  	}
   105  
   106  	if len(sd.Schema) > 1 {
   107  		return nil, gqlerror.ErrorPosf(sd.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.")
   108  	}
   109  
   110  	if len(sd.Schema) == 1 {
   111  		schema.Description = sd.Schema[0].Description
   112  		for _, entrypoint := range sd.Schema[0].OperationTypes {
   113  			def := schema.Types[entrypoint.Type]
   114  			if def == nil {
   115  				return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
   116  			}
   117  			switch entrypoint.Operation {
   118  			case Query:
   119  				schema.Query = def
   120  			case Mutation:
   121  				schema.Mutation = def
   122  			case Subscription:
   123  				schema.Subscription = def
   124  			}
   125  		}
   126  	}
   127  
   128  	for _, ext := range sd.SchemaExtension {
   129  		for _, entrypoint := range ext.OperationTypes {
   130  			def := schema.Types[entrypoint.Type]
   131  			if def == nil {
   132  				return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
   133  			}
   134  			switch entrypoint.Operation {
   135  			case Query:
   136  				schema.Query = def
   137  			case Mutation:
   138  				schema.Mutation = def
   139  			case Subscription:
   140  				schema.Subscription = def
   141  			}
   142  		}
   143  	}
   144  
   145  	if err := validateTypeDefinitions(&schema); err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	if err := validateDirectiveDefinitions(&schema); err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	// Inferred root operation type names should be performed only when a `schema` directive is
   154  	// **not** provided, when it is, `Mutation` and `Subscription` becomes valid types and are not
   155  	// assigned as a root operation on the schema.
   156  	if len(sd.Schema) == 0 {
   157  		if schema.Query == nil && schema.Types["Query"] != nil {
   158  			schema.Query = schema.Types["Query"]
   159  		}
   160  
   161  		if schema.Mutation == nil && schema.Types["Mutation"] != nil {
   162  			schema.Mutation = schema.Types["Mutation"]
   163  		}
   164  
   165  		if schema.Subscription == nil && schema.Types["Subscription"] != nil {
   166  			schema.Subscription = schema.Types["Subscription"]
   167  		}
   168  	}
   169  
   170  	if schema.Query != nil {
   171  		schema.Query.Fields = append(
   172  			schema.Query.Fields,
   173  			&FieldDefinition{
   174  				Name: "__schema",
   175  				Type: NonNullNamedType("__Schema", nil),
   176  			},
   177  			&FieldDefinition{
   178  				Name: "__type",
   179  				Type: NamedType("__Type", nil),
   180  				Arguments: ArgumentDefinitionList{
   181  					{Name: "name", Type: NonNullNamedType("String", nil)},
   182  				},
   183  			},
   184  		)
   185  	}
   186  
   187  	return &schema, nil
   188  }
   189  
   190  func validateTypeDefinitions(schema *Schema) *gqlerror.Error {
   191  	types := make([]string, 0, len(schema.Types))
   192  	for typ := range schema.Types {
   193  		types = append(types, typ)
   194  	}
   195  	sort.Strings(types)
   196  	for _, typ := range types {
   197  		err := validateDefinition(schema, schema.Types[typ])
   198  		if err != nil {
   199  			return err
   200  		}
   201  	}
   202  	return nil
   203  }
   204  
   205  func validateDirectiveDefinitions(schema *Schema) *gqlerror.Error {
   206  	directives := make([]string, 0, len(schema.Directives))
   207  	for directive := range schema.Directives {
   208  		directives = append(directives, directive)
   209  	}
   210  	sort.Strings(directives)
   211  	for _, directive := range directives {
   212  		err := validateDirective(schema, schema.Directives[directive])
   213  		if err != nil {
   214  			return err
   215  		}
   216  	}
   217  	return nil
   218  }
   219  
   220  func validateDirective(schema *Schema, def *DirectiveDefinition) *gqlerror.Error {
   221  	if err := validateName(def.Position, def.Name); err != nil {
   222  		// now, GraphQL spec doesn't have reserved directive name
   223  		return err
   224  	}
   225  
   226  	return validateArgs(schema, def.Arguments, def)
   227  }
   228  
   229  func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
   230  	for _, field := range def.Fields {
   231  		if err := validateName(field.Position, field.Name); err != nil {
   232  			// now, GraphQL spec doesn't have reserved field name
   233  			return err
   234  		}
   235  		if err := validateTypeRef(schema, field.Type); err != nil {
   236  			return err
   237  		}
   238  		if err := validateArgs(schema, field.Arguments, nil); err != nil {
   239  			return err
   240  		}
   241  		wantDirLocation := LocationFieldDefinition
   242  		if def.Kind == InputObject {
   243  			wantDirLocation = LocationInputFieldDefinition
   244  		}
   245  		if err := validateDirectives(schema, field.Directives, wantDirLocation, nil); err != nil {
   246  			return err
   247  		}
   248  	}
   249  
   250  	for _, typ := range def.Types {
   251  		typDef := schema.Types[typ]
   252  		if typDef == nil {
   253  			return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(typ))
   254  		}
   255  		if !isValidKind(typDef.Kind, Object) {
   256  			return gqlerror.ErrorPosf(def.Position, "%s type %s must be %s.", def.Kind, strconv.Quote(typ), kindList(Object))
   257  		}
   258  	}
   259  
   260  	for _, intf := range def.Interfaces {
   261  		if err := validateImplements(schema, def, intf); err != nil {
   262  			return err
   263  		}
   264  	}
   265  
   266  	switch def.Kind {
   267  	case Object, Interface:
   268  		if len(def.Fields) == 0 {
   269  			return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more fields.", def.Kind, def.Name)
   270  		}
   271  		for _, field := range def.Fields {
   272  			if typ, ok := schema.Types[field.Type.Name()]; ok {
   273  				if !isValidKind(typ.Kind, Scalar, Object, Interface, Union, Enum) {
   274  					return gqlerror.ErrorPosf(field.Position, "%s %s: field must be one of %s.", def.Kind, def.Name, kindList(Scalar, Object, Interface, Union, Enum))
   275  				}
   276  			}
   277  		}
   278  	case Enum:
   279  		if len(def.EnumValues) == 0 {
   280  			return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more unique enum values.", def.Kind, def.Name)
   281  		}
   282  		for _, value := range def.EnumValues {
   283  			for _, nonEnum := range [3]string{"true", "false", "null"} {
   284  				if value.Name == nonEnum {
   285  					return gqlerror.ErrorPosf(def.Position, "%s %s: non-enum value %s.", def.Kind, def.Name, value.Name)
   286  				}
   287  			}
   288  			if err := validateDirectives(schema, value.Directives, LocationEnumValue, nil); err != nil {
   289  				return err
   290  			}
   291  		}
   292  	case InputObject:
   293  		if len(def.Fields) == 0 {
   294  			return gqlerror.ErrorPosf(def.Position, "%s %s: must define one or more input fields.", def.Kind, def.Name)
   295  		}
   296  		for _, field := range def.Fields {
   297  			if typ, ok := schema.Types[field.Type.Name()]; ok {
   298  				if !isValidKind(typ.Kind, Scalar, Enum, InputObject) {
   299  					return gqlerror.ErrorPosf(field.Position, "%s %s: field must be one of %s.", typ.Kind, field.Name, kindList(Scalar, Enum, InputObject))
   300  				}
   301  			}
   302  		}
   303  	}
   304  
   305  	for idx, field1 := range def.Fields {
   306  		for _, field2 := range def.Fields[idx+1:] {
   307  			if field1.Name == field2.Name {
   308  				return gqlerror.ErrorPosf(field2.Position, "Field %s.%s can only be defined once.", def.Name, field2.Name)
   309  			}
   310  		}
   311  	}
   312  
   313  	if !def.BuiltIn {
   314  		// GraphQL spec has reserved type names a lot!
   315  		err := validateName(def.Position, def.Name)
   316  		if err != nil {
   317  			return err
   318  		}
   319  	}
   320  
   321  	return validateDirectives(schema, def.Directives, DirectiveLocation(def.Kind), nil)
   322  }
   323  
   324  func validateTypeRef(schema *Schema, typ *Type) *gqlerror.Error {
   325  	if schema.Types[typ.Name()] == nil {
   326  		return gqlerror.ErrorPosf(typ.Position, "Undefined type %s.", typ.Name())
   327  	}
   328  	return nil
   329  }
   330  
   331  func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective *DirectiveDefinition) *gqlerror.Error {
   332  	for _, arg := range args {
   333  		if err := validateName(arg.Position, arg.Name); err != nil {
   334  			// now, GraphQL spec doesn't have reserved argument name
   335  			return err
   336  		}
   337  		if err := validateTypeRef(schema, arg.Type); err != nil {
   338  			return err
   339  		}
   340  		def := schema.Types[arg.Type.Name()]
   341  		if !def.IsInputType() {
   342  			return gqlerror.ErrorPosf(
   343  				arg.Position,
   344  				"cannot use %s as argument %s because %s is not a valid input type",
   345  				arg.Type.String(),
   346  				arg.Name,
   347  				def.Kind,
   348  			)
   349  		}
   350  		if err := validateDirectives(schema, arg.Directives, LocationArgumentDefinition, currentDirective); err != nil {
   351  			return err
   352  		}
   353  	}
   354  	return nil
   355  }
   356  
   357  func validateDirectives(schema *Schema, dirs DirectiveList, location DirectiveLocation, currentDirective *DirectiveDefinition) *gqlerror.Error {
   358  	for _, dir := range dirs {
   359  		if err := validateName(dir.Position, dir.Name); err != nil {
   360  			// now, GraphQL spec doesn't have reserved directive name
   361  			return err
   362  		}
   363  		if currentDirective != nil && dir.Name == currentDirective.Name {
   364  			return gqlerror.ErrorPosf(dir.Position, "Directive %s cannot refer to itself.", currentDirective.Name)
   365  		}
   366  		dirDefinition := schema.Directives[dir.Name]
   367  		if dirDefinition == nil {
   368  			return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
   369  		}
   370  		validKind := false
   371  		for _, dirLocation := range dirDefinition.Locations {
   372  			if dirLocation == location {
   373  				validKind = true
   374  				break
   375  			}
   376  		}
   377  		if !validKind {
   378  			return gqlerror.ErrorPosf(dir.Position, "Directive %s is not applicable on %s.", dir.Name, location)
   379  		}
   380  		for _, arg := range dir.Arguments {
   381  			if dirDefinition.Arguments.ForName(arg.Name) == nil {
   382  				return gqlerror.ErrorPosf(arg.Position, "Undefined argument %s for directive %s.", arg.Name, dir.Name)
   383  			}
   384  		}
   385  		for _, schemaArg := range dirDefinition.Arguments {
   386  			if schemaArg.Type.NonNull && schemaArg.DefaultValue == nil {
   387  				if arg := dir.Arguments.ForName(schemaArg.Name); arg == nil || arg.Value.Kind == NullValue {
   388  					return gqlerror.ErrorPosf(dir.Position, "Argument %s for directive %s cannot be null.", schemaArg.Name, dir.Name)
   389  				}
   390  			}
   391  		}
   392  		dir.Definition = schema.Directives[dir.Name]
   393  	}
   394  	return nil
   395  }
   396  
   397  func validateImplements(schema *Schema, def *Definition, intfName string) *gqlerror.Error {
   398  	// see validation rules at the bottom of
   399  	// https://spec.graphql.org/October2021/#sec-Objects
   400  	intf := schema.Types[intfName]
   401  	if intf == nil {
   402  		return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
   403  	}
   404  	if intf.Kind != Interface {
   405  		return gqlerror.ErrorPosf(def.Position, "%s is a non interface type %s.", strconv.Quote(intfName), intf.Kind)
   406  	}
   407  	for _, requiredField := range intf.Fields {
   408  		foundField := def.Fields.ForName(requiredField.Name)
   409  		if foundField == nil {
   410  			return gqlerror.ErrorPosf(def.Position,
   411  				`For %s to implement %s it must have a field called %s.`,
   412  				def.Name, intf.Name, requiredField.Name,
   413  			)
   414  		}
   415  
   416  		if !isCovariant(schema, requiredField.Type, foundField.Type) {
   417  			return gqlerror.ErrorPosf(foundField.Position,
   418  				`For %s to implement %s the field %s must have type %s.`,
   419  				def.Name, intf.Name, requiredField.Name, requiredField.Type.String(),
   420  			)
   421  		}
   422  
   423  		for _, requiredArg := range requiredField.Arguments {
   424  			foundArg := foundField.Arguments.ForName(requiredArg.Name)
   425  			if foundArg == nil {
   426  				return gqlerror.ErrorPosf(foundField.Position,
   427  					`For %s to implement %s the field %s must have the same arguments but it is missing %s.`,
   428  					def.Name, intf.Name, requiredField.Name, requiredArg.Name,
   429  				)
   430  			}
   431  
   432  			if !requiredArg.Type.IsCompatible(foundArg.Type) {
   433  				return gqlerror.ErrorPosf(foundArg.Position,
   434  					`For %s to implement %s the field %s must have the same arguments but %s has the wrong type.`,
   435  					def.Name, intf.Name, requiredField.Name, requiredArg.Name,
   436  				)
   437  			}
   438  		}
   439  		for _, foundArgs := range foundField.Arguments {
   440  			if requiredField.Arguments.ForName(foundArgs.Name) == nil && foundArgs.Type.NonNull && foundArgs.DefaultValue == nil {
   441  				return gqlerror.ErrorPosf(foundArgs.Position,
   442  					`For %s to implement %s any additional arguments on %s must be optional or have a default value but %s is required.`,
   443  					def.Name, intf.Name, foundField.Name, foundArgs.Name,
   444  				)
   445  			}
   446  		}
   447  	}
   448  	return validateTypeImplementsAncestors(schema, def, intfName)
   449  }
   450  
   451  // validateTypeImplementsAncestors
   452  // https://github.com/graphql/graphql-js/blob/47bd8c8897c72d3efc17ecb1599a95cee6bac5e8/src/type/validate.ts#L428
   453  func validateTypeImplementsAncestors(schema *Schema, def *Definition, intfName string) *gqlerror.Error {
   454  	intf := schema.Types[intfName]
   455  	if intf == nil {
   456  		return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName))
   457  	}
   458  	for _, transitive := range intf.Interfaces {
   459  		if !containsString(def.Interfaces, transitive) {
   460  			if transitive == def.Name {
   461  				return gqlerror.ErrorPosf(def.Position,
   462  					`Type %s cannot implement %s because it would create a circular reference.`,
   463  					def.Name, intfName,
   464  				)
   465  			}
   466  			return gqlerror.ErrorPosf(def.Position,
   467  				`Type %s must implement %s because it is implemented by %s.`,
   468  				def.Name, transitive, intfName,
   469  			)
   470  		}
   471  	}
   472  	return nil
   473  }
   474  
   475  func containsString(slice []string, want string) bool {
   476  	for _, str := range slice {
   477  		if want == str {
   478  			return true
   479  		}
   480  	}
   481  	return false
   482  }
   483  
   484  func isCovariant(schema *Schema, required *Type, actual *Type) bool {
   485  	if required.NonNull && !actual.NonNull {
   486  		return false
   487  	}
   488  
   489  	if required.NamedType != "" {
   490  		if required.NamedType == actual.NamedType {
   491  			return true
   492  		}
   493  		for _, pt := range schema.PossibleTypes[required.NamedType] {
   494  			if pt.Name == actual.NamedType {
   495  				return true
   496  			}
   497  		}
   498  		return false
   499  	}
   500  
   501  	if required.Elem != nil && actual.Elem == nil {
   502  		return false
   503  	}
   504  
   505  	return isCovariant(schema, required.Elem, actual.Elem)
   506  }
   507  
   508  func validateName(pos *Position, name string) *gqlerror.Error {
   509  	if strings.HasPrefix(name, "__") {
   510  		return gqlerror.ErrorPosf(pos, `Name "%s" must not begin with "__", which is reserved by GraphQL introspection.`, name)
   511  	}
   512  	return nil
   513  }
   514  
   515  func isValidKind(kind DefinitionKind, valid ...DefinitionKind) bool {
   516  	for _, k := range valid {
   517  		if kind == k {
   518  			return true
   519  		}
   520  	}
   521  	return false
   522  }
   523  
   524  func kindList(kinds ...DefinitionKind) string {
   525  	s := make([]string, len(kinds))
   526  	for i, k := range kinds {
   527  		s[i] = string(k)
   528  	}
   529  	return strings.Join(s, ", ")
   530  }
   531  

View as plain text