Skip to content

Commit

Permalink
@OneOf input objects
Browse files Browse the repository at this point in the history
  • Loading branch information
benjie committed Feb 19, 2021
1 parent c385058 commit b4d9a09
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
86 changes: 83 additions & 3 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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 :
Expand All @@ -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
Expand Down Expand Up @@ -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**

Expand Down Expand Up @@ -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
}
```
5 changes: 5 additions & 0 deletions spec/Section 4 -- Introspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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}.


Expand Down
16 changes: 16 additions & 0 deletions spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit b4d9a09

Please sign in to comment.