use std::sync::Arc;
use log::warn;
use serde::de::DeserializeOwned;
use sharesight_types::ApiEndpoint;
pub struct Client {
client: reqwest::Client,
api_host: Arc<String>,
credentials: Credentials,
}
enum Credentials {
AccessToken(String),
}
impl Credentials {
fn access_token(&self) -> &str {
match self {
Credentials::AccessToken(t) => t,
}
}
}
impl Client {
pub fn new_with_token_and_host(access_token: String, api_host: String) -> Self {
Client {
api_host: Arc::new(api_host),
credentials: Credentials::AccessToken(access_token),
client: reqwest::Client::default(),
}
}
pub async fn execute<'a, T: ApiEndpoint<'a>, U: DeserializeOwned>(
&'a self,
parameters: &'a T::Parameters,
) -> Result<U, SharesightReqwestError> {
let method = match T::HTTP_METHOD {
sharesight_types::ApiHttpMethod::Get => reqwest::Method::GET,
sharesight_types::ApiHttpMethod::Post => reqwest::Method::POST,
sharesight_types::ApiHttpMethod::Put => reqwest::Method::PUT,
sharesight_types::ApiHttpMethod::Delete => reqwest::Method::DELETE,
};
let resp = self
.client
.request(method, T::url(&self.api_host, parameters).to_string())
.bearer_auth(self.credentials.access_token())
.json(parameters)
.send()
.await?;
if resp.status().is_success() {
let full = resp.bytes().await?;
let slice = if full.is_empty() {
b"null".as_slice()
} else {
&full
};
Ok(serde_json::from_slice(slice).map_err(|e| {
if let Ok(s) = std::str::from_utf8(&full) {
warn!("Error deserializing json: {:?}\n{}", e, s);
} else {
warn!("Error deserializing json - not valid utf-8: {:?}", e);
}
e
})?)
} else {
Err(SharesightReqwestError::Http(
resp.url().clone(),
resp.status(),
resp.text().await?,
))
}
}
}
impl AsRef<reqwest::Client> for Client {
fn as_ref(&self) -> &reqwest::Client {
&self.client
}
}
#[derive(Debug, thiserror::Error)]
pub enum SharesightReqwestError {
#[error("Http request returned non-success status code\n{0} {1}\n{2}")]
Http(reqwest::Url, reqwest::StatusCode, String),
#[error("Http error occurred\n{0:?}")]
Reqwest(#[from] reqwest::Error),
#[error("Deserialize error occurred\n{0:?}")]
Deserialize(#[from] serde_json::Error),
}