test and bugfix basic folder crud
This commit is contained in:
parent
ebc349de64
commit
fddc74ef27
93
Cargo.lock
generated
93
Cargo.lock
generated
@ -91,6 +91,7 @@ dependencies = [
|
||||
"secrecy",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simplelog",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@ -125,6 +126,15 @@ 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"
|
||||
@ -661,6 +671,12 @@ 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"
|
||||
@ -670,6 +686,15 @@ 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"
|
||||
@ -776,6 +801,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@ -1144,6 +1175,17 @@ 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"
|
||||
@ -1243,6 +1285,15 @@ 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"
|
||||
@ -1263,6 +1314,39 @@ 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"
|
||||
@ -1566,6 +1650,15 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
|
||||
@ -14,5 +14,6 @@ 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"] }
|
||||
|
||||
@ -4,9 +4,19 @@ use std::fmt::Display;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Client;
|
||||
use crate::api::DomainExtention;
|
||||
use crate::api::rules::contactgroups::HostContactGroups;
|
||||
use crate::api::rules::snmp::SnmpCommunity;
|
||||
use crate::client::ApiClient;
|
||||
|
||||
pub const ROOT_FOLDER: &str = "/";
|
||||
|
||||
impl Client {
|
||||
pub fn folder_api(&self) -> ApiClient<FolderConfig> {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct FolderConfig {
|
||||
@ -137,9 +147,12 @@ pub struct FolderReadQuery;
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct FolderUpdateRequest {
|
||||
pub title: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub attributes: Option<FolderAttributes>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub update_attributes: Option<FolderAttributes>,
|
||||
pub remove_attributes: Option<FolderAttributes>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub remove_attributes: Vec<String>,
|
||||
}
|
||||
|
||||
impl FolderUpdateRequest {
|
||||
@ -148,7 +161,7 @@ impl FolderUpdateRequest {
|
||||
title,
|
||||
attributes: Some(attributes),
|
||||
update_attributes: None,
|
||||
remove_attributes: None,
|
||||
remove_attributes: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn update(title: String, attributes: FolderAttributes) -> Self {
|
||||
@ -156,20 +169,20 @@ impl FolderUpdateRequest {
|
||||
title,
|
||||
attributes: None,
|
||||
update_attributes: Some(attributes),
|
||||
remove_attributes: None,
|
||||
remove_attributes: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn remove(title: String, attributes: FolderAttributes) -> Self {
|
||||
pub fn remove(title: String, attributes: Vec<String>) -> Self {
|
||||
Self {
|
||||
title,
|
||||
attributes: None,
|
||||
update_attributes: None,
|
||||
remove_attributes: Some(attributes),
|
||||
remove_attributes: attributes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub struct FolderDeleteQuery {
|
||||
pub delete_mode: FolderDeleteMode,
|
||||
}
|
||||
@ -180,9 +193,10 @@ impl FolderDeleteQuery {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum FolderDeleteMode {
|
||||
#[default]
|
||||
Recursive,
|
||||
AbotyOnNonEmpty,
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ pub enum HttpMethod {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct DomainObject<E> {
|
||||
pub domain_type: DomainType,
|
||||
pub instance_id: String,
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub title: Option<String>,
|
||||
pub extensions: E,
|
||||
@ -70,7 +70,7 @@ pub(crate) struct DomainObject<E> {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct DomainCollection<E> {
|
||||
pub domain_type: DomainType,
|
||||
pub instance_id: String,
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub title: Option<String>,
|
||||
pub value: Vec<DomainObject<E>>,
|
||||
@ -80,7 +80,7 @@ pub(crate) struct DomainCollection<E> {
|
||||
// pub extensions: Option<E>,
|
||||
}
|
||||
|
||||
pub(crate) trait DomainExtention: DeserializeOwned + Serialize {
|
||||
pub trait DomainExtention: DeserializeOwned + Serialize {
|
||||
const DOMAIN_TYPE: DomainType;
|
||||
|
||||
type CreationRequest: Serialize;
|
||||
@ -92,9 +92,9 @@ pub(crate) trait DomainExtention: DeserializeOwned + Serialize {
|
||||
|
||||
// these are essentials tags for domain extentions to signal that bulk operations are possible
|
||||
// not every extention supports bulk operations. and not all extentions that do support all of them
|
||||
pub(crate) trait BulkCreateDomainExtention: DomainExtention {}
|
||||
pub(crate) trait BulkReadDomainExtention: DomainExtention {}
|
||||
pub(crate) trait BulkUpdateDomainExtention: DomainExtention {
|
||||
pub trait BulkCreateDomainExtention: DomainExtention {}
|
||||
pub trait BulkReadDomainExtention: DomainExtention {}
|
||||
pub trait BulkUpdateDomainExtention: DomainExtention {
|
||||
type BulkUpdateRequest: Serialize;
|
||||
}
|
||||
pub(crate) trait BulkDeleteDomainExtention: DomainExtention {}
|
||||
pub trait BulkDeleteDomainExtention: DomainExtention {}
|
||||
|
||||
@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::trace;
|
||||
use log::{debug, trace};
|
||||
use reqwest::header::{HeaderName, HeaderValue};
|
||||
use reqwest::{Certificate, Request};
|
||||
use serde::de::DeserializeOwned;
|
||||
@ -15,6 +15,7 @@ use crate::api::{
|
||||
};
|
||||
use crate::{Error, Result};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Client(Arc<InnerClient>);
|
||||
struct InnerClient {
|
||||
http: reqwest::Client,
|
||||
@ -52,7 +53,7 @@ pub struct ClientBuilder<T> {
|
||||
}
|
||||
|
||||
impl<T> ClientBuilder<T> {
|
||||
fn new() -> ClientBuilder<RequiresLocation> {
|
||||
pub fn new() -> ClientBuilder<RequiresLocation> {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
@ -132,6 +133,15 @@ impl ClientBuilder<RequiresNothing> {
|
||||
"https"
|
||||
}
|
||||
}
|
||||
fn port(&self) -> u16 {
|
||||
if self.port != 0 {
|
||||
self.port
|
||||
} else if self.ssl == SslStrategy::NoSll {
|
||||
80
|
||||
} else {
|
||||
443
|
||||
}
|
||||
}
|
||||
fn basic_auth_header(&self) -> ClientBuildResult<(HeaderName, HeaderValue)> {
|
||||
let header = format!("Bearer {} {}", self.username, self.password);
|
||||
let mut header = HeaderValue::from_str(&header)?;
|
||||
@ -144,7 +154,7 @@ impl ClientBuilder<RequiresNothing> {
|
||||
"{}://{}:{}/{}/{API_BASE}",
|
||||
self.protocol(),
|
||||
self.hostname,
|
||||
self.port,
|
||||
self.port(),
|
||||
self.sitename,
|
||||
);
|
||||
trace!("checkmk url: {url}");
|
||||
@ -209,20 +219,28 @@ impl InnerClient {
|
||||
}
|
||||
|
||||
pub(crate) async fn handle_request(&self, request: Request) -> Result<String> {
|
||||
trace!("sending {}-request to {}", request.method(), request.url());
|
||||
let permit = self.semaphore.acquire().await.unwrap();
|
||||
debug!("sending {}-request to {}", request.method(), request.url());
|
||||
|
||||
let response = self
|
||||
.http
|
||||
.execute(request)
|
||||
.await
|
||||
.map_err(Error::SendRequest)?;
|
||||
|
||||
let status = response.status();
|
||||
let body = response.text().await.map_err(Error::ReceiveBody)?;
|
||||
drop(permit);
|
||||
trace!("response from checkmk ({status}): {body}");
|
||||
|
||||
if body.contains("Checkmk: Site Not Started") {
|
||||
return Err(Error::CheckmkNotStarted);
|
||||
}
|
||||
|
||||
if status.is_success() {
|
||||
Ok(body)
|
||||
} else {
|
||||
let cmkerror = serde_json::from_str(&body).map_err(Error::DeserializeResponse)?;
|
||||
let cmkerror = serde_json::from_str(&body)
|
||||
.map_err(Error::DeserializeResponse)?;
|
||||
Err(Error::CheckmkError(cmkerror))
|
||||
}
|
||||
}
|
||||
@ -396,6 +414,15 @@ pub struct ApiClient<D: DomainExtention> {
|
||||
_marker: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl <D: DomainExtention> From<&Client> for ApiClient<D> {
|
||||
fn from(value: &Client) -> Self {
|
||||
Self {
|
||||
inner: value.0.clone(),
|
||||
_marker: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DomainExtention> ApiClient<D> {
|
||||
pub async fn create(
|
||||
&self,
|
||||
|
||||
@ -25,6 +25,9 @@ pub enum Error {
|
||||
#[error("Recieved an error from checkmk ({}): {}", .0.status, .0.detail)]
|
||||
CheckmkError(#[source] CheckmkError),
|
||||
|
||||
#[error("Checkmk site has not yet started. log in as site user and execute 'omd start'")]
|
||||
CheckmkNotStarted,
|
||||
|
||||
#[error("Missing header: {0}")]
|
||||
MissingHeader(&'static str),
|
||||
#[error("Failed to parse ETag header: {0}")]
|
||||
|
||||
@ -3,3 +3,4 @@ mod client;
|
||||
mod error;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use client::{Client, ClientBuilder};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user