Skip to content

Commit 96b146d

Browse files
Added 'GraphQLSchema.getRootType' and deprecate getOperationRootType (#3305)
1 parent 2170e4a commit 96b146d

File tree

8 files changed

+95
-30
lines changed

8 files changed

+95
-30
lines changed

src/execution/__tests__/executor-test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,29 @@ describe('Execute: Handles basic execution tasks', () => {
908908
expect(result).to.deep.equal({ data: { a: 'b' } });
909909
});
910910

911+
it('resolves to an error if schema does not support operation', () => {
912+
const schema = new GraphQLSchema({ assumeValid: true });
913+
914+
const document = parse(`
915+
query Q { __typename }
916+
mutation M { __typename }
917+
subscription S { __typename }
918+
`);
919+
920+
// FIXME: errors should be wrapped into ExecutionResult
921+
expect(() =>
922+
executeSync({ schema, document, operationName: 'Q' }),
923+
).to.throw('Schema is not configured to execute query operation.');
924+
925+
expect(() =>
926+
executeSync({ schema, document, operationName: 'M' }),
927+
).to.throw('Schema is not configured to execute mutation operation.');
928+
929+
expect(() =>
930+
executeSync({ schema, document, operationName: 'S' }),
931+
).to.throw('Schema is not configured to execute subscription operation.');
932+
});
933+
911934
it('correct field ordering despite execution order', async () => {
912935
const schema = new GraphQLSchema({
913936
query: new GraphQLObjectType({

src/execution/__tests__/subscribe-test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,22 @@ describe('Subscription Initialization Phase', () => {
367367
);
368368
});
369369

370+
it('resolves to an error if schema does not support subscriptions', async () => {
371+
const schema = new GraphQLSchema({ query: DummyQueryType });
372+
const document = parse('subscription { unknownField }');
373+
374+
const result = await subscribe({ schema, document });
375+
expectJSON(result).to.deep.equal({
376+
errors: [
377+
{
378+
message:
379+
'Schema is not configured to execute subscription operation.',
380+
locations: [{ line: 1, column: 1 }],
381+
},
382+
],
383+
});
384+
});
385+
370386
it('resolves to an error for unknown subscription field', async () => {
371387
const schema = new GraphQLSchema({
372388
query: DummyQueryType,

src/execution/execute.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ import {
5151
isNonNullType,
5252
} from '../type/definition';
5353

54-
import { getOperationRootType } from '../utilities/getOperationRootType';
55-
5654
import { getVariableValues, getArgumentValues } from './values';
5755
import {
5856
collectFields,
@@ -336,12 +334,19 @@ function executeOperation(
336334
operation: OperationDefinitionNode,
337335
rootValue: unknown,
338336
): PromiseOrValue<ObjMap<unknown> | null> {
339-
const type = getOperationRootType(exeContext.schema, operation);
340-
const fields = collectFields(
337+
const rootType = exeContext.schema.getRootType(operation.operation);
338+
if (rootType == null) {
339+
throw new GraphQLError(
340+
`Schema is not configured to execute ${operation.operation} operation.`,
341+
operation,
342+
);
343+
}
344+
345+
const rootFields = collectFields(
341346
exeContext.schema,
342347
exeContext.fragments,
343348
exeContext.variableValues,
344-
type,
349+
rootType,
345350
operation.selectionSet,
346351
);
347352

@@ -353,8 +358,14 @@ function executeOperation(
353358
try {
354359
const result =
355360
operation.operation === 'mutation'
356-
? executeFieldsSerially(exeContext, type, rootValue, path, fields)
357-
: executeFields(exeContext, type, rootValue, path, fields);
361+
? executeFieldsSerially(
362+
exeContext,
363+
rootType,
364+
rootValue,
365+
path,
366+
rootFields,
367+
)
368+
: executeFields(exeContext, rootType, rootValue, path, rootFields);
358369
if (isPromise(result)) {
359370
return result.then(undefined, (error) => {
360371
exeContext.errors.push(error);

src/execution/subscribe.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import type { DocumentNode } from '../language/ast';
1111
import type { GraphQLSchema } from '../type/schema';
1212
import type { GraphQLFieldResolver } from '../type/definition';
1313

14-
import { getOperationRootType } from '../utilities/getOperationRootType';
15-
1614
import type {
1715
ExecutionArgs,
1816
ExecutionResult,
@@ -194,16 +192,24 @@ async function executeSubscription(
194192
): Promise<unknown> {
195193
const { schema, fragments, operation, variableValues, rootValue } =
196194
exeContext;
197-
const type = getOperationRootType(schema, operation);
198-
const fields = collectFields(
195+
196+
const rootType = schema.getSubscriptionType();
197+
if (rootType == null) {
198+
throw new GraphQLError(
199+
'Schema is not configured to execute subscription operation.',
200+
operation,
201+
);
202+
}
203+
204+
const rootFields = collectFields(
199205
schema,
200206
fragments,
201207
variableValues,
202-
type,
208+
rootType,
203209
operation.selectionSet,
204210
);
205-
const [responseName, fieldNodes] = [...fields.entries()][0];
206-
const fieldDef = getFieldDef(schema, type, fieldNodes[0]);
211+
const [responseName, fieldNodes] = [...rootFields.entries()][0];
212+
const fieldDef = getFieldDef(schema, rootType, fieldNodes[0]);
207213

208214
if (!fieldDef) {
209215
const fieldName = fieldNodes[0].name.value;
@@ -213,8 +219,14 @@ async function executeSubscription(
213219
);
214220
}
215221

216-
const path = addPath(undefined, responseName, type.name);
217-
const info = buildResolveInfo(exeContext, fieldDef, fieldNodes, type, path);
222+
const path = addPath(undefined, responseName, rootType.name);
223+
const info = buildResolveInfo(
224+
exeContext,
225+
fieldDef,
226+
fieldNodes,
227+
rootType,
228+
path,
229+
);
218230

219231
try {
220232
// Implements the "ResolveFieldEventStream" algorithm from GraphQL specification.

src/type/schema.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { Maybe } from '../jsutils/Maybe';
99
import type { GraphQLError } from '../error/GraphQLError';
1010

1111
import type {
12+
OperationTypeNode,
1213
SchemaDefinitionNode,
1314
SchemaExtensionNode,
1415
} from '../language/ast';
@@ -275,6 +276,17 @@ export class GraphQLSchema {
275276
return this._subscriptionType;
276277
}
277278

279+
getRootType(operation: OperationTypeNode): Maybe<GraphQLObjectType> {
280+
switch (operation) {
281+
case 'query':
282+
return this.getQueryType();
283+
case 'mutation':
284+
return this.getMutationType();
285+
case 'subscription':
286+
return this.getSubscriptionType();
287+
}
288+
}
289+
278290
getTypeMap(): TypeMap {
279291
return this._typeMap;
280292
}

src/utilities/TypeInfo.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -169,19 +169,8 @@ export class TypeInfo {
169169
this._directive = schema.getDirective(node.name.value);
170170
break;
171171
case Kind.OPERATION_DEFINITION: {
172-
let type: unknown;
173-
switch (node.operation) {
174-
case 'query':
175-
type = schema.getQueryType();
176-
break;
177-
case 'mutation':
178-
type = schema.getMutationType();
179-
break;
180-
case 'subscription':
181-
type = schema.getSubscriptionType();
182-
break;
183-
}
184-
this._typeStack.push(isObjectType(type) ? type : undefined);
172+
const rootType = schema.getRootType(node.operation);
173+
this._typeStack.push(isObjectType(rootType) ? rootType : undefined);
185174
break;
186175
}
187176
case Kind.INLINE_FRAGMENT:

src/utilities/__tests__/getOperationRootType-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function getOperationNode(doc: DocumentNode): OperationDefinitionNode {
4040
return operationNode;
4141
}
4242

43-
describe('getOperationRootType', () => {
43+
describe('Deprecated - getOperationRootType', () => {
4444
it('Gets a Query type for an unnamed OperationDefinitionNode', () => {
4545
const testSchema = new GraphQLSchema({
4646
query: queryType,

src/utilities/getOperationRootType.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import type { GraphQLObjectType } from '../type/definition';
1010

1111
/**
1212
* Extracts the root type of the operation from the schema.
13+
*
14+
* @deprecated Please use `GraphQLSchema.getRootType` instead. Will be removed in v17
1315
*/
1416
export function getOperationRootType(
1517
schema: GraphQLSchema,

0 commit comments

Comments
 (0)