From a1563a909ce30555acf706f394f6d990a8403cb5 Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Sun, 22 Sep 2024 00:24:40 +0300 Subject: [PATCH 1/4] remove OneOf-specific rule in favor of update to VariablesInAllowedPositions for simplicity, this PR retains the same problems for variables with defaults that are fixed by strict All Variable Usages Are Allowed --- spec/Section 5 -- Validation.md | 138 +++++++++----------------------- 1 file changed, 39 insertions(+), 99 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index a4d5208db..8a2faf981 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1452,103 +1452,6 @@ arguments, an input object may have required fields. An input field is required if it has a non-null type and does not have a default value. Otherwise, the input object field is optional. -### OneOf Input Objects Have Exactly One Field - -**Formal Specification** - -- For each {operation} in {document}: - - Let {oneofInputObjects} be all OneOf Input Objects transitively included in - the {operation}. - - For each {oneofInputObject} in {oneofInputObjects}: - - Let {fields} be the fields provided by {oneofInputObject}. - - {fields} must contain exactly one entry. - - Let {field} be the sole entry in {fields}. - - Let {value} be the value of {field}. - - {value} must not be the {null} literal. - - If {value} is a variable: - - Let {variableName} be the name of {variable}. - - Let {variableDefinition} be the {VariableDefinition} named - {variableName} defined within {operation}. - - Let {variableType} be the expected type of {variableDefinition}. - - {variableType} must be a non-null type. - -**Explanatory Text** - -OneOf Input Objects require that exactly one field must be supplied and that -field must not be {null}. - -An empty OneOf Input Object is invalid. - -```graphql counter-example -mutation addPet { - addPet(pet: {}) { - name - } -} -``` - -Multiple fields are not allowed. - -```graphql counter-example -mutation addPet($cat: CatInput, $dog: DogInput) { - addPet(pet: { cat: $cat, dog: $dog }) { - name - } -} -``` - -```graphql counter-example -mutation addPet($dog: DogInput) { - addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) { - name - } -} -``` - -```graphql counter-example -mutation addPet { - addPet(pet: { cat: { name: "Brontie" }, dog: null }) { - name - } -} -``` - -Variables used for OneOf Input Object fields must be non-nullable. - -```graphql example -mutation addPet($cat: CatInput!) { - addPet(pet: { cat: $cat }) { - name - } -} -``` - -```graphql counter-example -mutation addPet($cat: CatInput) { - addPet(pet: { cat: $cat }) { - name - } -} -``` - -If a field with a literal value is present then the value must not be {null}. - -```graphql example -mutation addPet { - addPet(pet: { cat: { name: "Brontie" } }) { - name - } -} -``` - -```graphql counter-example -mutation addPet { - addPet(pet: { cat: null }) { - name - } -} -``` - ## Directives ### Directives Are Defined @@ -1989,8 +1892,8 @@ IsVariableUsageAllowed(variableDefinition, variableUsage): - Let {variableType} be the expected type of {variableDefinition}. - Let {locationType} be the expected type of the {Argument}, {ObjectField}, or {ListValue} entry where {variableUsage} is located. -- If {locationType} is a non-null type AND {variableType} is NOT a non-null - type: +- If {IsNonNullPosition(locationType, variableUsage)} AND {variableType} is NOT + a non-null type: - Let {hasNonNullVariableDefaultValue} be {true} if a default value exists for {variableDefinition} and is not the value {null}. - Let {hasLocationDefaultValue} be {true} if a default value exists for the @@ -2001,6 +1904,15 @@ IsVariableUsageAllowed(variableDefinition, variableUsage): - Return {AreTypesCompatible(variableType, nullableLocationType)}. - Return {AreTypesCompatible(variableType, locationType)}. +IsNonNullPosition(locationType, variableUsage): + +- Let {isOneOfField} be {true} if {variableUsage} is located within an + {ObjectField} entry and the parent type of {ObjectField} is a OneOf Input + Object; otherwise {false}. +- If {isOneOfField} is {true} or {locationType} is a non-null type, return + {true}. +- Return {false}. + AreTypesCompatible(variableType, locationType): - If {locationType} is a non-null type: @@ -2089,6 +2001,34 @@ query listToNonNullList($booleanList: [Boolean]) { This would fail validation because a `[T]` cannot be passed to a `[T]!`. Similarly a `[T]` cannot be passed to a `[T!]`. +Variables used for OneOf Input Object fields must be non-nullable. + +```graphql example +mutation addPet($cat: CatInput!) { + addPet(pet: { cat: $cat }) { + name + } +} +``` + +```graphql counter-example +mutation addPet($cat: CatInput) { + addPet(pet: { cat: $cat }) { + name + } +} +``` + +Variables used for OneOf Input Object fields cannot have default values. + +```graphql counter-example +mutation addPet($cat: CatInput = { name: "Kitty" }) { + addPet(pet: { cat: $cat }) { + name + } +} +``` + **Allowing Optional Variables When Default Values Exist** A notable exception to typical variable type compatibility is allowing a From b45c0e400c63b423bfd40a36a052c24f7fdec3fb Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 12:00:04 +0100 Subject: [PATCH 2/4] Clarify IsNonNullPosition algorithm --- spec/Section 5 -- Validation.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 8a2faf981..c5abf7cc5 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1906,11 +1906,11 @@ IsVariableUsageAllowed(variableDefinition, variableUsage): IsNonNullPosition(locationType, variableUsage): -- Let {isOneOfField} be {true} if {variableUsage} is located within an - {ObjectField} entry and the parent type of {ObjectField} is a OneOf Input - Object; otherwise {false}. -- If {isOneOfField} is {true} or {locationType} is a non-null type, return - {true}. +- If {locationType} is a non-null type, return {true}. +- If the location of {variableUsage} is an {ObjectField}: + - Let {parentLocationType} be the expected type of {ObjectField}'s parent + {ObjectValue}. + - If {parentLocationType} is a OneOf Input Object type, return {true}. - Return {false}. AreTypesCompatible(variableType, locationType): From 0c9830e4f727b6e693d90829711a741f449bae8a Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 12:03:36 +0100 Subject: [PATCH 3/4] Clarify OneOf examples --- spec/Section 5 -- Validation.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index c5abf7cc5..33fd88f98 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -2004,25 +2004,25 @@ Similarly a `[T]` cannot be passed to a `[T!]`. Variables used for OneOf Input Object fields must be non-nullable. ```graphql example -mutation addPet($cat: CatInput!) { +mutation addCat($cat: CatInput!) { addPet(pet: { cat: $cat }) { name } } -``` - -```graphql counter-example -mutation addPet($cat: CatInput) { +mutation addCatWithDefault($cat: CatInput! = { name: "Brontie" }) { addPet(pet: { cat: $cat }) { name } } ``` -Variables used for OneOf Input Object fields cannot have default values. - ```graphql counter-example -mutation addPet($cat: CatInput = { name: "Kitty" }) { +mutation addNullableCat($cat: CatInput) { + addPet(pet: { cat: $cat }) { + name + } +} +mutation addNullableCatWithDefault($cat: CatInput = { name: "Brontie" }) { addPet(pet: { cat: $cat }) { name } From c4d0b50193d628bc2e662edbaa16aebbf3ed0e0d Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 12:10:36 +0100 Subject: [PATCH 4/4] Add more examples --- spec/Section 5 -- Validation.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 33fd88f98..b7a421643 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -42,7 +42,8 @@ type Query { } type Mutation { - addPet(pet: PetInput): Pet + addPet(pet: PetInput!): Pet + addPets(pet: [PetInput!]!): [Pet] } enum DogCommand { @@ -1351,6 +1352,12 @@ query goodComplexDefaultValue($search: FindDogInput = { name: "Fido" }) { name } } + +mutation addPet($pet: PetInput! = { cat: { name: "Brontie" } }) { + addPet(pet: $pet) { + name + } +} ``` Non-coercible values (such as a String into an Int) are invalid. The following @@ -1366,6 +1373,24 @@ query badComplexValue { name } } + +mutation oneOfWithNoFields { + addPet(pet: {}) { + name + } +} + +mutation oneOfWithTwoFields($dog: DogInput) { + addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) { + name + } +} + +mutation listOfOneOfWithNullableVariable($dog: DogInput) { + addPets(pets: [{ dog: $dog }]) { + name + } +} ``` ### Input Object Field Names