Skip to content

Commit 522f495

Browse files
authored
original execute should throw if defer/stream directives are present (#3722)
1 parent 1bf71ee commit 522f495

File tree

3 files changed

+60
-20
lines changed

3 files changed

+60
-20
lines changed

src/execution/__tests__/defer-test.ts

+9-16
Original file line numberDiff line numberDiff line change
@@ -723,21 +723,14 @@ describe('Execute: defer directive', () => {
723723
... @defer { hero { id } }
724724
}
725725
`;
726-
expectJSON(
727-
await expectPromise(
728-
execute({
729-
schema,
730-
document: parse(doc),
731-
rootValue: {},
732-
}),
733-
).toResolve(),
734-
).toDeepEqual({
735-
errors: [
736-
{
737-
message:
738-
'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)',
739-
},
740-
],
741-
});
726+
await expectPromise(
727+
execute({
728+
schema,
729+
document: parse(doc),
730+
rootValue: {},
731+
}),
732+
).toRejectWith(
733+
'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)',
734+
);
742735
});
743736
});

src/execution/__tests__/executor-test.ts

+38
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import {
1717
GraphQLScalarType,
1818
GraphQLUnionType,
1919
} from '../../type/definition.js';
20+
import {
21+
GraphQLDeferDirective,
22+
GraphQLStreamDirective,
23+
} from '../../type/directives.js';
2024
import {
2125
GraphQLBoolean,
2226
GraphQLInt,
@@ -914,6 +918,40 @@ describe('Execute: Handles basic execution tasks', () => {
914918
expect(result).to.deep.equal({ data: { a: 'b' } });
915919
});
916920

921+
it('errors when using original execute with schemas including experimental @defer directive', () => {
922+
const schema = new GraphQLSchema({
923+
query: new GraphQLObjectType({
924+
name: 'Q',
925+
fields: {
926+
a: { type: GraphQLString },
927+
},
928+
}),
929+
directives: [GraphQLDeferDirective],
930+
});
931+
const document = parse('query Q { a }');
932+
933+
expect(() => execute({ schema, document })).to.throw(
934+
'The provided schema unexpectedly contains experimental directives (@defer or @stream). These directives may only be utilized if experimental execution features are explicitly enabled.',
935+
);
936+
});
937+
938+
it('errors when using original execute with schemas including experimental @stream directive', () => {
939+
const schema = new GraphQLSchema({
940+
query: new GraphQLObjectType({
941+
name: 'Q',
942+
fields: {
943+
a: { type: GraphQLString },
944+
},
945+
}),
946+
directives: [GraphQLStreamDirective],
947+
});
948+
const document = parse('query Q { a }');
949+
950+
expect(() => execute({ schema, document })).to.throw(
951+
'The provided schema unexpectedly contains experimental directives (@defer or @stream). These directives may only be utilized if experimental execution features are explicitly enabled.',
952+
);
953+
});
954+
917955
it('resolves to an error if schema does not support operation', () => {
918956
const schema = new GraphQLSchema({ assumeValid: true });
919957

src/execution/execute.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ export interface ExecutionArgs {
263263
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
264264
}
265265

266+
const UNEXPECTED_EXPERIMENTAL_DIRECTIVES =
267+
'The provided schema unexpectedly contains experimental directives (@defer or @stream). These directives may only be utilized if experimental execution features are explicitly enabled.';
268+
266269
const UNEXPECTED_MULTIPLE_PAYLOADS =
267270
'Executing this GraphQL operation would unexpectedly produce multiple payloads (due to @defer or @stream directive)';
268271

@@ -278,24 +281,30 @@ const UNEXPECTED_MULTIPLE_PAYLOADS =
278281
*
279282
* This function does not support incremental delivery (`@defer` and `@stream`).
280283
* If an operation which would defer or stream data is executed with this
281-
* function, it will throw or resolve to an object containing an error instead.
284+
* function, it will throw or return a rejected promise.
282285
* Use `experimentalExecuteIncrementally` if you want to support incremental
283286
* delivery.
284287
*/
285288
export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
289+
if (args.schema.getDirective('defer') || args.schema.getDirective('stream')) {
290+
throw new Error(UNEXPECTED_EXPERIMENTAL_DIRECTIVES);
291+
}
292+
286293
const result = experimentalExecuteIncrementally(args);
287294
if (!isPromise(result)) {
288295
if ('initialResult' in result) {
296+
// This can happen if the operation contains @defer or @stream directives
297+
// and is not validated prior to execution
289298
throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS);
290299
}
291300
return result;
292301
}
293302

294303
return result.then((incrementalResult) => {
295304
if ('initialResult' in incrementalResult) {
296-
return {
297-
errors: [new GraphQLError(UNEXPECTED_MULTIPLE_PAYLOADS)],
298-
};
305+
// This can happen if the operation contains @defer or @stream directives
306+
// and is not validated prior to execution
307+
throw new Error(UNEXPECTED_MULTIPLE_PAYLOADS);
299308
}
300309
return incrementalResult;
301310
});

0 commit comments

Comments
 (0)