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