diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 7c06624414..0e2966ee1e 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -333,7 +333,7 @@ export function experimentalExecuteQueryOrMutationOrSubscriptionEvent( ); } - const collectedFields = collectFields( + const { groupedFieldSet, newDeferUsages } = collectFields( schema, fragments, variableValues, @@ -342,24 +342,14 @@ export function experimentalExecuteQueryOrMutationOrSubscriptionEvent( hideSuggestions, ); - const { groupedFieldSet, newDeferUsages } = collectedFields; - const graphqlWrappedResult = - newDeferUsages.length === 0 - ? executeRootGroupedFieldSet( - exeContext, - operation.operation, - rootType, - rootValue, - groupedFieldSet, - undefined, - ) - : executeExecutionPlan( - exeContext, - rootType, - rootValue, - newDeferUsages, - buildExecutionPlan(groupedFieldSet), - ); + const graphqlWrappedResult = executeRootExecutionPlan( + exeContext, + operation.operation, + rootType, + rootValue, + groupedFieldSet, + newDeferUsages, + ); if (isPromise(graphqlWrappedResult)) { return graphqlWrappedResult.then( @@ -376,78 +366,6 @@ export function experimentalExecuteQueryOrMutationOrSubscriptionEvent( } } -function executeExecutionPlan( - exeContext: ExecutionContext, - returnType: GraphQLObjectType, - sourceValue: unknown, - newDeferUsages: ReadonlyArray, - executionPlan: ExecutionPlan, - path?: Path | undefined, - 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, -): PromiseOrValue>> { - if (isPromise(result)) { - return result.then((resolved) => { - addIncrementalDataRecords(resolved, newPendingExecutionGroups); - return resolved; - }); - } - - addIncrementalDataRecords(result, newPendingExecutionGroups); - return result; -} - -function addIncrementalDataRecords( - graphqlWrappedResult: GraphQLWrappedResult, - incrementalDataRecords: ReadonlyArray | undefined, -): void { - if (incrementalDataRecords === undefined) { - return; - } - if (graphqlWrappedResult.incrementalDataRecords === undefined) { - graphqlWrappedResult.incrementalDataRecords = [...incrementalDataRecords]; - } else { - graphqlWrappedResult.incrementalDataRecords.push(...incrementalDataRecords); - } -} - function withError( errors: Array | undefined, error: GraphQLError, @@ -603,6 +521,73 @@ export function validateExecutionArgs( }; } +function executeRootExecutionPlan( + exeContext: ExecutionContext, + operation: OperationTypeNode, + rootType: GraphQLObjectType, + rootValue: unknown, + originalGroupedFieldSet: GroupedFieldSet, + newDeferUsages: ReadonlyArray, +): PromiseOrValue>> { + if (newDeferUsages.length === 0) { + return executeRootGroupedFieldSet( + exeContext, + operation, + rootType, + rootValue, + originalGroupedFieldSet, + undefined, + ); + } + const newDeferMap = getNewDeferMap(newDeferUsages, undefined, undefined); + + const { groupedFieldSet, newGroupedFieldSets } = buildExecutionPlan( + originalGroupedFieldSet, + ); + + const graphqlWrappedResult = executeRootGroupedFieldSet( + exeContext, + operation, + rootType, + rootValue, + groupedFieldSet, + newDeferMap, + ); + + if (newGroupedFieldSets.size > 0) { + const newPendingExecutionGroups = collectExecutionGroups( + exeContext, + rootType, + rootValue, + undefined, + undefined, + newGroupedFieldSets, + newDeferMap, + ); + + return withNewExecutionGroups( + graphqlWrappedResult, + newPendingExecutionGroups, + ); + } + return graphqlWrappedResult; +} + +function withNewExecutionGroups( + result: PromiseOrValue>>, + newPendingExecutionGroups: ReadonlyArray, +): PromiseOrValue>> { + if (isPromise(result)) { + return result.then((resolved) => { + addIncrementalDataRecords(resolved, newPendingExecutionGroups); + return resolved; + }); + } + + addIncrementalDataRecords(result, newPendingExecutionGroups); + return result; +} + function executeRootGroupedFieldSet( exeContext: ExecutionContext, operation: OperationTypeNode, @@ -714,6 +699,20 @@ function executeFieldsSerially( ); } +function addIncrementalDataRecords( + graphqlWrappedResult: GraphQLWrappedResult, + incrementalDataRecords: ReadonlyArray | undefined, +): void { + if (incrementalDataRecords === undefined) { + return; + } + if (graphqlWrappedResult.incrementalDataRecords === undefined) { + graphqlWrappedResult.incrementalDataRecords = [...incrementalDataRecords]; + } else { + graphqlWrappedResult.incrementalDataRecords.push(...incrementalDataRecords); + } +} + /** * Implements the "Executing selection sets" section of the spec * for fields that may be executed in parallel. @@ -1871,29 +1870,74 @@ function collectAndExecuteSubfields( fieldDetailsList, ); const { groupedFieldSet, newDeferUsages } = collectedSubfields; - return deferMap === undefined && newDeferUsages.length === 0 - ? executeFields( - exeContext, - returnType, - result, - path, - groupedFieldSet, - incrementalContext, - undefined, - ) - : executeExecutionPlan( - exeContext, - returnType, - result, - newDeferUsages, - buildSubExecutionPlan( - groupedFieldSet, - incrementalContext?.deferUsageSet, - ), - path, - incrementalContext, - deferMap, - ); + return executeSubExecutionPlan( + exeContext, + returnType, + result, + groupedFieldSet, + newDeferUsages, + path, + incrementalContext, + deferMap, + ); +} + +function executeSubExecutionPlan( + exeContext: ExecutionContext, + returnType: GraphQLObjectType, + sourceValue: unknown, + originalGroupedFieldSet: GroupedFieldSet, + newDeferUsages: ReadonlyArray, + path?: Path | undefined, + incrementalContext?: IncrementalContext | undefined, + deferMap?: ReadonlyMap | undefined, +): PromiseOrValue>> { + if (deferMap === undefined && newDeferUsages.length === 0) { + return executeFields( + exeContext, + returnType, + sourceValue, + path, + originalGroupedFieldSet, + incrementalContext, + deferMap, + ); + } + + const newDeferMap = getNewDeferMap(newDeferUsages, deferMap, path); + + const { groupedFieldSet, newGroupedFieldSets } = buildSubExecutionPlan( + originalGroupedFieldSet, + incrementalContext?.deferUsageSet, + ); + + 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 buildSubExecutionPlan(