1use std::collections::HashMap;
2
3use futures_util::{pin_mut, AsyncRead, AsyncReadExt};
4use serde::{Deserialize, Serialize};
5
6use crate::{variables::Variables, GqlValue};
7
8#[derive(Serialize, Deserialize, Debug)]
9#[serde(rename_all = "camelCase")]
10pub struct Request {
11 #[serde(default)]
12 pub query: String,
13 #[serde(default)]
14 pub operation_name: Option<String>,
15 #[serde(default)]
16 pub variables: Variables,
17 #[serde(default)]
18 pub extensions: HashMap<String, GqlValue>,
19}
20
21pub async fn receive_http_request(
22 body: impl AsyncRead + Send,
23) -> Result<Request, HttpRequestError> {
24 receive_json_body(body).await
25}
26
27pub async fn receive_json_body(body: impl AsyncRead) -> Result<Request, HttpRequestError> {
28 let mut data = Vec::new();
29 pin_mut!(body);
30
31 body.read_to_end(&mut data)
32 .await
33 .map_err(HttpRequestError::Io)?;
34 Ok(serde_json::from_slice::<Request>(&data)
35 .map_err(|err| HttpRequestError::InvalidRequest(Box::new(err)))?)
36}
37
38#[derive(Debug)]
39pub enum HttpRequestError {
40 Io(std::io::Error),
41 InvalidRequest(Box<dyn std::error::Error + Send + Sync>),
42}
43
44#[cfg(test)]
45mod tests {
46 use serde_json::Number;
47
48 use crate::{GqlValue, Request};
49
50 #[test]
51 fn test_operation_name() {
52 let query_doc = r#"{"query": "{ hero droids jedi }", "operationName": "hero"}"#;
53 let req = serde_json::from_str::<Request>(query_doc).unwrap();
54 assert_eq!(req.query, "{ hero droids jedi }");
55 assert_eq!(req.operation_name, Some("hero".to_string()));
56 assert!(req.variables.0.is_empty());
57 }
58
59 #[test]
60 fn test_variables() {
61 let query_doc = r#"{"query": "{ hero droids jedi }", "variables": {"var1": 100, "var2": "value", "var3": [1,1,1]}}"#;
62 let req = serde_json::from_str::<Request>(query_doc).unwrap();
63 assert_eq!(req.query, "{ hero droids jedi }");
64 assert!(req.operation_name.is_none());
65
66 assert_eq!(
67 req.variables.0.get("var1"),
68 Some(&GqlValue::Number(Number::from(100 as i32)))
69 );
70 assert_eq!(
71 req.variables.0.get("var2"),
72 Some(&GqlValue::String(String::from("value")))
73 );
74 assert_eq!(
75 req.variables.0.get("var3"),
76 Some(&GqlValue::List(vec![
77 GqlValue::Number(Number::from(1 as i32)),
78 GqlValue::Number(Number::from(1 as i32)),
79 GqlValue::Number(Number::from(1 as i32))
80 ]))
81 );
82 }
83
84 #[test]
85 fn test_null_variables() {
86 let query_doc = r#"{"query": "{ hero droids jedi }", "variables": null}"#;
87 let req = serde_json::from_str::<Request>(query_doc).unwrap();
88 assert_eq!(req.query, "{ hero droids jedi }");
89 assert!(req.operation_name.is_none());
90 assert!(req.variables.0.is_empty());
91 }
92}