Skip to content

Commit 557a773

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 cf0ded8 commit 557a773

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
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
},
@@ -332,7 +331,7 @@ describe('Subscription Initialization Phase', () => {
332331
);
333332
});
334333

335-
it('throws an error for unknown root field', async () => {
334+
it('unknown field should result in closed subscription', async () => {
336335
const ast = parse(`
337336
subscription {
338337
unknownField
@@ -341,10 +340,10 @@ describe('Subscription Initialization Phase', () => {
341340

342341
const pubsub = new EventEmitter();
343342

344-
expectPromiseToThrow(
345-
() => createSubscription(pubsub, emailSchema, ast),
346-
'This subscription is not defined by the schema.',
347-
);
343+
const { subscription } = await createSubscription(pubsub, emailSchema, ast);
344+
345+
const payload = await subscription.next();
346+
expect(payload).to.deep.equal({ done: true, value: undefined });
348347
});
349348

350349
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 & 2 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';
@@ -224,7 +224,10 @@ export function createSourceEventStream(
224224
const fieldNodes = fields[responseName];
225225
const fieldNode = fieldNodes[0];
226226
const fieldDef = getFieldDef(schema, type, fieldNode.name.value);
227-
invariant(fieldDef, 'This subscription is not defined by the schema.');
227+
228+
if (!fieldDef) {
229+
return resolve(emptyAsyncIterator());
230+
}
228231

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

0 commit comments

Comments
 (0)