Skip to content

Commit 6731c76

Browse files
authored
polish: introduce executeExecutionPlan to conform to spec algo (#4178)
Previously, this was inlined into both executeOperation and collectAndExecuteSubfields, now it is a separate function. executeExecutionPlan calls "getNewDeferMap" instead of "addNewDeferredFragments" to also conform to the spec functional/immutable style. ![image](https://github.com/user-attachments/assets/95918a1d-e024-411a-87a7-f3835e26a810)
1 parent 5c7d4d1 commit 6731c76

File tree

1 file changed

+89
-105
lines changed

1 file changed

+89
-105
lines changed

src/execution/execute.ts

+89-105
Original file line numberDiff line numberDiff line change
@@ -281,52 +281,26 @@ function executeOperation(
281281
rootType,
282282
operation,
283283
);
284-
let groupedFieldSet = collectedFields.groupedFieldSet;
285-
const newDeferUsages = collectedFields.newDeferUsages;
286-
let graphqlWrappedResult: PromiseOrValue<
287-
GraphQLWrappedResult<ObjMap<unknown>>
288-
>;
289-
if (newDeferUsages.length === 0) {
290-
graphqlWrappedResult = executeRootGroupedFieldSet(
291-
exeContext,
292-
operation.operation,
293-
rootType,
294-
rootValue,
295-
groupedFieldSet,
296-
undefined,
297-
);
298-
} else {
299-
const executionPlan = buildExecutionPlan(groupedFieldSet);
300-
groupedFieldSet = executionPlan.groupedFieldSet;
301-
const newGroupedFieldSets = executionPlan.newGroupedFieldSets;
302-
const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map());
303284

304-
graphqlWrappedResult = executeRootGroupedFieldSet(
305-
exeContext,
306-
operation.operation,
307-
rootType,
308-
rootValue,
309-
groupedFieldSet,
310-
newDeferMap,
311-
);
312-
313-
if (newGroupedFieldSets.size > 0) {
314-
const newPendingExecutionGroups = collectExecutionGroups(
315-
exeContext,
316-
rootType,
317-
rootValue,
318-
undefined,
319-
undefined,
320-
newGroupedFieldSets,
321-
newDeferMap,
322-
);
285+
const { groupedFieldSet, newDeferUsages } = collectedFields;
286+
const graphqlWrappedResult =
287+
newDeferUsages.length === 0
288+
? executeRootGroupedFieldSet(
289+
exeContext,
290+
operation.operation,
291+
rootType,
292+
rootValue,
293+
groupedFieldSet,
294+
undefined,
295+
)
296+
: executeExecutionPlan(
297+
exeContext,
298+
rootType,
299+
rootValue,
300+
newDeferUsages,
301+
buildExecutionPlan(groupedFieldSet),
302+
);
323303

324-
graphqlWrappedResult = withNewExecutionGroups(
325-
graphqlWrappedResult,
326-
newPendingExecutionGroups,
327-
);
328-
}
329-
}
330304
if (isPromise(graphqlWrappedResult)) {
331305
return graphqlWrappedResult.then(
332306
(resolved) => buildDataResponse(exeContext, resolved[0], resolved[1]),
@@ -346,6 +320,49 @@ function executeOperation(
346320
}
347321
}
348322

323+
function executeExecutionPlan(
324+
exeContext: ExecutionContext,
325+
returnType: GraphQLObjectType,
326+
sourceValue: unknown,
327+
newDeferUsages: ReadonlyArray<DeferUsage>,
328+
executionPlan: ExecutionPlan,
329+
path?: Path | undefined,
330+
incrementalContext?: IncrementalContext | undefined,
331+
deferMap?: ReadonlyMap<DeferUsage, DeferredFragmentRecord> | undefined,
332+
): PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>> {
333+
const newDeferMap = getNewDeferMap(newDeferUsages, deferMap, path);
334+
335+
const { groupedFieldSet, newGroupedFieldSets } = executionPlan;
336+
337+
const graphqlWrappedResult = executeFields(
338+
exeContext,
339+
returnType,
340+
sourceValue,
341+
path,
342+
groupedFieldSet,
343+
incrementalContext,
344+
newDeferMap,
345+
);
346+
347+
if (newGroupedFieldSets.size > 0) {
348+
const newPendingExecutionGroups = collectExecutionGroups(
349+
exeContext,
350+
returnType,
351+
sourceValue,
352+
path,
353+
incrementalContext?.deferUsageSet,
354+
newGroupedFieldSets,
355+
newDeferMap,
356+
);
357+
358+
return withNewExecutionGroups(
359+
graphqlWrappedResult,
360+
newPendingExecutionGroups,
361+
);
362+
}
363+
return graphqlWrappedResult;
364+
}
365+
349366
function withNewExecutionGroups(
350367
result: PromiseOrValue<GraphQLWrappedResult<ObjMap<unknown>>>,
351368
newPendingExecutionGroups: ReadonlyArray<PendingExecutionGroup>,
@@ -1663,21 +1680,14 @@ function invalidReturnTypeError(
16631680
*
16641681
* Note: As defer directives may be used with operations returning lists,
16651682
* a DeferUsage object may correspond to many DeferredFragmentRecords.
1666-
*
1667-
* DeferredFragmentRecord creation includes the following steps:
1668-
* 1. The new DeferredFragmentRecord is instantiated at the given path.
1669-
* 2. The parent result record is calculated from the given incremental data
1670-
* record.
1671-
* 3. The IncrementalPublisher is notified that a new DeferredFragmentRecord
1672-
* with the calculated parent has been added; the record will be released only
1673-
* after the parent has completed.
1674-
*
16751683
*/
1676-
function addNewDeferredFragments(
1684+
function getNewDeferMap(
16771685
newDeferUsages: ReadonlyArray<DeferUsage>,
1678-
newDeferMap: Map<DeferUsage, DeferredFragmentRecord>,
1686+
deferMap?: ReadonlyMap<DeferUsage, DeferredFragmentRecord> | undefined,
16791687
path?: Path | undefined,
16801688
): ReadonlyMap<DeferUsage, DeferredFragmentRecord> {
1689+
const newDeferMap = new Map(deferMap);
1690+
16811691
// For each new deferUsage object:
16821692
for (const newDeferUsage of newDeferUsages) {
16831693
const parentDeferUsage = newDeferUsage.parentDeferUsage;
@@ -1724,56 +1734,30 @@ function collectAndExecuteSubfields(
17241734
returnType,
17251735
fieldGroup,
17261736
);
1727-
let groupedFieldSet = collectedSubfields.groupedFieldSet;
1728-
const newDeferUsages = collectedSubfields.newDeferUsages;
1729-
if (deferMap === undefined && newDeferUsages.length === 0) {
1730-
return executeFields(
1731-
exeContext,
1732-
returnType,
1733-
result,
1734-
path,
1735-
groupedFieldSet,
1736-
incrementalContext,
1737-
undefined,
1738-
);
1739-
}
1740-
const subExecutionPlan = buildSubExecutionPlan(
1741-
groupedFieldSet,
1742-
incrementalContext?.deferUsageSet,
1743-
);
1744-
1745-
groupedFieldSet = subExecutionPlan.groupedFieldSet;
1746-
const newGroupedFieldSets = subExecutionPlan.newGroupedFieldSets;
1747-
const newDeferMap = addNewDeferredFragments(
1748-
newDeferUsages,
1749-
new Map(deferMap),
1750-
path,
1751-
);
1752-
1753-
const subFields = executeFields(
1754-
exeContext,
1755-
returnType,
1756-
result,
1757-
path,
1758-
groupedFieldSet,
1759-
incrementalContext,
1760-
newDeferMap,
1761-
);
1762-
1763-
if (newGroupedFieldSets.size > 0) {
1764-
const newPendingExecutionGroups = collectExecutionGroups(
1765-
exeContext,
1766-
returnType,
1767-
result,
1768-
path,
1769-
incrementalContext?.deferUsageSet,
1770-
newGroupedFieldSets,
1771-
newDeferMap,
1772-
);
1773-
1774-
return withNewExecutionGroups(subFields, newPendingExecutionGroups);
1775-
}
1776-
return subFields;
1737+
const { groupedFieldSet, newDeferUsages } = collectedSubfields;
1738+
return deferMap === undefined && newDeferUsages.length === 0
1739+
? executeFields(
1740+
exeContext,
1741+
returnType,
1742+
result,
1743+
path,
1744+
groupedFieldSet,
1745+
incrementalContext,
1746+
undefined,
1747+
)
1748+
: executeExecutionPlan(
1749+
exeContext,
1750+
returnType,
1751+
result,
1752+
newDeferUsages,
1753+
buildSubExecutionPlan(
1754+
groupedFieldSet,
1755+
incrementalContext?.deferUsageSet,
1756+
),
1757+
path,
1758+
incrementalContext,
1759+
deferMap,
1760+
);
17771761
}
17781762

17791763
function buildSubExecutionPlan(

0 commit comments

Comments
 (0)