improve ssh-cli interaction surface
All checks were successful
Build Container / Build Container (push) Successful in 4m17s
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
This commit is contained in:
parent
fb19ca3840
commit
bc0c1b85ca
5 changed files with 320 additions and 215 deletions
78
ssh-cli/src/api_client.rs
Normal file
78
ssh-cli/src/api_client.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
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(())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue