Skip to content

Commit 0b2bfb8

Browse files
committed
Deprecate SubscriptionArgs and broaden ExecutionArgs (#3295)
1 parent 302ab18 commit 0b2bfb8

File tree

3 files changed

+70
-44
lines changed

3 files changed

+70
-44
lines changed

src/execution/__tests__/subscribe-test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,38 @@ describe('Subscription Initialization Phase', () => {
256256
await subscription.return();
257257
});
258258

259+
it('uses a custom default subscribeFieldResolver', async () => {
260+
const schema = new GraphQLSchema({
261+
query: DummyQueryType,
262+
subscription: new GraphQLObjectType({
263+
name: 'Subscription',
264+
fields: {
265+
foo: { type: GraphQLString },
266+
},
267+
}),
268+
});
269+
270+
async function* fooGenerator() {
271+
yield { foo: 'FooValue' };
272+
}
273+
274+
const subscription = await subscribe({
275+
schema,
276+
document: parse('subscription { foo }'),
277+
rootValue: { customFoo: fooGenerator },
278+
subscribeFieldResolver: (root) => root.customFoo(),
279+
});
280+
invariant(isAsyncIterable(subscription));
281+
282+
expect(await subscription.next()).to.deep.equal({
283+
done: false,
284+
value: { data: { foo: 'FooValue' } },
285+
});
286+
287+
// Close subscription
288+
await subscription.return();
289+
});
290+
259291
it('should only resolve the first field of invalid multi-field', async () => {
260292
async function* fooGenerator() {
261293
yield { foo: 'FooValue' };

src/execution/execute.ts

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export interface ExecutionContext {
114114
variableValues: { [variable: string]: unknown };
115115
fieldResolver: GraphQLFieldResolver<any, any>;
116116
typeResolver: GraphQLTypeResolver<any, any>;
117+
subscribeFieldResolver: GraphQLFieldResolver<any, any>;
117118
errors: Array<GraphQLError>;
118119
}
119120

@@ -151,6 +152,7 @@ export interface ExecutionArgs {
151152
operationName?: Maybe<string>;
152153
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
153154
typeResolver?: Maybe<GraphQLTypeResolver<any, any>>;
155+
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
154156
}
155157

156158
/**
@@ -164,32 +166,14 @@ export interface ExecutionArgs {
164166
* a GraphQLError will be thrown immediately explaining the invalid input.
165167
*/
166168
export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
167-
const {
168-
schema,
169-
document,
170-
rootValue,
171-
contextValue,
172-
variableValues,
173-
operationName,
174-
fieldResolver,
175-
typeResolver,
176-
} = args;
169+
const { schema, document, variableValues, rootValue } = args;
177170

178171
// If arguments are missing or incorrect, throw an error.
179172
assertValidExecutionArguments(schema, document, variableValues);
180173

181174
// If a valid execution context cannot be created due to incorrect arguments,
182175
// a "Response" with only errors is returned.
183-
const exeContext = buildExecutionContext(
184-
schema,
185-
document,
186-
rootValue,
187-
contextValue,
188-
variableValues,
189-
operationName,
190-
fieldResolver,
191-
typeResolver,
192-
);
176+
const exeContext = buildExecutionContext(args);
193177

194178
// Return early errors if execution context failed.
195179
if (!('schema' in exeContext)) {
@@ -271,15 +255,20 @@ export function assertValidExecutionArguments(
271255
* @internal
272256
*/
273257
export function buildExecutionContext(
274-
schema: GraphQLSchema,
275-
document: DocumentNode,
276-
rootValue: unknown,
277-
contextValue: unknown,
278-
rawVariableValues: Maybe<{ readonly [variable: string]: unknown }>,
279-
operationName: Maybe<string>,
280-
fieldResolver: Maybe<GraphQLFieldResolver<unknown, unknown>>,
281-
typeResolver?: Maybe<GraphQLTypeResolver<unknown, unknown>>,
258+
args: ExecutionArgs,
282259
): ReadonlyArray<GraphQLError> | ExecutionContext {
260+
const {
261+
schema,
262+
document,
263+
rootValue,
264+
contextValue,
265+
variableValues: rawVariableValues,
266+
operationName,
267+
fieldResolver,
268+
typeResolver,
269+
subscribeFieldResolver,
270+
} = args;
271+
283272
let operation: OperationDefinitionNode | undefined;
284273
const fragments: ObjMap<FragmentDefinitionNode> = Object.create(null);
285274
for (const definition of document.definitions) {
@@ -334,6 +323,7 @@ export function buildExecutionContext(
334323
variableValues: coercedVariableValues.coerced,
335324
fieldResolver: fieldResolver ?? defaultFieldResolver,
336325
typeResolver: typeResolver ?? defaultTypeResolver,
326+
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
337327
errors: [],
338328
};
339329
}

src/execution/subscribe.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import type { GraphQLFieldResolver } from '../type/definition';
1313

1414
import { getOperationRootType } from '../utilities/getOperationRootType';
1515

16-
import type { ExecutionResult, ExecutionContext } from './execute';
16+
import type {
17+
ExecutionArgs,
18+
ExecutionResult,
19+
ExecutionContext,
20+
} from './execute';
1721
import { collectFields } from './collectFields';
1822
import { getArgumentValues } from './values';
1923
import {
@@ -25,16 +29,16 @@ import {
2529
} from './execute';
2630
import { mapAsyncIterator } from './mapAsyncIterator';
2731

28-
export interface SubscriptionArgs {
29-
schema: GraphQLSchema;
30-
document: DocumentNode;
31-
rootValue?: unknown;
32-
contextValue?: unknown;
33-
variableValues?: Maybe<{ readonly [variable: string]: unknown }>;
34-
operationName?: Maybe<string>;
35-
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
36-
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
37-
}
32+
/**
33+
* @deprecated use ExecutionArgs instead.
34+
*
35+
* ExecutionArgs has been broadened to include all properties
36+
* within SubscriptionArgs. The SubscriptionArgs type is retained
37+
* for backwards compatibility and will be removed in the next major
38+
* version.
39+
*/
40+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
41+
export interface SubscriptionArgs extends ExecutionArgs {}
3842

3943
/**
4044
* Implements the "Subscribe" algorithm described in the GraphQL specification.
@@ -141,23 +145,23 @@ export async function createSourceEventStream(
141145
contextValue?: unknown,
142146
variableValues?: Maybe<{ readonly [variable: string]: unknown }>,
143147
operationName?: Maybe<string>,
144-
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>,
148+
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>,
145149
): Promise<AsyncIterable<unknown> | ExecutionResult> {
146150
// If arguments are missing or incorrectly typed, this is an internal
147151
// developer mistake which should throw an early error.
148152
assertValidExecutionArguments(schema, document, variableValues);
149153

150154
// If a valid execution context cannot be created due to incorrect arguments,
151155
// a "Response" with only errors is returned.
152-
const exeContext = buildExecutionContext(
156+
const exeContext = buildExecutionContext({
153157
schema,
154158
document,
155159
rootValue,
156160
contextValue,
157161
variableValues,
158162
operationName,
159-
fieldResolver,
160-
);
163+
subscribeFieldResolver,
164+
});
161165

162166
// Return early errors if execution context failed.
163167
if (!('schema' in exeContext)) {
@@ -228,7 +232,7 @@ async function executeSubscription(
228232

229233
// Call the `subscribe()` resolver or the default resolver to produce an
230234
// AsyncIterable yielding raw payloads.
231-
const resolveFn = fieldDef.subscribe ?? exeContext.fieldResolver;
235+
const resolveFn = fieldDef.subscribe ?? exeContext.subscribeFieldResolver;
232236
const eventStream = await resolveFn(rootValue, args, contextValue, info);
233237

234238
if (eventStream instanceof Error) {

0 commit comments

Comments
 (0)