Skip to content

Commit ede09f7

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 4d8c2eb commit ede09f7

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
@@ -832,15 +832,6 @@ subsequentPayloads, asyncRecord):
832832
- Append {fieldName} to {path}.
833833
- Let {argumentValues} be the result of {CoerceArgumentValues(objectType, field,
834834
variableValues)}
835-
- If {field} provides the directive `@stream`, let {streamDirective} be that
836-
directive.
837-
- Let {initialCount} be the value or variable provided to {streamDirective}'s
838-
{initialCount} argument.
839-
- Let {resolvedValue} be {ResolveFieldGenerator(objectType, objectValue,
840-
fieldName, argumentValues)}.
841-
- Let {result} be the result of calling {CompleteValue(fieldType, fields,
842-
resolvedValue, variableValues, path, subsequentPayloads, asyncRecord)}.
843-
- Return {result}.
844835
- Let {resolvedValue} be {ResolveFieldValue(objectType, objectValue, fieldName,
845836
argumentValues)}.
846837
- Let {result} be the result of calling {CompleteValue(fieldType, fields,
@@ -908,20 +899,17 @@ must only allow usage of variables of appropriate types.
908899
While nearly all of GraphQL execution can be described generically, ultimately
909900
the internal system exposing the GraphQL interface must provide values. This is
910901
exposed via {ResolveFieldValue}, which produces a value for a given field on a
911-
type for a real value. In addition, {ResolveFieldGenerator} will be exposed to
912-
produce an iterator for a field with `List` return type. The internal system may
913-
optionally define a generator function. In the case where the generator is not
914-
defined, the GraphQL executor provides a default generator. For example, a
915-
trivial generator that yields the entire list upon the first iteration.
902+
type for a real value.
916903

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

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

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

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

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

951929
### Value Completion
952930

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

961939
#### Execute Stream Field
962940

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

0 commit comments

Comments
 (0)