use reqwest::{Client, Response};
use serde::{de::DeserializeOwned, Serialize};
use tokio::sync::RwLock;
use crate::{
config::HighLevelConfig,
error::{Error, Result},
};
pub struct HttpClient {
inner: Client,
pub base_url: String,
pub api_version: String,
token: RwLock<Option<String>>,
}
impl HttpClient {
pub fn new(config: &HighLevelConfig) -> Self {
Self {
inner: Client::new(),
base_url: config.base_url.clone(),
api_version: config.api_version.clone(),
token: RwLock::new(None),
}
}
pub async fn set_token(&self, token: String) {
*self.token.write().await = Some(token);
}
pub async fn get_token(&self) -> Option<String> {
self.token.read().await.clone()
}
fn url(&self, path: &str) -> String {
format!("{}{}", self.base_url, path)
}
async fn handle_response<T: DeserializeOwned>(&self, resp: Response) -> Result<T> {
let status = resp.status();
if status.is_success() {
Ok(resp.json::<T>().await?)
} else {
let code = status.as_u16();
let body = resp.text().await.unwrap_or_default();
let message = serde_json::from_str::<serde_json::Value>(&body)
.ok()
.and_then(|v| {
v.get("message")
.and_then(|m| m.as_str())
.map(|s| s.to_string())
})
.unwrap_or(body);
Err(Error::Api { status: code, message })
}
}
async fn handle_no_content(&self, resp: Response) -> Result<()> {
let status = resp.status();
if status.is_success() {
Ok(())
} else {
let code = status.as_u16();
let message = resp.text().await.unwrap_or_else(|_| status.to_string());
Err(Error::Api { status: code, message })
}
}
pub async fn get<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.get(self.url(path))
.header("Version", &self.api_version)
.header("Content-Type", "application/json");
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_response(req.send().await?).await
}
pub async fn get_with_query<T: DeserializeOwned, Q: Serialize + ?Sized>(
&self,
path: &str,
query: &Q,
) -> Result<T> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.get(self.url(path))
.header("Version", &self.api_version)
.header("Content-Type", "application/json")
.query(query);
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_response(req.send().await?).await
}
pub async fn post<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.post(self.url(path))
.header("Version", &self.api_version)
.json(body);
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_response(req.send().await?).await
}
pub async fn put<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.put(self.url(path))
.header("Version", &self.api_version)
.json(body);
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_response(req.send().await?).await
}
pub async fn patch<T: DeserializeOwned, B: Serialize>(&self, path: &str, body: &B) -> Result<T> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.patch(self.url(path))
.header("Version", &self.api_version)
.json(body);
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_response(req.send().await?).await
}
pub async fn delete<T: DeserializeOwned>(&self, path: &str) -> Result<T> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.delete(self.url(path))
.header("Version", &self.api_version);
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_response(req.send().await?).await
}
pub async fn delete_with_body<T: DeserializeOwned, B: Serialize>(
&self,
path: &str,
body: &B,
) -> Result<T> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.delete(self.url(path))
.header("Version", &self.api_version)
.json(body);
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_response(req.send().await?).await
}
pub async fn delete_no_content(&self, path: &str) -> Result<()> {
let token = self.token.read().await.clone();
let mut req = self
.inner
.delete(self.url(path))
.header("Version", &self.api_version);
if let Some(t) = token {
req = req.bearer_auth(t);
}
self.handle_no_content(req.send().await?).await
}
}