Skip to main content

graphql_tools/validation/rules/
unique_argument_names.rs

1use std::collections::HashMap;
2
3use crate::parser::Pos;
4
5use super::ValidationRule;
6use crate::ast::{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<'doc> OperationVisitor<'doc, 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(&self) -> &'static str {
88        "UniqueArgumentNames"
89    }
90
91    fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
92        Box::new(UniqueArgumentNames::new())
93    }
94}
95
96#[test]
97fn no_arguments_on_field() {
98    use crate::validation::test_utils::*;
99
100    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
101    let errors = test_operation_with_schema(
102        "{
103          field
104        }",
105        TEST_SCHEMA,
106        &mut plan,
107    );
108
109    assert_eq!(get_messages(&errors).len(), 0);
110}
111
112#[test]
113fn no_arguments_on_directive() {
114    use crate::validation::test_utils::*;
115
116    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
117    let errors = test_operation_with_schema(
118        "{
119          field @directive
120        }",
121        TEST_SCHEMA,
122        &mut plan,
123    );
124
125    assert_eq!(get_messages(&errors).len(), 0);
126}
127
128#[test]
129fn argument_on_field() {
130    use crate::validation::test_utils::*;
131
132    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
133    let errors = test_operation_with_schema(
134        "{
135          field(arg: \"value\")
136        }",
137        TEST_SCHEMA,
138        &mut plan,
139    );
140
141    assert_eq!(get_messages(&errors).len(), 0);
142}
143
144#[test]
145fn argument_on_directive() {
146    use crate::validation::test_utils::*;
147
148    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
149    let errors = test_operation_with_schema(
150        "{
151          field @directive(arg: \"value\")
152        }",
153        TEST_SCHEMA,
154        &mut plan,
155    );
156
157    assert_eq!(get_messages(&errors).len(), 0);
158}
159
160#[test]
161fn same_argument_on_two_fields() {
162    use crate::validation::test_utils::*;
163
164    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
165    let errors = test_operation_with_schema(
166        "{
167          one: field(arg: \"value\")
168          two: field(arg: \"value\")
169        }",
170        TEST_SCHEMA,
171        &mut plan,
172    );
173
174    assert_eq!(get_messages(&errors).len(), 0);
175}
176
177#[test]
178fn same_argument_on_field_and_directive() {
179    use crate::validation::test_utils::*;
180
181    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
182    let errors = test_operation_with_schema(
183        "{
184          field(arg: \"value\") @directive(arg: \"value\")
185        }",
186        TEST_SCHEMA,
187        &mut plan,
188    );
189
190    assert_eq!(get_messages(&errors).len(), 0);
191}
192
193#[test]
194fn same_argument_on_two_directives() {
195    use crate::validation::test_utils::*;
196
197    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
198    let errors = test_operation_with_schema(
199        "{
200          field @directive1(arg: \"value\") @directive2(arg: \"value\")
201        }",
202        TEST_SCHEMA,
203        &mut plan,
204    );
205
206    assert_eq!(get_messages(&errors).len(), 0);
207}
208
209#[test]
210fn multiple_field_arguments() {
211    use crate::validation::test_utils::*;
212
213    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
214    let errors = test_operation_with_schema(
215        "{
216          field(arg1: \"value\", arg2: \"value\", arg3: \"value\")
217        }",
218        TEST_SCHEMA,
219        &mut plan,
220    );
221
222    assert_eq!(get_messages(&errors).len(), 0);
223}
224
225#[test]
226fn multiple_directive_argument() {
227    use crate::validation::test_utils::*;
228
229    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
230    let errors = test_operation_with_schema(
231        "{
232          field @directive(arg1: \"value\", arg2: \"value\", arg3: \"value\")
233        }",
234        TEST_SCHEMA,
235        &mut plan,
236    );
237
238    assert_eq!(get_messages(&errors).len(), 0);
239}
240
241#[test]
242fn duplicate_field_arguments() {
243    use crate::validation::test_utils::*;
244
245    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
246    let errors = test_operation_with_schema(
247        "{
248          field(arg1: \"value\", arg1: \"value\")
249        }",
250        TEST_SCHEMA,
251        &mut plan,
252    );
253
254    let messages = get_messages(&errors);
255    assert_eq!(messages.len(), 1);
256    assert_eq!(
257        messages,
258        vec!["There can be only one argument named \"arg1\"."]
259    );
260}
261
262#[test]
263fn many_duplicate_field_arguments() {
264    use crate::validation::test_utils::*;
265
266    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
267    let errors = test_operation_with_schema(
268        "{
269          field(arg1: \"value\", arg1: \"value\", arg1: \"value\")
270        }",
271        TEST_SCHEMA,
272        &mut plan,
273    );
274
275    let messages = get_messages(&errors);
276    assert_eq!(messages.len(), 1);
277    assert_eq!(
278        messages,
279        vec!["There can be only one argument named \"arg1\"."]
280    );
281}
282
283#[test]
284fn duplicate_directive_arguments() {
285    use crate::validation::test_utils::*;
286
287    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
288    let errors = test_operation_with_schema(
289        "{
290          field @directive(arg1: \"value\", arg1: \"value\")
291        }",
292        TEST_SCHEMA,
293        &mut plan,
294    );
295
296    let messages = get_messages(&errors);
297    assert_eq!(messages.len(), 1);
298    assert_eq!(
299        messages,
300        vec!["There can be only one argument named \"arg1\"."]
301    );
302}
303
304#[test]
305fn many_duplicate_directive_arguments() {
306    use crate::validation::test_utils::*;
307
308    let mut plan = create_plan_from_rule(Box::new(UniqueArgumentNames {}));
309    let errors = test_operation_with_schema(
310        "{
311          field @directive(arg1: \"value\", arg1: \"value\", arg1: \"value\")
312        }",
313        TEST_SCHEMA,
314        &mut plan,
315    );
316
317    let messages = get_messages(&errors);
318    assert_eq!(messages.len(), 1);
319    assert_eq!(
320        messages,
321        vec!["There can be only one argument named \"arg1\"."]
322    );
323}