use bson::{Document, doc};
use derive_builder::Builder;
use derive_default_builder::DefaultBuilder;
use partial_derive2::Partial;
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, Display, EnumDiscriminants, EnumString};
use typeshare::typeshare;
use crate::entities::MaintenanceWindow;
use super::{
ResourceTarget,
alert::AlertDataVariant,
resource::{Resource, ResourceListItem, ResourceQuery},
};
#[cfg(feature = "utoipa")]
#[derive(utoipa::ToSchema)]
#[schema(as = Alerter)]
pub struct AlerterSchema(
#[schema(inline)]
pub Resource<AlerterConfig, crate::entities::NoData>,
);
#[typeshare]
pub type Alerter = Resource<AlerterConfig, ()>;
#[typeshare]
pub type AlerterListItem = ResourceListItem<AlerterListItemInfo>;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct AlerterListItemInfo {
pub enabled: bool,
pub endpoint_type: AlerterEndpointVariant,
}
#[typeshare(serialized_as = "Partial<AlerterConfig>")]
pub type _PartialAlerterConfig = PartialAlerterConfig;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct AlerterConfig {
#[serde(default)]
#[builder(default)]
pub enabled: bool,
#[serde(default)]
#[builder(default)]
pub endpoint: AlerterEndpoint,
#[serde(default)]
#[builder(default)]
pub alert_types: Vec<AlertDataVariant>,
#[serde(default)]
#[builder(default)]
pub resources: Vec<ResourceTarget>,
#[serde(default)]
#[builder(default)]
pub except_resources: Vec<ResourceTarget>,
#[serde(default)]
#[builder(default)]
pub maintenance_windows: Vec<MaintenanceWindow>,
}
impl AlerterConfig {
pub fn builder() -> AlerterConfigBuilder {
AlerterConfigBuilder::default()
}
}
#[allow(clippy::derivable_impls)]
impl Default for AlerterConfig {
fn default() -> Self {
Self {
enabled: Default::default(),
endpoint: Default::default(),
alert_types: Default::default(),
resources: Default::default(),
except_resources: Default::default(),
maintenance_windows: Default::default(),
}
}
}
#[cfg(feature = "utoipa")]
impl utoipa::PartialSchema for PartialAlerterConfig {
fn schema()
-> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::schema!(#[inline] std::collections::HashMap<String, serde_json::Value>).into()
}
}
#[cfg(feature = "utoipa")]
impl utoipa::ToSchema for PartialAlerterConfig {}
#[typeshare]
#[derive(
Debug, Clone, PartialEq, Serialize, Deserialize, EnumDiscriminants,
)]
#[strum_discriminants(name(AlerterEndpointVariant))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(
not(feature = "utoipa"),
strum_discriminants(derive(
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
Display,
EnumString,
AsRefStr
))
)]
#[cfg_attr(
feature = "utoipa",
strum_discriminants(derive(
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
Display,
EnumString,
AsRefStr,
utoipa::ToSchema
))
)]
#[serde(tag = "type", content = "params")]
pub enum AlerterEndpoint {
Custom(CustomAlerterEndpoint),
Slack(SlackAlerterEndpoint),
Discord(DiscordAlerterEndpoint),
Ntfy(NtfyAlerterEndpoint),
Pushover(PushoverAlerterEndpoint),
}
impl Default for AlerterEndpoint {
fn default() -> Self {
Self::Custom(Default::default())
}
}
#[typeshare]
#[derive(
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct CustomAlerterEndpoint {
#[serde(default = "default_custom_url")]
#[builder(default = "default_custom_url()")]
pub url: String,
}
impl Default for CustomAlerterEndpoint {
fn default() -> Self {
Self {
url: default_custom_url(),
}
}
}
fn default_custom_url() -> String {
String::from("http://localhost:7000")
}
#[typeshare]
#[derive(
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct SlackAlerterEndpoint {
#[serde(default = "default_slack_url")]
#[builder(default = "default_slack_url()")]
pub url: String,
}
impl Default for SlackAlerterEndpoint {
fn default() -> Self {
Self {
url: default_slack_url(),
}
}
}
fn default_slack_url() -> String {
String::from(
"https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
)
}
#[typeshare]
#[derive(
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct DiscordAlerterEndpoint {
#[serde(default = "default_discord_url")]
#[builder(default = "default_discord_url()")]
pub url: String,
}
impl Default for DiscordAlerterEndpoint {
fn default() -> Self {
Self {
url: default_discord_url(),
}
}
}
fn default_discord_url() -> String {
String::from(
"https://discord.com/api/webhooks/XXXXXXXXXXXX/XXXX-XXXXXXXXXX",
)
}
#[typeshare]
#[derive(
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct NtfyAlerterEndpoint {
#[serde(default = "default_ntfy_url")]
#[builder(default = "default_ntfy_url()")]
pub url: String,
pub email: Option<String>,
}
impl Default for NtfyAlerterEndpoint {
fn default() -> Self {
Self {
url: default_ntfy_url(),
email: None,
}
}
}
fn default_ntfy_url() -> String {
String::from("http://localhost:8080/komodo")
}
#[typeshare]
#[derive(
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct PushoverAlerterEndpoint {
#[serde(default = "default_pushover_url")]
#[builder(default = "default_pushover_url()")]
pub url: String,
}
impl Default for PushoverAlerterEndpoint {
fn default() -> Self {
Self {
url: default_pushover_url(),
}
}
}
fn default_pushover_url() -> String {
String::from(
"https://api.pushover.net/1/messages.json?token=XXXXXXXXXXXXX&user=XXXXXXXXXXXXX",
)
}
#[typeshare]
pub type AlerterQuery = ResourceQuery<AlerterQuerySpecifics>;
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Default, DefaultBuilder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct AlerterQuerySpecifics {
pub enabled: Option<bool>,
pub types: Vec<AlerterEndpointVariant>,
}
impl super::resource::AddFilters for AlerterQuerySpecifics {
fn add_filters(&self, filters: &mut Document) {
if let Some(enabled) = self.enabled {
filters.insert("config.enabled", enabled);
}
let types =
self.types.iter().map(|t| t.as_ref()).collect::<Vec<_>>();
if !self.types.is_empty() {
filters.insert("config.endpoint.type", doc! { "$in": types });
}
}
}