diff options
author | Albin Suresh <albin.suresh@softwareag.com> | 2021-12-22 15:02:24 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-22 15:02:24 +0530 |
commit | 0d61811849ce9931b91b65a7e94865d99f4bb394 (patch) | |
tree | 5b6e2b4d3d44be2f1256842fb4b9d16c9e7ca2df /crates/core/thin_edge_json/src | |
parent | 888a4e70ca44d9b41d645148b7a24d89a984da7c (diff) |
Closes #667 Thin Edge JSON alarm support (#726)
* Closes #667 Thin Edge JSON alarm support
Diffstat (limited to 'crates/core/thin_edge_json/src')
-rw-r--r-- | crates/core/thin_edge_json/src/alarm.rs | 217 | ||||
-rw-r--r-- | crates/core/thin_edge_json/src/lib.rs | 1 |
2 files changed, 218 insertions, 0 deletions
diff --git a/crates/core/thin_edge_json/src/alarm.rs b/crates/core/thin_edge_json/src/alarm.rs new file mode 100644 index 00000000..1ace73d7 --- /dev/null +++ b/crates/core/thin_edge_json/src/alarm.rs @@ -0,0 +1,217 @@ +use std::convert::{TryFrom, TryInto}; + +use serde::Deserialize; +use time::OffsetDateTime; + +/// In-memory representation of ThinEdge JSON alarm. +#[derive(Debug, Deserialize, PartialEq)] +pub struct ThinEdgeAlarm { + pub name: String, + pub severity: AlarmSeverity, + pub data: Option<ThinEdgeAlarmData>, +} + +#[derive(Debug, Deserialize, PartialEq)] +pub enum AlarmSeverity { + Critical, + Major, + Minor, + Warning, +} + +/// In-memory representation of ThinEdge JSON alarm payload +#[derive(Debug, Deserialize, PartialEq)] +pub struct ThinEdgeAlarmData { + pub message: Option<String>, + #[serde(default)] + #[serde(deserialize_with = "clock::deserialize_iso8601_timestamp")] + pub time: Option<OffsetDateTime>, +} + +#[derive(thiserror::Error, Debug)] +pub enum ThinEdgeJsonDeserializerError { + #[error("Unsupported topic: {0}")] + UnsupportedTopic(String), + + #[error("Unsupported alarm severity in topic: {0}")] + UnsupportedAlarmSeverity(String), + + #[error(transparent)] + SerdeJsonError(#[from] serde_json::error::Error), +} + +impl TryFrom<&str> for AlarmSeverity { + type Error = ThinEdgeJsonDeserializerError; + + fn try_from(value: &str) -> Result<Self, Self::Error> { + match value { + "critical" => Ok(AlarmSeverity::Critical), + "major" => Ok(AlarmSeverity::Major), + "minor" => Ok(AlarmSeverity::Minor), + "warning" => Ok(AlarmSeverity::Warning), + invalid => Err(ThinEdgeJsonDeserializerError::UnsupportedAlarmSeverity( + invalid.into(), + ))?, + } + } +} + +impl ThinEdgeAlarm { + pub fn try_from( + mqtt_topic: &str, + mqtt_payload: &str, + ) -> Result<Self, ThinEdgeJsonDeserializerError> { + let topic_split: Vec<&str> = mqtt_topic.split('/').collect(); + if topic_split.len() == 4 { + let alarm_name = topic_split[3]; + if alarm_name.is_empty() { + return Err(ThinEdgeJsonDeserializerError::UnsupportedTopic( + mqtt_topic.into(), + )); + } + + let alarm_severity = topic_split[2]; + + let alarm_data = if mqtt_payload.is_empty() { + None + } else { + Some(serde_json::from_str(mqtt_payload)?) + }; + + Ok(Self { + name: alarm_name.into(), + severity: alarm_severity.try_into()?, + data: alarm_data, + }) + } else { + return Err(ThinEdgeJsonDeserializerError::UnsupportedTopic( + mqtt_topic.into(), + )); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use assert_matches::assert_matches; + use serde_json::{json, Value}; + use test_case::test_case; + use time::macros::datetime; + + #[test_case( + "tedge/alarms/critical/temperature_alarm", + json!({ + "message": "I raised it", + "time": "2021-04-23T19:00:00+05:00", + }), + ThinEdgeAlarm { + name: "temperature_alarm".into(), + severity: AlarmSeverity::Critical, + data: Some(ThinEdgeAlarmData { + message: Some("I raised it".into()), + time: Some(datetime!(2021-04-23 19:00:00 +05:00)), + }), + }; + "critical alarm parsing" + )] + #[test_case( + "tedge/alarms/major/temperature_alarm", + json!({ + "message": "I raised it", + }), + ThinEdgeAlarm { + name: "temperature_alarm".into(), + severity: AlarmSeverity::Major, + data: Some(ThinEdgeAlarmData { + message: Some("I raised it".into()), + time: None, + }), + }; + "major alarm parsing without timestamp" + )] + #[test_case( + "tedge/alarms/minor/temperature_alarm", + json!({ + "time": "2021-04-23T19:00:00+05:00", + }), + ThinEdgeAlarm { + name: "temperature_alarm".into(), + severity: AlarmSeverity::Minor, + data: Some(ThinEdgeAlarmData { + message: None, + time: Some(datetime!(2021-04-23 19:00:00 +05:00)), + }), + }; + "minor alarm parsing without message" + )] + #[test_case( + "tedge/alarms/warning/temperature_alarm", + json!({}), + ThinEdgeAlarm { + name: "temperature_alarm".into(), + severity: AlarmSeverity::Warning, + data: Some(ThinEdgeAlarmData { + message: None, + time: None, + }), + }; + "warning alarm parsing without message or timestamp" + )] + fn parse_thin_edge_alarm_json( + alarm_topic: &str, + alarm_payload: Value, + expected_alarm: ThinEdgeAlarm, + ) { + let alarm = + ThinEdgeAlarm::try_from(alarm_topic, alarm_payload.to_string().as_str()).unwrap(); + + assert_eq!(alarm, expected_alarm); + } + + #[test] + fn alarm_translation_empty_alarm_name() { + let result = ThinEdgeAlarm::try_from("tedge/alarms/critical/", "{}"); + + assert_matches!( + result, + Err(ThinEdgeJsonDeserializerError::UnsupportedTopic(_)) + ); + } + + #[test] + fn alarm_translation_empty_severity() { + let result = ThinEdgeAlarm::try_from("tedge/alarms//some_alarm", "{}"); + + assert_matches!( + result, + Err(ThinEdgeJsonDeserializerError::UnsupportedAlarmSeverity(_)) + ); + } + + #[test] + fn alarm_translation_empty_severity_and_name() { + let result = ThinEdgeAlarm::try_from("tedge/alarms//", "{}"); + + assert_matches!( + result, + Err(ThinEdgeJsonDeserializerError::UnsupportedTopic(_)) + ); + } + + #[test] + fn alarm_translation_invalid_severity() { + let result = ThinEdgeAlarm::try_from("tedge/alarms/invalid_severity/foo", "{}"); + + assert_matches!( + result, + Err(ThinEdgeJsonDeserializerError::UnsupportedAlarmSeverity(_)) + ); + } + + #[test] + fn alarm_translation_clear_alarm_with_empty_payload() { + let result = ThinEdgeAlarm::try_from("tedge/alarms/critical/temperature_high_alarm", ""); + assert_matches!(result.unwrap().data, None); + } +} diff --git a/crates/core/thin_edge_json/src/lib.rs b/crates/core/thin_edge_json/src/lib.rs index 56664969..441b5441 100644 --- a/crates/core/thin_edge_json/src/lib.rs +++ b/crates/core/thin_edge_json/src/lib.rs @@ -1,6 +1,7 @@ //! A library to create [ThinEdgeJson][1] from bytes of json data by validating it. //! [1]: https://github.com/thin-edge/thin-edge.io/blob/main/docs/src/architecture/thin-edge-json.md +pub mod alarm; pub mod builder; pub mod data; pub mod group; |