Skip to content

Commit ebb5efb

Browse files
committed
add validation rule preventing defer or stream that cannot be disabled on subscriptions
1 parent fc890c3 commit ebb5efb

File tree

4 files changed

+397
-0
lines changed

4 files changed

+397
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
import { describe, it } from 'mocha';
2+
3+
import { buildSchema } from '../../utilities/buildASTSchema.js';
4+
5+
import { DeferStreamDirectiveOnValidOperationsRule } from '../rules/DeferStreamDirectiveOnValidOperationsRule.js';
6+
7+
import { expectValidationErrorsWithSchema } from './harness.js';
8+
9+
function expectErrors(queryStr: string) {
10+
return expectValidationErrorsWithSchema(
11+
schema,
12+
DeferStreamDirectiveOnValidOperationsRule,
13+
queryStr,
14+
);
15+
}
16+
17+
function expectValid(queryStr: string) {
18+
expectErrors(queryStr).toDeepEqual([]);
19+
}
20+
21+
const schema = buildSchema(`
22+
type Message {
23+
body: String
24+
sender: String
25+
}
26+
27+
type SubscriptionRoot {
28+
subscriptionField: Message
29+
subscriptionListField: [Message]
30+
}
31+
32+
type MutationRoot {
33+
mutationField: Message
34+
mutationListField: [Message]
35+
}
36+
37+
type QueryRoot {
38+
message: Message
39+
messages: [Message]
40+
}
41+
42+
schema {
43+
query: QueryRoot
44+
mutation: MutationRoot
45+
subscription: SubscriptionRoot
46+
}
47+
`);
48+
49+
describe('Validate: Defer/Stream directive on valid operations', () => {
50+
it('Defer fragment spread nested in query operation', () => {
51+
expectValid(`
52+
{
53+
message {
54+
...myFragment @defer
55+
}
56+
}
57+
fragment myFragment on Message {
58+
message {
59+
body
60+
}
61+
}
62+
`);
63+
});
64+
it('Defer inline fragment spread in query operation', () => {
65+
expectValid(`
66+
{
67+
... @defer {
68+
message {
69+
body
70+
}
71+
}
72+
}
73+
`);
74+
});
75+
it('Defer fragment spread on mutation field', () => {
76+
expectValid(`
77+
mutation {
78+
mutationField {
79+
...myFragment @defer
80+
}
81+
}
82+
fragment myFragment on Message {
83+
body
84+
}
85+
`);
86+
});
87+
it('Defer inline fragment spread on mutation field', () => {
88+
expectValid(`
89+
mutation {
90+
mutationField {
91+
... @defer {
92+
body
93+
}
94+
}
95+
}
96+
`);
97+
});
98+
it('Defer fragment spread on subscription field', () => {
99+
expectErrors(`
100+
subscription {
101+
subscriptionField {
102+
...myFragment @defer
103+
}
104+
}
105+
fragment myFragment on Message {
106+
body
107+
}
108+
`).toDeepEqual([
109+
{
110+
locations: [{ column: 25, line: 4 }],
111+
message:
112+
'Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
113+
},
114+
]);
115+
});
116+
it('Defer fragment spread with boolean true if argument', () => {
117+
expectErrors(`
118+
subscription {
119+
subscriptionField {
120+
...myFragment @defer(if: true)
121+
}
122+
}
123+
fragment myFragment on Message {
124+
body
125+
}
126+
`).toDeepEqual([
127+
{
128+
locations: [{ column: 25, line: 4 }],
129+
message:
130+
'Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
131+
},
132+
]);
133+
});
134+
it('Defer fragment spread with boolean false if argument', () => {
135+
expectValid(`
136+
subscription {
137+
subscriptionField {
138+
...myFragment @defer(if: false)
139+
}
140+
}
141+
fragment myFragment on Message {
142+
body
143+
}
144+
`);
145+
});
146+
it('Defer fragment spread on query in multi operation document', () => {
147+
expectValid(`
148+
subscription MySubscription {
149+
subscriptionField {
150+
...myFragment
151+
}
152+
}
153+
query MyQuery {
154+
message {
155+
...myFragment @defer
156+
}
157+
}
158+
fragment myFragment on Message {
159+
body
160+
}
161+
`);
162+
});
163+
it('Defer fragment spread on subscription in multi operation document', () => {
164+
expectErrors(`
165+
subscription MySubscription {
166+
subscriptionField {
167+
...myFragment @defer
168+
}
169+
}
170+
query MyQuery {
171+
message {
172+
...myFragment @defer
173+
}
174+
}
175+
fragment myFragment on Message {
176+
body
177+
}
178+
`).toDeepEqual([
179+
{
180+
locations: [{ column: 25, line: 4 }],
181+
message:
182+
'Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
183+
},
184+
]);
185+
});
186+
it('Defer fragment spread with invalid if argument', () => {
187+
expectErrors(`
188+
subscription MySubscription {
189+
subscriptionField {
190+
...myFragment @defer(if: "Oops")
191+
}
192+
}
193+
fragment myFragment on Message {
194+
body
195+
}
196+
`).toDeepEqual([
197+
{
198+
locations: [{ column: 25, line: 4 }],
199+
message:
200+
'Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
201+
},
202+
]);
203+
});
204+
it('Stream on query field', () => {
205+
expectValid(`
206+
{
207+
messages @stream {
208+
name
209+
}
210+
}
211+
`);
212+
});
213+
it('Stream on mutation field', () => {
214+
expectValid(`
215+
mutation {
216+
mutationField {
217+
messages @stream
218+
}
219+
}
220+
`);
221+
});
222+
it('Stream on fragment on mutation field', () => {
223+
expectValid(`
224+
mutation {
225+
mutationField {
226+
...myFragment
227+
}
228+
}
229+
fragment myFragment on Message {
230+
messages @stream
231+
}
232+
`);
233+
});
234+
it('Stream on subscription field', () => {
235+
expectErrors(`
236+
subscription {
237+
subscriptionField {
238+
messages @stream
239+
}
240+
}
241+
`).toDeepEqual([
242+
{
243+
message:
244+
'Stream directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
245+
locations: [{ line: 4, column: 20 }],
246+
},
247+
]);
248+
});
249+
it('Stream on fragment on subscription field', () => {
250+
expectErrors(`
251+
subscription {
252+
subscriptionField {
253+
...myFragment
254+
}
255+
}
256+
fragment myFragment on Message {
257+
messages @stream
258+
}
259+
`).toDeepEqual([
260+
{
261+
message:
262+
'Stream directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
263+
locations: [{ line: 8, column: 18 }],
264+
},
265+
]);
266+
});
267+
it('Stream on fragment on query in multi operation document', () => {
268+
expectValid(`
269+
subscription MySubscription {
270+
subscriptionField {
271+
message
272+
}
273+
}
274+
query MyQuery {
275+
message {
276+
...myFragment
277+
}
278+
}
279+
fragment myFragment on Message {
280+
messages @stream
281+
}
282+
`);
283+
});
284+
it('Stream on subscription in multi operation document', () => {
285+
expectErrors(`
286+
query MyQuery {
287+
message {
288+
...myFragment
289+
}
290+
}
291+
subscription MySubscription {
292+
subscriptionField {
293+
message {
294+
...myFragment
295+
}
296+
}
297+
}
298+
fragment myFragment on Message {
299+
messages @stream
300+
}
301+
`).toDeepEqual([
302+
{
303+
message:
304+
'Stream directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.',
305+
locations: [{ line: 15, column: 18 }],
306+
},
307+
]);
308+
});
309+
});

src/validation/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ export { DeferStreamDirectiveLabelRule } from './rules/DeferStreamDirectiveLabel
1212
// Spec Section: "Defer And Stream Directives Are Used On Valid Root Field"
1313
export { DeferStreamDirectiveOnRootFieldRule } from './rules/DeferStreamDirectiveOnRootFieldRule.js';
1414

15+
// Spec Section: "Defer And Stream Directives Are Used On Valid Operations"
16+
export { DeferStreamDirectiveOnValidOperationsRule } from './rules/DeferStreamDirectiveOnValidOperationsRule.js';
17+
1518
// Spec Section: "Executable Definitions"
1619
export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule.js';
1720

0 commit comments

Comments
 (0)