use std::rc::Rc;
use serde::{de::DeserializeOwned, Serialize};
use crate::{
authenticate::Authenticator,
throttle::{Throttle, ThrottleMechanism},
Error, RestRequest,
};
#[derive(Clone)]
pub struct RestConfig {
endpoint: String,
client: reqwest::Client,
throttle: Option<Throttle>,
authenticator: Option<Rc<Box<dyn Authenticator>>>,
}
impl RestConfig {
pub fn new<S: AsRef<str>>(endpoint: S) -> Self {
log::trace!(
"Create a new RestConfig with endpoint {}.",
endpoint.as_ref()
);
Self {
endpoint: endpoint.as_ref().to_string(),
client: reqwest::Client::new(),
throttle: None,
authenticator: None,
}
}
pub async fn execute<T: DeserializeOwned, E: DeserializeOwned, Q: Serialize, B: Serialize>(
&self,
req: &RestRequest<Q, B>,
) -> Result<T, Error<E>> {
if let Some(throttle) = &self.throttle {
throttle.throttle().await;
}
let full_path = self.endpoint.clone() + &req.path;
log::debug!("Executing a REST-Request to {:?}", full_path);
let request = self.client.request(req.method.clone(), full_path);
let request = if let Some(query) = &req.query {
log::trace!("Request got a query.");
request.query(query)
} else {
request
};
let request = if let Some(body) = &req.body {
log::trace!("Request got a body.");
request.json(body)
} else {
request
};
let request = if req.authenticate {
if let Some(authenticator) = &self.authenticator {
authenticator.authenticate(request)
} else {
log::warn!(
"A request wanted to be authenticated, but no authenticator was registered."
);
request
}
} else {
request
};
log::trace!("Sending REST-Request.");
let response = request.send().await.map_err(|e| Error::Request(e))?;
log::trace!("Converting REST-Request to wanted result type.");
let bytes = response.bytes().await.unwrap(); let maybe_json = serde_json::from_slice(&bytes);
if let Ok(converted) = maybe_json {
Ok(converted)
} else if let Ok(error) = serde_json::from_slice(&bytes) {
Err(Error::Api(error))
} else {
Err(Error::BodyNotParsable(
maybe_json.err().expect("Error to be present"),
))
}
}
}
pub struct RestConfigBuilder {
config: RestConfig,
}
impl RestConfigBuilder {
pub fn new<S: AsRef<str>>(endpoint: S) -> Self {
Self {
config: RestConfig::new(endpoint),
}
}
pub fn client(mut self, client: reqwest::Client) -> Self {
self.config.client = client;
self
}
pub fn throttle<T: 'static + ThrottleMechanism>(mut self, throttle: T) -> Self {
self.config.throttle = Some(Throttle::new(throttle));
self
}
pub fn authenticator<T: 'static + Authenticator>(mut self, authenticator: T) -> Self {
self.config.authenticator = Some(Rc::new(Box::new(authenticator)));
self
}
pub fn build(self) -> RestConfig {
self.config
}
}
#[cfg(test)]
mod test {
use crate::StandardRestError;
use super::*;
use httpmock::prelude::*;
use serde::Deserialize;
#[derive(Deserialize)]
struct Empty {}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Json {
key: String,
}
#[tokio::test]
async fn config_simple_get() -> Result<(), Error<StandardRestError>> {
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method(GET).path("/test");
then.status(200)
.header("content-type", "application/json; charset=UTF-8")
.body("{}");
});
let config = RestConfig::new(server.base_url());
let _: Empty = config.execute(&RestRequest::<(), ()>::get("/test")).await?;
mock.assert();
Ok(())
}
#[tokio::test]
async fn config_query_get() -> Result<(), Error<StandardRestError>> {
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method(GET).path("/test").query_param("key", "value");
then.status(200)
.header("content-type", "application/json; charset=UTF-8")
.body("{}");
});
let config = RestConfig::new(server.base_url());
let _: Empty = config
.execute(&RestRequest::<&Json, ()>::get("/test").query(&Json {
key: "value".to_string(),
}))
.await?;
mock.assert();
Ok(())
}
#[tokio::test]
async fn config_simple_post() -> Result<(), Error<StandardRestError>> {
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method(POST).path("/test").body(r#"{"key":"value"}"#);
then.status(200)
.header("content-type", "application/json; charset=UTF-8")
.body("{}");
});
let config = RestConfig::new(server.base_url());
let _: Empty = config
.execute(&RestRequest::<(), &Json>::post("/test").body(&Json {
key: "value".to_string(),
}))
.await?;
mock.assert();
Ok(())
}
#[tokio::test]
async fn config_simple_return() -> Result<(), Error<StandardRestError>> {
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method(GET).path("/test");
then.status(200)
.header("content-type", "application/json; charset=UTF-8")
.body(r#"{"key":"value"}"#);
});
let config = RestConfig::new(server.base_url());
let query: Json = config.execute(&RestRequest::<(), ()>::get("/test")).await?;
mock.assert();
assert_eq!(
query,
Json {
key: "value".to_string()
}
);
Ok(())
}
}