From b4d9a09ed7a3ce9d62d4ea3e328cf38f34971afb Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Fri, 19 Feb 2021 13:01:07 +0000 Subject: [PATCH] @oneOf input objects --- spec/Section 3 -- Type System.md | 86 ++++++++++++++++++++++++++++-- spec/Section 4 -- Introspection.md | 5 ++ spec/Section 5 -- Validation.md | 16 ++++++ 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 0aa70ee6d..25ff77f24 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,11 +1633,13 @@ 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. - ### Input Object Extensions InputObjectTypeExtension : @@ -1605,6 +1660,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 +1874,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 +2043,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..95464494e 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 Object Has 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