From c35ea620b36ff1f2776b9a0f8ccbdffb72369e9c Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 25 Mar 2026 02:18:58 -0400 Subject: [PATCH] add rootly stub --- Cargo.lock | 16 ++++++++ Cargo.toml | 2 + crates/api_clients/rootly_client/Cargo.toml | 31 ++++++++++++++ .../rootly_client/src/creds/mod.rs | 1 + .../rootly_client/src/creds/rootly_api_key.rs | 11 +++++ .../rootly_client/src/error/mod.rs | 4 ++ .../src/error/rootly_client_error.rs | 22 ++++++++++ .../rootly_client/src/error/rootly_error.rs | 41 +++++++++++++++++++ .../src/error/rootly_generic_api_error.rs | 35 ++++++++++++++++ .../src/error/rootly_specific_api_error.rs | 18 ++++++++ crates/api_clients/rootly_client/src/lib.rs | 7 ++++ .../rootly_client/src/requests/mod.rs | 1 + .../src/test_utils/get_test_api_key.rs | 9 ++++ .../rootly_client/src/test_utils/mod.rs | 2 + 14 files changed, 200 insertions(+) create mode 100644 crates/api_clients/rootly_client/Cargo.toml create mode 100644 crates/api_clients/rootly_client/src/creds/mod.rs create mode 100644 crates/api_clients/rootly_client/src/creds/rootly_api_key.rs create mode 100644 crates/api_clients/rootly_client/src/error/mod.rs create mode 100644 crates/api_clients/rootly_client/src/error/rootly_client_error.rs create mode 100644 crates/api_clients/rootly_client/src/error/rootly_error.rs create mode 100644 crates/api_clients/rootly_client/src/error/rootly_generic_api_error.rs create mode 100644 crates/api_clients/rootly_client/src/error/rootly_specific_api_error.rs create mode 100644 crates/api_clients/rootly_client/src/lib.rs create mode 100644 crates/api_clients/rootly_client/src/requests/mod.rs create mode 100644 crates/api_clients/rootly_client/src/test_utils/get_test_api_key.rs create mode 100644 crates/api_clients/rootly_client/src/test_utils/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 85f403bdf2..1bfc3deabf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9584,6 +9584,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rootly_client" +version = "0.0.1" +dependencies = [ + "env", + "env_logger 0.11.9", + "errors", + "log", + "reqwest 0.12.23", + "serde", + "serde_derive", + "serde_json", + "tokio", + "workspace-build-acceleration", +] + [[package]] name = "rsa" version = "0.9.6" diff --git a/Cargo.toml b/Cargo.toml index a40520a568..6a6257dc38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "crates/api_clients/muapi_client", "crates/api_clients/openai_sora_client", "crates/api_clients/openart_client", + "crates/api_clients/rootly_client", "crates/api_clients/runwayml_client", "crates/api_clients/seedance2pro_client", "crates/api_clients/thumbnail_generator", @@ -239,6 +240,7 @@ ffmpeg_utils = { path = "crates/lib/ffmpeg_utils" } file_to_base64_url = { path = "crates/lib/files/file_to_base64_url" } filesys = { path = "crates/lib/files/filesys" } grok_client = { path = "crates/api_clients/grok_client" } +rootly_client = { path = "crates/api_clients/rootly_client" } http_headers = { path = "crates/lib/http_headers" } images = { path = "crates/lib/files/images" } jobs_common = { path = "crates/lib/jobs_common" } diff --git a/crates/api_clients/rootly_client/Cargo.toml b/crates/api_clients/rootly_client/Cargo.toml new file mode 100644 index 0000000000..75368085f9 --- /dev/null +++ b/crates/api_clients/rootly_client/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "rootly_client" +edition = "2024" +version = "0.0.1" +publish = false + +[package.metadata.cargo-udeps.ignore] +normal = ["workspace-build-acceleration"] + +[lib] +name = "rootly_client" +path = "src/lib.rs" + +[dependencies] +# Workspace hack (faster compile times) +workspace-build-acceleration.workspace = true + +# Internal +errors.workspace = true + +# External +log.workspace = true +reqwest.workspace = true +serde.workspace = true +serde_derive.workspace = true +serde_json.workspace = true + +[dev-dependencies] +env.workspace = true +env_logger.workspace = true +tokio = { version = "1", features = ["full"] } diff --git a/crates/api_clients/rootly_client/src/creds/mod.rs b/crates/api_clients/rootly_client/src/creds/mod.rs new file mode 100644 index 0000000000..d1ff838f3a --- /dev/null +++ b/crates/api_clients/rootly_client/src/creds/mod.rs @@ -0,0 +1 @@ +pub mod rootly_api_key; diff --git a/crates/api_clients/rootly_client/src/creds/rootly_api_key.rs b/crates/api_clients/rootly_client/src/creds/rootly_api_key.rs new file mode 100644 index 0000000000..26d13de483 --- /dev/null +++ b/crates/api_clients/rootly_client/src/creds/rootly_api_key.rs @@ -0,0 +1,11 @@ +/// Holds the API key for authenticating with the Rootly API. +#[derive(Clone)] +pub struct RootlyApiKey { + pub (crate) api_key: String, +} + +impl RootlyApiKey { + pub fn new(api_key: String) -> Self { + Self { api_key } + } +} diff --git a/crates/api_clients/rootly_client/src/error/mod.rs b/crates/api_clients/rootly_client/src/error/mod.rs new file mode 100644 index 0000000000..a6bbc571f3 --- /dev/null +++ b/crates/api_clients/rootly_client/src/error/mod.rs @@ -0,0 +1,4 @@ +pub mod rootly_client_error; +pub mod rootly_error; +pub mod rootly_generic_api_error; +pub mod rootly_specific_api_error; diff --git a/crates/api_clients/rootly_client/src/error/rootly_client_error.rs b/crates/api_clients/rootly_client/src/error/rootly_client_error.rs new file mode 100644 index 0000000000..d488644493 --- /dev/null +++ b/crates/api_clients/rootly_client/src/error/rootly_client_error.rs @@ -0,0 +1,22 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum RootlyClientError { + /// No API key is present. + NoApiKeyPresent, + + /// An error was encountered in building the reqwest client. + ReqwestClientError(reqwest::Error), +} + +impl Error for RootlyClientError {} + +impl Display for RootlyClientError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::NoApiKeyPresent => write!(f, "No API key present."), + Self::ReqwestClientError(err) => write!(f, "Reqwest client error (during client creation): {}", err), + } + } +} diff --git a/crates/api_clients/rootly_client/src/error/rootly_error.rs b/crates/api_clients/rootly_client/src/error/rootly_error.rs new file mode 100644 index 0000000000..84f966fa30 --- /dev/null +++ b/crates/api_clients/rootly_client/src/error/rootly_error.rs @@ -0,0 +1,41 @@ +use crate::error::rootly_client_error::RootlyClientError; +use crate::error::rootly_generic_api_error::RootlyGenericApiError; +use crate::error::rootly_specific_api_error::RootlySpecificApiError; +use std::error::Error; + +#[derive(Debug)] +pub enum RootlyError { + Client(RootlyClientError), + ApiSpecific(RootlySpecificApiError), + ApiGeneric(RootlyGenericApiError), +} + +impl Error for RootlyError {} + +impl std::fmt::Display for RootlyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Client(e) => write!(f, "RootlyClientError: {:?}", e), + Self::ApiSpecific(e) => write!(f, "RootlySpecificApiError: {:?}", e), + Self::ApiGeneric(e) => write!(f, "RootlyGenericApiError: {:?}", e), + } + } +} + +impl From for RootlyError { + fn from(error: RootlyClientError) -> Self { + Self::Client(error) + } +} + +impl From for RootlyError { + fn from(error: RootlySpecificApiError) -> Self { + Self::ApiSpecific(error) + } +} + +impl From for RootlyError { + fn from(error: RootlyGenericApiError) -> Self { + Self::ApiGeneric(error) + } +} diff --git a/crates/api_clients/rootly_client/src/error/rootly_generic_api_error.rs b/crates/api_clients/rootly_client/src/error/rootly_generic_api_error.rs new file mode 100644 index 0000000000..8d26ddf30a --- /dev/null +++ b/crates/api_clients/rootly_client/src/error/rootly_generic_api_error.rs @@ -0,0 +1,35 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; +use reqwest::StatusCode; + +#[derive(Debug)] +pub enum RootlyGenericApiError { + /// serde_json::Error, likely from JSON deserialization schema mismatch. + /// Includes the original body. + SerdeResponseParseErrorWithBody(serde_json::Error, String), + + /// An uncategorized bad HTTP response. + UncategorizedBadResponse(String), + + /// An uncategorized bad HTTP response with status code and body. + UncategorizedBadResponseWithStatusAndBody { + status_code: StatusCode, + body: String, + }, + + /// An uncaught error from the HTTP client. + ReqwestError(reqwest::Error), +} + +impl Error for RootlyGenericApiError {} + +impl Display for RootlyGenericApiError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::SerdeResponseParseErrorWithBody(err, body) => write!(f, "Failed to parse response body: {:?}. Body: {}", err, body), + Self::UncategorizedBadResponse(msg) => write!(f, "Uncategorized bad response: {}", msg), + Self::UncategorizedBadResponseWithStatusAndBody { status_code, body } => write!(f, "Uncategorized bad response: status code {}, body: {}", status_code, body), + Self::ReqwestError(err) => write!(f, "Reqwest client error: {}", err), + } + } +} diff --git a/crates/api_clients/rootly_client/src/error/rootly_specific_api_error.rs b/crates/api_clients/rootly_client/src/error/rootly_specific_api_error.rs new file mode 100644 index 0000000000..83a8692f25 --- /dev/null +++ b/crates/api_clients/rootly_client/src/error/rootly_specific_api_error.rs @@ -0,0 +1,18 @@ +use std::error::Error; +use std::fmt::{Display, Formatter}; + +#[derive(Debug)] +pub enum RootlySpecificApiError { + /// The API key is invalid or expired. + Unauthorized, +} + +impl Error for RootlySpecificApiError {} + +impl Display for RootlySpecificApiError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Unauthorized => write!(f, "Unauthorized: API key is invalid or expired."), + } + } +} diff --git a/crates/api_clients/rootly_client/src/lib.rs b/crates/api_clients/rootly_client/src/lib.rs new file mode 100644 index 0000000000..026f2a5b0f --- /dev/null +++ b/crates/api_clients/rootly_client/src/lib.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +pub (crate) mod test_utils; + +// Public API +pub mod creds; +pub mod error; +pub mod requests; diff --git a/crates/api_clients/rootly_client/src/requests/mod.rs b/crates/api_clients/rootly_client/src/requests/mod.rs new file mode 100644 index 0000000000..4e4cf828e7 --- /dev/null +++ b/crates/api_clients/rootly_client/src/requests/mod.rs @@ -0,0 +1 @@ +// Endpoint request implementations will go here. diff --git a/crates/api_clients/rootly_client/src/test_utils/get_test_api_key.rs b/crates/api_clients/rootly_client/src/test_utils/get_test_api_key.rs new file mode 100644 index 0000000000..b8b6712727 --- /dev/null +++ b/crates/api_clients/rootly_client/src/test_utils/get_test_api_key.rs @@ -0,0 +1,9 @@ +use errors::AnyhowResult; +use std::fs::read_to_string; + +#[cfg(test)] +pub fn get_test_api_key() -> AnyhowResult { + let api_key = read_to_string("/Users/bt/Artcraft/credentials/rootly_api_key.txt")?; + let api_key = api_key.trim().to_string(); + Ok(api_key) +} diff --git a/crates/api_clients/rootly_client/src/test_utils/mod.rs b/crates/api_clients/rootly_client/src/test_utils/mod.rs new file mode 100644 index 0000000000..9bcbd2bb64 --- /dev/null +++ b/crates/api_clients/rootly_client/src/test_utils/mod.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +pub (crate) mod get_test_api_key;