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