Skip to content

Commit 50d555c

Browse files
authored
Add KnownOperationTypes rule (#3601)
New Spec PR: graphql/graphql-spec#1098 Old Spec PR: graphql/graphql-spec#947 Original issue raised by @benjaminjkraft : #3592
1 parent e5bcf5b commit 50d555c

File tree

5 files changed

+99
-0
lines changed

5 files changed

+99
-0
lines changed

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ export {
379379
KnownArgumentNamesRule,
380380
KnownDirectivesRule,
381381
KnownFragmentNamesRule,
382+
KnownOperationTypesRule,
382383
KnownTypeNamesRule,
383384
LoneAnonymousOperationRule,
384385
NoFragmentCyclesRule,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { describe, it } from 'mocha';
2+
3+
import { KnownOperationTypesRule } from '../rules/KnownOperationTypesRule.js';
4+
5+
import { expectValidationErrors } from './harness.js';
6+
7+
function expectErrors(queryStr: string) {
8+
return expectValidationErrors(KnownOperationTypesRule, queryStr);
9+
}
10+
11+
function expectValid(queryStr: string) {
12+
expectErrors(queryStr).toDeepEqual([]);
13+
}
14+
15+
describe('Validate: Known operation types', () => {
16+
it('one known operation', () => {
17+
expectValid(`
18+
{ field }
19+
`);
20+
});
21+
22+
it('unknown mutation operation', () => {
23+
expectErrors(`
24+
mutation { field }
25+
`).toDeepEqual([
26+
{
27+
message: 'The mutation operation is not supported by the schema.',
28+
locations: [{ line: 2, column: 7 }],
29+
},
30+
]);
31+
});
32+
33+
it('unknown subscription operation', () => {
34+
expectErrors(`
35+
subscription { field }
36+
`).toDeepEqual([
37+
{
38+
message: 'The subscription operation is not supported by the schema.',
39+
locations: [{ line: 2, column: 7 }],
40+
},
41+
]);
42+
});
43+
44+
it('mixture of known and unknown operations', () => {
45+
expectErrors(`
46+
query { field }
47+
mutation { field }
48+
subscription { field }
49+
`).toDeepEqual([
50+
{
51+
message: 'The mutation operation is not supported by the schema.',
52+
locations: [{ line: 3, column: 7 }],
53+
},
54+
{
55+
message: 'The subscription operation is not supported by the schema.',
56+
locations: [{ line: 4, column: 7 }],
57+
},
58+
]);
59+
});
60+
});

src/validation/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export { KnownDirectivesRule } from './rules/KnownDirectivesRule.js';
3333
// Spec Section: "Fragment spread target defined"
3434
export { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule.js';
3535

36+
// Spec Section: "Operation Type Existence"
37+
export { KnownOperationTypesRule } from './rules/KnownOperationTypesRule.js';
38+
3639
// Spec Section: "Fragment Spread Type Existence"
3740
export { KnownTypeNamesRule } from './rules/KnownTypeNamesRule.js';
3841

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { GraphQLError } from '../../error/GraphQLError.js';
2+
3+
import type { ASTVisitor } from '../../language/visitor.js';
4+
5+
import type { ValidationContext } from '../ValidationContext.js';
6+
7+
/**
8+
* Known Operation Types
9+
*
10+
* A GraphQL document is only valid if when it contains an operation,
11+
* the root type for the operation exists within the schema.
12+
*
13+
* See https://spec.graphql.org/draft/#sec-Operation-Type-Existence
14+
*/
15+
export function KnownOperationTypesRule(
16+
context: ValidationContext,
17+
): ASTVisitor {
18+
const schema = context.getSchema();
19+
return {
20+
OperationDefinition(node) {
21+
const operation = node.operation;
22+
if (!schema.getRootType(operation)) {
23+
context.reportError(
24+
new GraphQLError(
25+
`The ${operation} operation is not supported by the schema.`,
26+
{ nodes: node },
27+
),
28+
);
29+
}
30+
},
31+
};
32+
}

src/validation/specifiedRules.ts

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
import { KnownDirectivesRule } from './rules/KnownDirectivesRule.js';
2020
// Spec Section: "Fragment spread target defined"
2121
import { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule.js';
22+
// Spec Section: "Operation Type Existence"
23+
import { KnownOperationTypesRule } from './rules/KnownOperationTypesRule.js';
2224
// Spec Section: "Fragment Spread Type Existence"
2325
import { KnownTypeNamesRule } from './rules/KnownTypeNamesRule.js';
2426
// Spec Section: "Lone Anonymous Operation"
@@ -91,6 +93,7 @@ export const recommendedRules = Object.freeze([MaxIntrospectionDepthRule]);
9193
*/
9294
export const specifiedRules: ReadonlyArray<ValidationRule> = Object.freeze([
9395
ExecutableDefinitionsRule,
96+
KnownOperationTypesRule,
9497
UniqueOperationNamesRule,
9598
LoneAnonymousOperationRule,
9699
SingleFieldSubscriptionsRule,

0 commit comments

Comments
 (0)