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