Skip to content

Commit

Permalink
Add KnownOperationTypes rule (#3601)
Browse files Browse the repository at this point in the history
New Spec PR: graphql/graphql-spec#1098
Old Spec PR: graphql/graphql-spec#947
Original issue raised by @benjaminjkraft : #3592
  • Loading branch information
yaacovCR authored Dec 5, 2024
1 parent e5bcf5b commit 50d555c
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ export {
KnownArgumentNamesRule,
KnownDirectivesRule,
KnownFragmentNamesRule,
KnownOperationTypesRule,
KnownTypeNamesRule,
LoneAnonymousOperationRule,
NoFragmentCyclesRule,
Expand Down
60 changes: 60 additions & 0 deletions src/validation/__tests__/KnownOperationTypesRules-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, it } from 'mocha';

import { KnownOperationTypesRule } from '../rules/KnownOperationTypesRule.js';

import { expectValidationErrors } from './harness.js';

function expectErrors(queryStr: string) {
return expectValidationErrors(KnownOperationTypesRule, queryStr);
}

function expectValid(queryStr: string) {
expectErrors(queryStr).toDeepEqual([]);
}

describe('Validate: Known operation types', () => {
it('one known operation', () => {
expectValid(`
{ field }
`);
});

it('unknown mutation operation', () => {
expectErrors(`
mutation { field }
`).toDeepEqual([
{
message: 'The mutation operation is not supported by the schema.',
locations: [{ line: 2, column: 7 }],
},
]);
});

it('unknown subscription operation', () => {
expectErrors(`
subscription { field }
`).toDeepEqual([
{
message: 'The subscription operation is not supported by the schema.',
locations: [{ line: 2, column: 7 }],
},
]);
});

it('mixture of known and unknown operations', () => {
expectErrors(`
query { field }
mutation { field }
subscription { field }
`).toDeepEqual([
{
message: 'The mutation operation is not supported by the schema.',
locations: [{ line: 3, column: 7 }],
},
{
message: 'The subscription operation is not supported by the schema.',
locations: [{ line: 4, column: 7 }],
},
]);
});
});
3 changes: 3 additions & 0 deletions src/validation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export { KnownDirectivesRule } from './rules/KnownDirectivesRule.js';
// Spec Section: "Fragment spread target defined"
export { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule.js';

// Spec Section: "Operation Type Existence"
export { KnownOperationTypesRule } from './rules/KnownOperationTypesRule.js';

// Spec Section: "Fragment Spread Type Existence"
export { KnownTypeNamesRule } from './rules/KnownTypeNamesRule.js';

Expand Down
32 changes: 32 additions & 0 deletions src/validation/rules/KnownOperationTypesRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { GraphQLError } from '../../error/GraphQLError.js';

import type { ASTVisitor } from '../../language/visitor.js';

import type { ValidationContext } from '../ValidationContext.js';

/**
* Known Operation Types
*
* A GraphQL document is only valid if when it contains an operation,
* the root type for the operation exists within the schema.
*
* See https://spec.graphql.org/draft/#sec-Operation-Type-Existence
*/
export function KnownOperationTypesRule(
context: ValidationContext,
): ASTVisitor {
const schema = context.getSchema();
return {
OperationDefinition(node) {
const operation = node.operation;
if (!schema.getRootType(operation)) {
context.reportError(
new GraphQLError(
`The ${operation} operation is not supported by the schema.`,
{ nodes: node },
),
);
}
},
};
}
3 changes: 3 additions & 0 deletions src/validation/specifiedRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
import { KnownDirectivesRule } from './rules/KnownDirectivesRule.js';
// Spec Section: "Fragment spread target defined"
import { KnownFragmentNamesRule } from './rules/KnownFragmentNamesRule.js';
// Spec Section: "Operation Type Existence"
import { KnownOperationTypesRule } from './rules/KnownOperationTypesRule.js';
// Spec Section: "Fragment Spread Type Existence"
import { KnownTypeNamesRule } from './rules/KnownTypeNamesRule.js';
// Spec Section: "Lone Anonymous Operation"
Expand Down Expand Up @@ -91,6 +93,7 @@ export const recommendedRules = Object.freeze([MaxIntrospectionDepthRule]);
*/
export const specifiedRules: ReadonlyArray<ValidationRule> = Object.freeze([
ExecutableDefinitionsRule,
KnownOperationTypesRule,
UniqueOperationNamesRule,
LoneAnonymousOperationRule,
SingleFieldSubscriptionsRule,
Expand Down

0 comments on commit 50d555c

Please sign in to comment.