From c385058693ab5e308e780801a1b892e54605f513 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 19 Feb 2021 12:31:01 +0000 Subject: [PATCH 01/26] Renumber list items --- spec/Section 3 -- Type System.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index a31a944f3..0aa70ee6d 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -832,14 +832,14 @@ IsValidImplementation(type, implementedType): defined in {implementedType}. 1. Let {field} be that named field on {type}. 2. Let {implementedField} be that named field on {implementedType}. - 1. {field} must include an argument of the same name for every argument + 3. {field} must include an argument of the same name for every argument defined in {implementedField}. 1. That named argument on {field} must accept the same type (invariant) as that named argument on {implementedField}. - 2. {field} may include additional arguments not defined in + 4. {field} may include additional arguments not defined in {implementedField}, but any additional argument must not be required, e.g. must not be of a non-nullable type. - 3. {field} must return a type which is equal to or a sub-type of + 5. {field} must return a type which is equal to or a sub-type of (covariant) the return type of {implementedField} field's return type: 1. Let {fieldType} be the return type of {field}. 2. Let {implementedFieldType} be the return type of {implementedField}. @@ -1600,10 +1600,10 @@ be used by a GraphQL service which is itself an extension of another GraphQL ser Input object type extensions have the potential to be invalid if incorrectly defined. 1. The named type must already be defined and must be a Input Object type. -3. All fields of an Input Object type extension must have unique names. -4. All fields of an Input Object type extension must not already be a field of +2. All fields of an Input Object type extension must have unique names. +3. All fields of an Input Object type extension must not already be a field of the original Input Object. -5. Any non-repeatable directives provided must not already apply to the +4. Any non-repeatable directives provided must not already apply to the original Input Object type. From f6bd659cfa089c5b5778fc649c278617afc7ba7c Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 19 Feb 2021 13:01:07 +0000 Subject: [PATCH 02/26] @oneOf input objects --- spec/Section 3 -- Type System.md | 85 +++++++++++++++++++++++++++++- spec/Section 4 -- Introspection.md | 5 ++ spec/Section 5 -- Validation.md | 16 ++++++ 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 0aa70ee6d..fb3d5d4bf 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1448,6 +1448,28 @@ define arguments or contain references to interfaces and unions, neither of which is appropriate for use as an input argument. For this reason, input objects have a separate type in the system. +**Oneof Input Objects** + +Oneof Input Objects are a special variant of Input Objects where the type +system asserts that exactly one of the fields must be set and non-null, all +others being omitted. This is useful for representing situations where an input +may be one of many different options. + +When using the type system definition language, the `@oneOf` directive is used +to indicate that an Input Object is a Oneof Input Object (and thus requires +exactly one of its field be provided): + +```graphql +input UserUniqueCondition @oneOf { + id: ID + username: String + organizationAndEmail: OrganizationAndEmailInput +} +``` + +In schema introspection, the `__Type.oneField` field will return {true} for +Oneof Input Objects, and {false} for all other Input Objects. + **Circular References** Input Objects are allowed to reference other Input Objects as field types. A @@ -1570,6 +1592,37 @@ Literal Value | Variables | Coerced Value `{ b: $var }` | `{ var: null }` | Error: {b} must be non-null. `{ b: 123, c: "xyz" }` | `{}` | Error: Unexpected field {c} + +Following are examples of input coercion for a Oneof Input Object with a +`String` member field `a` and an `Int` member field `b`: + +```graphql example +input ExampleInputTagged @oneOf { + a: String + b: Int +} +``` + +Literal Value | Variables | Coerced Value +------------------------ | ----------------------- | --------------------------- +`{ a: "abc", b: 123 }` | `{}` | Error: Exactly one key must be specified +`{ a: null, b: 123 }` | `{}` | Error: Exactly one key must be specified +`{ b: 123 }` | `{}` | `{ b: 123 }` +`{ a: $var, b: 123 }` | `{ var: null }` | Error: Exactly one key must be specified +`{ a: $var, b: 123 }` | `{}` | `{ b: 123 }` +`{ b: $var }` | `{ var: 123 }` | `{ b: 123 }` +`$var` | `{ var: { b: 123 } }` | `{ b: 123 }` +`"abc123"` | `{}` | Error: Incorrect value +`$var` | `{ var: "abc123" } }` | Error: Incorrect value +`{ a: "abc", b: "123" }` | `{}` | Error: Exactly one key must be specified +`{ b: "123" }` | `{}` | Error: Incorrect value for member field {b} +`{ a: "abc" }` | `{}` | `{ a: "abc" }` +`{ b: $var }` | `{}` | Error: No keys were specified +`$var` | `{ var: { a: "abc" } }` | `{ a: "abc" }` +`{ a: "abc", b: null }` | `{}` | Error: Exactly one key must be specified +`{ b: $var }` | `{ var: null }` | Error: Value for member field {b} must be non-null +`{ b: 123, c: "xyz" }` | `{}` | Error: Exactly one key must be specified + **Type Validation** 1. An Input Object type must define one or more input fields. @@ -1580,6 +1633,9 @@ Literal Value | Variables | Coerced Value characters {"__"} (two underscores). 3. The input field must accept a type where {IsInputType(inputFieldType)} returns {true}. + 4. If the Input Object is a Oneof Input Object then: + 1. The type of the input field must be nullable. + 2. The input field must not have a default value. 3. If an Input Object references itself either directly or through referenced Input Objects, at least one of the fields in the chain of references must be either a nullable or a List type. @@ -1605,6 +1661,10 @@ Input object type extensions have the potential to be invalid if incorrectly def the original Input Object. 4. Any non-repeatable directives provided must not already apply to the original Input Object type. +5. If the original Input Object is a Oneof Input Object then: + 1. All fields of the Input Object type extension must be nullable. + 2. All fields of the Input Object type extension must not have default + values. ## List @@ -1815,8 +1875,12 @@ by a validator, executor, or client tool such as a code generator. GraphQL implementations should provide the `@skip` and `@include` directives. GraphQL implementations that support the type system definition language must -provide the `@deprecated` directive if representing deprecated portions of -the schema. +provide: + +- the `@deprecated` directive if representing deprecated portions of the + schema; +- the `@oneOf` directive if representing types that require exactly one field + (i.e. Oneof Input Objects). **Custom Directives** @@ -1980,3 +2044,20 @@ type ExampleType { oldField: String @deprecated(reason: "Use `newField`.") } ``` + +### @oneOf + +```graphql +directive @oneOf on INPUT_OBJECT +``` + +The `@oneOf` directive is used within the type system definition language +to indicate an Input Object is a Oneof Input Object. + +```graphql example +input UserUniqueCondition @oneOf { + id: ID + username: String + organizationAndEmail: OrganizationAndEmailInput +} +``` diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 2735417f1..c3d4d0a41 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -147,6 +147,9 @@ type __Type { # should be non-null for NON_NULL and LIST only, must be null for the others ofType: __Type + + # should be non-null for INPUT_OBJECT only + oneField: Boolean } type __Field { @@ -336,6 +339,8 @@ Fields * `name` must return a String. * `description` may return a String or {null}. * `inputFields`: a list of `InputValue`. +* `oneField` must return {true} for Oneof Input Objects, {false} for all other + Input Objects. * All other fields must return {null}. diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 7e7238ed5..64336718b 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1424,6 +1424,22 @@ 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 Oneof Input Object in the document: + * Let {fields} be the fields provided by that Oneof Input Object. + * {fields} must contain exactly one entry. + * For the sole {field} in {fields}: + * Let {value} be the value of {field}. + * {value} must not be the {null} literal. + +**Explanatory Text** + +Oneof Input Objects require that exactly one field must be supplied and that +field must not be null. + ## Directives From 39e593cb8b0d13a50997b3edaba5edd06d491abb Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 19 Feb 2021 15:45:42 +0000 Subject: [PATCH 03/26] @oneOf fields --- spec/Section 3 -- Type System.md | 53 ++++++++++++++++++++++++++++-- spec/Section 4 -- Introspection.md | 3 ++ spec/Section 5 -- Validation.md | 14 ++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index fb3d5d4bf..3713da402 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -818,6 +818,9 @@ of rules must be adhered to by every Object type in a GraphQL schema. characters {"__"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. + 3. If the field is a Oneof Field: + 1. The field must be nullable. + 2. The field must not have a default value. 3. An object type may declare that it implements one or more unique interfaces. 4. An object type must be a super-set of all interfaces it implements: 1. Let this object type be {objectType}. @@ -845,6 +848,8 @@ IsValidImplementation(type, implementedType): 2. Let {implementedFieldType} be the return type of {implementedField}. 3. {IsValidImplementationFieldType(fieldType, implementedFieldType)} must be {true}. + 6. {field} must be a Oneof Field if and only if {implementedField} is a + Oneof Field. IsValidImplementationFieldType(fieldType, implementedFieldType): 1. If {fieldType} is a Non-Null type: @@ -917,6 +922,30 @@ May yield the result: The type of an object field argument must be an input type (any type except an Object, Interface, or Union type). +**Oneof Fields** + +Oneof Fields are a special variant of Object Type fields where the type system +asserts that exactly one of the field's arguments must be set and non-null, all +others being omitted. This is useful for representing situations where an input +may be one of many different options. + +When using the type system definition language, the `@oneOf` directive is used +to indicate that a Field is a Oneof Field (and thus requires exactly one of its +arguments be provided): + +```graphql +type Query { + findUser( + byID: ID + byUsername: String + byEmail: String + byRegistrationNumber: Int + ): User @oneOf +} +``` + +In schema introspection, the `__Field.oneArgument` field will return {true} for +Oneof Fields, and {false} for all other Fields. ### Field Deprecation @@ -1160,6 +1189,9 @@ Interface types have the potential to be invalid if incorrectly defined. characters {"__"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. + 3. If the field is a Oneof Field: + 1. The field must be nullable. + 2. The field must not have a default value. 3. An interface type may declare that it implements one or more unique interfaces, but may not implement itself. 4. An interface type must be a super-set of all interfaces it implements: @@ -1880,7 +1912,8 @@ provide: - the `@deprecated` directive if representing deprecated portions of the schema; - the `@oneOf` directive if representing types that require exactly one field - (i.e. Oneof Input Objects). + (i.e. Oneof Input Objects) or fields that require exactly one argument (i.e. + Oneof Fields). **Custom Directives** @@ -2048,11 +2081,14 @@ type ExampleType { ### @oneOf ```graphql -directive @oneOf on INPUT_OBJECT +directive @oneOf on INPUT_OBJECT | FIELD_DEFINITION ``` The `@oneOf` directive is used within the type system definition language -to indicate an Input Object is a Oneof Input Object. +to indicate: + +- an Input Object is a Oneof Input Object, or +- an Object Type's Field is a Oneof Field. ```graphql example input UserUniqueCondition @oneOf { @@ -2061,3 +2097,14 @@ input UserUniqueCondition @oneOf { organizationAndEmail: OrganizationAndEmailInput } ``` + +```graphql example +type Query { + findUser( + byID: ID + byUsername: String + byEmail: String + byRegistrationNumber: Int + ): User @oneOf +} +``` diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index c3d4d0a41..5b2f7fdb0 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -159,6 +159,7 @@ type __Field { type: __Type! isDeprecated: Boolean! deprecationReason: String + oneArgument: Boolean! } type __InputValue { @@ -385,6 +386,8 @@ Fields * `isDeprecated` returns {true} if this field should no longer be used, otherwise {false}. * `deprecationReason` optionally provides a reason why this field is deprecated. +* `oneArgument` must return {true} for Oneof Fields, {false} for all other + Fields. ### The __InputValue Type diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 64336718b..5cb8f9ba4 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -780,6 +780,20 @@ fragment missingRequiredArg on Arguments { } ``` +#### Oneof Fields Have Exactly One Argument + +* For each Oneof Field in the document: + * Let {arguments} be the arguments provided by the Field. + * {arguments} must contain exactly one entry. + * For the sole {argument} in {arguments}: + * Let {value} be the value of {argument}. + * {value} must not be the {null} literal. + +**Explanatory Text** + +Oneof Fields require that exactly one argument must be supplied and that +argument must not be null. + ## Fragments ### Fragment Declarations From b6741c3e5327573d35f51dbbbb38059ed8799884 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 26 Feb 2021 09:32:34 +0000 Subject: [PATCH 04/26] Fix typos (thanks @eapache!) --- spec/Section 3 -- Type System.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 3713da402..15bf8389b 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -819,8 +819,8 @@ of rules must be adhered to by every Object type in a GraphQL schema. 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. 3. If the field is a Oneof Field: - 1. The field must be nullable. - 2. The field must not have a default value. + 1. The argument must be nullable. + 2. The argument must not have a default value. 3. An object type may declare that it implements one or more unique interfaces. 4. An object type must be a super-set of all interfaces it implements: 1. Let this object type be {objectType}. @@ -1190,8 +1190,8 @@ Interface types have the potential to be invalid if incorrectly defined. 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. 3. If the field is a Oneof Field: - 1. The field must be nullable. - 2. The field must not have a default value. + 1. The argument must be nullable. + 2. The argument must not have a default value. 3. An interface type may declare that it implements one or more unique interfaces, but may not implement itself. 4. An interface type must be a super-set of all interfaces it implements: From d17d5ec701c62254b660043682421f70334f82fa Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 6 Mar 2021 11:44:00 +0000 Subject: [PATCH 05/26] Much stricter validation for oneof literals (with examples) --- spec/Section 5 -- Validation.md | 121 ++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 7 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 5cb8f9ba4..434cf5152 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -40,6 +40,10 @@ type Query { dog: Dog } +type Mutation { + addPet(pet: PetInput): Pet +} + enum DogCommand { SIT, DOWN, HEEL } type Dog implements Pet { @@ -81,6 +85,23 @@ type Cat implements Pet { union CatOrDog = Cat | Dog union DogOrHuman = Dog | Human union HumanOrAlien = Human | Alien + +input CatInput { + name: String! + nickname: String + meowVolume: Int +} + +input DogInput { + name: String! + nickname: String + barkVolume: Int +} + +input PetInput @oneOf { + cat: CatInput + dog: DogInput +} ``` @@ -1442,17 +1463,103 @@ input object field is optional. **Formal Specification** -* For each Oneof Input Object in the document: - * Let {fields} be the fields provided by that Oneof Input Object. - * {fields} must contain exactly one entry. - * For the sole {field} in {fields}: - * Let {value} be the value of {field}. - * {value} must not be the {null} literal. +* 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 not be empty. + * Let {literalFields} be the entries in {fields} that have literal values. + * If {literalFields} is not empty: + * {literalFields} must contain exactly one entry. + * {fields} must contain exactly one entry. + * For the sole {field} in {literalFields}: + * Let {value} be the value of {field}. + * {value} must not be the {null} literal. + * Otherwise: + * Let {variableUsages} be all variable usages within {oneofInputObject} directly. + * Assert: {variableUsages} is not empty. + * For each {variableUsage} in {variableUsages}: + * Let {variableName} be the name of {variableUsage}. + * Let {variableDefinition} be the {VariableDefinition} named {variableName} defined within {operation}. + * Let {variableType} be the expected type of {variableDefinition}. + * If {variableType} is a non-null type: + * {fields} must contain exactly one entry. **Explanatory Text** Oneof Input Objects require that exactly one field must be supplied and that -field must not be null. +field must not be {null}. + +An empty Oneof Input Object is invalid. + +```graphgl counter-example +query addPet { + addPet(pet: {}) { + name + } +} +``` + +Multiple fields of a Oneof Input Object may be specified via variables (at most +one of which may be provided at run time), thus we allow multiple variable +fields. + +```graphgl example +query addPet($cat: CatInput, $dog: DogInput) { + addPet(pet: {cat: $cat, dog: $dog}) { + name + } +} +``` + +If a field of a Oneof Input Object is specified via a non-nullable variable, no +other fields may be specified: + +```graphgl example +query addPet($cat: CatInput!) { + addPet(pet: { cat: $cat }) { + name + } +} +``` + +```graphgl counter-example +query addPet($cat: CatInput!, $dog: DogInput) { + addPet(pet: { cat: $cat, dog: $dog }) { + name + } +} +``` + +If a field with a literal value is present then the value must +not be {null} and no fields with variable values are allowed. + +```graphgl example +query addPet { + addPet(pet: { cat: { name: "Brontie" } }) { + name + } +} +``` + +```graphgl counter-example +query addPet { + addPet(pet: { cat: null }) { + name + } +} +``` + +```graphgl counter-example +query addPet($dog: DogInput) { + addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) { + name + } +} +``` + +Note: When the fields of a Oneof Input Object are supplied via variables, we +assert that the input object has exactly one field during coercion. ## Directives From dca382698779475ae5d3cac8d4964df19b3df10b Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 6 Mar 2021 11:54:52 +0000 Subject: [PATCH 06/26] Add missing coercion rule --- spec/Section 3 -- Type System.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 15bf8389b..0e493b57e 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1595,6 +1595,9 @@ is constructed with the following rules: definition does not provide a default value, the input object field definition's default value should be used. +* If the input object is a oneof input object, the map must contain exactly one + entry, and the value of that entry must not be {null}. + Following are examples of input coercion for an input object type with a `String` field `a` and a required (non-null) `Int!` field `b`: From 7e02f5aff9d143ab484fc68fad1674c5a86c5e9a Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 6 Mar 2021 11:58:37 +0000 Subject: [PATCH 07/26] Clearer wording of oneof coercion rule --- spec/Section 3 -- Type System.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 0e493b57e..40982810c 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1595,8 +1595,13 @@ is constructed with the following rules: definition does not provide a default value, the input object field definition's default value should be used. -* If the input object is a oneof input object, the map must contain exactly one - entry, and the value of that entry must not be {null}. +* If the input object is a Oneof Input Object: + + * If the coerced unordered map does not contain exactly one entry, an error + must be thrown. + + * If the value of the single entry in the coerced unordered map is {null}, an + error must be thrown. Following are examples of input coercion for an input object type with a `String` field `a` and a required (non-null) `Int!` field `b`: From 4111476cd07e58bc9ee87741ad73cd06f42d544d Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 6 Mar 2021 12:07:44 +0000 Subject: [PATCH 08/26] Add more examples for clarity --- spec/Section 3 -- Type System.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 40982810c..a059c0367 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1662,6 +1662,11 @@ Literal Value | Variables | Coerced Value `{ a: "abc", b: null }` | `{}` | Error: Exactly one key must be specified `{ b: $var }` | `{ var: null }` | Error: Value for member field {b} must be non-null `{ b: 123, c: "xyz" }` | `{}` | Error: Exactly one key must be specified +`{ a: $a, b: $b }` | `{}` | Error: Exactly one key must be specified +`{ a: $a, b: $b }` | `{ a: "abc" }` | `{ a: "abc" }` +`{ a: $a, b: $b }` | `{ b: 123 }` | `{ b: 123 }` +`{ a: $a, b: $b }` | `{ a: "abc", b: 123 }` | Error: Exactly one key must be specified +`{ a: $a, b: $b }` | `{ a: null, b: 123 }` | Error: Exactly one key must be specified **Type Validation** From 6754e0a018545d1f8e423fb15cd35462c5c42397 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 6 Mar 2021 12:11:36 +0000 Subject: [PATCH 09/26] Rename introspection fields to oneOf --- spec/Section 3 -- Type System.md | 6 +++--- spec/Section 4 -- Introspection.md | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index a059c0367..c74c9558a 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -944,8 +944,8 @@ type Query { } ``` -In schema introspection, the `__Field.oneArgument` field will return {true} for -Oneof Fields, and {false} for all other Fields. +In schema introspection, the `__Field.oneOf` field will return {true} for Oneof +Fields, and {false} for all other Fields. ### Field Deprecation @@ -1499,7 +1499,7 @@ input UserUniqueCondition @oneOf { } ``` -In schema introspection, the `__Type.oneField` field will return {true} for +In schema introspection, the `__Type.oneOf` field will return {true} for Oneof Input Objects, and {false} for all other Input Objects. **Circular References** diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 5b2f7fdb0..ca7ffb95d 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -149,7 +149,7 @@ type __Type { ofType: __Type # should be non-null for INPUT_OBJECT only - oneField: Boolean + oneOf: Boolean } type __Field { @@ -159,7 +159,7 @@ type __Field { type: __Type! isDeprecated: Boolean! deprecationReason: String - oneArgument: Boolean! + oneOf: Boolean! } type __InputValue { @@ -340,7 +340,7 @@ Fields * `name` must return a String. * `description` may return a String or {null}. * `inputFields`: a list of `InputValue`. -* `oneField` must return {true} for Oneof Input Objects, {false} for all other +* `oneOf` must return {true} for Oneof Input Objects, {false} for all other Input Objects. * All other fields must return {null}. @@ -386,8 +386,7 @@ Fields * `isDeprecated` returns {true} if this field should no longer be used, otherwise {false}. * `deprecationReason` optionally provides a reason why this field is deprecated. -* `oneArgument` must return {true} for Oneof Fields, {false} for all other - Fields. +* `oneOf` must return {true} for Oneof Fields, {false} for all other Fields. ### The __InputValue Type From 7c4c1a2bbe6d10b29433eeb2b3628f4982321f76 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 6 Mar 2021 12:54:33 +0000 Subject: [PATCH 10/26] Oneof's now require exactly one field/argument, and non-nullable variables. --- spec/Section 3 -- Type System.md | 27 +++++----- spec/Section 5 -- Validation.md | 85 +++++++++++++++++--------------- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index c74c9558a..b90d6b6f0 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1595,13 +1595,20 @@ is constructed with the following rules: definition does not provide a default value, the input object field definition's default value should be used. -* If the input object is a Oneof Input Object: +Further, if the input object is a Oneof Input Object, the following additional +rules apply: - * If the coerced unordered map does not contain exactly one entry, an error - must be thrown. +* If the input object literal or unordered map does not contain exactly one + entry, an error must be thrown. - * If the value of the single entry in the coerced unordered map is {null}, an - error must be thrown. +* If the single entry in the input object literal or unordered map is {null}, + an error must be thrown. + +* If the coerced unordered map does not contain exactly one entry, an error + must be thrown. + +* If the value of the single entry in the coerced unordered map is {null}, an + error must be thrown. Following are examples of input coercion for an input object type with a `String` field `a` and a required (non-null) `Int!` field `b`: @@ -1649,7 +1656,7 @@ Literal Value | Variables | Coerced Value `{ a: null, b: 123 }` | `{}` | Error: Exactly one key must be specified `{ b: 123 }` | `{}` | `{ b: 123 }` `{ a: $var, b: 123 }` | `{ var: null }` | Error: Exactly one key must be specified -`{ a: $var, b: 123 }` | `{}` | `{ b: 123 }` +`{ a: $var, b: 123 }` | `{}` | Error: Exactly one key must be specified `{ b: $var }` | `{ var: 123 }` | `{ b: 123 }` `$var` | `{ var: { b: 123 } }` | `{ b: 123 }` `"abc123"` | `{}` | Error: Incorrect value @@ -1657,16 +1664,12 @@ Literal Value | Variables | Coerced Value `{ a: "abc", b: "123" }` | `{}` | Error: Exactly one key must be specified `{ b: "123" }` | `{}` | Error: Incorrect value for member field {b} `{ a: "abc" }` | `{}` | `{ a: "abc" }` -`{ b: $var }` | `{}` | Error: No keys were specified +`{ b: $var }` | `{}` | Error: Exactly one key must be specified `$var` | `{ var: { a: "abc" } }` | `{ a: "abc" }` `{ a: "abc", b: null }` | `{}` | Error: Exactly one key must be specified `{ b: $var }` | `{ var: null }` | Error: Value for member field {b} must be non-null `{ b: 123, c: "xyz" }` | `{}` | Error: Exactly one key must be specified -`{ a: $a, b: $b }` | `{}` | Error: Exactly one key must be specified -`{ a: $a, b: $b }` | `{ a: "abc" }` | `{ a: "abc" }` -`{ a: $a, b: $b }` | `{ b: 123 }` | `{ b: 123 }` -`{ a: $a, b: $b }` | `{ a: "abc", b: 123 }` | Error: Exactly one key must be specified -`{ a: $a, b: $b }` | `{ a: null, b: 123 }` | Error: Exactly one key must be specified +`{ a: $a, b: $b }` | `{ a: "abc" }` | Error: Exactly one key must be specified **Type Validation** diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 434cf5152..b928546e0 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -803,12 +803,19 @@ fragment missingRequiredArg on Arguments { #### Oneof Fields Have Exactly One Argument -* For each Oneof Field in the document: - * Let {arguments} be the arguments provided by the Field. - * {arguments} must contain exactly one entry. - * For the sole {argument} in {arguments}: +* For each {operation} in {document}: + * Let {oneofFields} be all Oneof Fields transitively included in the {operation}. + * For each {oneofField} in {oneofFields}: + * Let {arguments} be the arguments provided by {oneofField}. + * {arguments} must contain exactly one entry. + * Let {argument} be the sole entry in {arguments}. * Let {value} be the value of {argument}. * {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** @@ -1467,23 +1474,15 @@ input object field is optional. * 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 not be empty. - * Let {literalFields} be the entries in {fields} that have literal values. - * If {literalFields} is not empty: - * {literalFields} must contain exactly one entry. - * {fields} must contain exactly one entry. - * For the sole {field} in {literalFields}: - * Let {value} be the value of {field}. - * {value} must not be the {null} literal. - * Otherwise: - * Let {variableUsages} be all variable usages within {oneofInputObject} directly. - * Assert: {variableUsages} is not empty. - * For each {variableUsage} in {variableUsages}: - * Let {variableName} be the name of {variableUsage}. - * Let {variableDefinition} be the {VariableDefinition} named {variableName} defined within {operation}. - * Let {variableType} be the expected type of {variableDefinition}. - * If {variableType} is a non-null type: - * {fields} must contain exactly one entry. + * {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** @@ -1500,11 +1499,9 @@ query addPet { } ``` -Multiple fields of a Oneof Input Object may be specified via variables (at most -one of which may be provided at run time), thus we allow multiple variable -fields. +Multiple fields are not allowed. -```graphgl example +```graphgl counter-example query addPet($cat: CatInput, $dog: DogInput) { addPet(pet: {cat: $cat, dog: $dog}) { name @@ -1512,8 +1509,24 @@ query addPet($cat: CatInput, $dog: DogInput) { } ``` -If a field of a Oneof Input Object is specified via a non-nullable variable, no -other fields may be specified: +```graphgl counter-example +query addPet($dog: DogInput) { + addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) { + name + } +} +``` + +```graphgl counter-example +query addPet { + addPet(pet: { cat: { name: "Brontie" }, dog: null }) { + name + } +} +``` + + +Variables used for Oneof Input Object fields must be non-nullable. ```graphgl example query addPet($cat: CatInput!) { @@ -1524,15 +1537,16 @@ query addPet($cat: CatInput!) { ``` ```graphgl counter-example -query addPet($cat: CatInput!, $dog: DogInput) { - addPet(pet: { cat: $cat, dog: $dog }) { +query addPet($cat: CatInput) { + addPet(pet: { cat: $cat }) { name } } ``` + If a field with a literal value is present then the value must -not be {null} and no fields with variable values are allowed. +not be {null}. ```graphgl example query addPet { @@ -1550,17 +1564,6 @@ query addPet { } ``` -```graphgl counter-example -query addPet($dog: DogInput) { - addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) { - name - } -} -``` - -Note: When the fields of a Oneof Input Object are supplied via variables, we -assert that the input object has exactly one field during coercion. - ## Directives From bb225f77781adfc23df14e391db5ee4a01d1eb6a Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Sat, 6 Mar 2021 13:09:06 +0000 Subject: [PATCH 11/26] Remove extraneous newline --- spec/Section 5 -- Validation.md | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index b928546e0..428acdf12 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1564,7 +1564,6 @@ query addPet { } ``` - ## Directives From 05fde061b5acb44fdb4bfe0c809fcaf673c7a0d0 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 8 Apr 2021 12:21:42 +0100 Subject: [PATCH 12/26] graphgl -> graphql --- 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 428acdf12..e80708f53 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1491,7 +1491,7 @@ field must not be {null}. An empty Oneof Input Object is invalid. -```graphgl counter-example +```graphql counter-example query addPet { addPet(pet: {}) { name @@ -1501,7 +1501,7 @@ query addPet { Multiple fields are not allowed. -```graphgl counter-example +```graphql counter-example query addPet($cat: CatInput, $dog: DogInput) { addPet(pet: {cat: $cat, dog: $dog}) { name @@ -1509,7 +1509,7 @@ query addPet($cat: CatInput, $dog: DogInput) { } ``` -```graphgl counter-example +```graphql counter-example query addPet($dog: DogInput) { addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) { name @@ -1517,7 +1517,7 @@ query addPet($dog: DogInput) { } ``` -```graphgl counter-example +```graphql counter-example query addPet { addPet(pet: { cat: { name: "Brontie" }, dog: null }) { name @@ -1528,7 +1528,7 @@ query addPet { Variables used for Oneof Input Object fields must be non-nullable. -```graphgl example +```graphql example query addPet($cat: CatInput!) { addPet(pet: { cat: $cat }) { name @@ -1536,7 +1536,7 @@ query addPet($cat: CatInput!) { } ``` -```graphgl counter-example +```graphql counter-example query addPet($cat: CatInput) { addPet(pet: { cat: $cat }) { name @@ -1548,7 +1548,7 @@ query addPet($cat: CatInput) { If a field with a literal value is present then the value must not be {null}. -```graphgl example +```graphql example query addPet { addPet(pet: { cat: { name: "Brontie" } }) { name @@ -1556,7 +1556,7 @@ query addPet { } ``` -```graphgl counter-example +```graphql counter-example query addPet { addPet(pet: { cat: null }) { name From e8f6145dacfe96ec9a4e9e3ee599b225801371f4 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 8 Apr 2021 12:33:09 +0100 Subject: [PATCH 13/26] Apply suggestions from @eapache's review --- spec/Section 3 -- Type System.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index b90d6b6f0..751a64192 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -926,8 +926,8 @@ Object, Interface, or Union type). Oneof Fields are a special variant of Object Type fields where the type system asserts that exactly one of the field's arguments must be set and non-null, all -others being omitted. This is useful for representing situations where an input -may be one of many different options. +others being omitted. This is useful for representing situations where a field +provides more than one input option to accomplish the same (or similar) goal. When using the type system definition language, the `@oneOf` directive is used to indicate that a Field is a Oneof Field (and thus requires exactly one of its @@ -1664,7 +1664,7 @@ Literal Value | Variables | Coerced Value `{ a: "abc", b: "123" }` | `{}` | Error: Exactly one key must be specified `{ b: "123" }` | `{}` | Error: Incorrect value for member field {b} `{ a: "abc" }` | `{}` | `{ a: "abc" }` -`{ b: $var }` | `{}` | Error: Exactly one key must be specified +`{ b: $var }` | `{}` | Error: Value for member field {b} must be specified `$var` | `{ var: { a: "abc" } }` | `{ a: "abc" }` `{ a: "abc", b: null }` | `{}` | Error: Exactly one key must be specified `{ b: $var }` | `{ var: null }` | Error: Value for member field {b} must be non-null @@ -1927,9 +1927,7 @@ provide: - the `@deprecated` directive if representing deprecated portions of the schema; -- the `@oneOf` directive if representing types that require exactly one field - (i.e. Oneof Input Objects) or fields that require exactly one argument (i.e. - Oneof Fields). +- the `@oneOf` directive if the schema contains Oneof Input Objects or Oneof Fields. **Custom Directives** From 08abf496828c4786c21f2d2c0cf01de816c441c3 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 23 Dec 2021 14:17:11 +0000 Subject: [PATCH 14/26] Apply suggestions from code review Co-authored-by: Michael Staib --- 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 e80708f53..8a18af50c 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1492,7 +1492,7 @@ field must not be {null}. An empty Oneof Input Object is invalid. ```graphql counter-example -query addPet { +mutation addPet { addPet(pet: {}) { name } @@ -1502,7 +1502,7 @@ query addPet { Multiple fields are not allowed. ```graphql counter-example -query addPet($cat: CatInput, $dog: DogInput) { +mutation addPet($cat: CatInput, $dog: DogInput) { addPet(pet: {cat: $cat, dog: $dog}) { name } @@ -1510,7 +1510,7 @@ query addPet($cat: CatInput, $dog: DogInput) { ``` ```graphql counter-example -query addPet($dog: DogInput) { +mutation addPet($dog: DogInput) { addPet(pet: { cat: { name: "Brontie" }, dog: $dog }) { name } @@ -1518,7 +1518,7 @@ query addPet($dog: DogInput) { ``` ```graphql counter-example -query addPet { +mutation addPet { addPet(pet: { cat: { name: "Brontie" }, dog: null }) { name } @@ -1529,7 +1529,7 @@ query addPet { Variables used for Oneof Input Object fields must be non-nullable. ```graphql example -query addPet($cat: CatInput!) { +mutation addPet($cat: CatInput!) { addPet(pet: { cat: $cat }) { name } @@ -1537,7 +1537,7 @@ query addPet($cat: CatInput!) { ``` ```graphql counter-example -query addPet($cat: CatInput) { +mutation addPet($cat: CatInput) { addPet(pet: { cat: $cat }) { name } @@ -1549,7 +1549,7 @@ If a field with a literal value is present then the value must not be {null}. ```graphql example -query addPet { +mutation addPet { addPet(pet: { cat: { name: "Brontie" } }) { name } @@ -1557,7 +1557,7 @@ query addPet { ``` ```graphql counter-example -query addPet { +mutation addPet { addPet(pet: { cat: null }) { name } From 59cb12d0670f258d9e4fb974c2545501d1911e7b Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Tue, 4 Jan 2022 18:40:24 +0000 Subject: [PATCH 15/26] Update spec/Section 3 -- Type System.md --- spec/Section 3 -- Type System.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 751a64192..8635aaec2 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1668,7 +1668,7 @@ Literal Value | Variables | Coerced Value `$var` | `{ var: { a: "abc" } }` | `{ a: "abc" }` `{ a: "abc", b: null }` | `{}` | Error: Exactly one key must be specified `{ b: $var }` | `{ var: null }` | Error: Value for member field {b} must be non-null -`{ b: 123, c: "xyz" }` | `{}` | Error: Exactly one key must be specified +`{ b: 123, c: "xyz" }` | `{}` | Error: Unexpected field {c} `{ a: $a, b: $b }` | `{ a: "abc" }` | Error: Exactly one key must be specified **Type Validation** From 99aa5d935eddeb001f6aa3feb8a9134f9cc844a6 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Tue, 22 Mar 2022 16:00:28 +0000 Subject: [PATCH 16/26] Remove Oneof Fields from spec --- spec/Section 3 -- Type System.md | 45 +++--------------------------- spec/Section 4 -- Introspection.md | 6 ++-- spec/Section 5 -- Validation.md | 23 --------------- 3 files changed, 6 insertions(+), 68 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 27551fcef..3edc69899 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -879,9 +879,6 @@ of rules must be adhered to by every Object type in a GraphQL schema. {"\_\_"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. - 3. If the field is a Oneof Field: - 1. The argument must be nullable. - 2. The argument must not have a default value. 3. An object type may declare that it implements one or more unique interfaces. 4. An object type must be a super-set of all interfaces it implements: 1. Let this object type be {objectType}. @@ -909,8 +906,6 @@ IsValidImplementation(type, implementedType): 2. Let {implementedFieldType} be the return type of {implementedField}. 3. {IsValidImplementationFieldType(fieldType, implementedFieldType)} must be {true}. - 6. {field} must be a Oneof Field if and only if {implementedField} is a - Oneof Field. IsValidImplementationFieldType(fieldType, implementedFieldType): @@ -984,31 +979,6 @@ May return the result: The type of an object field argument must be an input type (any type except an Object, Interface, or Union type). -**Oneof Fields** - -Oneof Fields are a special variant of Object Type fields where the type system -asserts that exactly one of the field's arguments must be set and non-null, all -others being omitted. This is useful for representing situations where a field -provides more than one input option to accomplish the same (or similar) goal. - -When using the type system definition language, the `@oneOf` directive is used -to indicate that a Field is a Oneof Field (and thus requires exactly one of its -arguments be provided): - -```graphql -type Query { - findUser( - byID: ID - byUsername: String - byEmail: String - byRegistrationNumber: Int - ): User @oneOf -} -``` - -In schema introspection, the `__Field.oneOf` field will return {true} for Oneof -Fields, and {false} for all other Fields. - ### Field Deprecation Fields in an object may be marked as deprecated as deemed necessary by the @@ -1254,9 +1224,6 @@ Interface types have the potential to be invalid if incorrectly defined. {"\_\_"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. - 3. If the field is a Oneof Field: - 1. The argument must be nullable. - 2. The argument must not have a default value. 3. An interface type may declare that it implements one or more unique interfaces, but may not implement itself. 4. An interface type must be a super-set of all interfaces it implements: @@ -1712,7 +1679,7 @@ input ExampleInputObject { | `{ b: $var }` | `{ var: null }` | Error: {b} must be non-null. | | `{ b: 123, c: "xyz" }` | `{}` | Error: Unexpected field {c} | -Following are examples of input coercion for a Oneof Input Object with a +Following are examples of input coercion for a oneOf input object type with a `String` member field `a` and an `Int` member field `b`: ```graphql example @@ -2007,8 +1974,7 @@ GraphQL implementations that support the type system definition language should provide the `@specifiedBy` directive if representing custom scalar definitions. GraphQL implementations that support the type system definition language should -provide the `@oneOf` directive if the schema contains Oneof Input Objects or -Oneof Fields. +provide the `@oneOf` directive if representing Oneof Input Objects. When representing a GraphQL schema using the type system definition language any _built-in directive_ may be omitted for brevity. @@ -2201,14 +2167,11 @@ scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") ### @oneOf ```graphql -directive @oneOf on INPUT_OBJECT | FIELD_DEFINITION +directive @oneOf on INPUT_OBJECT ``` The `@oneOf` directive is used within the type system definition language to -indicate: - -- an Input Object is a Oneof Input Object, or -- an Object Type's Field is a Oneof Field. +indicate an Input Object is a Oneof Input Object. ```graphql example input UserUniqueCondition @oneOf { diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index b197daa26..69f850b0a 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -172,7 +172,6 @@ type __Field { type: __Type! isDeprecated: Boolean! deprecationReason: String - oneOf: Boolean! } type __InputValue { @@ -370,8 +369,8 @@ Fields\: - `name` must return a String. - `description` may return a String or {null}. - `inputFields` must return the set of input fields as a list of `__InputValue`. -- `oneOf` must return {true} for Oneof Input Objects, {false} for all other - Input Objects. +- `oneOf` must return {true} when representing a Oneof Input Object, {false} + otherwise. - All other fields must return {null}. **List** @@ -422,7 +421,6 @@ Fields\: - `isDeprecated` returns {true} if this field should no longer be used, otherwise {false}. - `deprecationReason` optionally provides a reason why this field is deprecated. -- `oneOf` must return {true} for Oneof Fields, {false} for all other Fields. ### The \_\_InputValue Type diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index af886366e..1192aba98 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -804,29 +804,6 @@ fragment missingRequiredArg on Arguments { } ``` -#### Oneof Fields Have Exactly One Argument - -- For each {operation} in {document}: - - Let {oneofFields} be all Oneof Fields transitively included in the - {operation}. - - For each {oneofField} in {oneofFields}: - - Let {arguments} be the arguments provided by {oneofField}. - - {arguments} must contain exactly one entry. - - Let {argument} be the sole entry in {arguments}. - - Let {value} be the value of {argument}. - - {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 Fields require that exactly one argument must be supplied and that -argument must not be null. - ## Fragments ### Fragment Declarations From 691087d93ce8c00ab615acf4a4ce694bec542c64 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Tue, 22 Mar 2022 16:03:42 +0000 Subject: [PATCH 17/26] Oneof -> OneOf --- spec/Section 3 -- Type System.md | 18 +++++++++--------- spec/Section 4 -- Introspection.md | 2 +- spec/Section 5 -- Validation.md | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 3edc69899..18e976079 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1520,15 +1520,15 @@ define arguments or contain references to interfaces and unions, neither of which is appropriate for use as an input argument. For this reason, input objects have a separate type in the system. -**Oneof Input Objects** +**OneOf Input Objects** -Oneof Input Objects are a special variant of Input Objects where the type system +OneOf Input Objects are a special variant of Input Objects where the type system asserts that exactly one of the fields must be set and non-null, all others being omitted. This is useful for representing situations where an input may be one of many different options. When using the type system definition language, the `@oneOf` directive is used -to indicate that an Input Object is a Oneof Input Object (and thus requires +to indicate that an Input Object is a OneOf Input Object (and thus requires exactly one of its field be provided): ```graphql @@ -1539,7 +1539,7 @@ input UserUniqueCondition @oneOf { } ``` -In schema introspection, the `__Type.oneOf` field will return {true} for Oneof +In schema introspection, the `__Type.oneOf` field will return {true} for OneOf Input Objects, and {false} for all other Input Objects. **Circular References** @@ -1635,7 +1635,7 @@ is constructed with the following rules: does not provide a default value, the input object field definition's default value should be used. -Further, if the input object is a Oneof Input Object, the following additional +Further, if the input object is a OneOf Input Object, the following additional rules apply: - If the input object literal or unordered map does not contain exactly one @@ -1720,7 +1720,7 @@ input ExampleInputTagged @oneOf { {"\_\_"} (two underscores). 3. The input field must accept a type where {IsInputType(inputFieldType)} returns {true}. - 4. If the Input Object is a Oneof Input Object then: + 4. If the Input Object is a OneOf Input Object then: 1. The type of the input field must be nullable. 2. The input field must not have a default value. 3. If an Input Object references itself either directly or through referenced @@ -1750,7 +1750,7 @@ defined. the original Input Object. 4. Any non-repeatable directives provided must not already apply to the original Input Object type. -5. If the original Input Object is a Oneof Input Object then: +5. If the original Input Object is a OneOf Input Object then: 1. All fields of the Input Object type extension must be nullable. 2. All fields of the Input Object type extension must not have default values. @@ -1974,7 +1974,7 @@ GraphQL implementations that support the type system definition language should provide the `@specifiedBy` directive if representing custom scalar definitions. GraphQL implementations that support the type system definition language should -provide the `@oneOf` directive if representing Oneof Input Objects. +provide the `@oneOf` directive if representing OneOf Input Objects. When representing a GraphQL schema using the type system definition language any _built-in directive_ may be omitted for brevity. @@ -2171,7 +2171,7 @@ directive @oneOf on INPUT_OBJECT ``` The `@oneOf` directive is used within the type system definition language to -indicate an Input Object is a Oneof Input Object. +indicate an Input Object is a OneOf Input Object. ```graphql example input UserUniqueCondition @oneOf { diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 69f850b0a..5d0a793e3 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -369,7 +369,7 @@ Fields\: - `name` must return a String. - `description` may return a String or {null}. - `inputFields` must return the set of input fields as a list of `__InputValue`. -- `oneOf` must return {true} when representing a Oneof Input Object, {false} +- `oneOf` must return {true} when representing a OneOf Input Object, {false} otherwise. - All other fields must return {null}. diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 1192aba98..c5b4b9a12 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -1427,12 +1427,12 @@ 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 +### OneOf Input Objects Have Exactly One Field **Formal Specification** - For each {operation} in {document}: - - Let {oneofInputObjects} be all Oneof Input Objects transitively included in + - 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}. @@ -1449,10 +1449,10 @@ input object field is optional. **Explanatory Text** -Oneof Input Objects require that exactly one field must be supplied and that +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. +An empty OneOf Input Object is invalid. ```graphql counter-example mutation addPet { @@ -1488,7 +1488,7 @@ mutation addPet { } ``` -Variables used for Oneof Input Object fields must be non-nullable. +Variables used for OneOf Input Object fields must be non-nullable. ```graphql example mutation addPet($cat: CatInput!) { From 7109dbcb1855527c5aa7077d8468efd4822b8c80 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Tue, 22 Mar 2022 16:51:06 +0000 Subject: [PATCH 18/26] Spellings --- cspell.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cspell.yml b/cspell.yml index e8aa73355..08c453732 100644 --- a/cspell.yml +++ b/cspell.yml @@ -21,3 +21,5 @@ words: - tatooine - zuck - zuckerberg + - brontie + - oneOf From 05ab541cf3da11b5d0a447b7ffc7e8e37bde427c Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 6 May 2022 15:23:58 +0100 Subject: [PATCH 19/26] Remove out of date example --- spec/Section 3 -- Type System.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 18e976079..f381894f1 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -2180,14 +2180,3 @@ input UserUniqueCondition @oneOf { organizationAndEmail: OrganizationAndEmailInput } ``` - -```graphql example -type Query { - findUser( - byID: ID - byUsername: String - byEmail: String - byRegistrationNumber: Int - ): User @oneOf -} -``` From 6a6be52d47fb7fc828d758dfc659a8198b9f6b0f Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 25 May 2022 10:40:44 +0100 Subject: [PATCH 20/26] Rename __Type.oneOf to __Type.isOneOf --- spec/Section 3 -- Type System.md | 2 +- spec/Section 4 -- Introspection.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index f381894f1..fc3f6117e 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1539,7 +1539,7 @@ input UserUniqueCondition @oneOf { } ``` -In schema introspection, the `__Type.oneOf` field will return {true} for OneOf +In schema introspection, the `__Type.isOneOf` field will return {true} for OneOf Input Objects, and {false} for all other Input Objects. **Circular References** diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 5d0a793e3..327665dde 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -151,7 +151,7 @@ type __Type { # may be non-null for custom SCALAR, otherwise null. specifiedByURL: String # should be non-null for INPUT_OBJECT only - oneOf: Boolean + isOneOf: Boolean } enum __TypeKind { @@ -369,7 +369,7 @@ Fields\: - `name` must return a String. - `description` may return a String or {null}. - `inputFields` must return the set of input fields as a list of `__InputValue`. -- `oneOf` must return {true} when representing a OneOf Input Object, {false} +- `isOneOf` must return {true} when representing a OneOf Input Object, {false} otherwise. - All other fields must return {null}. From de87d2f9734c4d3623ae1e0e87bd831b6f499473 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 25 May 2022 17:40:59 +0100 Subject: [PATCH 21/26] Add a:null example --- spec/Section 3 -- Type System.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index fc3f6117e..756d68155 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1693,6 +1693,7 @@ input ExampleInputTagged @oneOf { | ------------------------ | ----------------------- | --------------------------------------------------- | | `{ a: "abc", b: 123 }` | `{}` | Error: Exactly one key must be specified | | `{ a: null, b: 123 }` | `{}` | Error: Exactly one key must be specified | +| `{ a: null }` | `{}` | Error: Value for member field {a} must be non-null | | `{ b: 123 }` | `{}` | `{ b: 123 }` | | `{ a: $var, b: 123 }` | `{ var: null }` | Error: Exactly one key must be specified | | `{ a: $var, b: 123 }` | `{}` | Error: Exactly one key must be specified | From 57e23889a53a3524d598a0708d9e9fed892ee81f Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 25 May 2022 17:47:14 +0100 Subject: [PATCH 22/26] Rewrite to avoid ambiguity of language --- spec/Section 3 -- Type System.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 756d68155..eda0f0eae 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1639,15 +1639,15 @@ Further, if the input object is a OneOf Input Object, the following additional rules apply: - If the input object literal or unordered map does not contain exactly one - entry, an error must be thrown. + entry an error must be thrown. -- If the single entry in the input object literal or unordered map is {null}, an - error must be thrown. +- Within the input object literal or unordered map, if the single entry is + {null} an error must be thrown. -- If the coerced unordered map does not contain exactly one entry, an error must +- If the coerced unordered map does not contain exactly one entry an error must be thrown. -- If the value of the single entry in the coerced unordered map is {null}, an +- If the value of the single entry in the coerced unordered map is {null} an error must be thrown. Following are examples of input coercion for an input object type with a From 5a966f2a7514b58acd8c5d6bb3f247a9ca38d822 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 26 May 2022 10:06:50 +0100 Subject: [PATCH 23/26] Forbid 'extend input' from introducing the @oneOf directive --- spec/Section 3 -- Type System.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index eda0f0eae..1e5ef99b6 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1751,7 +1751,9 @@ defined. the original Input Object. 4. Any non-repeatable directives provided must not already apply to the original Input Object type. -5. If the original Input Object is a OneOf Input Object then: +5. The `@oneOf` directive must not be provided by an Input Object type + extension. +6. If the original Input Object is a OneOf Input Object then: 1. All fields of the Input Object type extension must be nullable. 2. All fields of the Input Object type extension must not have default values. From d10623323ad0b15f462c11aa0e8b712677143dab Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Wed, 27 Mar 2024 16:12:16 +0000 Subject: [PATCH 24/26] Add yet more examples to the example coercion table --- spec/Section 3 -- Type System.md | 51 +++++++++++++++++++------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 7136b3d85..aceee0f7d 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1735,27 +1735,36 @@ input ExampleInputTagged @oneOf { } ``` -| Literal Value | Variables | Coerced Value | -| ------------------------ | ----------------------- | --------------------------------------------------- | -| `{ a: "abc", b: 123 }` | `{}` | Error: Exactly one key must be specified | -| `{ a: null, b: 123 }` | `{}` | Error: Exactly one key must be specified | -| `{ a: null }` | `{}` | Error: Value for member field {a} must be non-null | -| `{ b: 123 }` | `{}` | `{ b: 123 }` | -| `{ a: $var, b: 123 }` | `{ var: null }` | Error: Exactly one key must be specified | -| `{ a: $var, b: 123 }` | `{}` | Error: Exactly one key must be specified | -| `{ b: $var }` | `{ var: 123 }` | `{ b: 123 }` | -| `$var` | `{ var: { b: 123 } }` | `{ b: 123 }` | -| `"abc123"` | `{}` | Error: Incorrect value | -| `$var` | `{ var: "abc123" } }` | Error: Incorrect value | -| `{ a: "abc", b: "123" }` | `{}` | Error: Exactly one key must be specified | -| `{ b: "123" }` | `{}` | Error: Incorrect value for member field {b} | -| `{ a: "abc" }` | `{}` | `{ a: "abc" }` | -| `{ b: $var }` | `{}` | Error: Value for member field {b} must be specified | -| `$var` | `{ var: { a: "abc" } }` | `{ a: "abc" }` | -| `{ a: "abc", b: null }` | `{}` | Error: Exactly one key must be specified | -| `{ b: $var }` | `{ var: null }` | Error: Value for member field {b} must be non-null | -| `{ b: 123, c: "xyz" }` | `{}` | Error: Unexpected field {c} | -| `{ a: $a, b: $b }` | `{ a: "abc" }` | Error: Exactly one key must be specified | +| Literal Value | Variables | Coerced Value | +| ------------------------ | -------------------------------- | --------------------------------------------------- | +| `{ a: "abc", b: 123 }` | `{}` | Error: Exactly one key must be specified | +| `{ a: null, b: 123 }` | `{}` | Error: Exactly one key must be specified | +| `{ a: null, b: null }` | `{}` | Error: Exactly one key must be specified | +| `{ a: null }` | `{}` | Error: Value for member field {a} must be non-null | +| `{ b: 123 }` | `{}` | `{ b: 123 }` | +| `{}` | `{}` | Error: Exactly one key must be specified | +| `{ a: $a, b: 123 }` | `{ a: null }` | Error: Exactly one key must be specified | +| `{ a: $a, b: 123 }` | `{}` | Error: Exactly one key must be specified | +| `{ a: $a, b: $b }` | `{ a: "abc" }` | Error: Exactly one key must be specified | +| `{ b: $b }` | `{ b: 123 }` | `{ b: 123 }` | +| `$var` | `{ var: { b: 123 } }` | `{ b: 123 }` | +| `$var` | `{ var: { a: "abc", b: 123 } }` | Error: Exactly one key must be specified | +| `$var` | `{ var: { a: "abc", b: null } }` | Error: Exactly one key must be specified | +| `$var` | `{ var: { a: null } }` | Error: Value for member field {a} must be non-null | +| `$var` | `{ var: {} }` | Error: Exactly one key must be specified | +| `"abc123"` | `{}` | Error: Incorrect value | +| `$var` | `{ var: "abc123" } }` | Error: Incorrect value | +| `{ a: "abc", b: "123" }` | `{}` | Error: Exactly one key must be specified | +| `{ b: "123" }` | `{}` | Error: Incorrect value for member field {b} | +| `$var` | `{ var: { b: "abc" } }` | Error: Incorrect value for member field {b} | +| `{ a: "abc" }` | `{}` | `{ a: "abc" }` | +| `{ b: $b }` | `{}` | Error: Value for member field {b} must be specified | +| `$var` | `{ var: { a: "abc" } }` | `{ a: "abc" }` | +| `{ a: "abc", b: null }` | `{}` | Error: Exactly one key must be specified | +| `{ b: $b }` | `{ b: null }` | Error: Value for member field {b} must be non-null | +| `{ b: 123, c: "xyz" }` | `{}` | Error: Unexpected field {c} | +| `$var` | `{ var: { b: 123, c: "xyz" } }` | Error: Unexpected field {c} | +| `{ a: $a, b: $b }` | `{ a: "abc" }` | Error: Exactly one key must be specified | **Type Validation** From 87d0b2295028522033b3f54f3392ef2f7f294e50 Mon Sep 17 00:00:00 2001 From: Benjie Date: Tue, 4 Jun 2024 18:37:57 +0100 Subject: [PATCH 25/26] Indicate `@oneOf` is a built-in directive Co-authored-by: Shane Krueger --- spec/Section 3 -- Type System.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index aceee0f7d..ec679ea5a 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -2259,8 +2259,8 @@ scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") directive @oneOf on INPUT_OBJECT ``` -The `@oneOf` directive is used within the type system definition language to -indicate an Input Object is a OneOf Input Object. +The `@oneOf` _built-in directive_ is used within the type system definition +language to indicate an Input Object is a OneOf Input Object. ```graphql example input UserUniqueCondition @oneOf { From d88d62a5befdedf9d48780c1d7da9c514c04b6fe Mon Sep 17 00:00:00 2001 From: Benjie Date: Wed, 5 Jun 2024 11:42:40 +0100 Subject: [PATCH 26/26] Update spec/Section 3 -- Type System.md --- spec/Section 3 -- Type System.md | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index ec679ea5a..c79901771 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -1764,7 +1764,6 @@ input ExampleInputTagged @oneOf { | `{ b: $b }` | `{ b: null }` | Error: Value for member field {b} must be non-null | | `{ b: 123, c: "xyz" }` | `{}` | Error: Unexpected field {c} | | `$var` | `{ var: { b: 123, c: "xyz" } }` | Error: Unexpected field {c} | -| `{ a: $a, b: $b }` | `{ a: "abc" }` | Error: Exactly one key must be specified | **Type Validation**