Skip to content

Commit db643e9

Browse files
authored
createSourceEventStream: introduce named arguments and deprecate positional arguments (#3634)
Deprecates the positional arguments to createSourceEventStream, to be removed in the next major version, in favor of named arguments. Motivation: 1. aligns createSourceEventStream with the other exported entrypoints graphql, execute, and subscribe 2. allows simplification of mapSourceToResponse suggested by @IvanGoncharov
1 parent 6d42ced commit db643e9

File tree

2 files changed

+95
-52
lines changed

2 files changed

+95
-52
lines changed

src/execution/__tests__/subscribe-test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ function subscribeWithBadFn(
191191

192192
return expectEqualPromisesOrValues(
193193
subscribe({ schema, document }),
194-
createSourceEventStream(schema, document),
194+
createSourceEventStream({ schema, document }),
195195
);
196196
}
197197

@@ -421,6 +421,49 @@ describe('Subscription Initialization Phase', () => {
421421
expect(() => subscribe({ schema })).to.throw('Must provide document.');
422422
});
423423

424+
it('Deprecated: allows positional arguments to createSourceEventStream', () => {
425+
async function* fooGenerator() {
426+
/* c8 ignore next 2 */
427+
yield { foo: 'FooValue' };
428+
}
429+
430+
const schema = new GraphQLSchema({
431+
query: DummyQueryType,
432+
subscription: new GraphQLObjectType({
433+
name: 'Subscription',
434+
fields: {
435+
foo: { type: GraphQLString, subscribe: fooGenerator },
436+
},
437+
}),
438+
});
439+
const document = parse('subscription { foo }');
440+
441+
const eventStream = createSourceEventStream(schema, document);
442+
assert(isAsyncIterable(eventStream));
443+
});
444+
445+
it('Deprecated: throws an error if document is missing when using positional arguments', async () => {
446+
const schema = new GraphQLSchema({
447+
query: DummyQueryType,
448+
subscription: new GraphQLObjectType({
449+
name: 'Subscription',
450+
fields: {
451+
foo: { type: GraphQLString },
452+
},
453+
}),
454+
});
455+
456+
// @ts-expect-error
457+
expect(() => createSourceEventStream(schema, null)).to.throw(
458+
'Must provide document.',
459+
);
460+
461+
// @ts-expect-error
462+
expect(() => createSourceEventStream(schema)).to.throw(
463+
'Must provide document.',
464+
);
465+
});
466+
424467
it('resolves to an error if schema does not support subscriptions', async () => {
425468
const schema = new GraphQLSchema({ query: DummyQueryType });
426469
const document = parse('subscription { unknownField }');

src/execution/subscribe.ts

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -54,60 +54,20 @@ export function subscribe(
5454
): PromiseOrValue<
5555
AsyncGenerator<ExecutionResult, void, void> | ExecutionResult
5656
> {
57-
const {
58-
schema,
59-
document,
60-
rootValue,
61-
contextValue,
62-
variableValues,
63-
operationName,
64-
fieldResolver,
65-
subscribeFieldResolver,
66-
} = args;
67-
68-
const resultOrStream = createSourceEventStream(
69-
schema,
70-
document,
71-
rootValue,
72-
contextValue,
73-
variableValues,
74-
operationName,
75-
subscribeFieldResolver,
76-
);
57+
const resultOrStream = createSourceEventStream(args);
7758

7859
if (isPromise(resultOrStream)) {
7960
return resultOrStream.then((resolvedResultOrStream) =>
80-
mapSourceToResponse(
81-
schema,
82-
document,
83-
resolvedResultOrStream,
84-
contextValue,
85-
variableValues,
86-
operationName,
87-
fieldResolver,
88-
),
61+
mapSourceToResponse(resolvedResultOrStream, args),
8962
);
9063
}
9164

92-
return mapSourceToResponse(
93-
schema,
94-
document,
95-
resultOrStream,
96-
contextValue,
97-
variableValues,
98-
operationName,
99-
fieldResolver,
100-
);
65+
return mapSourceToResponse(resultOrStream, args);
10166
}
10267

10368
function mapSourceToResponse(
104-
schema: GraphQLSchema,
105-
document: DocumentNode,
10669
resultOrStream: ExecutionResult | AsyncIterable<unknown>,
107-
contextValue?: unknown,
108-
variableValues?: Maybe<{ readonly [variable: string]: unknown }>,
109-
operationName?: Maybe<string>,
110-
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>,
70+
args: ExecutionArgs,
11171
): PromiseOrValue<
11272
AsyncGenerator<ExecutionResult, void, void> | ExecutionResult
11373
> {
@@ -123,17 +83,42 @@ function mapSourceToResponse(
12383
// "ExecuteQuery" algorithm, for which `execute` is also used.
12484
return mapAsyncIterator(resultOrStream, (payload: unknown) =>
12585
execute({
126-
schema,
127-
document,
86+
...args,
12887
rootValue: payload,
129-
contextValue,
130-
variableValues,
131-
operationName,
132-
fieldResolver,
13388
}),
13489
);
13590
}
13691

92+
type BackwardsCompatibleArgs =
93+
| [options: ExecutionArgs]
94+
| [
95+
schema: ExecutionArgs['schema'],
96+
document: ExecutionArgs['document'],
97+
rootValue?: ExecutionArgs['rootValue'],
98+
contextValue?: ExecutionArgs['contextValue'],
99+
variableValues?: ExecutionArgs['variableValues'],
100+
operationName?: ExecutionArgs['operationName'],
101+
subscribeFieldResolver?: ExecutionArgs['subscribeFieldResolver'],
102+
];
103+
104+
function toNormalizedArgs(args: BackwardsCompatibleArgs): ExecutionArgs {
105+
const firstArg = args[0];
106+
if ('document' in firstArg) {
107+
return firstArg;
108+
}
109+
110+
return {
111+
schema: firstArg,
112+
// FIXME: when underlying TS bug fixed, see https://github.com/microsoft/TypeScript/issues/31613
113+
document: args[1] as DocumentNode,
114+
rootValue: args[2],
115+
contextValue: args[3],
116+
variableValues: args[4],
117+
operationName: args[5],
118+
subscribeFieldResolver: args[6],
119+
};
120+
}
121+
137122
/**
138123
* Implements the "CreateSourceEventStream" algorithm described in the
139124
* GraphQL specification, resolving the subscription source event stream.
@@ -162,6 +147,10 @@ function mapSourceToResponse(
162147
* or otherwise separating these two steps. For more on this, see the
163148
* "Supporting Subscriptions at Scale" information in the GraphQL specification.
164149
*/
150+
export function createSourceEventStream(
151+
args: ExecutionArgs,
152+
): PromiseOrValue<AsyncIterable<unknown> | ExecutionResult>;
153+
/** @deprecated will be removed in next major version in favor of named arguments */
165154
export function createSourceEventStream(
166155
schema: GraphQLSchema,
167156
document: DocumentNode,
@@ -170,7 +159,18 @@ export function createSourceEventStream(
170159
variableValues?: Maybe<{ readonly [variable: string]: unknown }>,
171160
operationName?: Maybe<string>,
172161
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>,
173-
): PromiseOrValue<AsyncIterable<unknown> | ExecutionResult> {
162+
): PromiseOrValue<AsyncIterable<unknown> | ExecutionResult>;
163+
export function createSourceEventStream(...rawArgs: BackwardsCompatibleArgs) {
164+
const {
165+
schema,
166+
document,
167+
rootValue,
168+
contextValue,
169+
variableValues,
170+
operationName,
171+
subscribeFieldResolver,
172+
} = toNormalizedArgs(rawArgs);
173+
174174
// If arguments are missing or incorrectly typed, this is an internal
175175
// developer mistake which should throw an early error.
176176
assertValidExecutionArguments(schema, document, variableValues);

0 commit comments

Comments
 (0)