Skip to content

Commit

Permalink
feat: convert device profile request to tedge json format
Browse files Browse the repository at this point in the history
Signed-off-by: Krzysztof Piotrowski <[email protected]>
  • Loading branch information
Ruadhri17 committed Jul 26, 2024
1 parent 8d4542b commit 403d198
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 1 deletion.
32 changes: 32 additions & 0 deletions crates/core/tedge_api/src/device_profile_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,35 @@ pub enum OperationPayload {
#[serde(rename = "payload")]
Config(ConfigInfo),
}

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

self.operations.push(firmware_operation);
}

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

self.operations.push(software_operation);
}

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

self.operations.push(config_operation);
}
}
13 changes: 12 additions & 1 deletion crates/extensions/c8y_mapper_ext/src/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,19 @@ impl CumulocityConverter {
}
C8yDeviceControlOperation::DeviceProfile(request) => {
if self.config.capabilities.device_profile {
vec![]
if let Some(profile_name) = extras.get("profileName") {
self.convert_device_profile_request(
device_xid,
cmd_id,
request,
serde_json::from_value(profile_name.clone())?,
)?
} else {
error!("Received a c8y_DeviceProfile without a profile name");
vec![]
}
} else {
warn!("Received a c8y_DeviceProfile operation, however, device_profile feature is disabled");
vec![]
}
}
Expand Down
176 changes: 176 additions & 0 deletions crates/extensions/c8y_mapper_ext/src/operations/device_profile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use crate::converter::CumulocityConverter;
use crate::error::CumulocityMapperError;
use c8y_api::json_c8y_deserializer::C8yDeviceProfile;
use tedge_api::device_profile_command::DeviceProfileCmdPayload;
use tedge_api::entity_store::EntityExternalId;
use tedge_api::mqtt_topics::Channel;
use tedge_api::mqtt_topics::OperationType;
use tedge_api::CommandStatus;
use tedge_api::Jsonify;
use tedge_mqtt_ext::MqttMessage;

impl CumulocityConverter {
/// Convert c8y_DeviceProfile JSON over MQTT operation to ThinEdge device_profile command.
pub fn convert_device_profile_request(
&self,
device_xid: String,
cmd_id: String,
device_profile_request: C8yDeviceProfile,
profile_name: String,
) -> Result<Vec<MqttMessage>, CumulocityMapperError> {
let entity_xid: EntityExternalId = device_xid.into();

let target = self.entity_store.try_get_by_external_id(&entity_xid)?;

let channel = Channel::Command {
operation: OperationType::DeviceProfile,
cmd_id,
};
let topic = self.mqtt_schema.topic_for(&target.topic_id, &channel);

let mut request = DeviceProfileCmdPayload {
status: CommandStatus::Init,
name: profile_name,
operations: Vec::new(),
};

request.add_firmware(device_profile_request.get_firmware_info());
request.add_software(device_profile_request.try_get_software_info()?);
for config in device_profile_request.get_config_info() {
request.add_config(config);
}

// Command messages must be retained
Ok(vec![
MqttMessage::new(&topic, request.to_json()).with_retain()
])
}
}

#[cfg(test)]
mod tests {
use crate::tests::skip_init_messages;
use crate::tests::spawn_c8y_mapper_actor;
use crate::tests::TestHandle;

use c8y_api::json_c8y_deserializer::C8yDeviceControlTopic;
use serde_json::json;
use std::time::Duration;
use tedge_actors::test_helpers::MessageReceiverExt;
use tedge_actors::Sender;
use tedge_mqtt_ext::test_helpers::assert_received_includes_json;
use tedge_mqtt_ext::MqttMessage;
use tedge_test_utils::fs::TempTedgeDir;

const TEST_TIMEOUT_MS: Duration = Duration::from_millis(3000);

#[tokio::test]
async fn mapper_converts_device_profile_operation_for_main_device() {
let ttd = TempTedgeDir::new();
let test_handle = spawn_c8y_mapper_actor(&ttd, true).await;
let TestHandle { mqtt, .. } = test_handle;
let mut mqtt = mqtt.with_timeout(TEST_TIMEOUT_MS);

skip_init_messages(&mut mqtt).await;

// Simulate c8y_DeviceProfile operation delivered via JSON over MQTT
mqtt.send(MqttMessage::new(
&C8yDeviceControlTopic::topic(&"c8y".try_into().unwrap()),
json!({
"id": "123456",
"profileName": "test-profile",
"c8y_DeviceProfile": {
"software": [
{
"softwareType": "apt",
"name": "test-software-1",
"action": "install",
"version": "latest",
"url": " "
},
{
"softwareType": "apt",
"name": "test-software-2",
"action": "install",
"version": "latest",
"url": " "
}
],
"configuration": [
{
"name": "test-software-1",
"type": "path/config/test-software-1",
"url": "http://www.my.url"
}
],
"firmware": {
"name": "test-firmware",
"version": "1.0",
"url": "http://www.my.url"
}
},
"externalSource": {
"externalId": "test-device",
"type": "c8y_Serial"
}
})
.to_string(),
))
.await
.expect("Send failed");

assert_received_includes_json(
&mut mqtt,
[(
"te/device/main///cmd/device_profile/c8y-mapper-123456",
json!({
"status": "init",
"name": "test-profile",
"operations": [
{
"operation": "firmware_update",
"skip": false,
"payload": {
"name": "test-firmware",
"version": "1.0",
"url": "http://www.my.url"
}
},
{
"operation": "software_update",
"skip": false,
"payload": {
"updateList": [
{
"type": "apt",
"modules": [
{
"name": "test-software-1",
"version": "latest",
"action": "install"
},
{
"name": "test-software-2",
"version": "latest",
"action": "install"
}
]
}
]
}
},
{
"operation": "config_update",
"skip": false,
"payload": {
"type": "path/config/test-software-1",
"remoteUrl":"http://www.my.url"
}
}
]
}),
)],
)
.await;
}
}
1 change: 1 addition & 0 deletions crates/extensions/c8y_mapper_ext/src/operations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use tracing::error;

pub mod config_snapshot;
pub mod config_update;
pub mod device_profile;
mod error;
pub mod firmware_update;
pub mod log_upload;
Expand Down

0 comments on commit 403d198

Please sign in to comment.