Skip to content

Commit

Permalink
Deprecate SubscriptionArgs and broaden ExecutionArgs (#3306)
Browse files Browse the repository at this point in the history
Co-authored-by: Ivan Goncharov <ivan.goncharov.ua@gmail.com>
  • Loading branch information
yaacovCR and IvanGoncharov committed Oct 11, 2021
1 parent 302ab18 commit 6aee19b
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 44 deletions.
32 changes: 32 additions & 0 deletions src/execution/__tests__/subscribe-test.ts
Expand Up @@ -256,6 +256,38 @@ describe('Subscription Initialization Phase', () => {
await subscription.return();
});

it('uses a custom default subscribeFieldResolver', async () => {
const schema = new GraphQLSchema({
query: DummyQueryType,
subscription: new GraphQLObjectType({
name: 'Subscription',
fields: {
foo: { type: GraphQLString },
},
}),
});

async function* fooGenerator() {
yield { foo: 'FooValue' };
}

const subscription = await subscribe({
schema,
document: parse('subscription { foo }'),
rootValue: { customFoo: fooGenerator },
subscribeFieldResolver: (root) => root.customFoo(),
});
invariant(isAsyncIterable(subscription));

expect(await subscription.next()).to.deep.equal({
done: false,
value: { data: { foo: 'FooValue' } },
});

// Close subscription
await subscription.return();
});

it('should only resolve the first field of invalid multi-field', async () => {
async function* fooGenerator() {
yield { foo: 'FooValue' };
Expand Down
46 changes: 18 additions & 28 deletions src/execution/execute.ts
Expand Up @@ -114,6 +114,7 @@ export interface ExecutionContext {
variableValues: { [variable: string]: unknown };
fieldResolver: GraphQLFieldResolver<any, any>;
typeResolver: GraphQLTypeResolver<any, any>;
subscribeFieldResolver: GraphQLFieldResolver<any, any>;
errors: Array<GraphQLError>;
}

Expand Down Expand Up @@ -151,6 +152,7 @@ export interface ExecutionArgs {
operationName?: Maybe<string>;
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
typeResolver?: Maybe<GraphQLTypeResolver<any, any>>;
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
}

/**
Expand All @@ -164,32 +166,14 @@ export interface ExecutionArgs {
* a GraphQLError will be thrown immediately explaining the invalid input.
*/
export function execute(args: ExecutionArgs): PromiseOrValue<ExecutionResult> {
const {
schema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
} = args;
const { schema, document, variableValues, rootValue } = args;

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

// If a valid execution context cannot be created due to incorrect arguments,
// a "Response" with only errors is returned.
const exeContext = buildExecutionContext(
schema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
);
const exeContext = buildExecutionContext(args);

// Return early errors if execution context failed.
if (!('schema' in exeContext)) {
Expand Down Expand Up @@ -271,15 +255,20 @@ export function assertValidExecutionArguments(
* @internal
*/
export function buildExecutionContext(
schema: GraphQLSchema,
document: DocumentNode,
rootValue: unknown,
contextValue: unknown,
rawVariableValues: Maybe<{ readonly [variable: string]: unknown }>,
operationName: Maybe<string>,
fieldResolver: Maybe<GraphQLFieldResolver<unknown, unknown>>,
typeResolver?: Maybe<GraphQLTypeResolver<unknown, unknown>>,
args: ExecutionArgs,
): ReadonlyArray<GraphQLError> | ExecutionContext {
const {
schema,
document,
rootValue,
contextValue,
variableValues: rawVariableValues,
operationName,
fieldResolver,
typeResolver,
subscribeFieldResolver,
} = args;

let operation: OperationDefinitionNode | undefined;
const fragments: ObjMap<FragmentDefinitionNode> = Object.create(null);
for (const definition of document.definitions) {
Expand Down Expand Up @@ -334,6 +323,7 @@ export function buildExecutionContext(
variableValues: coercedVariableValues.coerced,
fieldResolver: fieldResolver ?? defaultFieldResolver,
typeResolver: typeResolver ?? defaultTypeResolver,
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
errors: [],
};
}
Expand Down
35 changes: 19 additions & 16 deletions src/execution/subscribe.ts
Expand Up @@ -13,7 +13,11 @@ import type { GraphQLFieldResolver } from '../type/definition';

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

import type { ExecutionResult, ExecutionContext } from './execute';
import type {
ExecutionArgs,
ExecutionResult,
ExecutionContext,
} from './execute';
import { collectFields } from './collectFields';
import { getArgumentValues } from './values';
import {
Expand All @@ -25,16 +29,15 @@ import {
} from './execute';
import { mapAsyncIterator } from './mapAsyncIterator';

export interface SubscriptionArgs {
schema: GraphQLSchema;
document: DocumentNode;
rootValue?: unknown;
contextValue?: unknown;
variableValues?: Maybe<{ readonly [variable: string]: unknown }>;
operationName?: Maybe<string>;
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
}
/**
* @deprecated use ExecutionArgs instead. Will be removed in v17
*
* ExecutionArgs has been broadened to include all properties
* within SubscriptionArgs. The SubscriptionArgs type is retained
* for backwards compatibility.
*/
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SubscriptionArgs extends ExecutionArgs {}

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

// If a valid execution context cannot be created due to incorrect arguments,
// a "Response" with only errors is returned.
const exeContext = buildExecutionContext(
const exeContext = buildExecutionContext({
schema,
document,
rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
);
subscribeFieldResolver,
});

// Return early errors if execution context failed.
if (!('schema' in exeContext)) {
Expand Down Expand Up @@ -228,7 +231,7 @@ async function executeSubscription(

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

if (eventStream instanceof Error) {
Expand Down

0 comments on commit 6aee19b

Please sign in to comment.