Skip to content

Commit f98ca57

Browse files
committed
add spec edits for references
references are an alternative syntax for fragment metafields such as `__fulfilled`
1 parent 3aa021f commit f98ca57

File tree

3 files changed

+138
-16
lines changed

3 files changed

+138
-16
lines changed

spec/Section 2 -- Language.md

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ which returns the result:
516516

517517
## Fragments
518518

519-
FragmentSpread : ... FragmentName Directives?
519+
FragmentSpread : ... Reference? FragmentName Directives?
520520

521521
FragmentDefinition : fragment FragmentName TypeCondition Directives?
522522
SelectionSet
@@ -663,9 +663,80 @@ be present and `likers` will not. Conversely when the result is a `Page`,
663663
}
664664
```
665665

666+
## Fragment Spread References
667+
668+
Reference : Name : SignalSet
669+
670+
SignalSet: { Signal+ }
671+
672+
Signal : `selected`
673+
674+
Note: Additional signal types may be added to facilitate incremental delivery or
675+
other functionality.
676+
677+
By default, utilizing fragment spreads does not alter the response shape. It may
678+
sometimes be convenient, however, to include a reference within the response
679+
signalling referring to a fragment containing information about its execution,
680+
e.g. whether its selections were collected and spread. In particular, this may
681+
be useful when fragments are included conditionally, such as with
682+
[type conditions](#sec-type-conditions), especially in the presence of
683+
potentially complex type hierarchies.
684+
685+
For example:
686+
687+
```graphql example
688+
query FragmentReferences {
689+
nodes(ids: [1, 42]) {
690+
id
691+
UserFields { selected }: ...userFragment
692+
SuperUserFields { selected }: ...superUserFragment
693+
}
694+
}
695+
696+
fragment userFragment on User {
697+
friends {
698+
count
699+
}
700+
}
701+
702+
fragment superUserFragment on SuperUser {
703+
privilegeLevel
704+
}
705+
```
706+
707+
The `nodes` root field returns a list where each element could potentially be of
708+
many types. When the object in the `nodes` result is a `User`,
709+
`UserFields.selected` will be present. If the result is also a `SuperUser`,
710+
`SuperUserFields.selected` will be present.
711+
712+
```json example
713+
{
714+
"nodes": [
715+
{
716+
"id": 1,
717+
"UserFields": { "selected": null },
718+
"friends": { "count": 1234 }
719+
},
720+
{
721+
"id": 42,
722+
"UserFields": { "selected": null },
723+
"friends": { "count": 5678 },
724+
"SuperUserFields": { "selected": null },
725+
"privilegeLevel": 20
726+
}
727+
]
728+
}
729+
```
730+
731+
Note: The value corresponding to the `selected` signal within a reference is not
732+
specified.
733+
734+
Note: References may not be included for fragments whose parent type is the root
735+
subscription type.
736+
666737
### Inline Fragments
667738

668-
InlineFragment : ... TypeCondition? Directives? SelectionSet
739+
InlineFragment : Reference? ... TypeCondition? Directives? SelectionSet
669740

670741
Fragments can also be defined inline within a selection set. This is useful for
671742
conditionally including fields based on a type condition or applying a directive

spec/Section 5 -- Validation.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,20 @@ FieldsInSetCanMerge(set):
429429
- Let {mergedSet} be the result of adding the selection set of {fieldA} and
430430
the selection set of {fieldB}.
431431
- {FieldsInSetCanMerge(mergedSet)} must be true.
432+
- {ReferencesInSetCanMerge(set)} must be true.
433+
434+
ReferencesInSetCanMerge(references):
435+
436+
- Let {fieldResponseNames} be the set of response names for fields within {set}
437+
including visiting fragments and inline fragments.
438+
- Let {references} be the set of references in {set}, including visiting
439+
fragments and inline fragments.
440+
- For each {reference} in {references}:
441+
- The name of {reference} must not be contained by {fieldResponseNames}.
442+
- Given each pair of members {referenceA} and {referenceB} in {references}:
443+
- If the parent types of {fieldA} and {fieldB} are equal or if either is not
444+
an Object Type:
445+
- {referenceA} and {referenceB} must have different names.
432446

433447
SameResponseShape(fieldA, fieldB):
434448

spec/Section 6 -- Execution.md

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,8 @@ map.
334334

335335
ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues):
336336

337-
- Let {groupedFieldSet} be the result of {CollectFields(objectType,
338-
selectionSet, variableValues)}.
337+
- Let {groupedFieldSet} and {groupedSignals} be the result of
338+
{CollectFields(objectType, selectionSet, variableValues)}.
339339
- Initialize {resultMap} to an empty ordered map.
340340
- For each {groupedFieldSet} as {responseKey} and {fields}:
341341
- Let {fieldName} be the name of the first entry in {fields}. Note: This value
@@ -346,6 +346,12 @@ ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues):
346346
- Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
347347
fields, variableValues)}.
348348
- Set {responseValue} as the value for {responseKey} in {resultMap}.
349+
- For each {groupedSignals} as {referenceName} and {requestedSignals}:
350+
- Initialize {signalValues} to an empty ordered map.
351+
- For each {signal} in {requestedSignals}:
352+
- If the signal type of {signal} is `selected`:
353+
- Set {null} as the value for `selected` in {signalValues}.
354+
- Set {signalValues} as the value for {referenceName} in {resultMap}.
349355
- Return {resultMap}.
350356

351357
Note: {resultMap} is ordered by which fields appear first in the operation. This
@@ -461,11 +467,12 @@ A correct executor must generate the following result for that selection set:
461467

462468
### Field Collection
463469

464-
Before execution, the selection set is converted to a grouped field set by
465-
calling {CollectFields()}. Each entry in the grouped field set is a list of
466-
fields that share a response key (the alias if defined, otherwise the field
467-
name). This ensures all fields with the same response key (including those in
468-
referenced fragments) are executed at the same time.
470+
Before execution, the selection set is converted to a grouped field set and
471+
grouped reference signal set by calling {CollectFields()}. Each entry in the
472+
grouped field set is a list of fields that share a response key (the alias if
473+
defined, otherwise the field name). This ensures all fields with the same
474+
response key (including those in referenced fragments) are executed at the same
475+
time.
469476

470477
As an example, collecting the fields of this selection set would collect two
471478
instances of the field `a` and one of field `b`:
@@ -494,6 +501,7 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
494501

495502
- If {visitedFragments} is not provided, initialize it to the empty set.
496503
- Initialize {groupedFields} to an empty ordered map of lists.
504+
- Initialize {groupedSignals} to an empty ordered map of sets.
497505
- For each {selection} in {selectionSet}:
498506
- If {selection} provides the directive `@skip`, let {skipDirective} be that
499507
directive.
@@ -523,32 +531,52 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
523531
- Let {fragmentType} be the type condition on {fragment}.
524532
- If {DoesFragmentTypeApply(objectType, fragmentType)} is false, continue
525533
with the next {selection} in {selectionSet}.
534+
- If a {reference} is defined for the given {fragment}:
535+
- Let {signalsForReference} be the result of calling
536+
{CollectSignals(reference)}.
537+
- Let {referenceName} be the name of {reference}.
538+
- Set the entry for {referenceName} in {groupedSignals} to
539+
{signalsForReference}.
526540
- Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
527-
- Let {fragmentGroupedFieldSet} be the result of calling
528-
{CollectFields(objectType, fragmentSelectionSet, variableValues,
529-
visitedFragments)}.
541+
- Let {fragmentGroupedFieldSet} and {fragmentRequestedSignals} be the result
542+
of calling {CollectFields(objectType, fragmentSelectionSet,
543+
variableValues, visitedFragments)}.
530544
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
531545
- Let {responseKey} be the response key shared by all fields in
532546
{fragmentGroup}.
533547
- Let {groupForResponseKey} be the list in {groupedFields} for
534548
{responseKey}; if no such list exists, create it as an empty list.
535549
- Append all items in {fragmentGroup} to {groupForResponseKey}.
550+
- For each {fragmentRequestedSignals} as {referenceName} and
551+
{signalsForReference}:
552+
- Set the entry for {referenceName} in {groupedSignals} to
553+
{signalsForReference}.
536554
- If {selection} is an {InlineFragment}:
537555
- Let {fragmentType} be the type condition on {selection}.
538556
- If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
539557
fragmentType)} is false, continue with the next {selection} in
540558
{selectionSet}.
559+
- If a {reference} is defined for the given {fragment}:
560+
- Let {signalsForReference} be the result of calling
561+
{CollectSignals(reference)}.
562+
- Let {referenceName} be the name of {reference}.
563+
- Set the entry for {referenceName} in {requestedSignals} to
564+
{signalsForReference}.
541565
- Let {fragmentSelectionSet} be the top-level selection set of {selection}.
542-
- Let {fragmentGroupedFieldSet} be the result of calling
543-
{CollectFields(objectType, fragmentSelectionSet, variableValues,
544-
visitedFragments)}.
566+
- Let {fragmentGroupedFieldSet} and {fragmentRequestedSignals} be the result
567+
of calling {CollectFields(objectType, fragmentSelectionSet,
568+
variableValues, visitedFragments)}.
545569
- For each {fragmentGroup} in {fragmentGroupedFieldSet}:
546570
- Let {responseKey} be the response key shared by all fields in
547571
{fragmentGroup}.
548572
- Let {groupForResponseKey} be the list in {groupedFields} for
549573
{responseKey}; if no such list exists, create it as an empty list.
550574
- Append all items in {fragmentGroup} to {groupForResponseKey}.
551-
- Return {groupedFields}.
575+
- For each {fragmentRequestedSignals} as {referenceName} and
576+
{signalsForReference}:
577+
- Set the entry for {referenceName} in {groupedSignals} to
578+
{signalsForReference}.
579+
- Return {groupedFields} and {groupedSignals}.
552580

553581
DoesFragmentTypeApply(objectType, fragmentType):
554582

@@ -562,6 +590,15 @@ DoesFragmentTypeApply(objectType, fragmentType):
562590
- if {objectType} is a possible type of {fragmentType}, return {true}
563591
otherwise return {false}.
564592

593+
CollectSignals(reference):
594+
595+
- Let {signalsForReference} be an empty set.
596+
- Let {signalSet} be the signal set of {reference}.
597+
- For each {signal} in {signalSet}:
598+
- Let {signalType} be the signal type of {signal}.
599+
- Add {signalType} to {signalsForReference}.
600+
- Return {signalsForReference}.
601+
565602
Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
566603
directives may be applied in either order since they apply commutatively.
567604

0 commit comments

Comments
 (0)