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