diff --git a/readme.md b/readme.md index 9f49ec78..ce756974 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@ gqlparser [![CircleCI](https://badgen.net/circleci/github/vektah/gqlparser/maste This is a parser for graphql, written to mirror the graphql-js reference implementation as closely while remaining idiomatic and easy to use. -spec target: June 2018 (Schema definition language, block strings as descriptions, error paths & extension) +spec target: [October 2021](https://spec.graphql.org/October2021/) and [select portions of the Draft](https://spec.graphql.org/draft/), based on the graphql-js reference implementation [graphql-js v16.10.0](https://github.com/graphql/graphql-js/releases/tag/v16.10.0). This includes Schema definition language, block strings as descriptions, error paths & extension, etc. If there is a spec update or [new release](https://github.com/graphql/graphql-spec/releases), please follow [this process to update](./validator/imported/readme.md) and submit a PR. This parser is used by [gqlgen](https://github.com/99designs/gqlgen), and it should be reasonably stable. diff --git a/validator/imported/.npmrc b/validator/imported/.npmrc new file mode 100644 index 00000000..b74bb011 --- /dev/null +++ b/validator/imported/.npmrc @@ -0,0 +1,5 @@ +# .npmrc +# engine-strict=true is so npm will stubbornly refuse +# to install (or even consider installing) any package +# that claims to not be compatible with the current Node.js version. +engine-strict=true diff --git a/validator/imported/export.js b/validator/imported/export.js index 234cc10d..3786678b 100644 --- a/validator/imported/export.js +++ b/validator/imported/export.js @@ -1,7 +1,7 @@ import fs from "fs"; import Module from "module"; import { testSchema } from "./graphql-js/src/validation/__tests__/harness"; -import { printSchema } from "./graphql-js/src/utilities"; +import { printSchema } from "./graphql-js/src"; import yaml from "js-yaml"; let schemas = []; diff --git a/validator/imported/export.sh b/validator/imported/export.sh index a4220038..40b80a52 100755 --- a/validator/imported/export.sh +++ b/validator/imported/export.sh @@ -10,12 +10,12 @@ GIT_REF=origin/main if [[ -f "$EXPORTER_ROOT/graphql-js-commit.log" ]] ; then GIT_REF=$(cat "$EXPORTER_ROOT/graphql-js-commit.log") fi -echo $GIT_REF +echo "$GIT_REF" if [[ -d "$REPO_DIR" ]] ; then echo "fetching graphql-js with ${GIT_REF}" cd "$REPO_DIR" || exit - git fetch origin master + git fetch origin main git checkout "$GIT_REF" git reset --hard else @@ -24,7 +24,7 @@ else cd "$REPO_DIR" || exit git checkout "$GIT_REF" fi -git rev-parse HEAD > $EXPORTER_ROOT/graphql-js-commit.log +git rev-parse HEAD > "$EXPORTER_ROOT/graphql-js-commit.log" cd "$EXPORTER_ROOT" || exit diff --git a/validator/imported/graphql-js-commit.log b/validator/imported/graphql-js-commit.log index 293640ad..58d20686 100644 --- a/validator/imported/graphql-js-commit.log +++ b/validator/imported/graphql-js-commit.log @@ -1 +1 @@ -f597c694339b7c488f05496806e404659f6ff955 +48afd37a48d37f6d3342b959e8cb34e1ecbfeffb diff --git a/validator/imported/package.json b/validator/imported/package.json index b7ae7f6f..ef866ed6 100644 --- a/validator/imported/package.json +++ b/validator/imported/package.json @@ -15,5 +15,8 @@ }, "devDependencies": { "prettier": "^3.4.2" + }, + "engines": { + "node": ">= 22" } } diff --git a/validator/imported/readme.md b/validator/imported/readme.md index f403dfc0..f7bdba32 100644 --- a/validator/imported/readme.md +++ b/validator/imported/readme.md @@ -2,7 +2,7 @@ These specs have been generated from the testsuite in [graphql/graph-js](https://github.com/graphql/graphql-js). -Direct modifications should not be made, instead take a look at the exporter. +Direct modifications should not be made to most of this directory, instead take a look at the exporter. ```shell script # update to latest @@ -11,3 +11,7 @@ $ rm graphql-js-commit.log && ./export.sh # re-generate with known revision $ ./export.sh ``` + +You will then need to manually update the [`validator/prelude.graphql`](./validator/prelude.graphql) file, [`validator/schema.go`](./validator/schema.go) file, and possibly other files relevant to those specific changes like [./validator/rules/](./validator/rules/), etc. + +Please in your PR description note the git release tag that corresponds to the graphql-js commit. diff --git a/validator/imported/spec/ExecutableDefinitionsRule.spec.yml b/validator/imported/spec/ExecutableDefinitionsRule.spec.yml index 8286c964..0dd9d18b 100644 --- a/validator/imported/spec/ExecutableDefinitionsRule.spec.yml +++ b/validator/imported/spec/ExecutableDefinitionsRule.spec.yml @@ -8,7 +8,7 @@ name } } - + errors: [] - name: with operation and fragment rule: ExecutableDefinitions @@ -25,7 +25,7 @@ fragment Frag on Dog { name } - + errors: [] - name: with type definition rule: ExecutableDefinitions @@ -45,7 +45,7 @@ extend type Dog { color: String } - + errors: - message: The "Cow" definition is not executable. locations: @@ -67,7 +67,7 @@ } extend schema @directive - + errors: - message: The schema definition is not executable. locations: diff --git a/validator/imported/spec/FieldsOnCorrectTypeRule.spec.yml b/validator/imported/spec/FieldsOnCorrectTypeRule.spec.yml index a582cb77..30231052 100644 --- a/validator/imported/spec/FieldsOnCorrectTypeRule.spec.yml +++ b/validator/imported/spec/FieldsOnCorrectTypeRule.spec.yml @@ -7,7 +7,7 @@ __typename name } - + errors: [] - name: Aliased object field selection rule: FieldsOnCorrectType @@ -18,7 +18,7 @@ tn : __typename otherName : name } - + errors: [] - name: Interface field selection rule: FieldsOnCorrectType @@ -29,7 +29,7 @@ __typename name } - + errors: [] - name: Aliased interface field selection rule: FieldsOnCorrectType @@ -39,7 +39,7 @@ fragment interfaceFieldSelection on Pet { otherName : name } - + errors: [] - name: Lying alias selection rule: FieldsOnCorrectType @@ -49,7 +49,7 @@ fragment lyingAliasSelection on Dog { name : nickname } - + errors: [] - name: Ignores fields on unknown type rule: FieldsOnCorrectType @@ -59,7 +59,7 @@ fragment unknownSelection on UnknownType { unknownField } - + errors: [] - name: reports errors when type is known again rule: FieldsOnCorrectType @@ -73,7 +73,7 @@ } } } - + errors: - message: Cannot query field "unknown_pet_field" on type "Pet". locations: @@ -89,7 +89,7 @@ fragment fieldNotDefined on Dog { meowVolume } - + errors: - message: Cannot query field "meowVolume" on type "Dog". Did you mean "barkVolume"? locations: @@ -104,7 +104,7 @@ deeper_unknown_field } } - + errors: - message: Cannot query field "unknown_field" on type "Dog". locations: @@ -119,7 +119,7 @@ unknown_field } } - + errors: - message: Cannot query field "unknown_field" on type "Pet". locations: @@ -134,7 +134,7 @@ meowVolume } } - + errors: - message: Cannot query field "meowVolume" on type "Dog". Did you mean "barkVolume"? locations: @@ -147,7 +147,7 @@ fragment aliasedFieldTargetNotDefined on Dog { volume : mooVolume } - + errors: - message: Cannot query field "mooVolume" on type "Dog". Did you mean "barkVolume"? locations: @@ -160,7 +160,7 @@ fragment aliasedLyingFieldTargetNotDefined on Dog { barkVolume : kawVolume } - + errors: - message: Cannot query field "kawVolume" on type "Dog". Did you mean "barkVolume"? locations: @@ -173,7 +173,7 @@ fragment notDefinedOnInterface on Pet { tailLength } - + errors: - message: Cannot query field "tailLength" on type "Pet". locations: @@ -186,7 +186,7 @@ fragment definedOnImplementorsButNotInterface on Pet { nickname } - + errors: - message: Cannot query field "nickname" on type "Pet". Did you mean to use an inline fragment on "Cat" or "Dog"? locations: @@ -199,7 +199,7 @@ fragment directFieldSelectionOnUnion on CatOrDog { __typename } - + errors: [] - name: Direct field selection on union rule: FieldsOnCorrectType @@ -209,7 +209,7 @@ fragment directFieldSelectionOnUnion on CatOrDog { directField } - + errors: - message: Cannot query field "directField" on type "CatOrDog". locations: @@ -222,7 +222,7 @@ fragment definedOnImplementorsQueriedOnUnion on CatOrDog { name } - + errors: - message: Cannot query field "name" on type "CatOrDog". Did you mean to use an inline fragment on "Pet", "Cat", or "Dog"? locations: @@ -240,5 +240,5 @@ name } } - + errors: [] diff --git a/validator/imported/spec/FragmentsOnCompositeTypesRule.spec.yml b/validator/imported/spec/FragmentsOnCompositeTypesRule.spec.yml index 3d9e7a89..7d84e6f7 100644 --- a/validator/imported/spec/FragmentsOnCompositeTypesRule.spec.yml +++ b/validator/imported/spec/FragmentsOnCompositeTypesRule.spec.yml @@ -6,7 +6,7 @@ fragment validFragment on Dog { barks } - + errors: [] - name: interface is valid fragment type rule: FragmentsOnCompositeTypes @@ -16,7 +16,7 @@ fragment validFragment on Pet { name } - + errors: [] - name: object is valid inline fragment type rule: FragmentsOnCompositeTypes @@ -28,7 +28,7 @@ barks } } - + errors: [] - name: interface is valid inline fragment type rule: FragmentsOnCompositeTypes @@ -40,7 +40,7 @@ name } } - + errors: [] - name: inline fragment without type is valid rule: FragmentsOnCompositeTypes @@ -52,7 +52,7 @@ name } } - + errors: [] - name: union is valid fragment type rule: FragmentsOnCompositeTypes @@ -62,7 +62,7 @@ fragment validFragment on CatOrDog { __typename } - + errors: [] - name: scalar is invalid fragment type rule: FragmentsOnCompositeTypes @@ -72,7 +72,7 @@ fragment scalarFragment on Boolean { bad } - + errors: - message: Fragment "scalarFragment" cannot condition on non composite type "Boolean". locations: @@ -85,7 +85,7 @@ fragment scalarFragment on FurColor { bad } - + errors: - message: Fragment "scalarFragment" cannot condition on non composite type "FurColor". locations: @@ -98,7 +98,7 @@ fragment inputFragment on ComplexInput { stringField } - + errors: - message: Fragment "inputFragment" cannot condition on non composite type "ComplexInput". locations: @@ -113,7 +113,7 @@ barks } } - + errors: - message: Fragment cannot condition on non composite type "String". locations: diff --git a/validator/imported/spec/KnownArgumentNamesRule.spec.yml b/validator/imported/spec/KnownArgumentNamesRule.spec.yml index 03ca5158..bafbe556 100644 --- a/validator/imported/spec/KnownArgumentNamesRule.spec.yml +++ b/validator/imported/spec/KnownArgumentNamesRule.spec.yml @@ -6,7 +6,7 @@ fragment argOnRequiredArg on Dog { doesKnowCommand(dogCommand: SIT) } - + errors: [] - name: multiple args are known rule: KnownArgumentNames @@ -16,7 +16,7 @@ fragment multipleArgs on ComplicatedArgs { multipleReqs(req1: 1, req2: 2) } - + errors: [] - name: ignores args of unknown fields rule: KnownArgumentNames @@ -26,7 +26,7 @@ fragment argOnUnknownField on Dog { unknownField(unknownArg: SIT) } - + errors: [] - name: multiple args in reverse order are known rule: KnownArgumentNames @@ -36,7 +36,7 @@ fragment multipleArgsReverseOrder on ComplicatedArgs { multipleReqs(req2: 2, req1: 1) } - + errors: [] - name: no args on optional arg rule: KnownArgumentNames @@ -46,7 +46,7 @@ fragment noArgOnOptionalArg on Dog { isHouseTrained } - + errors: [] - name: args are known deeply rule: KnownArgumentNames @@ -65,7 +65,7 @@ } } } - + errors: [] - name: directive args are known rule: KnownArgumentNames @@ -75,7 +75,7 @@ { dog @skip(if: true) } - + errors: [] - name: field args are invalid rule: KnownArgumentNames @@ -85,7 +85,7 @@ { dog @skip(unless: true) } - + errors: - message: Unknown argument "unless" on directive "@skip". locations: @@ -98,7 +98,7 @@ { dog @onField } - + errors: [] - name: arg passed to directive without arg is reported rule: KnownArgumentNames @@ -108,7 +108,7 @@ { dog @onField(if: true) } - + errors: - message: Unknown argument "if" on directive "@onField". locations: @@ -121,7 +121,7 @@ { dog @skip(iff: true) } - + errors: - message: Unknown argument "iff" on directive "@skip". Did you mean "if"? locations: @@ -134,7 +134,7 @@ fragment invalidArgName on Dog { doesKnowCommand(unknown: true) } - + errors: - message: Unknown argument "unknown" on field "Dog.doesKnowCommand". locations: @@ -147,7 +147,7 @@ fragment invalidArgName on Dog { doesKnowCommand(DogCommand: true) } - + errors: - message: Unknown argument "DogCommand" on field "Dog.doesKnowCommand". Did you mean "dogCommand"? locations: @@ -160,7 +160,7 @@ fragment oneGoodArgOneInvalidArg on Dog { doesKnowCommand(whoKnows: 1, dogCommand: SIT, unknown: true) } - + errors: - message: Unknown argument "whoKnows" on field "Dog.doesKnowCommand". locations: @@ -185,7 +185,7 @@ } } } - + errors: - message: Unknown argument "unknown" on field "Dog.doesKnowCommand". locations: diff --git a/validator/imported/spec/KnownDirectivesRule.spec.yml b/validator/imported/spec/KnownDirectivesRule.spec.yml index 36fc9a5b..61b3f177 100644 --- a/validator/imported/spec/KnownDirectivesRule.spec.yml +++ b/validator/imported/spec/KnownDirectivesRule.spec.yml @@ -11,7 +11,7 @@ fragment Frag on Dog { name } - + errors: [] - name: with standard directives rule: KnownDirectives @@ -28,7 +28,7 @@ } } } - + errors: [] - name: with unknown directive rule: KnownDirectives @@ -40,7 +40,7 @@ name } } - + errors: - message: Unknown directive "@unknown". locations: @@ -59,7 +59,7 @@ } } } - + errors: - message: Unknown directive "@unknown". locations: @@ -95,7 +95,7 @@ fragment Frag on Human @onFragmentDefinition { name @onField } - + errors: [] - name: with misplaced directives rule: KnownDirectives @@ -122,7 +122,7 @@ fragment Frag on Human @onQuery { name @onQuery } - + errors: - message: Directive "@onQuery" may not be used on VARIABLE_DEFINITION. locations: diff --git a/validator/imported/spec/KnownFragmentNamesRule.spec.yml b/validator/imported/spec/KnownFragmentNamesRule.spec.yml index ba997f49..812019a3 100644 --- a/validator/imported/spec/KnownFragmentNamesRule.spec.yml +++ b/validator/imported/spec/KnownFragmentNamesRule.spec.yml @@ -24,7 +24,7 @@ fragment HumanFields3 on Human { name } - + errors: [] - name: unknown fragment names are invalid rule: KnownFragmentNames @@ -43,7 +43,7 @@ name ...UnknownFragment3 } - + errors: - message: Unknown fragment "UnknownFragment1". locations: diff --git a/validator/imported/spec/KnownTypeNamesRule.spec.yml b/validator/imported/spec/KnownTypeNamesRule.spec.yml index 2007840e..02cf57b3 100644 --- a/validator/imported/spec/KnownTypeNamesRule.spec.yml +++ b/validator/imported/spec/KnownTypeNamesRule.spec.yml @@ -16,7 +16,7 @@ fragment PetFields on Pet { name } - + errors: [] - name: unknown type names are invalid rule: KnownTypeNames @@ -32,7 +32,7 @@ fragment PetFields on Peat { name } - + errors: - message: Unknown type "JumbledUpLetters". locations: @@ -51,7 +51,7 @@ query ($id: ID, $float: Float, $int: Int) { __typename } - + errors: - message: Unknown type "ID". locations: diff --git a/validator/imported/spec/LoneAnonymousOperationRule.spec.yml b/validator/imported/spec/LoneAnonymousOperationRule.spec.yml index 0950467b..80f382a1 100644 --- a/validator/imported/spec/LoneAnonymousOperationRule.spec.yml +++ b/validator/imported/spec/LoneAnonymousOperationRule.spec.yml @@ -6,7 +6,7 @@ fragment fragA on Type { field } - + errors: [] - name: one anon operation rule: LoneAnonymousOperation @@ -16,7 +16,7 @@ { field } - + errors: [] - name: multiple named operations rule: LoneAnonymousOperation @@ -30,7 +30,7 @@ query Bar { field } - + errors: [] - name: anon operation with fragment rule: LoneAnonymousOperation @@ -43,7 +43,7 @@ fragment Foo on Type { field } - + errors: [] - name: multiple anon operations rule: LoneAnonymousOperation @@ -56,7 +56,7 @@ { fieldB } - + errors: - message: This anonymous operation must be the only defined operation. locations: @@ -75,7 +75,7 @@ mutation Foo { fieldB } - + errors: - message: This anonymous operation must be the only defined operation. locations: @@ -91,7 +91,7 @@ subscription Foo { fieldB } - + errors: - message: This anonymous operation must be the only defined operation. locations: diff --git a/validator/imported/spec/MaxIntrospectionDepthRule.spec.yml b/validator/imported/spec/MaxIntrospectionDepthRule.spec.yml new file mode 100644 index 00000000..ff2e115c --- /dev/null +++ b/validator/imported/spec/MaxIntrospectionDepthRule.spec.yml @@ -0,0 +1,674 @@ +- name: default introspection query + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + query IntrospectionQuery { + __schema { + + queryType { name kind } + mutationType { name kind } + subscriptionType { name kind } + types { + ...FullType + } + directives { + name + description + + locations + args { + ...InputValue + } + } + } + } + + fragment FullType on __Type { + kind + name + description + + + fields(includeDeprecated: true) { + name + description + args { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } + } + + fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue + + + } + + fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } + } + } + + errors: [] +- name: all options introspection query + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + query IntrospectionQuery { + __schema { + description + queryType { name kind } + mutationType { name kind } + subscriptionType { name kind } + types { + ...FullType + } + directives { + name + description + isRepeatable + locations + args(includeDeprecated: true) { + ...InputValue + } + } + } + } + + fragment FullType on __Type { + kind + name + description + specifiedByURL + + fields(includeDeprecated: true) { + name + description + args(includeDeprecated: true) { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields(includeDeprecated: true) { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } + } + + fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue + isDeprecated + deprecationReason + } + + fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } + } + } + + errors: [] +- name: 3 flat fields introspection query + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __type(name: "Query") { + trueFields: fields(includeDeprecated: true) { + name + } + falseFields: fields(includeDeprecated: false) { + name + } + omittedFields: fields { + name + } + } + } + + errors: [] +- name: 3 fields deep introspection query from __schema + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 interfaces deep introspection query from __schema + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __schema { + types { + interfaces { + interfaces { + interfaces { + name + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 possibleTypes deep introspection query from __schema + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __schema { + types { + possibleTypes { + possibleTypes { + possibleTypes { + name + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 inputFields deep introspection query from __schema + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __schema { + types { + inputFields { + type { + inputFields { + type { + inputFields { + type { + name + } + } + } + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 fields deep introspection query from multiple __schema + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + one: __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + two: __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + three: __schema { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} + - locations: + - {column: 7, line: 18} + message: Maximum introspection depth exceeded + - locations: + - {column: 7, line: 33} + message: Maximum introspection depth exceeded +- name: 3 fields deep introspection query from __type + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 fields deep introspection query from multiple __type + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + one: __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + two: __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + three: __type(name: "Query") { + types { + fields { + type { + fields { + type { + fields { + name + } + } + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} + - locations: + - {column: 7, line: 18} + message: Maximum introspection depth exceeded + - locations: + - {column: 7, line: 33} + message: Maximum introspection depth exceeded +- name: 1 fields deep with 3 fields introspection query + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __schema { + types { + fields { + type { + oneFields: fields { + name + } + twoFields: fields { + name + } + threeFields: fields { + name + } + } + } + } + } + } + + errors: [] +- name: 3 fields deep from varying parents introspection query + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + __schema { + types { + fields { + type { + fields { + type { + ofType { + fields { + name + } + } + } + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 fields deep introspection query with inline fragments + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + query test { + __schema { + types { + ... on __Type { + fields { + type { + ... on __Type { + ofType { + fields { + type { + ... on __Type { + fields { + name + } + } + } + } + } + } + } + } + } + } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 fields deep introspection query with fragments + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + query test { + __schema { + types { + ...One + } + } + } + + fragment One on __Type { + fields { + type { + ...Two + } + } + } + + fragment Two on __Type { + fields { + type { + ...Three + } + } + } + + fragment Three on __Type { + fields { + name + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 7, line: 3} +- name: 3 fields deep inside inline fragment on query + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + { + ... { + __schema { types { fields { type { fields { type { fields { name } } } } } } } + } + } + + errors: + - message: Maximum introspection depth exceeded + locations: + - {column: 9, line: 4} +- name: opts out if fragment is missing + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + query test { + __schema { + types { + ...Missing + } + } + } + + errors: [] +- name: doesn't infinitely recurse on fragment cycle + rule: MaxIntrospectionDepth + schema: 0 + query: |2- + + query test { + __schema { + types { + ...Cycle + } + } + } + fragment Cycle on __Type { + ...Cycle + } + + errors: [] diff --git a/validator/imported/spec/NoDeprecatedCustomRule.spec.yml b/validator/imported/spec/NoDeprecatedCustomRule.spec.yml index ff8df4ac..8576a580 100644 --- a/validator/imported/spec/NoDeprecatedCustomRule.spec.yml +++ b/validator/imported/spec/NoDeprecatedCustomRule.spec.yml @@ -6,7 +6,7 @@ { normalField } - + errors: [] - name: no deprecated fields/ignores unknown fields rule: NoDeprecatedCustom @@ -20,7 +20,7 @@ fragment UnknownFragment on UnknownType { deprecatedField } - + errors: [] - name: no deprecated fields/reports error when a deprecated field is selected rule: NoDeprecatedCustom @@ -34,7 +34,7 @@ fragment QueryFragment on Query { deprecatedField } - + errors: - message: The field Query.deprecatedField is deprecated. Some field reason. locations: @@ -50,7 +50,7 @@ { normalField(normalArg: "") } - + errors: [] - name: no deprecated arguments on fields/ignores unknown arguments rule: NoDeprecatedCustom @@ -61,7 +61,7 @@ someField(unknownArg: "") unknownField(deprecatedArg: "") } - + errors: [] - name: no deprecated arguments on fields/reports error when a deprecated argument is used rule: NoDeprecatedCustom @@ -71,7 +71,7 @@ { someField(deprecatedArg: "") } - + errors: - message: Field "Query.someField" argument "deprecatedArg" is deprecated. Some arg reason. locations: @@ -84,7 +84,7 @@ { someField @someDirective(normalArg: "") } - + errors: [] - name: no deprecated arguments on directives/ignores unknown arguments rule: NoDeprecatedCustom @@ -95,7 +95,7 @@ someField @someDirective(unknownArg: "") someField @unknownDirective(deprecatedArg: "") } - + errors: [] - name: no deprecated arguments on directives/reports error when a deprecated argument is used rule: NoDeprecatedCustom @@ -105,7 +105,7 @@ { someField @someDirective(deprecatedArg: "") } - + errors: - message: Directive "@someDirective" argument "deprecatedArg" is deprecated. Some arg reason. locations: @@ -120,7 +120,7 @@ someArg: { normalField: "" } ) @someDirective(someArg: { normalField: "" }) } - + errors: [] - name: no deprecated input fields/ignores unknown input fields rule: NoDeprecatedCustom @@ -140,7 +140,7 @@ unknownArg: { unknownField: "" } ) } - + errors: [] - name: no deprecated input fields/reports error when a deprecated input field is used rule: NoDeprecatedCustom @@ -152,7 +152,7 @@ someArg: { deprecatedField: "" } ) @someDirective(someArg: { deprecatedField: "" }) } - + errors: - message: The input field InputType.deprecatedField is deprecated. Some input field reason. locations: @@ -168,7 +168,7 @@ { normalField(enumArg: NORMAL_VALUE) } - + errors: [] - name: no deprecated enum values/ignores unknown enum values rule: NoDeprecatedCustom @@ -187,7 +187,7 @@ fragment SomeFragment on Query { someField(enumArg: UNKNOWN_VALUE) } - + errors: [] - name: no deprecated enum values/reports error when a deprecated enum value is used rule: NoDeprecatedCustom @@ -199,7 +199,7 @@ ) { someField(enumArg: DEPRECATED_VALUE) } - + errors: - message: The enum value "EnumType.DEPRECATED_VALUE" is deprecated. Some enum reason. locations: diff --git a/validator/imported/spec/NoFragmentCyclesRule.spec.yml b/validator/imported/spec/NoFragmentCyclesRule.spec.yml index b2bbdb8a..c47b0a09 100644 --- a/validator/imported/spec/NoFragmentCyclesRule.spec.yml +++ b/validator/imported/spec/NoFragmentCyclesRule.spec.yml @@ -5,7 +5,7 @@ fragment fragA on Dog { ...fragB } fragment fragB on Dog { name } - + errors: [] - name: spreading twice is not circular rule: NoFragmentCycles @@ -14,7 +14,7 @@ fragment fragA on Dog { ...fragB, ...fragB } fragment fragB on Dog { name } - + errors: [] - name: spreading twice indirectly is not circular rule: NoFragmentCycles @@ -24,7 +24,7 @@ fragment fragA on Dog { ...fragB, ...fragC } fragment fragB on Dog { ...fragC } fragment fragC on Dog { name } - + errors: [] - name: double spread within abstract types rule: NoFragmentCycles @@ -40,7 +40,7 @@ ... on Dog { ...nameFragment } ... on Cat { ...nameFragment } } - + errors: [] - name: does not false positive on unknown fragment rule: NoFragmentCycles @@ -50,7 +50,7 @@ fragment nameFragment on Pet { ...UnknownFragment } - + errors: [] - name: spreading recursively within field fails rule: NoFragmentCycles @@ -58,7 +58,7 @@ query: |2- fragment fragA on Human { relatives { ...fragA } }, - + errors: - message: Cannot spread fragment "fragA" within itself. locations: @@ -69,7 +69,7 @@ query: |2- fragment fragA on Dog { ...fragA } - + errors: - message: Cannot spread fragment "fragA" within itself. locations: @@ -84,7 +84,7 @@ ...fragA } } - + errors: - message: Cannot spread fragment "fragA" within itself. locations: @@ -96,7 +96,7 @@ fragment fragA on Dog { ...fragB } fragment fragB on Dog { ...fragA } - + errors: - message: Cannot spread fragment "fragA" within itself via "fragB". locations: @@ -109,7 +109,7 @@ fragment fragB on Dog { ...fragA } fragment fragA on Dog { ...fragB } - + errors: - message: Cannot spread fragment "fragB" within itself via "fragA". locations: @@ -130,7 +130,7 @@ ...fragA } } - + errors: - message: Cannot spread fragment "fragA" within itself via "fragB". locations: @@ -149,7 +149,7 @@ fragment fragZ on Dog { ...fragO } fragment fragO on Dog { ...fragP } fragment fragP on Dog { ...fragA, ...fragX } - + errors: - message: Cannot spread fragment "fragA" within itself via "fragB", "fragC", "fragO", "fragP". locations: @@ -173,7 +173,7 @@ fragment fragA on Dog { ...fragB, ...fragC } fragment fragB on Dog { ...fragA } fragment fragC on Dog { ...fragA } - + errors: - message: Cannot spread fragment "fragA" within itself via "fragB". locations: @@ -191,7 +191,7 @@ fragment fragA on Dog { ...fragC } fragment fragB on Dog { ...fragC } fragment fragC on Dog { ...fragA, ...fragB } - + errors: - message: Cannot spread fragment "fragA" within itself via "fragC". locations: @@ -209,7 +209,7 @@ fragment fragA on Dog { ...fragB } fragment fragB on Dog { ...fragB, ...fragC } fragment fragC on Dog { ...fragA, ...fragB } - + errors: - message: Cannot spread fragment "fragB" within itself. locations: diff --git a/validator/imported/spec/NoSchemaIntrospectionCustomRule.spec.yml b/validator/imported/spec/NoSchemaIntrospectionCustomRule.spec.yml index 12ca2f27..d289c5fb 100644 --- a/validator/imported/spec/NoSchemaIntrospectionCustomRule.spec.yml +++ b/validator/imported/spec/NoSchemaIntrospectionCustomRule.spec.yml @@ -9,7 +9,7 @@ someField } } - + errors: [] - name: ignores fields not in the schema rule: NoSchemaIntrospectionCustom @@ -19,7 +19,7 @@ { __introspect } - + errors: [] - name: reports error when a field with an introspection type is requested rule: NoSchemaIntrospectionCustom @@ -33,7 +33,7 @@ } } } - + errors: - message: GraphQL introspection has been disabled, but the requested query contained the field "__schema". locations: @@ -53,7 +53,7 @@ } } } - + errors: - message: GraphQL introspection has been disabled, but the requested query contained the field "__schema". locations: @@ -77,7 +77,7 @@ } } } - + errors: - message: GraphQL introspection has been disabled, but the requested query contained the field "__schema". locations: @@ -95,7 +95,7 @@ introspectionField } } - + errors: - message: GraphQL introspection has been disabled, but the requested query contained the field "introspectionField". locations: diff --git a/validator/imported/spec/NoUndefinedVariablesRule.spec.yml b/validator/imported/spec/NoUndefinedVariablesRule.spec.yml index 84e4c2f7..f96d7df5 100644 --- a/validator/imported/spec/NoUndefinedVariablesRule.spec.yml +++ b/validator/imported/spec/NoUndefinedVariablesRule.spec.yml @@ -6,7 +6,7 @@ query Foo($a: String, $b: String, $c: String) { field(a: $a, b: $b, c: $c) } - + errors: [] - name: all variables deeply defined rule: NoUndefinedVariables @@ -20,7 +20,7 @@ } } } - + errors: [] - name: all variables deeply in inline fragments defined rule: NoUndefinedVariables @@ -38,7 +38,7 @@ } } } - + errors: [] - name: all variables in fragments deeply defined rule: NoUndefinedVariables @@ -61,7 +61,7 @@ fragment FragC on Type { field(c: $c) } - + errors: [] - name: variable within single fragment defined in multiple operations rule: NoUndefinedVariables @@ -77,7 +77,7 @@ fragment FragA on Type { field(a: $a) } - + errors: [] - name: variable within fragments defined in operations rule: NoUndefinedVariables @@ -96,7 +96,7 @@ fragment FragB on Type { field(b: $b) } - + errors: [] - name: variable within recursive fragment defined rule: NoUndefinedVariables @@ -111,7 +111,7 @@ ...FragA } } - + errors: [] - name: variable not defined rule: NoUndefinedVariables @@ -121,7 +121,7 @@ query Foo($a: String, $b: String, $c: String) { field(a: $a, b: $b, c: $c, d: $d) } - + errors: - message: Variable "$d" is not defined by operation "Foo". locations: @@ -135,7 +135,7 @@ { field(a: $a) } - + errors: - message: Variable "$a" is not defined. locations: @@ -149,7 +149,7 @@ query Foo($b: String) { field(a: $a, b: $b, c: $c) } - + errors: - message: Variable "$a" is not defined by operation "Foo". locations: @@ -170,7 +170,7 @@ fragment FragA on Type { field(a: $a) } - + errors: - message: Variable "$a" is not defined. locations: @@ -197,7 +197,7 @@ fragment FragC on Type { field(c: $c) } - + errors: - message: Variable "$c" is not defined by operation "Foo". locations: @@ -224,7 +224,7 @@ fragment FragC on Type { field(c: $c) } - + errors: - message: Variable "$a" is not defined by operation "Foo". locations: @@ -248,7 +248,7 @@ fragment FragAB on Type { field(a: $a, b: $b) } - + errors: - message: Variable "$b" is not defined by operation "Foo". locations: @@ -272,7 +272,7 @@ fragment FragAB on Type { field(a: $a, b: $b) } - + errors: - message: Variable "$a" is not defined by operation "Foo". locations: @@ -299,7 +299,7 @@ fragment FragB on Type { field(b: $b) } - + errors: - message: Variable "$a" is not defined by operation "Foo". locations: @@ -328,7 +328,7 @@ fragment FragC on Type { field2(c: $c) } - + errors: - message: Variable "$a" is not defined by operation "Foo". locations: diff --git a/validator/imported/spec/NoUnusedFragmentsRule.spec.yml b/validator/imported/spec/NoUnusedFragmentsRule.spec.yml index 42f14df0..04cc0a11 100644 --- a/validator/imported/spec/NoUnusedFragmentsRule.spec.yml +++ b/validator/imported/spec/NoUnusedFragmentsRule.spec.yml @@ -21,7 +21,7 @@ fragment HumanFields3 on Human { name } - + errors: [] - name: all fragment names are used by multiple operations rule: NoUnusedFragments @@ -48,7 +48,7 @@ fragment HumanFields3 on Human { name } - + errors: [] - name: contains unknown fragments rule: NoUnusedFragments @@ -81,7 +81,7 @@ fragment Unused2 on Human { name } - + errors: - message: Fragment "Unused1" is never used. locations: @@ -122,7 +122,7 @@ name ...Unused1 } - + errors: - message: Fragment "Unused1" is never used. locations: @@ -143,7 +143,7 @@ fragment foo on Human { name } - + errors: - message: Fragment "foo" is never used. locations: diff --git a/validator/imported/spec/NoUnusedVariablesRule.spec.yml b/validator/imported/spec/NoUnusedVariablesRule.spec.yml index 515a9ffa..1fb24132 100644 --- a/validator/imported/spec/NoUnusedVariablesRule.spec.yml +++ b/validator/imported/spec/NoUnusedVariablesRule.spec.yml @@ -6,7 +6,7 @@ query ($a: String, $b: String, $c: String) { field(a: $a, b: $b, c: $c) } - + errors: [] - name: uses all variables deeply rule: NoUnusedVariables @@ -20,7 +20,7 @@ } } } - + errors: [] - name: uses all variables deeply in inline fragments rule: NoUnusedVariables @@ -38,7 +38,7 @@ } } } - + errors: [] - name: uses all variables in fragments rule: NoUnusedVariables @@ -61,7 +61,7 @@ fragment FragC on Type { field(c: $c) } - + errors: [] - name: variable used by fragment in multiple operations rule: NoUnusedVariables @@ -80,7 +80,7 @@ fragment FragB on Type { field(b: $b) } - + errors: [] - name: variable used by recursive fragment rule: NoUnusedVariables @@ -95,7 +95,7 @@ ...FragA } } - + errors: [] - name: variable not used rule: NoUnusedVariables @@ -105,7 +105,7 @@ query ($a: String, $b: String, $c: String) { field(a: $a, b: $b) } - + errors: - message: Variable "$c" is never used. locations: @@ -118,7 +118,7 @@ query Foo($a: String, $b: String, $c: String) { field(b: $b) } - + errors: - message: Variable "$a" is never used in operation "Foo". locations: @@ -147,7 +147,7 @@ fragment FragC on Type { field } - + errors: - message: Variable "$c" is never used in operation "Foo". locations: @@ -173,7 +173,7 @@ fragment FragC on Type { field } - + errors: - message: Variable "$a" is never used in operation "Foo". locations: @@ -195,7 +195,7 @@ fragment FragB on Type { field(b: $b) } - + errors: - message: Variable "$b" is never used in operation "Foo". locations: @@ -217,7 +217,7 @@ fragment FragB on Type { field(b: $b) } - + errors: - message: Variable "$b" is never used in operation "Foo". locations: diff --git a/validator/imported/spec/OverlappingFieldsCanBeMergedRule.spec.yml b/validator/imported/spec/OverlappingFieldsCanBeMergedRule.spec.yml index 895e4020..1a979720 100644 --- a/validator/imported/spec/OverlappingFieldsCanBeMergedRule.spec.yml +++ b/validator/imported/spec/OverlappingFieldsCanBeMergedRule.spec.yml @@ -7,7 +7,7 @@ name nickname } - + errors: [] - name: identical fields rule: OverlappingFieldsCanBeMerged @@ -18,7 +18,7 @@ name name } - + errors: [] - name: identical fields with identical args rule: OverlappingFieldsCanBeMerged @@ -29,7 +29,7 @@ doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: SIT) } - + errors: [] - name: identical fields with identical directives rule: OverlappingFieldsCanBeMerged @@ -40,7 +40,7 @@ name @include(if: true) name @include(if: true) } - + errors: [] - name: different args with different aliases rule: OverlappingFieldsCanBeMerged @@ -51,7 +51,7 @@ knowsSit: doesKnowCommand(dogCommand: SIT) knowsDown: doesKnowCommand(dogCommand: DOWN) } - + errors: [] - name: different directives with different aliases rule: OverlappingFieldsCanBeMerged @@ -62,7 +62,7 @@ nameIfTrue: name @include(if: true) nameIfFalse: name @include(if: false) } - + errors: [] - name: different skip/include directives accepted rule: OverlappingFieldsCanBeMerged @@ -73,7 +73,7 @@ name @include(if: true) name @include(if: false) } - + errors: [] - name: Same aliases with different field targets rule: OverlappingFieldsCanBeMerged @@ -84,7 +84,7 @@ fido: name fido: nickname } - + errors: - message: Fields "fido" conflict because "name" and "nickname" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -103,7 +103,7 @@ name: nickname } } - + errors: [] - name: Alias masking direct field access rule: OverlappingFieldsCanBeMerged @@ -114,7 +114,7 @@ name: nickname name } - + errors: - message: Fields "name" conflict because "nickname" and "name" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -129,7 +129,7 @@ doesKnowCommand doesKnowCommand(dogCommand: HEEL) } - + errors: - message: Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -144,7 +144,7 @@ doesKnowCommand(dogCommand: SIT) doesKnowCommand } - + errors: - message: Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -159,7 +159,7 @@ doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: HEEL) } - + errors: - message: Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -174,7 +174,7 @@ isAtLocation(x: 0) isAtLocation(y: 0) } - + errors: - message: Fields "isAtLocation" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -193,7 +193,7 @@ name } } - + errors: [] - name: allows different order of args rule: OverlappingFieldsCanBeMerged @@ -204,7 +204,7 @@ someField(a: null, b: null) someField(b: null, a: null) } - + errors: [] - name: allows different order of input object fields in arg values rule: OverlappingFieldsCanBeMerged @@ -215,7 +215,7 @@ someField(arg: { a: null, b: null }) someField(arg: { b: null, a: null }) } - + errors: [] - name: encounters conflict in fragments rule: OverlappingFieldsCanBeMerged @@ -232,7 +232,7 @@ fragment B on Type { x: b } - + errors: - message: Fields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -264,7 +264,7 @@ fragment B on Type { x: b } - + errors: - message: Fields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -291,7 +291,7 @@ x: b } } - + errors: - message: Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -314,7 +314,7 @@ y: d } } - + errors: - message: Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields and subfields "y" conflict because "c" and "d" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -341,7 +341,7 @@ } } } - + errors: - message: Fields "field" conflict because subfields "deepField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -371,7 +371,7 @@ } } } - + errors: - message: Fields "deepField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -407,7 +407,7 @@ } } } - + errors: - message: Fields "deeperField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -442,7 +442,7 @@ fragment J on T { x: b } - + errors: - message: Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields and subfields "y" conflict because "c" and "d" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -452,6 +452,30 @@ - {line: 6, column: 9} - {line: 22, column: 9} - {line: 18, column: 9} +- name: reports deep conflict after nested fragments + rule: OverlappingFieldsCanBeMerged + schema: 0 + query: |2- + + fragment F on T { + ...G + } + fragment G on T { + ...H + } + fragment H on T { + x: a + } + { + x: b + ...F + } + + errors: + - message: Fields "x" conflict because "b" and "a" are different fields. Use different aliases on the fields to fetch both if this was intentional. + locations: + - {line: 12, column: 9} + - {line: 9, column: 9} - name: ignores unknown fragments rule: OverlappingFieldsCanBeMerged schema: 0 @@ -467,7 +491,7 @@ field ...OtherUnknown } - + errors: [] - name: return types must be unambiguous/conflicting return types which potentially overlap rule: OverlappingFieldsCanBeMerged @@ -484,7 +508,7 @@ } } } - + errors: - message: Fields "scalar" conflict because they return conflicting types "Int" and "String!". Use different aliases on the fields to fetch both if this was intentional. locations: @@ -509,7 +533,7 @@ } } } - + errors: [] - name: return types must be unambiguous/disallows differing return types despite no overlap rule: OverlappingFieldsCanBeMerged @@ -526,7 +550,7 @@ } } } - + errors: - message: Fields "scalar" conflict because they return conflicting types "Int" and "String". Use different aliases on the fields to fetch both if this was intentional. locations: @@ -579,7 +603,7 @@ fragment Y on SomeBox { scalar: unrelatedField } - + errors: - message: Fields "other" conflict because subfields "scalar" conflict because "scalar" and "unrelatedField" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -602,7 +626,7 @@ } } } - + errors: - message: Fields "scalar" conflict because they return conflicting types "String!" and "String". Use different aliases on the fields to fetch both if this was intentional. locations: @@ -627,7 +651,7 @@ } } } - + errors: - message: Fields "box" conflict because they return conflicting types "[StringBox]" and "StringBox". Use different aliases on the fields to fetch both if this was intentional. locations: @@ -652,7 +676,7 @@ } } } - + errors: - message: Fields "box" conflict because they return conflicting types "StringBox" and "[StringBox]". Use different aliases on the fields to fetch both if this was intentional. locations: @@ -678,7 +702,7 @@ } } } - + errors: - message: Fields "val" conflict because "scalar" and "unrelatedField" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -703,7 +727,7 @@ } } } - + errors: - message: Fields "box" conflict because subfields "scalar" conflict because they return conflicting types "String" and "Int". Use different aliases on the fields to fetch both if this was intentional. locations: @@ -726,7 +750,7 @@ } } } - + errors: [] - name: return types must be unambiguous/same wrapped scalar return types rule: OverlappingFieldsCanBeMerged @@ -743,7 +767,7 @@ } } } - + errors: [] - name: return types must be unambiguous/allows inline fragments without type condition rule: OverlappingFieldsCanBeMerged @@ -756,7 +780,7 @@ a } } - + errors: [] - name: return types must be unambiguous/compares deep types including list rule: OverlappingFieldsCanBeMerged @@ -781,7 +805,7 @@ } } } - + errors: - message: Fields "edges" conflict because subfields "node" conflict because subfields "id" conflict because "name" and "id" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -806,7 +830,7 @@ } } } - + errors: [] - name: return types must be unambiguous/works for field names that are JS keywords rule: OverlappingFieldsCanBeMerged @@ -818,7 +842,7 @@ constructor } } - + errors: [] - name: does not infinite loop on recursive fragment rule: OverlappingFieldsCanBeMerged @@ -830,7 +854,7 @@ } fragment fragA on Human { name, relatives { name, ...fragA } } - + errors: [] - name: does not infinite loop on immediately recursive fragment rule: OverlappingFieldsCanBeMerged @@ -842,7 +866,7 @@ } fragment fragA on Human { name, ...fragA } - + errors: [] - name: does not infinite loop on recursive fragment with a field named after fragment rule: OverlappingFieldsCanBeMerged @@ -855,7 +879,7 @@ } fragment fragA on Query { ...fragA } - + errors: [] - name: finds invalid cases even with field named after fragment rule: OverlappingFieldsCanBeMerged @@ -870,7 +894,7 @@ fragment fragA on Type { fragA: b } - + errors: - message: Fields "fragA" conflict because "fragA" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: @@ -889,7 +913,7 @@ fragment fragA on Human { name, ...fragB } fragment fragB on Human { name, ...fragC } fragment fragC on Human { name, ...fragA } - + errors: [] - name: finds invalid case even with immediately recursive fragment rule: OverlappingFieldsCanBeMerged @@ -901,9 +925,38 @@ fido: name fido: nickname } - + errors: - message: Fields "fido" conflict because "name" and "nickname" are different fields. Use different aliases on the fields to fetch both if this was intentional. locations: - {line: 4, column: 9} - {line: 5, column: 9} +- name: does not infinite loop on recursive fragments separated by fields + rule: OverlappingFieldsCanBeMerged + schema: 0 + query: |2- + + { + ...fragA + ...fragB + } + + fragment fragA on T { + x { + ...fragA + x { + ...fragA + } + } + } + + fragment fragB on T { + x { + ...fragB + x { + ...fragB + } + } + } + + errors: [] diff --git a/validator/imported/spec/PossibleFragmentSpreadsRule.spec.yml b/validator/imported/spec/PossibleFragmentSpreadsRule.spec.yml index 099c5512..f7c58a4d 100644 --- a/validator/imported/spec/PossibleFragmentSpreadsRule.spec.yml +++ b/validator/imported/spec/PossibleFragmentSpreadsRule.spec.yml @@ -5,7 +5,7 @@ fragment objectWithinObject on Dog { ...dogFragment } fragment dogFragment on Dog { barkVolume } - + errors: [] - name: of the same object with inline fragment rule: PossibleFragmentSpreads @@ -13,7 +13,7 @@ query: |2- fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } } - + errors: [] - name: object into an implemented interface rule: PossibleFragmentSpreads @@ -22,7 +22,7 @@ fragment objectWithinInterface on Pet { ...dogFragment } fragment dogFragment on Dog { barkVolume } - + errors: [] - name: object into containing union rule: PossibleFragmentSpreads @@ -31,7 +31,7 @@ fragment objectWithinUnion on CatOrDog { ...dogFragment } fragment dogFragment on Dog { barkVolume } - + errors: [] - name: union into contained object rule: PossibleFragmentSpreads @@ -40,7 +40,7 @@ fragment unionWithinObject on Dog { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - + errors: [] - name: union into overlapping interface rule: PossibleFragmentSpreads @@ -49,7 +49,7 @@ fragment unionWithinInterface on Pet { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - + errors: [] - name: union into overlapping union rule: PossibleFragmentSpreads @@ -58,7 +58,7 @@ fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - + errors: [] - name: interface into implemented object rule: PossibleFragmentSpreads @@ -67,7 +67,7 @@ fragment interfaceWithinObject on Dog { ...petFragment } fragment petFragment on Pet { name } - + errors: [] - name: interface into overlapping interface rule: PossibleFragmentSpreads @@ -76,7 +76,7 @@ fragment interfaceWithinInterface on Pet { ...beingFragment } fragment beingFragment on Being { name } - + errors: [] - name: interface into overlapping interface in inline fragment rule: PossibleFragmentSpreads @@ -84,7 +84,7 @@ query: |2- fragment interfaceWithinInterface on Pet { ... on Being { name } } - + errors: [] - name: interface into overlapping union rule: PossibleFragmentSpreads @@ -93,7 +93,7 @@ fragment interfaceWithinUnion on CatOrDog { ...petFragment } fragment petFragment on Pet { name } - + errors: [] - name: ignores incorrect type (caught by FragmentsOnCompositeTypesRule) rule: PossibleFragmentSpreads @@ -102,7 +102,7 @@ fragment petFragment on Pet { ...badInADifferentWay } fragment badInADifferentWay on String { name } - + errors: [] - name: ignores unknown fragments (caught by KnownFragmentNamesRule) rule: PossibleFragmentSpreads @@ -110,7 +110,7 @@ query: |2- fragment petFragment on Pet { ...UnknownFragment } - + errors: [] - name: different object into object rule: PossibleFragmentSpreads @@ -119,7 +119,7 @@ fragment invalidObjectWithinObject on Cat { ...dogFragment } fragment dogFragment on Dog { barkVolume } - + errors: - message: Fragment "dogFragment" cannot be spread here as objects of type "Cat" can never be of type "Dog". locations: @@ -132,7 +132,7 @@ fragment invalidObjectWithinObjectAnon on Cat { ... on Dog { barkVolume } } - + errors: - message: Fragment cannot be spread here as objects of type "Cat" can never be of type "Dog". locations: @@ -144,7 +144,7 @@ fragment invalidObjectWithinInterface on Pet { ...humanFragment } fragment humanFragment on Human { pets { name } } - + errors: - message: Fragment "humanFragment" cannot be spread here as objects of type "Pet" can never be of type "Human". locations: @@ -156,7 +156,7 @@ fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment } fragment humanFragment on Human { pets { name } } - + errors: - message: Fragment "humanFragment" cannot be spread here as objects of type "CatOrDog" can never be of type "Human". locations: @@ -168,7 +168,7 @@ fragment invalidUnionWithinObject on Human { ...catOrDogFragment } fragment catOrDogFragment on CatOrDog { __typename } - + errors: - message: Fragment "catOrDogFragment" cannot be spread here as objects of type "Human" can never be of type "CatOrDog". locations: @@ -180,7 +180,7 @@ fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment } fragment humanOrAlienFragment on HumanOrAlien { __typename } - + errors: - message: Fragment "humanOrAlienFragment" cannot be spread here as objects of type "Pet" can never be of type "HumanOrAlien". locations: @@ -192,7 +192,7 @@ fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment } fragment humanOrAlienFragment on HumanOrAlien { __typename } - + errors: - message: Fragment "humanOrAlienFragment" cannot be spread here as objects of type "CatOrDog" can never be of type "HumanOrAlien". locations: @@ -204,7 +204,7 @@ fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment } fragment intelligentFragment on Intelligent { iq } - + errors: - message: Fragment "intelligentFragment" cannot be spread here as objects of type "Cat" can never be of type "Intelligent". locations: @@ -218,7 +218,7 @@ ...intelligentFragment } fragment intelligentFragment on Intelligent { iq } - + errors: - message: Fragment "intelligentFragment" cannot be spread here as objects of type "Pet" can never be of type "Intelligent". locations: @@ -231,7 +231,7 @@ fragment invalidInterfaceWithinInterfaceAnon on Pet { ...on Intelligent { iq } } - + errors: - message: Fragment cannot be spread here as objects of type "Pet" can never be of type "Intelligent". locations: @@ -243,7 +243,7 @@ fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment } fragment petFragment on Pet { name } - + errors: - message: Fragment "petFragment" cannot be spread here as objects of type "HumanOrAlien" can never be of type "Pet". locations: diff --git a/validator/imported/spec/ProvidedRequiredArgumentsRule.spec.yml b/validator/imported/spec/ProvidedRequiredArgumentsRule.spec.yml index f5369bc1..31006ed4 100644 --- a/validator/imported/spec/ProvidedRequiredArgumentsRule.spec.yml +++ b/validator/imported/spec/ProvidedRequiredArgumentsRule.spec.yml @@ -8,7 +8,7 @@ isHouseTrained(unknownArgument: true) } } - + errors: [] - name: Valid non-nullable value/Arg on optional arg rule: ProvidedRequiredArguments @@ -20,7 +20,7 @@ isHouseTrained(atOtherHomes: true) } } - + errors: [] - name: Valid non-nullable value/No Arg on optional arg rule: ProvidedRequiredArguments @@ -32,7 +32,7 @@ isHouseTrained } } - + errors: [] - name: Valid non-nullable value/No arg on non-null field with default rule: ProvidedRequiredArguments @@ -44,7 +44,7 @@ nonNullFieldWithDefault } } - + errors: [] - name: Valid non-nullable value/Multiple args rule: ProvidedRequiredArguments @@ -56,7 +56,7 @@ multipleReqs(req1: 1, req2: 2) } } - + errors: [] - name: Valid non-nullable value/Multiple args reverse order rule: ProvidedRequiredArguments @@ -68,7 +68,7 @@ multipleReqs(req2: 2, req1: 1) } } - + errors: [] - name: Valid non-nullable value/No args on multiple optional rule: ProvidedRequiredArguments @@ -80,7 +80,7 @@ multipleOpts } } - + errors: [] - name: Valid non-nullable value/One arg on multiple optional rule: ProvidedRequiredArguments @@ -92,7 +92,7 @@ multipleOpts(opt1: 1) } } - + errors: [] - name: Valid non-nullable value/Second arg on multiple optional rule: ProvidedRequiredArguments @@ -104,7 +104,7 @@ multipleOpts(opt2: 1) } } - + errors: [] - name: Valid non-nullable value/Multiple required args on mixedList rule: ProvidedRequiredArguments @@ -116,7 +116,7 @@ multipleOptAndReq(req1: 3, req2: 4) } } - + errors: [] - name: Valid non-nullable value/Multiple required and one optional arg on mixedList rule: ProvidedRequiredArguments @@ -128,7 +128,7 @@ multipleOptAndReq(req1: 3, req2: 4, opt1: 5) } } - + errors: [] - name: Valid non-nullable value/All required and optional args on mixedList rule: ProvidedRequiredArguments @@ -140,7 +140,7 @@ multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) } } - + errors: [] - name: Invalid non-nullable value/Missing one non-nullable argument rule: ProvidedRequiredArguments @@ -152,7 +152,7 @@ multipleReqs(req2: 2) } } - + errors: - message: Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided. locations: @@ -167,7 +167,7 @@ multipleReqs } } - + errors: - message: Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided. locations: @@ -185,7 +185,7 @@ multipleReqs(req1: "one") } } - + errors: - message: Field "multipleReqs" argument "req2" of type "Int!" is required, but it was not provided. locations: @@ -198,7 +198,7 @@ { dog @unknown } - + errors: [] - name: Directive arguments/with directives of valid types rule: ProvidedRequiredArguments @@ -213,7 +213,7 @@ name } } - + errors: [] - name: Directive arguments/with directive with missing types rule: ProvidedRequiredArguments @@ -225,7 +225,7 @@ name @skip } } - + errors: - message: Directive "@include" argument "if" of type "Boolean!" is required, but it was not provided. locations: diff --git a/validator/imported/spec/ScalarLeafsRule.spec.yml b/validator/imported/spec/ScalarLeafsRule.spec.yml index 7a7a521f..dd4ae98b 100644 --- a/validator/imported/spec/ScalarLeafsRule.spec.yml +++ b/validator/imported/spec/ScalarLeafsRule.spec.yml @@ -6,7 +6,7 @@ fragment scalarSelection on Dog { barks } - + errors: [] - name: object type missing selection rule: ScalarLeafs @@ -16,7 +16,7 @@ query directQueryOnObjectWithoutSubFields { human } - + errors: - message: Field "human" of type "Human" must have a selection of subfields. Did you mean "human { ... }"? locations: @@ -29,7 +29,7 @@ { human { pets } } - + errors: - message: Field "pets" of type "[Pet]" must have a selection of subfields. Did you mean "pets { ... }"? locations: @@ -42,7 +42,7 @@ fragment scalarSelectionWithArgs on Dog { doesKnowCommand(dogCommand: SIT) } - + errors: [] - name: scalar selection not allowed on Boolean rule: ScalarLeafs @@ -52,7 +52,7 @@ fragment scalarSelectionsNotAllowedOnBoolean on Dog { barks { sinceWhen } } - + errors: - message: Field "barks" must not have a selection since type "Boolean" has no subfields. locations: @@ -65,7 +65,7 @@ fragment scalarSelectionsNotAllowedOnEnum on Cat { furColor { inHexDec } } - + errors: - message: Field "furColor" must not have a selection since type "FurColor" has no subfields. locations: @@ -78,7 +78,7 @@ fragment scalarSelectionsNotAllowedWithArgs on Dog { doesKnowCommand(dogCommand: SIT) { sinceWhen } } - + errors: - message: Field "doesKnowCommand" must not have a selection since type "Boolean" has no subfields. locations: @@ -91,7 +91,7 @@ fragment scalarSelectionsNotAllowedWithDirectives on Dog { name @include(if: true) { isAlsoHumanName } } - + errors: - message: Field "name" must not have a selection since type "String" has no subfields. locations: @@ -104,7 +104,7 @@ fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen } } - + errors: - message: Field "doesKnowCommand" must not have a selection since type "Boolean" has no subfields. locations: diff --git a/validator/imported/spec/SingleFieldSubscriptionsRule.spec.yml b/validator/imported/spec/SingleFieldSubscriptionsRule.spec.yml index ad715545..fd26e445 100644 --- a/validator/imported/spec/SingleFieldSubscriptionsRule.spec.yml +++ b/validator/imported/spec/SingleFieldSubscriptionsRule.spec.yml @@ -6,7 +6,7 @@ subscription ImportantEmails { importantEmails } - + errors: [] - name: valid subscription with fragment rule: SingleFieldSubscriptions @@ -23,7 +23,7 @@ sender } } - + errors: [] - name: valid subscription with fragment and field rule: SingleFieldSubscriptions @@ -43,7 +43,7 @@ sender } } - + errors: [] - name: fails with more than one root field rule: SingleFieldSubscriptions @@ -54,7 +54,7 @@ importantEmails notImportantEmails } - + errors: - message: Subscription "ImportantEmails" must select only one top level field. locations: @@ -68,7 +68,7 @@ importantEmails __typename } - + errors: - message: Subscription "ImportantEmails" must select only one top level field. locations: @@ -88,7 +88,7 @@ fragment Introspection on SubscriptionRoot { typename: __typename } - + errors: - message: Subscription "ImportantEmails" must select only one top level field. locations: @@ -106,7 +106,7 @@ notImportantEmails spamEmails } - + errors: - message: Subscription "ImportantEmails" must select only one top level field. locations: @@ -132,7 +132,7 @@ fragment SpamEmails on SubscriptionRoot { spamEmails } - + errors: - message: Subscription "ImportantEmails" must select only one top level field. locations: @@ -151,7 +151,7 @@ fragment A on SubscriptionRoot { ...A } - + errors: [] - name: fails with many more than one root field via fragments (anonymous) rule: SingleFieldSubscriptions @@ -180,7 +180,7 @@ spamEmails ...NonExistentFragment } - + errors: - message: Anonymous Subscription must select only one top level field. locations: @@ -198,7 +198,7 @@ importantEmails notImportantEmails } - + errors: - message: Anonymous Subscription must select only one top level field. locations: @@ -211,7 +211,7 @@ subscription ImportantEmails { __typename } - + errors: - message: Subscription "ImportantEmails" must not select an introspection top level field. locations: @@ -224,7 +224,7 @@ subscription { __typename } - + errors: - message: Anonymous Subscription must not select an introspection top level field. locations: @@ -237,5 +237,5 @@ subscription { __typename } - + errors: [] diff --git a/validator/imported/spec/UniqueArgumentNamesRule.spec.yml b/validator/imported/spec/UniqueArgumentNamesRule.spec.yml index f3553cce..996f2a40 100644 --- a/validator/imported/spec/UniqueArgumentNamesRule.spec.yml +++ b/validator/imported/spec/UniqueArgumentNamesRule.spec.yml @@ -6,7 +6,7 @@ { field } - + errors: [] - name: no arguments on directive rule: UniqueArgumentNames @@ -16,7 +16,7 @@ { field @directive } - + errors: [] - name: argument on field rule: UniqueArgumentNames @@ -26,7 +26,7 @@ { field(arg: "value") } - + errors: [] - name: argument on directive rule: UniqueArgumentNames @@ -36,7 +36,7 @@ { field @directive(arg: "value") } - + errors: [] - name: same argument on two fields rule: UniqueArgumentNames @@ -47,7 +47,7 @@ one: field(arg: "value") two: field(arg: "value") } - + errors: [] - name: same argument on field and directive rule: UniqueArgumentNames @@ -57,7 +57,7 @@ { field(arg: "value") @directive(arg: "value") } - + errors: [] - name: same argument on two directives rule: UniqueArgumentNames @@ -67,7 +67,7 @@ { field @directive1(arg: "value") @directive2(arg: "value") } - + errors: [] - name: multiple field arguments rule: UniqueArgumentNames @@ -77,7 +77,7 @@ { field(arg1: "value", arg2: "value", arg3: "value") } - + errors: [] - name: multiple directive arguments rule: UniqueArgumentNames @@ -87,7 +87,7 @@ { field @directive(arg1: "value", arg2: "value", arg3: "value") } - + errors: [] - name: duplicate field arguments rule: UniqueArgumentNames @@ -97,7 +97,7 @@ { field(arg1: "value", arg1: "value") } - + errors: - message: There can be only one argument named "arg1". locations: @@ -111,7 +111,7 @@ { field(arg1: "value", arg1: "value", arg1: "value") } - + errors: - message: There can be only one argument named "arg1". locations: @@ -126,7 +126,7 @@ { field @directive(arg1: "value", arg1: "value") } - + errors: - message: There can be only one argument named "arg1". locations: @@ -140,7 +140,7 @@ { field @directive(arg1: "value", arg1: "value", arg1: "value") } - + errors: - message: There can be only one argument named "arg1". locations: diff --git a/validator/imported/spec/UniqueDirectivesPerLocationRule.spec.yml b/validator/imported/spec/UniqueDirectivesPerLocationRule.spec.yml index 81acb066..50903ad3 100644 --- a/validator/imported/spec/UniqueDirectivesPerLocationRule.spec.yml +++ b/validator/imported/spec/UniqueDirectivesPerLocationRule.spec.yml @@ -6,7 +6,7 @@ fragment Test on Type { field } - + errors: [] - name: unique directives in different locations rule: UniqueDirectivesPerLocation @@ -16,7 +16,7 @@ fragment Test on Type @directiveA { field @directiveB } - + errors: [] - name: unique directives in same locations rule: UniqueDirectivesPerLocation @@ -26,7 +26,7 @@ fragment Test on Type @directiveA @directiveB { field @directiveA @directiveB } - + errors: [] - name: same directives in different locations rule: UniqueDirectivesPerLocation @@ -36,7 +36,7 @@ fragment Test on Type @directiveA { field @directiveA } - + errors: [] - name: same directives in similar locations rule: UniqueDirectivesPerLocation @@ -47,7 +47,7 @@ field @directive field @directive } - + errors: [] - name: repeatable directives in same location rule: UniqueDirectivesPerLocation @@ -57,7 +57,7 @@ fragment Test on Type @repeatable @repeatable { field @repeatable @repeatable } - + errors: [] - name: unknown directives must be ignored rule: UniqueDirectivesPerLocation @@ -71,7 +71,7 @@ extend type Test @unknown { anotherField: String! } - + errors: [] - name: duplicate directives in one location rule: UniqueDirectivesPerLocation @@ -81,7 +81,7 @@ fragment Test on Type { field @directive @directive } - + errors: - message: The directive "@directive" can only be used once at this location. locations: @@ -95,7 +95,7 @@ fragment Test on Type { field @directive @directive @directive } - + errors: - message: The directive "@directive" can only be used once at this location. locations: @@ -113,7 +113,7 @@ fragment Test on Type { field @directiveA @directiveB @directiveA @directiveB } - + errors: - message: The directive "@directiveA" can only be used once at this location. locations: @@ -131,7 +131,7 @@ fragment Test on Type @directive @directive { field @directive @directive } - + errors: - message: The directive "@directive" can only be used once at this location. locations: diff --git a/validator/imported/spec/UniqueFragmentNamesRule.spec.yml b/validator/imported/spec/UniqueFragmentNamesRule.spec.yml index 4d294f0c..98a30223 100644 --- a/validator/imported/spec/UniqueFragmentNamesRule.spec.yml +++ b/validator/imported/spec/UniqueFragmentNamesRule.spec.yml @@ -6,7 +6,7 @@ { field } - + errors: [] - name: one fragment rule: UniqueFragmentNames @@ -20,7 +20,7 @@ fragment fragA on Type { field } - + errors: [] - name: many fragments rule: UniqueFragmentNames @@ -41,7 +41,7 @@ fragment fragC on Type { fieldC } - + errors: [] - name: inline fragments are always unique rule: UniqueFragmentNames @@ -56,7 +56,7 @@ fieldB } } - + errors: [] - name: fragment and operation named the same rule: UniqueFragmentNames @@ -69,7 +69,7 @@ fragment Foo on Type { field } - + errors: [] - name: fragments named the same rule: UniqueFragmentNames @@ -85,7 +85,7 @@ fragment fragA on Type { fieldB } - + errors: - message: There can be only one fragment named "fragA". locations: @@ -102,7 +102,7 @@ fragment fragA on Type { fieldB } - + errors: - message: There can be only one fragment named "fragA". locations: diff --git a/validator/imported/spec/UniqueInputFieldNamesRule.spec.yml b/validator/imported/spec/UniqueInputFieldNamesRule.spec.yml index 5f28dd6a..2e76cf45 100644 --- a/validator/imported/spec/UniqueInputFieldNamesRule.spec.yml +++ b/validator/imported/spec/UniqueInputFieldNamesRule.spec.yml @@ -6,7 +6,7 @@ { field(arg: { f: true }) } - + errors: [] - name: same input object within two args rule: UniqueInputFieldNames @@ -16,7 +16,7 @@ { field(arg1: { f: true }, arg2: { f: true }) } - + errors: [] - name: multiple input object fields rule: UniqueInputFieldNames @@ -26,7 +26,7 @@ { field(arg: { f1: "value", f2: "value", f3: "value" }) } - + errors: [] - name: allows for nested input objects with similar fields rule: UniqueInputFieldNames @@ -44,7 +44,7 @@ id: 1 }) } - + errors: [] - name: duplicate input object fields rule: UniqueInputFieldNames @@ -54,7 +54,7 @@ { field(arg: { f1: "value", f1: "value" }) } - + errors: - message: There can be only one input field named "f1". locations: @@ -68,7 +68,7 @@ { field(arg: { f1: "value", f1: "value", f1: "value" }) } - + errors: - message: There can be only one input field named "f1". locations: @@ -86,7 +86,7 @@ { field(arg: { f1: {f2: "value", f2: "value" }}) } - + errors: - message: There can be only one input field named "f2". locations: diff --git a/validator/imported/spec/UniqueOperationNamesRule.spec.yml b/validator/imported/spec/UniqueOperationNamesRule.spec.yml index 195803eb..8fcebdf0 100644 --- a/validator/imported/spec/UniqueOperationNamesRule.spec.yml +++ b/validator/imported/spec/UniqueOperationNamesRule.spec.yml @@ -6,7 +6,7 @@ fragment fragA on Type { field } - + errors: [] - name: one anon operation rule: UniqueOperationNames @@ -16,7 +16,7 @@ { field } - + errors: [] - name: one named operation rule: UniqueOperationNames @@ -26,7 +26,7 @@ query Foo { field } - + errors: [] - name: multiple operations rule: UniqueOperationNames @@ -40,7 +40,7 @@ query Bar { field } - + errors: [] - name: multiple operations of different types rule: UniqueOperationNames @@ -58,7 +58,7 @@ subscription Baz { field } - + errors: [] - name: fragment and operation named the same rule: UniqueOperationNames @@ -71,7 +71,7 @@ fragment Foo on Type { field } - + errors: [] - name: multiple operations of same name rule: UniqueOperationNames @@ -84,7 +84,7 @@ query Foo { fieldB } - + errors: - message: There can be only one operation named "Foo". locations: @@ -101,7 +101,7 @@ mutation Foo { fieldB } - + errors: - message: There can be only one operation named "Foo". locations: @@ -118,7 +118,7 @@ subscription Foo { fieldB } - + errors: - message: There can be only one operation named "Foo". locations: diff --git a/validator/imported/spec/UniqueVariableNamesRule.spec.yml b/validator/imported/spec/UniqueVariableNamesRule.spec.yml index 8a380da9..9b9b7f07 100644 --- a/validator/imported/spec/UniqueVariableNamesRule.spec.yml +++ b/validator/imported/spec/UniqueVariableNamesRule.spec.yml @@ -5,7 +5,7 @@ query A($x: Int, $y: String) { __typename } query B($x: String, $y: Int) { __typename } - + errors: [] - name: duplicate variable names rule: UniqueVariableNames @@ -15,7 +15,7 @@ query A($x: Int, $x: Int, $x: String) { __typename } query B($x: String, $x: Int) { __typename } query C($x: Int, $x: Int) { __typename } - + errors: - message: There can be only one variable named "$x". locations: diff --git a/validator/imported/spec/ValuesOfCorrectTypeRule.spec.yml b/validator/imported/spec/ValuesOfCorrectTypeRule.spec.yml index 83990de5..194f5824 100644 --- a/validator/imported/spec/ValuesOfCorrectTypeRule.spec.yml +++ b/validator/imported/spec/ValuesOfCorrectTypeRule.spec.yml @@ -8,7 +8,7 @@ intArgField(intArg: 2) } } - + errors: [] - name: Valid values/Good negative int value rule: ValuesOfCorrectType @@ -20,7 +20,7 @@ intArgField(intArg: -2) } } - + errors: [] - name: Valid values/Good boolean value rule: ValuesOfCorrectType @@ -32,7 +32,7 @@ booleanArgField(booleanArg: true) } } - + errors: [] - name: Valid values/Good string value rule: ValuesOfCorrectType @@ -44,7 +44,7 @@ stringArgField(stringArg: "foo") } } - + errors: [] - name: Valid values/Good float value rule: ValuesOfCorrectType @@ -56,7 +56,7 @@ floatArgField(floatArg: 1.1) } } - + errors: [] - name: Valid values/Good negative float value rule: ValuesOfCorrectType @@ -68,7 +68,7 @@ floatArgField(floatArg: -1.1) } } - + errors: [] - name: Valid values/Int into Float rule: ValuesOfCorrectType @@ -80,7 +80,7 @@ floatArgField(floatArg: 1) } } - + errors: [] - name: Valid values/Int into ID rule: ValuesOfCorrectType @@ -92,7 +92,7 @@ idArgField(idArg: 1) } } - + errors: [] - name: Valid values/String into ID rule: ValuesOfCorrectType @@ -104,7 +104,7 @@ idArgField(idArg: "someIdString") } } - + errors: [] - name: Valid values/Good enum value rule: ValuesOfCorrectType @@ -116,7 +116,7 @@ doesKnowCommand(dogCommand: SIT) } } - + errors: [] - name: Valid values/Enum with undefined value rule: ValuesOfCorrectType @@ -128,7 +128,7 @@ enumArgField(enumArg: UNKNOWN) } } - + errors: [] - name: Valid values/Enum with null value rule: ValuesOfCorrectType @@ -140,7 +140,7 @@ enumArgField(enumArg: NO_FUR) } } - + errors: [] - name: Valid values/null into nullable type rule: ValuesOfCorrectType @@ -152,7 +152,7 @@ intArgField(intArg: null) } } - + errors: [] - name: Valid values/null into nullable type rule: ValuesOfCorrectType @@ -164,7 +164,7 @@ name } } - + errors: [] - name: Invalid String values/Int into String rule: ValuesOfCorrectType @@ -176,7 +176,7 @@ stringArgField(stringArg: 1) } } - + errors: - message: 'String cannot represent a non string value: 1' locations: @@ -191,7 +191,7 @@ stringArgField(stringArg: 1.0) } } - + errors: - message: 'String cannot represent a non string value: 1.0' locations: @@ -206,7 +206,7 @@ stringArgField(stringArg: true) } } - + errors: - message: 'String cannot represent a non string value: true' locations: @@ -221,7 +221,7 @@ stringArgField(stringArg: BAR) } } - + errors: - message: 'String cannot represent a non string value: BAR' locations: @@ -236,7 +236,7 @@ intArgField(intArg: "3") } } - + errors: - message: 'Int cannot represent non-integer value: "3"' locations: @@ -251,7 +251,7 @@ intArgField(intArg: 829384293849283498239482938) } } - + errors: - message: 'Int cannot represent non 32-bit signed integer value: 829384293849283498239482938' locations: @@ -266,7 +266,7 @@ intArgField(intArg: FOO) } } - + errors: - message: 'Int cannot represent non-integer value: FOO' locations: @@ -281,7 +281,7 @@ intArgField(intArg: 3.0) } } - + errors: - message: 'Int cannot represent non-integer value: 3.0' locations: @@ -296,7 +296,7 @@ intArgField(intArg: 3.333) } } - + errors: - message: 'Int cannot represent non-integer value: 3.333' locations: @@ -311,7 +311,7 @@ floatArgField(floatArg: "3.333") } } - + errors: - message: 'Float cannot represent non numeric value: "3.333"' locations: @@ -326,7 +326,7 @@ floatArgField(floatArg: true) } } - + errors: - message: 'Float cannot represent non numeric value: true' locations: @@ -341,7 +341,7 @@ floatArgField(floatArg: FOO) } } - + errors: - message: 'Float cannot represent non numeric value: FOO' locations: @@ -356,7 +356,7 @@ booleanArgField(booleanArg: 2) } } - + errors: - message: 'Boolean cannot represent a non boolean value: 2' locations: @@ -371,7 +371,7 @@ booleanArgField(booleanArg: 1.0) } } - + errors: - message: 'Boolean cannot represent a non boolean value: 1.0' locations: @@ -386,7 +386,7 @@ booleanArgField(booleanArg: "true") } } - + errors: - message: 'Boolean cannot represent a non boolean value: "true"' locations: @@ -401,7 +401,7 @@ booleanArgField(booleanArg: TRUE) } } - + errors: - message: 'Boolean cannot represent a non boolean value: TRUE' locations: @@ -416,7 +416,7 @@ idArgField(idArg: 1.0) } } - + errors: - message: 'ID cannot represent a non-string and non-integer value: 1.0' locations: @@ -431,7 +431,7 @@ idArgField(idArg: true) } } - + errors: - message: 'ID cannot represent a non-string and non-integer value: true' locations: @@ -446,7 +446,7 @@ idArgField(idArg: SOMETHING) } } - + errors: - message: 'ID cannot represent a non-string and non-integer value: SOMETHING' locations: @@ -461,7 +461,7 @@ doesKnowCommand(dogCommand: 2) } } - + errors: - message: 'Enum "DogCommand" cannot represent non-enum value: 2.' locations: @@ -476,7 +476,7 @@ doesKnowCommand(dogCommand: 1.0) } } - + errors: - message: 'Enum "DogCommand" cannot represent non-enum value: 1.0.' locations: @@ -491,7 +491,7 @@ doesKnowCommand(dogCommand: "SIT") } } - + errors: - message: 'Enum "DogCommand" cannot represent non-enum value: "SIT". Did you mean the enum value "SIT"?' locations: @@ -506,7 +506,7 @@ doesKnowCommand(dogCommand: true) } } - + errors: - message: 'Enum "DogCommand" cannot represent non-enum value: true.' locations: @@ -521,7 +521,7 @@ doesKnowCommand(dogCommand: JUGGLE) } } - + errors: - message: Value "JUGGLE" does not exist in "DogCommand" enum. locations: @@ -536,7 +536,7 @@ doesKnowCommand(dogCommand: sit) } } - + errors: - message: Value "sit" does not exist in "DogCommand" enum. Did you mean the enum value "SIT"? locations: @@ -551,7 +551,7 @@ stringListArgField(stringListArg: ["one", null, "two"]) } } - + errors: [] - name: Valid List value/Empty list value rule: ValuesOfCorrectType @@ -563,7 +563,7 @@ stringListArgField(stringListArg: []) } } - + errors: [] - name: Valid List value/Null value rule: ValuesOfCorrectType @@ -575,7 +575,7 @@ stringListArgField(stringListArg: null) } } - + errors: [] - name: Valid List value/Single value into List rule: ValuesOfCorrectType @@ -587,7 +587,7 @@ stringListArgField(stringListArg: "one") } } - + errors: [] - name: Invalid List value/Incorrect item type rule: ValuesOfCorrectType @@ -599,7 +599,7 @@ stringListArgField(stringListArg: ["one", 2]) } } - + errors: - message: 'String cannot represent a non string value: 2' locations: @@ -614,7 +614,7 @@ stringListArgField(stringListArg: 1) } } - + errors: - message: 'String cannot represent a non string value: 1' locations: @@ -629,7 +629,7 @@ isHouseTrained(atOtherHomes: true) } } - + errors: [] - name: Valid non-nullable value/No Arg on optional arg rule: ValuesOfCorrectType @@ -641,7 +641,7 @@ isHouseTrained } } - + errors: [] - name: Valid non-nullable value/Multiple args rule: ValuesOfCorrectType @@ -653,7 +653,7 @@ multipleReqs(req1: 1, req2: 2) } } - + errors: [] - name: Valid non-nullable value/Multiple args reverse order rule: ValuesOfCorrectType @@ -665,7 +665,7 @@ multipleReqs(req2: 2, req1: 1) } } - + errors: [] - name: Valid non-nullable value/No args on multiple optional rule: ValuesOfCorrectType @@ -677,7 +677,7 @@ multipleOpts } } - + errors: [] - name: Valid non-nullable value/One arg on multiple optional rule: ValuesOfCorrectType @@ -689,7 +689,7 @@ multipleOpts(opt1: 1) } } - + errors: [] - name: Valid non-nullable value/Second arg on multiple optional rule: ValuesOfCorrectType @@ -701,7 +701,7 @@ multipleOpts(opt2: 1) } } - + errors: [] - name: Valid non-nullable value/Multiple required args on mixedList rule: ValuesOfCorrectType @@ -713,7 +713,7 @@ multipleOptAndReq(req1: 3, req2: 4) } } - + errors: [] - name: Valid non-nullable value/Multiple required and one optional arg on mixedList rule: ValuesOfCorrectType @@ -725,7 +725,7 @@ multipleOptAndReq(req1: 3, req2: 4, opt1: 5) } } - + errors: [] - name: Valid non-nullable value/All required and optional args on mixedList rule: ValuesOfCorrectType @@ -737,7 +737,7 @@ multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) } } - + errors: [] - name: Invalid non-nullable value/Incorrect value type rule: ValuesOfCorrectType @@ -749,7 +749,7 @@ multipleReqs(req2: "two", req1: "one") } } - + errors: - message: 'Int cannot represent non-integer value: "two"' locations: @@ -767,7 +767,7 @@ multipleReqs(req1: "one") } } - + errors: - message: 'Int cannot represent non-integer value: "one"' locations: @@ -782,7 +782,7 @@ multipleReqs(req1: null) } } - + errors: - message: Expected value of type "Int!", found null. locations: @@ -797,7 +797,7 @@ complexArgField } } - + errors: [] - name: Valid input object value/Partial object, only required rule: ValuesOfCorrectType @@ -809,7 +809,7 @@ complexArgField(complexArg: { requiredField: true }) } } - + errors: [] - name: Valid input object value/Partial object, required field can be falsy rule: ValuesOfCorrectType @@ -821,7 +821,7 @@ complexArgField(complexArg: { requiredField: false }) } } - + errors: [] - name: Valid input object value/Partial object, including required rule: ValuesOfCorrectType @@ -833,7 +833,7 @@ complexArgField(complexArg: { requiredField: true, intField: 4 }) } } - + errors: [] - name: Valid input object value/Full object rule: ValuesOfCorrectType @@ -851,7 +851,7 @@ }) } } - + errors: [] - name: Valid input object value/Full object with fields in different order rule: ValuesOfCorrectType @@ -869,7 +869,31 @@ }) } } + + errors: [] +- name: Valid oneOf input object value/Exactly one field + rule: ValuesOfCorrectType + schema: 0 + query: |2- + + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: "abc" }) + } + } + + errors: [] +- name: Valid oneOf input object value/Exactly one non-nullable variable + rule: ValuesOfCorrectType + schema: 0 + query: |2- + query ($string: String!) { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: $string }) + } + } + errors: [] - name: Invalid input object value/Partial object, missing required rule: ValuesOfCorrectType @@ -881,7 +905,7 @@ complexArgField(complexArg: { intField: 4 }) } } - + errors: - message: Field "ComplexInput.requiredField" of required type "Boolean!" was not provided. locations: @@ -899,7 +923,7 @@ }) } } - + errors: - message: 'String cannot represent a non string value: 2' locations: @@ -917,7 +941,7 @@ }) } } - + errors: - message: Expected value of type "Boolean!", found null. locations: @@ -935,7 +959,7 @@ }) } } - + errors: - message: Field "invalidField" is not defined by type "ComplexInput". Did you mean "intField"? locations: @@ -959,8 +983,68 @@ test3: anyArg(arg: [123, "abc"]) test4: anyArg(arg: {deep: [123, "abc"]}) } - + errors: [] +- name: Invalid oneOf input object value/Invalid field type + rule: ValuesOfCorrectType + schema: 0 + query: |2- + + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: 2 }) + } + } + + errors: + - message: 'String cannot represent a non string value: 2' + locations: + - {line: 4, column: 52} +- name: Invalid oneOf input object value/Exactly one null field + rule: ValuesOfCorrectType + schema: 0 + query: |2- + + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: null }) + } + } + + errors: + - message: Field "OneOfInput.stringField" must be non-null. + locations: + - {line: 4, column: 37} +- name: Invalid oneOf input object value/Exactly one nullable variable + rule: ValuesOfCorrectType + schema: 0 + query: |2- + + query ($string: String) { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: $string }) + } + } + + errors: + - message: Variable "string" must be non-nullable to be used for OneOf Input Object "OneOfInput". + locations: + - {line: 4, column: 37} +- name: Invalid oneOf input object value/More than one field + rule: ValuesOfCorrectType + schema: 0 + query: |2- + + { + complicatedArgs { + oneOfArgField(oneOfArg: { stringField: "abc", intField: 123 }) + } + } + + errors: + - message: OneOf Input Object "OneOfInput" must specify exactly one key. + locations: + - {line: 4, column: 37} - name: Directive arguments/with directives of valid types rule: ValuesOfCorrectType schema: 0 @@ -974,7 +1058,7 @@ name } } - + errors: [] - name: Directive arguments/with directive with incorrect types rule: ValuesOfCorrectType @@ -986,7 +1070,7 @@ name @skip(if: ENUM) } } - + errors: - message: 'Boolean cannot represent a non boolean value: "yes"' locations: @@ -1007,7 +1091,7 @@ ) { dog { name } } - + errors: [] - name: Variable default values/variables with valid default null values rule: ValuesOfCorrectType @@ -1021,7 +1105,7 @@ ) { dog { name } } - + errors: [] - name: Variable default values/variables with invalid default null values rule: ValuesOfCorrectType @@ -1035,7 +1119,7 @@ ) { dog { name } } - + errors: - message: Expected value of type "Int!", found null. locations: @@ -1058,7 +1142,7 @@ ) { dog { name } } - + errors: - message: 'Int cannot represent non-integer value: "one"' locations: @@ -1079,7 +1163,7 @@ ) { dog { name } } - + errors: - message: 'Boolean cannot represent a non boolean value: 123' locations: @@ -1095,7 +1179,7 @@ query MissingRequiredField($a: ComplexInput = {intField: 3}) { dog { name } } - + errors: - message: Field "ComplexInput.requiredField" of required type "Boolean!" was not provided. locations: @@ -1108,7 +1192,7 @@ query InvalidItem($a: [String] = ["one", 2]) { dog { name } } - + errors: - message: 'String cannot represent a non string value: 2' locations: diff --git a/validator/imported/spec/VariablesAreInputTypesRule.spec.yml b/validator/imported/spec/VariablesAreInputTypesRule.spec.yml index be0813d4..5fb2d305 100644 --- a/validator/imported/spec/VariablesAreInputTypesRule.spec.yml +++ b/validator/imported/spec/VariablesAreInputTypesRule.spec.yml @@ -6,7 +6,7 @@ query Foo($a: Unknown, $b: [[Unknown!]]!) { field(a: $a, b: $b) } - + errors: [] - name: input types are valid rule: VariablesAreInputTypes @@ -16,7 +16,7 @@ query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) { field(a: $a, b: $b, c: $c) } - + errors: [] - name: output types are invalid rule: VariablesAreInputTypes @@ -26,7 +26,7 @@ query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) { field(a: $a, b: $b, c: $c) } - + errors: - locations: - {line: 2, column: 21} diff --git a/validator/imported/spec/VariablesInAllowedPositionRule.spec.yml b/validator/imported/spec/VariablesInAllowedPositionRule.spec.yml index f9b48c95..e21f0e74 100644 --- a/validator/imported/spec/VariablesInAllowedPositionRule.spec.yml +++ b/validator/imported/spec/VariablesInAllowedPositionRule.spec.yml @@ -9,7 +9,7 @@ booleanArgField(booleanArg: $booleanArg) } } - + errors: [] - name: Boolean => Boolean within fragment rule: VariablesInAllowedPosition @@ -25,7 +25,7 @@ ...booleanArgFrag } } - + errors: [] - name: Boolean => Boolean within fragment rule: VariablesInAllowedPosition @@ -41,7 +41,7 @@ fragment booleanArgFrag on ComplicatedArgs { booleanArgField(booleanArg: $booleanArg) } - + errors: [] - name: Boolean! => Boolean rule: VariablesInAllowedPosition @@ -54,7 +54,7 @@ booleanArgField(booleanArg: $nonNullBooleanArg) } } - + errors: [] - name: Boolean! => Boolean within fragment rule: VariablesInAllowedPosition @@ -71,7 +71,7 @@ ...booleanArgFrag } } - + errors: [] - name: '[String] => [String]' rule: VariablesInAllowedPosition @@ -84,7 +84,7 @@ stringListArgField(stringListArg: $stringListVar) } } - + errors: [] - name: '[String!] => [String]' rule: VariablesInAllowedPosition @@ -97,7 +97,7 @@ stringListArgField(stringListArg: $stringListVar) } } - + errors: [] - name: String => [String] in item position rule: VariablesInAllowedPosition @@ -110,7 +110,7 @@ stringListArgField(stringListArg: [$stringVar]) } } - + errors: [] - name: String! => [String] in item position rule: VariablesInAllowedPosition @@ -123,7 +123,7 @@ stringListArgField(stringListArg: [$stringVar]) } } - + errors: [] - name: ComplexInput => ComplexInput rule: VariablesInAllowedPosition @@ -136,7 +136,7 @@ complexArgField(complexArg: $complexVar) } } - + errors: [] - name: ComplexInput => ComplexInput in field position rule: VariablesInAllowedPosition @@ -149,7 +149,7 @@ complexArgField(complexArg: {requiredArg: $boolVar}) } } - + errors: [] - name: Boolean! => Boolean! in directive rule: VariablesInAllowedPosition @@ -160,7 +160,7 @@ { dog @include(if: $boolVar) } - + errors: [] - name: Int => Int! rule: VariablesInAllowedPosition @@ -172,7 +172,7 @@ nonNullIntArgField(nonNullIntArg: $intArg) } } - + errors: - message: Variable "$intArg" of type "Int" used in position expecting type "Int!". locations: @@ -192,7 +192,7 @@ ...nonNullIntArgFieldFrag } } - + errors: - message: Variable "$intArg" of type "Int" used in position expecting type "Int!". locations: @@ -216,7 +216,7 @@ ...outerFrag } } - + errors: - message: Variable "$intArg" of type "Int" used in position expecting type "Int!". locations: @@ -232,7 +232,7 @@ booleanArgField(booleanArg: $stringVar) } } - + errors: - message: Variable "$stringVar" of type "String" used in position expecting type "Boolean". locations: @@ -248,7 +248,7 @@ stringListArgField(stringListArg: $stringVar) } } - + errors: - message: Variable "$stringVar" of type "String" used in position expecting type "[String]". locations: @@ -262,7 +262,7 @@ query Query($boolVar: Boolean) { dog @include(if: $boolVar) } - + errors: - message: Variable "$boolVar" of type "Boolean" used in position expecting type "Boolean!". locations: @@ -276,7 +276,7 @@ query Query($stringVar: String) { dog @include(if: $stringVar) } - + errors: - message: Variable "$stringVar" of type "String" used in position expecting type "Boolean!". locations: @@ -293,7 +293,7 @@ stringListNonNullArgField(stringListNonNullArg: $stringListVar) } } - + errors: - message: Variable "$stringListVar" of type "[String]" used in position expecting type "[String!]". locations: @@ -309,7 +309,7 @@ nonNullIntArgField(nonNullIntArg: $intVar) } } - + errors: - message: Variable "$intVar" of type "Int" used in position expecting type "Int!". locations: diff --git a/validator/imported/spec/schemas.yml b/validator/imported/spec/schemas.yml index 5eeafed3..75f8d315 100644 --- a/validator/imported/spec/schemas.yml +++ b/validator/imported/spec/schemas.yml @@ -72,6 +72,11 @@ stringListField: [String] } + input OneOfInput @oneOf { + stringField: String + intField: Int + } + type ComplicatedArgs { intArgField(intArg: Int): String nonNullIntArgField(nonNullIntArg: Int!): String @@ -83,6 +88,7 @@ stringListArgField(stringListArg: [String]): String stringListNonNullArgField(stringListNonNullArg: [String!]): String complexArgField(complexArg: ComplexInput): String + oneOfArgField(oneOfArg: OneOfInput): String multipleReqs(req1: Int!, req2: Int!): String nonNullFieldWithDefault(arg: Int! = 0): String multipleOpts(opt1: Int = 0, opt2: Int = 0): String @@ -450,6 +456,11 @@ stringListField: [String] } + input OneOfInput @oneOf { + stringField: String + intField: Int + } + type ComplicatedArgs { intArgField(intArg: Int): String nonNullIntArgField(nonNullIntArg: Int!): String @@ -461,6 +472,7 @@ stringListArgField(stringListArg: [String]): String stringListNonNullArgField(stringListNonNullArg: [String!]): String complexArgField(complexArg: ComplexInput): String + oneOfArgField(oneOfArg: OneOfInput): String multipleReqs(req1: Int!, req2: Int!): String nonNullFieldWithDefault(arg: Int! = 0): String multipleOpts(opt1: Int = 0, opt2: Int = 0): String diff --git a/validator/prelude.graphql b/validator/prelude.graphql index e199da5d..142a3877 100644 --- a/validator/prelude.graphql +++ b/validator/prelude.graphql @@ -30,6 +30,9 @@ directive @specifiedBy(url: String!) on SCALAR "The @defer directive may be specified on a fragment spread to imply de-prioritization, that causes the fragment to be omitted in the initial response, and delivered as a subsequent response afterward. A query with @defer directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred delivered in a subsequent response. @include and @skip take precedence over @defer." directive @defer(if: Boolean = true, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT +"The `@oneOf` _built-in directive_ is used within the type system definition language to indicate an Input Object is a OneOf Input Object." +directive @oneOf on INPUT_OBJECT + type __Schema { description: String types: [__Type!]! @@ -52,17 +55,29 @@ type __Type { # must be non-null for ENUM, otherwise null. enumValues(includeDeprecated: Boolean = false): [__EnumValue!] # must be non-null for INPUT_OBJECT, otherwise null. - inputFields: [__InputValue!] + inputFields(includeDeprecated: Boolean = false): [__InputValue!] # must be non-null for NON_NULL and LIST, otherwise null. ofType: __Type # may be non-null for custom SCALAR, otherwise null. specifiedByURL: String + isOneOf: Boolean +} + +enum __TypeKind { + SCALAR + OBJECT + INTERFACE + UNION + ENUM + INPUT_OBJECT + LIST + NON_NULL } type __Field { name: String! description: String - args: [__InputValue!]! + args(includeDeprecated: Boolean = false): [__InputValue!]! type: __Type! isDeprecated: Boolean! deprecationReason: String @@ -73,6 +88,8 @@ type __InputValue { description: String type: __Type! defaultValue: String + isDeprecated: Boolean! + deprecationReason: String } type __EnumValue { @@ -82,22 +99,11 @@ type __EnumValue { deprecationReason: String } -enum __TypeKind { - SCALAR - OBJECT - INTERFACE - UNION - ENUM - INPUT_OBJECT - LIST - NON_NULL -} - type __Directive { name: String! description: String locations: [__DirectiveLocation!]! - args: [__InputValue!]! + args(includeDeprecated: Boolean = false): [__InputValue!]! isRepeatable: Boolean! } diff --git a/validator/rules/max_introspection_depth.go b/validator/rules/max_introspection_depth.go new file mode 100644 index 00000000..98fac18b --- /dev/null +++ b/validator/rules/max_introspection_depth.go @@ -0,0 +1,90 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:revive // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +const maxListsDepth = 3 + +var MaxIntrospectionDepth = Rule{ + Name: "MaxIntrospectionDepth", + RuleFunc: func(observers *Events, addError AddErrFunc) { + // Counts the depth of list fields in "__Type" recursively and + // returns `true` if the limit has been reached. + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.Name == "__schema" || field.Name == "__type" { + visitedFragments := make(map[string]bool) + if checkDepthField(field, visitedFragments, 0) { + addError( + Message(`Maximum introspection depth exceeded`), + At(field.Position), + ) + } + return + } + }) + }, +} + +func checkDepthSelectionSet(selectionSet ast.SelectionSet, visitedFragments map[string]bool, depth int) bool { + for _, child := range selectionSet { + if field, ok := child.(*ast.Field); ok { + if checkDepthField(field, visitedFragments, depth) { + return true + } + } + if fragmentSpread, ok := child.(*ast.FragmentSpread); ok { + if checkDepthFragmentSpread(fragmentSpread, visitedFragments, depth) { + return true + } + } + if inlineFragment, ok := child.(*ast.InlineFragment); ok { + if checkDepthSelectionSet(inlineFragment.SelectionSet, visitedFragments, depth) { + return true + } + } + } + return false +} + +func checkDepthField(field *ast.Field, visitedFragments map[string]bool, depth int) bool { + if field.Name == "fields" || + field.Name == "interfaces" || + field.Name == "possibleTypes" || + field.Name == "inputFields" { + depth++ + if depth >= maxListsDepth { + return true + } + } + return checkDepthSelectionSet(field.SelectionSet, visitedFragments, depth) +} + +func checkDepthFragmentSpread(fragmentSpread *ast.FragmentSpread, visitedFragments map[string]bool, depth int) bool { + fragmentName := fragmentSpread.Name + if visited, ok := visitedFragments[fragmentName]; ok && visited { + // Fragment cycles are handled by `NoFragmentCyclesRule`. + return false + } + fragment := fragmentSpread.Definition + if fragment == nil { + // Missing fragments checks are handled by `KnownFragmentNamesRule`. + return false + } + + // Rather than following an immutable programming pattern which has + // significant memory and garbage collection overhead, we've opted to + // take a mutable approach for efficiency's sake. Importantly visiting a + // fragment twice is fine, so long as you don't do one visit inside the + // other. + visitedFragments[fragmentName] = true + defer delete(visitedFragments, fragmentName) + return checkDepthSelectionSet(fragment.SelectionSet, visitedFragments, depth) +} + +func init() { + AddRule(MaxIntrospectionDepth.Name, MaxIntrospectionDepth.RuleFunc) +} diff --git a/validator/rules/values_of_correct_type.go b/validator/rules/values_of_correct_type.go index 6cfdc3dc..cce4f4b2 100644 --- a/validator/rules/values_of_correct_type.go +++ b/validator/rules/values_of_correct_type.go @@ -132,6 +132,42 @@ func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disable } } + for _, directive := range value.Definition.Directives { + if directive.Name == "oneOf" { + func() { + if len(value.Children) != 1 { + addError( + Message(`OneOf Input Object "%s" must specify exactly one key.`, value.Definition.Name), + At(value.Position), + ) + return + } + + fieldValue := value.Children[0].Value + isNullLiteral := fieldValue == nil || fieldValue.Kind == ast.NullValue + if isNullLiteral { + addError( + Message(`Field "%s.%s" must be non-null.`, value.Definition.Name, value.Definition.Fields[0].Name), + At(fieldValue.Position), + ) + return + } + + isVariable := fieldValue.Kind == ast.Variable + if isVariable { + variableName := fieldValue.VariableDefinition.Variable + isNullableVariable := !fieldValue.VariableDefinition.Type.NonNull + if isNullableVariable { + addError( + Message(`Variable "%s" must be non-nullable to be used for OneOf Input Object "%s".`, variableName, value.Definition.Name), + At(fieldValue.Position), + ) + } + } + }() + } + } + for _, fieldValue := range value.Children { if value.Definition.Fields.ForName(fieldValue.Name) == nil { if disableSuggestion { diff --git a/validator/schema.go b/validator/schema.go index 9f9ddde4..812e4f3d 100644 --- a/validator/schema.go +++ b/validator/schema.go @@ -86,7 +86,7 @@ func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) { // scalars, it may (ยง3.13) define builtin directives. Here we check for // that, and reject doubly-defined directives otherwise. switch dir.Name { - case "include", "skip", "deprecated", "specifiedBy", "defer": // the builtins + case "include", "skip", "deprecated", "specifiedBy", "defer", "oneOf": // the builtins // In principle here we might want to validate that the // directives are the same. But they might not be, if the // server has an older spec than we do. (Plus, validating this