fiberplane_api_client/
api_client.rs1use crate::builder::ApiClientBuilder;
2use anyhow::Result;
3use bytes::Bytes;
4use fiberplane_models::paging::{PagedVec, HAS_MORE_RESULTS_KEY, TOTAL_RESULTS_KEY};
5use reqwest::{Client, Method, RequestBuilder, Response, StatusCode};
6use thiserror::Error;
7use url::Url;
8
9#[derive(Debug, Error)]
10pub enum ApiClientError<T> {
11 #[error("An invalid URL was provided: {0}")]
13 ParseError(#[from] url::ParseError),
14
15 #[error("An error occurred while making the request: {0}")]
17 ClientError(#[from] reqwest::Error),
18
19 #[error(transparent)]
22 ServiceError(T),
23
24 #[error("API returned an unknown response: Status: {0}, Body: {1:?}")]
27 InvalidResponse(StatusCode, Bytes),
28}
29
30#[derive(Debug)]
31pub struct ApiClient {
32 pub client: Client,
33 pub server: Url,
34}
35
36impl ApiClient {
37 pub fn request(
38 &self,
39 method: Method,
40 endpoint: &str,
41 ) -> Result<RequestBuilder, url::ParseError> {
42 let url = self.server.join(endpoint)?;
43
44 Ok(self.client.request(method, url))
45 }
46
47 pub fn builder(base_url: Url) -> ApiClientBuilder {
48 ApiClientBuilder::new(base_url)
49 }
50
51 pub async fn do_req_paged<T, E>(
52 &self,
53 req: RequestBuilder,
54 ) -> Result<PagedVec<T>, ApiClientError<E>>
55 where
56 T: serde::de::DeserializeOwned,
57 E: serde::de::DeserializeOwned,
58 {
59 let response = req.send().await?;
61
62 let status_code = response.status();
65 let has_more_results = Self::parse_has_more_results_header(&response);
66 let total_results = Self::parse_total_results_header(&response);
67
68 let body = response.bytes().await?;
70
71 if let Ok(result) = serde_json::from_slice::<Vec<T>>(&body) {
73 let result = PagedVec {
75 inner: result,
76 has_more_results,
77 total_results,
78 };
79 return Ok(result);
80 }
81
82 if let Ok(result) = serde_json::from_slice::<E>(&body) {
84 return Err(ApiClientError::ServiceError(result));
85 }
86
87 Err(ApiClientError::InvalidResponse(status_code, body))
90 }
91
92 pub async fn do_req<T, E>(&self, req: RequestBuilder) -> Result<T, ApiClientError<E>>
93 where
94 T: serde::de::DeserializeOwned,
95 E: serde::de::DeserializeOwned,
96 {
97 let response = req.send().await?;
99
100 let status_code = response.status();
103
104 let body = response.bytes().await?;
106
107 if let Ok(result) = serde_json::from_slice::<T>(&body) {
109 return Ok(result);
110 }
111
112 if let Ok(result) = serde_json::from_slice::<E>(&body) {
114 return Err(ApiClientError::ServiceError(result));
115 }
116
117 Err(ApiClientError::InvalidResponse(status_code, body))
120 }
121
122 fn parse_has_more_results_header(response: &Response) -> bool {
125 response
126 .headers()
127 .get(HAS_MORE_RESULTS_KEY)
128 .map_or(false, |value| {
129 value
130 .to_str()
131 .map(|value| value.parse().unwrap_or_default())
132 .unwrap_or_default()
133 })
134 }
135
136 fn parse_total_results_header(response: &Response) -> Option<u32> {
139 response
140 .headers()
141 .get(TOTAL_RESULTS_KEY)
142 .map(|value| value.to_str().ok().and_then(|value| value.parse().ok()))
143 .unwrap_or_default()
144 }
145}