graphql_tools/validation/rules/
provided_required_arguments.rs1use super::ValidationRule;
2use crate::ast::{OperationVisitor, OperationVisitorContext};
3use crate::static_graphql::query::Value;
4use crate::static_graphql::schema::InputValue;
5use crate::validation::utils::{ValidationError, ValidationErrorContext};
6
7pub struct ProvidedRequiredArguments;
14
15impl Default for ProvidedRequiredArguments {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21impl ProvidedRequiredArguments {
22 pub fn new() -> Self {
23 ProvidedRequiredArguments
24 }
25}
26
27impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for ProvidedRequiredArguments {
28 fn enter_field(
29 &mut self,
30 visitor_context: &mut OperationVisitorContext,
31 user_context: &mut ValidationErrorContext,
32 field: &crate::static_graphql::query::Field,
33 ) {
34 if let Some(parent_type) = visitor_context.current_parent_type() {
35 if let Some(field_def) = parent_type.field_by_name(&field.name) {
36 let missing_required_args =
37 validate_arguments(&field.arguments, &field_def.arguments);
38
39 for missing in missing_required_args {
40 user_context.report_error(ValidationError {error_code: self.error_code(),
41 locations: vec![field.position],
42 message: format!("Field \"{}\" argument \"{}\" of type \"{}\" is required, but it was not provided.",
43 field.name, missing.name, missing.value_type),
44 });
45 }
46 }
47 }
48 }
49
50 fn enter_directive(
51 &mut self,
52 visitor_context: &mut OperationVisitorContext,
53 user_context: &mut ValidationErrorContext,
54 directive: &crate::static_graphql::query::Directive,
55 ) {
56 let known_directives = &visitor_context.directives;
57
58 if let Some(directive_def) = known_directives.get(&directive.name) {
59 let missing_required_args =
60 validate_arguments(&directive.arguments, &directive_def.arguments);
61
62 for missing in missing_required_args {
63 user_context.report_error(ValidationError {error_code: self.error_code(),
64 locations: vec![directive.position],
65 message: format!("Directive \"@{}\" argument \"{}\" of type \"{}\" is required, but it was not provided.",
66 directive.name, missing.name, missing.value_type),
67 });
68 }
69 }
70 }
71}
72
73fn validate_arguments(
74 arguments_used: &[(String, Value)],
75 arguments_defined: &[InputValue],
76) -> Vec<InputValue> {
77 arguments_defined
78 .iter()
79 .filter_map(|field_arg_def| {
80 if field_arg_def.is_required()
81 && !arguments_used
82 .iter()
83 .any(|(name, _value)| name.eq(&field_arg_def.name))
84 {
85 Some(field_arg_def.clone())
86 } else {
87 None
88 }
89 })
90 .collect()
91}
92
93impl ValidationRule for ProvidedRequiredArguments {
94 fn error_code(&self) -> &'static str {
95 "ProvidedRequiredArguments"
96 }
97
98 fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
99 Box::new(ProvidedRequiredArguments::new())
100 }
101}
102
103#[test]
104fn ignores_unknown_arguments() {
105 use crate::validation::test_utils::*;
106
107 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
108 let errors = test_operation_with_schema(
109 "{
110 dog {
111 isHouseTrained(unknownArgument: true)
112 }
113 }",
114 TEST_SCHEMA,
115 &mut plan,
116 );
117
118 assert_eq!(get_messages(&errors).len(), 0);
119}
120
121#[test]
122fn arg_on_optional_arg() {
123 use crate::validation::test_utils::*;
124
125 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
126 let errors = test_operation_with_schema(
127 "{
128 dog {
129 isHouseTrained(atOtherHomes: true)
130 }
131 }",
132 TEST_SCHEMA,
133 &mut plan,
134 );
135
136 assert_eq!(get_messages(&errors).len(), 0);
137}
138
139#[test]
140fn no_arg_on_optional_arg() {
141 use crate::validation::test_utils::*;
142
143 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
144 let errors = test_operation_with_schema(
145 "{
146 dog {
147 isHouseTrained
148 }
149 }",
150 TEST_SCHEMA,
151 &mut plan,
152 );
153
154 assert_eq!(get_messages(&errors).len(), 0);
155}
156
157#[test]
158fn no_arg_on_non_null_field_with_default() {
159 use crate::validation::test_utils::*;
160
161 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
162 let errors = test_operation_with_schema(
163 "{
164 complicatedArgs {
165 nonNullFieldWithDefault
166 }
167 }",
168 TEST_SCHEMA,
169 &mut plan,
170 );
171
172 assert_eq!(get_messages(&errors).len(), 0);
173}
174
175#[test]
176fn multiple_args() {
177 use crate::validation::test_utils::*;
178
179 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
180 let errors = test_operation_with_schema(
181 "{
182 complicatedArgs {
183 multipleReqs(req1: 1, req2: 2)
184 }
185 }",
186 TEST_SCHEMA,
187 &mut plan,
188 );
189
190 assert_eq!(get_messages(&errors).len(), 0);
191}
192
193#[test]
194fn multiple_args_reverse_order() {
195 use crate::validation::test_utils::*;
196
197 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
198 let errors = test_operation_with_schema(
199 "{
200 complicatedArgs {
201 multipleReqs(req2: 2, req1: 1)
202 }
203 }",
204 TEST_SCHEMA,
205 &mut plan,
206 );
207
208 assert_eq!(get_messages(&errors).len(), 0);
209}
210
211#[test]
212fn no_args_on_multiple_optional() {
213 use crate::validation::test_utils::*;
214
215 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
216 let errors = test_operation_with_schema(
217 "{
218 complicatedArgs {
219 multipleOpts
220 }
221 }",
222 TEST_SCHEMA,
223 &mut plan,
224 );
225
226 assert_eq!(get_messages(&errors).len(), 0);
227}
228
229#[test]
230fn one_arg_on_multiple_optional() {
231 use crate::validation::test_utils::*;
232
233 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
234 let errors = test_operation_with_schema(
235 "{
236 complicatedArgs {
237 multipleOpts(opt1: 1)
238 }
239 }",
240 TEST_SCHEMA,
241 &mut plan,
242 );
243
244 assert_eq!(get_messages(&errors).len(), 0);
245}
246
247#[test]
248fn second_arg_on_multiple_optional() {
249 use crate::validation::test_utils::*;
250
251 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
252 let errors = test_operation_with_schema(
253 "{
254 complicatedArgs {
255 multipleOpts(opt2: 1)
256 }
257 }",
258 TEST_SCHEMA,
259 &mut plan,
260 );
261
262 assert_eq!(get_messages(&errors).len(), 0);
263}
264
265#[test]
266fn multiple_required_args_on_mixed_list() {
267 use crate::validation::test_utils::*;
268
269 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
270 let errors = test_operation_with_schema(
271 "{
272 complicatedArgs {
273 multipleOptAndReq(req1: 3, req2: 4)
274 }
275 }",
276 TEST_SCHEMA,
277 &mut plan,
278 );
279
280 assert_eq!(get_messages(&errors).len(), 0);
281}
282
283#[test]
284fn multiple_required_and_one_optional_arg_on_mixedlist() {
285 use crate::validation::test_utils::*;
286
287 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
288 let errors = test_operation_with_schema(
289 "{
290 complicatedArgs {
291 multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
292 }
293 }",
294 TEST_SCHEMA,
295 &mut plan,
296 );
297
298 assert_eq!(get_messages(&errors).len(), 0);
299}
300
301#[test]
302fn all_required_and_optional_args_on_mixedlist() {
303 use crate::validation::test_utils::*;
304
305 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
306 let errors = test_operation_with_schema(
307 "{
308 complicatedArgs {
309 multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
310 }
311 }",
312 TEST_SCHEMA,
313 &mut plan,
314 );
315
316 assert_eq!(get_messages(&errors).len(), 0);
317}
318
319#[test]
320fn missing_one_non_nullable_argument() {
321 use crate::validation::test_utils::*;
322
323 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
324 let errors = test_operation_with_schema(
325 "{
326 complicatedArgs {
327 multipleReqs(req2: 2)
328 }
329 }",
330 TEST_SCHEMA,
331 &mut plan,
332 );
333
334 let messages = get_messages(&errors);
335 assert_eq!(messages.len(), 1);
336 assert_eq!(messages, vec![
337 "Field \"multipleReqs\" argument \"req1\" of type \"Int!\" is required, but it was not provided."
338 ]);
339}
340
341#[test]
342fn missing_multiple_non_nullable_arguments() {
343 use crate::validation::test_utils::*;
344
345 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
346 let errors = test_operation_with_schema(
347 "{
348 complicatedArgs {
349 multipleReqs
350 }
351 }",
352 TEST_SCHEMA,
353 &mut plan,
354 );
355
356 let messages = get_messages(&errors);
357 assert_eq!(messages.len(), 2);
358 assert_eq!(messages, vec![
359 "Field \"multipleReqs\" argument \"req1\" of type \"Int!\" is required, but it was not provided.",
360 "Field \"multipleReqs\" argument \"req2\" of type \"Int!\" is required, but it was not provided."
361 ]);
362}
363
364#[test]
365fn incorrect_value_and_missing_argument() {
366 use crate::validation::test_utils::*;
367
368 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
369 let errors = test_operation_with_schema(
370 "{
371 complicatedArgs {
372 multipleReqs(req1: \"one\")
373 }
374 }",
375 TEST_SCHEMA,
376 &mut plan,
377 );
378
379 let messages = get_messages(&errors);
380 assert_eq!(messages.len(), 1);
381 assert_eq!(messages, vec![
382 "Field \"multipleReqs\" argument \"req2\" of type \"Int!\" is required, but it was not provided."
383 ]);
384}
385
386#[test]
387fn ignores_unknown_directives() {
388 use crate::validation::test_utils::*;
389
390 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
391 let errors = test_operation_with_schema(
392 "{
393 dog @unknown
394 }",
395 TEST_SCHEMA,
396 &mut plan,
397 );
398
399 let messages = get_messages(&errors);
400 assert_eq!(messages.len(), 0);
401}
402
403#[test]
404fn with_directives_of_valid_types() {
405 use crate::validation::test_utils::*;
406
407 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
408 let errors = test_operation_with_schema(
409 "{
410 dog @include(if: true) {
411 name
412 }
413 human @skip(if: false) {
414 name
415 }
416 }",
417 TEST_SCHEMA,
418 &mut plan,
419 );
420
421 let messages = get_messages(&errors);
422 assert_eq!(messages.len(), 0);
423}
424
425#[test]
426fn with_directive_with_missing_types() {
427 use crate::validation::test_utils::*;
428
429 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
430 let errors = test_operation_with_schema(
431 "{
432 dog @include {
433 name @skip
434 }
435 }",
436 TEST_SCHEMA,
437 &mut plan,
438 );
439
440 let messages = get_messages(&errors);
441 assert_eq!(messages.len(), 2);
442 assert_eq!(messages, vec![
443 "Directive \"@include\" argument \"if\" of type \"Boolean!\" is required, but it was not provided.",
444 "Directive \"@skip\" argument \"if\" of type \"Boolean!\" is required, but it was not provided."
445 ])
446}