1use crate::{CloudError as RestError, Result};
10use reqwest::Client;
11use serde::Serialize;
12use std::sync::Arc;
13
14#[derive(Debug, Clone)]
40pub struct CloudClientBuilder {
41 api_key: Option<String>,
42 api_secret: Option<String>,
43 base_url: String,
44 timeout: std::time::Duration,
45}
46
47impl Default for CloudClientBuilder {
48 fn default() -> Self {
49 Self {
50 api_key: None,
51 api_secret: None,
52 base_url: "https://api.redislabs.com/v1".to_string(),
53 timeout: std::time::Duration::from_secs(30),
54 }
55 }
56}
57
58impl CloudClientBuilder {
59 pub fn new() -> Self {
61 Self::default()
62 }
63
64 pub fn api_key(mut self, key: impl Into<String>) -> Self {
66 self.api_key = Some(key.into());
67 self
68 }
69
70 pub fn api_secret(mut self, secret: impl Into<String>) -> Self {
72 self.api_secret = Some(secret.into());
73 self
74 }
75
76 pub fn base_url(mut self, url: impl Into<String>) -> Self {
78 self.base_url = url.into();
79 self
80 }
81
82 pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
84 self.timeout = timeout;
85 self
86 }
87
88 pub fn build(self) -> Result<CloudClient> {
90 let api_key = self
91 .api_key
92 .ok_or_else(|| RestError::ConnectionError("API key is required".to_string()))?;
93 let api_secret = self
94 .api_secret
95 .ok_or_else(|| RestError::ConnectionError("API secret is required".to_string()))?;
96
97 let client = Client::builder()
98 .timeout(self.timeout)
99 .build()
100 .map_err(|e| RestError::ConnectionError(e.to_string()))?;
101
102 Ok(CloudClient {
103 api_key,
104 api_secret,
105 base_url: self.base_url,
106 timeout: self.timeout,
107 client: Arc::new(client),
108 })
109 }
110}
111
112#[derive(Clone)]
114pub struct CloudClient {
115 pub(crate) api_key: String,
116 pub(crate) api_secret: String,
117 pub(crate) base_url: String,
118 #[allow(dead_code)]
119 pub(crate) timeout: std::time::Duration,
120 pub(crate) client: Arc<Client>,
121}
122
123impl CloudClient {
124 pub fn builder() -> CloudClientBuilder {
126 CloudClientBuilder::new()
127 }
128
129 pub async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T> {
131 let url = format!("{}{}", self.base_url, path);
132
133 let response = self
135 .client
136 .get(&url)
137 .header("x-api-key", &self.api_key)
138 .header("x-api-secret-key", &self.api_secret)
139 .send()
140 .await?;
141
142 self.handle_response(response).await
143 }
144
145 pub async fn post<B: Serialize, T: serde::de::DeserializeOwned>(
147 &self,
148 path: &str,
149 body: &B,
150 ) -> Result<T> {
151 let url = format!("{}{}", self.base_url, path);
152
153 let response = self
155 .client
156 .post(&url)
157 .header("x-api-key", &self.api_key)
158 .header("x-api-secret-key", &self.api_secret)
159 .json(body)
160 .send()
161 .await?;
162
163 self.handle_response(response).await
164 }
165
166 pub async fn put<B: Serialize, T: serde::de::DeserializeOwned>(
168 &self,
169 path: &str,
170 body: &B,
171 ) -> Result<T> {
172 let url = format!("{}{}", self.base_url, path);
173
174 let response = self
176 .client
177 .put(&url)
178 .header("x-api-key", &self.api_key)
179 .header("x-api-secret-key", &self.api_secret)
180 .json(body)
181 .send()
182 .await?;
183
184 self.handle_response(response).await
185 }
186
187 pub async fn delete(&self, path: &str) -> Result<()> {
189 let url = format!("{}{}", self.base_url, path);
190
191 let response = self
193 .client
194 .delete(&url)
195 .header("x-api-key", &self.api_key)
196 .header("x-api-secret-key", &self.api_secret)
197 .send()
198 .await?;
199
200 if response.status().is_success() {
201 Ok(())
202 } else {
203 let status = response.status();
204 let text = response.text().await.unwrap_or_default();
205
206 match status.as_u16() {
207 400 => Err(RestError::BadRequest { message: text }),
208 401 => Err(RestError::AuthenticationFailed { message: text }),
209 403 => Err(RestError::Forbidden { message: text }),
210 404 => Err(RestError::NotFound { message: text }),
211 412 => Err(RestError::PreconditionFailed),
212 500 => Err(RestError::InternalServerError { message: text }),
213 503 => Err(RestError::ServiceUnavailable { message: text }),
214 _ => Err(RestError::ApiError {
215 code: status.as_u16(),
216 message: text,
217 }),
218 }
219 }
220 }
221
222 pub async fn get_raw(&self, path: &str) -> Result<serde_json::Value> {
224 self.get(path).await
225 }
226
227 pub async fn post_raw(&self, path: &str, body: serde_json::Value) -> Result<serde_json::Value> {
229 self.post(path, &body).await
230 }
231
232 pub async fn put_raw(&self, path: &str, body: serde_json::Value) -> Result<serde_json::Value> {
234 self.put(path, &body).await
235 }
236
237 pub async fn patch_raw(
239 &self,
240 path: &str,
241 body: serde_json::Value,
242 ) -> Result<serde_json::Value> {
243 let url = format!("{}{}", self.base_url, path);
244
245 let response = self
247 .client
248 .patch(&url)
249 .header("x-api-key", &self.api_key)
250 .header("x-api-secret-key", &self.api_secret)
251 .json(&body)
252 .send()
253 .await?;
254
255 self.handle_response(response).await
256 }
257
258 pub async fn delete_raw(&self, path: &str) -> Result<serde_json::Value> {
260 let url = format!("{}{}", self.base_url, path);
261
262 let response = self
264 .client
265 .delete(&url)
266 .header("x-api-key", &self.api_key)
267 .header("x-api-secret-key", &self.api_secret)
268 .send()
269 .await?;
270
271 if response.status().is_success() {
272 if response.content_length() == Some(0) {
273 Ok(serde_json::json!({"status": "deleted"}))
274 } else {
275 response.json().await.map_err(Into::into)
276 }
277 } else {
278 let status = response.status();
279 let text = response.text().await.unwrap_or_default();
280
281 match status.as_u16() {
282 400 => Err(RestError::BadRequest { message: text }),
283 401 => Err(RestError::AuthenticationFailed { message: text }),
284 403 => Err(RestError::Forbidden { message: text }),
285 404 => Err(RestError::NotFound { message: text }),
286 412 => Err(RestError::PreconditionFailed),
287 500 => Err(RestError::InternalServerError { message: text }),
288 503 => Err(RestError::ServiceUnavailable { message: text }),
289 _ => Err(RestError::ApiError {
290 code: status.as_u16(),
291 message: text,
292 }),
293 }
294 }
295 }
296
297 async fn handle_response<T: serde::de::DeserializeOwned>(
299 &self,
300 response: reqwest::Response,
301 ) -> Result<T> {
302 let status = response.status();
303
304 if status.is_success() {
305 let text = response.text().await.map_err(|e| {
307 RestError::ConnectionError(format!("Failed to read response: {}", e))
308 })?;
309
310 serde_json::from_str::<T>(&text).map_err(|e| {
312 RestError::JsonError(e)
314 })
315 } else {
316 let text = response.text().await.unwrap_or_default();
317
318 match status.as_u16() {
319 400 => Err(RestError::BadRequest { message: text }),
320 401 => Err(RestError::AuthenticationFailed { message: text }),
321 403 => Err(RestError::Forbidden { message: text }),
322 404 => Err(RestError::NotFound { message: text }),
323 412 => Err(RestError::PreconditionFailed),
324 500 => Err(RestError::InternalServerError { message: text }),
325 503 => Err(RestError::ServiceUnavailable { message: text }),
326 _ => Err(RestError::ApiError {
327 code: status.as_u16(),
328 message: text,
329 }),
330 }
331 }
332 }
333}