rusty_gql/
request.rs

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}