Files
komodo/lib/monitor_client/src/lib.rs
2023-01-01 20:51:57 +00:00

336 lines
10 KiB
Rust

// #![allow(unused)]
use anyhow::{anyhow, Context};
use monitor_types::User;
use reqwest::StatusCode;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::json;
pub use futures_util;
pub use tokio_tungstenite;
pub use monitor_types as types;
mod build;
mod deployment;
mod group;
mod permissions;
mod procedure;
mod secret;
mod server;
mod update;
#[derive(Clone)]
pub struct MonitorClient {
http_client: reqwest::Client,
url: String,
token: String,
}
impl MonitorClient {
pub fn new_with_token(url: &str, token: impl Into<String>) -> MonitorClient {
MonitorClient {
http_client: reqwest::Client::new(),
url: parse_url(url),
token: token.into(),
}
}
pub async fn new_with_password(
url: &str,
username: impl Into<String>,
password: impl Into<String>,
) -> anyhow::Result<MonitorClient> {
let mut client = MonitorClient::new_with_token(url, "");
let token = client
.post_string(
"/auth/local/login",
json!({ "username": username.into(), "password": password.into() }),
)
.await
.context("failed to log in with password")?;
client.token = token;
Ok(client)
}
pub async fn new_with_secret(
url: &str,
username: impl Into<String>,
secret: impl Into<String>,
) -> anyhow::Result<MonitorClient> {
let mut client = MonitorClient::new_with_token(url, "");
let token = client
.post_string(
"/auth/secret/login",
json!({ "username": username.into(), "secret": secret.into() }),
)
.await
.context("failed to log in with secret")?;
client.token = token;
Ok(client)
}
pub async fn new_with_new_account(
url: &str,
username: impl Into<String>,
password: impl Into<String>,
) -> anyhow::Result<MonitorClient> {
let mut client = MonitorClient::new_with_token(url, "");
client.token = client.create_user(username, password).await?;
Ok(client)
}
pub async fn create_user(
&self,
username: impl Into<String>,
password: impl Into<String>,
) -> anyhow::Result<String> {
self.post_string(
"/auth/local/create_user",
json!({ "username": username.into(), "password": password.into() }),
)
.await
}
pub async fn get_user(&self) -> anyhow::Result<User> {
self.get("/api/user", Option::<()>::None).await
}
pub async fn get_username(&self, user_id: &str) -> anyhow::Result<String> {
self.get_string(&format!("/api/username/{user_id}"), Option::<()>::None)
.await
}
async fn get<R: DeserializeOwned>(
&self,
endpoint: &str,
query: impl Serialize,
) -> anyhow::Result<R> {
let res = self
.http_client
.get(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token))
.query(&query)
.send()
.await
.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.json().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
async fn get_string(&self, endpoint: &str, query: impl Serialize) -> anyhow::Result<String> {
let res = self
.http_client
.get(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token))
.query(&query)
.send()
.await
.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.text().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
async fn post<B: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<R> {
let req = self
.http_client
.post(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token));
let req = if let Some(body) = body.into() {
req.header("Content-Type", "application/json").json(&body)
} else {
req
};
let res = req.send().await.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.json().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
async fn post_string<B: Serialize>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<String> {
let req = self
.http_client
.post(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token));
let req = if let Some(body) = body.into() {
req.header("Content-Type", "application/json").json(&body)
} else {
req
};
let res = req.send().await.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.text().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
async fn patch<B: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<R> {
let req = self
.http_client
.patch(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token));
let req = if let Some(body) = body.into() {
req.header("Content-Type", "application/json").json(&body)
} else {
req
};
let res = req.send().await.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.json().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
async fn _patch_string<B: Serialize>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<String> {
let req = self
.http_client
.patch(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token));
let req = if let Some(body) = body.into() {
req.header("Content-Type", "application/json").json(&body)
} else {
req
};
let res = req.send().await.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.text().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
async fn delete<B: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<R> {
let req = self
.http_client
.delete(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token));
let req = if let Some(body) = body.into() {
req.header("Content-Type", "application/json").json(&body)
} else {
req
};
let res = req.send().await.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.json().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
async fn _delete_string<B: Serialize>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<String> {
let req = self
.http_client
.delete(format!("{}{endpoint}", self.url))
.header("Authorization", format!("Bearer {}", self.token));
let req = if let Some(body) = body.into() {
req.header("Content-Type", "application/json").json(&body)
} else {
req
};
let res = req.send().await.context("failed to reach monitor api")?;
let status = res.status();
if status == StatusCode::OK {
match res.text().await {
Ok(res) => Ok(res),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
} else {
match res.text().await {
Ok(res) => Err(anyhow!("{status}: {res}")),
Err(e) => Err(anyhow!("{status}: {e:#?}")),
}
}
}
}
fn parse_url(url: &str) -> String {
if url.chars().nth(url.len() - 1).unwrap() == '/' {
url[..url.len() - 1].to_string()
} else {
url.to_string()
}
}