Skip to content

Commit 6022932

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 63efaef commit 6022932

4 files changed

Lines changed: 66 additions & 10 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 & 9 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
export interface ExecutionContext {
@@ -169,6 +172,11 @@ export interface ExecutionArgs {
169172
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
170173
typeResolver?: Maybe<GraphQLTypeResolver<any, any>>;
171174
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
175+
perEventExecutor?: Maybe<
176+
(
177+
validatedExecutionArgs: ValidatedExecutionArgs,
178+
) => PromiseOrValue<ExecutionResult>
179+
>;
172180
/** Additional execution options. */
173181
options?: {
174182
/** Set the maximum number of errors allowed for coercing (defaults to 50). */
@@ -196,7 +204,7 @@ export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
196204
return { errors: validatedExecutionArgs };
197205
}
198206

199-
return executeOperation(validatedExecutionArgs);
207+
return executeQueryOrMutationOrSubscriptionEvent(validatedExecutionArgs);
200208
}
201209

202210
/**
@@ -214,7 +222,7 @@ export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
214222
* at which point we still log the error and null the parent field, which
215223
* in this case is the entire response.
216224
*/
217-
function executeOperation(
225+
function executeQueryOrMutationOrSubscriptionEvent(
218226
validatedExecutionArgs: ValidatedExecutionArgs,
219227
): PromiseOrValue<ExecutionResult> {
220228
const exeContext: ExecutionContext = {
@@ -313,6 +321,7 @@ export function validateExecutionArgs(
313321
fieldResolver,
314322
typeResolver,
315323
subscribeFieldResolver,
324+
perEventExecutor,
316325
options,
317326
} = args;
318327

@@ -386,6 +395,7 @@ export function validateExecutionArgs(
386395
fieldResolver: fieldResolver ?? defaultFieldResolver,
387396
typeResolver: typeResolver ?? defaultTypeResolver,
388397
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
398+
perEventExecutor: perEventExecutor ?? executeSubscriptionEvent,
389399
};
390400
}
391401

@@ -1388,21 +1398,22 @@ function mapSourceToResponse(
13881398
// For each payload yielded from a subscription, map it over the normal
13891399
// GraphQL `execute` function, with `payload` as the rootValue.
13901400
// This implements the "MapSourceToResponseEvent" algorithm described in
1391-
// the GraphQL specification. The `execute` function provides the
1392-
// "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
1393-
// "ExecuteQuery" algorithm, for which `execute` is also used.
1401+
// the GraphQL specification..
13941402
return mapAsyncIterable(resultOrStream, (payload: unknown) => {
13951403
const perEventExecutionArgs: ValidatedExecutionArgs = {
13961404
...validatedExecutionArgs,
13971405
rootValue: payload,
13981406
};
1399-
// typecast to ExecutionResult, not possible to return
1400-
// ExperimentalIncrementalExecutionResults when
1401-
// exeContext.operation is 'subscription'.
1402-
return executeOperation(perEventExecutionArgs) as ExecutionResult;
1407+
return validatedExecutionArgs.perEventExecutor(perEventExecutionArgs);
14031408
});
14041409
}
14051410

1411+
export function executeSubscriptionEvent(
1412+
validatedExecutionArgs: ValidatedExecutionArgs,
1413+
): PromiseOrValue<ExecutionResult> {
1414+
return executeQueryOrMutationOrSubscriptionEvent(validatedExecutionArgs);
1415+
}
1416+
14061417
/**
14071418
* Implements the "CreateSourceEventStream" algorithm described in the
14081419
* 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)