graphql_tools/validation/rules/
known_argument_names.rs1use super::ValidationRule;
2use crate::ast::ext::TypeDefinitionExtension;
3use crate::ast::{
4 visit_document, FieldByNameExtension, OperationVisitor, OperationVisitorContext,
5 SchemaDocumentExtension,
6};
7use crate::static_graphql::query::Directive;
8use crate::static_graphql::schema::{InputValue, TypeDefinition};
9use crate::validation::utils::{ValidationError, ValidationErrorContext};
10pub struct KnownArgumentNames<'a> {
18 current_known_arguments: Option<(ArgumentParent<'a>, &'a Vec<InputValue>)>,
19}
20
21#[derive(Debug)]
22enum ArgumentParent<'a> {
23 Field(&'a str, &'a TypeDefinition),
24 Directive(&'a str),
25}
26
27impl<'a> Default for KnownArgumentNames<'a> {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl<'a> KnownArgumentNames<'a> {
34 pub fn new() -> Self {
35 KnownArgumentNames {
36 current_known_arguments: None,
37 }
38 }
39}
40
41impl<'a> OperationVisitor<'a, ValidationErrorContext> for KnownArgumentNames<'a> {
42 fn enter_directive(
43 &mut self,
44 visitor_context: &mut OperationVisitorContext<'a>,
45 _: &mut ValidationErrorContext,
46 directive: &Directive,
47 ) {
48 if let Some(directive_def) = visitor_context.schema.directive_by_name(&directive.name) {
49 self.current_known_arguments = Some((
50 ArgumentParent::Directive(&directive_def.name),
51 &directive_def.arguments,
52 ));
53 }
54 }
55
56 fn leave_directive(
57 &mut self,
58 _: &mut OperationVisitorContext,
59 _: &mut ValidationErrorContext,
60 _: &crate::static_graphql::query::Directive,
61 ) {
62 self.current_known_arguments = None;
63 }
64
65 fn enter_field(
66 &mut self,
67 visitor_context: &mut OperationVisitorContext<'a>,
68 _: &mut ValidationErrorContext,
69 field: &crate::static_graphql::query::Field,
70 ) {
71 if let Some(parent_type) = visitor_context.current_parent_type() {
72 if let Some(field_def) = parent_type.field_by_name(&field.name) {
73 self.current_known_arguments = Some((
74 ArgumentParent::Field(
75 &field_def.name,
76 visitor_context
77 .current_parent_type()
78 .expect("Missing parent type"),
79 ),
80 &field_def.arguments,
81 ));
82 }
83 }
84 }
85
86 fn leave_field(
87 &mut self,
88 _: &mut OperationVisitorContext,
89 _: &mut ValidationErrorContext,
90 _: &crate::static_graphql::query::Field,
91 ) {
92 self.current_known_arguments = None;
93 }
94
95 fn enter_argument(
96 &mut self,
97 _: &mut OperationVisitorContext,
98 user_context: &mut ValidationErrorContext,
99 (argument_name, _argument_value): &(String, crate::static_graphql::query::Value),
100 ) {
101 if let Some((arg_position, args)) = &self.current_known_arguments {
102 if !args.iter().any(|a| a.name.eq(argument_name)) {
103 match arg_position {
104 ArgumentParent::Field(field_name, type_name) => {
105 user_context.report_error(ValidationError {
106 error_code: self.error_code(),
107 message: format!(
108 "Unknown argument \"{}\" on field \"{}.{}\".",
109 argument_name,
110 type_name.name(),
111 field_name
112 ),
113 locations: vec![],
114 })
115 }
116 ArgumentParent::Directive(directive_name) => {
117 user_context.report_error(ValidationError {
118 error_code: self.error_code(),
119 message: format!(
120 "Unknown argument \"{}\" on directive \"@{}\".",
121 argument_name, directive_name
122 ),
123 locations: vec![],
124 })
125 }
126 };
127 }
128 }
129 }
130}
131
132impl<'k> ValidationRule for KnownArgumentNames<'k> {
133 fn error_code<'a>(&self) -> &'a str {
134 "KnownArgumentNames"
135 }
136
137 fn validate(
138 &self,
139 ctx: &mut OperationVisitorContext,
140 error_collector: &mut ValidationErrorContext,
141 ) {
142 visit_document(
143 &mut KnownArgumentNames::new(),
144 ctx.operation,
145 ctx,
146 error_collector,
147 );
148 }
149}
150
151#[test]
152fn single_arg_is_known() {
153 use crate::validation::test_utils::*;
154
155 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
156 let errors = test_operation_with_schema(
157 "fragment argOnRequiredArg on Dog {
158 doesKnowCommand(dogCommand: SIT)
159 }",
160 TEST_SCHEMA,
161 &mut plan,
162 );
163
164 assert_eq!(get_messages(&errors).len(), 0);
165}
166
167#[test]
168fn multple_args_are_known() {
169 use crate::validation::test_utils::*;
170
171 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
172 let errors = test_operation_with_schema(
173 "fragment multipleArgs on ComplicatedArgs {
174 multipleReqs(req1: 1, req2: 2)
175 }",
176 TEST_SCHEMA,
177 &mut plan,
178 );
179
180 assert_eq!(get_messages(&errors).len(), 0);
181}
182
183#[test]
184fn ignores_args_of_unknown_fields() {
185 use crate::validation::test_utils::*;
186
187 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
188 let errors = test_operation_with_schema(
189 "fragment argOnUnknownField on Dog {
190 unknownField(unknownArg: SIT)
191 }",
192 TEST_SCHEMA,
193 &mut plan,
194 );
195
196 assert_eq!(get_messages(&errors).len(), 0);
197}
198
199#[test]
200fn multiple_args_in_reverse_order_are_known() {
201 use crate::validation::test_utils::*;
202
203 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
204 let errors = test_operation_with_schema(
205 "fragment multipleArgsReverseOrder on ComplicatedArgs {
206 multipleReqs(req2: 2, req1: 1)
207 }",
208 TEST_SCHEMA,
209 &mut plan,
210 );
211
212 assert_eq!(get_messages(&errors).len(), 0);
213}
214
215#[test]
216fn no_args_on_optional_arg() {
217 use crate::validation::test_utils::*;
218
219 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
220 let errors = test_operation_with_schema(
221 "fragment noArgOnOptionalArg on Dog {
222 isHouseTrained
223 }",
224 TEST_SCHEMA,
225 &mut plan,
226 );
227
228 assert_eq!(get_messages(&errors).len(), 0);
229}
230
231#[test]
232fn args_are_known_deeply() {
233 use crate::validation::test_utils::*;
234
235 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
236 let errors = test_operation_with_schema(
237 "{
238 dog {
239 doesKnowCommand(dogCommand: SIT)
240 }
241 human {
242 pet {
243 ... on Dog {
244 doesKnowCommand(dogCommand: SIT)
245 }
246 }
247 }
248 }",
249 TEST_SCHEMA,
250 &mut plan,
251 );
252
253 assert_eq!(get_messages(&errors).len(), 0);
254}
255
256#[test]
257fn directive_args_are_known() {
258 use crate::validation::test_utils::*;
259
260 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
261 let errors = test_operation_with_schema(
262 "{
263 dog @skip(if: true)
264 }",
265 TEST_SCHEMA,
266 &mut plan,
267 );
268
269 assert_eq!(get_messages(&errors).len(), 0);
270}
271
272#[test]
273fn field_args_are_invalid() {
274 use crate::validation::test_utils::*;
275
276 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
277 let errors = test_operation_with_schema(
278 "{
279 dog @skip(unless: true)
280 }",
281 TEST_SCHEMA,
282 &mut plan,
283 );
284
285 let messages = get_messages(&errors);
286 assert_eq!(messages.len(), 1);
287 assert_eq!(
288 messages,
289 vec!["Unknown argument \"unless\" on directive \"@skip\"."]
290 );
291}
292
293#[test]
294fn directive_without_args_is_valid() {
295 use crate::validation::test_utils::*;
296
297 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
298 let errors = test_operation_with_schema(
299 " {
300 dog @onField
301 }",
302 TEST_SCHEMA,
303 &mut plan,
304 );
305
306 let messages = get_messages(&errors);
307 assert_eq!(messages.len(), 0);
308}
309
310#[test]
311fn arg_passed_to_directive_without_arg_is_reported() {
312 use crate::validation::test_utils::*;
313
314 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
315 let errors = test_operation_with_schema(
316 " {
317 dog @onField(if: true)
318 }",
319 TEST_SCHEMA,
320 &mut plan,
321 );
322
323 let messages = get_messages(&errors);
324 assert_eq!(messages.len(), 1);
325 assert_eq!(
326 messages,
327 vec!["Unknown argument \"if\" on directive \"@onField\"."]
328 );
329}
330
331#[test]
332#[ignore = "Suggestions are not yet supported"]
333fn misspelled_directive_args_are_reported() {
334 use crate::validation::test_utils::*;
335
336 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
337 let errors = test_operation_with_schema(
338 "{
339 dog @skip(iff: true)
340 }",
341 TEST_SCHEMA,
342 &mut plan,
343 );
344
345 let messages = get_messages(&errors);
346 assert_eq!(messages.len(), 1);
347 assert_eq!(
348 messages,
349 vec!["Unknown argument \"iff\" on directive \"@onField\". Did you mean \"if\"?"]
350 );
351}
352
353#[test]
354fn invalid_arg_name() {
355 use crate::validation::test_utils::*;
356
357 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
358 let errors = test_operation_with_schema(
359 "fragment invalidArgName on Dog {
360 doesKnowCommand(unknown: true)
361 }",
362 TEST_SCHEMA,
363 &mut plan,
364 );
365
366 let messages = get_messages(&errors);
367 assert_eq!(messages.len(), 1);
368 assert_eq!(
369 messages,
370 vec!["Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\"."]
371 );
372}
373
374#[test]
375#[ignore = "Suggestions are not yet supported"]
376fn misspelled_arg_name_is_reported() {
377 use crate::validation::test_utils::*;
378
379 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
380 let errors = test_operation_with_schema(
381 "fragment invalidArgName on Dog {
382 doesKnowCommand(DogCommand: true)
383 }",
384 TEST_SCHEMA,
385 &mut plan,
386 );
387
388 let messages = get_messages(&errors);
389 assert_eq!(messages.len(), 1);
390 assert_eq!(
391 messages,
392 vec!["Unknown argument \"DogCommand\" on field \"Dog.doesKnowCommand\". Did you mean \"dogCommand\"?"]
393 );
394}
395
396#[test]
397fn unknown_args_amongst_known_args() {
398 use crate::validation::test_utils::*;
399
400 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
401 let errors = test_operation_with_schema(
402 "fragment oneGoodArgOneInvalidArg on Dog {
403 doesKnowCommand(whoKnows: 1, dogCommand: SIT, unknown: true)
404 }",
405 TEST_SCHEMA,
406 &mut plan,
407 );
408
409 let messages = get_messages(&errors);
410 assert_eq!(messages.len(), 2);
411 assert_eq!(
412 messages,
413 vec![
414 "Unknown argument \"whoKnows\" on field \"Dog.doesKnowCommand\".",
415 "Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\"."
416 ]
417 );
418}
419
420#[test]
421fn unknown_args_deeply() {
422 use crate::validation::test_utils::*;
423
424 let mut plan = create_plan_from_rule(Box::new(KnownArgumentNames::new()));
425 let errors = test_operation_with_schema(
426 "{
427 dog {
428 doesKnowCommand(unknown: true)
429 }
430 human {
431 pet {
432 ... on Dog {
433 doesKnowCommand(unknown: true)
434 }
435 }
436 }
437 }",
438 TEST_SCHEMA,
439 &mut plan,
440 );
441
442 let messages = get_messages(&errors);
443 assert_eq!(messages.len(), 2);
444 assert_eq!(
445 messages,
446 vec![
447 "Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\".",
448 "Unknown argument \"unknown\" on field \"Dog.doesKnowCommand\"."
449 ]
450 );
451}