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