Skip to content

Commit 8d0b21f

Browse files
committed
remove ResolveFIeldGenerator (#4)
* streamline stream execution Currently, these spec changes introduce a new internal function named `ResolveFieldGenerator` that is suggested parallels `ResolveFieldValue`. This function is used during field execution such that if the stream directive is specified, it is called instead of `ResolveFieldValue`. The reference implementation, however, does not require any such function, simply utilizing the result of `ResolveFieldValue`. With incremental delivery, collections completed by `CompleteValue` should be explicitly iterated using a well-defined iterator, such that the iterator can be passed to `ExecuteStreamField`. But this does not require a new internal function to be specified/exposed. Moreover, introducing this function causes a mixing of concerns between the `ExecuteField` and `CompleteValue` algorithms; Currently, if stream is specified for a field, `ExecuteField` extracts the iterator and passes it to `CompleteValue`, while if stream is not specified, the `ExecuteField` passes the collection, i.e. the iterable, not the iterator. In the stream case, this shunts some of the logic checking the validity of resolution results into field execution. In fact, it exposes a specification "bug" => in the stream case, no checking is actually done that `ResolveFieldGenerator` returns an iterator! This change removes `ResolveFieldGenerator` and with it some complexity, and brings it in line with the reference implementation. The reference implementation contains some simplification of the algorithm for the synchronous iterator case (we don't have to preserve the iterator on the StreamRecord, because there will be no early close required and we don't have to set isCompletedIterator, beacuse we don't have to create a dummy payload for termination of the asynchronous stream), We could consider also removing these bits as well, as they are an implementation detail in terms of how our dispatcher is managing its iterators, but that should be left for another change. * run prettier
1 parent c967b2f commit 8d0b21f

File tree

1 file changed

+44
-73
lines changed

1 file changed

+44
-73
lines changed

spec/Section 6 -- Execution.md

Lines changed: 44 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -831,15 +831,6 @@ subsequentPayloads, asyncRecord):
831831
- Append {fieldName} to {path}.
832832
- Let {argumentValues} be the result of {CoerceArgumentValues(objectType, field,
833833
variableValues)}
834-
- If {field} provides the directive `@stream`, let {streamDirective} be that
835-
directive.
836-
- Let {initialCount} be the value or variable provided to {streamDirective}'s
837-
{initialCount} argument.
838-
- Let {resolvedValue} be {ResolveFieldGenerator(objectType, objectValue,
839-
fieldName, argumentValues)}.
840-
- Let {result} be the result of calling {CompleteValue(fieldType, fields,
841-
resolvedValue, variableValues, path, subsequentPayloads, asyncRecord)}.
842-
- Return {result}.
843834
- Let {resolvedValue} be {ResolveFieldValue(objectType, objectValue, fieldName,
844835
argumentValues)}.
845836
- Let {result} be the result of calling {CompleteValue(fieldType, fields,
@@ -907,20 +898,17 @@ must only allow usage of variables of appropriate types.
907898
While nearly all of GraphQL execution can be described generically, ultimately
908899
the internal system exposing the GraphQL interface must provide values. This is
909900
exposed via {ResolveFieldValue}, which produces a value for a given field on a
910-
type for a real value. In addition, {ResolveFieldGenerator} will be exposed to
911-
produce an iterator for a field with `List` return type. The internal system may
912-
optionally define a generator function. In the case where the generator is not
913-
defined, the GraphQL executor provides a default generator. For example, a
914-
trivial generator that yields the entire list upon the first iteration.
901+
type for a real value.
915902

916-
As an example, a {ResolveFieldValue} might accept the {objectType} `Person`, the
917-
{field} {"soulMate"}, and the {objectValue} representing John Lennon. It would
918-
be expected to yield the value representing Yoko Ono.
903+
As an example, this might accept the {objectType} `Person`, the {field}
904+
{"soulMate"}, and the {objectValue} representing John Lennon. It would be
905+
expected to yield the value representing Yoko Ono.
919906

920-
A {ResolveFieldGenerator} might accept the {objectType} `MusicBand`, the {field}
921-
{"members"}, and the {objectValue} representing Beatles. It would be expected to
922-
yield a iterator of values representing John Lennon, Paul McCartney, Ringo Starr
923-
and George Harrison.
907+
List values are resolved similarly. For example, {ResolveFieldValue} might also
908+
accept the {objectType} `MusicBand`, the {field} {"members"}, and the
909+
{objectValue} representing the Beatles. It would be expected to yield a
910+
collection of values representing John Lennon, Paul McCartney, Ringo Starr and
911+
George Harrison.
924912

925913
ResolveFieldValue(objectType, objectValue, fieldName, argumentValues):
926914

@@ -929,33 +917,23 @@ ResolveFieldValue(objectType, objectValue, fieldName, argumentValues):
929917
- Return the result of calling {resolver}, providing {objectValue} and
930918
{argumentValues}.
931919

932-
ResolveFieldGenerator(objectType, objectValue, fieldName, argumentValues):
933-
934-
- If {objectType} provide an internal function {generatorResolver} for
935-
generating partially resolved value of a list field named {fieldName}:
936-
- Let {generatorResolver} be the internal function.
937-
- Return the iterator from calling {generatorResolver}, providing
938-
{objectValue} and {argumentValues}.
939-
- Create {generator} from {ResolveFieldValue(objectType, objectValue, fieldName,
940-
argumentValues)}.
941-
- Return {generator}.
942-
943920
Note: It is common for {resolver} to be asynchronous due to relying on reading
944921
an underlying database or networked service to produce a value. This
945922
necessitates the rest of a GraphQL executor to handle an asynchronous execution
946-
flow. In addition, a common implementation of {generator} is to leverage
947-
asynchronous iterators or asynchronous generators provided by many programming
948-
languages.
923+
flow. In addition, an implementation for collections may leverage asynchronous
924+
iterators or asynchronous generators provided by many programming languages.
925+
This may be particularly helpful when used in conjunction with the `@stream`
926+
directive.
949927

950928
### Value Completion
951929

952930
After resolving the value for a field, it is completed by ensuring it adheres to
953931
the expected return type. If the return type is another Object type, then the
954-
field execution process continues recursively. In the case where a value
955-
returned for a list type field is an iterator due to `@stream` specified on the
956-
field, value completion iterates over the iterator until the number of items
957-
yield by the iterator satisfies `initialCount` specified on the `@stream`
958-
directive.
932+
field execution process continues recursively. If the return type is a List
933+
type, each member of the resolved collection is completed using the same value
934+
completion process. In the case where `@stream` is specified on a field of list
935+
type, value completion iterates over the collection until the number of items
936+
yielded items satisfies `initialCount` specified on the `@stream` directive.
959937

960938
#### Execute Stream Field
961939

@@ -1007,45 +985,38 @@ subsequentPayloads, asyncRecord):
1007985
- If {result} is {null} (or another internal value similar to {null} such as
1008986
{undefined}), return {null}.
1009987
- If {fieldType} is a List type:
1010-
- If {result} is an iterator:
1011-
- Let {field} be the first entry in {fields}.
1012-
- Let {innerType} be the inner type of {fieldType}.
1013-
- If {field} provides the directive `@stream` and its {if} argument is not
1014-
{false} and is not a variable in {variableValues} with the value {false}
1015-
and {innerType} is the outermost return type of the list type defined for
1016-
{field}:
1017-
- Let {streamDirective} be that directive.
988+
- If {result} is not a collection of values, raise a _field error_.
989+
- Let {field} be the first entry in {fields}.
990+
- Let {innerType} be the inner type of {fieldType}.
991+
- If {field} provides the directive `@stream` and its {if} argument is not
992+
{false} and is not a variable in {variableValues} with the value {false} and
993+
{innerType} is the outermost return type of the list type defined for
994+
{field}:
995+
- Let {streamDirective} be that directive.
1018996
- Let {initialCount} be the value or variable provided to
1019997
{streamDirective}'s {initialCount} argument.
1020998
- If {initialCount} is less than zero, raise a _field error_.
1021999
- Let {label} be the value or variable provided to {streamDirective}'s
10221000
{label} argument.
1023-
- Let {initialItems} be an empty list
1024-
- Let {index} be zero.
1001+
- Let {iterator} be an iterator for {result}.
1002+
- Let {items} be an empty list.
1003+
- Let {index} be zero.
10251004
- While {result} is not closed:
1026-
- If {streamDirective} is not defined or {index} is not greater than or
1027-
equal to {initialCount}:
1028-
- Wait for the next item from {result}.
1029-
- Let {resultItem} be the item retrieved from {result}.
1030-
- Let {itemPath} be {path} with {index} appended.
1031-
- Let {resolvedItem} be the result of calling {CompleteValue(innerType,
1032-
fields, resultItem, variableValues, itemPath, subsequentPayloads,
1033-
asyncRecord)}.
1034-
- Append {resolvedItem} to {initialItems}.
1035-
- Increment {index}.
1036-
- If {streamDirective} is defined and {index} is greater than or equal to
1037-
{initialCount}:
1038-
- Call {ExecuteStreamField(label, result, index, fields, innerType,
1039-
path, asyncRecord, subsequentPayloads)}.
1040-
- Let {result} be {initialItems}.
1041-
- Exit while loop.
1042-
- Return {initialItems}.
1043-
- If {result} is not a collection of values, raise a _field error_.
1044-
- Let {innerType} be the inner type of {fieldType}.
1045-
- Return a list where each list item is the result of calling
1046-
{CompleteValue(innerType, fields, resultItem, variableValues, itemPath,
1047-
subsequentPayloads, asyncRecord)}, where {resultItem} is each item in
1048-
{result} and {itemPath} is {path} with the index of the item appended.
1005+
- If {streamDirective} is defined and {index} is greater than or equal to
1006+
{initialCount}:
1007+
- Call {ExecuteStreamField(label, iterator, index, fields, innerType,
1008+
path, asyncRecord, subsequentPayloads)}.
1009+
- Return {items}.
1010+
- Otherwise:
1011+
- Retrieve the next item from {result} via the {iterator}.
1012+
- Let {resultItem} be the item retrieved from {result}.
1013+
- Let {itemPath} be {path} with {index} appended.
1014+
- Let {resolvedItem} be the result of calling {CompleteValue(innerType,
1015+
fields, resultItem, variableValues, itemPath, subsequentPayloads,
1016+
asyncRecord)}.
1017+
- Append {resolvedItem} to {initialItems}.
1018+
- Increment {index}.
1019+
- Return {items}.
10491020
- If {fieldType} is a Scalar or Enum type:
10501021
- Return the result of {CoerceResult(fieldType, result)}.
10511022
- If {fieldType} is an Object, Interface, or Union type:

0 commit comments

Comments
 (0)