graphql_tools/validation/rules/
unique_directives_per_location.rs1use std::collections::HashSet;
2
3use super::ValidationRule;
4use crate::static_graphql::query::{
5 Directive, Field, FragmentDefinition, FragmentSpread, InlineFragment, OperationDefinition,
6};
7use crate::{
8 ast::{OperationVisitor, OperationVisitorContext},
9 validation::utils::{ValidationError, ValidationErrorContext},
10};
11
12pub struct UniqueDirectivesPerLocation {}
19
20impl Default for UniqueDirectivesPerLocation {
21 fn default() -> Self {
22 Self::new()
23 }
24}
25
26impl UniqueDirectivesPerLocation {
27 pub fn new() -> Self {
28 UniqueDirectivesPerLocation {}
29 }
30
31 pub fn check_duplicate_directive(
32 &self,
33 ctx: &mut OperationVisitorContext,
34 err_context: &mut ValidationErrorContext,
35 directives: &[Directive],
36 ) {
37 let mut exists = HashSet::new();
38
39 for directive in directives {
40 if let Some(meta_directive) = ctx.directives.get(&directive.name) {
41 if !meta_directive.repeatable {
42 if exists.contains(&directive.name) {
43 err_context.report_error(ValidationError {
44 error_code: self.error_code(),
45 locations: vec![directive.position],
46 message: format!("Duplicate directive \"{}\"", &directive.name),
47 });
48
49 continue;
50 }
51
52 exists.insert(directive.name.clone());
53 }
54 }
55 }
56 }
57}
58
59impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for UniqueDirectivesPerLocation {
60 fn enter_operation_definition(
61 &mut self,
62 ctx: &mut OperationVisitorContext<'doc>,
63 err_ctx: &mut ValidationErrorContext,
64 operation: &OperationDefinition,
65 ) {
66 self.check_duplicate_directive(ctx, err_ctx, operation.directives());
67 }
68
69 fn enter_field(
70 &mut self,
71 ctx: &mut OperationVisitorContext<'doc>,
72 err_ctx: &mut ValidationErrorContext,
73 field: &Field,
74 ) {
75 self.check_duplicate_directive(ctx, err_ctx, &field.directives);
76 }
77
78 fn enter_fragment_definition(
79 &mut self,
80 ctx: &mut OperationVisitorContext<'doc>,
81 err_ctx: &mut ValidationErrorContext,
82 fragment: &FragmentDefinition,
83 ) {
84 self.check_duplicate_directive(ctx, err_ctx, &fragment.directives);
85 }
86
87 fn enter_fragment_spread(
88 &mut self,
89 ctx: &mut OperationVisitorContext<'doc>,
90 err_ctx: &mut ValidationErrorContext,
91 fragment_spread: &FragmentSpread,
92 ) {
93 self.check_duplicate_directive(ctx, err_ctx, &fragment_spread.directives)
94 }
95
96 fn enter_inline_fragment(
97 &mut self,
98 ctx: &mut OperationVisitorContext<'doc>,
99 err_ctx: &mut ValidationErrorContext,
100 inline_fragment: &InlineFragment,
101 ) {
102 self.check_duplicate_directive(ctx, err_ctx, &inline_fragment.directives)
103 }
104}
105
106impl ValidationRule for UniqueDirectivesPerLocation {
107 fn error_code(&self) -> &'static str {
108 "UniqueDirectivesPerLocation"
109 }
110
111 fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
112 Box::new(UniqueDirectivesPerLocation::new())
113 }
114}
115
116#[test]
117fn no_directives() {
118 use crate::validation::test_utils::*;
119
120 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
121 let errors = test_operation_with_schema(
122 "fragment Test on Type {
123 field
124 }",
125 TEST_SCHEMA,
126 &mut plan,
127 );
128
129 assert_eq!(get_messages(&errors).len(), 0);
130}
131
132#[test]
133fn unique_directives_in_different_locations() {
134 use crate::validation::test_utils::*;
135
136 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
137 let errors = test_operation_with_schema(
138 "fragment Test on Type @directiveA {
139 field @directiveB
140 }",
141 TEST_SCHEMA,
142 &mut plan,
143 );
144
145 assert_eq!(get_messages(&errors).len(), 0);
146}
147
148#[test]
149fn unique_directives_in_same_location() {
150 use crate::validation::test_utils::*;
151
152 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
153 let errors = test_operation_with_schema(
154 "fragment Test on Type @directiveA @directiveB {
155 field @directiveA @directiveB
156 }",
157 TEST_SCHEMA,
158 &mut plan,
159 );
160
161 assert_eq!(get_messages(&errors).len(), 0);
162}
163
164#[test]
165fn same_directives_in_different_locations() {
166 use crate::validation::test_utils::*;
167
168 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
169 let errors = test_operation_with_schema(
170 "fragment Test on Type @directiveA {
171 field @directiveA
172 }",
173 TEST_SCHEMA,
174 &mut plan,
175 );
176
177 assert_eq!(get_messages(&errors).len(), 0);
178}
179
180#[test]
181fn same_directives_in_similar_locations() {
182 use crate::validation::test_utils::*;
183
184 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
185 let errors = test_operation_with_schema(
186 "fragment Test on Type {
187 field @directive
188 field @directive
189 }",
190 TEST_SCHEMA,
191 &mut plan,
192 );
193
194 assert_eq!(get_messages(&errors).len(), 0);
195}
196
197#[test]
198fn repeatable_directives_in_same_location() {
199 use crate::validation::test_utils::*;
200
201 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
202 let errors = test_operation_with_schema(
203 "fragment Test on Type @repeatable @repeatable {
204 field @repeatable @repeatable
205 }",
206 TEST_SCHEMA,
207 &mut plan,
208 );
209
210 assert_eq!(get_messages(&errors).len(), 0);
211}
212
213#[test]
214fn unknown_directives_must_be_ignored() {
215 use crate::validation::test_utils::*;
216
217 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
218 let errors = test_operation_with_schema(
219 "fragment Test on Type @repeatable @repeatable {
220 field @repeatable @repeatable
221 }",
222 TEST_SCHEMA,
223 &mut plan,
224 );
225
226 assert_eq!(get_messages(&errors).len(), 0);
227}
228
229#[test]
230fn duplicate_directives_in_one_location() {
231 use crate::validation::test_utils::*;
232
233 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
234 let errors = test_operation_with_schema(
235 "fragment Test on Type {
236 field @onField @onField
237 }",
238 TEST_SCHEMA,
239 &mut plan,
240 );
241 let messages = get_messages(&errors);
242 assert_eq!(messages.len(), 1);
243 assert_eq!(messages, vec!["Duplicate directive \"onField\""])
244}
245
246#[test]
247fn many_duplicate_directives_in_one_location() {
248 use crate::validation::test_utils::*;
249
250 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
251 let errors = test_operation_with_schema(
252 "fragment Test on Type {
253 field @onField @onField @onField
254 }",
255 TEST_SCHEMA,
256 &mut plan,
257 );
258 let messages = get_messages(&errors);
259 assert_eq!(messages.len(), 2);
260 assert_eq!(
261 messages,
262 vec![
263 "Duplicate directive \"onField\"",
264 "Duplicate directive \"onField\""
265 ]
266 )
267}
268
269#[test]
270fn different_duplicate_directives_in_one_location() {
271 use crate::validation::test_utils::*;
272
273 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
274 let errors = test_operation_with_schema(
275 "fragment Test on Type {
276 field @onField @testDirective @onField @testDirective
277 }",
278 TEST_SCHEMA,
279 &mut plan,
280 );
281 let messages = get_messages(&errors);
282 assert_eq!(messages.len(), 2);
283 assert_eq!(
284 messages,
285 vec![
286 "Duplicate directive \"onField\"",
287 "Duplicate directive \"testDirective\""
288 ]
289 )
290}
291
292#[test]
293fn duplicate_directives_in_many_location() {
294 use crate::validation::test_utils::*;
295
296 let mut plan = create_plan_from_rule(Box::new(UniqueDirectivesPerLocation::new()));
297 let errors = test_operation_with_schema(
298 "fragment Test on Type @onFragmentDefinition @onFragmentDefinition {
299 field @onField @onField
300 }",
301 TEST_SCHEMA,
302 &mut plan,
303 );
304 let messages = get_messages(&errors);
305 assert_eq!(messages.len(), 2);
306 assert_eq!(
307 messages,
308 vec![
309 "Duplicate directive \"onFragmentDefinition\"",
310 "Duplicate directive \"onField\""
311 ]
312 )
313}