Skip to content

Commit 312138f

Browse files
committed
incremental: disable early execution by default
1 parent 5fc2723 commit 312138f

File tree

5 files changed

+686
-158
lines changed

5 files changed

+686
-158
lines changed

src/execution/IncrementalGraph.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Future } from '../jsutils/Future.js';
12
import { isPromise } from '../jsutils/isPromise.js';
23
import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js';
34

@@ -123,8 +124,13 @@ export class IncrementalGraph {
123124
incrementalDataRecord.streamItemRecords,
124125
);
125126
} else {
127+
const deferredGroupedFieldSetResult =
128+
incrementalDataRecord.deferredGroupedFieldSetResult;
126129
const result =
127-
incrementalDataRecord.deferredGroupedFieldSetResult.value;
130+
deferredGroupedFieldSetResult instanceof Future
131+
? deferredGroupedFieldSetResult.value
132+
: deferredGroupedFieldSetResult();
133+
128134
if (isPromise(result)) {
129135
// eslint-disable-next-line @typescript-eslint/no-floating-promises
130136
result.then((resolved) =>
@@ -315,7 +321,10 @@ export class IncrementalGraph {
315321
let incrementalDataRecords: Array<IncrementalDataRecord> = [];
316322
let streamItemRecord: StreamItemRecord | undefined;
317323
while ((streamItemRecord = streamItemRecords.shift()) !== undefined) {
318-
let result = streamItemRecord.value;
324+
let result =
325+
typeof streamItemRecord === 'function'
326+
? streamItemRecord()
327+
: streamItemRecord.value;
319328
if (isPromise(result)) {
320329
if (items.length > 0) {
321330
this._enqueue({

src/execution/__tests__/defer-test.ts

Lines changed: 150 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,16 @@ const query = new GraphQLObjectType({
135135

136136
const schema = new GraphQLSchema({ query });
137137

138-
async function complete(document: DocumentNode, rootValue: unknown = { hero }) {
138+
async function complete(
139+
document: DocumentNode,
140+
rootValue: unknown = { hero },
141+
enableEarlyExecution = false,
142+
) {
139143
const result = await experimentalExecuteIncrementally({
140144
schema,
141145
document,
142146
rootValue,
147+
enableEarlyExecution,
143148
});
144149

145150
if ('initialResult' in result) {
@@ -247,6 +252,118 @@ describe('Execute: defer directive', () => {
247252
},
248253
]);
249254
});
255+
it('Does not execute deferred fragments early when not specified', async () => {
256+
const document = parse(`
257+
query HeroNameQuery {
258+
hero {
259+
id
260+
...NameFragment @defer
261+
}
262+
}
263+
fragment NameFragment on Hero {
264+
name
265+
}
266+
`);
267+
const order: Array<string> = [];
268+
const result = await complete(document, {
269+
hero: {
270+
...hero,
271+
id: async () => {
272+
await resolveOnNextTick();
273+
await resolveOnNextTick();
274+
order.push('slow-id');
275+
return hero.id;
276+
},
277+
name: () => {
278+
order.push('fast-name');
279+
return hero.name;
280+
},
281+
},
282+
});
283+
284+
expectJSON(result).toDeepEqual([
285+
{
286+
data: {
287+
hero: {
288+
id: '1',
289+
},
290+
},
291+
pending: [{ id: '0', path: ['hero'] }],
292+
hasNext: true,
293+
},
294+
{
295+
incremental: [
296+
{
297+
data: {
298+
name: 'Luke',
299+
},
300+
id: '0',
301+
},
302+
],
303+
completed: [{ id: '0' }],
304+
hasNext: false,
305+
},
306+
]);
307+
expect(order).to.deep.equal(['slow-id', 'fast-name']);
308+
});
309+
it('Does execute deferred fragments early when specified', async () => {
310+
const document = parse(`
311+
query HeroNameQuery {
312+
hero {
313+
id
314+
...NameFragment @defer
315+
}
316+
}
317+
fragment NameFragment on Hero {
318+
name
319+
}
320+
`);
321+
const order: Array<string> = [];
322+
const result = await complete(
323+
document,
324+
{
325+
hero: {
326+
...hero,
327+
id: async () => {
328+
await resolveOnNextTick();
329+
await resolveOnNextTick();
330+
order.push('slow-id');
331+
return hero.id;
332+
},
333+
name: () => {
334+
order.push('fast-name');
335+
return hero.name;
336+
},
337+
},
338+
},
339+
true,
340+
);
341+
342+
expectJSON(result).toDeepEqual([
343+
{
344+
data: {
345+
hero: {
346+
id: '1',
347+
},
348+
},
349+
pending: [{ id: '0', path: ['hero'] }],
350+
hasNext: true,
351+
},
352+
{
353+
incremental: [
354+
{
355+
data: {
356+
name: 'Luke',
357+
},
358+
id: '0',
359+
},
360+
],
361+
completed: [{ id: '0' }],
362+
hasNext: false,
363+
},
364+
]);
365+
expect(order).to.deep.equal(['fast-name', 'slow-id']);
366+
});
250367
it('Can defer fragments on the top level Query field', async () => {
251368
const document = parse(`
252369
query HeroNameQuery {
@@ -1492,20 +1609,24 @@ describe('Execute: defer directive', () => {
14921609
}
14931610
}
14941611
`);
1495-
const result = await complete(document, {
1496-
a: {
1497-
b: {
1498-
c: {
1499-
d: 'd',
1500-
nonNullErrorField: async () => {
1501-
await resolveOnNextTick();
1502-
return null;
1612+
const result = await complete(
1613+
document,
1614+
{
1615+
a: {
1616+
b: {
1617+
c: {
1618+
d: 'd',
1619+
nonNullErrorField: async () => {
1620+
await resolveOnNextTick();
1621+
return null;
1622+
},
15031623
},
15041624
},
1625+
someField: 'someField',
15051626
},
1506-
someField: 'someField',
15071627
},
1508-
});
1628+
true,
1629+
);
15091630
expectJSON(result).toDeepEqual([
15101631
{
15111632
data: {
@@ -1564,12 +1685,16 @@ describe('Execute: defer directive', () => {
15641685
}
15651686
}
15661687
`);
1567-
const result = await complete(document, {
1568-
hero: {
1569-
...hero,
1570-
nonNullName: () => null,
1688+
const result = await complete(
1689+
document,
1690+
{
1691+
hero: {
1692+
...hero,
1693+
nonNullName: () => null,
1694+
},
15711695
},
1572-
});
1696+
true,
1697+
);
15731698
expectJSON(result).toDeepEqual({
15741699
data: {
15751700
hero: null,
@@ -1596,12 +1721,16 @@ describe('Execute: defer directive', () => {
15961721
}
15971722
}
15981723
`);
1599-
const result = await complete(document, {
1600-
hero: {
1601-
...hero,
1602-
nonNullName: () => null,
1724+
const result = await complete(
1725+
document,
1726+
{
1727+
hero: {
1728+
...hero,
1729+
nonNullName: () => null,
1730+
},
16031731
},
1604-
});
1732+
true,
1733+
);
16051734
expectJSON(result).toDeepEqual([
16061735
{
16071736
data: {},

0 commit comments

Comments
 (0)