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