Skip to content

Commit

Permalink
Merge pull request #3362 from Ruadhri17/operation-fragments-fix
Browse files Browse the repository at this point in the history
fix: c8y operation fragments for firmware/software update and device profile
  • Loading branch information
Ruadhri17 authored Feb 18, 2025
2 parents 1f4e5d1 + 580e897 commit 83b65bd
Show file tree
Hide file tree
Showing 11 changed files with 313 additions and 116 deletions.
25 changes: 13 additions & 12 deletions crates/core/c8y_api/src/json_c8y_deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use std::collections::HashMap;
use tedge_api::commands::SoftwareModuleAction;
use tedge_api::commands::SoftwareModuleItem;
use tedge_api::commands::SoftwareRequestResponseSoftwareList;
use tedge_api::device_profile::ConfigPayload;
use tedge_api::device_profile::FirmwarePayload;
use tedge_api::device_profile::SoftwarePayload;
use tedge_api::mqtt_topics::EntityTopicId;
Expand Down Expand Up @@ -440,6 +439,7 @@ pub struct C8yUploadConfigFile {
/// use c8y_api::json_c8y_deserializer::C8yDownloadConfigFile;
///
/// // Example input from c8y
/// // Note: Name property is available only when JSON object is created from the device profile payload
/// // Note: Legacy config download operations will not have the type property
/// let data = r#"
/// {
Expand All @@ -453,20 +453,13 @@ pub struct C8yUploadConfigFile {
#[derive(Debug, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct C8yDownloadConfigFile {
#[serde(default = "Default::default")]
pub name: String,
#[serde(rename = "type", default = "default_config_type")]
pub config_type: String,
pub url: String,
}

impl From<C8yDownloadConfigFile> for ConfigPayload {
fn from(value: C8yDownloadConfigFile) -> Self {
ConfigPayload {
config_type: value.config_type,
remote_url: Some(value.url),
}
}
}

/// Representation of c8y_Firmware JSON object
///
/// ```rust
Expand Down Expand Up @@ -593,6 +586,7 @@ mod tests {
use crate::json_c8y_deserializer::C8ySoftwareUpdateModule;
use assert_json_diff::assert_json_eq;
use serde_json::json;
use tedge_api::device_profile::ConfigPayload;
use tedge_api::device_profile::DeviceProfileCmdPayload;
use tedge_api::mqtt_topics::EntityTopicId;
use tedge_api::CommandStatus;
Expand Down Expand Up @@ -1012,7 +1006,12 @@ mod tests {
.expect("failed to extract software info"),
);
for config in req.configuration {
thin_edge_json.add_config(config.into());
thin_edge_json.add_config(ConfigPayload {
name: config.name,
config_type: config.config_type,
remote_url: Some(config.url.clone()),
server_url: Some(config.url),
});
}
let expected_thin_edge_json = json!({
"status": "init",
Expand Down Expand Up @@ -1054,8 +1053,10 @@ mod tests {
"operation": "config_update",
"skip": false,
"payload": {
"name": "collectd-v2",
"type": "collectd.conf",
"remoteUrl": "http://www.example.url/inventory/binaries/88395"
"remoteUrl": "http://www.example.url/inventory/binaries/88395",
"serverUrl": "http://www.example.url/inventory/binaries/88395"
}
}
]
Expand Down
15 changes: 15 additions & 0 deletions crates/core/c8y_api/src/smartrest/inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use tedge_config::TopicPrefix;

use super::message_ids::CHILD_DEVICE_CREATION;
use super::message_ids::SERVICE_CREATION;
use super::message_ids::SET_CURRENTLY_INSTALLED_CONFIGURATION;
use super::message_ids::SET_DEVICE_PROFILE_THAT_IS_BEING_APPLIED;
use super::message_ids::SET_REQUIRED_AVAILABILITY;
use super::payload::SmartrestPayload;
Expand Down Expand Up @@ -134,6 +135,20 @@ pub fn service_creation_message_payload(
Ok(payload)
}

pub fn set_c8y_config_fragment(
config_type: &str,
remote_url: &str,
name: Option<&str>,
) -> SmartrestPayload {
SmartrestPayload::serialize((
SET_CURRENTLY_INSTALLED_CONFIGURATION,
config_type,
remote_url,
name,
))
.expect("shouldn't put payload over size limit")
}

/// Create a SmartREST message to set a response interval for c8y_RequiredAvailability.
///
/// In the SmartREST 117 message, the interval must be in MINUTES, and can be <=0,
Expand Down
1 change: 1 addition & 0 deletions crates/core/tedge_api/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,7 @@ pub struct ConfigUpdateCmdPayload {
#[serde(skip_serializing_if = "Option::is_none")]
pub tedge_url: Option<String>,
pub remote_url: String,
pub server_url: String,
#[serde(rename = "type")]
pub config_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down
26 changes: 13 additions & 13 deletions crates/core/tedge_api/src/device_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,19 @@ impl CommandPayload for DeviceProfileCmdPayload {
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DeviceProfileOperation {
operation: OperationType,
skip: bool,
#[serde(flatten)]
payload: OperationPayload,
pub operation: OperationPayload,
pub skip: bool,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "operation", content = "payload")]
pub enum OperationPayload {
#[serde(rename = "payload")]
#[serde(rename = "firmware_update")]
Firmware(FirmwarePayload),
#[serde(rename = "payload")]
#[serde(rename = "software_update")]
Software(SoftwarePayload),
#[serde(rename = "payload")]
#[serde(rename = "config_update")]
Config(ConfigPayload),
}

Expand All @@ -64,6 +63,8 @@ pub struct FirmwarePayload {
pub remote_url: Option<String>,
}

impl Jsonify for FirmwarePayload {}

#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SoftwarePayload {
Expand All @@ -73,37 +74,36 @@ pub struct SoftwarePayload {
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ConfigPayload {
pub name: String,
#[serde(rename = "type")]
pub config_type: String,
pub remote_url: Option<String>,
pub server_url: Option<String>,
}

impl DeviceProfileCmdPayload {
pub fn add_firmware(&mut self, firmware: FirmwarePayload) {
let firmware_operation = DeviceProfileOperation {
operation: OperationType::FirmwareUpdate,
operation: OperationPayload::Firmware(firmware),
skip: false,
payload: OperationPayload::Firmware(firmware),
};

self.operations.push(firmware_operation);
}

pub fn add_software(&mut self, software: SoftwarePayload) {
let software_operation = DeviceProfileOperation {
operation: OperationType::SoftwareUpdate,
operation: OperationPayload::Software(software),
skip: false,
payload: OperationPayload::Software(software),
};

self.operations.push(software_operation);
}

pub fn add_config(&mut self, config: ConfigPayload) {
let config_operation = DeviceProfileOperation {
operation: OperationType::ConfigUpdate,
operation: OperationPayload::Config(config),
skip: false,
payload: OperationPayload::Config(config),
};

self.operations.push(config_operation);
Expand Down
22 changes: 17 additions & 5 deletions crates/extensions/c8y_mapper_ext/src/operations/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use tedge_api::commands::ConfigUpdateCmdPayload;
use tedge_api::commands::FirmwareUpdateCmdPayload;
use tedge_api::commands::LogMetadata;
use tedge_api::commands::LogUploadCmdPayload;
use tedge_api::device_profile::ConfigPayload;
use tedge_api::device_profile::DeviceProfileCmdPayload;
use tedge_api::entity::EntityExternalId;
use tedge_api::mqtt_topics::Channel;
Expand Down Expand Up @@ -288,6 +289,7 @@ impl CumulocityConverter {
status: CommandStatus::Init,
tedge_url: None,
remote_url,
server_url: config_download_request.url.clone(),
config_type: config_download_request.config_type.clone(),
path: None,
log_path: None,
Expand Down Expand Up @@ -368,11 +370,21 @@ impl CumulocityConverter {
request.add_software(software.try_into()?);
}

for mut config in device_profile_request.configuration {
if let Ok(cumulocity_url) = self.http_proxy.local_proxy_url(&config.url) {
config.url = cumulocity_url.into();
}
request.add_config(config.into());
for config in device_profile_request.configuration {
let remote_url = if let Ok(c8y_url) = self.http_proxy.local_proxy_url(&config.url) {
c8y_url.to_string()
} else {
config.url.clone()
};

let config = ConfigPayload {
name: config.name,
config_type: config.config_type,
remote_url: Some(remote_url),
server_url: Some(config.url),
};

request.add_config(config);
}

// Command messages must be retained
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Context;
use c8y_api::smartrest::inventory::set_c8y_config_fragment;
use c8y_api::smartrest::smartrest_serializer::CumulocitySupportedOperations;
use tedge_api::commands::CommandStatus;
use tedge_api::commands::ConfigUpdateCmd;
Expand Down Expand Up @@ -47,14 +48,23 @@ impl OperationContext {
extra_messages: vec![],
}),
CommandStatus::Successful => {
let config_fragment = MqttMessage::new(
sm_topic,
set_c8y_config_fragment(
&command.payload.config_type,
&command.payload.server_url,
None, // Config update payload is not provided with name field
),
);

let smartrest_operation_status = self.get_smartrest_successful_status_payload(
CumulocitySupportedOperations::C8yDownloadConfigFile,
cmd_id,
);
let c8y_notification = MqttMessage::new(sm_topic, smartrest_operation_status);

Ok(OperationOutcome::Finished {
messages: vec![c8y_notification],
messages: vec![config_fragment, c8y_notification],
})
}
CommandStatus::Failed { reason } => Err(anyhow::anyhow!(reason).into()),
Expand Down Expand Up @@ -196,6 +206,7 @@ mod tests {
"status": "executing",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/test-device/config_update/typeA-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "typeA",
})
.to_string(),
Expand All @@ -213,6 +224,7 @@ mod tests {
"status": "failed",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/test-device/config_update/typeA-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "typeA",
"reason": "Something went wrong"
})
Expand Down Expand Up @@ -258,6 +270,7 @@ mod tests {
"status": "executing",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/child1/config_update/typeA-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "typeA",
})
.to_string(),
Expand All @@ -279,6 +292,7 @@ mod tests {
"status": "failed",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/child1/config_update/typeA-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "typeA",
"reason": "Something went wrong"
})
Expand Down Expand Up @@ -318,6 +332,7 @@ mod tests {
"status": "executing",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/test-device/config_update/typeA-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "typeA",
})
.to_string(),
Expand All @@ -335,6 +350,7 @@ mod tests {
"status": "failed",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/test-device/config_update/typeA-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "typeA",
"reason": "Something went wrong"
})
Expand Down Expand Up @@ -364,13 +380,21 @@ mod tests {
"status": "successful",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/test-device/config_update/path:type:A-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "path/type/A",
})
.to_string(),
))
.await
.expect("Send failed");

// Expect `120` smartrest message on `c8y/s/us`.
assert_received_contains_str(
&mut mqtt,
[("c8y/s/us", "120,path/type/A,http://www.my.url")],
)
.await;

// Expect `503` smartrest message on `c8y/s/us`.
assert_received_contains_str(&mut mqtt, [("c8y/s/us", "503,c8y_DownloadConfigFile")]).await;
}
Expand Down Expand Up @@ -401,13 +425,21 @@ mod tests {
"status": "successful",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/child1/config_update/typeA-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "typeA",
})
.to_string(),
))
.await
.expect("Send failed");

// Expect `120` smartrest message on `c8y/s/us`.
assert_received_contains_str(
&mut mqtt,
[("c8y/s/us/child1", "120,typeA,http://www.my.url")],
)
.await;

// Expect `503` smartrest message on child topic.
assert_received_contains_str(
&mut mqtt,
Expand Down Expand Up @@ -436,13 +468,21 @@ mod tests {
"status": "successful",
"tedgeUrl": "http://localhost:8888/tedge/file-transfer/test-device/config_update/path:type:A-c8y-mapper-1234",
"remoteUrl": "http://www.my.url",
"serverUrl": "http://www.my.url",
"type": "path/type/A",
})
.to_string(),
))
.await
.expect("Send failed");

// Expect `120` smartrest message on `c8y/s/us`.
assert_received_contains_str(
&mut mqtt,
[("c8y/s/us", "120,path/type/A,http://www.my.url")],
)
.await;

// Expect `503` smartrest message on `c8y/s/us`.
assert_received_contains_str(&mut mqtt, [("c8y/s/us", "506,1234")]).await;
}
Expand Down
Loading

0 comments on commit 83b65bd

Please sign in to comment.