use dalloriam_cloud_protocol::jd::{
Area, AreaList, Category, CategoryList, Item, ItemsList, NewItem,
};
use reqwest::{Response, StatusCode};
use snafu::{ResultExt, Snafu};
use crate::error::AuthError;
use crate::{service, Credentials};
#[derive(Debug, Snafu)]
pub enum JdError {
TokenValidationError {
source: AuthError,
},
AccessDenied,
#[snafu(display("unexpected status {}", status))]
UnexpectedError {
status: StatusCode,
},
ResponseDeserializationError {
source: reqwest::Error,
},
CreateArea {
source: reqwest::Error,
},
ListAreas {
source: reqwest::Error,
},
CreateCategory {
source: reqwest::Error,
},
ListCategories {
source: reqwest::Error,
},
CreateItem {
source: reqwest::Error,
},
Search {
source: reqwest::Error,
},
}
type Result<T> = std::result::Result<T, JdError>;
pub struct JdClient {
client: reqwest::Client,
credentials: Credentials,
}
impl JdClient {
pub async fn new(credentials: Credentials) -> Result<Self> {
let credentials = credentials
.ensure_token()
.await
.context(TokenValidationSnafu)?;
Ok(Self {
client: reqwest::Client::default(),
credentials,
})
}
fn check_error(&self, resp: &Response) -> Result<()> {
if !resp.status().is_success() {
return match resp.status() {
StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => Err(JdError::AccessDenied),
_ => Err(JdError::UnexpectedError {
status: resp.status(),
}),
};
}
Ok(())
}
pub async fn create_area(&self, area: &Area) -> Result<()> {
let resp = self
.client
.post(&format!("{}/area/{}", service::JD_HOST, area.id))
.query(&[("name", &area.name)])
.header("Authorization", self.credentials.to_string())
.send()
.await
.context(CreateAreaSnafu)?;
self.check_error(&resp)
}
pub async fn list_areas(&self) -> Result<AreaList> {
let resp = self
.client
.get(&format!("{}/area", service::JD_HOST))
.header("Authorization", self.credentials.to_string())
.send()
.await
.context(ListAreasSnafu)?;
self.check_error(&resp)?;
resp.json::<AreaList>()
.await
.context(ResponseDeserializationSnafu)
}
pub async fn create_category(&self, category: &Category) -> Result<()> {
let resp = self
.client
.post(&format!("{}/category/{}", service::JD_HOST, category.id))
.query(&[("name", &category.name)])
.header("Authorization", self.credentials.to_string())
.send()
.await
.context(CreateCategorySnafu)?;
self.check_error(&resp)
}
pub async fn list_categories(&self) -> Result<CategoryList> {
let resp = self
.client
.get(&format!("{}/category", service::JD_HOST))
.header("Authorization", self.credentials.to_string())
.send()
.await
.context(ListCategoriesSnafu)?;
self.check_error(&resp)?;
resp.json::<CategoryList>()
.await
.context(ResponseDeserializationSnafu)
}
pub async fn create_item(&self, item: &NewItem) -> Result<Item> {
let resp = self
.client
.post(&format!(
"{}/category/{}/item",
service::JD_HOST,
item.category_id
))
.query(&[("name", &item.name)])
.header("Authorization", self.credentials.to_string())
.send()
.await
.context(CreateItemSnafu)?;
self.check_error(&resp)?;
resp.json::<Item>()
.await
.context(ResponseDeserializationSnafu)
}
pub async fn put_item(&self, item: &Item) -> Result<()> {
let resp = self
.client
.post(&format!(
"{}/category/{}/item/{}",
service::JD_HOST,
item.category_id,
item.id
))
.query(&[("name", &item.name)])
.header("Authorization", self.credentials.to_string())
.send()
.await
.context(CreateItemSnafu)?;
self.check_error(&resp)
}
pub async fn search(
&self,
category: Option<usize>,
query: Option<String>,
) -> Result<ItemsList> {
let mut qstr = vec![];
if let Some(cat) = category {
qstr.push(("category", cat.to_string()));
}
if let Some(q) = query {
qstr.push(("q", q));
}
let resp = self
.client
.post(&format!("{}/search", service::JD_HOST))
.query(&qstr)
.header("Authorization", self.credentials.to_string())
.send()
.await
.context(SearchSnafu)?;
self.check_error(&resp)?;
resp.json::<ItemsList>()
.await
.context(ResponseDeserializationSnafu)
}
}