opensession_api_client/
client.rs1use std::time::Duration;
2
3use anyhow::{bail, Result};
4use serde::Serialize;
5
6use opensession_api::*;
7
8pub struct ApiClient {
14 client: reqwest::Client,
15 base_url: String,
16 auth_token: Option<String>,
17}
18
19impl ApiClient {
20 pub fn new(base_url: &str, timeout: Duration) -> Result<Self> {
22 let client = reqwest::Client::builder().timeout(timeout).build()?;
23 Ok(Self {
24 client,
25 base_url: base_url.trim_end_matches('/').to_string(),
26 auth_token: None,
27 })
28 }
29
30 pub fn with_client(client: reqwest::Client, base_url: &str) -> Self {
32 Self {
33 client,
34 base_url: base_url.trim_end_matches('/').to_string(),
35 auth_token: None,
36 }
37 }
38
39 pub fn set_auth(&mut self, token: String) {
40 self.auth_token = Some(token);
41 }
42
43 pub fn auth_token(&self) -> Option<&str> {
44 self.auth_token.as_deref()
45 }
46
47 pub fn base_url(&self) -> &str {
48 &self.base_url
49 }
50
51 pub fn reqwest_client(&self) -> &reqwest::Client {
53 &self.client
54 }
55
56 fn url(&self, path: &str) -> String {
57 format!("{}/api{}", self.base_url, path)
58 }
59
60 fn token_or_bail(&self) -> Result<&str> {
61 self.auth_token
62 .as_deref()
63 .ok_or_else(|| anyhow::anyhow!("auth token not set"))
64 }
65
66 pub async fn health(&self) -> Result<HealthResponse> {
69 let resp = self.client.get(self.url("/health")).send().await?;
70 parse_response(resp).await
71 }
72
73 pub async fn login(&self, req: &LoginRequest) -> Result<AuthTokenResponse> {
76 let resp = self
77 .client
78 .post(self.url("/auth/login"))
79 .json(req)
80 .send()
81 .await?;
82 parse_response(resp).await
83 }
84
85 pub async fn register(&self, req: &AuthRegisterRequest) -> Result<AuthTokenResponse> {
86 let resp = self
87 .client
88 .post(self.url("/auth/register"))
89 .json(req)
90 .send()
91 .await?;
92 parse_response(resp).await
93 }
94
95 pub async fn verify(&self) -> Result<VerifyResponse> {
96 let token = self.token_or_bail()?;
97 let resp = self
98 .client
99 .post(self.url("/auth/verify"))
100 .bearer_auth(token)
101 .send()
102 .await?;
103 parse_response(resp).await
104 }
105
106 pub async fn me(&self) -> Result<UserSettingsResponse> {
107 let token = self.token_or_bail()?;
108 let resp = self
109 .client
110 .get(self.url("/auth/me"))
111 .bearer_auth(token)
112 .send()
113 .await?;
114 parse_response(resp).await
115 }
116
117 pub async fn refresh(&self, req: &RefreshRequest) -> Result<AuthTokenResponse> {
118 let resp = self
119 .client
120 .post(self.url("/auth/refresh"))
121 .json(req)
122 .send()
123 .await?;
124 parse_response(resp).await
125 }
126
127 pub async fn logout(&self, req: &LogoutRequest) -> Result<OkResponse> {
128 let token = self.token_or_bail()?;
129 let resp = self
130 .client
131 .post(self.url("/auth/logout"))
132 .bearer_auth(token)
133 .json(req)
134 .send()
135 .await?;
136 parse_response(resp).await
137 }
138
139 pub async fn change_password(&self, req: &ChangePasswordRequest) -> Result<OkResponse> {
140 let token = self.token_or_bail()?;
141 let resp = self
142 .client
143 .post(self.url("/auth/change-password"))
144 .bearer_auth(token)
145 .json(req)
146 .send()
147 .await?;
148 parse_response(resp).await
149 }
150
151 pub async fn regenerate_key(&self) -> Result<RegenerateKeyResponse> {
152 let token = self.token_or_bail()?;
153 let resp = self
154 .client
155 .post(self.url("/auth/regenerate-key"))
156 .bearer_auth(token)
157 .send()
158 .await?;
159 parse_response(resp).await
160 }
161
162 pub async fn upload_session(&self, req: &UploadRequest) -> Result<UploadResponse> {
165 let token = self.token_or_bail()?;
166 let resp = self
167 .client
168 .post(self.url("/sessions"))
169 .bearer_auth(token)
170 .json(req)
171 .send()
172 .await?;
173 parse_response(resp).await
174 }
175
176 pub async fn list_sessions(&self, query: &SessionListQuery) -> Result<SessionListResponse> {
177 let token = self.token_or_bail()?;
178 let mut url = self.url("/sessions");
179
180 let mut params = Vec::new();
182 params.push(format!("page={}", query.page));
183 params.push(format!("per_page={}", query.per_page));
184 if let Some(ref s) = query.search {
185 params.push(format!("search={s}"));
186 }
187 if let Some(ref t) = query.tool {
188 params.push(format!("tool={t}"));
189 }
190 if let Some(ref s) = query.sort {
191 params.push(format!("sort={s}"));
192 }
193 if let Some(ref r) = query.time_range {
194 params.push(format!("time_range={r}"));
195 }
196 if !params.is_empty() {
197 url = format!("{}?{}", url, params.join("&"));
198 }
199
200 let resp = self.client.get(&url).bearer_auth(token).send().await?;
201 parse_response(resp).await
202 }
203
204 pub async fn get_session(&self, id: &str) -> Result<SessionDetail> {
205 let token = self.token_or_bail()?;
206 let resp = self
207 .client
208 .get(self.url(&format!("/sessions/{id}")))
209 .bearer_auth(token)
210 .send()
211 .await?;
212 parse_response(resp).await
213 }
214
215 pub async fn delete_session(&self, id: &str) -> Result<OkResponse> {
216 let token = self.token_or_bail()?;
217 let resp = self
218 .client
219 .delete(self.url(&format!("/sessions/{id}")))
220 .bearer_auth(token)
221 .send()
222 .await?;
223 parse_response(resp).await
224 }
225
226 pub async fn get_session_raw(&self, id: &str) -> Result<serde_json::Value> {
227 let token = self.token_or_bail()?;
228 let resp = self
229 .client
230 .get(self.url(&format!("/sessions/{id}/raw")))
231 .bearer_auth(token)
232 .send()
233 .await?;
234 parse_response(resp).await
235 }
236
237 pub async fn get_with_auth(&self, path: &str, token: &str) -> Result<reqwest::Response> {
241 Ok(self
242 .client
243 .get(self.url(path))
244 .bearer_auth(token)
245 .send()
246 .await?)
247 }
248
249 pub async fn post_with_auth(&self, path: &str, token: &str) -> Result<reqwest::Response> {
251 Ok(self
252 .client
253 .post(self.url(path))
254 .bearer_auth(token)
255 .send()
256 .await?)
257 }
258
259 pub async fn post_json_with_auth<T: Serialize>(
261 &self,
262 path: &str,
263 token: &str,
264 body: &T,
265 ) -> Result<reqwest::Response> {
266 Ok(self
267 .client
268 .post(self.url(path))
269 .bearer_auth(token)
270 .json(body)
271 .send()
272 .await?)
273 }
274
275 pub async fn put_json_with_auth<T: Serialize>(
277 &self,
278 path: &str,
279 token: &str,
280 body: &T,
281 ) -> Result<reqwest::Response> {
282 Ok(self
283 .client
284 .put(self.url(path))
285 .bearer_auth(token)
286 .json(body)
287 .send()
288 .await?)
289 }
290
291 pub async fn delete_with_auth(&self, path: &str, token: &str) -> Result<reqwest::Response> {
293 Ok(self
294 .client
295 .delete(self.url(path))
296 .bearer_auth(token)
297 .send()
298 .await?)
299 }
300
301 pub async fn post_json_raw<T: Serialize>(
303 &self,
304 path: &str,
305 body: &T,
306 ) -> Result<reqwest::Response> {
307 Ok(self.client.post(self.url(path)).json(body).send().await?)
308 }
309}
310
311async fn parse_response<T: serde::de::DeserializeOwned>(resp: reqwest::Response) -> Result<T> {
314 let status = resp.status();
315 if !status.is_success() {
316 let body = resp.text().await.unwrap_or_default();
317 bail!("{status}: {body}");
318 }
319 Ok(resp.json().await?)
320}