Skip to content

Commit 159d159

Browse files
committed
updated to reflect newest behavior
1 parent 3822c97 commit 159d159

File tree

5 files changed

+98
-52
lines changed

5 files changed

+98
-52
lines changed

spec/Appendix B -- Grammar Summary.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,15 @@ Alias : Name :
162162

163163
Nullability :
164164

165-
- !
166-
- ?
165+
- ListNullability NullabilityDesignator?
166+
- NullabilityDesignator
167+
168+
ListNullability : `[` Nullability? `]`
169+
170+
NullabilityDesignator :
171+
172+
- `!`
173+
- `?`
167174

168175
Arguments[Const] : ( Argument[?Const]+ )
169176

spec/Section 2 -- Language.md

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -519,12 +519,12 @@ which returns the result:
519519

520520
Nullability :
521521

522-
- ListNullability NullabilityModifier?
523-
- NullabilityModifier
522+
- ListNullability NullabilityDesignator?
523+
- NullabilityDesignator
524524

525525
ListNullability : `[` Nullability? `]`
526526

527-
NullabilityModifier :
527+
NullabilityDesignator :
528528

529529
- `!`
530530
- `?`
@@ -534,13 +534,14 @@ a field should be `Non-Nullable` or a `?` to indicate that a field should be
534534
`Nullable`. These designators override the nullability set on a field by the
535535
schema for the operation where they're being used. In addition to being
536536
`Non-Nullable`, if a field marked with `!` resolves to `null`, it propagates to
537-
the nearest parent field marked with a `?` or to `data` if one does not exist.
538-
An error is added to the `errors` array identical to if the field had been
539-
`Non-Nullable` in the schema.
537+
the nearest parent field marked with a `?` or to `data` if there is not a parent
538+
marked with a `?`. An error is added to the `errors` array identical to if the
539+
field had been `Non-Nullable` in the schema.
540540

541541
In this example, we can indicate that a `user`'s `name` that could possibly be
542542
`null`, should not be `null` and that `null` propagation should halt at the
543-
`user` field:
543+
`user` field. We can use `?` to create null propagation boundary. `user` will be
544+
treated as `Nullable` for this operation:
544545

545546
```graphql example
546547
{
@@ -551,8 +552,8 @@ In this example, we can indicate that a `user`'s `name` that could possibly be
551552
}
552553
```
553554

554-
If `name` comes back non-`null`, then the return value is the same as if the
555-
nullability designator was not used:
555+
If `name` is resolved to a value other than `null`, then the return value is the
556+
same as if the designators were not used:
556557

557558
```json example
558559
{
@@ -563,9 +564,9 @@ nullability designator was not used:
563564
}
564565
```
565566

566-
In the event that `name` is `null`, the field's parent selection set becomes
567-
`null` in the result and an error is returned, just as if `name` was marked
568-
`Non-Nullable` in the schema:
567+
In the event that `name` resolves to `null`, the field's parent selection set
568+
becomes `null` in the result and an error is returned, just as if `name` was
569+
marked `Non-Nullable` in the schema:
569570

570571
```json example
571572
{
@@ -582,19 +583,33 @@ In the event that `name` is `null`, the field's parent selection set becomes
582583
}
583584
```
584585

585-
If `user` was `Non-Nullable` in the schema, but we don't want `null`s
586-
propagating past that point, then we can use `?` to create null propagation
587-
boundary. `User` will be treated as `Nullable` for this operation:
586+
If `!` is used on a field and it is not paired with `?` on a parent, then `null`
587+
will propagate all the way to the `data` response field.
588588

589589
```graphql example
590590
{
591-
user(id: 4)? {
591+
user(id: 4) {
592592
id
593593
name!
594594
}
595595
}
596596
```
597597

598+
Response:
599+
600+
```json example
601+
{
602+
"data": null,
603+
"errors": [
604+
{
605+
"locations": [{ "column": 13, "line": 4 }],
606+
"message": "Cannot return null for non-nullable field User.name.",
607+
"path": ["user", "name"]
608+
}
609+
]
610+
}
611+
```
612+
598613
Nullability designators can also be applied to list elements like so.
599614

600615
```graphql example
@@ -616,13 +631,13 @@ syntax can be applied to multidimensional lists.
616631
}
617632
```
618633

619-
Any element without a nullability designator will inherit its nullability from
620-
the schema definition, exactly the same as non-list fields do. When designating
621-
nullability for list fields, query authors can either use a single designator
622-
(`!` or `?`) to designate the nullability of the entire field, or they can use
623-
the list element nullability syntax displayed above. The number of dimensions
624-
indicated by list element nullability syntax is required to match the number of
625-
dimensions of the field. Anything else results in a query validation error.
634+
Any field without a nullability designator will inherit its nullability from the
635+
schema definition. When designating nullability for list fields, query authors
636+
can either use a single designator (`!` or `?`) to designate the nullability of
637+
the entire field, or they can use the list element nullability syntax displayed
638+
above. The number of dimensions indicated by list element nullability syntax is
639+
required to match the number of dimensions of the field. Anything else results
640+
in a query validation error.
626641

627642
## Fragments
628643

spec/Section 5 -- Validation.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -565,16 +565,16 @@ fragment conflictingDifferingResponses on Pet {
565565
```
566566

567567
The same is true if a field is designated `Non-Nullable` in an operation. In
568-
this case, `someValue` could be either a `String` or a `String!` which are two
568+
this case, `nickname` could be either a `String` or a `String!` which are two
569569
different types and therefore can not be merged:
570570

571571
```graphql counter-example
572572
fragment conflictingDifferingResponses on Pet {
573573
... on Dog {
574-
someValue: nickname
574+
nickname
575575
}
576576
... on Cat {
577-
someValue: nickname!
577+
nickname!
578578
}
579579
}
580580
```
@@ -587,8 +587,10 @@ fragment conflictingDifferingResponses on Pet {
587587
- Let {fieldDef} be the definition of {field}
588588
- Let {fieldType} be the type of {fieldDef}
589589
- Let {requiredStatus} be the required status of {field}
590-
- Let {designatorDepth} be the number of square bracket pairs in
590+
- Let {designatorDepth} be the number of ListNullability operators in
591591
{requiredStatus}
592+
- If {designatorDepth} is 0
593+
- return true
592594
- Let {typeDepth} be the number of list dimensions in {fieldType}
593595
- If {typeDepth} equals {designatorDepth} or {designatorDepth} equals 0 return
594596
true
@@ -599,7 +601,7 @@ fragment conflictingDifferingResponses on Pet {
599601
List fields can be marked with nullability designators that look like `[?]!` to
600602
indicate the nullability of the list's elements and the nullability of the list
601603
itself. For multi-dimensional lists, the designator would look something like
602-
`[[[!]?]]!`. If the designator is not a simple `!` or `?`, then the number of
604+
`[[[!]?]]!`. If any `ListNullability` operators are used then the number of
603605
dimensions of the designator are required to match the number of dimensions of
604606
the field's type. If the two do not match then a validation error is thrown.
605607

spec/Section 6 -- Execution.md

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -564,30 +564,49 @@ Each field requested in the grouped field set that is defined on the selected
564564
objectType will result in an entry in the response map. Field execution first
565565
coerces any provided argument values, then resolves a value for the field, and
566566
finally completes that value either by recursively executing another selection
567-
set or coercing a scalar value.
567+
set or coercing a scalar value. `ccnPropagationPairs` is an unordered map where
568+
the keys are paths of required fields, and values are paths of the nearest
569+
optional parent to those required fields. `currentPropagationPath` starts as an
570+
empty path to indicate that `null` propagation should continue until it hits
571+
`data` if there is no optional field.
568572

569-
ExecuteField(objectType, objectValue, fieldType, fields, variableValues):
573+
ExecuteField(objectType, objectValue, fieldType, fields, variableValues,
574+
currentPropagationPath, ccnPropagationPairs):
570575

571576
- Let {field} be the first entry in {fields}.
572577
- Let {fieldName} be the field name of {field}.
573578
- Let {requiredStatus} be the required status of {field}.
579+
- Let {newPropagationPath} be {path} if {requiredStatus} is optional, otherwise
580+
let {newPropagationPath} be {currentPropagationPath}
581+
- If {requiredStatus} is optional:
582+
- Let {newPropagationPath} be {path}
583+
- If {requiredStatus} is required:
584+
- Set {path} to {newPropagationPath} in {ccnPropagationPairs}
574585
- Let {argumentValues} be the result of {CoerceArgumentValues(objectType, field,
575586
variableValues)}
576587
- Let {resolvedValue} be {ResolveFieldValue(objectType, objectValue, fieldName,
577588
argumentValues)}.
578-
- Let {modifiedFieldType} be {ModifiedOutputType(fieldType, requiredStatus)}.
589+
- Let {modifiedFieldType} be {ApplyRequiredStatus(fieldType, requiredStatus)}.
579590
- Return the result of {CompleteValue(modifiedFieldType, fields, resolvedValue,
580-
variableValues)}.
591+
variableValues, newPropagationPath, ccnPropagationPairs)}.
581592

582593
## Accounting For Client Controlled Nullability Designators
583594

584595
A field can have its nullability status set either in its service's schema, or a
585-
nullability designator (! or ?) can override it for the duration of an
596+
nullability designator (`!` or `?`) can override it for the duration of an
586597
execution. In order to determine a field's true nullability, both are taken into
587-
account and a final type is produced.
588-
589-
ModifiedOutputType(outputType, requiredStatus):
590-
598+
account and a final type is produced. A field marked with a `!` is called a
599+
"required field" and a field marked with a `?` is called an optional field.
600+
601+
ApplyRequiredStatus(type, requiredStatus):
602+
603+
- If there is no {requiredStatus}:
604+
- return {type}
605+
- If {requiredStatus} is not a list:
606+
- If {requiredStatus} is required:
607+
- return a `Non-Null` version of {type}
608+
- If {requiredStatus} is optional:
609+
- return a nullable version of {type}
591610
- Create a {stack} initially containing {type}.
592611
- As long as the top of {stack} is a list:
593612
- Let {currentType} be the top item of {stack}.
@@ -616,8 +635,8 @@ ModifiedOutputType(outputType, requiredStatus):
616635
- If the nullable type of {listType} is not a list
617636
- Pop the top of {stack} and set {listType} to the result
618637
- If {listType} does not exist:
619-
- Throw an error because {requiredStatus} had more list dimensions than
620-
{outputType} and is invalid.
638+
- Raise a field error because {requiredStatus} had more list dimensions
639+
than {outputType} and is invalid.
621640
- If {resultingType} exist:
622641
- If {listType} is Non-Nullable:
623642
- Set {resultingType} to a Non-Nullable list where the element is
@@ -627,11 +646,9 @@ ModifiedOutputType(outputType, requiredStatus):
627646
- Continue onto the next node.
628647
- Set {resultingType} to {listType}
629648
- If {stack} is not empty:
630-
- Throw an error because {requiredStatus} had fewer list dimensions than
649+
- Raise a field error because {requiredStatus} had fewer list dimensions than
631650
{outputType} and is invalid.
632651
- Return {resultingType}.
633-
- Otherwise:
634-
- Return {outputType}.
635652

636653
### Coercing Field Arguments
637654

@@ -835,8 +852,9 @@ response.
835852

836853
If the result of resolving a field is {null} (either because the function to
837854
resolve the field returned {null} or because a field error was raised), and the
838-
{ModifiedOutputType} of that field is of a `Non-Null` type, then a field error
839-
is raised. The error must be added to the {"errors"} list in the response.
855+
type of the field after {ApplyRequiredStatus} has been applied to it is of a
856+
`Non-Null` type, then a field error is raised. The error must be added to the
857+
{"errors"} list in the response.
840858

841859
If the field returns {null} because of a field error which has already been
842860
added to the {"errors"} list in the response, the {"errors"} list must not be
@@ -845,8 +863,11 @@ field.
845863

846864
Since `Non-Null` type fields cannot be {null}, field errors are propagated to be
847865
handled by the parent field. If the parent field may be {null} then it resolves
848-
to {null}, otherwise if its {ModifiedOutputType} is a `Non-Null` type, the field
849-
error is further propagated to its parent field.
866+
to {null}, otherwise if it is a `Non-Null` type, the field error is further
867+
propagated to its parent field.
868+
869+
If a required field resolves to {null}, propagation instead happens until an
870+
optional field is found.
850871

851872
If a `List` type wraps a `Non-Null` type, and one of the elements of that list
852873
resolves to {null}, then the entire list must resolve to {null}. If the `List`

spec/Section 7 -- Response.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,11 @@ The response might look like:
160160
}
161161
```
162162

163-
If the field which experienced an error was declared as `Non-Null` or designated
164-
`Non-Null` in the query document, the `null` result will propagate to the next
165-
nullable field. In that case, the `path` for the error should include the full
166-
path to the result field where the error was raised, even if that field is not
163+
If the field which experienced an error was declared as `Non-Null`, the `null`
164+
result will propagate to the next nullable field. If it was marked with a
165+
required designator, then it will propagate to the nearest optional parent field
166+
instead. In either case, the `path` for the error should include the full path
167+
to the result field where the error was raised, even if that field is not
167168
present in the response.
168169

169170
For example, if the `name` field from above had declared a `Non-Null` return

0 commit comments

Comments
 (0)