rs_firebase_admin_sdk/client/
mod.rs

1//! HTTP(S) client traits for hanling API calls
2
3pub mod error;
4pub mod url_params;
5
6use crate::credentials::get_headers;
7use bytes::Bytes;
8use error::{ApiClientError, FireBaseAPIErrorResponse};
9use error_stack::{Report, ResultExt};
10use google_cloud_auth::credentials::Credentials;
11use http::Method;
12use serde::{Serialize, de::DeserializeOwned};
13use std::future::Future;
14use std::iter::Iterator;
15use url_params::UrlParams;
16
17pub trait ApiHttpClient: Send + Sync + 'static {
18    fn send_request<ResponseT: Send + DeserializeOwned>(
19        &self,
20        uri: String,
21        method: Method,
22    ) -> impl Future<Output = Result<ResponseT, Report<ApiClientError>>> + Send;
23
24    fn send_request_with_params<
25        ResponseT: DeserializeOwned + Send,
26        ParamsT: Iterator<Item = (String, String)> + Send,
27    >(
28        &self,
29        uri: String,
30        params: ParamsT,
31        method: Method,
32    ) -> impl Future<Output = Result<ResponseT, Report<ApiClientError>>> + Send;
33
34    fn send_request_body<RequestT: Serialize + Send, ResponseT: DeserializeOwned + Send>(
35        &self,
36        uri: String,
37        method: Method,
38        request_body: RequestT,
39    ) -> impl Future<Output = Result<ResponseT, Report<ApiClientError>>> + Send;
40
41    fn send_request_body_get_bytes<RequestT: Serialize + Send>(
42        &self,
43        uri: String,
44        method: Method,
45        request_body: RequestT,
46    ) -> impl Future<Output = Result<Bytes, Report<ApiClientError>>> + Send;
47
48    fn send_request_body_empty_response<RequestT: Serialize + Send>(
49        &self,
50        uri: String,
51        method: Method,
52        request_body: RequestT,
53    ) -> impl Future<Output = Result<(), Report<ApiClientError>>> + Send;
54}
55
56trait SetReqBody<T: Serialize> {
57    fn set_request_body(self, body: Option<T>) -> Self;
58}
59
60impl<T: Serialize> SetReqBody<T> for reqwest::RequestBuilder {
61    fn set_request_body(self, body: Option<T>) -> Self {
62        if let Some(body) = body {
63            return self.json(&body);
64        }
65
66        self
67    }
68}
69
70pub struct ReqwestApiClient {
71    client: reqwest::Client,
72    credentials: Credentials,
73}
74
75impl ReqwestApiClient {
76    pub fn new(client: reqwest::Client, credentials: Credentials) -> Self {
77        Self {
78            client,
79            credentials,
80        }
81    }
82
83    async fn handle_response(
84        resp: reqwest::Response,
85    ) -> Result<reqwest::Response, Report<ApiClientError>> {
86        if resp.status() != reqwest::StatusCode::OK {
87            let error_response: FireBaseAPIErrorResponse = resp
88                .json()
89                .await
90                .change_context(ApiClientError::FailedToReceiveResponse)?;
91
92            return Err(Report::new(ApiClientError::ServerError(
93                error_response.error,
94            )));
95        }
96
97        Ok(resp)
98    }
99
100    async fn handle_request<B: Serialize + Send>(
101        &self,
102        url: &str,
103        method: Method,
104        body: Option<B>,
105    ) -> Result<reqwest::Response, Report<ApiClientError>> {
106        self.client
107            .request(method, url)
108            .headers(
109                get_headers(&self.credentials)
110                    .await
111                    .change_context(ApiClientError::FailedToSendRequest)?,
112            )
113            .set_request_body(body)
114            .send()
115            .await
116            .change_context(ApiClientError::FailedToSendRequest)
117    }
118}
119
120impl ApiHttpClient for ReqwestApiClient {
121    async fn send_request<ResponseT: Send + DeserializeOwned>(
122        &self,
123        url: String,
124        method: Method,
125    ) -> Result<ResponseT, Report<ApiClientError>> {
126        Self::handle_response(self.handle_request::<()>(&url, method, None).await?)
127            .await?
128            .json()
129            .await
130            .change_context(ApiClientError::FailedToReceiveResponse)
131    }
132
133    async fn send_request_with_params<
134        ResponseT: DeserializeOwned + Send,
135        ParamsT: Iterator<Item = (String, String)> + Send,
136    >(
137        &self,
138        url: String,
139        params: ParamsT,
140        method: Method,
141    ) -> Result<ResponseT, Report<ApiClientError>> {
142        let url: String = url + &params.into_url_params();
143        Self::handle_response(self.handle_request::<()>(&url, method, None).await?)
144            .await?
145            .json()
146            .await
147            .change_context(ApiClientError::FailedToReceiveResponse)
148    }
149
150    async fn send_request_body<RequestT: Serialize + Send, ResponseT: DeserializeOwned + Send>(
151        &self,
152        url: String,
153        method: Method,
154        request_body: RequestT,
155    ) -> Result<ResponseT, Report<ApiClientError>> {
156        Self::handle_response(
157            self.handle_request(&url, method, Some(request_body))
158                .await?,
159        )
160        .await?
161        .json()
162        .await
163        .change_context(ApiClientError::FailedToReceiveResponse)
164    }
165
166    async fn send_request_body_get_bytes<RequestT: Serialize + Send>(
167        &self,
168        url: String,
169        method: Method,
170        request_body: RequestT,
171    ) -> Result<Bytes, Report<ApiClientError>> {
172        Self::handle_response(
173            self.handle_request(&url, method, Some(request_body))
174                .await?,
175        )
176        .await?
177        .bytes()
178        .await
179        .change_context(ApiClientError::FailedToReceiveResponse)
180    }
181
182    async fn send_request_body_empty_response<RequestT: Serialize + Send>(
183        &self,
184        url: String,
185        method: Method,
186        request_body: RequestT,
187    ) -> Result<(), Report<ApiClientError>> {
188        Self::handle_response(
189            self.handle_request(&url, method, Some(request_body))
190                .await?,
191        )
192        .await?;
193
194        Ok(())
195    }
196}