Skip to content

Commit 5a98194

Browse files
committed
add legacy non-branching incremental executor
1 parent 8e01e23 commit 5a98194

File tree

6 files changed

+6739
-0
lines changed

6 files changed

+6739
-0
lines changed

src/execution/__tests__/executor-test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,14 @@ import {
4242
} from '../execute.js';
4343
import type { ExecutionResult } from '../Executor.js';
4444
import { collectSubfields, getStreamUsage } from '../Executor.js';
45+
import { legacyExecuteIncrementally } from '../legacyIncremental/legacyExecuteIncrementally.js';
4546

4647
function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
4748
return expectEqualPromisesOrValues([
4849
executeThrowingOnIncremental(args),
4950
executeIgnoringIncremental(args),
5051
experimentalExecuteIncrementally(args),
52+
legacyExecuteIncrementally(args),
5153
]) as PromiseOrValue<ExecutionResult>;
5254
}
5355

@@ -56,6 +58,7 @@ function executeSync(args: ExecutionArgs): ExecutionResult {
5658
executeSyncWrappingThrowingOnIncremental(args),
5759
executeIgnoringIncremental(args),
5860
experimentalExecuteIncrementally(args),
61+
legacyExecuteIncrementally(args),
5962
]) as ExecutionResult;
6063
}
6164

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
import { AccumulatorMap } from '../../jsutils/AccumulatorMap.js';
2+
import { getBySet } from '../../jsutils/getBySet.js';
3+
import { invariant } from '../../jsutils/invariant.js';
4+
import { isSameSet } from '../../jsutils/isSameSet.js';
5+
import { memoize1 } from '../../jsutils/memoize1.js';
6+
import { memoize2 } from '../../jsutils/memoize2.js';
7+
import type { ObjMap } from '../../jsutils/ObjMap.js';
8+
9+
import type {
10+
GraphQLError,
11+
GraphQLFormattedError,
12+
} from '../../error/GraphQLError.js';
13+
14+
import type {
15+
DeferUsage,
16+
FieldDetails,
17+
GroupedFieldSet,
18+
} from '../collectFields.js';
19+
import type { ExecutionResult, FormattedExecutionResult } from '../Executor.js';
20+
import type {
21+
DeferUsageSet,
22+
ExecutionPlan,
23+
} from '../incremental/buildExecutionPlan.js';
24+
import { IncrementalExecutor } from '../incremental/IncrementalExecutor.js';
25+
26+
import { BranchingIncrementalPublisher } from './BranchingIncrementalPublisher.js';
27+
28+
export interface LegacyExperimentalIncrementalExecutionResults<
29+
TInitialData = ObjMap<unknown>,
30+
TDeferredData = ObjMap<unknown>,
31+
TStreamItem = unknown,
32+
TExtensions = ObjMap<unknown>,
33+
> {
34+
initialResult: LegacyInitialIncrementalExecutionResult<
35+
TInitialData,
36+
TExtensions
37+
>;
38+
subsequentResults: AsyncGenerator<
39+
LegacySubsequentIncrementalExecutionResult<
40+
TDeferredData,
41+
TStreamItem,
42+
TExtensions
43+
>,
44+
void,
45+
void
46+
>;
47+
}
48+
49+
export interface LegacyInitialIncrementalExecutionResult<
50+
TInitialData = ObjMap<unknown>,
51+
TExtensions = ObjMap<unknown>,
52+
> extends ExecutionResult<TInitialData, TExtensions> {
53+
data: TInitialData;
54+
hasNext: true;
55+
extensions?: TExtensions;
56+
}
57+
58+
export interface LegacySubsequentIncrementalExecutionResult<
59+
TDeferredData = ObjMap<unknown>,
60+
TStreamItem = unknown,
61+
TExtensions = ObjMap<unknown>,
62+
> {
63+
incremental?: ReadonlyArray<
64+
LegacyIncrementalResult<TDeferredData, TStreamItem, TExtensions>
65+
>;
66+
hasNext: boolean;
67+
extensions?: TExtensions;
68+
}
69+
70+
export type LegacyIncrementalResult<
71+
TDeferredData = ObjMap<unknown>,
72+
TStreamItem = unknown,
73+
TExtensions = ObjMap<unknown>,
74+
> =
75+
| LegacyIncrementalDeferResult<TDeferredData, TExtensions>
76+
| LegacyIncrementalStreamResult<TStreamItem, TExtensions>;
77+
78+
export interface LegacyIncrementalDeferResult<
79+
TDeferredData = ObjMap<unknown>,
80+
TExtensions = ObjMap<unknown>,
81+
> extends ExecutionResult<TDeferredData, TExtensions> {
82+
path: ReadonlyArray<string | number>;
83+
label?: string;
84+
}
85+
86+
export interface LegacyIncrementalStreamResult<
87+
TStreamItem = unknown,
88+
TExtensions = ObjMap<unknown>,
89+
> {
90+
errors?: ReadonlyArray<GraphQLError>;
91+
items: ReadonlyArray<TStreamItem> | null;
92+
path: ReadonlyArray<string | number>;
93+
label?: string;
94+
extensions?: TExtensions;
95+
}
96+
97+
const buildBranchingExecutionPlanFromInitial = memoize1(
98+
(groupedFieldSet: GroupedFieldSet) =>
99+
buildBranchingExecutionPlan(groupedFieldSet),
100+
);
101+
102+
const buildBranchingExecutionPlanFromDeferred = memoize2(
103+
(groupedFieldSet: GroupedFieldSet, deferUsageSet: DeferUsageSet) =>
104+
buildBranchingExecutionPlan(groupedFieldSet, deferUsageSet),
105+
);
106+
107+
export interface FormattedLegacyExperimentalIncrementalExecutionResults<
108+
TInitialData = ObjMap<unknown>,
109+
TDeferredData = ObjMap<unknown>,
110+
TStreamItem = unknown,
111+
TExtensions = ObjMap<unknown>,
112+
> {
113+
initialResult: FormattedLegacyInitialIncrementalExecutionResult<
114+
TInitialData,
115+
TExtensions
116+
>;
117+
subsequentResults: AsyncGenerator<
118+
FormattedLegacySubsequentIncrementalExecutionResult<
119+
TDeferredData,
120+
TStreamItem,
121+
TExtensions
122+
>,
123+
void,
124+
void
125+
>;
126+
}
127+
128+
export interface FormattedLegacyInitialIncrementalExecutionResult<
129+
TInitialData = ObjMap<unknown>,
130+
TExtensions = ObjMap<unknown>,
131+
> extends FormattedExecutionResult<TInitialData, TExtensions> {
132+
data: TInitialData;
133+
hasNext: true;
134+
extensions?: TExtensions;
135+
}
136+
137+
export interface FormattedLegacySubsequentIncrementalExecutionResult<
138+
TDeferredData = ObjMap<unknown>,
139+
TStreamItem = unknown,
140+
TExtensions = ObjMap<unknown>,
141+
> {
142+
incremental?: ReadonlyArray<
143+
FormattedLegacyIncrementalResult<TDeferredData, TStreamItem, TExtensions>
144+
>;
145+
hasNext: boolean;
146+
extensions?: TExtensions;
147+
}
148+
149+
export type FormattedLegacyIncrementalResult<
150+
TDeferredData = ObjMap<unknown>,
151+
TStreamItem = unknown,
152+
TExtensions = ObjMap<unknown>,
153+
> =
154+
| FormattedLegacyIncrementalDeferResult<TDeferredData, TExtensions>
155+
| FormattedLegacyIncrementalStreamResult<TStreamItem, TExtensions>;
156+
157+
export interface FormattedLegacyIncrementalDeferResult<
158+
TDeferredData = ObjMap<unknown>,
159+
TExtensions = ObjMap<unknown>,
160+
> extends FormattedExecutionResult<TDeferredData, TExtensions> {
161+
path: ReadonlyArray<string | number>;
162+
label?: string;
163+
}
164+
165+
export interface FormattedLegacyIncrementalStreamResult<
166+
TStreamItem = unknown,
167+
TExtensions = ObjMap<unknown>,
168+
> {
169+
errors?: ReadonlyArray<GraphQLFormattedError>;
170+
items: ReadonlyArray<TStreamItem> | null;
171+
path: ReadonlyArray<string | number>;
172+
label?: string;
173+
extensions?: TExtensions;
174+
}
175+
/** @internal */
176+
export class BranchingIncrementalExecutor extends IncrementalExecutor<LegacyExperimentalIncrementalExecutionResults> {
177+
override createSubExecutor(
178+
deferUsageSet?: DeferUsageSet,
179+
): IncrementalExecutor<LegacyExperimentalIncrementalExecutionResults> {
180+
return new BranchingIncrementalExecutor(
181+
this.validatedExecutionArgs,
182+
this.sharedResolverAbortSignal,
183+
deferUsageSet,
184+
);
185+
}
186+
187+
override buildResponse(
188+
data: ObjMap<unknown> | null,
189+
): ExecutionResult | LegacyExperimentalIncrementalExecutionResults {
190+
const work = this.getIncrementalWork();
191+
const { tasks, streams } = work;
192+
if (tasks?.length === 0 && streams?.length === 0) {
193+
return super.buildResponse(data);
194+
}
195+
196+
const errors = this.collectedErrors.errors;
197+
invariant(data !== null);
198+
const incrementalPublisher = new BranchingIncrementalPublisher();
199+
return incrementalPublisher.buildResponse(
200+
data,
201+
errors,
202+
work,
203+
this.validatedExecutionArgs.externalAbortSignal,
204+
() => this.resolverAbortController?.abort(),
205+
);
206+
}
207+
208+
override buildRootExecutionPlan(
209+
originalGroupedFieldSet: GroupedFieldSet,
210+
): ExecutionPlan {
211+
return buildBranchingExecutionPlanFromInitial(originalGroupedFieldSet);
212+
}
213+
214+
override buildSubExecutionPlan(
215+
originalGroupedFieldSet: GroupedFieldSet,
216+
): ExecutionPlan {
217+
return this.deferUsageSet === undefined
218+
? buildBranchingExecutionPlanFromInitial(originalGroupedFieldSet)
219+
: buildBranchingExecutionPlanFromDeferred(
220+
originalGroupedFieldSet,
221+
this.deferUsageSet,
222+
);
223+
}
224+
}
225+
226+
function buildBranchingExecutionPlan(
227+
originalGroupedFieldSet: GroupedFieldSet,
228+
parentDeferUsages: DeferUsageSet = new Set<DeferUsage>(),
229+
): ExecutionPlan {
230+
const groupedFieldSet = new AccumulatorMap<string, FieldDetails>();
231+
232+
const newGroupedFieldSets = new Map<
233+
DeferUsageSet,
234+
AccumulatorMap<string, FieldDetails>
235+
>();
236+
237+
for (const [responseKey, fieldGroup] of originalGroupedFieldSet) {
238+
for (const fieldDetails of fieldGroup) {
239+
const deferUsage = fieldDetails.deferUsage;
240+
const deferUsageSet =
241+
deferUsage === undefined
242+
? new Set<DeferUsage>()
243+
: new Set([deferUsage]);
244+
if (isSameSet(parentDeferUsages, deferUsageSet)) {
245+
groupedFieldSet.add(responseKey, fieldDetails);
246+
} else {
247+
let newGroupedFieldSet = getBySet(newGroupedFieldSets, deferUsageSet);
248+
if (newGroupedFieldSet === undefined) {
249+
newGroupedFieldSet = new AccumulatorMap();
250+
newGroupedFieldSets.set(deferUsageSet, newGroupedFieldSet);
251+
}
252+
newGroupedFieldSet.add(responseKey, fieldDetails);
253+
}
254+
}
255+
}
256+
257+
return {
258+
groupedFieldSet,
259+
newGroupedFieldSets,
260+
};
261+
}

0 commit comments

Comments
 (0)