graphql
Version:
A Query Language and Runtime which can target any service.
1 lines • 7.94 kB
Source Map (JSON)
{"version":3,"file":"DeferStreamDirectiveOnRootFieldRule.js","sourceRoot":"","sources":["../../../src/validation/rules/DeferStreamDirectiveOnRootFieldRule.ts"],"names":[],"mappings":"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","sourcesContent":["/** @category Validation Rules */\n\nimport { GraphQLError } from '../../error/GraphQLError.ts';\n\nimport type {\n FragmentDefinitionNode,\n FragmentSpreadNode,\n InlineFragmentNode,\n OperationDefinitionNode,\n OperationTypeNode,\n SelectionSetNode,\n} from '../../language/ast.ts';\nimport { Kind } from '../../language/kinds.ts';\nimport type { ASTVisitor } from '../../language/visitor.ts';\n\nimport type { GraphQLObjectType } from '../../type/definition.ts';\nimport {\n GraphQLDeferDirective,\n GraphQLStreamDirective,\n} from '../../type/directives.ts';\n\nimport type { ValidationContext } from '../ValidationContext.ts';\n\n/**\n * Defer and stream directives are used on valid root field\n *\n * A GraphQL document is only valid if defer directives are not used on root mutation or subscription types.\n * @param context - The validation context used while checking the document.\n * @returns A visitor that reports validation errors for this rule.\n * @example\n * ```ts\n * import { parse } from 'graphql/language';\n * import { buildSchema } from 'graphql/utilities';\n * import {\n * validate,\n * DeferStreamDirectiveOnRootFieldRule,\n * } from 'graphql/validation';\n *\n * const schema = buildSchema(`\n * type Query {\n * message: String\n * }\n *\n * type Mutation {\n * updateMessage: String\n * }\n * `);\n * const invalidDocument = parse(`\n * mutation { ... @defer { updateMessage } }\n * `);\n * const validDocument = parse(`\n * { ... @defer { message } }\n * `);\n *\n * validate(schema, invalidDocument, [DeferStreamDirectiveOnRootFieldRule]).length; // => 1\n * validate(schema, validDocument, [DeferStreamDirectiveOnRootFieldRule]); // => []\n * ```\n */\nexport function DeferStreamDirectiveOnRootFieldRule(\n context: ValidationContext,\n): ASTVisitor {\n return {\n OperationDefinition(node: OperationDefinitionNode) {\n const document = context.getDocument();\n const fragments = new Map<string, FragmentDefinitionNode>();\n\n for (const definition of document.definitions) {\n if (definition.kind === Kind.FRAGMENT_DEFINITION) {\n fragments.set(definition.name.value, definition);\n }\n }\n if (node.operation !== 'subscription' && node.operation !== 'mutation') {\n return;\n }\n const schema = context.getSchema();\n const rootType = schema.getRootType(node.operation);\n if (rootType) {\n forbidDeferStream({\n context,\n operationType: node.operation,\n rootType,\n fragments,\n selectionSet: node.selectionSet,\n visitedFragments: new Set(),\n });\n }\n },\n };\n}\n\nfunction forbidDeferStream({\n context,\n operationType,\n rootType,\n fragments,\n selectionSet,\n visitedFragments,\n}: {\n context: ValidationContext;\n operationType: OperationTypeNode;\n rootType: GraphQLObjectType;\n fragments: Map<string, FragmentDefinitionNode>;\n selectionSet: SelectionSetNode;\n visitedFragments: Set<string>;\n}) {\n for (const selection of selectionSet.selections) {\n if (selection.kind === 'Field') {\n const stream = selection.directives?.find(\n (d) => d.name.value === GraphQLStreamDirective.name,\n );\n if (stream) {\n context.reportError(\n new GraphQLError(\n `Stream directive cannot be used on root ${operationType} type \"${rootType}\".`,\n { nodes: stream },\n ),\n );\n }\n } else if (selection.kind === 'FragmentSpread') {\n const fragmentName = selection.name.value;\n if (visitedFragments.has(fragmentName)) {\n continue;\n }\n const fragment = fragments.get(fragmentName);\n if (fragment) {\n const defer = getDeferDirective(selection);\n if (defer !== undefined) {\n context.reportError(\n new GraphQLError(\n `Defer directive cannot be used on root ${operationType} type \"${rootType}\".`,\n { nodes: defer },\n ),\n );\n }\n forbidDeferStream({\n context,\n operationType,\n rootType,\n fragments,\n selectionSet: fragment.selectionSet,\n visitedFragments,\n });\n }\n visitedFragments.add(fragmentName);\n } else if (selection.kind === 'InlineFragment') {\n const defer = getDeferDirective(selection);\n if (defer !== undefined) {\n context.reportError(\n new GraphQLError(\n `Defer directive cannot be used on root ${operationType} type \"${rootType}\".`,\n { nodes: defer },\n ),\n );\n }\n forbidDeferStream({\n context,\n operationType,\n rootType,\n fragments,\n selectionSet: selection.selectionSet,\n visitedFragments,\n });\n }\n }\n}\n\nfunction getDeferDirective(fragment: FragmentSpreadNode | InlineFragmentNode) {\n return fragment.directives?.find(\n (d) => d.name.value === GraphQLDeferDirective.name,\n );\n}\n"]}