Skip to main content

graphql_tools/validation/rules/
unique_input_field_names.rs

1use std::collections::HashSet;
2
3use super::ValidationRule;
4use crate::ast::{OperationVisitor, OperationVisitorContext};
5use crate::static_graphql::query::Value;
6use crate::validation::utils::{ValidationError, ValidationErrorContext};
7
8/// Unique input field names
9///
10/// A GraphQL input object is only valid if all supplied fields are
11/// uniquely named.
12///
13/// See https://spec.graphql.org/draft/#sec-Input-Object-Field-Uniqueness
14pub struct UniqueInputFieldNames;
15
16impl Default for UniqueInputFieldNames {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22impl UniqueInputFieldNames {
23    pub fn new() -> Self {
24        UniqueInputFieldNames
25    }
26}
27
28impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for UniqueInputFieldNames {
29    fn enter_object_value(
30        &mut self,
31        _: &mut OperationVisitorContext,
32        user_context: &mut ValidationErrorContext,
33        object_value: &[(String, Value)],
34    ) {
35        let mut seen = HashSet::new();
36        for (field_name, _) in object_value {
37            if !seen.insert(field_name) {
38                user_context.report_error(ValidationError {
39                    error_code: self.error_code(),
40                    message: format!(
41                        "There can be only one input field named \"{}\".",
42                        field_name
43                    ),
44                    locations: vec![],
45                });
46            }
47        }
48    }
49}
50
51impl ValidationRule for UniqueInputFieldNames {
52    fn error_code(&self) -> &'static str {
53        "UniqueInputFieldNames"
54    }
55
56    fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
57        Box::new(UniqueInputFieldNames::new())
58    }
59}
60
61#[test]
62fn input_object_with_fields() {
63    use crate::validation::test_utils::*;
64
65    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
66    let errors = test_operation_with_schema(
67        "{
68          complicatedArgs {
69            complexArgField(complexArg: { requiredField: true })
70          }
71        }",
72        TEST_SCHEMA,
73        &mut plan,
74    );
75
76    assert_eq!(get_messages(&errors).len(), 0);
77}
78
79#[test]
80fn input_object_with_two_fields() {
81    use crate::validation::test_utils::*;
82
83    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
84    let errors = test_operation_with_schema(
85        "{
86          complicatedArgs {
87            complexArgField(complexArg: { requiredField: true, intField: 5 })
88          }
89        }",
90        TEST_SCHEMA,
91        &mut plan,
92    );
93
94    assert_eq!(get_messages(&errors).len(), 0);
95}
96
97#[test]
98fn same_input_object_within_two_args() {
99    use crate::validation::test_utils::*;
100
101    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
102    let errors = test_operation_with_schema(
103        "{
104          complicatedArgs {
105            a: complexArgField(complexArg: { requiredField: true })
106            b: complexArgField(complexArg: { requiredField: true })
107          }
108        }",
109        TEST_SCHEMA,
110        &mut plan,
111    );
112
113    assert_eq!(get_messages(&errors).len(), 0);
114}
115
116#[test]
117fn multiple_input_object_fields() {
118    use crate::validation::test_utils::*;
119
120    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
121    let errors = test_operation_with_schema(
122        "{
123          complicatedArgs {
124            complexArgField(complexArg: { requiredField: true, intField: 5, stringField: \"hello\" })
125          }
126        }",
127        TEST_SCHEMA,
128        &mut plan,
129    );
130
131    assert_eq!(get_messages(&errors).len(), 0);
132}
133
134#[test]
135fn allows_for_nested_input_objects_with_similar_fields() {
136    use crate::validation::test_utils::*;
137
138    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
139    let errors = test_operation_with_schema(
140        "{
141          complicatedArgs {
142            complexArgField(complexArg: { requiredField: true, nested: { requiredField: false } })
143          }
144        }",
145        TEST_SCHEMA,
146        &mut plan,
147    );
148
149    assert_eq!(get_messages(&errors).len(), 0);
150}
151
152#[test]
153fn duplicate_input_object_fields() {
154    use crate::validation::test_utils::*;
155
156    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
157    let errors = test_operation_with_schema(
158        "{
159          complicatedArgs {
160            complexArgField(complexArg: { requiredField: true, requiredField: true })
161          }
162        }",
163        TEST_SCHEMA,
164        &mut plan,
165    );
166
167    let messages = get_messages(&errors);
168    assert_eq!(messages.len(), 1);
169    assert_eq!(
170        messages,
171        vec!["There can be only one input field named \"requiredField\"."]
172    );
173}
174#[test]
175fn many_duplicate_input_object_fields() {
176    use crate::validation::test_utils::*;
177
178    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
179    let errors = test_operation_with_schema(
180        "{
181          complicatedArgs {
182            complexArgField(complexArg: { intField: 5, intField: 6, intField: 7 })
183          }
184        }",
185        TEST_SCHEMA,
186        &mut plan,
187    );
188
189    let messages = get_messages(&errors);
190    assert_eq!(messages.len(), 2);
191    assert_eq!(
192        messages,
193        vec!["There can be only one input field named \"intField\"."; 2]
194    );
195}
196
197#[test]
198fn nested_duplicate_input_object_fields() {
199    use crate::validation::test_utils::*;
200
201    let mut plan = create_plan_from_rule(Box::new(UniqueInputFieldNames {}));
202    let errors = test_operation_with_schema(
203        "{
204          complicatedArgs {
205            complexArgField(complexArg: { stringField: \"a\", nested: { intField: 1, intField: 2 } })
206          }
207        }",
208        TEST_SCHEMA,
209        &mut plan,
210    );
211
212    let messages = get_messages(&errors);
213    assert_eq!(messages.len(), 1);
214    assert_eq!(
215        messages,
216        vec!["There can be only one input field named \"intField\"."]
217    );
218}