Skip to main content

pipedash_core/domain/
device_alert.rs

1use chrono::{
2    DateTime,
3    Utc,
4};
5use serde::{
6    Deserialize,
7    Serialize,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum AlertKind {
13    Failed,
14    Degraded,
15    LongRun,
16    Outage,
17    Recovered,
18    Test,
19}
20
21impl AlertKind {
22    pub fn as_str(self) -> &'static str {
23        match self {
24            Self::Failed => "failed",
25            Self::Degraded => "degraded",
26            Self::LongRun => "long_run",
27            Self::Outage => "outage",
28            Self::Recovered => "recovered",
29            Self::Test => "test",
30        }
31    }
32
33    pub fn parse(value: &str) -> Option<Self> {
34        Some(match value {
35            "failed" => Self::Failed,
36            "degraded" => Self::Degraded,
37            "long_run" => Self::LongRun,
38            "outage" => Self::Outage,
39            "recovered" => Self::Recovered,
40            "test" => Self::Test,
41            _ => return None,
42        })
43    }
44
45    pub fn tone(self) -> &'static str {
46        match self {
47            Self::Failed | Self::Degraded => "danger",
48            Self::LongRun | Self::Outage => "warning",
49            Self::Recovered | Self::Test => "success",
50        }
51    }
52}
53
54#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
55pub struct DeviceAlert {
56    pub seq: i64,
57    pub id: String,
58    pub key: String,
59    pub kind: AlertKind,
60    pub title: String,
61    pub body: String,
62    pub pipeline_id: Option<String>,
63    pub provider_id: Option<i64>,
64    pub created_at: DateTime<Utc>,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct NewDeviceAlert {
69    pub key: String,
70    pub kind: AlertKind,
71    pub title: String,
72    pub body: String,
73    pub pipeline_id: Option<String>,
74    pub provider_id: Option<i64>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct DevicePush {
79    pub device_id: String,
80    pub relay_subscription_id: String,
81    pub created_at: DateTime<Utc>,
82    pub last_success_at: Option<DateTime<Utc>>,
83    pub undelivered_seq: Option<i64>,
84    pub undelivered_max_seq: Option<i64>,
85}
86
87pub fn new_alert_id() -> String {
88    format!("{:032x}", rand::random::<u128>())
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn kinds_round_trip_through_their_wire_names() {
97        for kind in [
98            AlertKind::Failed,
99            AlertKind::Degraded,
100            AlertKind::LongRun,
101            AlertKind::Outage,
102            AlertKind::Recovered,
103            AlertKind::Test,
104        ] {
105            assert_eq!(AlertKind::parse(kind.as_str()), Some(kind));
106            assert_eq!(
107                serde_json::to_value(kind).unwrap(),
108                serde_json::json!(kind.as_str())
109            );
110        }
111        assert_eq!(AlertKind::parse("nope"), None);
112    }
113
114    #[test]
115    fn tones_group_kinds_by_severity() {
116        assert_eq!(AlertKind::Failed.tone(), "danger");
117        assert_eq!(AlertKind::Degraded.tone(), "danger");
118        assert_eq!(AlertKind::LongRun.tone(), "warning");
119        assert_eq!(AlertKind::Outage.tone(), "warning");
120        assert_eq!(AlertKind::Recovered.tone(), "success");
121        assert_eq!(AlertKind::Test.tone(), "success");
122    }
123
124    #[test]
125    fn alert_ids_are_32_hex_characters() {
126        let id = new_alert_id();
127        assert_eq!(id.len(), 32);
128        assert!(id.bytes().all(|b| b.is_ascii_hexdigit()));
129        assert_ne!(id, new_alert_id());
130    }
131}