-
Notifications
You must be signed in to change notification settings - Fork 1.1k
RFC: OneOf Input Objects #825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
benjie
wants to merge
53
commits into
graphql:main
Choose a base branch
from
benjie:oneof-v2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
c385058
Renumber list items
benjie f6bd659
@oneOf input objects
benjie 39e593c
@oneOf fields
benjie b6741c3
Fix typos (thanks @eapache!)
benjie d17d5ec
Much stricter validation for oneof literals (with examples)
benjie dca3826
Add missing coercion rule
benjie 7e02f5a
Clearer wording of oneof coercion rule
benjie 4111476
Add more examples for clarity
benjie 6754e0a
Rename introspection fields to oneOf
benjie 7c4c1a2
Oneof's now require exactly one field/argument, and non-nullable vari…
benjie bb225f7
Remove extraneous newline
benjie 05fde06
graphgl -> graphql
benjie e8f6145
Apply suggestions from @eapache's review
benjie 08abf49
Apply suggestions from code review
benjie 59cb12d
Update spec/Section 3 -- Type System.md
benjie c470afb
Merge branch 'main' into oneof-v2
benjie 99aa5d9
Remove Oneof Fields from spec
benjie 691087d
Oneof -> OneOf
benjie 7109dbc
Spellings
benjie 05ab541
Remove out of date example
benjie 6a6be52
Rename __Type.oneOf to __Type.isOneOf
benjie de87d2f
Add a:null example
benjie 57e2388
Rewrite to avoid ambiguity of language
benjie 5a966f2
Forbid 'extend input' from introducing the @oneOf directive
benjie e78d2b5
Merge branch 'main' into oneof-v2
benjie c6cd857
Merge branch 'main' into oneof-v2
benjie d106233
Add yet more examples to the example coercion table
benjie 87d0b22
Indicate `@oneOf` is a built-in directive
benjie d88d62a
Update spec/Section 3 -- Type System.md
benjie a810aef
Merge branch 'main' into oneof-v2
benjie a1563a9
remove OneOf-specific rule in favor of update to VariablesInAllowedPo…
yaacovCR b45c0e4
Clarify IsNonNullPosition algorithm
benjie 0c9830e
Clarify OneOf examples
benjie c4d0b50
Add more examples
benjie 340594e
Remove new validation rule in favour of updates to existing rules
benjie dbccf84
Null literal is separate
benjie 17f1304
Merge branch 'main' into oneof-v2
benjie d15f7bb
Merge branch 'main' into oneof-v2
benjie 6310475
Use 'execution error' and 'raise' rather than throw an error
benjie c26463f
Update spec/Section 3 -- Type System.md
benjie ff986be
Whitespace
benjie 016f47f
Clarify algorithm
benjie b1ff1ab
Rename 'Tagged'
benjie 9bec236
editorial: define and link _OneOf Input Object_
leebyron 59ad4b7
Merge branch 'main' into oneof-v2
leebyron dddd4b8
Merge branch 'main' into oneof-v2
benjie 0f19c3e
execution error -> request error (input coercion)
benjie 128807c
Simplify and clarify OneOf Input Object additional coercion rules
benjie d3e359b
Clarity and correctness
benjie ec85436
Simplify
benjie 47cbe95
Use a colon
benjie 2998f7d
Use the correct error for the situation
benjie 5f6e777
Remove example which will not always fail until #1059 is adopted
benjie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}. | ||
|
@@ -832,19 +835,21 @@ 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}. | ||
3. {IsValidImplementationFieldType(fieldType, implementedFieldType)} | ||
must be {true}. | ||
6. {field} must be a Oneof Field if and only if {implementedField} is a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels kinda awkward, like we're skirting around the idea of having fundamentally different types of fields without really committing to it? |
||
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. | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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. | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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: | ||
|
@@ -1448,6 +1480,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): | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```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. | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
**Circular References** | ||
|
||
Input Objects are allowed to reference other Input Objects as field types. A | ||
|
@@ -1570,6 +1624,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 { | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
`{ 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 | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
benjie marked this conversation as resolved.
Show resolved
Hide resolved
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
**Type Validation** | ||
|
||
1. An Input Object type must define one or more input fields. | ||
michaelstaib marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
@@ -1580,6 +1665,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. | ||
|
@@ -1600,11 +1688,15 @@ 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. | ||
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 +1907,13 @@ 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 | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(i.e. Oneof Input Objects) or fields that require exactly one argument (i.e. | ||
Oneof Fields). | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
**Custom Directives** | ||
|
||
|
@@ -1980,3 +2077,34 @@ type ExampleType { | |
oldField: String @deprecated(reason: "Use `newField`.") | ||
} | ||
``` | ||
|
||
### @oneOf | ||
|
||
```graphql | ||
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, or | ||
- an Object Type's Field is a Oneof Field. | ||
|
||
```graphql example | ||
input UserUniqueCondition @oneOf { | ||
id: ID | ||
username: String | ||
organizationAndEmail: OrganizationAndEmailInput | ||
} | ||
``` | ||
|
||
```graphql example | ||
type Query { | ||
benjie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
findUser( | ||
byID: ID | ||
byUsername: String | ||
byEmail: String | ||
byRegistrationNumber: Int | ||
): User @oneOf | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.