Skip to content

Commit c5b19f8

Browse files
committed
add ability to use perEventExecutor (#4211)
by exporting executeSubscriptionEvent() and adding option for to provide a custom fn addresses #894 cf. #2485 , #3071
1 parent bb2dfdb commit c5b19f8

4 files changed

Lines changed: 66 additions & 7 deletions

File tree

src/execution/__tests__/subscribe-test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ import {
2121
import { GraphQLSchema } from '../../type/schema.js';
2222

2323
import type { ExecutionArgs, ExecutionResult } from '../execute.js';
24-
import { createSourceEventStream, subscribe } from '../execute.js';
24+
import {
25+
createSourceEventStream,
26+
executeSubscriptionEvent,
27+
subscribe,
28+
} from '../execute.js';
2529

2630
import { SimplePubSub } from './simplePubSub.js';
2731

@@ -326,6 +330,45 @@ describe('Subscription Initialization Phase', () => {
326330
});
327331
});
328332

333+
it('uses a custom default perEventExecutor', async () => {
334+
const schema = new GraphQLSchema({
335+
query: DummyQueryType,
336+
subscription: new GraphQLObjectType({
337+
name: 'Subscription',
338+
fields: {
339+
foo: { type: GraphQLString },
340+
},
341+
}),
342+
});
343+
344+
async function* fooGenerator() {
345+
yield { foo: 'FooValue' };
346+
}
347+
348+
let count = 0;
349+
const subscription = subscribe({
350+
schema,
351+
document: parse('subscription { foo }'),
352+
rootValue: { foo: fooGenerator },
353+
perEventExecutor: (validatedArgs) => {
354+
count++;
355+
return executeSubscriptionEvent(validatedArgs);
356+
},
357+
});
358+
assert(isAsyncIterable(subscription));
359+
360+
expect(await subscription.next()).to.deep.equal({
361+
done: false,
362+
value: { data: { foo: 'FooValue' } },
363+
});
364+
365+
expect(await subscription.next()).to.deep.equal({
366+
done: true,
367+
value: undefined,
368+
});
369+
expect(count).to.equal(1);
370+
});
371+
329372
it('should only resolve the first field of invalid multi-field', async () => {
330373
async function* fooGenerator() {
331374
yield { foo: 'FooValue' };

src/execution/execute.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ export interface ValidatedExecutionArgs {
127127
fieldResolver: GraphQLFieldResolver<any, any>;
128128
typeResolver: GraphQLTypeResolver<any, any>;
129129
subscribeFieldResolver: GraphQLFieldResolver<any, any>;
130+
perEventExecutor: (
131+
validatedExecutionArgs: ValidatedExecutionArgs,
132+
) => PromiseOrValue<ExecutionResult>;
130133
}
131134

132135
/**
@@ -208,6 +211,11 @@ export interface ExecutionArgs {
208211
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
209212
typeResolver?: Maybe<GraphQLTypeResolver<any, any>>;
210213
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
214+
perEventExecutor?: Maybe<
215+
(
216+
validatedExecutionArgs: ValidatedExecutionArgs,
217+
) => PromiseOrValue<ExecutionResult>
218+
>;
211219
/** Additional execution options. */
212220
options?: {
213221
/** Set the maximum number of errors allowed for coercing (defaults to 50). */
@@ -235,7 +243,7 @@ export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
235243
return { errors: validatedExecutionArgs };
236244
}
237245

238-
return executeOperation(validatedExecutionArgs);
246+
return executeQueryOrMutationOrSubscriptionEvent(validatedExecutionArgs);
239247
}
240248

241249
/**
@@ -253,7 +261,7 @@ export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
253261
* at which point we still log the error and null the parent field, which
254262
* in this case is the entire response.
255263
*/
256-
function executeOperation(
264+
function executeQueryOrMutationOrSubscriptionEvent(
257265
validatedExecutionArgs: ValidatedExecutionArgs,
258266
): PromiseOrValue<ExecutionResult> {
259267
const exeContext: ExecutionContext = {
@@ -352,6 +360,7 @@ export function validateExecutionArgs(
352360
fieldResolver,
353361
typeResolver,
354362
subscribeFieldResolver,
363+
perEventExecutor,
355364
options,
356365
} = args;
357366

@@ -425,6 +434,7 @@ export function validateExecutionArgs(
425434
fieldResolver: fieldResolver ?? defaultFieldResolver,
426435
typeResolver: typeResolver ?? defaultTypeResolver,
427436
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
437+
perEventExecutor: perEventExecutor ?? executeSubscriptionEvent,
428438
};
429439
}
430440

@@ -1419,18 +1429,22 @@ function mapSourceToResponse(
14191429
// For each payload yielded from a subscription, map it over the normal
14201430
// GraphQL `execute` function, with `payload` as the rootValue.
14211431
// This implements the "MapSourceToResponseEvent" algorithm described in
1422-
// the GraphQL specification. The `execute` function provides the
1423-
// "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
1424-
// "ExecuteQuery" algorithm, for which `execute` is also used.
1432+
// the GraphQL specification..
14251433
return mapAsyncIterable(resultOrStream, (payload: unknown) => {
14261434
const perEventExecutionArgs: ValidatedExecutionArgs = {
14271435
...validatedExecutionArgs,
14281436
rootValue: payload,
14291437
};
1430-
return executeOperation(perEventExecutionArgs);
1438+
return validatedExecutionArgs.perEventExecutor(perEventExecutionArgs);
14311439
});
14321440
}
14331441

1442+
export function executeSubscriptionEvent(
1443+
validatedExecutionArgs: ValidatedExecutionArgs,
1444+
): PromiseOrValue<ExecutionResult> {
1445+
return executeQueryOrMutationOrSubscriptionEvent(validatedExecutionArgs);
1446+
}
1447+
14341448
/**
14351449
* Implements the "CreateSourceEventStream" algorithm described in the
14361450
* GraphQL specification, resolving the subscription source event stream.

src/execution/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export { pathToArray as responsePathAsArray } from '../jsutils/Path.js';
33
export {
44
createSourceEventStream,
55
execute,
6+
executeSubscriptionEvent,
67
executeSync,
78
defaultFieldResolver,
89
defaultTypeResolver,

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ export type {
324324
// Execute GraphQL queries.
325325
export {
326326
execute,
327+
executeSubscriptionEvent,
327328
executeSync,
328329
defaultFieldResolver,
329330
defaultTypeResolver,

0 commit comments

Comments
 (0)