summaryrefslogtreecommitdiffstats
path: root/crates/core/json_sm/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/core/json_sm/src/lib.rs')
-rw-r--r--crates/core/json_sm/src/lib.rs910
1 files changed, 910 insertions, 0 deletions
diff --git a/crates/core/json_sm/src/lib.rs b/crates/core/json_sm/src/lib.rs
new file mode 100644
index 00000000..d2153a19
--- /dev/null
+++ b/crates/core/json_sm/src/lib.rs
@@ -0,0 +1,910 @@
+mod error;
+mod messages;
+mod software;
+
+pub use error::*;
+pub use messages::{
+ software_filter_topic, Auth, DownloadInfo, Jsonify, SoftwareListRequest, SoftwareListResponse,
+ SoftwareOperationStatus, SoftwareRequestResponse, SoftwareUpdateRequest,
+ SoftwareUpdateResponse,
+};
+pub use software::*;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use regex::Regex;
+
+ #[test]
+ fn topic_names() {
+ // There are two topics for each kind of requests,
+ // one for the requests, the other for the responses
+ assert_eq!(
+ SoftwareListRequest::topic_name(),
+ "tedge/commands/req/software/list"
+ );
+ assert_eq!(
+ SoftwareListResponse::topic_name(),
+ "tedge/commands/res/software/list"
+ );
+ assert_eq!(
+ SoftwareUpdateRequest::topic_name(),
+ "tedge/commands/req/software/update"
+ );
+ assert_eq!(
+ SoftwareUpdateResponse::topic_name(),
+ "tedge/commands/res/software/update"
+ );
+ }
+
+ #[test]
+ fn creating_a_software_list_request() {
+ let request = SoftwareListRequest::new_with_id("1");
+
+ let expected_json = r#"{"id":"1"}"#;
+ let actual_json = request.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, expected_json);
+ }
+
+ #[test]
+ fn creating_a_software_list_request_with_generated_id() {
+ let request = SoftwareListRequest::new();
+ let generated_id = request.id;
+
+ // The generated id is a nanoid of 21 characters from A-Za-z0-9_~
+ let re = Regex::new(r"[A-Za-z0-9_~-]{21,21}").unwrap();
+ assert!(re.is_match(&generated_id));
+ }
+
+ #[test]
+ fn using_a_software_list_request() {
+ let json_request = r#"{"id":"123"}"#;
+ let request = SoftwareListRequest::from_json(json_request).expect("Failed to deserialize");
+
+ assert_eq!(request.id, "123");
+ }
+
+ #[test]
+ fn creating_a_software_list_response() {
+ let request = SoftwareListRequest::new_with_id("1");
+ let mut response = SoftwareListResponse::new(&request);
+
+ response.add_modules(
+ "debian",
+ vec![
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "a".to_string(),
+ version: None,
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "b".to_string(),
+ version: Some("1.0".to_string()),
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "c".to_string(),
+ version: None,
+ url: Some("https://foobar.io/c.deb".into()),
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "d".to_string(),
+ version: Some("beta".to_string()),
+ url: Some("https://foobar.io/d.deb".into()),
+ file_path: None,
+ },
+ ],
+ );
+
+ response.add_modules(
+ "apama",
+ vec![SoftwareModule {
+ module_type: Some("apama".to_string()),
+ name: "m".to_string(),
+ version: None,
+ url: Some("https://foobar.io/m.epl".into()),
+ file_path: None,
+ }],
+ );
+
+ let expected_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 actual_json = response.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, remove_whitespace(expected_json));
+ }
+
+ #[test]
+ fn using_a_software_list_response() {
+ let json_response = r#"{
+ "id": "123",
+ "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 response =
+ SoftwareListResponse::from_json(json_response).expect("Failed to deserialize");
+
+ assert_eq!(response.id(), "123");
+ assert_eq!(response.status(), SoftwareOperationStatus::Successful);
+ assert_eq!(response.error(), None);
+
+ // The mapper can use then the current list of modules
+ assert_eq!(
+ response.modules(),
+ vec![
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "a".to_string(),
+ version: None,
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "b".to_string(),
+ version: Some("1.0".to_string()),
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "c".to_string(),
+ version: None,
+ url: Some("https://foobar.io/c.deb".into()),
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "d".to_string(),
+ version: Some("beta".to_string()),
+ url: Some("https://foobar.io/d.deb".into()),
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("apama".to_string()),
+ name: "m".to_string(),
+ version: None,
+ url: Some("https://foobar.io/m.epl".into()),
+ file_path: None,
+ },
+ ]
+ );
+ }
+
+ #[test]
+ fn creating_a_software_list_error() {
+ let request = SoftwareListRequest::new_with_id("123");
+ let mut response = SoftwareListResponse::new(&request);
+
+ response.set_error("Request_timed-out");
+
+ let expected_json = r#"{
+ "id": "123",
+ "status": "failed",
+ "reason": "Request_timed-out"
+ }"#;
+
+ let actual_json = response.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, remove_whitespace(expected_json));
+ }
+
+ #[test]
+ fn using_a_software_list_error() {
+ let json_response = r#"{
+ "id": "123",
+ "status": "failed",
+ "reason": "Request timed-out"
+ }"#;
+ let response =
+ SoftwareListResponse::from_json(json_response).expect("Failed to deserialize");
+
+ assert_eq!(response.id(), "123");
+ assert_eq!(response.status(), SoftwareOperationStatus::Failed);
+ assert_eq!(response.error(), Some("Request timed-out".into()));
+ assert_eq!(response.modules(), vec![]);
+ }
+
+ #[test]
+ fn creating_a_software_update_request() {
+ let mut request = SoftwareUpdateRequest::new_with_id("123");
+
+ request.add_updates(
+ "debian",
+ vec![
+ SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "nodered".to_string(),
+ version: Some("1.0.0".to_string()),
+ url: None,
+ file_path: None,
+ }),
+ SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "collectd".to_string(),
+ version: Some("5.7".to_string()),
+ url: Some(
+ "https://collectd.org/download/collectd-tarballs/collectd-5.12.0.tar.bz2"
+ .into(),
+ ),
+ file_path: None,
+ }),
+ ],
+ );
+
+ request.add_updates(
+ "docker",
+ vec![
+ SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "nginx".to_string(),
+ version: Some("1.21.0".to_string()),
+ url: None,
+ file_path: None,
+ }),
+ SoftwareModuleUpdate::remove(SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ }),
+ ],
+ );
+
+ let expected_json = r#"{
+ "id": "123",
+ "updateList": [
+ {
+ "type": "debian",
+ "modules": [
+ {
+ "name": "nodered",
+ "version": "1.0.0",
+ "action": "install"
+ },
+ {
+ "name": "collectd",
+ "version": "5.7",
+ "url": "https://collectd.org/download/collectd-tarballs/collectd-5.12.0.tar.bz2",
+ "action": "install"
+ }
+ ]
+ },
+ {
+ "type": "docker",
+ "modules": [
+ {
+ "name": "nginx",
+ "version": "1.21.0",
+ "action": "install"
+ },
+ {
+ "name": "mongodb",
+ "version": "4.4.6",
+ "action": "remove"
+ }
+ ]
+ }
+ ]
+ }"#;
+ let actual_json = request.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, remove_whitespace(expected_json));
+ }
+
+ #[test]
+ fn creating_a_software_update_request_grouping_updates_per_plugin() {
+ let mut request = SoftwareUpdateRequest::new_with_id("123");
+
+ request.add_update(SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "nodered".to_string(),
+ version: Some("1.0.0".to_string()),
+ url: None,
+ file_path: None,
+ }));
+ request.add_update(SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "nginx".to_string(),
+ version: Some("1.21.0".to_string()),
+ url: None,
+ file_path: None,
+ }));
+ request.add_update(SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "collectd".to_string(),
+ version: Some("5.7".to_string()),
+ url: Some(
+ "https://collectd.org/download/collectd-tarballs/collectd-5.12.0.tar.bz2".into(),
+ ),
+ file_path: None,
+ }));
+ request.add_update(SoftwareModuleUpdate::remove(SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ }));
+
+ let expected_json = r#"{
+ "id": "123",
+ "updateList": [
+ {
+ "type": "debian",
+ "modules": [
+ {
+ "name": "nodered",
+ "version": "1.0.0",
+ "action": "install"
+ },
+ {
+ "name": "collectd",
+ "version": "5.7",
+ "url": "https://collectd.org/download/collectd-tarballs/collectd-5.12.0.tar.bz2",
+ "action": "install"
+ }
+ ]
+ },
+ {
+ "type": "docker",
+ "modules": [
+ {
+ "name": "nginx",
+ "version": "1.21.0",
+ "action": "install"
+ },
+ {
+ "name": "mongodb",
+ "version": "4.4.6",
+ "action": "remove"
+ }
+ ]
+ }
+ ]
+ }"#;
+ let actual_json = request.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, remove_whitespace(expected_json));
+ }
+
+ #[test]
+ fn creating_a_software_update_request_grouping_updates_per_plugin_using_default() {
+ let mut request = SoftwareUpdateRequest::new_with_id("123");
+
+ request.add_update(SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: None, // I.e. default
+ name: "nodered".to_string(),
+ version: Some("1.0.0".to_string()),
+ url: None,
+ file_path: None,
+ }));
+ request.add_update(SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("".to_string()), // I.e. default
+ name: "nginx".to_string(),
+ version: Some("1.21.0".to_string()),
+ url: None,
+ file_path: None,
+ }));
+ request.add_update(SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("default".to_string()), // I.e. default
+ name: "collectd".to_string(),
+ version: Some("5.7".to_string()),
+ url: None,
+ file_path: None,
+ }));
+ request.add_update(SoftwareModuleUpdate::remove(SoftwareModule {
+ module_type: Some("debian".to_string()), // Unless specified otherwise, this is not the default
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ }));
+
+ let expected_json = r#"{
+ "id": "123",
+ "updateList": [
+ {
+ "type": "default",
+ "modules": [
+ {
+ "name": "nodered",
+ "version": "1.0.0",
+ "action": "install"
+ },
+ {
+ "name": "nginx",
+ "version": "1.21.0",
+ "action": "install"
+ },
+ {
+ "name": "collectd",
+ "version": "5.7",
+ "action": "install"
+ }
+ ]
+ },
+ {
+ "type": "debian",
+ "modules": [
+ {
+ "name": "mongodb",
+ "version": "4.4.6",
+ "action": "remove"
+ }
+ ]
+ }
+ ]
+ }"#;
+ let actual_json = request.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, remove_whitespace(expected_json));
+ }
+
+ #[test]
+ fn creating_a_software_update_request_with_generated_id() {
+ let request = SoftwareUpdateRequest::new();
+ let generated_id = request.id;
+
+ // The generated id is a nanoid of 21 characters from A-Za-z0-9_~
+ let re = Regex::new(r"[A-Za-z0-9_~-]{21,21}").unwrap();
+ assert!(re.is_match(&generated_id));
+ }
+
+ #[test]
+ fn using_a_software_update_request() {
+ let json_request = r#"{
+ "id": "123",
+ "updateList": [
+ {
+ "type": "debian",
+ "modules": [
+ {
+ "name": "nodered",
+ "version": "1.0.0",
+ "action": "install"
+ },
+ {
+ "name": "collectd",
+ "version": "5.7",
+ "url": "https://collectd.org/download/collectd-tarballs/collectd-5.12.0.tar.bz2",
+ "action": "install"
+ }
+ ]
+ },
+ {
+ "type": "docker",
+ "modules": [
+ {
+ "name": "nginx",
+ "version": "1.21.0",
+ "action": "install"
+ },
+ {
+ "name": "mongodb",
+ "version": "4.4.6",
+ "action": "remove"
+ }
+ ]
+ }
+ ]
+ }"#;
+ let request =
+ SoftwareUpdateRequest::from_json(json_request).expect("Failed to deserialize");
+
+ assert_eq!(request.id, "123");
+
+ assert_eq!(
+ request.modules_types(),
+ vec!["debian".to_string(), "docker".to_string(),]
+ );
+
+ assert_eq!(
+ request.updates_for("debian"),
+ vec![
+ SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "nodered".to_string(),
+ version: Some("1.0.0".to_string()),
+ url: None,
+ file_path: None,
+ }),
+ SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "collectd".to_string(),
+ version: Some("5.7".to_string()),
+ url: Some(
+ "https://collectd.org/download/collectd-tarballs/collectd-5.12.0.tar.bz2"
+ .into(),
+ ),
+ file_path: None,
+ }),
+ ]
+ );
+
+ assert_eq!(
+ request.updates_for("docker"),
+ vec![
+ SoftwareModuleUpdate::install(SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "nginx".to_string(),
+ version: Some("1.21.0".to_string()),
+ url: None,
+ file_path: None,
+ }),
+ SoftwareModuleUpdate::remove(SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ }),
+ ]
+ );
+ }
+
+ #[test]
+ fn creating_a_software_update_response() {
+ let request = SoftwareUpdateRequest::new_with_id("123");
+ let response = SoftwareUpdateResponse::new(&request);
+
+ let expected_json = r#"{
+ "id": "123",
+ "status": "executing"
+ }"#;
+
+ let actual_json = response.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, remove_whitespace(expected_json));
+ }
+
+ #[test]
+ fn using_a_software_update_executing_response() {
+ let json_response = r#"{
+ "id": "123",
+ "status": "executing"
+ }"#;
+ let response =
+ SoftwareUpdateResponse::from_json(json_response).expect("Failed to deserialize");
+
+ assert_eq!(response.id(), "123".to_string());
+ assert_eq!(response.status(), SoftwareOperationStatus::Executing);
+ assert_eq!(response.error(), None);
+ assert_eq!(response.modules(), vec![]);
+ }
+
+ #[test]
+ fn finalizing_a_software_update_response() {
+ let request = SoftwareUpdateRequest::new_with_id("123");
+ let mut response = SoftwareUpdateResponse::new(&request);
+
+ response.add_modules(
+ "debian",
+ vec![
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "nodered".to_string(),
+ version: Some("1.0.0".to_string()),
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "collectd".to_string(),
+ version: Some("5.7".to_string()),
+ url: None,
+ file_path: None,
+ },
+ ],
+ );
+
+ response.add_modules(
+ "docker",
+ vec![
+ SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "nginx".to_string(),
+ version: Some("1.21.0".to_string()),
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ },
+ ],
+ );
+
+ let expected_json = r#"{
+ "id": "123",
+ "status": "successful",
+ "currentSoftwareList": [
+ {
+ "type": "debian",
+ "modules": [
+ {
+ "name": "nodered",
+ "version": "1.0.0"
+ },
+ {
+ "name": "collectd",
+ "version": "5.7"
+ }
+ ]
+ },
+ {
+ "type": "docker",
+ "modules": [
+ {
+ "name": "nginx",
+ "version": "1.21.0"
+ },
+ {
+ "name": "mongodb",
+ "version": "4.4.6"
+ }
+ ]
+ }
+ ]
+ }"#;
+
+ let actual_json = response.to_json().expect("Failed to serialize");
+ assert_eq!(actual_json, remove_whitespace(expected_json));
+ }
+
+ #[test]
+ fn finalizing_a_software_update_error() {
+ let request = SoftwareUpdateRequest::new_with_id("123");
+ let mut response = SoftwareUpdateResponse::new(&request);
+
+ response.set_error("2 errors: fail to install [ collectd ] fail to remove [ mongodb ]");
+ response.add_errors(
+ "debian",
+ vec![SoftwareError::Install {
+ module: SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "collectd".to_string(),
+ version: Some("5.7".to_string()),
+ url: None,
+ file_path: None,
+ },
+ reason: "Network timeout".to_string(),
+ }],
+ );
+
+ response.add_errors(
+ "docker",
+ vec![SoftwareError::Remove {
+ module: SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ },
+ reason: "Other components dependent on it".to_string(),
+ }],
+ );
+
+ response.add_modules(
+ "debian",
+ vec![SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "nodered".to_string(),
+ version: Some("1.0.0".to_string()),
+ url: None,
+ file_path: None,
+ }],
+ );
+
+ response.add_modules(
+ "docker",
+ vec![
+ SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "nginx".to_string(),
+ version: Some("1.21.0".to_string()),
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ },
+ ],
+ );
+
+ let expected_json = r#"{
+ "id": "123",
+ "status":"failed",
+ "reason":"2 errors: fail to install [ collectd ] fail to remove [ mongodb ]",
+ "currentSoftwareList": [
+ {
+ "type": "debian",
+ "modules": [
+ {
+ "name": "nodered",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "type": "docker",
+ "modules": [
+ {
+ "name": "nginx",
+ "version": "1.21.0"
+ },
+ {
+ "name": "mongodb",
+ "version": "4.4.6"
+ }
+ ]
+ }
+ ],
+ "failures":[
+ {
+ "type":"debian",
+ "modules": [
+ {
+ "name":"collectd",
+ "version":"5.7",
+ "action":"install",
+ "reason":"Network timeout"
+ }
+ ]
+ },
+ {
+ "type":"docker",
+ "modules": [
+ {
+ "name": "mongodb",
+ "version": "4.4.6",
+ "action":"remove",
+ "reason":"Other components dependent on it"
+ }
+ ]
+ }
+ ]
+ }"#;
+
+ let actual_json = response.to_json().expect("Failed to serialize");
+ assert_eq!(
+ remove_whitespace(&actual_json),
+ remove_whitespace(expected_json)
+ );
+ }
+
+ #[test]
+ fn using_a_software_update_response() {
+ let json_response = r#"{
+ "id": "123",
+ "status":"failed",
+ "reason":"2 errors: fail to install [ collectd ] fail to remove [ mongodb ]",
+ "currentSoftwareList": [
+ {
+ "type": "debian",
+ "modules": [
+ {
+ "name": "nodered",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "type": "docker",
+ "modules": [
+ {
+ "name": "nginx",
+ "version": "1.21.0"
+ },
+ {
+ "name": "mongodb",
+ "version": "4.4.6"
+ }
+ ]
+ }
+ ],
+ "failures": [
+ {
+ "type":"debian",
+ "modules": [
+ {
+ "name":"collectd",
+ "version":"5.7",
+ "action":"install",
+ "reason":"Network timeout"
+ }
+ ]
+ },
+ {
+ "type":"docker",
+ "modules": [
+ {
+ "name": "mongodb",
+ "version": "4.4.6",
+ "action":"remove",
+ "reason":"Other components dependent on it"
+ }
+ ]
+ }
+ ]
+ }"#;
+ let response =
+ SoftwareUpdateResponse::from_json(json_response).expect("Failed to deserialize");
+
+ assert_eq!(response.id(), "123");
+ assert_eq!(response.status(), SoftwareOperationStatus::Failed);
+ assert_eq!(
+ response.error(),
+ Some("2 errors: fail to install [ collectd ] fail to remove [ mongodb ]".into())
+ );
+
+ // The C8Y mapper doesn't use the failures list
+ // => no support for now
+
+ // The mapper can request the updated list of modules
+ assert_eq!(
+ response.modules(),
+ vec![
+ SoftwareModule {
+ module_type: Some("debian".to_string()),
+ name: "nodered".to_string(),
+ version: Some("1.0.0".to_string()),
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "nginx".to_string(),
+ version: Some("1.21.0".to_string()),
+ url: None,
+ file_path: None,
+ },
+ SoftwareModule {
+ module_type: Some("docker".to_string()),
+ name: "mongodb".to_string(),
+ version: Some("4.4.6".to_string()),
+ url: None,
+ file_path: None,
+ },
+ ]
+ );
+ }
+
+ fn remove_whitespace(s: &str) -> String {
+ let mut s = String::from(s);
+ s.retain(|c| !c.is_whitespace());
+ s
+ }
+}