Skip to content

Commit 5a41f86

Browse files
authored
defer/stream: split incremental delivery into new entry points (#3703)
1 parent efda9be commit 5a41f86

File tree

10 files changed

+570
-183
lines changed

10 files changed

+570
-183
lines changed

src/execution/__tests__/defer-test.ts

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { expect } from 'chai';
12
import { describe, it } from 'mocha';
23

34
import { expectJSON } from '../../__testUtils__/expectJSON';
5+
import { expectPromise } from '../../__testUtils__/expectPromise';
46
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick';
57

6-
import { isAsyncIterable } from '../../jsutils/isAsyncIterable';
7-
88
import type { DocumentNode } from '../../language/ast';
99
import { parse } from '../../language/parser';
1010

@@ -16,7 +16,11 @@ import {
1616
import { GraphQLID, GraphQLString } from '../../type/scalars';
1717
import { GraphQLSchema } from '../../type/schema';
1818

19-
import { execute } from '../execute';
19+
import type {
20+
InitialIncrementalExecutionResult,
21+
SubsequentIncrementalExecutionResult,
22+
} from '../execute';
23+
import { execute, experimentalExecuteIncrementally } from '../execute';
2024

2125
const friendType = new GraphQLObjectType({
2226
fields: {
@@ -77,23 +81,25 @@ const query = new GraphQLObjectType({
7781
name: 'Query',
7882
});
7983

80-
async function complete(document: DocumentNode) {
81-
const schema = new GraphQLSchema({ query });
84+
const schema = new GraphQLSchema({ query });
8285

83-
const result = await execute({
86+
async function complete(document: DocumentNode) {
87+
const result = await experimentalExecuteIncrementally({
8488
schema,
8589
document,
8690
rootValue: {},
8791
});
8892

89-
if (isAsyncIterable(result)) {
90-
const results = [];
91-
for await (const patch of result) {
93+
if ('initialResult' in result) {
94+
const results: Array<
95+
InitialIncrementalExecutionResult | SubsequentIncrementalExecutionResult
96+
> = [result.initialResult];
97+
for await (const patch of result.subsequentResults) {
9298
results.push(patch);
9399
}
94100
return results;
95101
}
96-
return result;
102+
return result.singleResult;
97103
}
98104

99105
describe('Execute: defer directive', () => {
@@ -670,4 +676,46 @@ describe('Execute: defer directive', () => {
670676
},
671677
]);
672678
});
679+
680+
it('original execute function throws error if anything is deferred and everything else is sync', () => {
681+
const doc = `
682+
query Deferred {
683+
... @defer { hero { id } }
684+
}
685+
`;
686+
expect(() =>
687+
execute({
688+
schema,
689+
document: parse(doc),
690+
rootValue: {},
691+
}),
692+
).to.throw(
693+
'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)',
694+
);
695+
});
696+
697+
it('original execute function resolves to error if anything is deferred and something else is async', async () => {
698+
const doc = `
699+
query Deferred {
700+
hero { slowField }
701+
... @defer { hero { id } }
702+
}
703+
`;
704+
expectJSON(
705+
await expectPromise(
706+
execute({
707+
schema,
708+
document: parse(doc),
709+
rootValue: {},
710+
}),
711+
).toResolve(),
712+
).toDeepEqual({
713+
errors: [
714+
{
715+
message:
716+
'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)',
717+
},
718+
],
719+
});
720+
});
673721
});

src/execution/__tests__/lists-test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { GraphQLSchema } from '../../type/schema';
1414

1515
import { buildSchema } from '../../utilities/buildASTSchema';
1616

17-
import type { AsyncExecutionResult, ExecutionResult } from '../execute';
17+
import type { ExecutionResult } from '../execute';
1818
import { execute, executeSync } from '../execute';
1919

2020
describe('Execute: Accepts any iterable as list value', () => {
@@ -85,9 +85,7 @@ describe('Execute: Accepts async iterables as list value', () => {
8585

8686
function completeObjectList(
8787
resolve: GraphQLFieldResolver<{ index: number }, unknown>,
88-
): PromiseOrValue<
89-
ExecutionResult | AsyncGenerator<AsyncExecutionResult, void, void>
90-
> {
88+
): PromiseOrValue<ExecutionResult> {
9189
const schema = new GraphQLSchema({
9290
query: new GraphQLObjectType({
9391
name: 'Query',

src/execution/__tests__/mutations-test.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import { describe, it } from 'mocha';
44
import { expectJSON } from '../../__testUtils__/expectJSON';
55
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick';
66

7-
import { isAsyncIterable } from '../../jsutils/isAsyncIterable';
8-
97
import { parse } from '../../language/parser';
108

119
import { GraphQLObjectType } from '../../type/definition';
1210
import { GraphQLInt } from '../../type/scalars';
1311
import { GraphQLSchema } from '../../type/schema';
1412

15-
import { execute, executeSync } from '../execute';
13+
import {
14+
execute,
15+
executeSync,
16+
experimentalExecuteIncrementally,
17+
} from '../execute';
1618

1719
class NumberHolder {
1820
theNumber: number;
@@ -216,15 +218,16 @@ describe('Execute: Handles mutation execution ordering', () => {
216218
`);
217219

218220
const rootValue = new Root(6);
219-
const mutationResult = await execute({
221+
const mutationResult = await experimentalExecuteIncrementally({
220222
schema,
221223
document,
222224
rootValue,
223225
});
224226
const patches = [];
225227

226-
assert(isAsyncIterable(mutationResult));
227-
for await (const patch of mutationResult) {
228+
assert('initialResult' in mutationResult);
229+
patches.push(mutationResult.initialResult);
230+
for await (const patch of mutationResult.subsequentResults) {
228231
patches.push(patch);
229232
}
230233

@@ -291,15 +294,16 @@ describe('Execute: Handles mutation execution ordering', () => {
291294
`);
292295

293296
const rootValue = new Root(6);
294-
const mutationResult = await execute({
297+
const mutationResult = await experimentalExecuteIncrementally({
295298
schema,
296299
document,
297300
rootValue,
298301
});
299302
const patches = [];
300303

301-
assert(isAsyncIterable(mutationResult));
302-
for await (const patch of mutationResult) {
304+
assert('initialResult' in mutationResult);
305+
patches.push(mutationResult.initialResult);
306+
for await (const patch of mutationResult.subsequentResults) {
303307
patches.push(patch);
304308
}
305309

src/execution/__tests__/nonnull-test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { GraphQLSchema } from '../../type/schema';
1313

1414
import { buildSchema } from '../../utilities/buildASTSchema';
1515

16-
import type { AsyncExecutionResult, ExecutionResult } from '../execute';
16+
import type { ExecutionResult } from '../execute';
1717
import { execute, executeSync } from '../execute';
1818

1919
const syncError = new Error('sync');
@@ -111,9 +111,7 @@ const schema = buildSchema(`
111111
function executeQuery(
112112
query: string,
113113
rootValue: unknown,
114-
): PromiseOrValue<
115-
ExecutionResult | AsyncGenerator<AsyncExecutionResult, void, void>
116-
> {
114+
): PromiseOrValue<ExecutionResult> {
117115
return execute({ schema, document: parse(query), rootValue });
118116
}
119117

0 commit comments

Comments
 (0)