use crate::model::{
apps::{
AppDataQuery, AppDataQueryResult, AppDataSelectMode, AppDataSelectQuery, ThirdPartyApp,
},
ApiReturn, Error, Result,
};
use reqwest::{
multipart::{Form, Part},
Client as HttpClient,
};
pub use reqwest::Method;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
macro_rules! api_return_ok {
($ret:ty, $res:ident) => {
match $res.json::<ApiReturn<$ret>>().await {
Ok(x) => {
if x.ok {
Ok(x.payload)
} else {
Err(Error::MiscError(x.message))
}
}
Err(e) => Err(Error::MiscError(e.to_string())),
}
};
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimplifiedQuery {
pub query: AppDataSelectQuery,
pub mode: AppDataSelectMode,
}
#[derive(Debug, Clone)]
pub struct DataClient {
pub http: HttpClient,
pub api_key: String,
pub host: String,
}
impl DataClient {
pub fn new(host: Option<String>, api_key: String) -> Self {
Self {
http: HttpClient::new(),
api_key,
host: host.unwrap_or("https://tetratto.com".to_string()),
}
}
pub async fn get_app(&self) -> Result<ThirdPartyApp> {
match self
.http
.get(format!("{}/api/v1/app_data/app", self.host))
.header("Atto-Secret-Key", &self.api_key)
.send()
.await
{
Ok(x) => api_return_ok!(ThirdPartyApp, x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
pub async fn query(&self, query: &SimplifiedQuery) -> Result<AppDataQueryResult> {
match self
.http
.post(format!("{}/api/v1/app_data/query", self.host))
.header("Atto-Secret-Key", &self.api_key)
.json(&query)
.send()
.await
{
Ok(x) => api_return_ok!(AppDataQueryResult, x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
pub async fn insert(&self, key: String, value: String) -> Result<String> {
match self
.http
.post(format!("{}/api/v1/app_data", self.host))
.header("Atto-Secret-Key", &self.api_key)
.json(&serde_json::Value::Object({
let mut map = serde_json::Map::new();
map.insert("key".to_string(), serde_json::Value::String(key));
map.insert("value".to_string(), serde_json::Value::String(value));
map
}))
.send()
.await
{
Ok(x) => api_return_ok!(String, x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
pub async fn update(&self, id: usize, value: String) -> Result<()> {
match self
.http
.post(format!("{}/api/v1/app_data/{id}/value", self.host))
.header("Atto-Secret-Key", &self.api_key)
.json(&serde_json::Value::Object({
let mut map = serde_json::Map::new();
map.insert("value".to_string(), serde_json::Value::String(value));
map
}))
.send()
.await
{
Ok(x) => api_return_ok!((), x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
pub async fn rename(&self, id: usize, key: String) -> Result<()> {
match self
.http
.post(format!("{}/api/v1/app_data/{id}/key", self.host))
.header("Atto-Secret-Key", &self.api_key)
.json(&serde_json::Value::Object({
let mut map = serde_json::Map::new();
map.insert("key".to_string(), serde_json::Value::String(key));
map
}))
.send()
.await
{
Ok(x) => api_return_ok!((), x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
pub async fn remove(&self, id: usize) -> Result<()> {
match self
.http
.delete(format!("{}/api/v1/app_data/{id}", self.host))
.header("Atto-Secret-Key", &self.api_key)
.send()
.await
{
Ok(x) => api_return_ok!((), x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
pub async fn remove_query(&self, query: &AppDataQuery) -> Result<()> {
match self
.http
.delete(format!("{}/api/v1/app_data/query", self.host))
.header("Atto-Secret-Key", &self.api_key)
.json(&query)
.send()
.await
{
Ok(x) => api_return_ok!((), x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ApiClientState {
pub user_token: String,
pub user_verifier: String,
pub user_id: usize,
pub app_id: usize,
}
#[derive(Debug, Clone)]
pub struct ApiClient {
pub http: HttpClient,
pub state: ApiClientState,
pub host: String,
}
impl ApiClient {
pub fn new(host: Option<String>, state: ApiClientState) -> Self {
Self {
http: HttpClient::new(),
state,
host: host.unwrap_or("https://tetratto.com".to_string()),
}
}
pub async fn refresh_token(&mut self) -> Result<String> {
match self
.http
.post(format!(
"{}/api/v1/auth/user/{}/grants/{}/refresh",
self.host, self.state.user_id, self.state.app_id
))
.header("X-Cookie", &format!("Atto-Grant={}", self.state.user_token))
.json(&serde_json::Value::Object({
let mut map = serde_json::Map::new();
map.insert(
"verifier".to_string(),
serde_json::Value::String(self.state.user_verifier.to_owned()),
);
map
}))
.send()
.await
{
Ok(x) => {
let ret = api_return_ok!(String, x)?;
self.state.user_token = ret.clone();
Ok(ret)
}
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
pub async fn request<T, B>(
&self,
route: String,
method: Method,
body: Option<&B>,
) -> Result<ApiReturn<T>>
where
T: Serialize + DeserializeOwned,
B: Serialize + ?Sized,
{
if let Some(body) = body {
match self
.http
.request(method, format!("{}/api/v1/auth/{route}", self.host))
.header("X-Cookie", &format!("Atto-Grant={}", self.state.user_token))
.json(&body)
.send()
.await
{
Ok(x) => api_return_ok!(ApiReturn<T>, x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
} else {
match self
.http
.request(method, format!("{}/api/v1/auth/{route}", self.host))
.header("X-Cookie", &format!("Atto-Grant={}", self.state.user_token))
.send()
.await
{
Ok(x) => api_return_ok!(ApiReturn<T>, x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
}
pub async fn request_attachments<T, B>(
&self,
route: String,
attachments: Vec<Vec<u8>>,
body: &B,
) -> Result<ApiReturn<T>>
where
T: Serialize + DeserializeOwned,
B: Serialize + ?Sized,
{
let mut multipart_body = Form::new();
for v in attachments.clone() {
multipart_body = multipart_body.part(String::new(), Part::bytes(v));
}
drop(attachments);
multipart_body = multipart_body.part(
String::new(),
Part::text(serde_json::to_string(body).unwrap()),
);
match self
.http
.post(format!("{}/api/v1/auth/{route}", self.host))
.header("X-Cookie", &format!("Atto-Grant={}", self.state.user_token))
.multipart(multipart_body)
.send()
.await
{
Ok(x) => api_return_ok!(ApiReturn<T>, x),
Err(e) => Err(Error::MiscError(e.to_string())),
}
}
}