|
17 | 17 | * limitations under the License. |
18 | 18 | */ |
19 | 19 |
|
20 | | -import type { |
21 | | - ASTVisitor, |
22 | | - DirectiveNode, |
23 | | - ASTNode, |
24 | | - InterfaceTypeDefinitionNode, |
25 | | - ObjectTypeDefinitionNode, |
26 | | - EnumTypeDefinitionNode, |
27 | | - EnumTypeExtensionNode, |
28 | | - EnumValueDefinitionNode, |
29 | | - FieldDefinitionNode, |
30 | | - FieldNode, |
31 | | - FragmentDefinitionNode, |
32 | | - FragmentSpreadNode, |
33 | | - InlineFragmentNode, |
34 | | - InputObjectTypeDefinitionNode, |
35 | | - InputObjectTypeExtensionNode, |
36 | | - InputValueDefinitionNode, |
37 | | - InterfaceTypeExtensionNode, |
38 | | - ObjectTypeExtensionNode, |
39 | | - OperationDefinitionNode, |
40 | | - ScalarTypeDefinitionNode, |
41 | | - ScalarTypeExtensionNode, |
42 | | - SchemaDefinitionNode, |
43 | | - SchemaExtensionNode, |
44 | | - UnionTypeDefinitionNode, |
45 | | - UnionTypeExtensionNode, |
46 | | - VariableDefinitionNode, |
47 | | -} from "graphql"; |
| 20 | +import type { ASTVisitor, DirectiveNode, ASTNode } from "graphql"; |
48 | 21 | import { Kind, isTypeDefinitionNode, isTypeExtensionNode } from "graphql"; |
49 | 22 | import type { SDLValidationContext } from "graphql/validation/ValidationContext"; |
50 | 23 | import { invalidCombinations } from "../../utils/invalid-directive-combinations"; |
51 | 24 | import { assertValid, createGraphQLError, DocumentValidationError } from "../utils/document-validation-error"; |
52 | | -import { |
53 | | - getInheritedTypeNames, |
54 | | - hydrateInterfaceWithImplementedTypesMap, |
55 | | -} from "../utils/interface-to-implementing-types"; |
56 | 25 | import { getPathToNode } from "../utils/path-parser"; |
57 | 26 |
|
58 | | -type ASTNodeWithDirectives = |
59 | | - | OperationDefinitionNode |
60 | | - | VariableDefinitionNode |
61 | | - | FieldNode |
62 | | - | FragmentSpreadNode |
63 | | - | InlineFragmentNode |
64 | | - | FragmentDefinitionNode |
65 | | - | SchemaDefinitionNode |
66 | | - | ScalarTypeDefinitionNode |
67 | | - | ObjectTypeDefinitionNode |
68 | | - | FieldDefinitionNode |
69 | | - | InputValueDefinitionNode |
70 | | - | InterfaceTypeDefinitionNode |
71 | | - | UnionTypeDefinitionNode |
72 | | - | EnumTypeDefinitionNode |
73 | | - | EnumValueDefinitionNode |
74 | | - | InputObjectTypeDefinitionNode |
75 | | - | SchemaExtensionNode |
76 | | - | ScalarTypeExtensionNode |
77 | | - | ObjectTypeExtensionNode |
78 | | - | InterfaceTypeExtensionNode |
79 | | - | UnionTypeExtensionNode |
80 | | - | EnumTypeExtensionNode |
81 | | - | InputObjectTypeExtensionNode; |
82 | 27 | export function DirectiveCombinationValid(context: SDLValidationContext): ASTVisitor { |
83 | | - const interfaceToImplementingTypes = new Map<string, Set<string>>(); |
84 | | - const typeToDirectivesPerFieldMap = new Map<string, Map<string, readonly DirectiveNode[]>>(); |
85 | | - const typeToDirectivesMap = new Map<string, readonly DirectiveNode[]>(); |
86 | | - const hydrateWithDirectives = function ( |
87 | | - node: ASTNodeWithDirectives, |
88 | | - parentOfTraversedDef: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode | undefined |
89 | | - ) { |
90 | | - if (node.kind === Kind.OBJECT_TYPE_DEFINITION || node.kind === Kind.INTERFACE_TYPE_DEFINITION) { |
91 | | - typeToDirectivesMap.set(node.name.value, node.directives || []); |
92 | | - } |
93 | | - if (node.kind === Kind.FIELD_DEFINITION) { |
94 | | - if (!parentOfTraversedDef) { |
95 | | - return; |
96 | | - } |
97 | | - const seenFields = |
98 | | - typeToDirectivesPerFieldMap.get(parentOfTraversedDef.name.value) || |
99 | | - new Map<string, readonly DirectiveNode[]>(); |
100 | | - seenFields.set(node.name.value, node.directives || []); |
101 | | - typeToDirectivesPerFieldMap.set(parentOfTraversedDef.name.value, seenFields); |
102 | | - } |
103 | | - }; |
104 | | - const getDirectives = function ( |
105 | | - node: ASTNodeWithDirectives, |
106 | | - parentOfTraversedDef: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode | undefined |
107 | | - ): DirectiveNode[] { |
108 | | - const directivesToCheck: DirectiveNode[] = [...(node.directives || [])]; |
109 | | - if (node.kind === Kind.OBJECT_TYPE_DEFINITION || node.kind === Kind.INTERFACE_TYPE_DEFINITION) { |
110 | | - return getInheritedTypeNames(node, interfaceToImplementingTypes).reduce((acc, i) => { |
111 | | - const inheritedDirectives = typeToDirectivesMap.get(i) || []; |
112 | | - return acc.concat(inheritedDirectives); |
113 | | - }, directivesToCheck); |
114 | | - } |
115 | | - if (node.kind === Kind.FIELD_DEFINITION) { |
116 | | - if (!parentOfTraversedDef) { |
117 | | - return []; |
118 | | - } |
119 | | - return getInheritedTypeNames(parentOfTraversedDef, interfaceToImplementingTypes).reduce((acc, i) => { |
120 | | - const inheritedDirectives = typeToDirectivesPerFieldMap.get(i)?.get(node.name.value) || []; |
121 | | - return acc.concat(inheritedDirectives); |
122 | | - }, directivesToCheck); |
123 | | - } |
124 | | - return directivesToCheck; |
125 | | - }; |
126 | | - |
127 | 28 | return { |
128 | 29 | enter(node: ASTNode, _key, _parent, path, ancestors) { |
129 | 30 | if (!("directives" in node) || !node.directives) { |
130 | 31 | return; |
131 | 32 | } |
132 | | - const [pathToNode, traversedDef, parentOfTraversedDef] = getPathToNode(path, ancestors); |
| 33 | + const [pathToNode, traversedDef] = getPathToNode(path, ancestors); |
133 | 34 | const currentNodeErrorPath = |
134 | 35 | isTypeDefinitionNode(node) || isTypeExtensionNode(node) ? [...pathToNode, node.name.value] : pathToNode; |
135 | 36 |
|
136 | | - hydrateInterfaceWithImplementedTypesMap(node, interfaceToImplementingTypes); |
137 | | - hydrateWithDirectives(node, parentOfTraversedDef); |
138 | | - const directivesToCheck = getDirectives(node, parentOfTraversedDef); |
| 37 | + if (node.directives.length < 2) { |
| 38 | + // no directive combination to check |
| 39 | + return; |
| 40 | + } |
139 | 41 |
|
140 | | - const { isValid, errorMsg, errorPath } = assertValid(() => assertValidDirectives(directivesToCheck)); |
| 42 | + const { isValid, errorMsg, errorPath } = assertValid(() => |
| 43 | + assertValidDirectives(node.directives as DirectiveNode[]) |
| 44 | + ); |
141 | 45 | if (!isValid) { |
142 | 46 | context.reportError( |
143 | 47 | createGraphQLError({ |
|
0 commit comments