siera_cloudagent_python/
web.rs

1use crate::agent::CloudAgentPython;
2use reqwest::{Client, RequestBuilder, Url};
3use serde::de::DeserializeOwned;
4use serde_json::Value;
5use siera_agent::error::{Error, Result};
6use std::fmt::Debug;
7
8/// Call logic for http calls
9impl CloudAgentPython {
10    /// Builds a get request and calls the sender
11    ///
12    /// # Errors
13    ///
14    /// When it could not fulfill a GET request
15    pub async fn get<T: DeserializeOwned + Debug>(
16        &self,
17        url: Url,
18        query: Option<Vec<(&str, String)>>,
19    ) -> Result<T> {
20        let client = match &query {
21            Some(q) => Client::new().get(url).query(&q),
22            None => Client::new().get(url),
23        };
24
25        trace!({ "message": "Get request query", "query": query });
26
27        self.send::<T>(client).await
28    }
29
30    /// Builds a patch request and calls the sender
31    ///
32    /// # Errors
33    ///
34    /// When it could not fulfill a PATCH request
35    pub async fn patch<T: DeserializeOwned + Debug>(
36        &self,
37        url: Url,
38        query: Option<Vec<(&str, String)>>,
39    ) -> Result<T> {
40        let client = match &query {
41            Some(q) => Client::new().patch(url).query(&q),
42            None => Client::new().patch(url),
43        };
44
45        trace!({ "message": "Patch request query", "query": query});
46
47        self.send::<T>(client).await
48    }
49
50    /// Builds a post request and calls the sender
51    ///
52    /// # Errors
53    ///
54    /// When it could not fulfill a POST request
55    pub async fn post<T: DeserializeOwned + Debug>(
56        &self,
57        url: Url,
58        query: Option<Vec<(&str, String)>>,
59        body: Option<Value>,
60    ) -> Result<T> {
61        let client = Client::new().post(url).query(&query);
62
63        let client = match &body {
64            Some(b) => client.json(&b),
65            None => client,
66        };
67
68        trace!({ "message": "Post request body", "body": body, "query": query });
69
70        self.send::<T>(client).await
71    }
72
73    /// Sends any request
74    ///
75    /// # Errors
76    ///
77    /// When it could not fulfill the given request
78    pub async fn send<T: DeserializeOwned + Debug>(&self, client: RequestBuilder) -> Result<T> {
79        let client = match &self.api_key {
80            Some(a) => client.header("X-API-KEY", a),
81            None => client,
82        };
83
84        let client = match &self.auth_token {
85            Some(token) => client.header("Authorization", format!("Bearer {token}")),
86            None => client,
87        };
88
89        trace!({ "message": "About to send request" });
90        match client.send().await {
91            Ok(response) => {
92                let status_code = response.status().as_u16();
93                debug!({ "status_code": status_code });
94                match status_code {
95                    200..=299 => {
96                        let parsed: Value = response.json().await?;
97                        debug!({ "response": parsed });
98                        let parsed = if parsed.is_null() {
99                            serde_json::json!(())
100                        } else {
101                            parsed
102                        };
103                        serde_json::from_value(parsed).map_err(|e| {
104                            warn!({"error": e.to_string() });
105                            Error::UnableToParseResponse.into()
106                        })
107                    }
108                    // Issue credential message when attributes are not correct
109                    400 => Err(response.text().await?.into()),
110                    401 => Err(Error::AuthorizationFailed.into()),
111                    404 => Err(Error::UrlDoesNotExist.into()),
112                    422 => Err(response.text().await?.into()),
113                    503 => Err(Error::HttpServiceUnavailable.into()),
114                    500..=599 => Err(Error::InternalServerError(
115                        response.status().as_u16(),
116                        response.text().await?,
117                    )
118                    .into()),
119                    _ => Err(Error::UnknownResponseStatusCode(response.text().await?).into()),
120                }
121            }
122            Err(e) => {
123                warn!({ "message": "request failed", "error": e.to_string() });
124                Err(Error::UnreachableUrl.into())
125            }
126        }
127    }
128}