graphql_tools/validation/rules/
lone_anonymous_operation.rs1use super::ValidationRule;
2use crate::ast::{OperationVisitor, OperationVisitorContext};
3use crate::static_graphql::query::*;
4use crate::validation::utils::{ValidationError, ValidationErrorContext};
5
6pub struct LoneAnonymousOperation;
13
14impl Default for LoneAnonymousOperation {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl LoneAnonymousOperation {
21 pub fn new() -> Self {
22 LoneAnonymousOperation
23 }
24}
25
26impl<'doc> OperationVisitor<'doc, ValidationErrorContext> for LoneAnonymousOperation {
27 fn enter_document(
28 &mut self,
29 _: &mut OperationVisitorContext,
30 user_context: &mut ValidationErrorContext,
31 document: &Document,
32 ) {
33 let operations_count = document
34 .definitions
35 .iter()
36 .filter(|n| {
37 matches!(
38 n,
39 Definition::Operation(OperationDefinition::SelectionSet(_))
40 | Definition::Operation(OperationDefinition::Query(_))
41 | Definition::Operation(OperationDefinition::Mutation(_))
42 | Definition::Operation(OperationDefinition::Subscription(_))
43 )
44 })
45 .count();
46
47 for definition in &document.definitions {
48 match definition {
49 Definition::Operation(OperationDefinition::SelectionSet(_))
50 if operations_count > 1 =>
51 {
52 user_context.report_error(ValidationError {
53 error_code: self.error_code(),
54 message: "This anonymous operation must be the only defined operation."
55 .to_string(),
56 locations: vec![],
57 })
58 }
59 Definition::Operation(OperationDefinition::Query(query))
60 if query.name.is_none() && operations_count > 1 =>
61 {
62 user_context.report_error(ValidationError {
63 error_code: self.error_code(),
64 message: "This anonymous operation must be the only defined operation."
65 .to_string(),
66 locations: vec![query.position],
67 })
68 }
69 Definition::Operation(OperationDefinition::Mutation(mutation))
70 if mutation.name.is_none() && operations_count > 1 =>
71 {
72 user_context.report_error(ValidationError {
73 error_code: self.error_code(),
74 message: "This anonymous operation must be the only defined operation."
75 .to_string(),
76 locations: vec![mutation.position],
77 })
78 }
79 Definition::Operation(OperationDefinition::Subscription(subscription))
80 if subscription.name.is_none() && operations_count > 1 =>
81 {
82 user_context.report_error(ValidationError {
83 error_code: self.error_code(),
84 message: "This anonymous operation must be the only defined operation."
85 .to_string(),
86 locations: vec![subscription.position],
87 })
88 }
89 _ => {}
90 };
91 }
92 }
93}
94
95impl ValidationRule for LoneAnonymousOperation {
96 fn error_code(&self) -> &'static str {
97 "LoneAnonymousOperation"
98 }
99
100 fn visitor<'doc>(&self) -> super::ValidationVisitor<'doc> {
101 Box::new(LoneAnonymousOperation::new())
102 }
103}
104
105#[test]
106fn no_operations() {
107 use crate::validation::test_utils::*;
108
109 let mut plan = create_plan_from_rule(Box::new(LoneAnonymousOperation {}));
110 let errors = test_operation_with_schema(
111 "fragment fragA on Type {
112 field
113 }",
114 TEST_SCHEMA,
115 &mut plan,
116 );
117
118 assert_eq!(get_messages(&errors).len(), 0);
119}
120
121#[test]
122fn one_anon_operation() {
123 use crate::validation::test_utils::*;
124
125 let mut plan = create_plan_from_rule(Box::new(LoneAnonymousOperation {}));
126 let errors = test_operation_with_schema(
127 "{
128 field
129 }",
130 TEST_SCHEMA,
131 &mut plan,
132 );
133
134 assert_eq!(get_messages(&errors).len(), 0);
135}
136
137#[test]
138fn mutiple_named() {
139 use crate::validation::test_utils::*;
140
141 let mut plan = create_plan_from_rule(Box::new(LoneAnonymousOperation {}));
142 let errors = test_operation_with_schema(
143 "query Foo {
144 field
145 }
146 query Bar {
147 field
148 }",
149 TEST_SCHEMA,
150 &mut plan,
151 );
152
153 assert_eq!(get_messages(&errors).len(), 0);
154}
155
156#[test]
157fn anon_operation_with_fragment() {
158 use crate::validation::test_utils::*;
159
160 let mut plan = create_plan_from_rule(Box::new(LoneAnonymousOperation {}));
161 let errors = test_operation_with_schema(
162 "{
163 ...Foo
164 }
165 fragment Foo on Type {
166 field
167 }",
168 TEST_SCHEMA,
169 &mut plan,
170 );
171
172 assert_eq!(get_messages(&errors).len(), 0);
173}
174
175#[test]
176fn multiple_anon_operations() {
177 use crate::validation::test_utils::*;
178
179 let mut plan = create_plan_from_rule(Box::new(LoneAnonymousOperation {}));
180 let errors = test_operation_with_schema(
181 "{
182 fieldA
183 }
184 {
185 fieldB
186 }",
187 TEST_SCHEMA,
188 &mut plan,
189 );
190
191 let messages = get_messages(&errors);
192 assert_eq!(messages.len(), 2);
193 assert_eq!(
194 messages,
195 vec![
196 "This anonymous operation must be the only defined operation.",
197 "This anonymous operation must be the only defined operation."
198 ]
199 );
200}
201
202#[test]
203fn anon_operation_with_mutation() {
204 use crate::validation::test_utils::*;
205
206 let mut plan = create_plan_from_rule(Box::new(LoneAnonymousOperation {}));
207 let errors = test_operation_with_schema(
208 "{
209 fieldA
210 }
211 mutation Foo {
212 fieldB
213 }",
214 TEST_SCHEMA,
215 &mut plan,
216 );
217
218 let messages = get_messages(&errors);
219 assert_eq!(messages.len(), 1);
220 assert_eq!(
221 messages,
222 vec!["This anonymous operation must be the only defined operation."]
223 );
224}
225
226#[test]
227fn anon_operation_with_subscription() {
228 use crate::validation::test_utils::*;
229
230 let mut plan = create_plan_from_rule(Box::new(LoneAnonymousOperation {}));
231 let errors = test_operation_with_schema(
232 "{
233 fieldA
234 }
235 subscription Foo {
236 fieldB
237 }",
238 TEST_SCHEMA,
239 &mut plan,
240 );
241
242 let messages = get_messages(&errors);
243 assert_eq!(messages.len(), 1);
244 assert_eq!(
245 messages,
246 vec!["This anonymous operation must be the only defined operation."]
247 );
248}