Skip to content

Commit c78e33f

Browse files
committed
Test: Missing subscription field should not be an "internal error"
A remaining issue with subscriptions is attempting to subscribe to a missing field. The existing implementation creates an internal error which is an improper blame since there is no possibility to fix it internally - it is an issue with the query. For consistency with the same scenario for query/mutation operations, this simply returns nothing. I interpret the equivalent of "undefined" for a subscribe operation as an empty iteration. Note: this will require spec changes, however spec changes are necessary either way to resolve the ambiguity of this exact scenario.
1 parent 8038cef commit c78e33f

File tree

3 files changed

+42
-11
lines changed

3 files changed

+42
-11
lines changed

src/subscription/__tests__/subscribe-test.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ function emailSchemaWithResolvers(subscribeFn, resolveFn) {
7272
type: EmailEventType,
7373
resolve: resolveFn,
7474
subscribe: subscribeFn,
75-
// TODO: remove
7675
args: {
7776
priority: {type: GraphQLInt}
7877
}
@@ -347,7 +346,7 @@ describe('Subscription Initialization Phase', () => {
347346
);
348347
});
349348

350-
it('throws an error for unknown root field', async () => {
349+
it('unknown field should result in closed subscription', async () => {
351350
const ast = parse(`
352351
subscription {
353352
unknownField
@@ -356,10 +355,10 @@ describe('Subscription Initialization Phase', () => {
356355

357356
const pubsub = new EventEmitter();
358357

359-
expectPromiseToThrow(
360-
() => createSubscription(pubsub, emailSchema, ast),
361-
'This subscription is not defined by the schema.'
362-
);
358+
const { subscription } = await createSubscription(pubsub, emailSchema, ast);
359+
360+
const payload = await subscription.next();
361+
expect(payload).to.deep.equal({ done: true, value: undefined });
363362
});
364363

365364
it('throws an error if subscribe does not return an iterator', async () => {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright (c) 2017-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import { $$asyncIterator } from 'iterall';
11+
12+
/**
13+
* Returns an AsyncIterable which yields no values.
14+
*/
15+
export default function emptyAsyncIterator(): AsyncIterator<void> {
16+
// TODO: Flow doesn't support symbols as keys:
17+
// https://github.com/facebook/flow/issues/3258
18+
return ({
19+
next() {
20+
return Promise.resolve({ value: undefined, done: true });
21+
},
22+
return() {
23+
return Promise.resolve({ value: undefined, done: true });
24+
},
25+
throw(error) {
26+
return Promise.reject(error);
27+
},
28+
[$$asyncIterator]() {
29+
return this;
30+
},
31+
}: any);
32+
}

src/subscription/subscribe.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
responsePathAsArray,
2424
} from '../execution/execute';
2525
import { GraphQLSchema } from '../type/schema';
26-
import invariant from '../jsutils/invariant';
26+
import emptyAsyncIterator from './emptyAsyncIterator';
2727
import mapAsyncIterator from './mapAsyncIterator';
2828

2929
import type { ObjMap } from '../jsutils/ObjMap';
@@ -227,10 +227,10 @@ export function createSourceEventStream(
227227
const fieldNodes = fields[responseName];
228228
const fieldNode = fieldNodes[0];
229229
const fieldDef = getFieldDef(schema, type, fieldNode.name.value);
230-
invariant(
231-
fieldDef,
232-
'This subscription is not defined by the schema.'
233-
);
230+
231+
if (!fieldDef) {
232+
return resolve(emptyAsyncIterator());
233+
}
234234

235235
// Call the `subscribe()` resolver or the default resolver to produce an
236236
// AsyncIterable yielding raw payloads.

0 commit comments

Comments
 (0)