pipedash_core/domain/
device_alert.rs1use 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}