async_graphql/validation/rules/
provided_non_null_arguments.rs

1use crate::{
2    Positioned,
3    parser::types::{Directive, Field},
4    registry::MetaTypeName,
5    validation::visitor::{Visitor, VisitorContext},
6};
7
8#[derive(Default)]
9pub struct ProvidedNonNullArguments;
10
11impl<'a> Visitor<'a> for ProvidedNonNullArguments {
12    fn enter_directive(
13        &mut self,
14        ctx: &mut VisitorContext<'a>,
15        directive: &'a Positioned<Directive>,
16    ) {
17        if let Some(schema_directive) = ctx
18            .registry
19            .directives
20            .get(directive.node.name.node.as_str())
21        {
22            for arg in schema_directive.args.values() {
23                if MetaTypeName::create(&arg.ty).is_non_null()
24                    && arg.default_value.is_none()
25                    && !directive
26                        .node
27                        .arguments
28                        .iter()
29                        .any(|(name, _)| name.node == arg.name)
30                {
31                    ctx.report_error(vec![directive.pos],
32                            format!(
33                                "Directive \"@{}\" argument \"{}\" of type \"{}\" is required but not provided",
34                                directive.node.name, arg.name, arg.ty
35                            ));
36                }
37            }
38        }
39    }
40
41    fn enter_field(&mut self, ctx: &mut VisitorContext<'a>, field: &'a Positioned<Field>) {
42        if let Some(parent_type) = ctx.parent_type()
43            && let Some(schema_field) = parent_type.field_by_name(&field.node.name.node)
44        {
45            for arg in schema_field.args.values() {
46                if MetaTypeName::create(&arg.ty).is_non_null()
47                    && arg.default_value.is_none()
48                    && !field
49                        .node
50                        .arguments
51                        .iter()
52                        .any(|(name, _)| name.node == arg.name)
53                {
54                    ctx.report_error(
55                        vec![field.pos],
56                        format!(
57                            r#"Field "{}" argument "{}" of type "{}" is required but not provided"#,
58                            field.node.name,
59                            arg.name,
60                            parent_type.name()
61                        ),
62                    );
63                }
64            }
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    pub fn factory() -> ProvidedNonNullArguments {
74        ProvidedNonNullArguments
75    }
76
77    #[test]
78    fn ignores_unknown_arguments() {
79        expect_passes_rule!(
80            factory,
81            r#"
82          {
83            dog {
84              isHousetrained(unknownArgument: true)
85            }
86          }
87        "#,
88        );
89    }
90
91    #[test]
92    fn arg_on_optional_arg() {
93        expect_passes_rule!(
94            factory,
95            r#"
96            {
97              dog {
98                isHousetrained(atOtherHomes: true)
99              }
100            }
101        "#,
102        );
103    }
104
105    #[test]
106    fn no_arg_on_optional_arg() {
107        expect_passes_rule!(
108            factory,
109            r#"
110            {
111              dog {
112                isHousetrained
113              }
114            }
115        "#,
116        );
117    }
118
119    #[test]
120    fn multiple_args() {
121        expect_passes_rule!(
122            factory,
123            r#"
124            {
125              complicatedArgs {
126                multipleReqs(req1: 1, req2: 2)
127              }
128            }
129        "#,
130        );
131    }
132
133    #[test]
134    fn multiple_args_reverse_order() {
135        expect_passes_rule!(
136            factory,
137            r#"
138            {
139              complicatedArgs {
140                multipleReqs(req2: 2, req1: 1)
141              }
142            }
143        "#,
144        );
145    }
146
147    #[test]
148    fn no_args_on_multiple_optional() {
149        expect_passes_rule!(
150            factory,
151            r#"
152            {
153              complicatedArgs {
154                multipleOpts
155              }
156            }
157        "#,
158        );
159    }
160
161    #[test]
162    fn one_arg_on_multiple_optional() {
163        expect_passes_rule!(
164            factory,
165            r#"
166            {
167              complicatedArgs {
168                multipleOpts(opt1: 1)
169              }
170            }
171        "#,
172        );
173    }
174
175    #[test]
176    fn second_arg_on_multiple_optional() {
177        expect_passes_rule!(
178            factory,
179            r#"
180            {
181              complicatedArgs {
182                multipleOpts(opt2: 1)
183              }
184            }
185        "#,
186        );
187    }
188
189    #[test]
190    fn muliple_reqs_on_mixed_list() {
191        expect_passes_rule!(
192            factory,
193            r#"
194            {
195              complicatedArgs {
196                multipleOptAndReq(req1: 3, req2: 4)
197              }
198            }
199        "#,
200        );
201    }
202
203    #[test]
204    fn multiple_reqs_and_one_opt_on_mixed_list() {
205        expect_passes_rule!(
206            factory,
207            r#"
208            {
209              complicatedArgs {
210                multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
211              }
212            }
213        "#,
214        );
215    }
216
217    #[test]
218    fn all_reqs_on_opts_on_mixed_list() {
219        expect_passes_rule!(
220            factory,
221            r#"
222            {
223              complicatedArgs {
224                multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
225              }
226            }
227        "#,
228        );
229    }
230
231    #[test]
232    fn missing_one_non_nullable_argument() {
233        expect_fails_rule!(
234            factory,
235            r#"
236            {
237              complicatedArgs {
238                multipleReqs(req2: 2)
239              }
240            }
241        "#,
242        );
243    }
244
245    #[test]
246    fn missing_multiple_non_nullable_arguments() {
247        expect_fails_rule!(
248            factory,
249            r#"
250            {
251              complicatedArgs {
252                multipleReqs
253              }
254            }
255        "#,
256        );
257    }
258
259    #[test]
260    fn incorrect_value_and_missing_argument() {
261        expect_fails_rule!(
262            factory,
263            r#"
264            {
265              complicatedArgs {
266                multipleReqs(req1: "one")
267              }
268            }
269        "#,
270        );
271    }
272
273    #[test]
274    fn ignores_unknown_directives() {
275        expect_passes_rule!(
276            factory,
277            r#"
278            {
279              dog @unknown
280            }
281        "#,
282        );
283    }
284
285    #[test]
286    fn with_directives_of_valid_types() {
287        expect_passes_rule!(
288            factory,
289            r#"
290            {
291              dog @include(if: true) {
292                name
293              }
294              human @skip(if: false) {
295                name
296              }
297            }
298        "#,
299        );
300    }
301
302    #[test]
303    fn with_directive_with_missing_types() {
304        expect_fails_rule!(
305            factory,
306            r#"
307            {
308              dog @include {
309                name @skip
310              }
311            }
312        "#,
313        );
314    }
315}