Skip to main content

unifly_api/convert/
event.rs

1use chrono::Utc;
2
3use crate::model::common::DataSource;
4use crate::model::entity_id::{EntityId, MacAddress};
5use crate::model::event::{Alarm, Event, EventCategory, EventSeverity};
6use crate::session::models::{SessionAlarm, SessionEvent};
7use crate::websocket::UnifiEvent;
8
9use super::helpers::{parse_datetime, resolve_event_templates};
10
11fn map_event_category(subsystem: Option<&String>) -> EventCategory {
12    match subsystem.map(String::as_str) {
13        Some("wlan" | "lan" | "wan") => EventCategory::Network,
14        Some("device") => EventCategory::Device,
15        Some("client") => EventCategory::Client,
16        Some("system") => EventCategory::System,
17        Some("admin") => EventCategory::Admin,
18        Some("firewall") => EventCategory::Firewall,
19        Some("vpn") => EventCategory::Vpn,
20        _ => EventCategory::Unknown,
21    }
22}
23
24impl From<SessionEvent> for Event {
25    fn from(e: SessionEvent) -> Self {
26        Event {
27            id: Some(EntityId::from(e.id)),
28            timestamp: parse_datetime(e.datetime.as_ref()).unwrap_or_else(Utc::now),
29            category: map_event_category(e.subsystem.as_ref()),
30            severity: EventSeverity::Info,
31            event_type: e.key.clone().unwrap_or_default(),
32            message: resolve_event_templates(
33                &e.msg.unwrap_or_default(),
34                &serde_json::Value::Object(e.extra),
35            ),
36            device_mac: None,
37            client_mac: None,
38            site_id: e.site_id.map(EntityId::from),
39            raw_key: e.key,
40            source: DataSource::SessionApi,
41        }
42    }
43}
44
45// ── Alarm → Event ────────────────────────────────────────────────
46
47impl From<SessionAlarm> for Event {
48    fn from(a: SessionAlarm) -> Self {
49        Event {
50            id: Some(EntityId::from(a.id)),
51            timestamp: parse_datetime(a.datetime.as_ref()).unwrap_or_else(Utc::now),
52            category: EventCategory::System,
53            severity: EventSeverity::Warning,
54            event_type: a.key.clone().unwrap_or_default(),
55            message: a.msg.unwrap_or_default(),
56            device_mac: None,
57            client_mac: None,
58            site_id: None,
59            raw_key: a.key,
60            source: DataSource::SessionApi,
61        }
62    }
63}
64
65impl From<SessionAlarm> for Alarm {
66    fn from(a: SessionAlarm) -> Self {
67        Alarm {
68            id: EntityId::from(a.id),
69            timestamp: parse_datetime(a.datetime.as_ref()).unwrap_or_else(Utc::now),
70            category: EventCategory::System,
71            severity: EventSeverity::Warning,
72            message: a.msg.unwrap_or_default(),
73            archived: a.archived.unwrap_or(false),
74            device_mac: None,
75            site_id: None,
76        }
77    }
78}
79
80// ── WebSocket Event ──────────────────────────────────────────────
81
82fn infer_ws_severity(key: &str) -> EventSeverity {
83    let upper = key.to_uppercase();
84    if upper.contains("ERROR") || upper.contains("FAIL") {
85        EventSeverity::Error
86    } else if upper.contains("DISCONNECT") || upper.contains("LOST") || upper.contains("DOWN") {
87        EventSeverity::Warning
88    } else {
89        EventSeverity::Info
90    }
91}
92
93impl From<UnifiEvent> for Event {
94    fn from(e: UnifiEvent) -> Self {
95        let category = map_event_category(Some(&e.subsystem));
96        let severity = infer_ws_severity(&e.key);
97
98        let device_mac = e
99            .extra
100            .get("mac")
101            .or_else(|| e.extra.get("sw"))
102            .or_else(|| e.extra.get("ap"))
103            .and_then(|v| v.as_str())
104            .map(MacAddress::new);
105
106        let client_mac = e
107            .extra
108            .get("user")
109            .or_else(|| e.extra.get("sta"))
110            .and_then(|v| v.as_str())
111            .map(MacAddress::new);
112
113        let site_id = if e.site_id.is_empty() {
114            None
115        } else {
116            Some(EntityId::Legacy(e.site_id))
117        };
118
119        Event {
120            id: None,
121            timestamp: parse_datetime(e.datetime.as_ref()).unwrap_or_else(Utc::now),
122            category,
123            severity,
124            event_type: e.key.clone(),
125            message: resolve_event_templates(&e.message.unwrap_or_default(), &e.extra),
126            device_mac,
127            client_mac,
128            site_id,
129            raw_key: Some(e.key),
130            source: DataSource::SessionApi,
131        }
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn event_category_mapping() {
141        assert_eq!(
142            map_event_category(Some(&"wlan".into())),
143            EventCategory::Network
144        );
145        assert_eq!(
146            map_event_category(Some(&"device".into())),
147            EventCategory::Device
148        );
149        assert_eq!(
150            map_event_category(Some(&"admin".into())),
151            EventCategory::Admin
152        );
153        assert_eq!(map_event_category(None), EventCategory::Unknown);
154    }
155}