1use std::sync::Arc;
19
20use reqwest::{Method, RequestBuilder, StatusCode, Url};
21use serde::Deserialize;
22use serde::de::DeserializeOwned;
23
24use crate::error::{ApiError, Error};
25
26pub struct Client {
28 pub(crate) inner: reqwest::Client,
29 pub(crate) auth_client: Arc<mz_frontegg_client::client::Client>,
30 pub(crate) endpoint: Url,
31}
32
33pub mod cloud_provider;
34pub mod region;
35
36impl Client {
37 async fn build_global_request<P>(
39 &self,
40 method: Method,
41 path: P,
42 query: Option<&[(&str, &str)]>,
43 ) -> Result<RequestBuilder, Error>
44 where
45 P: IntoIterator,
46 P::Item: AsRef<str>,
47 {
48 let mut domain = self.endpoint.clone();
49 domain
50 .path_segments_mut()
51 .or(Err(Error::UrlBaseError))?
52 .clear()
53 .extend(path);
54
55 let mut req = self.inner.request(method, domain);
56 if let Some(query_params) = query {
57 req = req.query(&query_params);
58 }
59 let token = self.auth_client.auth().await?;
60
61 Ok(req.bearer_auth(token))
62 }
63
64 async fn send_request<T>(&self, req: RequestBuilder) -> Result<T, Error>
65 where
66 T: DeserializeOwned,
67 {
68 #[derive(Deserialize)]
69 #[serde(rename_all = "camelCase")]
70 struct ErrorResponse {
71 #[serde(default)]
72 message: Option<String>,
73 #[serde(default)]
74 errors: Vec<String>,
75 }
76
77 let res = req.send().await?;
78 let status_code = res.status();
79 if status_code.is_success() {
80 if status_code == StatusCode::NO_CONTENT {
81 Err(Error::SuccesfullButNoContent)
82 } else {
83 Ok(res.json().await?)
84 }
85 } else {
86 match res.json::<ErrorResponse>().await {
87 Ok(e) => {
88 let mut messages = e.errors;
89 messages.extend(e.message);
90 Err(Error::Api(ApiError {
91 status_code,
92 messages,
93 }))
94 }
95 Err(_) => Err(Error::Api(ApiError {
96 status_code,
97 messages: vec!["unable to decode error details".into()],
98 })),
99 }
100 }
101 }
102}