From 8e35b8466b9b9c757fb193c97934c8a426341565 Mon Sep 17 00:00:00 2001 From: Vincent Stuyck Date: Sat, 20 Dec 2025 00:40:34 +0100 Subject: [PATCH] add actual actions to the client --- Cargo.lock | 255 ++++++++++++++++++++++++++++++++----------------- Cargo.toml | 5 +- src/api/mod.rs | 64 ++++++++++++- src/client.rs | 116 +++++++++++++++++++++- 4 files changed, 344 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e57e62e..5de6786 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,19 +63,22 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "checkmk-api" version = "0.1.0" dependencies = [ - "base64", - "bytes", "chrono", "log", "reqwest", "secrecy", "serde", "serde_json", - "simplelog", "thiserror", "tokio", ] @@ -110,15 +113,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -243,8 +237,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -254,9 +250,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -359,6 +357,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots", ] [[package]] @@ -604,6 +603,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.7.6" @@ -644,12 +649,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-traits" version = "0.2.19" @@ -659,15 +658,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -775,10 +765,13 @@ dependencies = [ ] [[package]] -name = "powerfmt" -version = "0.2.0" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" @@ -789,6 +782,61 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" version = "1.0.42" @@ -804,6 +852,35 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -837,6 +914,8 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", @@ -844,6 +923,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -851,6 +931,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", ] [[package]] @@ -867,6 +948,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "1.1.2" @@ -887,6 +974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -899,6 +987,7 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ + "web-time", "zeroize", ] @@ -1043,17 +1132,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simplelog" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0" -dependencies = [ - "log", - "termcolor", - "time", -] - [[package]] name = "slab" version = "0.4.11" @@ -1153,15 +1231,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "2.0.17" @@ -1182,39 +1251,6 @@ dependencies = [ "syn", ] -[[package]] -name = "time" -version = "0.3.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" -dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" - -[[package]] -name = "time-macros" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -1225,6 +1261,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.48.0" @@ -1485,12 +1536,22 @@ dependencies = [ ] [[package]] -name = "winapi-util" -version = "0.1.11" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ - "windows-sys 0.61.2", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", ] [[package]] @@ -1754,6 +1815,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index 465dc5f..04d4b58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,14 +7,11 @@ authors = [ ] [dependencies] -base64 = "0.22.1" -bytes = "1.11.0" chrono = { version = "0.4.42", features = ["serde"] } log = "0.4.29" -reqwest = "0.12.26" +reqwest = { version = "0.12.26", features = ["json", "rustls-tls"] } secrecy = { version = "0.10.3", features = ["serde"] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" -simplelog = "0.12.2" thiserror = "2.0.17" tokio = { version = "1.48.0", features = ["full"] } diff --git a/src/api/mod.rs b/src/api/mod.rs index e9bedce..4742522 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,6 @@ use std::fmt; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] @@ -25,3 +25,65 @@ impl fmt::Display for DomainType { } } } + + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum HttpMethod { + Get, + Put, + Post, + Delete, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub(crate) struct RelationLink { + #[serde(rename = "domainType")] + pub domain_type: DomainType, + pub rel: String, + pub href: String, + pub method: HttpMethod, + pub r#type: String, + #[serde(default)] + pub title: Option, + // body_params: HashMap // i will deal with this later +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct DomainObject { + pub domain_type: DomainType, + pub instance_id: String, + #[serde(default)] + pub title: Option, + pub extensions: E, + // not used in this library + // #[serde(default)] + // pub links: Vec, + // deprecated in favour of links + // pub members: M +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct DomainCollection { + pub domain_type: DomainType, + pub instance_id: String, + #[serde(default)] + pub title: Option, + pub value: Vec>, + // not used in this library + // #[serde(default)] + // pub links: Vec, + // pub extensions: Option, +} + +pub(crate) trait DomainExtention: DeserializeOwned + Serialize { + const DOMAIN_TYPE: DomainType; + + type CreationRequest: Serialize; + type CreationQuery: Serialize; + type ReadQuery: Serialize; + type UpdateRequest: Serialize; + type DeleteQuery: Serialize; +} diff --git a/src/client.rs b/src/client.rs index cc08f60..97a5754 100644 --- a/src/client.rs +++ b/src/client.rs @@ -9,7 +9,7 @@ use reqwest::header::{HeaderName, HeaderValue}; use serde::de::DeserializeOwned; use tokio::sync::Semaphore; -use crate::api::DomainType; +use crate::api::{DomainCollection, DomainExtention, DomainObject, DomainType}; use crate::{Error, Result}; @@ -205,7 +205,6 @@ impl InnerClient { Err(Error::CheckmkError(cmkerror)) } } - pub(crate) async fn invoke_api(&self, request: Request) -> Result<()> { let _response = self.handle_request(request).await?; Ok(()) @@ -215,9 +214,9 @@ impl InnerClient { serde_json::from_str(&response).map_err(Error::DeserializeResponse) } - pub async fn get_etag(&self, domain_type: DomainType, id: &str) -> Result { + pub(crate) async fn get_etag(&self, object_url: &str) -> Result { let mut response = self.http - .head(self.object_url(domain_type, id)) + .head(object_url) .send() .await .map_err(Error::SendRequest)?; @@ -235,6 +234,115 @@ impl InnerClient { Err(Error::CheckmkError(cmkerror)) } } + + pub(crate) async fn create_domain_object( + &self, + body: &E::CreationRequest, + query: &E::CreationQuery + ) -> Result<()> { + let request = self.http + .post(self.collection_url(E::DOMAIN_TYPE)) + .json(body) + .query(query) + .build() + .unwrap(); + + self.invoke_api(request).await + } + pub(crate) async fn read_domain_object( + &self, + id: impl Display, + query: &E::ReadQuery + ) -> Result> { + let request = self.http + .get(self.object_url(E::DOMAIN_TYPE, id)) + .query(query) + .build() + .unwrap(); + + self.query_api(request).await + } + pub(crate) async fn update_domain_object( + &self, + id: impl Display, + request: &E::UpdateRequest, + ) -> Result<()> { + let url = self.object_url(E::DOMAIN_TYPE, id); + let etag = self.get_etag(&url).await?; + let request = self.http + .put(url) + .json(&request) + .header(reqwest::header::IF_MATCH, etag) + .build() + .unwrap(); + + self.invoke_api(request).await + } + pub(crate) async fn delete_domain_object( + &self, + id: impl Display, + params: &E::DeleteQuery, + ) -> Result<()> { + let request = self.http + .delete(self.object_url(E::DOMAIN_TYPE, id)) + .query(¶ms) + .build() + .unwrap(); + + self.invoke_api(request).await + } + + pub(crate) async fn bulk_create_domain_objects( + &self, + body: &[E::CreationRequest], + query: &E::CreationQuery + ) -> Result<()> { + let request = self.http + .post(self.bulk_action_url(E::DOMAIN_TYPE, BulkAction::Create)) + .json(body) + .query(query) + .build() + .unwrap(); + + self.invoke_api(request).await + } + pub(crate) async fn bulk_read_domain_objects( + &self, + query: &E::ReadQuery, + ) -> Result>> { + let request = self.http + .get(self.collection_url(E::DOMAIN_TYPE)) + .query(query) + .build() + .unwrap(); + + let response: DomainCollection = self.query_api(request).await?; + Ok(response.value) + } + pub(crate) async fn bulk_update_domain_objects( + &self, + body: &[E::UpdateRequest], + ) -> Result<()> { + let request = self.http + .put(self.bulk_action_url(E::DOMAIN_TYPE, BulkAction::Update)) + .json(body) + .build() + .unwrap(); + + self.invoke_api(request).await + } + pub(crate) async fn bulk_delete_domain_objects( + &self, + ids: &[String], + ) -> Result<()> { + let request = self.http + .post(self.bulk_action_url(E::DOMAIN_TYPE, BulkAction::Delete)) + .json(&ids) + .build() + .unwrap(); + + self.invoke_api(request).await + } } pub(crate) enum BulkAction {