Skip to content

Commit

Permalink
Alarms with custom fragments (#1699)
Browse files Browse the repository at this point in the history
Signed-off-by: Pradeep Kumar K J <pradeepkumar.kj@softwareag.com>
  • Loading branch information
PradeepKiruvale authored Feb 2, 2023
1 parent c9ed785 commit 8d4ecbf
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 16 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/core/c8y_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ tracing = { version = "0.1", features = ["attributes", "log"] }
anyhow = "1.0"
assert-json-diff = "2.0"
assert_matches = "1.5"
maplit = "1.0.2"
mockito = "0.31"
regex = "1.7"
tempfile = "3.3"
Expand Down
180 changes: 179 additions & 1 deletion crates/core/c8y_api/src/json_c8y.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;

use tedge_api::alarm::ThinEdgeAlarm;
use tedge_api::Jsonify;
use tedge_api::SoftwareListResponse;
use tedge_api::SoftwareModule;
Expand Down Expand Up @@ -209,10 +210,113 @@ fn update_the_external_source_event(
Ok(())
}

fn make_c8y_source_fragment(source_name: &str) -> Option<SourceInfo> {
Some(SourceInfo::new(source_name.into(), "c8y_Serial".into()))
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SourceInfo {
#[serde(rename = "externalId")]
pub id: String,
#[serde(rename = "type")]
pub source_type: String,
}

impl SourceInfo {
pub fn new(id: String, source_type: String) -> Self {
Self { id, source_type }
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct C8yCreateAlarm {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "externalSource")]
pub source: Option<SourceInfo>,

pub severity: String,

#[serde(rename = "type")]
pub alarm_type: String,

#[serde(with = "time::serde::rfc3339")]
pub time: OffsetDateTime,

pub text: String,

#[serde(flatten)]
pub fragments: HashMap<String, Value>,
}

impl C8yCreateAlarm {
pub fn new(
source: Option<SourceInfo>,
severity: String,
alarm_type: String,
time: OffsetDateTime,
text: String,
fragments: HashMap<String, Value>,
) -> Self {
Self {
source,
severity,
alarm_type,
time,
text,
fragments,
}
}
}

impl TryFrom<&ThinEdgeAlarm> for C8yCreateAlarm {
type Error = SMCumulocityMapperError;

fn try_from(alarm: &ThinEdgeAlarm) -> Result<Self, SMCumulocityMapperError> {
let severity = alarm.severity.to_string();
let alarm_type = alarm.name.to_owned();
let text;
let time;
let fragments;

match &alarm.to_owned().data {
None => {
text = alarm_type.clone();
time = OffsetDateTime::now_utc();
fragments = HashMap::new();
}
Some(data) => {
text = data.text.clone().unwrap_or_else(|| alarm_type.clone());
time = data.time.unwrap_or_else(OffsetDateTime::now_utc);
fragments = data.alarm_data.clone();
}
}

let source = if let Some(external_source) = &alarm.source {
make_c8y_source_fragment(external_source)
} else {
None
};

Ok(Self {
source,
severity,
alarm_type,
time,
text,
fragments,
})
}
}

#[cfg(test)]
mod tests {
use anyhow::Result;
use assert_matches::assert_matches;
use serde_json::json;
use tedge_api::alarm::AlarmSeverity;
use tedge_api::alarm::ThinEdgeAlarm;
use tedge_api::alarm::ThinEdgeAlarmData;
use tedge_api::event::ThinEdgeEventData;
use test_case::test_case;
use time::macros::datetime;
Expand Down Expand Up @@ -245,7 +349,7 @@ mod tests {
let input_json = r#"{
"id":"1",
"status":"successful",
"currentSoftwareList":[
"currentSoftwareList":[
{"type":"debian", "modules":[
{"name":"a"},
{"name":"b","version":"1.0"},
Expand Down Expand Up @@ -471,4 +575,78 @@ mod tests {

Ok(())
}

#[test_case(
ThinEdgeAlarm {
name: "temperature alarm".into(),
severity: AlarmSeverity::Critical,
data: Some(ThinEdgeAlarmData {
text: Some("Temperature went high".into()),
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: HashMap::new(),
}),
source: None,
},
C8yCreateAlarm {
severity: "CRITICAL".to_string(),
source: None,
alarm_type: "temperature alarm".into(),
time: datetime!(2021-04-23 19:00:00 +05:00),
text: "Temperature went high".into(),
fragments: HashMap::new(),
}
;"critical alarm translation"
)]
#[test_case(
ThinEdgeAlarm {
name: "temperature alarm".into(),
severity: AlarmSeverity::Critical,
data: Some(ThinEdgeAlarmData {
text: Some("Temperature went high".into()),
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: maplit::hashmap!{"SomeCustomFragment".to_string() => json!({"nested": {"value":"extra info"}})},
}),
source: None,
},
C8yCreateAlarm {
severity: "CRITICAL".to_string(),
source: None,
alarm_type: "temperature alarm".into(),
time: datetime!(2021-04-23 19:00:00 +05:00),
text: "Temperature went high".into(),
fragments: maplit::hashmap!{"SomeCustomFragment".to_string() => json!({"nested": {"value":"extra info"}})},
}
;"critical alarm translation with custom fragment"
)]
#[test_case(
ThinEdgeAlarm {
name: "temperature alarm".into(),
severity: AlarmSeverity::Critical,
data: Some(ThinEdgeAlarmData {
text: Some("Temperature went high".into()),
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: maplit::hashmap!{"SomeCustomFragment".to_string() => json!({"nested": {"value":"extra info"}})},
}),
source: Some("external_source".into()),
},
C8yCreateAlarm {
severity: "CRITICAL".to_string(),
source: Some(SourceInfo::new("external_source".to_string(),"c8y_Serial".to_string())),
alarm_type: "temperature alarm".into(),
time: datetime!(2021-04-23 19:00:00 +05:00),
text: "Temperature went high".into(),
fragments: maplit::hashmap!{"SomeCustomFragment".to_string() => json!({"nested": {"value":"extra info"}})},
}
;"critical alarm translation of child device with custom fragment"
)]
fn check_alarm_translation(
tedge_alarm: ThinEdgeAlarm,
expected_c8y_alarm: C8yCreateAlarm,
) -> Result<()> {
let actual_c8y_alarm = C8yCreateAlarm::try_from(&tedge_alarm)?;

assert_eq!(actual_c8y_alarm, expected_c8y_alarm);

Ok(())
}
}
42 changes: 37 additions & 5 deletions crates/core/c8y_api/src/smartrest/alarm.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use crate::smartrest::error::SmartRestSerializerError;
use tedge_api::alarm::AlarmSeverity;
use tedge_api::alarm::ThinEdgeAlarm;
use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime;

use crate::smartrest::error::SmartRestSerializerError;

/// Converts from thin-edge alarm to C8Y alarm SmartREST message
pub fn serialize_alarm(alarm: ThinEdgeAlarm) -> Result<String, SmartRestSerializerError> {
match alarm.data {
Expand All @@ -16,9 +15,7 @@ pub fn serialize_alarm(alarm: ThinEdgeAlarm) -> Result<String, SmartRestSerializ
AlarmSeverity::Minor => 303,
AlarmSeverity::Warning => 304,
};

let current_timestamp = OffsetDateTime::now_utc();

let smartrest_message = format!(
"{},{},\"{}\",{}",
smartrest_code,
Expand All @@ -29,7 +26,6 @@ pub fn serialize_alarm(alarm: ThinEdgeAlarm) -> Result<String, SmartRestSerializ
|timestamp| timestamp.format(&Rfc3339)
)?
);

Ok(smartrest_message)
}
}
Expand All @@ -39,6 +35,7 @@ pub fn serialize_alarm(alarm: ThinEdgeAlarm) -> Result<String, SmartRestSerializ
mod tests {
use super::*;
use assert_matches::assert_matches;
use maplit::hashmap;
use serde::Deserialize;
use tedge_api::alarm::ThinEdgeAlarmData;
use test_case::test_case;
Expand All @@ -51,7 +48,9 @@ mod tests {
data: Some(ThinEdgeAlarmData {
text: Some("I raised it".into()),
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: hashmap!{},
}),
source: None,
},
"301,temperature_alarm,\"I raised it\",2021-04-23T19:00:00+05:00"
;"critical alarm translation"
Expand All @@ -63,7 +62,9 @@ mod tests {
data: Some(ThinEdgeAlarmData {
text: Some("I raised it".into()),
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: hashmap!{},
}),
source: None,
},
"302,temperature_alarm,\"I raised it\",2021-04-23T19:00:00+05:00"
;"major alarm translation"
Expand All @@ -75,7 +76,9 @@ mod tests {
data: Some(ThinEdgeAlarmData {
text: None,
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: hashmap!{},
}),
source: None,
},
"303,temperature_alarm,\"\",2021-04-23T19:00:00+05:00"
;"minor alarm translation without message"
Expand All @@ -87,20 +90,47 @@ mod tests {
data: Some(ThinEdgeAlarmData {
text: Some("I, raised, it".into()),
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: hashmap!{},
}),
source: None,
},
"304,temperature_alarm,\"I, raised, it\",2021-04-23T19:00:00+05:00"
;"warning alarm translation with commas in message"
)]
#[test_case(
ThinEdgeAlarm {
name: "temperature_alarm".into(),
severity: AlarmSeverity::Warning,
data: Some(ThinEdgeAlarmData {
text: Some("External sensor raised alarm".into()),
time: Some(datetime!(2021-04-23 19:00:00 +05:00)),
alarm_data: hashmap!{},
}),
source: Some("External_source".to_string()),
},
"304,temperature_alarm,\"External sensor raised alarm\",2021-04-23T19:00:00+05:00"
;"warning alarm translation by external sensor"
)]
#[test_case(
ThinEdgeAlarm {
name: "temperature_alarm".into(),
severity: AlarmSeverity::Minor,
data: None,
source: None,
},
"306,temperature_alarm"
;"clear alarm translation"
)]
#[test_case(
ThinEdgeAlarm {
name: "temperature_alarm".into(),
severity: AlarmSeverity::Minor,
data: None,
source: Some("external_sensor".to_string()),
},
"306,temperature_alarm"
;"clear child alarm translation"
)]
fn check_alarm_translation(alarm: ThinEdgeAlarm, expected_smartrest_msg: &str) {
let result = serialize_alarm(alarm);

Expand All @@ -123,7 +153,9 @@ mod tests {
data: Some(ThinEdgeAlarmData {
text: Some("I raised it".into()),
time: None,
alarm_data: hashmap! {},
}),
source: None,
};

let smartrest_message = serialize_alarm(alarm).unwrap();
Expand Down
1 change: 1 addition & 0 deletions crates/core/tedge_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ anyhow = "1.0"
assert_matches = "1.5"
clock = { path = "../../common/clock" }
criterion = "0.3"
maplit = "1.0.2"
mockall = "0.11"
proptest = "1.0"
regex = "1.5"
Expand Down
Loading

0 comments on commit 8d4ecbf

Please sign in to comment.