pipedash-core 0.9.0

Framework-agnostic core library for Pipedash
Documentation
use chrono::{
    DateTime,
    Utc,
};
use serde::{
    Deserialize,
    Serialize,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AlertKind {
    Failed,
    Degraded,
    LongRun,
    Outage,
    Recovered,
    Test,
}

impl AlertKind {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Failed => "failed",
            Self::Degraded => "degraded",
            Self::LongRun => "long_run",
            Self::Outage => "outage",
            Self::Recovered => "recovered",
            Self::Test => "test",
        }
    }

    pub fn parse(value: &str) -> Option<Self> {
        Some(match value {
            "failed" => Self::Failed,
            "degraded" => Self::Degraded,
            "long_run" => Self::LongRun,
            "outage" => Self::Outage,
            "recovered" => Self::Recovered,
            "test" => Self::Test,
            _ => return None,
        })
    }

    pub fn tone(self) -> &'static str {
        match self {
            Self::Failed | Self::Degraded => "danger",
            Self::LongRun | Self::Outage => "warning",
            Self::Recovered | Self::Test => "success",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DeviceAlert {
    pub seq: i64,
    pub id: String,
    pub key: String,
    pub kind: AlertKind,
    pub title: String,
    pub body: String,
    pub pipeline_id: Option<String>,
    pub provider_id: Option<i64>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NewDeviceAlert {
    pub key: String,
    pub kind: AlertKind,
    pub title: String,
    pub body: String,
    pub pipeline_id: Option<String>,
    pub provider_id: Option<i64>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DevicePush {
    pub device_id: String,
    pub relay_subscription_id: String,
    pub created_at: DateTime<Utc>,
    pub last_success_at: Option<DateTime<Utc>>,
    pub undelivered_seq: Option<i64>,
    pub undelivered_max_seq: Option<i64>,
}

pub fn new_alert_id() -> String {
    format!("{:032x}", rand::random::<u128>())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn kinds_round_trip_through_their_wire_names() {
        for kind in [
            AlertKind::Failed,
            AlertKind::Degraded,
            AlertKind::LongRun,
            AlertKind::Outage,
            AlertKind::Recovered,
            AlertKind::Test,
        ] {
            assert_eq!(AlertKind::parse(kind.as_str()), Some(kind));
            assert_eq!(
                serde_json::to_value(kind).unwrap(),
                serde_json::json!(kind.as_str())
            );
        }
        assert_eq!(AlertKind::parse("nope"), None);
    }

    #[test]
    fn tones_group_kinds_by_severity() {
        assert_eq!(AlertKind::Failed.tone(), "danger");
        assert_eq!(AlertKind::Degraded.tone(), "danger");
        assert_eq!(AlertKind::LongRun.tone(), "warning");
        assert_eq!(AlertKind::Outage.tone(), "warning");
        assert_eq!(AlertKind::Recovered.tone(), "success");
        assert_eq!(AlertKind::Test.tone(), "success");
    }

    #[test]
    fn alert_ids_are_32_hex_characters() {
        let id = new_alert_id();
        assert_eq!(id.len(), 32);
        assert!(id.bytes().all(|b| b.is_ascii_hexdigit()));
        assert_ne!(id, new_alert_id());
    }
}