Skip to content

Commit

Permalink
refactor: subscribe: introduce new buildPerEventExecutionContext
Browse files Browse the repository at this point in the history
= introduces `buildPerEventExecutionContext` that creates an `ExecutionContext` for each subscribe event from the original `ExecutionContext` used to create the event stream
= `subscribe` now directly builds the `ExecutionContext` instead of relying on `createSourceEventStream`
= introduces `createSourceEventStreamImpl` and `executeImpl` functions that operate on the built `ExecutionContext` rather the `ExecutionArgs`
= `subscribe` calls the `createSourceEventStreamImpl` function on the original context and eventuallys calls `executeImpl` on the per event context created by `buildEventExecutionContext`.

Motivation:
1. remove unnecessary `buildExecutionContext` call, reducing duplicate work
2. paves the way for easily enhancing the `buildPerEventExecutionContext` to add a new `perEventContextFactory` argument to augment the context argument passed to resolvers as need per event.

depends on graphql#3638
  • Loading branch information
yaacovCR committed Jun 14, 2022
1 parent cfbc023 commit 656add9
Showing 1 changed file with 37 additions and 8 deletions.
45 changes: 37 additions & 8 deletions src/execution/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@ export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
return { errors: exeContext };
}

return executeImpl(exeContext);
}

function executeImpl(
exeContext: ExecutionContext,
): PromiseOrValue<ExecutionResult> {
// Return a Promise that will eventually resolve to the data described by
// The "Response" section of the GraphQL specification.
//
Expand Down Expand Up @@ -320,6 +326,17 @@ export function buildExecutionContext(
};
}

function buildPerEventExecutionContext(
exeContext: ExecutionContext,
payload: unknown,
): ExecutionContext {
return {
...exeContext,
rootValue: payload,
errors: [],
};
}

/**
* Implements the "Executing operations" section of the spec.
*/
Expand Down Expand Up @@ -1019,20 +1036,29 @@ export function subscribe(
): PromiseOrValue<
AsyncGenerator<ExecutionResult, void, void> | ExecutionResult
> {
const resultOrStream = createSourceEventStream(args);
// If a valid execution context cannot be created due to incorrect arguments,
// a "Response" with only errors is returned.
const exeContext = buildExecutionContext(args);

// Return early errors if execution context failed.
if (!('schema' in exeContext)) {
return { errors: exeContext };
}

const resultOrStream = createSourceEventStreamImpl(exeContext);

if (isPromise(resultOrStream)) {
return resultOrStream.then((resolvedResultOrStream) =>
mapSourceToResponse(resolvedResultOrStream, args),
mapSourceToResponse(exeContext, resolvedResultOrStream),
);
}

return mapSourceToResponse(resultOrStream, args);
return mapSourceToResponse(exeContext, resultOrStream);
}

function mapSourceToResponse(
exeContext: ExecutionContext,
resultOrStream: ExecutionResult | AsyncIterable<unknown>,
args: ExecutionArgs,
): PromiseOrValue<
AsyncGenerator<ExecutionResult, void, void> | ExecutionResult
> {
Expand All @@ -1047,10 +1073,7 @@ function mapSourceToResponse(
// "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
// "ExecuteQuery" algorithm, for which `execute` is also used.
return mapAsyncIterator(resultOrStream, (payload: unknown) =>
execute({
...args,
rootValue: payload,
}),
executeImpl(buildPerEventExecutionContext(exeContext, payload)),
);
}

Expand Down Expand Up @@ -1094,6 +1117,12 @@ export function createSourceEventStream(
return { errors: exeContext };
}

return createSourceEventStreamImpl(exeContext);
}

function createSourceEventStreamImpl(
exeContext: ExecutionContext,
): PromiseOrValue<AsyncIterable<unknown> | ExecutionResult> {
try {
const eventStream = executeSubscription(exeContext);
if (isPromise(eventStream)) {
Expand Down

0 comments on commit 656add9

Please sign in to comment.