Skip to main content

graphql_tools/validation/rules/
provided_required_arguments.rs

1use 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
7/// Provided required arguments
8///
9/// A field or directive is only valid if all required (non-null without a
10/// default value) field arguments have been provided.
11///
12/// See https://spec.graphql.org/draft/#sec-Required-Arguments
13pub 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}