initialize monitor client

This commit is contained in:
mbecker20
2023-06-18 06:15:31 +00:00
parent c510a64207
commit 5e51050fa3
3 changed files with 146 additions and 3 deletions

5
Cargo.lock generated
View File

@@ -1439,8 +1439,13 @@ dependencies = [
name = "monitor_client"
version = "1.0.0"
dependencies = [
"anyhow",
"envy",
"monitor_types",
"reqwest",
"resolver_api",
"serde",
"serde_json",
]
[[package]]

View File

@@ -10,3 +10,8 @@ license.workspace = true
[dependencies]
monitor_types.workspace = true
reqwest.workspace = true
anyhow.workspace = true
serde.workspace = true
serde_json.workspace = true
resolver_api.workspace = true
envy.workspace = true

View File

@@ -1,7 +1,23 @@
use anyhow::{anyhow, Context};
use monitor_types::requests::auth::{self, LoginLocalUserResponse, LoginWithSecretResponse};
use reqwest::StatusCode;
use resolver_api::HasResponse;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::json;
#[derive(Deserialize)]
struct MonitorEnv {
monitor_url: String,
monitor_token: Option<String>,
monitor_username: Option<String>,
monitor_password: Option<String>,
monitor_secret: Option<String>,
}
pub struct MonitorClient {
reqwest: reqwest::Client,
address: String,
token: String,
jwt: String,
}
impl MonitorClient {
@@ -9,9 +25,126 @@ impl MonitorClient {
MonitorClient {
reqwest: Default::default(),
address: address.into(),
token: token.into(),
jwt: token.into(),
}
}
pub fn new_with_credentials() {}
pub async fn new_with_password(
address: impl Into<String>,
username: impl Into<String>,
password: impl Into<String>,
) -> anyhow::Result<MonitorClient> {
let mut client = MonitorClient {
reqwest: Default::default(),
address: address.into(),
jwt: Default::default(),
};
let LoginLocalUserResponse { jwt } = client
.auth(auth::LoginLocalUser {
username: username.into(),
password: password.into(),
})
.await?;
client.jwt = jwt;
Ok(client)
}
pub async fn new_with_secret(
address: impl Into<String>,
username: impl Into<String>,
secret: impl Into<String>,
) -> anyhow::Result<MonitorClient> {
let mut client = MonitorClient {
reqwest: Default::default(),
address: address.into(),
jwt: Default::default(),
};
let LoginWithSecretResponse { jwt } = client
.auth(auth::LoginWithSecret {
username: username.into(),
secret: secret.into(),
})
.await?;
client.jwt = jwt;
Ok(client)
}
pub async fn new_from_env() -> anyhow::Result<MonitorClient> {
let env = envy::from_env::<MonitorEnv>()
.context("failed to parse environment for monitor client")?;
if let Some(token) = env.monitor_token {
Ok(MonitorClient::new_with_token(&env.monitor_url, token))
} else if let Some(password) = env.monitor_password {
let username = env.monitor_username.ok_or(anyhow!(
"must provide MONITOR_USERNAME to authenticate with MONITOR_PASSWORD"
))?;
MonitorClient::new_with_password(&env.monitor_url, username, password).await
} else if let Some(secret) = env.monitor_secret {
let username = env.monitor_username.ok_or(anyhow!(
"must provide MONITOR_USERNAME to authenticate with MONITOR_SECRET"
))?;
MonitorClient::new_with_secret(&env.monitor_url, username, secret).await
} else {
Err(anyhow!("failed to initialize monitor client from env | must provide one of: (MONITOR_TOKEN), (MONITOR_USERNAME and MONITOR_PASSWORD), (MONITOR_USERNAME and MONITOR_SECRET)"))
}
}
pub async fn api<T: HasResponse>(&self, request: T) -> anyhow::Result<T::Response> {
let req_type = T::req_type();
self.post(
"/api",
json!({
"type": req_type,
"params": request
}),
)
.await
}
pub async fn auth<T: HasResponse>(&self, request: T) -> anyhow::Result<T::Response> {
let req_type = T::req_type();
self.post(
"/auth",
json!({
"type": req_type,
"params": request
}),
)
.await
}
async fn post<B: Serialize, R: DeserializeOwned>(
&self,
endpoint: &str,
body: impl Into<Option<B>>,
) -> anyhow::Result<R> {
let req = self
.reqwest
.post(format!("{}{endpoint}", self.address))
.header("Authorization", format!("Bearer {}", self.jwt));
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:#?}")),
}
}
}
}