Skip to main content

graphql_tools/validation/rules/
provided_required_arguments.rs

1use 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
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<'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}