diff --git a/src/execution/__tests__/subscribe-test.ts b/src/execution/__tests__/subscribe-test.ts index 89b5cc07be..7bc1f3e55c 100644 --- a/src/execution/__tests__/subscribe-test.ts +++ b/src/execution/__tests__/subscribe-test.ts @@ -26,6 +26,7 @@ import { createSourceEventStream, executeSubscriptionEvent, subscribe, + validateExecutionArgs, } from '../execute.js'; import type { ExecutionResult } from '../Executor.js'; @@ -187,15 +188,42 @@ function subscribeWithBadFn( function subscribeWithBadArgs( args: ExecutionArgs, ): PromiseOrValue> { + const validatedExecutionArgs = validateExecutionArgs(args); + const sourceEventStreamResult = + 'schema' in validatedExecutionArgs + ? createSourceEventStream(validatedExecutionArgs) + : { errors: validatedExecutionArgs }; + return expectEqualPromisesOrValues([ subscribe(args), - createSourceEventStream(args), + sourceEventStreamResult, ]); } /* eslint-disable @typescript-eslint/require-await */ // Check all error cases when initializing the subscription. describe('Subscription Initialization Phase', () => { + it('throws for legacy ExecutionArgs passed to createSourceEventStream', () => { + const schema = new GraphQLSchema({ + query: DummyQueryType, + subscription: new GraphQLObjectType({ + name: 'Subscription', + fields: { + foo: { type: GraphQLString }, + }, + }), + }); + + expect(() => + createSourceEventStream({ + schema, + document: parse('subscription { foo }'), + } as never), + ).to.throw( + 'Passing ExecutionArgs to createSourceEventStream() was removed in graphql-js@17.0.0; call validateExecutionArgs() first and pass the result instead, or use subscribe() for the full subscription pipeline.', + ); + }); + it('accepts multiple subscription fields defined in schema', async () => { const schema = new GraphQLSchema({ query: DummyQueryType, diff --git a/src/execution/execute.ts b/src/execution/execute.ts index 4d71b30e85..9cf865c1ca 100644 --- a/src/execution/execute.ts +++ b/src/execution/execute.ts @@ -227,7 +227,7 @@ export function subscribe( return { errors: validatedExecutionArgs }; } - const resultOrStream = createSourceEventStreamImpl(validatedExecutionArgs); + const resultOrStream = createSourceEventStream(validatedExecutionArgs); if (isPromise(resultOrStream)) { return resultOrStream.then((resolvedResultOrStream) => @@ -240,12 +240,13 @@ export function subscribe( /** * Implements the "CreateSourceEventStream" algorithm described in the - * GraphQL specification, resolving the subscription source event stream. + * GraphQL specification, resolving the subscription source event stream for a + * previously validated subscription request. * * Returns a Promise which resolves to either an AsyncIterable (if successful) - * or an ExecutionResult (error). The promise will be rejected if the schema or - * other arguments to this function are invalid, or if the resolved event stream - * is not an async iterable. + * or an ExecutionResult (error). The promise will be rejected if the validated + * execution arguments are invalid, or if the resolved event stream is not an + * async iterable. * * If the client-provided arguments to this function do not result in a * compliant subscription, a GraphQL Response (ExecutionResult) with @@ -267,18 +268,26 @@ export function subscribe( * "Supporting Subscriptions at Scale" information in the GraphQL specification. */ export function createSourceEventStream( - args: ExecutionArgs, + validatedExecutionArgs: ValidatedExecutionArgs, ): PromiseOrValue | ExecutionResult> { - // If a valid execution context cannot be created due to incorrect arguments, - // a "Response" with only errors is returned. - const validatedExecutionArgs = validateExecutionArgs(args); - - // Return early errors if execution context failed. - if (!('schema' in validatedExecutionArgs)) { - return { errors: validatedExecutionArgs }; + if (!('operation' in validatedExecutionArgs)) { + throw new GraphQLError( + 'Passing ExecutionArgs to createSourceEventStream() was removed in graphql-js@17.0.0; call validateExecutionArgs() first and pass the result instead, or use subscribe() for the full subscription pipeline.', + ); } - return createSourceEventStreamImpl(validatedExecutionArgs); + try { + const eventStream = executeSubscription(validatedExecutionArgs); + if (isPromise(eventStream)) { + return eventStream.then(undefined, (error: unknown) => ({ + errors: [ensureGraphQLError(error)], + })); + } + + return eventStream; + } catch (error) { + return { errors: [ensureGraphQLError(error)] }; + } } export interface ExecutionArgs { @@ -534,23 +543,6 @@ function mapSourceToResponse( return mapAsyncIterable(resultOrStream, mapFn); } -function createSourceEventStreamImpl( - validatedExecutionArgs: ValidatedExecutionArgs, -): PromiseOrValue | ExecutionResult> { - try { - const eventStream = executeSubscription(validatedExecutionArgs); - if (isPromise(eventStream)) { - return eventStream.then(undefined, (error: unknown) => ({ - errors: [ensureGraphQLError(error)], - })); - } - - return eventStream; - } catch (error) { - return { errors: [ensureGraphQLError(error)] }; - } -} - function executeSubscription( validatedExecutionArgs: ValidatedExecutionArgs, ): PromiseOrValue> {