Skip to content

Commit

Permalink
c8y-mapper sends software list to advanced software management endpoint
Browse files Browse the repository at this point in the history
Since Cumulocity 10.14, it is recommended to send software list to the
advanced software management endpoint. To trigger this, first c8y-mapper
sends c8y_SupportedSoftwareTypes to inventory via JSON over MQTT.

Then, post/put software list to their HTTP endpoint.

Signed-off-by: Rina Fujino <[email protected]>
  • Loading branch information
rina23q committed Mar 11, 2024
1 parent 78aa757 commit 7e81021
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 430 deletions.
18 changes: 0 additions & 18 deletions crates/core/c8y_api/src/http_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ impl C8yEndPoint {
url_get_id
}

pub fn get_url_for_sw_list(&self, internal_id: String) -> String {
let mut url_update_swlist = self.get_base_url();
url_update_swlist.push_str("/inventory/managedObjects/");
url_update_swlist.push_str(&internal_id);
url_update_swlist
}

pub fn get_url_for_internal_id(&self, device_id: &str) -> String {
let mut url_get_id = self.get_base_url();
url_get_id.push_str("/identity/externalIds/c8y_Serial/");
Expand Down Expand Up @@ -199,17 +192,6 @@ mod tests {
);
}

#[test]
fn get_url_for_sw_list_returns_correct_address() {
let mut c8y = C8yEndPoint::new("test_host", "test_device");
c8y.devices_internal_id
.insert("test_device".to_string(), "12345".to_string());
let internal_id = c8y.get_internal_id("test_device".to_string()).unwrap();
let res = c8y.get_url_for_sw_list(internal_id);

assert_eq!(res, "https://test_host/inventory/managedObjects/12345");
}

#[test_case("http://aaa.test.com")]
#[test_case("https://aaa.test.com")]
#[test_case("ftp://aaa.test.com")]
Expand Down
172 changes: 0 additions & 172 deletions crates/core/c8y_api/src/json_c8y.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use download::DownloadInfo;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
Expand All @@ -9,13 +8,10 @@ use tedge_api::alarm::ThinEdgeAlarmData;
use tedge_api::entity_store::EntityMetadata;
use tedge_api::entity_store::EntityType;
use tedge_api::event::ThinEdgeEvent;
use tedge_api::messages::SoftwareListCommand;
use tedge_api::EntityStore;
use tedge_api::Jsonify;
use tedge_api::SoftwareModule;
use time::OffsetDateTime;

const EMPTY_STRING: &str = "";
const DEFAULT_ALARM_SEVERITY: AlarmSeverity = AlarmSeverity::Minor;
const DEFAULT_ALARM_TYPE: &str = "ThinEdgeAlarm";

Expand Down Expand Up @@ -70,60 +66,6 @@ impl InternalIdResponse {
}
}

#[derive(Debug, Deserialize, Serialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct C8ySoftwareModuleItem {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
pub software_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub url: Option<DownloadInfo>,
}

impl<'a> Jsonify<'a> for C8ySoftwareModuleItem {}

impl From<SoftwareModule> for C8ySoftwareModuleItem {
fn from(module: SoftwareModule) -> Self {
let url = if module.url.is_none() {
Some(EMPTY_STRING.into())
} else {
module.url
};

Self {
name: module.name,
version: module.version,
software_type: module.module_type.unwrap_or(SoftwareModule::default_type()),
url,
}
}
}

#[derive(Debug, Serialize, Eq, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct C8yUpdateSoftwareListResponse {
#[serde(rename = "c8y_SoftwareList")]
c8y_software_list: Option<Vec<C8ySoftwareModuleItem>>,
}

impl<'a> Jsonify<'a> for C8yUpdateSoftwareListResponse {}

impl From<&SoftwareListCommand> for C8yUpdateSoftwareListResponse {
fn from(list: &SoftwareListCommand) -> Self {
let mut new_list: Vec<C8ySoftwareModuleItem> = Vec::new();
list.modules().into_iter().for_each(|software_module| {
let c8y_software_module: C8ySoftwareModuleItem = software_module.into();
new_list.push(c8y_software_module);
});

Self {
c8y_software_list: Some(new_list),
}
}
}

impl From<ThinEdgeEvent> for C8yCreateEvent {
fn from(event: ThinEdgeEvent) -> Self {
let mut extras = HashMap::new();
Expand Down Expand Up @@ -370,127 +312,13 @@ mod tests {
use tedge_api::entity_store::EntityRegistrationMessage;
use tedge_api::entity_store::InvalidExternalIdError;
use tedge_api::event::ThinEdgeEventData;
use tedge_api::messages::SoftwareListCommandPayload;
use tedge_api::mqtt_topics::EntityTopicId;
use tedge_api::mqtt_topics::MqttSchema;
use test_case::test_case;
use time::macros::datetime;

use super::*;

#[test]
fn from_software_module_to_c8y_software_module_item() {
let software_module = SoftwareModule {
module_type: Some("a".into()),
name: "b".into(),
version: Some("c".into()),
url: Some("".into()),
file_path: None,
};

let expected_c8y_item = C8ySoftwareModuleItem {
name: "b".into(),
version: Some("c".into()),
software_type: "a".to_string(),
url: Some("".into()),
};

let converted: C8ySoftwareModuleItem = software_module.into();

assert_eq!(converted, expected_c8y_item);
}

#[test]
fn from_thin_edge_json_to_c8y_set_software_list() {
let input_json = r#"{
"id":"1",
"status":"successful",
"currentSoftwareList":[
{"type":"debian", "modules":[
{"name":"a"},
{"name":"b","version":"1.0"},
{"name":"c","url":"https://foobar.io/c.deb"},
{"name":"d","version":"beta","url":"https://foobar.io/d.deb"}
]},
{"type":"apama","modules":[
{"name":"m","url":"https://foobar.io/m.epl"}
]}
]}"#;

let command = SoftwareListCommand {
target: EntityTopicId::default_main_device(),
cmd_id: "1".to_string(),
payload: SoftwareListCommandPayload::from_json(input_json).unwrap(),
};

let c8y_software_list: C8yUpdateSoftwareListResponse = (&command).into();

let expected_struct = C8yUpdateSoftwareListResponse {
c8y_software_list: Some(vec![
C8ySoftwareModuleItem {
name: "a".into(),
version: None,
software_type: "debian".to_string(),
url: Some("".into()),
},
C8ySoftwareModuleItem {
name: "b".into(),
version: Some("1.0".into()),
software_type: "debian".to_string(),
url: Some("".into()),
},
C8ySoftwareModuleItem {
name: "c".into(),
version: None,
software_type: "debian".to_string(),
url: Some("https://foobar.io/c.deb".into()),
},
C8ySoftwareModuleItem {
name: "d".into(),
version: Some("beta".into()),
software_type: "debian".to_string(),
url: Some("https://foobar.io/d.deb".into()),
},
C8ySoftwareModuleItem {
name: "m".into(),
version: None,
software_type: "apama".to_string(),
url: Some("https://foobar.io/m.epl".into()),
},
]),
};

let expected_json = r#"{"c8y_SoftwareList":[{"name":"a","softwareType":"debian","url":""},{"name":"b","version":"1.0","softwareType":"debian","url":""},{"name":"c","softwareType":"debian","url":"https://foobar.io/c.deb"},{"name":"d","version":"beta","softwareType":"debian","url":"https://foobar.io/d.deb"},{"name":"m","softwareType":"apama","url":"https://foobar.io/m.epl"}]}"#;

assert_eq!(c8y_software_list, expected_struct);
assert_eq!(c8y_software_list.to_json(), expected_json);
}

#[test]
fn empty_to_c8y_set_software_list() {
let input_json = r#"{
"id":"1",
"status":"successful",
"currentSoftwareList":[]
}"#;

let command = &SoftwareListCommand {
target: EntityTopicId::default_main_device(),
cmd_id: "1".to_string(),
payload: SoftwareListCommandPayload::from_json(input_json).unwrap(),
};

let c8y_software_list: C8yUpdateSoftwareListResponse = command.into();

let expected_struct = C8yUpdateSoftwareListResponse {
c8y_software_list: Some(vec![]),
};
let expected_json = r#"{"c8y_SoftwareList":[]}"#;

assert_eq!(c8y_software_list, expected_struct);
assert_eq!(c8y_software_list.to_json(), expected_json);
}

#[test]
fn get_id_from_c8y_response() {
let managed_object = C8yManagedObject { id: "12345".into() };
Expand Down
Loading

0 comments on commit 7e81021

Please sign in to comment.