use anyhow::anyhow; use error::UbisyncError; use reqwest::{Client, StatusCode}; use tracing::debug; pub use ubisync_lib::*; use ubisync_lib::{ api::{ app::{AppCreatePotRequest, AppRegisterRequest, AppRegisterResponse}, UbisyncRequest, }, types::AppId, }; pub mod error; pub struct UbisyncClient { host: String, port: u16, selected_api_version: String, base_url: String, registration: AppRegistration, reqwest_client: Client, } pub struct AppRegistration { pub jwt_token: String, pub app_id: AppId, } impl UbisyncClient { pub async fn init( host: &str, port: u16, registration: Option, application_name: &str, application_description: &str, application_type: &str, ) -> Result { let http_client = Client::new(); let mut node_api_versions = http_client .get(&format!("http://{}:{}/versions", host, port)) .send() .await .expect("Failed to contact ubisync node, it may be offline.") .json::>() .await .expect("Failed to read ubisync node's available API versions."); node_api_versions.sort(); let selected_version = node_api_versions .get(0) .expect("No available API version returned by ubisync node"); let registration = match registration { Some(t) => t, None => { let response = http_client .put(Self::build_base_url(host, port, &selected_version) + "/app/register") .json(&AppRegisterRequest { name: application_name.to_string(), description: application_description.to_string(), app_type: application_type.to_string(), }) .send() .await .expect("App registration request failed."); if response.status() != StatusCode::OK { return Err(UbisyncError::AppRegistrationFailed); } let parsed = response .json::() .await .expect("Failed to extract JWT from app regstration request"); AppRegistration { jwt_token: parsed.token, app_id: parsed.app_id, } } }; Ok(UbisyncClient { host: host.to_string(), port: port, selected_api_version: selected_version.to_string(), base_url: Self::build_base_url(host, port, selected_version), registration, reqwest_client: http_client, }) } pub async fn create_default_pot(self) -> anyhow::Result { let response = self .send(AppCreatePotRequest { app_type: None }, ()) .await?; debug!("Created new pot with ID {:?}", response.pot_id); Ok(self) } pub async fn send( &self, request: R, parameters: R::PathParameters, ) -> anyhow::Result where R: UbisyncRequest, { self.reqwest_client .request( request.method(), &(self.base_url.to_owned() + &request.path(parameters)), ) .bearer_auth(&self.registration.jwt_token) .json(&request) .send() .await .map_err(|e| anyhow!(e))? .json::() .await .map_err(|e| anyhow!(e)) } pub async fn set_host(&mut self, host: String) { self.host = host; self.base_url = Self::build_base_url(&self.host, self.port, &self.selected_api_version); } pub async fn set_port(&mut self, port: u16) { self.port = port; self.base_url = Self::build_base_url(&self.host, self.port, &self.selected_api_version); } pub async fn set_api_version(&mut self, version: String) { self.selected_api_version = version; self.base_url = Self::build_base_url(&self.host, self.port, &self.selected_api_version); } pub fn app_id(&self) -> AppId { self.registration.app_id.clone() } fn build_base_url(host: &str, port: u16, api_version: &str) -> String { format!("http://{}:{}/{}", host, port, api_version) } }