service/http_server/api/client/
client.rs

1use reqwest::{header::HeaderMap, header::HeaderValue, Client};
2use url::Url;
3use uuid::Uuid;
4
5use super::error::ApiError;
6use super::ApiRequest;
7use crate::http_server::api::v0::bucket::list::{ListRequest, ListResponse};
8
9#[derive(Debug, Clone)]
10pub struct ApiClient {
11    pub remote: Url,
12    client: Client,
13}
14
15impl ApiClient {
16    pub fn new(remote: &Url) -> Result<Self, ApiError> {
17        let mut default_headers = HeaderMap::new();
18        default_headers.insert("Content-Type", HeaderValue::from_static("application/json"));
19        let client = Client::builder().default_headers(default_headers).build()?;
20
21        Ok(Self {
22            remote: remote.clone(),
23            client,
24        })
25    }
26
27    pub async fn call<T: ApiRequest>(&mut self, request: T) -> Result<T::Response, ApiError> {
28        let request_builder = request.build_request(&self.remote, &self.client);
29        let response = request_builder.send().await?;
30
31        if response.status().is_success() {
32            Ok(response.json::<T::Response>().await?)
33        } else {
34            Err(ApiError::HttpStatus(
35                response.status(),
36                response.text().await?,
37            ))
38        }
39    }
40
41    /// Resolve a bucket name to a UUID
42    /// Returns the first bucket with an exact name match
43    pub async fn resolve_bucket_name(&mut self, name: &str) -> Result<Uuid, ApiError> {
44        let request = ListRequest {
45            prefix: Some(name.to_string()),
46            limit: Some(100),
47        };
48
49        let response: ListResponse = self.call(request).await?;
50
51        response
52            .buckets
53            .into_iter()
54            .find(|b| b.name == name)
55            .map(|b| b.bucket_id)
56            .ok_or_else(|| {
57                ApiError::HttpStatus(
58                    reqwest::StatusCode::NOT_FOUND,
59                    format!("Bucket not found: {}", name),
60                )
61            })
62    }
63
64    /// Get the base URL for API requests
65    pub fn base_url(&self) -> &Url {
66        &self.remote
67    }
68
69    /// Get the underlying HTTP client for custom requests
70    pub fn http_client(&self) -> &Client {
71        &self.client
72    }
73}