Skip to content

Commit

Permalink
Picked Validation Related changes only from graph-gophers/graphql-go …
Browse files Browse the repository at this point in the history
…master
  • Loading branch information
abhif22 committed Sep 10, 2019
1 parent b9dc447 commit 87522cc
Show file tree
Hide file tree
Showing 4 changed files with 321 additions and 7 deletions.
7 changes: 3 additions & 4 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,12 @@ type Response struct {
}

// Validate validates the given query with the schema.
func (s *Schema) Validate(queryString string) []*errors.QueryError {
func (s *Schema) Validate(queryString string, variables map[string]interface{}) []*errors.QueryError {
doc, qErr := query.Parse(queryString)
if qErr != nil {
return []*errors.QueryError{qErr}
}

return validation.Validate(s.schema, doc, s.maxDepth)
return validation.Validate(s.schema, doc, variables, s.maxDepth)
}

// Exec executes the given query with the schema's resolver. It panics if the schema was created
Expand All @@ -143,7 +142,7 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str
}

validationFinish := s.validationTracer.TraceValidation()
errs := validation.Validate(s.schema, doc, s.maxDepth)
errs := validation.Validate(s.schema, doc, variables, s.maxDepth)
validationFinish(errs)
if len(errs) != 0 {
return &Response{Errors: errs}
Expand Down
263 changes: 262 additions & 1 deletion internal/validation/testdata/tests.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemas": [
"schema {\n query: QueryRoot\n}\n\ndirective @onQuery on QUERY\n\ndirective @onMutation on MUTATION\n\ndirective @onSubscription on SUBSCRIPTION\n\ndirective @onField on FIELD\n\ndirective @onFragmentDefinition on FRAGMENT_DEFINITION\n\ndirective @onFragmentSpread on FRAGMENT_SPREAD\n\ndirective @onInlineFragment on INLINE_FRAGMENT\n\ndirective @onSchema on SCHEMA\n\ndirective @onScalar on SCALAR\n\ndirective @onObject on OBJECT\n\ndirective @onFieldDefinition on FIELD_DEFINITION\n\ndirective @onArgumentDefinition on ARGUMENT_DEFINITION\n\ndirective @onInterface on INTERFACE\n\ndirective @onUnion on UNION\n\ndirective @onEnum on ENUM\n\ndirective @onEnumValue on ENUM_VALUE\n\ndirective @onInputObject on INPUT_OBJECT\n\ndirective @onInputFieldDefinition on INPUT_FIELD_DEFINITION\n\ntype Alien implements Being & Intelligent {\n iq: Int\n name(surname: Boolean): String\n numEyes: Int\n}\n\nscalar Any\n\ninterface Being {\n name(surname: Boolean): String\n}\n\ninterface Canine {\n name(surname: Boolean): String\n}\n\ntype Cat implements Being & Pet {\n name(surname: Boolean): String\n nickname: String\n meows: Boolean\n meowVolume: Int\n furColor: FurColor\n}\n\nunion CatOrDog = Dog | Cat\n\ninput ComplexInput {\n requiredField: Boolean!\n intField: Int\n stringField: String\n booleanField: Boolean\n stringListField: [String]\n}\n\ntype ComplicatedArgs {\n intArgField(intArg: Int): String\n nonNullIntArgField(nonNullIntArg: Int!): String\n stringArgField(stringArg: String): String\n booleanArgField(booleanArg: Boolean): String\n enumArgField(enumArg: FurColor): String\n floatArgField(floatArg: Float): String\n idArgField(idArg: ID): String\n stringListArgField(stringListArg: [String]): String\n stringListNonNullArgField(stringListNonNullArg: [String!]): String\n complexArgField(complexArg: ComplexInput): String\n multipleReqs(req1: Int!, req2: Int!): String\n multipleOpts(opt1: Int = 0, opt2: Int = 0): String\n multipleOptAndReq(req1: Int!, req2: Int!, opt1: Int = 0, opt2: Int = 0): String\n}\n\ntype Dog implements Being & Pet & Canine {\n name(surname: Boolean): String\n nickname: String\n barkVolume: Int\n barks: Boolean\n doesKnowCommand(dogCommand: DogCommand): Boolean\n isHousetrained(atOtherHomes: Boolean = true): Boolean\n isAtLocation(x: Int, y: Int): Boolean\n}\n\nenum DogCommand {\n SIT\n HEEL\n DOWN\n}\n\nunion DogOrHuman = Dog | Human\n\nenum FurColor {\n BROWN\n BLACK\n TAN\n SPOTTED\n NO_FUR\n UNKNOWN\n}\n\ntype Human implements Being & Intelligent {\n name(surname: Boolean): String\n pets: [Pet]\n relatives: [Human]\n iq: Int\n}\n\nunion HumanOrAlien = Human | Alien\n\ninterface Intelligent {\n iq: Int\n}\n\nscalar Invalid\n\ninterface Pet {\n name(surname: Boolean): String\n}\n\ntype QueryRoot {\n human(id: ID): Human\n alien: Alien\n dog: Dog\n cat: Cat\n pet: Pet\n catOrDog: CatOrDog\n dogOrHuman: DogOrHuman\n humanOrAlien: HumanOrAlien\n complicatedArgs: ComplicatedArgs\n invalidArg(arg: Invalid): String\n anyArg(arg: Any): String\n}\n",
"schema {\n query: QueryRoot\n}\n\ndirective @onQuery on QUERY\n\ndirective @onMutation on MUTATION\n\ndirective @onSubscription on SUBSCRIPTION\n\ndirective @onField on FIELD\n\ndirective @onFragmentDefinition on FRAGMENT_DEFINITION\n\ndirective @onFragmentSpread on FRAGMENT_SPREAD\n\ndirective @onInlineFragment on INLINE_FRAGMENT\n\ndirective @onSchema on SCHEMA\n\ndirective @onScalar on SCALAR\n\ndirective @onObject on OBJECT\n\ndirective @onFieldDefinition on FIELD_DEFINITION\n\ndirective @onArgumentDefinition on ARGUMENT_DEFINITION\n\ndirective @onInterface on INTERFACE\n\ndirective @onUnion on UNION\n\ndirective @onEnum on ENUM\n\ndirective @onEnumValue on ENUM_VALUE\n\ndirective @onInputObject on INPUT_OBJECT\n\ndirective @onInputFieldDefinition on INPUT_FIELD_DEFINITION\n\ntype Alien implements Being & Intelligent {\n iq: Int\n name(surname: Boolean): String\n numEyes: Int\n}\n\nscalar Any\n\ninterface Being {\n name(surname: Boolean): String\n}\n\ninterface Canine {\n name(surname: Boolean): String\n}\n\ntype Cat implements Being & Pet {\n name(surname: Boolean): String\n nickname: String\n meows: Boolean\n meowVolume: Int\n furColor: FurColor\n}\n\nunion CatOrDog = Dog | Cat\n\ninput ComplexInput {\n requiredField: Boolean!\n intField: Int\n stringField: String\n booleanField: Boolean\n stringListField: [String]\n enumField: FurColor\n nestedInput: SimpleInput}\n\ntype ComplicatedArgs {\n intArgField(intArg: Int): String\n nonNullIntArgField(nonNullIntArg: Int!): String\n stringArgField(stringArg: String): String\n booleanArgField(booleanArg: Boolean): String\n enumArgField(enumArg: FurColor): String\n enumArrayArgField(enumArrayArg: [FurColor!]!): String\n floatArgField(floatArg: Float): String\n idArgField(idArg: ID): String\n stringListArgField(stringListArg: [String]): String\n stringListNonNullArgField(stringListNonNullArg: [String!]): String\n complexArgField(complexArg: ComplexInput): String\n multipleReqs(req1: Int!, req2: Int!): String\n multipleOpts(opt1: Int = 0, opt2: Int = 0): String\n multipleOptAndReq(req1: Int!, req2: Int!, opt1: Int = 0, opt2: Int = 0): String\n}\n\ntype Dog implements Being & Pet & Canine {\n name(surname: Boolean): String\n nickname: String\n barkVolume: Int\n barks: Boolean\n doesKnowCommand(dogCommand: DogCommand): Boolean\n isHousetrained(atOtherHomes: Boolean = true): Boolean\n isAtLocation(x: Int, y: Int): Boolean\n}\n\nenum DogCommand {\n SIT\n HEEL\n DOWN\n}\n\nunion DogOrHuman = Dog | Human\n\nenum FurColor {\n BROWN\n BLACK\n TAN\n SPOTTED\n NO_FUR\n UNKNOWN\n}\n\ntype Human implements Being & Intelligent {\n name(surname: Boolean): String\n pets: [Pet]\n relatives: [Human]\n iq: Int\n}\n\nunion HumanOrAlien = Human | Alien\n\ninterface Intelligent {\n iq: Int\n}\n\nscalar Invalid\n\ninput SimpleInput {\n stringField: String\n stringListField: [String!]\n}\n\ninterface Pet {\n name(surname: Boolean): String\n}\n\ntype QueryRoot {\n human(id: ID): Human\n alien: Alien\n dog: Dog\n cat: Cat\n pet: Pet\n catOrDog: CatOrDog\n dogOrHuman: DogOrHuman\n humanOrAlien: HumanOrAlien\n complicatedArgs: ComplicatedArgs\n invalidArg(arg: Invalid): String\n anyArg(arg: Any): String\n}\n",
"schema {\n query: QueryRoot\n}\n\ntype Connection {\n edges: [Edge]\n}\n\ntype Edge {\n node: Node\n}\n\ntype IntBox implements SomeBox {\n scalar: Int\n deepBox: IntBox\n unrelatedField: String\n listStringBox: [StringBox]\n stringBox: StringBox\n intBox: IntBox\n}\n\ntype Node {\n id: ID\n name: String\n}\n\ninterface NonNullStringBox1 {\n scalar: String!\n}\n\ntype NonNullStringBox1Impl implements SomeBox & NonNullStringBox1 {\n scalar: String!\n unrelatedField: String\n deepBox: SomeBox\n}\n\ninterface NonNullStringBox2 {\n scalar: String!\n}\n\ntype NonNullStringBox2Impl implements SomeBox & NonNullStringBox2 {\n scalar: String!\n unrelatedField: String\n deepBox: SomeBox\n}\n\ntype QueryRoot {\n someBox: SomeBox\n connection: Connection\n}\n\ninterface SomeBox {\n deepBox: SomeBox\n unrelatedField: String\n}\n\ntype StringBox implements SomeBox {\n scalar: String\n deepBox: StringBox\n unrelatedField: String\n listStringBox: [StringBox]\n stringBox: StringBox\n intBox: IntBox\n}\n",
"type Foo {\n constructor: String\n}\n\ntype Query {\n foo: Foo\n}\n"
],
Expand Down Expand Up @@ -1614,6 +1614,267 @@
}
]
},
{
"name": "Validate: Arguments have valid type/valid enum constant in query",
"rule": "ArgumentsOfCorrectType",
"schema": 0,
"query": "\n query Query {\n complicatedArgs {\n enumArgField(enumArg: BROWN)\n }\n }\n ",
"errors": []
},
{
"name": "Validate: Arguments have valid type/invalid enum constant in query text",
"rule": "ArgumentsOfCorrectType",
"schema": 0,
"query": "\n query Query {\n complicatedArgs {\n enumArgField(enumArg: RAINBOW)\n }\n }\n ",
"errors": [
{
"message": "Argument \"enumArg\" has invalid value RAINBOW.\nExpected type \"FurColor\", found RAINBOW.",
"locations": [
{
"line": 4,
"column": 33
}
]
}
]
},
{
"name": "Validate: Arguments have valid type/optional enum constant in query",
"rule": "ArgumentsOfCorrectType",
"schema": 0,
"query": "\n query Query {\n complicatedArgs {\n enumArgField()\n }\n }\n ",
"errors": []
},
{
"name": "Validate: Variables have valid type/valid enum constant in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($color: FurColor) {\n complicatedArgs {\n enumArgField(enumArg: $color)\n }\n }\n ",
"vars": {
"color": "BROWN"
},
"errors": []
},
{
"name": "Validate: Variables have valid type/invalid enum constant in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($color: FurColor) {\n complicatedArgs {\n enumArgField(enumArg: $color)\n }\n }\n ",
"vars": {
"color": "RAINBOW"
},
"errors": [
{
"message": "Variable \"color\" has invalid value RAINBOW.\nExpected type \"FurColor\", found RAINBOW.",
"locations": [
{
"line": 2,
"column": 19
}
]
}
]
},
{
"name": "Validate: Variables have valid type/optional enum constant variable null",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($color: FurColor) {\n complicatedArgs {\n enumArgField(enumArg: $color)\n }\n }\n ",
"vars": {
"color": null
},
"errors": []
},
{
"name": "Validate: Variables have valid type/list with single value in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($stringListArg: [String!])\n {\n stringListArgField(stringListArg: $stringListArg)\n }\n ",
"vars": {
"stringListArg": "single value"
},
"errors": []
},
{
"name": "Validate: Variables have valid type/list with list value in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($stringListArg: [String!])\n {\n stringListArgField(stringListArg: $stringListArg)\n }\n ",
"vars": {
"stringListArg": ["first value", "second value"]
},
"errors": []
},
{
"name": "Validate: Variables have valid type/input type with invalid input in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($complexVar: ComplexInput)\n {\n complicatedArgs {\n complexArgField(complexArg: $complexVar)\n }\n }\n ",
"vars": {
"complexVar": "not input"
},
"errors": [
{
"message": "Variable \"complexVar\" has invalid type string.\nExpected type \"ComplexInput\", found not input.",
"locations": [
{
"line": 2,
"column": 19
}
]
}
]
},
{
"name": "Validate: Variables have valid type/input type with invalid enum constant in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($complexVar: ComplexInput)\n {\n complicatedArgs {\n complexArgField(complexArg: $complexVar)\n }\n }\n ",
"vars": {
"complexVar": {
"requiredField": true,
"enumField": "RAINBOW"
}
},
"errors": [
{
"message": "Variable \"enumField\" has invalid value RAINBOW.\nExpected type \"FurColor\", found RAINBOW.",
"locations": [
{
"line": 73,
"column": 3
}
]
}
]
},
{
"name": "Validate: Variables have valid type/input type with optional enum constant variable null",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($complexVar: ComplexInput)\n {\n complicatedArgs {\n complexArgField(complexArg: $complexVar)\n }\n }\n ",
"vars": {
"complexVar": {
"requiredField": true,
"enumField": null
}
},
"errors": []
},
{
"name": "Validate: Variables have valid type/input type with nested input string from variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($complexVar: ComplexInput)\n {\n complicatedArgs {\n complexArgField(complexArg: $complexVar)\n }\n }\n ",
"vars": {
"complexVar": {
"requiredField": true,
"nestedInput": {
"stringField": "something"
}
}
},
"errors": []
},
{
"name": "Validate: Variables have valid type/input type with nested input string list with single value from variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($complexVar: ComplexInput)\n {\n complicatedArgs {\n complexArgField(complexArg: $complexVar)\n }\n }\n ",
"vars": {
"complexVar": {
"requiredField": true,
"nestedInput": {
"stringListField": "something"
}
}
},
"errors": []
},
{
"name": "Validate: Variables have valid type/input type with nested input string list from variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($complexVar: ComplexInput)\n {\n complicatedArgs {\n complexArgField(complexArg: $complexVar)\n }\n }\n ",
"vars": {
"complexVar": {
"requiredField": true,
"nestedInput": {
"stringListField": ["first", "second"]
}
}
},
"errors": []
},
{
"name": "Validate: Variables have valid type/number as enum",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($color: FurColor) {\n complicatedArgs {\n enumArgField(enumArg: $color)\n }\n }\n ",
"vars": {
"color": 42
},
"errors": [
{
"message": "Variable \"color\" has invalid type float64.\nExpected type \"FurColor\", found 42.",
"locations": [
{
"line": 2,
"column": 19
}
]
}
]
},
{
"name": "Validate: Variables have valid type/valid enum array constant in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($colors: [FurColor!]!) {\n complicatedArgs {\n enumArrayArgField(enumArrayArg: $colors)\n }\n }\n ",
"vars": {
"colors": ["BROWN", "BLACK", "SPOTTED"]
},
"errors": []
},
{
"name": "Validate: Variables have valid type/invalid enum array constant in variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($colors: [FurColor!]!) {\n complicatedArgs {\n enumArrayArgField(enumArrayArg: $colors)\n }\n }\n ",
"vars": {
"colors": ["TEAL", "AUBERGINE"]
},
"errors": [
{
"message": "Variable \"colors\" has invalid value TEAL.\nExpected type \"FurColor\", found TEAL.",
"locations": [
{
"line": 2,
"column": 19
}
]
},
{
"message": "Variable \"colors\" has invalid value AUBERGINE.\nExpected type \"FurColor\", found AUBERGINE.",
"locations": [
{
"line": 2,
"column": 19
}
]
}
]
},
{
"name": "Validate: Variables have valid type/string as enum array variable",
"rule": "VariablesOfCorrectType",
"schema": 0,
"query": "\n query Query($colors: [FurColor!]!) {\n complicatedArgs {\n enumArrayArgField(enumArrayArg: $colors)\n }\n }\n ",
"vars": {
"colors": "BROWN"
},
"errors": []
},
{
"name": "Validate: Overlapping fields can be merged/unique fields",
"rule": "OverlappingFieldsCanBeMerged",
Expand Down
Loading

0 comments on commit 87522cc

Please sign in to comment.