graphql_tools/validation/rules/
provided_required_arguments.rs1use super::ValidationRule;
2use crate::ast::{visit_document, 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<'a> OperationVisitor<'a, 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<'a>(&self) -> &'a str {
95 "ProvidedRequiredArguments"
96 }
97
98 fn validate(
99 &self,
100 ctx: &mut OperationVisitorContext,
101 error_collector: &mut ValidationErrorContext,
102 ) {
103 visit_document(
104 &mut ProvidedRequiredArguments::new(),
105 ctx.operation,
106 ctx,
107 error_collector,
108 );
109 }
110}
111
112#[test]
113fn ignores_unknown_arguments() {
114 use crate::validation::test_utils::*;
115
116 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
117 let errors = test_operation_with_schema(
118 "{
119 dog {
120 isHouseTrained(unknownArgument: true)
121 }
122 }",
123 TEST_SCHEMA,
124 &mut plan,
125 );
126
127 assert_eq!(get_messages(&errors).len(), 0);
128}
129
130#[test]
131fn arg_on_optional_arg() {
132 use crate::validation::test_utils::*;
133
134 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
135 let errors = test_operation_with_schema(
136 "{
137 dog {
138 isHouseTrained(atOtherHomes: true)
139 }
140 }",
141 TEST_SCHEMA,
142 &mut plan,
143 );
144
145 assert_eq!(get_messages(&errors).len(), 0);
146}
147
148#[test]
149fn no_arg_on_optional_arg() {
150 use crate::validation::test_utils::*;
151
152 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
153 let errors = test_operation_with_schema(
154 "{
155 dog {
156 isHouseTrained
157 }
158 }",
159 TEST_SCHEMA,
160 &mut plan,
161 );
162
163 assert_eq!(get_messages(&errors).len(), 0);
164}
165
166#[test]
167fn multiple_args() {
168 use crate::validation::test_utils::*;
169
170 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
171 let errors = test_operation_with_schema(
172 "{
173 complicatedArgs {
174 multipleReqs(req1: 1, req2: 2)
175 }
176 }",
177 TEST_SCHEMA,
178 &mut plan,
179 );
180
181 assert_eq!(get_messages(&errors).len(), 0);
182}
183
184#[test]
185fn multiple_args_reverse_order() {
186 use crate::validation::test_utils::*;
187
188 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
189 let errors = test_operation_with_schema(
190 "{
191 complicatedArgs {
192 multipleReqs(req2: 2, req1: 1)
193 }
194 }",
195 TEST_SCHEMA,
196 &mut plan,
197 );
198
199 assert_eq!(get_messages(&errors).len(), 0);
200}
201
202#[test]
203fn no_args_on_multiple_optional() {
204 use crate::validation::test_utils::*;
205
206 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
207 let errors = test_operation_with_schema(
208 "{
209 complicatedArgs {
210 multipleOpts
211 }
212 }",
213 TEST_SCHEMA,
214 &mut plan,
215 );
216
217 assert_eq!(get_messages(&errors).len(), 0);
218}
219
220#[test]
221fn one_arg_on_multiple_optional() {
222 use crate::validation::test_utils::*;
223
224 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
225 let errors = test_operation_with_schema(
226 "{
227 complicatedArgs {
228 multipleOpts(opt1: 1)
229 }
230 }",
231 TEST_SCHEMA,
232 &mut plan,
233 );
234
235 assert_eq!(get_messages(&errors).len(), 0);
236}
237
238#[test]
239fn second_arg_on_multiple_optional() {
240 use crate::validation::test_utils::*;
241
242 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
243 let errors = test_operation_with_schema(
244 "{
245 complicatedArgs {
246 multipleOpts(opt2: 1)
247 }
248 }",
249 TEST_SCHEMA,
250 &mut plan,
251 );
252
253 assert_eq!(get_messages(&errors).len(), 0);
254}
255
256#[test]
257fn multiple_required_args_on_mixed_list() {
258 use crate::validation::test_utils::*;
259
260 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
261 let errors = test_operation_with_schema(
262 "{
263 complicatedArgs {
264 multipleOptAndReq(req1: 3, req2: 4)
265 }
266 }",
267 TEST_SCHEMA,
268 &mut plan,
269 );
270
271 assert_eq!(get_messages(&errors).len(), 0);
272}
273
274#[test]
275fn multiple_required_and_one_optional_arg_on_mixedlist() {
276 use crate::validation::test_utils::*;
277
278 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
279 let errors = test_operation_with_schema(
280 "{
281 complicatedArgs {
282 multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
283 }
284 }",
285 TEST_SCHEMA,
286 &mut plan,
287 );
288
289 assert_eq!(get_messages(&errors).len(), 0);
290}
291
292#[test]
293fn all_required_and_optional_args_on_mixedlist() {
294 use crate::validation::test_utils::*;
295
296 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
297 let errors = test_operation_with_schema(
298 "{
299 complicatedArgs {
300 multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
301 }
302 }",
303 TEST_SCHEMA,
304 &mut plan,
305 );
306
307 assert_eq!(get_messages(&errors).len(), 0);
308}
309
310#[test]
311fn missing_one_non_nullable_argument() {
312 use crate::validation::test_utils::*;
313
314 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
315 let errors = test_operation_with_schema(
316 "{
317 complicatedArgs {
318 multipleReqs(req2: 2)
319 }
320 }",
321 TEST_SCHEMA,
322 &mut plan,
323 );
324
325 let messages = get_messages(&errors);
326 assert_eq!(messages.len(), 1);
327 assert_eq!(messages, vec![
328 "Field \"multipleReqs\" argument \"req1\" of type \"Int!\" is required, but it was not provided."
329 ]);
330}
331
332#[test]
333fn missing_multiple_non_nullable_arguments() {
334 use crate::validation::test_utils::*;
335
336 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
337 let errors = test_operation_with_schema(
338 "{
339 complicatedArgs {
340 multipleReqs
341 }
342 }",
343 TEST_SCHEMA,
344 &mut plan,
345 );
346
347 let messages = get_messages(&errors);
348 assert_eq!(messages.len(), 2);
349 assert_eq!(messages, vec![
350 "Field \"multipleReqs\" argument \"req1\" of type \"Int!\" is required, but it was not provided.",
351 "Field \"multipleReqs\" argument \"req2\" of type \"Int!\" is required, but it was not provided."
352 ]);
353}
354
355#[test]
356fn incorrect_value_and_missing_argument() {
357 use crate::validation::test_utils::*;
358
359 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
360 let errors = test_operation_with_schema(
361 "{
362 complicatedArgs {
363 multipleReqs(req1: \"one\")
364 }
365 }",
366 TEST_SCHEMA,
367 &mut plan,
368 );
369
370 let messages = get_messages(&errors);
371 assert_eq!(messages.len(), 1);
372 assert_eq!(messages, vec![
373 "Field \"multipleReqs\" argument \"req2\" of type \"Int!\" is required, but it was not provided."
374 ]);
375}
376
377#[test]
378fn ignores_unknown_directives() {
379 use crate::validation::test_utils::*;
380
381 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
382 let errors = test_operation_with_schema(
383 "{
384 dog @unknown
385 }",
386 TEST_SCHEMA,
387 &mut plan,
388 );
389
390 let messages = get_messages(&errors);
391 assert_eq!(messages.len(), 0);
392}
393
394#[test]
395fn with_directives_of_valid_types() {
396 use crate::validation::test_utils::*;
397
398 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
399 let errors = test_operation_with_schema(
400 "{
401 dog @include(if: true) {
402 name
403 }
404 human @skip(if: false) {
405 name
406 }
407 }",
408 TEST_SCHEMA,
409 &mut plan,
410 );
411
412 let messages = get_messages(&errors);
413 assert_eq!(messages.len(), 0);
414}
415
416#[test]
417fn with_directive_with_missing_types() {
418 use crate::validation::test_utils::*;
419
420 let mut plan = create_plan_from_rule(Box::new(ProvidedRequiredArguments {}));
421 let errors = test_operation_with_schema(
422 "{
423 dog @include {
424 name @skip
425 }
426 }",
427 TEST_SCHEMA,
428 &mut plan,
429 );
430
431 let messages = get_messages(&errors);
432 assert_eq!(messages.len(), 2);
433 assert_eq!(messages, vec![
434 "Directive \"@include\" argument \"if\" of type \"Boolean!\" is required, but it was not provided.",
435 "Directive \"@skip\" argument \"if\" of type \"Boolean!\" is required, but it was not provided."
436 ])
437}