use crate::Error;
use log::debug;
use serde::Serialize;
pub mod dav;
pub mod file;
pub mod models;
pub mod session;
pub mod share;
pub mod site;
pub mod uri;
pub mod user;
pub mod workflow;
#[derive(Debug, Clone)]
pub struct ApiV4Client {
pub base_url: String,
pub http_client: reqwest::Client,
pub token: Option<String>,
pub refresh_token: Option<String>,
}
impl ApiV4Client {
pub fn new(base_url: &str) -> Self {
crate::ensure_tls_crypto_provider();
Self {
base_url: base_url.to_string(),
http_client: reqwest::Client::new(),
token: None,
refresh_token: None,
}
}
pub fn set_token(&mut self, token: String) {
self.token = Some(token);
}
pub fn clear_token(&mut self) {
self.token = None;
self.refresh_token = None;
}
pub fn set_refresh_token(&mut self, token: String) {
self.refresh_token = Some(token);
}
pub fn get_refresh_token(&self) -> Option<&str> {
self.refresh_token.as_deref()
}
fn get_url(&self, endpoint: &str) -> String {
format!(
"{}/api/v4/{}",
self.base_url.trim_end_matches('/'),
endpoint.trim_start_matches('/')
)
}
pub async fn get<T>(&self, endpoint: &str) -> Result<T, Error>
where
T: serde::de::DeserializeOwned + std::fmt::Debug,
{
let url = self.get_url(endpoint);
let mut request = self.http_client.get(&url);
if let Some(token) = &self.token {
request = request.bearer_auth(token);
}
debug!("GET URL: {}", url);
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let raw_text = response.text().await?;
if let Ok(api_response) =
serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& api_response.code != 0
{
return Err(Error::Api {
code: api_response.code,
message: api_response.msg,
});
}
return Err(Error::Api {
code: status.as_u16() as i32,
message: raw_text.trim().to_string(),
});
}
let raw_text = response.text().await?;
if let Ok(base) = serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& base.code == 401
{
return Err(Error::Unauthorized(base.msg));
}
match serde_json::from_str::<T>(&raw_text) {
Ok(json) => {
debug!("Response status: {}, JSON: {:?}", status, json);
Ok(json)
}
Err(e) => {
debug!("JSON parse error: {}, raw response: {}", e, raw_text);
Err(Error::Json(e))
}
}
}
pub async fn post<T>(&self, endpoint: &str, body: &impl Serialize) -> Result<T, Error>
where
T: serde::de::DeserializeOwned + std::fmt::Debug,
{
let url = self.get_url(endpoint);
let mut request = self.http_client.post(&url).json(body);
if let Some(token) = &self.token {
request = request.bearer_auth(token);
}
debug!("POST URL: {}", url);
if let Ok(json_str) = serde_json::to_string(body) {
debug!("Request body: {}", json_str);
}
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let raw_text = response.text().await?;
if let Ok(api_response) =
serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& api_response.code != 0
{
return Err(Error::Api {
code: api_response.code,
message: api_response.msg,
});
}
return Err(Error::Api {
code: status.as_u16() as i32,
message: raw_text.trim().to_string(),
});
}
let raw_text = response.text().await?;
if let Ok(base) = serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& base.code == 401
{
return Err(Error::Unauthorized(base.msg));
}
match serde_json::from_str::<T>(&raw_text) {
Ok(json) => {
debug!("Response status: {}, JSON: {:?}", status, json);
Ok(json)
}
Err(e) => {
debug!("JSON parse error: {}, raw response: {}", e, raw_text);
Err(Error::Json(e))
}
}
}
pub async fn put<T>(&self, endpoint: &str, body: &impl Serialize) -> Result<T, Error>
where
T: serde::de::DeserializeOwned + std::fmt::Debug,
{
let url = self.get_url(endpoint);
let mut request = self.http_client.put(&url).json(body);
if let Some(token) = &self.token {
request = request.bearer_auth(token);
}
debug!("PUT URL: {}", url);
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let raw_text = response.text().await?;
if let Ok(api_response) =
serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& api_response.code != 0
{
return Err(Error::Api {
code: api_response.code,
message: api_response.msg,
});
}
return Err(Error::Api {
code: status.as_u16() as i32,
message: raw_text.trim().to_string(),
});
}
let raw_text = response.text().await?;
match serde_json::from_str::<T>(&raw_text) {
Ok(json) => {
debug!("Response status: {}, JSON: {:?}", status, json);
Ok(json)
}
Err(e) => {
debug!("JSON parse error: {}, raw response: {}", e, raw_text);
Err(Error::Json(e))
}
}
}
pub async fn patch<T>(&self, endpoint: &str, body: &impl Serialize) -> Result<T, Error>
where
T: serde::de::DeserializeOwned + std::fmt::Debug,
{
let url = self.get_url(endpoint);
let mut request = self.http_client.patch(&url).json(body);
if let Some(token) = &self.token {
request = request.bearer_auth(token);
}
debug!("PATCH URL: {}", url);
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let raw_text = response.text().await?;
if let Ok(api_response) =
serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& api_response.code != 0
{
return Err(Error::Api {
code: api_response.code,
message: api_response.msg,
});
}
return Err(Error::Api {
code: status.as_u16() as i32,
message: raw_text.trim().to_string(),
});
}
let raw_text = response.text().await?;
match serde_json::from_str::<T>(&raw_text) {
Ok(json) => {
debug!("Response status: {}, JSON: {:?}", status, json);
Ok(json)
}
Err(e) => {
debug!("JSON parse error: {}, raw response: {}", e, raw_text);
Err(Error::Json(e))
}
}
}
pub async fn delete<T>(&self, endpoint: &str) -> Result<T, Error>
where
T: serde::de::DeserializeOwned + std::fmt::Debug,
{
let url = self.get_url(endpoint);
let mut request = self.http_client.delete(&url);
if let Some(token) = &self.token {
request = request.bearer_auth(token);
}
debug!("DELETE URL: {}", url);
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let raw_text = response.text().await?;
if let Ok(api_response) =
serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& api_response.code != 0
{
return Err(Error::Api {
code: api_response.code,
message: api_response.msg,
});
}
return Err(Error::Api {
code: status.as_u16() as i32,
message: raw_text.trim().to_string(),
});
}
let raw_text = response.text().await?;
if let Ok(base) = serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& base.code == 401
{
return Err(Error::Unauthorized(base.msg));
}
match serde_json::from_str::<T>(&raw_text) {
Ok(json) => {
debug!("Response status: {}, JSON: {:?}", status, json);
Ok(json)
}
Err(e) => {
debug!("JSON parse error: {}, raw response: {}", e, raw_text);
Err(Error::Json(e))
}
}
}
pub async fn delete_with_body<T>(
&self,
endpoint: &str,
body: &impl Serialize,
) -> Result<T, Error>
where
T: serde::de::DeserializeOwned + std::fmt::Debug,
{
let url = self.get_url(endpoint);
let mut request = self.http_client.delete(&url).json(body);
if let Some(token) = &self.token {
request = request.bearer_auth(token);
}
debug!("DELETE URL: {} with body", url);
let response = request.send().await?;
let status = response.status();
if !status.is_success() {
let raw_text = response.text().await?;
if let Ok(api_response) =
serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& api_response.code != 0
{
return Err(Error::Api {
code: api_response.code,
message: api_response.msg,
});
}
return Err(Error::Api {
code: status.as_u16() as i32,
message: raw_text.trim().to_string(),
});
}
let raw_text = response.text().await?;
if let Ok(base) = serde_json::from_str::<crate::ApiResponse<serde_json::Value>>(&raw_text)
&& base.code == 401
{
return Err(Error::Unauthorized(base.msg));
}
match serde_json::from_str::<T>(&raw_text) {
Ok(json) => {
debug!("Response status: {}, JSON: {:?}", status, json);
Ok(json)
}
Err(e) => {
debug!("JSON parse error: {}, raw response: {}", e, raw_text);
Err(Error::Json(e))
}
}
}
}
#[cfg(test)]
mod tests {
use super::ApiV4Client;
#[test]
fn clear_token_removes_refresh_token() {
let mut client = ApiV4Client::new("https://example.com");
client.set_token("access_token".to_string());
client.set_refresh_token("refresh_token".to_string());
client.clear_token();
assert!(client.token.is_none());
assert!(client.get_refresh_token().is_none());
}
}