Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 10ccfea

Browse files
committed
Subscriptions should normalize requests only once
The `subscribe` method calls `createSourceEventStream` and `execute`, each of which normalize the request properties separately. By normalizing requests within the `subscribe` function, we can pass the normalized arguments to the implementation for `createSourceEventStream` and `execute`, i.e. new functions `createSourceEventStreamImpl` and `executeQueryOrMutation`.
1 parent fdf3e33 commit 10ccfea

File tree

3 files changed

+52
-31
lines changed

3 files changed

+52
-31
lines changed

src/execution/__tests__/subscribe-test.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,16 +502,29 @@ describe('Subscription Initialization Phase', () => {
502502

503503
// If we receive variables that cannot be coerced correctly, subscribe() will
504504
// resolve to an ExecutionResult that contains an informative error description.
505-
const result = await subscribe({ schema, document, variableValues });
506-
expectJSON(result).to.deep.equal({
505+
const expectedResult = {
507506
errors: [
508507
{
509508
message:
510509
'Variable "$arg" got invalid value "meow"; Int cannot represent non-integer value: "meow"',
511510
locations: [{ line: 2, column: 21 }],
512511
},
513512
],
514-
});
513+
};
514+
515+
expectJSON(
516+
await subscribe({ schema, document, variableValues }),
517+
).to.deep.equal(expectedResult);
518+
519+
expectJSON(
520+
await createSourceEventStream(
521+
schema,
522+
document,
523+
undefined,
524+
undefined,
525+
variableValues,
526+
),
527+
).to.deep.equal(expectedResult);
515528
});
516529
});
517530

src/execution/execute.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,11 @@ export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
180180
return { errors: exeContext };
181181
}
182182

183-
// Return data or a Promise that will eventually resolve to the data described
183+
return executeQueryOrMutation(exeContext);
184+
}
185+
186+
export function executeQueryOrMutation(exeContext: ExecutionContext) {
187+
// Return data or a Promise that will eventually resolve to the data described
184188
// by the "Response" section of the GraphQL specification.
185189

186190
// If errors are encountered while executing a GraphQL field, only that

src/execution/subscribe.ts

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
assertValidExecutionArguments,
2525
buildExecutionContext,
2626
buildResolveInfo,
27-
execute,
27+
executeQueryOrMutation,
2828
getFieldDef,
2929
} from './execute';
3030
import { mapAsyncIterator } from './mapAsyncIterator';
@@ -64,26 +64,28 @@ export interface SubscriptionArgs extends ExecutionArgs {}
6464
export async function subscribe(
6565
args: SubscriptionArgs,
6666
): Promise<AsyncGenerator<ExecutionResult, void, void> | ExecutionResult> {
67-
const {
68-
schema,
69-
document,
70-
rootValue,
71-
contextValue,
72-
variableValues,
73-
operationName,
74-
fieldResolver,
75-
subscribeFieldResolver,
76-
} = args;
67+
const { schema, document, variableValues } = args;
7768

78-
const resultOrStream = await createSourceEventStream(
79-
schema,
80-
document,
81-
rootValue,
82-
contextValue,
83-
variableValues,
84-
operationName,
85-
subscribeFieldResolver,
86-
);
69+
// If arguments are missing or incorrectly typed, this is an internal
70+
// developer mistake which should throw an early error.
71+
assertValidExecutionArguments(schema, document, variableValues);
72+
73+
// If a valid execution context cannot be created due to incorrect arguments,
74+
// a "Response" with only errors is returned.
75+
const exeContext = buildExecutionContext(args);
76+
77+
// Return early errors if execution context failed.
78+
if (!('schema' in exeContext)) {
79+
return { errors: exeContext };
80+
}
81+
82+
return executeSubscription(exeContext);
83+
}
84+
85+
async function executeSubscription(
86+
exeContext: ExecutionContext,
87+
): Promise<AsyncGenerator<ExecutionResult, void, void> | ExecutionResult> {
88+
const resultOrStream = await createSourceEventStreamImpl(exeContext);
8789

8890
if (!isAsyncIterable(resultOrStream)) {
8991
return resultOrStream;
@@ -96,14 +98,10 @@ export async function subscribe(
9698
// "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
9799
// "ExecuteQuery" algorithm, for which `execute` is also used.
98100
const mapSourceToResponse = (payload: unknown) =>
99-
execute({
100-
schema,
101-
document,
101+
executeQueryOrMutation({
102+
...exeContext,
102103
rootValue: payload,
103-
contextValue,
104-
variableValues,
105-
operationName,
106-
fieldResolver,
104+
errors: [],
107105
});
108106

109107
// Map every source value to a ExecutionResult value as described above.
@@ -168,6 +166,12 @@ export async function createSourceEventStream(
168166
return { errors: exeContext };
169167
}
170168

169+
return createSourceEventStreamImpl(exeContext);
170+
}
171+
172+
export async function createSourceEventStreamImpl(
173+
exeContext: ExecutionContext,
174+
): Promise<AsyncIterable<unknown> | ExecutionResult> {
171175
try {
172176
const eventStream = await executeSubscriptionRootField(exeContext);
173177

0 commit comments

Comments
 (0)