server_watchdog/infrastructure/common/
api_client.rs1use reqwest::Client;
2use serde::de::DeserializeOwned;
3use serde::Serialize;
4
5pub struct ApiClient {
6 base_url: String,
7 http: Client,
8 token: Option<String>,
9}
10
11impl ApiClient {
12 pub fn new(base_url: impl Into<String>) -> Self {
13 Self {
14 base_url: base_url.into(),
15 http: Client::new(),
16 token: None,
17 }
18 }
19
20 pub fn with_token(mut self, token: String) -> Self {
21 self.token = Some(token);
22 self
23 }
24
25 fn build_url(&self,
26 path: &str,
27 params: Option<&[(&str, &str)]>
28 ) -> String {
29 let mut url = format!(
30 "{}/{}",
31 self.base_url.trim_end_matches('/'),
32 path.trim_start_matches('/')
33 );
34
35 if let Some(params) = params {
36 if !params.is_empty() {
37 url.push('?');
38 let query = params.iter()
39 .map(|(k, v)| format!("{}={}", k, v))
40 .collect::<Vec<_>>()
41 .join("&");
42 url.push_str(&query);
43 }
44 }
45
46 url
47 }
48
49 fn apply_headers<'a>(
50 &'a self,
51 mut req: reqwest::RequestBuilder,
52 extra_headers: Option<&[(&str, &str)]>
53 ) -> reqwest::RequestBuilder {
54 if let Some(token) = &self.token {
55 req = req.bearer_auth(token);
56 }
57
58 if let Some(headers) = extra_headers {
59 for (k, v) in headers {
60 req = req.header(*k, *v);
61 }
62 }
63
64 req
65 }
66
67 pub async fn get_json<TResp>(
71 &self,
72 path: &str,
73 extra_headers: Option<&[(&str, &str)]>,
74 params: Option<&[(&str, &str)]>,
75 ) -> Result<TResp, Box<dyn std::error::Error + Send + Sync>>
76 where
77 TResp: DeserializeOwned,
78 {
79 let url = self.build_url(path, params);
80 let req = self.http.get(&url);
81 let req = self.apply_headers(req, extra_headers);
82
83 let resp = req.send().await?.error_for_status()?;
84 Ok(resp.json::<TResp>().await?)
85 }
86
87 pub async fn post_json<TBody, TResp>(
91 &self,
92 path: &str,
93 body: &TBody,
94 extra_headers: Option<&[(&str, &str)]>,
95 params: Option<&[(&str, &str)]>,
96 ) -> Result<TResp, Box<dyn std::error::Error + Send + Sync>>
97 where
98 TBody: Serialize + ?Sized,
99 TResp: DeserializeOwned,
100 {
101 let url = self.build_url(path, params);
102 let req = self.http.post(&url).json(body);
103 let req = self.apply_headers(req, extra_headers);
104
105 let resp = req.send().await?.error_for_status()?;
106 Ok(resp.json::<TResp>().await?)
107 }
108
109 pub async fn post_form<TForm, TResp>(
113 &self,
114 path: &str,
115 form: &TForm,
116 extra_headers: Option<&[(&str, &str)]>,
117 params: Option<&[(&str, &str)]>,
118 ) -> Result<TResp, Box<dyn std::error::Error + Send + Sync>>
119 where
120 TForm: Serialize + ?Sized,
121 TResp: DeserializeOwned,
122 {
123 let url = self.build_url(path, params);
124 let req = self.http.post(&url).form(form);
125 let req = self.apply_headers(req, extra_headers);
126
127 let resp = req.send().await?.error_for_status()?;
128 Ok(resp.json::<TResp>().await?)
129 }
130
131 pub async fn put_json<TBody, TResp>(
135 &self,
136 path: &str,
137 body: &TBody,
138 extra_headers: Option<&[(&str, &str)]>,
139 params: Option<&[(&str, &str)]>,
140 ) -> Result<TResp, Box<dyn std::error::Error + Send + Sync>>
141 where
142 TBody: Serialize + ?Sized,
143 TResp: DeserializeOwned,
144 {
145 let url = self.build_url(path, params);
146 let req = self.http.put(&url).json(body);
147 let req = self.apply_headers(req, extra_headers);
148
149 let resp = req.send().await?.error_for_status()?;
150 Ok(resp.json::<TResp>().await?)
151 }
152
153 pub async fn put_form<TForm, TResp>(
157 &self,
158 path: &str,
159 form: &TForm,
160 extra_headers: Option<&[(&str, &str)]>,
161 params: Option<&[(&str, &str)]>,
162 ) -> Result<TResp, Box<dyn std::error::Error + Send + Sync>>
163 where
164 TForm: Serialize + ?Sized,
165 TResp: DeserializeOwned,
166 {
167 let url = self.build_url(path, params);
168 let req = self.http.put(&url).form(form);
169 let req = self.apply_headers(req, extra_headers);
170
171 let resp = req.send().await?.error_for_status()?;
172 Ok(resp.json::<TResp>().await?)
173 }
174
175 pub async fn delete_json<TResp>(
179 &self,
180 path: &str,
181 extra_headers: Option<&[(&str, &str)]>,
182 params: Option<&[(&str, &str)]>,
183 ) -> Result<TResp, Box<dyn std::error::Error + Send + Sync>>
184 where
185 TResp: DeserializeOwned,
186 {
187 let url = self.build_url(path, params);
188 let req = self.http.delete(&url);
189 let req = self.apply_headers(req, extra_headers);
190
191 let resp = req.send().await?.error_for_status()?;
192 Ok(resp.json::<TResp>().await?)
193 }
194}