use crate::{
core::PropagateCurrentContext,
error::{ClientError, ErrorInformation},
openid::{TokenInjector, TokenProvider},
};
use async_trait::async_trait;
use reqwest::{Response, StatusCode};
use serde::{de::DeserializeOwned, Serialize};
use std::marker::Send;
use url::Url;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub(crate) trait CoreClient {
fn client(&self) -> &reqwest::Client;
fn token_provider(&self) -> &dyn TokenProvider;
#[doc(hidden)]
async fn read<T>(&self, url: Url) -> Result<Option<T>, ClientError>
where
Self: Send,
T: DeserializeOwned,
{
self.read_with_query_parameters(url, None).await
}
async fn read_with_query_parameters<T>(
&self,
url: Url,
query: Option<Vec<(String, String)>>,
) -> Result<Option<T>, ClientError>
where
Self: Send,
T: DeserializeOwned,
{
let query = query.unwrap_or_default();
let req = self
.client()
.get(url)
.query(&query)
.propagate_current_context()
.inject_token(self.token_provider())
.await?;
Self::read_response(req.send().await?).await
}
async fn read_response<T: DeserializeOwned>(
response: Response,
) -> Result<Option<T>, ClientError> {
log::debug!("Eval get response: {:#?}", response);
match response.status() {
StatusCode::OK => Ok(Some(response.json().await?)),
StatusCode::NOT_FOUND => Ok(None),
_ => Self::default_response(response).await,
}
}
async fn update<A>(&self, url: Url, payload: Option<A>) -> Result<bool, ClientError>
where
Self: Send,
A: Serialize + Send + Sync,
{
let req = if let Some(p) = payload {
self.client().put(url).json(&p)
} else {
self.client().put(url)
}
.propagate_current_context()
.inject_token(self.token_provider())
.await?;
Self::update_response(req.send().await?).await
}
async fn update_response(response: Response) -> Result<bool, ClientError> {
log::debug!("Eval update response: {:#?}", response);
match response.status() {
StatusCode::OK | StatusCode::NO_CONTENT | StatusCode::ACCEPTED => Ok(true),
StatusCode::NOT_FOUND => Ok(false),
_ => Self::default_response(response).await,
}
}
async fn delete(&self, url: Url) -> Result<bool, ClientError>
where
Self: Send,
{
let req = self
.client()
.delete(url)
.inject_token(self.token_provider())
.await?;
Self::delete_response(req.send().await?).await
}
async fn delete_response(response: Response) -> Result<bool, ClientError> {
log::debug!("Eval delete response: {:#?}", response);
match response.status() {
StatusCode::OK | StatusCode::NO_CONTENT => Ok(true),
StatusCode::NOT_FOUND => Ok(false),
_ => Self::default_response(response).await,
}
}
async fn create<P, T>(&self, url: Url, payload: Option<P>) -> Result<Option<T>, ClientError>
where
Self: Send,
P: Serialize + Send + Sync,
T: DeserializeOwned,
{
self.create_with_query_parameters(url, payload, None).await
}
async fn create_with_query_parameters<P, T>(
&self,
url: Url,
payload: Option<P>,
query: Option<Vec<(String, String)>>,
) -> Result<Option<T>, ClientError>
where
Self: Send,
P: Serialize + Send + Sync,
T: DeserializeOwned,
{
let query = query.unwrap_or_default();
let req = if let Some(p) = payload {
self.client().post(url).json(&p)
} else {
self.client().post(url)
}
.query(&query)
.propagate_current_context()
.inject_token(self.token_provider())
.await?;
Self::create_response(req.send().await?).await
}
async fn create_response<T: DeserializeOwned>(
response: Response,
) -> Result<Option<T>, ClientError> {
log::debug!("Eval create response: {:#?}", response);
match response.status() {
StatusCode::CREATED | StatusCode::ACCEPTED => Ok(None),
StatusCode::OK => Ok(Some(response.json().await?)),
_ => Self::default_response(response).await,
}
}
async fn default_response<T>(response: Response) -> Result<T, ClientError> {
let code = response.status();
match response.json::<ErrorInformation>().await {
Ok(info) => Err(ClientError::Service { code, error: info }),
Err(_) => Err(ClientError::Response(code)),
}
}
}