graphql_tools/validation/rules/
unique_argument_names.rs

1use std::collections::HashMap;
2
3use crate::parser::Pos;
4
5use super::ValidationRule;
6use crate::ast::{visit_document, OperationVisitor, OperationVisitorContext};
7use crate::static_graphql::query::Value;
8use crate::validation::utils::{ValidationError, ValidationErrorContext};
9
10/// Unique argument names
11///
12/// A GraphQL field or directive is only valid if all supplied arguments are
13/// uniquely named.
14///
15/// See https://spec.graphql.org/draft/#sec-Argument-Names
16pub struct UniqueArgumentNames;
17
18impl Default for UniqueArgumentNames {
19    fn default() -> Self {
20        Self::new()
21    }
22}
23
24impl UniqueArgumentNames {
25    pub fn new() -> Self {
26        UniqueArgumentNames
27    }
28}
29
30impl<'a> OperationVisitor<'a, ValidationErrorContext> for UniqueArgumentNames {
31    fn enter_field(
32        &mut self,
33        _: &mut OperationVisitorContext,
34        user_context: &mut ValidationErrorContext,
35        field: &crate::static_graphql::query::Field,
36    ) {
37        let found_args = collect_from_arguments(field.position, &field.arguments);
38
39        found_args.iter().for_each(|(arg_name, positions)| {
40            if positions.len() > 1 {
41                user_context.report_error(ValidationError {
42                    error_code: self.error_code(),
43                    message: format!("There can be only one argument named \"{}\".", arg_name),
44                    locations: positions.clone(),
45                })
46            }
47        });
48    }
49
50    fn enter_directive(
51        &mut self,
52        _: &mut OperationVisitorContext,
53        user_context: &mut ValidationErrorContext,
54        directive: &crate::static_graphql::query::Directive,
55    ) {
56        let found_args = collect_from_arguments(directive.position, &directive.arguments);
57
58        found_args.iter().for_each(|(arg_name, positions)| {
59            if positions.len() > 1 {
60                user_context.report_error(ValidationError {
61                    error_code: self.error_code(),
62                    message: format!("There can be only one argument named \"{}\".", arg_name),
63                    locations: positions.clone(),
64                })
65            }
66        });
67    }
68}
69
70fn collect_from_arguments(
71    reported_position: Pos,
72    arguments: &Vec<(String, Value)>,
73) -> HashMap<String, Vec<Pos>> {
74    let mut found_args = HashMap::<String, Vec<Pos>>::new();
75
76    for (arg_name, _arg_value) in arguments {
77        found_args
78            .entry(arg_name.clone())
79            .or_default()
80            .push(reported_position);
81    }
82
83    found_args
84}
85
86impl ValidationRule for UniqueArgumentNames {
87    fn error_code<'a>(&self) -> &'a str {
88        "UniqueArgumentNames"
89    }
90
91    fn validate(
92        &self,
93        ctx: &mut OperationVisitorContext,
94        error_collector: &mut ValidationErrorContext,
95    ) {
96        visit_document(
97            &mut UniqueArgumentNames::new(),
98            ctx.operation,
99            ctx,
100            error_collector,
101        );
102    }
103}
104
105#[test]
106fn no_arguments_on_field() {
107    use crate::validation::test_utils::*;
108
109    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
110    let errors = test_operation_with_schema(
111        "{
112          field
113        }",
114        TEST_SCHEMA,
115        &mut plan,
116    );
117
118    assert_eq!(get_messages(&errors).len(), 0);
119}
120
121#[test]
122fn no_arguments_on_directive() {
123    use crate::validation::test_utils::*;
124
125    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
126    let errors = test_operation_with_schema(
127        "{
128          field @directive
129        }",
130        TEST_SCHEMA,
131        &mut plan,
132    );
133
134    assert_eq!(get_messages(&errors).len(), 0);
135}
136
137#[test]
138fn argument_on_field() {
139    use crate::validation::test_utils::*;
140
141    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
142    let errors = test_operation_with_schema(
143        "{
144          field(arg: \"value\")
145        }",
146        TEST_SCHEMA,
147        &mut plan,
148    );
149
150    assert_eq!(get_messages(&errors).len(), 0);
151}
152
153#[test]
154fn argument_on_directive() {
155    use crate::validation::test_utils::*;
156
157    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
158    let errors = test_operation_with_schema(
159        "{
160          field @directive(arg: \"value\")
161        }",
162        TEST_SCHEMA,
163        &mut plan,
164    );
165
166    assert_eq!(get_messages(&errors).len(), 0);
167}
168
169#[test]
170fn same_argument_on_two_fields() {
171    use crate::validation::test_utils::*;
172
173    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
174    let errors = test_operation_with_schema(
175        "{
176          one: field(arg: \"value\")
177          two: field(arg: \"value\")
178        }",
179        TEST_SCHEMA,
180        &mut plan,
181    );
182
183    assert_eq!(get_messages(&errors).len(), 0);
184}
185
186#[test]
187fn same_argument_on_field_and_directive() {
188    use crate::validation::test_utils::*;
189
190    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
191    let errors = test_operation_with_schema(
192        "{
193          field(arg: \"value\") @directive(arg: \"value\")
194        }",
195        TEST_SCHEMA,
196        &mut plan,
197    );
198
199    assert_eq!(get_messages(&errors).len(), 0);
200}
201
202#[test]
203fn same_argument_on_two_directives() {
204    use crate::validation::test_utils::*;
205
206    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
207    let errors = test_operation_with_schema(
208        "{
209          field @directive1(arg: \"value\") @directive2(arg: \"value\")
210        }",
211        TEST_SCHEMA,
212        &mut plan,
213    );
214
215    assert_eq!(get_messages(&errors).len(), 0);
216}
217
218#[test]
219fn multiple_field_arguments() {
220    use crate::validation::test_utils::*;
221
222    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
223    let errors = test_operation_with_schema(
224        "{
225          field(arg1: \"value\", arg2: \"value\", arg3: \"value\")
226        }",
227        TEST_SCHEMA,
228        &mut plan,
229    );
230
231    assert_eq!(get_messages(&errors).len(), 0);
232}
233
234#[test]
235fn multiple_directive_argument() {
236    use crate::validation::test_utils::*;
237
238    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
239    let errors = test_operation_with_schema(
240        "{
241          field @directive(arg1: \"value\", arg2: \"value\", arg3: \"value\")
242        }",
243        TEST_SCHEMA,
244        &mut plan,
245    );
246
247    assert_eq!(get_messages(&errors).len(), 0);
248}
249
250#[test]
251fn duplicate_field_arguments() {
252    use crate::validation::test_utils::*;
253
254    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
255    let errors = test_operation_with_schema(
256        "{
257          field(arg1: \"value\", arg1: \"value\")
258        }",
259        TEST_SCHEMA,
260        &mut plan,
261    );
262
263    let messages = get_messages(&errors);
264    assert_eq!(messages.len(), 1);
265    assert_eq!(
266        messages,
267        vec!["There can be only one argument named \"arg1\"."]
268    );
269}
270
271#[test]
272fn many_duplicate_field_arguments() {
273    use crate::validation::test_utils::*;
274
275    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
276    let errors = test_operation_with_schema(
277        "{
278          field(arg1: \"value\", arg1: \"value\", arg1: \"value\")
279        }",
280        TEST_SCHEMA,
281        &mut plan,
282    );
283
284    let messages = get_messages(&errors);
285    assert_eq!(messages.len(), 1);
286    assert_eq!(
287        messages,
288        vec!["There can be only one argument named \"arg1\"."]
289    );
290}
291
292#[test]
293fn duplicate_directive_arguments() {
294    use crate::validation::test_utils::*;
295
296    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
297    let errors = test_operation_with_schema(
298        "{
299          field @directive(arg1: \"value\", arg1: \"value\")
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!["There can be only one argument named \"arg1\"."]
310    );
311}
312
313#[test]
314fn many_duplicate_directive_arguments() {
315    use crate::validation::test_utils::*;
316
317    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
318    let errors = test_operation_with_schema(
319        "{
320          field @directive(arg1: \"value\", arg1: \"value\", arg1: \"value\")
321        }",
322        TEST_SCHEMA,
323        &mut plan,
324    );
325
326    let messages = get_messages(&errors);
327    assert_eq!(messages.len(), 1);
328    assert_eq!(
329        messages,
330        vec!["There can be only one argument named \"arg1\"."]
331    );
332}