From 9e3255e70587afd9e1cb414816bca3f70b1b0e6e Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Mon, 26 Aug 2024 15:53:32 +0300 Subject: [PATCH 1/2] polish: introduce executeExecutionPlan to conform to spec algo 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. --- src/execution/execute.ts | 197 ++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 105 deletions(-) diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 99582b828d..408fac0b31 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -281,52 +281,29 @@ function executeOperation( rootType, operation, ); - let groupedFieldSet = collectedFields.groupedFieldSet; - const newDeferUsages = collectedFields.newDeferUsages; - let graphqlWrappedResult: PromiseOrValue< - GraphQLWrappedResult> - >; - if (newDeferUsages.length === 0) { - graphqlWrappedResult = executeRootGroupedFieldSet( - exeContext, - operation.operation, - rootType, - rootValue, - groupedFieldSet, - undefined, - ); - } else { - const executionPlan = buildExecutionPlan(groupedFieldSet); - groupedFieldSet = executionPlan.groupedFieldSet; - const newGroupedFieldSets = executionPlan.newGroupedFieldSets; - const newDeferMap = addNewDeferredFragments(newDeferUsages, new Map()); - graphqlWrappedResult = executeRootGroupedFieldSet( - exeContext, - operation.operation, - rootType, - rootValue, - groupedFieldSet, - newDeferMap, - ); - - if (newGroupedFieldSets.size > 0) { - const newPendingExecutionGroups = collectExecutionGroups( - exeContext, - rootType, - rootValue, - undefined, - undefined, - newGroupedFieldSets, - newDeferMap, - ); + const { groupedFieldSet, newDeferUsages } = collectedFields; + const graphqlWrappedResult = + newDeferUsages.length === 0 + ? executeRootGroupedFieldSet( + exeContext, + operation.operation, + rootType, + rootValue, + groupedFieldSet, + undefined, + ) + : executeExecutionPlan( + exeContext, + rootType, + rootValue, + undefined, + newDeferUsages, + buildExecutionPlan(groupedFieldSet), + undefined, + undefined, + ); - graphqlWrappedResult = withNewExecutionGroups( - graphqlWrappedResult, - newPendingExecutionGroups, - ); - } - } if (isPromise(graphqlWrappedResult)) { return graphqlWrappedResult.then( (resolved) => buildDataResponse(exeContext, resolved[0], resolved[1]), @@ -346,6 +323,49 @@ function executeOperation( } } +function executeExecutionPlan( + exeContext: ExecutionContext, + returnType: GraphQLObjectType, + sourceValue: unknown, + path: Path | undefined, + newDeferUsages: ReadonlyArray, + executionPlan: ExecutionPlan, + incrementalContext: IncrementalContext | undefined, + deferMap: ReadonlyMap | undefined, +): PromiseOrValue>> { + const newDeferMap = getNewDeferMap(newDeferUsages, deferMap, path); + + const { groupedFieldSet, newGroupedFieldSets } = executionPlan; + + const graphqlWrappedResult = executeFields( + exeContext, + returnType, + sourceValue, + path, + groupedFieldSet, + incrementalContext, + newDeferMap, + ); + + if (newGroupedFieldSets.size > 0) { + const newPendingExecutionGroups = collectExecutionGroups( + exeContext, + returnType, + sourceValue, + path, + incrementalContext?.deferUsageSet, + newGroupedFieldSets, + newDeferMap, + ); + + return withNewExecutionGroups( + graphqlWrappedResult, + newPendingExecutionGroups, + ); + } + return graphqlWrappedResult; +} + function withNewExecutionGroups( result: PromiseOrValue>>, newPendingExecutionGroups: ReadonlyArray, @@ -1663,21 +1683,14 @@ function invalidReturnTypeError( * * Note: As defer directives may be used with operations returning lists, * a DeferUsage object may correspond to many DeferredFragmentRecords. - * - * DeferredFragmentRecord creation includes the following steps: - * 1. The new DeferredFragmentRecord is instantiated at the given path. - * 2. The parent result record is calculated from the given incremental data - * record. - * 3. The IncrementalPublisher is notified that a new DeferredFragmentRecord - * with the calculated parent has been added; the record will be released only - * after the parent has completed. - * */ -function addNewDeferredFragments( +function getNewDeferMap( newDeferUsages: ReadonlyArray, - newDeferMap: Map, + deferMap?: ReadonlyMap | undefined, path?: Path | undefined, ): ReadonlyMap { + const newDeferMap = new Map(deferMap); + // For each new deferUsage object: for (const newDeferUsage of newDeferUsages) { const parentDeferUsage = newDeferUsage.parentDeferUsage; @@ -1724,56 +1737,30 @@ function collectAndExecuteSubfields( returnType, fieldGroup, ); - let groupedFieldSet = collectedSubfields.groupedFieldSet; - const newDeferUsages = collectedSubfields.newDeferUsages; - if (deferMap === undefined && newDeferUsages.length === 0) { - return executeFields( - exeContext, - returnType, - result, - path, - groupedFieldSet, - incrementalContext, - undefined, - ); - } - const subExecutionPlan = buildSubExecutionPlan( - groupedFieldSet, - incrementalContext?.deferUsageSet, - ); - - groupedFieldSet = subExecutionPlan.groupedFieldSet; - const newGroupedFieldSets = subExecutionPlan.newGroupedFieldSets; - const newDeferMap = addNewDeferredFragments( - newDeferUsages, - new Map(deferMap), - path, - ); - - const subFields = executeFields( - exeContext, - returnType, - result, - path, - groupedFieldSet, - incrementalContext, - newDeferMap, - ); - - if (newGroupedFieldSets.size > 0) { - const newPendingExecutionGroups = collectExecutionGroups( - exeContext, - returnType, - result, - path, - incrementalContext?.deferUsageSet, - newGroupedFieldSets, - newDeferMap, - ); - - return withNewExecutionGroups(subFields, newPendingExecutionGroups); - } - return subFields; + const { groupedFieldSet, newDeferUsages } = collectedSubfields; + return deferMap === undefined && newDeferUsages.length === 0 + ? executeFields( + exeContext, + returnType, + result, + path, + groupedFieldSet, + incrementalContext, + undefined, + ) + : executeExecutionPlan( + exeContext, + returnType, + result, + path, + newDeferUsages, + buildSubExecutionPlan( + groupedFieldSet, + incrementalContext?.deferUsageSet, + ), + incrementalContext, + deferMap, + ); } function buildSubExecutionPlan( From 692ebaab78603552790731d0367229c47d0c715e Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Mon, 26 Aug 2024 15:57:26 +0300 Subject: [PATCH 2/2] reorder args to leave off undefined --- src/execution/execute.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 408fac0b31..2b4025401c 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -297,11 +297,8 @@ function executeOperation( exeContext, rootType, rootValue, - undefined, newDeferUsages, buildExecutionPlan(groupedFieldSet), - undefined, - undefined, ); if (isPromise(graphqlWrappedResult)) { @@ -327,11 +324,11 @@ function executeExecutionPlan( exeContext: ExecutionContext, returnType: GraphQLObjectType, sourceValue: unknown, - path: Path | undefined, newDeferUsages: ReadonlyArray, executionPlan: ExecutionPlan, - incrementalContext: IncrementalContext | undefined, - deferMap: ReadonlyMap | undefined, + path?: Path | undefined, + incrementalContext?: IncrementalContext | undefined, + deferMap?: ReadonlyMap | undefined, ): PromiseOrValue>> { const newDeferMap = getNewDeferMap(newDeferUsages, deferMap, path); @@ -1752,12 +1749,12 @@ function collectAndExecuteSubfields( exeContext, returnType, result, - path, newDeferUsages, buildSubExecutionPlan( groupedFieldSet, incrementalContext?.deferUsageSet, ), + path, incrementalContext, deferMap, );