async_graphql/validation/rules/
provided_non_null_arguments.rs1use 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}