graphql_tools/validation/rules/
unique_operation_names.rs1use std::collections::HashMap;
2
3use super::ValidationRule;
4use crate::ast::{visit_document, AstNodeWithName, OperationVisitor, OperationVisitorContext};
5use crate::static_graphql::query::*;
6use crate::validation::utils::{ValidationError, ValidationErrorContext};
7
8pub struct UniqueOperationNames<'a> {
14 findings_counter: HashMap<&'a str, i32>,
15}
16
17impl<'a> OperationVisitor<'a, ValidationErrorContext> for UniqueOperationNames<'a> {
18 fn enter_operation_definition(
19 &mut self,
20 _: &mut OperationVisitorContext,
21 _: &mut ValidationErrorContext,
22 operation_definition: &'a OperationDefinition,
23 ) {
24 if let Some(name) = operation_definition.node_name() {
25 self.store_finding(name);
26 }
27 }
28}
29
30impl<'a> Default for UniqueOperationNames<'a> {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl<'a> UniqueOperationNames<'a> {
37 pub fn new() -> Self {
38 Self {
39 findings_counter: HashMap::new(),
40 }
41 }
42
43 fn store_finding(&mut self, name: &'a str) {
44 let value = *self.findings_counter.entry(name).or_insert(0);
45 self.findings_counter.insert(name, value + 1);
46 }
47}
48
49impl<'u> ValidationRule for UniqueOperationNames<'u> {
50 fn error_code<'a>(&self) -> &'a str {
51 "UniqueOperationNames"
52 }
53
54 fn validate(
55 &self,
56 ctx: &mut OperationVisitorContext,
57 error_collector: &mut ValidationErrorContext,
58 ) {
59 let mut rule = UniqueOperationNames::new();
60
61 visit_document(&mut rule, ctx.operation, ctx, error_collector);
62
63 rule.findings_counter
64 .into_iter()
65 .filter(|(_key, value)| *value > 1)
66 .for_each(|(key, _value)| {
67 error_collector.report_error(ValidationError {
68 error_code: self.error_code(),
69 message: format!("There can be only one operation named \"{}\".", key),
70 locations: vec![],
71 })
72 })
73 }
74}
75
76#[test]
77fn no_operations() {
78 use crate::validation::test_utils::*;
79
80 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
81 let errors = test_operation_with_schema(
82 "fragment fragA on Type {
83 field
84 }",
85 TEST_SCHEMA,
86 &mut plan,
87 );
88
89 assert_eq!(get_messages(&errors).len(), 0);
90}
91
92#[test]
93fn one_anon_operation() {
94 use crate::validation::test_utils::*;
95
96 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
97 let errors = test_operation_with_schema(
98 "{
99 field
100 }",
101 TEST_SCHEMA,
102 &mut plan,
103 );
104
105 let messages = get_messages(&errors);
106 assert_eq!(messages.len(), 0);
107}
108
109#[test]
110fn one_named_operation() {
111 use crate::validation::test_utils::*;
112
113 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
114 let errors = test_operation_with_schema(
115 "query Foo {
116 field
117 }",
118 TEST_SCHEMA,
119 &mut plan,
120 );
121
122 let messages = get_messages(&errors);
123 assert_eq!(messages.len(), 0);
124}
125
126#[test]
127fn multiple_operations() {
128 use crate::validation::test_utils::*;
129
130 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
131 let errors = test_operation_with_schema(
132 "query Foo {
133 field
134 }
135 query Bar {
136 field
137 }",
138 TEST_SCHEMA,
139 &mut plan,
140 );
141
142 let messages = get_messages(&errors);
143 assert_eq!(messages.len(), 0);
144}
145
146#[test]
147fn multiple_operations_of_different_types() {
148 use crate::validation::test_utils::*;
149
150 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
151 let errors = test_operation_with_schema(
152 "query Foo {
153 field
154 }
155 mutation Bar {
156 field
157 }
158 subscription Baz {
159 field
160 }",
161 TEST_SCHEMA,
162 &mut plan,
163 );
164
165 let messages = get_messages(&errors);
166 assert_eq!(messages.len(), 0);
167}
168
169#[test]
170fn fragment_and_operation_named_the_same() {
171 use crate::validation::test_utils::*;
172
173 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
174 let errors = test_operation_with_schema(
175 "query Foo {
176 ...Foo
177 }
178 fragment Foo on Type {
179 field
180 }",
181 TEST_SCHEMA,
182 &mut plan,
183 );
184
185 let messages = get_messages(&errors);
186 assert_eq!(messages.len(), 0);
187}
188
189#[test]
190fn multiple_operations_of_same_name() {
191 use crate::validation::test_utils::*;
192
193 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
194 let errors = test_operation_with_schema(
195 "query Foo {
196 fieldA
197 }
198 query Foo {
199 fieldB
200 }",
201 TEST_SCHEMA,
202 &mut plan,
203 );
204
205 let messages = get_messages(&errors);
206 assert_eq!(messages.len(), 1);
207 assert_eq!(
208 messages,
209 vec!["There can be only one operation named \"Foo\".",]
210 );
211}
212
213#[test]
214fn multiple_ops_of_same_name_of_different_types_mutation() {
215 use crate::validation::test_utils::*;
216
217 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
218 let errors = test_operation_with_schema(
219 "query Foo {
220 fieldA
221 }
222 mutation Foo {
223 fieldB
224 }",
225 TEST_SCHEMA,
226 &mut plan,
227 );
228
229 let messages = get_messages(&errors);
230 assert_eq!(messages.len(), 1);
231 assert_eq!(
232 messages,
233 vec!["There can be only one operation named \"Foo\".",]
234 );
235}
236
237#[test]
238fn multiple_ops_of_same_name_of_different_types_subscription() {
239 use crate::validation::test_utils::*;
240
241 let mut plan = create_plan_from_rule(Box::new(UniqueOperationNames::new()));
242 let errors = test_operation_with_schema(
243 "query Foo {
244 fieldA
245 }
246 subscription Foo {
247 fieldB
248 }",
249 TEST_SCHEMA,
250 &mut plan,
251 );
252
253 let messages = get_messages(&errors);
254 assert_eq!(messages.len(), 1);
255 assert_eq!(
256 messages,
257 vec!["There can be only one operation named \"Foo\".",]
258 );
259}