Skip to content

Commit 3d521c2

Browse files
committed
rework circular references section
reframe the issue as as general class of input object habitation, not just a special edge case around pure OneOf trees. This allows handling the case of mixed OneOf/non-OneOf types, like: ```graphql input A @OneOf { b: B } input B { a: A! } ``` To support this, I've also rewritten the previously-proposed `OneOfInputObjectCanBeProvidedAFiniteValue` function as `InputObjectCanBeProvidedAFiniteValue`, which handles both pure inputs, pure OneOfs, and mixed input/OneOfs validation.
1 parent 1e86f0a commit 3d521c2

File tree

1 file changed

+42
-45
lines changed

1 file changed

+42
-45
lines changed

spec/Section 3 -- Type System.md

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,9 +1594,9 @@ Input Objects are allowed to reference other Input Objects as field types. A
15941594
circular reference occurs when an Input Object references itself either directly
15951595
or through referenced Input Objects.
15961596

1597-
Circular references are generally allowed, however they may not be defined as an
1598-
unbroken chain of Non-Null singular fields. Such Input Objects are invalid
1599-
because there is no way to provide a legal value for them.
1597+
Circular references are generally allowed, however they may not form cycles such
1598+
that no finite value can be provided. Such Input Objects are invalid because
1599+
there is no way to provide a legal value for them.
16001600

16011601
This example of a circularly-referenced input type is valid as the field `self`
16021602
may be omitted or the value {null}.
@@ -1642,32 +1642,19 @@ input Second {
16421642
}
16431643
```
16441644

1645-
_OneOf Input Objects_ introduce another case where a field cannot be provided a
1646-
finite value. Because exactly one field must be set and must be non-null,
1647-
nullable fields do not provide an escape from recursion as they do in regular
1648-
Input Objects. This OneOf Input Object is invalid because its only field
1649-
references itself, and a non-null value must always be provided:
1645+
Because _OneOf Input Objects_ require exactly one field to be set and non-null,
1646+
nullable fields do not allow a finite value to be provided as they do in other
1647+
Input Objects. This example is invalid because providing a value for `First`
1648+
requires a non-null `Second`, and constructing a `Second` requires a non-null
1649+
`First`:
16501650

16511651
```graphql counter-example
1652-
input Example @oneOf {
1653-
self: Example
1652+
input First @oneOf {
1653+
second: Second
16541654
}
1655-
```
1656-
1657-
However, a OneOf Input Object that references itself is valid when at least one
1658-
field provides a path to a type that is not a OneOf Input Object:
16591655

1660-
```graphql example
1661-
input PetInput @oneOf {
1662-
cat: CatInput
1663-
dog: DogInput
1664-
self: PetInput
1665-
}
1666-
input CatInput {
1667-
name: String!
1668-
}
1669-
input DogInput {
1670-
name: String!
1656+
input Second {
1657+
first: First!
16711658
}
16721659
```
16731660

@@ -1755,12 +1742,8 @@ input ExampleInputObject {
17551742
5. If the Input Object is a _OneOf Input Object_ then:
17561743
1. The type of the input field must be nullable.
17571744
2. The input field must not have a default value.
1758-
3. If an Input Object references itself either directly or through referenced
1759-
Input Objects, at least one of the fields in the chain of references must be
1760-
either a nullable or a List type.
1745+
3. {InputObjectCanBeProvidedAFiniteValue(inputObject)} must be {true}.
17611746
4. {InputObjectDefaultValueHasCycle(inputObject)} must be {false}.
1762-
5. If the Input Object is a _OneOf Input Object_,
1763-
{OneOfInputObjectCanBeProvidedAFiniteValue(inputObject)} must be {true}.
17641747

17651748
InputObjectDefaultValueHasCycle(inputObject, defaultValue, visitedFields):
17661749

@@ -1799,25 +1782,39 @@ InputFieldDefaultValueHasCycle(field, defaultValue, visitedFields):
17991782
- Return {InputObjectDefaultValueHasCycle(namedFieldType, fieldDefaultValue,
18001783
nextVisitedFields)}.
18011784

1802-
OneOfInputObjectCanBeProvidedAFiniteValue(oneOfInputObject, visited):
1785+
InputObjectCanBeProvidedAFiniteValue(inputObject, visited):
18031786

18041787
- If {visited} is not provided, initialize it to the empty set.
1805-
- If {oneOfInputObject} is within {visited}:
1788+
- If {inputObject} is in {visited}:
18061789
- Return {false}.
1807-
- Let {nextVisited} be a new set containing {oneOfInputObject} and everything
1790+
- Let {nextVisited} be a new set containing {inputObject} and everything
18081791
from {visited}.
1809-
- For each field {field} of {oneOfInputObject}:
1810-
- Let {fieldType} be the type of {field}.
1811-
- If {fieldType} is a List type:
1812-
- Return {true}.
1813-
- Let {namedFieldType} be the underlying named type of {fieldType}.
1814-
- If {namedFieldType} is not an Input Object type:
1815-
- Return {true}.
1816-
- If {namedFieldType} is not a _OneOf Input Object_:
1817-
- Return {true}.
1818-
- If {OneOfInputObjectCanBeProvidedAFiniteValue(namedFieldType, nextVisited)}:
1819-
- Return {true}.
1820-
- Return {false}.
1792+
- If {inputObject} is a _OneOf Input Object_:
1793+
- For each field {field} of {inputObject}:
1794+
- Let {fieldType} be the type of {field}.
1795+
- If {FieldTypeCanBeProvidedAFiniteValue(fieldType, nextVisited)}:
1796+
- Return {true}.
1797+
- Return {false}.
1798+
- Otherwise:
1799+
- For each field {field} of {inputObject}:
1800+
- Let {fieldType} be the type of {field}.
1801+
- If {fieldType} is Non-Null:
1802+
- Let {innerType} be the type wrapped by {fieldType}.
1803+
- If not {FieldTypeCanBeProvidedAFiniteValue(innerType, nextVisited)}:
1804+
- Return {false}.
1805+
- Return {true}.
1806+
1807+
FieldTypeCanBeProvidedAFiniteValue(fieldType, visited):
1808+
1809+
- If {fieldType} is a List type:
1810+
- Return {true}.
1811+
- If {fieldType} is a Non-Null type:
1812+
- Let {innerType} be the type wrapped by {fieldType}.
1813+
- Return {FieldTypeCanBeProvidedAFiniteValue(innerType, visited)}.
1814+
- Assert: {fieldType} is a named type.
1815+
- If {fieldType} is not an Input Object type:
1816+
- Return {true}.
1817+
- Return {InputObjectCanBeProvidedAFiniteValue(fieldType, visited)}.
18211818

18221819
### OneOf Input Objects
18231820

0 commit comments

Comments
 (0)