1use std::{
2 any::Any,
3 fmt::{self, Debug, Formatter},
4};
5
6use serde::{Deserialize, Deserializer, Serialize};
7
8use crate::{
9 parser::{parse_query, types::ExecutableDocument},
10 schema::IntrospectionMode,
11 Data, Extensions, ParseRequestError, ServerError, UploadValue, Value, Variables,
12};
13
14#[non_exhaustive]
20#[derive(Serialize, Deserialize)]
21#[serde(rename_all = "camelCase")]
22pub struct Request {
23 #[serde(default)]
25 pub query: String,
26
27 #[serde(default, rename = "operationName")]
29 pub operation_name: Option<String>,
30
31 #[serde(default)]
33 pub variables: Variables,
34
35 #[serde(skip)]
37 pub uploads: Vec<UploadValue>,
38
39 #[serde(skip)]
43 pub data: Data,
44
45 #[serde(default)]
47 pub extensions: Extensions,
48
49 #[serde(skip)]
50 pub(crate) parsed_query: Option<ExecutableDocument>,
51
52 #[serde(skip)]
55 pub introspection_mode: IntrospectionMode,
56}
57
58impl Request {
59 pub fn new(query: impl Into<String>) -> Self {
61 Self {
62 query: query.into(),
63 operation_name: None,
64 variables: Variables::default(),
65 uploads: Vec::default(),
66 data: Data::default(),
67 extensions: Default::default(),
68 parsed_query: None,
69 introspection_mode: IntrospectionMode::Enabled,
70 }
71 }
72
73 #[must_use]
75 pub fn operation_name<T: Into<String>>(self, name: T) -> Self {
76 Self {
77 operation_name: Some(name.into()),
78 ..self
79 }
80 }
81
82 #[must_use]
84 pub fn variables(self, variables: Variables) -> Self {
85 Self { variables, ..self }
86 }
87
88 #[must_use]
90 pub fn data<D: Any + Send + Sync>(mut self, data: D) -> Self {
91 self.data.insert(data);
92 self
93 }
94
95 #[must_use]
97 pub fn disable_introspection(mut self) -> Self {
98 self.introspection_mode = IntrospectionMode::Disabled;
99 self
100 }
101
102 #[must_use]
104 pub fn only_introspection(mut self) -> Self {
105 self.introspection_mode = IntrospectionMode::IntrospectionOnly;
106 self
107 }
108
109 #[inline]
110 pub fn parsed_query(&mut self) -> Result<&ExecutableDocument, ServerError> {
115 if self.parsed_query.is_none() {
116 match parse_query(&self.query) {
117 Ok(parsed) => self.parsed_query = Some(parsed),
118 Err(error) => return Err(error.into()),
119 }
120 }
121
122 Ok(self.parsed_query.as_ref().unwrap())
125 }
126
127 pub fn set_parsed_query(&mut self, doc: ExecutableDocument) {
133 self.parsed_query = Some(doc);
134 }
135
136 pub fn set_upload(&mut self, var_path: &str, upload: UploadValue) {
143 fn variable_path<'a>(variables: &'a mut Variables, path: &str) -> Option<&'a mut Value> {
144 let mut parts = path.strip_prefix("variables.")?.split('.');
145
146 let initial = variables.get_mut(parts.next().unwrap())?;
147
148 parts.try_fold(initial, |current, part| match current {
149 Value::List(list) => part
150 .parse::<u32>()
151 .ok()
152 .and_then(|idx| usize::try_from(idx).ok())
153 .and_then(move |idx| list.get_mut(idx)),
154 Value::Object(obj) => obj.get_mut(part),
155 _ => None,
156 })
157 }
158
159 let variable = match variable_path(&mut self.variables, var_path) {
160 Some(variable) => variable,
161 None => return,
162 };
163 self.uploads.push(upload);
164 *variable = Value::String(format!("#__graphql_file__:{}", self.uploads.len() - 1));
165 }
166}
167
168impl<T: Into<String>> From<T> for Request {
169 fn from(query: T) -> Self {
170 Self::new(query)
171 }
172}
173
174impl Debug for Request {
175 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
176 f.debug_struct("Request")
177 .field("query", &self.query)
178 .field("operation_name", &self.operation_name)
179 .field("variables", &self.variables)
180 .field("extensions", &self.extensions)
181 .finish()
182 }
183}
184
185#[derive(Debug, Deserialize)]
190#[serde(untagged)]
191#[allow(clippy::large_enum_variant)] pub enum BatchRequest {
193 Single(Request),
195
196 #[serde(deserialize_with = "deserialize_non_empty_vec")]
198 Batch(Vec<Request>),
199}
200
201impl BatchRequest {
202 pub fn into_single(self) -> Result<Request, ParseRequestError> {
209 match self {
210 Self::Single(req) => Ok(req),
211 Self::Batch(_) => Err(ParseRequestError::UnsupportedBatch),
212 }
213 }
214
215 pub fn iter(&self) -> impl Iterator<Item = &Request> {
217 match self {
218 BatchRequest::Single(request) => {
219 Box::new(std::iter::once(request)) as Box<dyn Iterator<Item = &Request>>
220 }
221 BatchRequest::Batch(requests) => Box::new(requests.iter()),
222 }
223 }
224
225 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Request> {
227 match self {
228 BatchRequest::Single(request) => {
229 Box::new(std::iter::once(request)) as Box<dyn Iterator<Item = &mut Request>>
230 }
231 BatchRequest::Batch(requests) => Box::new(requests.iter_mut()),
232 }
233 }
234
235 #[must_use]
237 pub fn variables(mut self, variables: Variables) -> Self {
238 for request in self.iter_mut() {
239 request.variables = variables.clone();
240 }
241 self
242 }
243
244 #[must_use]
246 pub fn data<D: Any + Clone + Send + Sync>(mut self, data: D) -> Self {
247 for request in self.iter_mut() {
248 request.data.insert(data.clone());
249 }
250 self
251 }
252
253 #[must_use]
255 pub fn disable_introspection(mut self) -> Self {
256 for request in self.iter_mut() {
257 request.introspection_mode = IntrospectionMode::Disabled;
258 }
259 self
260 }
261
262 #[must_use]
264 pub fn introspection_only(mut self) -> Self {
265 for request in self.iter_mut() {
266 request.introspection_mode = IntrospectionMode::IntrospectionOnly;
267 }
268 self
269 }
270}
271
272fn deserialize_non_empty_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
273where
274 D: Deserializer<'de>,
275 T: Deserialize<'de>,
276{
277 use serde::de::Error as _;
278
279 let v = <Vec<T>>::deserialize(deserializer)?;
280 if v.is_empty() {
281 Err(D::Error::invalid_length(0, &"a non-empty sequence"))
282 } else {
283 Ok(v)
284 }
285}
286
287impl From<Request> for BatchRequest {
288 fn from(r: Request) -> Self {
289 BatchRequest::Single(r)
290 }
291}
292
293impl From<Vec<Request>> for BatchRequest {
294 fn from(r: Vec<Request>) -> Self {
295 BatchRequest::Batch(r)
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use crate::*;
302
303 #[test]
304 fn test_request() {
305 let request: Request = from_value(value! ({
306 "query": "{ a b c }"
307 }))
308 .unwrap();
309 assert!(request.variables.is_empty());
310 assert!(request.operation_name.is_none());
311 assert_eq!(request.query, "{ a b c }");
312 }
313
314 #[test]
315 fn test_request_with_operation_name() {
316 let request: Request = from_value(value! ({
317 "query": "{ a b c }",
318 "operationName": "a"
319 }))
320 .unwrap();
321 assert!(request.variables.is_empty());
322 assert_eq!(request.operation_name.as_deref(), Some("a"));
323 assert_eq!(request.query, "{ a b c }");
324 }
325
326 #[test]
327 fn test_request_with_variables() {
328 let request: Request = from_value(value! ({
329 "query": "{ a b c }",
330 "variables": {
331 "v1": 100,
332 "v2": [1, 2, 3],
333 "v3": "str",
334 }
335 }))
336 .unwrap();
337 assert_eq!(
338 request.variables.into_value(),
339 value!({
340 "v1": 100,
341 "v2": [1, 2, 3],
342 "v3": "str",
343 })
344 );
345 assert!(request.operation_name.is_none());
346 assert_eq!(request.query, "{ a b c }");
347 }
348
349 #[test]
350 fn test_deserialize_request_with_empty_object_variables() {
351 let request: Request = from_value(value! ({
352 "query": "{ a b c }",
353 "variables": {}
354 }))
355 .unwrap();
356 assert!(request.operation_name.is_none());
357 assert!(request.variables.is_empty());
358 }
359
360 #[test]
361 fn test_deserialize_request_with_empty_array_variables() {
362 let error: DeserializerError = from_value::<Request>(value! ({
363 "query": "{ a b c }",
364 "variables": []
365 }))
366 .unwrap_err();
367 assert_eq!(error.to_string(), "invalid type: sequence, expected a map");
368 }
369
370 #[test]
371 fn test_deserialize_request_with_null_variables() {
372 let request: Request = from_value(value! ({
373 "query": "{ a b c }",
374 "variables": null
375 }))
376 .unwrap();
377 assert!(request.operation_name.is_none());
378 assert!(request.variables.is_empty());
379 }
380
381 #[test]
382 fn test_batch_request_single() {
383 let request: BatchRequest = from_value(value! ({
384 "query": "{ a b c }"
385 }))
386 .unwrap();
387
388 if let BatchRequest::Single(request) = request {
389 assert!(request.variables.is_empty());
390 assert!(request.operation_name.is_none());
391 assert_eq!(request.query, "{ a b c }");
392 } else {
393 unreachable!()
394 }
395 }
396
397 #[test]
398 fn test_batch_request_batch() {
399 let request: BatchRequest = from_value(value!([
400 {
401 "query": "{ a b c }"
402 },
403 {
404 "query": "{ d e }"
405 }
406 ]))
407 .unwrap();
408
409 if let BatchRequest::Batch(requests) = request {
410 assert!(requests[0].variables.is_empty());
411 assert!(requests[0].operation_name.is_none());
412 assert_eq!(requests[0].query, "{ a b c }");
413
414 assert!(requests[1].variables.is_empty());
415 assert!(requests[1].operation_name.is_none());
416 assert_eq!(requests[1].query, "{ d e }");
417 } else {
418 unreachable!()
419 }
420 }
421}