use std::fmt::Debug;
use reqwest::{Method, RequestBuilder};
use thiserror::Error;
use url::Url;
mod alerts;
mod watches;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("reqwest: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("serde: {0}")]
Serde(#[from] serde_json::Error),
#[error("object owner id does not match. Expected {expected}, was {was}")]
OwnerRefMismatch { expected: String, was: String },
#[error("Invalid URL: {0}")]
UrlParse(#[from] url::ParseError),
}
#[derive(Clone)]
pub struct ApiClient {
host: Url,
api_url: Url,
api_token: String,
owner_ref: String,
client: reqwest::Client,
}
impl ApiClient {
pub fn new(host: Url, api_token: String, owner_ref: String) -> Result<ApiClient> {
ApiClient::with_reqwest(host, api_token, owner_ref, reqwest::Client::new())
}
pub fn with_reqwest(
host: Url,
api_token: String,
owner_ref: String,
client: reqwest::Client,
) -> Result<ApiClient> {
Ok(ApiClient {
host: host.clone(),
api_url: host.join("api/v1alpha1/")?,
api_token,
owner_ref,
client,
})
}
pub fn api_url(&self) -> &Url {
&self.api_url
}
pub fn owner_ref(&self) -> &str {
&self.owner_ref
}
fn request(&self, method: Method, path: &str) -> RequestBuilder {
#[allow(clippy::expect_used)]
self.client
.request(
method,
self.api_url
.join(path)
.expect("api resource path should be valid"),
)
.bearer_auth(&self.api_token)
}
pub async fn check_health(&self) -> Result<()> {
#[allow(clippy::expect_used)]
let _ = self
.client
.get(
self.host
.join("health")
.expect("health url path should be valid"),
)
.send()
.await?
.error_for_status()?;
Ok(())
}
}
impl Debug for ApiClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self {
host,
api_url,
api_token: _api_token,
owner_ref,
client,
} = self;
f.debug_struct("ApiClient")
.field("host", host)
.field("api_url", api_url)
.field("api_token", &&"XXXXXXXX")
.field("owner_ref", owner_ref)
.field("client", client)
.finish()
}
}
#[cfg(test)]
mod test {
use super::*;
use std::str::FromStr;
use url::Url;
#[tokio::test]
pub async fn parses_to_api_url() -> anyhow::Result<()> {
let host = Url::from_str("http://somehost:1234")?;
let client = ApiClient::new(host, "invalid".to_string(), "invalid".to_string())?;
assert_eq!(
client.api_url().to_string(),
"http://somehost:1234/api/v1alpha1/"
);
Ok(())
}
#[tokio::test]
pub async fn parses_to_api_url_trailing_slash() -> anyhow::Result<()> {
let host = Url::from_str("http://somehost:1234/")?;
let client = ApiClient::new(host, "invalid".to_string(), "invalid".to_string())?;
assert_eq!(
client.api_url().to_string(),
"http://somehost:1234/api/v1alpha1/"
);
Ok(())
}
#[tokio::test]
pub async fn parses_to_api_url_subpath() -> anyhow::Result<()> {
let host = Url::from_str("http://somehost:1234/subpath/")?;
let client = ApiClient::new(host, "invalid".to_string(), "invalid".to_string())?;
assert_eq!(
client.api_url().to_string(),
"http://somehost:1234/subpath/api/v1alpha1/"
);
Ok(())
}
}