All checks were successful
Build Container / Build Container (push) Successful in 4m17s
- better CLI parameter parsing - an actual help message - a new command to list currently known locks
78 lines
3.2 KiB
Rust
78 lines
3.2 KiB
Rust
use eyre::Context;
|
|
use eyre::eyre;
|
|
use reqwest::Url;
|
|
use reqwest::header::HeaderValue;
|
|
|
|
use crate::api_models::ApiLock;
|
|
use crate::api_models::DesiredLockState;
|
|
use crate::api_models::LockOperationRequest;
|
|
|
|
/// Client for interacting with the dooris API
|
|
pub struct ApiClient {
|
|
client: reqwest::Client,
|
|
authorization: HeaderValue,
|
|
server_url: Url,
|
|
}
|
|
|
|
impl ApiClient {
|
|
pub fn new_from_env() -> eyre::Result<Self> {
|
|
let client = reqwest::Client::builder()
|
|
.user_agent("dooris-ssh-cli")
|
|
.redirect(reqwest::redirect::Policy::none())
|
|
.http1_title_case_headers()
|
|
.build()
|
|
.expect("Could not construct http client even though all values are statically known to be valid");
|
|
|
|
let api_token = std::env::var("DOORIS_API_TOKEN").map_err(|_| eyre!("This ssh-cli is not configured correctly and does not have it's DOORIS_API_TOKEN environment variable set correctly. Contact your nearest server maid to fix the dooris setup."))?;
|
|
let api_token = format!("Static-Token {api_token}")
|
|
.try_into()
|
|
.context("Could not convert HTTP API Token to a valid HTTP header")?;
|
|
|
|
let server_url = std::env::var("DOORIS_SERVER_URL").map_err(|_| eyre!("This ssh-cli is not configured correctly and does not have it's DOORIS_SERVER_URL environment variable set correctly. Contact your nearest server maid to fix the dooris setup."))?;
|
|
let server_url = Url::parse(&server_url).context("Configured DOORIS_SERVER_URL is not a valid URL. Contact your nearest server maid to fix the dooris setup")?;
|
|
|
|
tracing::debug!("Client is connected to dooris at {server_url}");
|
|
Ok(Self {
|
|
client,
|
|
authorization: api_token,
|
|
server_url,
|
|
})
|
|
}
|
|
|
|
/// Fetch the current list of locks along with their state from the API
|
|
pub async fn fetch_locks(&self) -> eyre::Result<Vec<ApiLock>> {
|
|
tracing::debug!("Fetching locks from dooris API");
|
|
let url = self.server_url.join("api/locks/")?;
|
|
self.client
|
|
.get(url)
|
|
.header(reqwest::header::AUTHORIZATION, self.authorization.clone())
|
|
.send()
|
|
.await?
|
|
.error_for_status()
|
|
.context("Could not get list of locks from the dooris API")?
|
|
.json()
|
|
.await
|
|
.context("Could not parse list-locks response from dooris API")
|
|
}
|
|
|
|
/// Put the lock identified by `lock_id` into the given desired state.
|
|
///
|
|
/// Returns as soon as the operation has been handed of to the backend and does not wait for physical lock movements.
|
|
pub async fn operate_lock(
|
|
&self,
|
|
lock_id: &str,
|
|
desired_state: DesiredLockState,
|
|
) -> eyre::Result<()> {
|
|
tracing::debug!("Calling API to put {lock_id:?} into state {desired_state:?}");
|
|
let url = self.server_url.join(&format!("api/locks/{lock_id}"))?;
|
|
self.client
|
|
.patch(url)
|
|
.header(reqwest::header::AUTHORIZATION, self.authorization.clone())
|
|
.json(&LockOperationRequest { desired_state })
|
|
.send()
|
|
.await?
|
|
.error_for_status()
|
|
.context("Could not operate lock")?;
|
|
Ok(())
|
|
}
|
|
}
|