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