Skip to content

Commit 3a61463

Browse files
committed
WIP
1 parent f531737 commit 3a61463

File tree

2 files changed

+334
-0
lines changed

2 files changed

+334
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { expectJSON } from '../../__testUtils__/expectJSON.js';
5+
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
6+
7+
import type { DocumentNode } from '../../language/ast.js';
8+
import { parse } from '../../language/parser.js';
9+
10+
import {
11+
GraphQLObjectType,
12+
GraphQLSchema,
13+
GraphQLString,
14+
} from '../../type/index.js';
15+
16+
import { buildSchema } from '../../utilities/buildASTSchema.js';
17+
18+
import { execute, experimentalExecuteIncrementally } from '../execute.js';
19+
import type {
20+
InitialIncrementalExecutionResult,
21+
SubsequentIncrementalExecutionResult,
22+
} from '../types.js';
23+
24+
async function complete(
25+
document: DocumentNode,
26+
rootValue: unknown,
27+
abortSignal: AbortSignal,
28+
) {
29+
const result = await experimentalExecuteIncrementally({
30+
schema,
31+
document,
32+
rootValue,
33+
abortSignal,
34+
});
35+
36+
if ('initialResult' in result) {
37+
const results: Array<
38+
InitialIncrementalExecutionResult | SubsequentIncrementalExecutionResult
39+
> = [result.initialResult];
40+
for await (const patch of result.subsequentResults) {
41+
results.push(patch);
42+
}
43+
return results;
44+
}
45+
return result;
46+
}
47+
48+
const schema = buildSchema(/* GraphQL */ `
49+
type Todo {
50+
id: ID!
51+
text: String!
52+
completed: Boolean!
53+
author: User
54+
}
55+
56+
type User {
57+
id: ID!
58+
name: String!
59+
}
60+
61+
type Query {
62+
todo: Todo
63+
}
64+
65+
type Mutation {
66+
foo: String
67+
bar: String
68+
}
69+
`);
70+
71+
describe('Abort Signal', () => {
72+
it('should stop the execution when aborted in resolver', async () => {
73+
const abortController = new AbortController();
74+
const document = parse(/* GraphQL */ `
75+
query {
76+
todo {
77+
id
78+
author {
79+
id
80+
}
81+
}
82+
}
83+
`);
84+
const result = await execute({
85+
document,
86+
schema,
87+
abortSignal: abortController.signal,
88+
rootValue: {
89+
todo() {
90+
abortController.abort('Aborted');
91+
return {
92+
id: '1',
93+
text: 'Hello, World!',
94+
completed: false,
95+
/* c8 ignore next 3 */
96+
author: () => {
97+
expect.fail('Should not be called');
98+
},
99+
};
100+
},
101+
},
102+
});
103+
104+
expectJSON(result).toDeepEqual({
105+
data: {
106+
todo: null,
107+
},
108+
errors: [
109+
{
110+
locations: [
111+
{
112+
column: 9,
113+
line: 3,
114+
},
115+
],
116+
message: 'Aborted',
117+
path: ['todo'],
118+
},
119+
],
120+
});
121+
});
122+
123+
it('should stop the execution when aborted in deferred resolver', async () => {
124+
const abortController = new AbortController();
125+
const document = parse(/* GraphQL */ `
126+
query {
127+
todo {
128+
id
129+
... on Todo @defer {
130+
text
131+
}
132+
}
133+
}
134+
`);
135+
const result = await complete(
136+
document,
137+
{
138+
todo() {
139+
return {
140+
id: '1',
141+
text: () => {
142+
abortController.abort('Aborted');
143+
},
144+
};
145+
},
146+
},
147+
abortController.signal,
148+
);
149+
150+
expectJSON(result).toDeepEqual([
151+
{
152+
data: {
153+
todo: {
154+
id: '1',
155+
},
156+
},
157+
pending: [{ id: '0', path: ['todo'] }],
158+
hasNext: true,
159+
},
160+
{
161+
completed: [
162+
{
163+
errors: [
164+
{
165+
locations: [
166+
{
167+
column: 13,
168+
line: 6,
169+
},
170+
],
171+
message: 'Aborted',
172+
path: ['todo', 'text'],
173+
},
174+
],
175+
id: '0',
176+
},
177+
],
178+
hasNext: false,
179+
},
180+
]);
181+
});
182+
183+
it('should stop the for serial mutation execution', async () => {
184+
const abortController = new AbortController();
185+
const document = parse(/* GraphQL */ `
186+
mutation {
187+
foo
188+
bar
189+
}
190+
`);
191+
const result = await execute({
192+
document,
193+
schema,
194+
abortSignal: abortController.signal,
195+
rootValue: {
196+
foo() {
197+
abortController.abort('Aborted');
198+
return 'baz';
199+
},
200+
/* c8 ignore next 3 */
201+
bar() {
202+
expect.fail('Should not be called');
203+
},
204+
},
205+
});
206+
207+
expectJSON(result).toDeepEqual({
208+
data: null,
209+
errors: [
210+
{
211+
message: 'Aborted',
212+
},
213+
],
214+
});
215+
});
216+
217+
it('should stop the execution when aborted pre-execute', async () => {
218+
const abortController = new AbortController();
219+
const document = parse(/* GraphQL */ `
220+
query {
221+
todo {
222+
id
223+
author {
224+
id
225+
}
226+
}
227+
}
228+
`);
229+
abortController.abort('Aborted');
230+
const result = await execute({
231+
document,
232+
schema,
233+
abortSignal: abortController.signal,
234+
rootValue: {
235+
/* c8 ignore next 3 */
236+
todo() {
237+
return {};
238+
},
239+
},
240+
});
241+
242+
expectJSON(result).toDeepEqual({
243+
data: null,
244+
errors: [
245+
{
246+
message: 'Aborted',
247+
},
248+
],
249+
});
250+
});
251+
252+
it('exits early on abort mid-execution', async () => {
253+
const asyncObjectType = new GraphQLObjectType({
254+
name: 'AsyncObject',
255+
fields: {
256+
field: {
257+
type: GraphQLString,
258+
/* c8 ignore next 3 */
259+
resolve() {
260+
expect.fail('Should not be called');
261+
},
262+
},
263+
},
264+
});
265+
266+
const newSchema = new GraphQLSchema({
267+
query: new GraphQLObjectType({
268+
name: 'Query',
269+
fields: {
270+
asyncObject: {
271+
type: asyncObjectType,
272+
async resolve() {
273+
await resolveOnNextTick();
274+
return {};
275+
},
276+
},
277+
},
278+
}),
279+
});
280+
281+
const document = parse(`
282+
{
283+
asyncObject {
284+
field
285+
}
286+
}
287+
`);
288+
289+
const abortController = new AbortController();
290+
291+
const result = execute({
292+
schema: newSchema,
293+
document,
294+
abortSignal: abortController.signal,
295+
});
296+
297+
abortController.abort('This operation was aborted');
298+
299+
expectJSON(await result).toDeepEqual({
300+
data: { asyncObject: null },
301+
errors: [
302+
{
303+
message: 'This operation was aborted',
304+
locations: [{ line: 3, column: 9 }],
305+
path: ['asyncObject'],
306+
},
307+
],
308+
});
309+
});
310+
});

0 commit comments

Comments
 (0)