Skip to content

Commit c91524a

Browse files
committed
Disable nested child device operation support #2409
1 parent 23e71d9 commit c91524a

File tree

3 files changed

+168
-22
lines changed

3 files changed

+168
-22
lines changed

crates/extensions/c8y_mapper_ext/src/config_operations.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,13 +425,11 @@ impl CumulocityConverter {
425425
self.ops_dir.join(target.external_id.as_ref())
426426
}
427427
_ => {
428-
warn!("config_snapshot and config_update features for nested child devices are currently unsupported");
429428
return Ok(vec![]);
430429
}
431430
}
432431
}
433432
EntityType::Service => {
434-
warn!("config_snapshot and config_update features for services are currently unsupported");
435433
return Ok(vec![]);
436434
}
437435
};

crates/extensions/c8y_mapper_ext/src/converter.rs

Lines changed: 167 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::actor::IdDownloadRequest;
88
use crate::dynamic_discovery::DiscoverOp;
99
use crate::error::ConversionError;
1010
use crate::json;
11+
use anyhow::anyhow;
1112
use anyhow::Context;
1213
use c8y_api::http_proxy::C8yEndPoint;
1314
use c8y_api::json_c8y::C8yCreateEvent;
@@ -926,9 +927,27 @@ impl CumulocityConverter {
926927
vec![]
927928
}
928929

929-
Channel::CommandMetadata {
930-
operation: OperationType::Restart,
931-
} => self.register_restart_operation(&source).await?,
930+
Channel::CommandMetadata { operation } => {
931+
self.validate_operation_supported(operation, &source)?;
932+
match operation {
933+
OperationType::Restart => self.register_restart_operation(&source).await?,
934+
OperationType::SoftwareList => {
935+
self.register_software_list_operation(&source).await?
936+
}
937+
OperationType::SoftwareUpdate => {
938+
self.register_software_update_operation(&source).await?
939+
}
940+
OperationType::LogUpload => self.convert_log_metadata(&source, message)?,
941+
OperationType::ConfigSnapshot => {
942+
self.convert_config_snapshot_metadata(&source, message)?
943+
}
944+
OperationType::ConfigUpdate => {
945+
self.convert_config_update_metadata(&source, message)?
946+
}
947+
_ => vec![],
948+
}
949+
}
950+
932951
Channel::Command {
933952
operation: OperationType::Restart,
934953
cmd_id,
@@ -937,19 +956,13 @@ impl CumulocityConverter {
937956
.await?
938957
}
939958

940-
Channel::CommandMetadata {
941-
operation: OperationType::SoftwareList,
942-
} => self.register_software_list_operation(&source).await?,
943959
Channel::Command {
944960
operation: OperationType::SoftwareList,
945961
cmd_id,
946962
} if self.command_id.is_generator_of(cmd_id) => {
947963
self.publish_software_list(&source, cmd_id, message).await?
948964
}
949965

950-
Channel::CommandMetadata {
951-
operation: OperationType::SoftwareUpdate,
952-
} => self.register_software_update_operation(&source).await?,
953966
Channel::Command {
954967
operation: OperationType::SoftwareUpdate,
955968
cmd_id,
@@ -958,10 +971,6 @@ impl CumulocityConverter {
958971
.await?
959972
}
960973

961-
Channel::CommandMetadata {
962-
operation: OperationType::LogUpload,
963-
} => self.convert_log_metadata(&source, message)?,
964-
965974
Channel::Command {
966975
operation: OperationType::LogUpload,
967976
cmd_id,
@@ -970,9 +979,6 @@ impl CumulocityConverter {
970979
.await?
971980
}
972981

973-
Channel::CommandMetadata {
974-
operation: OperationType::ConfigSnapshot,
975-
} => self.convert_config_snapshot_metadata(&source, message)?,
976982
Channel::Command {
977983
operation: OperationType::ConfigSnapshot,
978984
cmd_id,
@@ -981,9 +987,6 @@ impl CumulocityConverter {
981987
.await?
982988
}
983989

984-
Channel::CommandMetadata {
985-
operation: OperationType::ConfigUpdate,
986-
} => self.convert_config_update_metadata(&source, message)?,
987990
Channel::Command {
988991
operation: OperationType::ConfigUpdate,
989992
cmd_id,
@@ -1000,6 +1003,29 @@ impl CumulocityConverter {
10001003
Ok(registration_messages)
10011004
}
10021005

1006+
fn validate_operation_supported(
1007+
&self,
1008+
op_type: &OperationType,
1009+
topic_id: &EntityTopicId,
1010+
) -> Result<(), ConversionError> {
1011+
let target = self.entity_store.try_get(topic_id)?;
1012+
1013+
match target.r#type {
1014+
EntityType::MainDevice => Ok(()),
1015+
EntityType::ChildDevice => match &target.parent {
1016+
Some(parent) if !parent.is_default_main_device() => {
1017+
Err(ConversionError::UnexpectedError(anyhow!(
1018+
"{op_type} operation for nested child devices are currently unsupported"
1019+
)))
1020+
}
1021+
_ => Ok(()),
1022+
},
1023+
EntityType::Service => Err(ConversionError::UnexpectedError(anyhow!(
1024+
"{op_type} operation for services are currently unsupported"
1025+
))),
1026+
}
1027+
}
1028+
10031029
pub fn register_and_convert_entity(
10041030
&mut self,
10051031
registration_message: &EntityRegistrationMessage,
@@ -2766,6 +2792,128 @@ pub(crate) mod tests {
27662792
assert_eq!(smartrest_fields.next().unwrap(), "up");
27672793
}
27682794

2795+
#[test_case("restart")]
2796+
#[test_case("software_list")]
2797+
#[test_case("software_update")]
2798+
#[test_case("log_upload")]
2799+
#[test_case("config_snapshot")]
2800+
#[test_case("config_update")]
2801+
#[test_case("custom_op")]
2802+
#[tokio::test]
2803+
async fn operations_not_supported_for_nested_child_devices(op_type: &str) {
2804+
let tmp_dir = TempTedgeDir::new();
2805+
let (mut converter, _http_proxy) = create_c8y_converter(&tmp_dir).await;
2806+
2807+
// Register immediate child device and nested child device
2808+
let reg_message = Message::new(
2809+
&Topic::new_unchecked("te/device/immediate_child//"),
2810+
json!({
2811+
"@type":"child-device",
2812+
"@parent":"device/main//",
2813+
"@id":"immediate_child"
2814+
})
2815+
.to_string(),
2816+
);
2817+
let _ = converter.convert(&reg_message).await;
2818+
let reg_message = Message::new(
2819+
&Topic::new_unchecked("te/device/nested_child//"),
2820+
json!({
2821+
"@type":"child-device",
2822+
"@parent":"device/immediate_child//",
2823+
"@id":"nested_child"
2824+
})
2825+
.to_string(),
2826+
);
2827+
let _ = converter.convert(&reg_message).await;
2828+
2829+
let capability_msg = Message::new(
2830+
&Topic::new_unchecked(&format!("te/device/nested_child///cmd/{op_type}")),
2831+
"[]",
2832+
);
2833+
let messages = converter.convert(&capability_msg).await;
2834+
2835+
assert_messages_matching(
2836+
&messages,
2837+
[(
2838+
"te/errors",
2839+
"operation for nested child devices are currently unsupported".into(),
2840+
)],
2841+
);
2842+
}
2843+
2844+
#[test_case("restart")]
2845+
#[test_case("software_list")]
2846+
#[test_case("software_update")]
2847+
#[test_case("log_upload")]
2848+
#[test_case("config_snapshot")]
2849+
#[test_case("config_update")]
2850+
#[test_case("custom_op")]
2851+
#[tokio::test]
2852+
async fn operations_not_supported_for_services(op_type: &str) {
2853+
let tmp_dir = TempTedgeDir::new();
2854+
let (mut converter, _http_proxy) = create_c8y_converter(&tmp_dir).await;
2855+
2856+
// Register main device service
2857+
let _ = converter
2858+
.convert(&Message::new(
2859+
&Topic::new_unchecked("te/device/main/service/dummy"),
2860+
json!({
2861+
"@type":"service",
2862+
})
2863+
.to_string(),
2864+
))
2865+
.await;
2866+
// Register immediate child device service
2867+
let _ = converter
2868+
.convert(&Message::new(
2869+
&Topic::new_unchecked("te/device/immediate_child/service/dummy"),
2870+
json!({
2871+
"@type":"service",
2872+
})
2873+
.to_string(),
2874+
))
2875+
.await;
2876+
// Register nested child device
2877+
let _ = converter
2878+
.convert(&Message::new(
2879+
&Topic::new_unchecked("te/device/nested_child//"),
2880+
json!({
2881+
"@type":"child-device",
2882+
"@parent":"device/immediate_child//",
2883+
})
2884+
.to_string(),
2885+
))
2886+
.await;
2887+
// Register nested child device service
2888+
let _ = converter
2889+
.convert(&Message::new(
2890+
&Topic::new_unchecked("te/device/nested_child/service/dummy"),
2891+
json!({
2892+
"@type":"service",
2893+
})
2894+
.to_string(),
2895+
))
2896+
.await;
2897+
2898+
for device_id in ["main", "immediate_child", "nested_child"] {
2899+
let messages = converter
2900+
.convert(&Message::new(
2901+
&Topic::new_unchecked(&format!(
2902+
"te/device/{device_id}/service/dummy/cmd/{op_type}"
2903+
)),
2904+
"[]",
2905+
))
2906+
.await;
2907+
assert_messages_matching(
2908+
&messages,
2909+
[(
2910+
"te/errors",
2911+
"operation for services are currently unsupported".into(),
2912+
)],
2913+
);
2914+
}
2915+
}
2916+
27692917
pub(crate) async fn create_c8y_converter(
27702918
tmp_dir: &TempTedgeDir,
27712919
) -> (

crates/extensions/c8y_mapper_ext/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ pub enum ConversionError {
126126
#[error(transparent)]
127127
InvalidExternalIdError(#[from] InvalidExternalIdError),
128128

129-
#[error("Unexpected error")]
129+
#[error("Unexpected error: {0}")]
130130
UnexpectedError(#[from] anyhow::Error),
131131
}
132132

0 commit comments

Comments
 (0)