cscart_rs/request/
mod.rs

1use serde_json::Value;
2
3#[derive(Debug, Clone)]
4pub struct Auth {
5    username: String,
6    api_key: String,
7}
8
9impl Auth {
10    pub fn username(&self) -> &str {
11        &self.username
12    }
13
14    pub fn api_key(&self) -> &str {
15        &self.api_key
16    }
17
18    pub fn from<I>(username: I, api_key: I) -> Self
19    where
20        I: Into<String>,
21    {
22        Auth {
23            username: username.into(),
24            api_key: api_key.into(),
25        }
26    }
27}
28
29pub trait HttpClient {
30    async fn get(url: String, auth: Auth, params: &[(String, String)]) -> anyhow::Result<String>;
31    async fn put(url: String, auth: Auth, body: Value) -> anyhow::Result<String>;
32    async fn post(url: String, auth: Auth, body: Value) -> anyhow::Result<String>;
33    async fn delete(url: String, auth: Auth, body: Value) -> anyhow::Result<String>;
34}
35
36#[derive(Debug)]
37pub struct Request {
38    host: String,
39    path: String,
40    username: String,
41    api_key: String,
42    query_params: Vec<(String, String)>,
43}
44
45#[derive(Debug)]
46pub struct RequestBuilder {
47    host: String,
48    path: String,
49    username: String,
50    api_key: String,
51    query_params: Vec<(String, String)>,
52}
53
54impl RequestBuilder {
55    pub fn host(mut self, host: &str) -> Self {
56        self.host = host.to_string();
57        self
58    }
59
60    pub fn path(mut self, path: &str) -> Self {
61        self.path = path.to_string();
62        self
63    }
64
65    pub fn username(mut self, username: &str) -> Self {
66        self.username = username.to_string();
67        self
68    }
69
70    pub fn api_key(mut self, api_key: &str) -> Self {
71        self.api_key = api_key.to_string();
72        self
73    }
74
75    pub fn params(mut self, params: &[(String, String)]) -> Self {
76        for param in params {
77            self.query_params.push(param.clone())
78        }
79        self
80    }
81
82    pub fn build(self) -> Request {
83        Request {
84            host: self.host,
85            path: self.path,
86            username: self.username,
87            api_key: self.api_key,
88            query_params: self.query_params,
89        }
90    }
91}
92
93#[allow(clippy::new_ret_no_self)]
94impl Request {
95    pub fn new() -> RequestBuilder {
96        RequestBuilder {
97            host: "".to_string(),
98            path: "".to_string(),
99            username: "".to_string(),
100            query_params: vec![],
101            api_key: "".to_string(),
102        }
103    }
104
105    pub async fn get(&self) -> anyhow::Result<String> {
106        let client = reqwest::Client::new();
107        let endpoint = format!("{}{}", &self.host, &self.path);
108        let url = reqwest::Url::parse_with_params(&endpoint, &self.query_params)?;
109        let data = client
110            .get(url)
111            .basic_auth(self.username.as_str(), Some(self.api_key.as_str()))
112            .query(&self.query_params)
113            .send()
114            .await?
115            .text()
116            .await?;
117
118        Ok(data)
119    }
120
121    pub async fn put(&self, body: Value) -> anyhow::Result<String> {
122        let client = reqwest::Client::new();
123        let mut endpoint: String = String::from(&self.host);
124
125        endpoint.push_str(&self.path);
126
127        let data = client
128            .put(endpoint)
129            .basic_auth(self.username.as_str(), Some(self.api_key.as_str()))
130            .query(&self.query_params)
131            .json(&body)
132            .send()
133            .await?
134            .text()
135            .await?;
136
137        Ok(data)
138    }
139
140    pub async fn post(&self, body: Value) -> anyhow::Result<String> {
141        let client = reqwest::Client::new();
142        let mut endpoint: String = String::from(&self.host);
143
144        endpoint.push_str(&self.path);
145
146        let data = client
147            .post(endpoint)
148            .basic_auth(self.username.as_str(), Some(self.api_key.as_str()))
149            .query(&self.query_params)
150            .json(&body)
151            .send()
152            .await?
153            .text()
154            .await?;
155        Ok(data)
156    }
157
158    pub async fn delete(&self) -> anyhow::Result<String> {
159        let client = reqwest::Client::new();
160        let mut endpoint: String = String::from(&self.host);
161
162        endpoint.push_str(&self.path);
163
164        let data = client
165            .delete(endpoint)
166            .basic_auth(self.username.as_str(), Some(self.api_key.as_str()))
167            .query(&self.query_params)
168            .send()
169            .await?
170            .text()
171            .await?;
172
173        Ok(data)
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use dotenv::dotenv;
181    use serde_json::json;
182
183    fn setup() -> Request {
184        dotenv().ok();
185        let api_key = std::env::var("CSCART_API_KEY").expect("No api key found");
186
187        let username = std::env::var("CSCART_USERNAME").expect("No username found");
188
189        let host = std::env::var("CSCART_HOST").expect("No host found");
190
191        Request::new()
192            .host(host.as_str())
193            .path("/api/2.0/categories")
194            .username(username.as_str())
195            .api_key(api_key.as_str())
196            .build()
197    }
198
199    #[tokio::test]
200    async fn it_makes_a_get_request() {
201        let client = setup();
202        let response = client.get().await;
203
204        match response {
205            Ok(_) => assert!(true),
206            Err(_) => assert!(false),
207        }
208    }
209
210    #[tokio::test]
211    async fn it_makes_a_put_request() {
212        let client = setup();
213        let body = json!({"data" : "testing"});
214        let response = client.put(body).await;
215
216        match response {
217            Ok(_) => assert!(true),
218            Err(_) => assert!(false),
219        }
220    }
221
222    #[tokio::test]
223    async fn it_makes_a_post_request() {
224        let client = setup();
225        let body = json!({"data" : "testing"});
226        let response = client.post(body).await;
227
228        match response {
229            Ok(_) => assert!(true),
230            Err(_) => assert!(false),
231        }
232    }
233
234    #[tokio::test]
235    async fn it_makes_a_delete_request() {
236        let client = setup();
237        let response = client.delete().await;
238
239        match response {
240            Ok(_) => assert!(true),
241            Err(_) => assert!(false),
242        }
243    }
244}