diff --git a/Cargo.lock b/Cargo.lock index 33b7e7a..3fb2746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3591,7 +3591,7 @@ dependencies = [ [[package]] name = "sunbeam-sdk" -version = "0.9.0" +version = "0.10.0" dependencies = [ "base64", "bytes", diff --git a/sunbeam-sdk/Cargo.toml b/sunbeam-sdk/Cargo.toml index 6d4b474..3dcebbb 100644 --- a/sunbeam-sdk/Cargo.toml +++ b/sunbeam-sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sunbeam-sdk" -version = "0.10.0" +version = "0.11.0" edition = "2024" description = "Sunbeam SDK — reusable library for cluster management" repository = "https://src.sunbeam.pt/studio/cli" diff --git a/sunbeam-sdk/src/lasuite/calendars.rs b/sunbeam-sdk/src/lasuite/calendars.rs new file mode 100644 index 0000000..36aff69 --- /dev/null +++ b/sunbeam-sdk/src/lasuite/calendars.rs @@ -0,0 +1,191 @@ +//! Calendars service client — calendars, events, RSVP. + +use crate::client::{AuthMethod, HttpTransport, ServiceClient}; +use crate::error::Result; +use reqwest::Method; +use super::types::*; + +/// Client for the La Suite Calendars API. +pub struct CalendarsClient { + pub(crate) transport: HttpTransport, +} + +impl ServiceClient for CalendarsClient { + fn service_name(&self) -> &'static str { + "calendars" + } + + fn base_url(&self) -> &str { + &self.transport.base_url + } + + fn from_parts(base_url: String, auth: AuthMethod) -> Self { + Self { + transport: HttpTransport::new(&base_url, auth), + } + } +} + +impl CalendarsClient { + /// Build a CalendarsClient from domain (e.g. `https://calendar.{domain}/api/v1.0`). + pub fn connect(domain: &str) -> Self { + let base_url = format!("https://calendar.{domain}/api/v1.0"); + Self::from_parts(base_url, AuthMethod::Bearer(String::new())) + } + + /// Set the bearer token for authentication. + pub fn with_token(mut self, token: &str) -> Self { + self.transport.set_auth(AuthMethod::Bearer(token.to_string())); + self + } + + // -- Calendars ---------------------------------------------------------- + + /// List calendars. + pub async fn list_calendars(&self) -> Result> { + self.transport + .json( + Method::GET, + "calendars/", + Option::<&()>::None, + "calendars list calendars", + ) + .await + } + + /// Get a single calendar by ID. + pub async fn get_calendar(&self, id: &str) -> Result { + self.transport + .json( + Method::GET, + &format!("calendars/{id}/"), + Option::<&()>::None, + "calendars get calendar", + ) + .await + } + + /// Create a new calendar. + pub async fn create_calendar(&self, body: &serde_json::Value) -> Result { + self.transport + .json(Method::POST, "calendars/", Some(body), "calendars create calendar") + .await + } + + // -- Events ------------------------------------------------------------- + + /// List events in a calendar. + pub async fn list_events(&self, calendar_id: &str) -> Result> { + self.transport + .json( + Method::GET, + &format!("calendars/{calendar_id}/events/"), + Option::<&()>::None, + "calendars list events", + ) + .await + } + + /// Get a single event. + pub async fn get_event( + &self, + calendar_id: &str, + event_id: &str, + ) -> Result { + self.transport + .json( + Method::GET, + &format!("calendars/{calendar_id}/events/{event_id}/"), + Option::<&()>::None, + "calendars get event", + ) + .await + } + + /// Create a new event in a calendar. + pub async fn create_event( + &self, + calendar_id: &str, + body: &serde_json::Value, + ) -> Result { + self.transport + .json( + Method::POST, + &format!("calendars/{calendar_id}/events/"), + Some(body), + "calendars create event", + ) + .await + } + + /// Update an event (partial). + pub async fn update_event( + &self, + calendar_id: &str, + event_id: &str, + body: &serde_json::Value, + ) -> Result { + self.transport + .json( + Method::PATCH, + &format!("calendars/{calendar_id}/events/{event_id}/"), + Some(body), + "calendars update event", + ) + .await + } + + /// Delete an event. + pub async fn delete_event( + &self, + calendar_id: &str, + event_id: &str, + ) -> Result<()> { + self.transport + .send( + Method::DELETE, + &format!("calendars/{calendar_id}/events/{event_id}/"), + Option::<&()>::None, + "calendars delete event", + ) + .await + } + + /// RSVP to an event. + pub async fn rsvp( + &self, + calendar_id: &str, + event_id: &str, + body: &serde_json::Value, + ) -> Result<()> { + self.transport + .send( + Method::POST, + &format!("calendars/{calendar_id}/events/{event_id}/rsvp/"), + Some(body), + "calendars rsvp", + ) + .await + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_connect_url() { + let c = CalendarsClient::connect("sunbeam.pt"); + assert_eq!(c.base_url(), "https://calendar.sunbeam.pt/api/v1.0"); + assert_eq!(c.service_name(), "calendars"); + } + + #[test] + fn test_from_parts() { + let c = CalendarsClient::from_parts( + "http://localhost:8000/api/v1.0".into(), + AuthMethod::Bearer("tok".into()), + ); + assert_eq!(c.base_url(), "http://localhost:8000/api/v1.0"); + } +} diff --git a/sunbeam-sdk/src/lasuite/docs.rs b/sunbeam-sdk/src/lasuite/docs.rs new file mode 100644 index 0000000..239236e --- /dev/null +++ b/sunbeam-sdk/src/lasuite/docs.rs @@ -0,0 +1,170 @@ +//! Docs service client — documents, templates, versions, invitations. + +use crate::client::{AuthMethod, HttpTransport, ServiceClient}; +use crate::error::Result; +use reqwest::Method; +use super::types::*; + +/// Client for the La Suite Docs API. +pub struct DocsClient { + pub(crate) transport: HttpTransport, +} + +impl ServiceClient for DocsClient { + fn service_name(&self) -> &'static str { + "docs" + } + + fn base_url(&self) -> &str { + &self.transport.base_url + } + + fn from_parts(base_url: String, auth: AuthMethod) -> Self { + Self { + transport: HttpTransport::new(&base_url, auth), + } + } +} + +impl DocsClient { + /// Build a DocsClient from domain (e.g. `https://docs.{domain}/api/v1.0`). + pub fn connect(domain: &str) -> Self { + let base_url = format!("https://docs.{domain}/api/v1.0"); + Self::from_parts(base_url, AuthMethod::Bearer(String::new())) + } + + /// Set the bearer token for authentication. + pub fn with_token(mut self, token: &str) -> Self { + self.transport.set_auth(AuthMethod::Bearer(token.to_string())); + self + } + + // -- Documents ---------------------------------------------------------- + + /// List documents with optional pagination. + pub async fn list_documents(&self, page: Option) -> Result> { + let path = match page { + Some(p) => format!("documents/?page={p}"), + None => "documents/".to_string(), + }; + self.transport + .json(Method::GET, &path, Option::<&()>::None, "docs list documents") + .await + } + + /// Get a single document by ID. + pub async fn get_document(&self, id: &str) -> Result { + self.transport + .json( + Method::GET, + &format!("documents/{id}/"), + Option::<&()>::None, + "docs get document", + ) + .await + } + + /// Create a new document. + pub async fn create_document(&self, body: &serde_json::Value) -> Result { + self.transport + .json(Method::POST, "documents/", Some(body), "docs create document") + .await + } + + /// Update a document (partial). + pub async fn update_document(&self, id: &str, body: &serde_json::Value) -> Result { + self.transport + .json( + Method::PATCH, + &format!("documents/{id}/"), + Some(body), + "docs update document", + ) + .await + } + + /// Delete a document. + pub async fn delete_document(&self, id: &str) -> Result<()> { + self.transport + .send( + Method::DELETE, + &format!("documents/{id}/"), + Option::<&()>::None, + "docs delete document", + ) + .await + } + + // -- Templates ---------------------------------------------------------- + + /// List templates with optional pagination. + pub async fn list_templates(&self, page: Option) -> Result> { + let path = match page { + Some(p) => format!("templates/?page={p}"), + None => "templates/".to_string(), + }; + self.transport + .json(Method::GET, &path, Option::<&()>::None, "docs list templates") + .await + } + + /// Create a new template. + pub async fn create_template(&self, body: &serde_json::Value) -> Result