Skip to content

Commit

Permalink
Merge pull request #3230 from albinsuresh/feat/entity-management-http…
Browse files Browse the repository at this point in the history
…-apis

feat: Entity registration HTTP API
  • Loading branch information
albinsuresh authored Jan 17, 2025
2 parents 630aa39 + 60d58ca commit 3de87d1
Show file tree
Hide file tree
Showing 52 changed files with 2,287 additions and 1,657 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions crates/common/tedge_config/src/tedge_config_cli/tedge_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,18 @@ define_tedge_config! {
#[tedge_config(example = "true", default(value = true))]
log_upload: bool,
},

entity_store: {
/// Enable auto registration feature
#[tedge_config(example = "true", default(value = true), deprecated_key = "c8y.entity_store.auto_register")]
auto_register: bool,

/// On a clean start, the whole state of the device, services and child-devices is resent to the cloud
#[tedge_config(example = "true", default(value = true), deprecated_key = "c8y.entity_store.clean_start")]
clean_start: bool,
},


},

software: {
Expand Down
136 changes: 37 additions & 99 deletions crates/core/c8y_api/src/json_c8y.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ use std::fmt;
use tedge_api::alarm::ThinEdgeAlarm;
use tedge_api::alarm::ThinEdgeAlarmData;
use tedge_api::commands::SoftwareListCommand;
use tedge_api::entity_store::EntityMetadata;
use tedge_api::entity_store::EntityType;
use tedge_api::entity::EntityExternalId;
use tedge_api::entity::EntityType;
use tedge_api::event::ThinEdgeEvent;
use tedge_api::EntityStore;
use tedge_api::Jsonify;
use tedge_api::SoftwareModule;
use time::OffsetDateTime;
Expand Down Expand Up @@ -239,38 +238,36 @@ pub struct C8yClearAlarm {
}

impl C8yAlarm {
pub fn try_from(
pub fn from(
alarm: &ThinEdgeAlarm,
entity_store: &EntityStore,
) -> Result<Self, C8yAlarmError> {
if let Some(entity) = entity_store.get(&alarm.source) {
let source = Self::convert_source(entity);
let alarm_type = Self::convert_alarm_type(&alarm.alarm_type);

let c8y_alarm = match alarm.data.as_ref() {
None => C8yAlarm::Clear(C8yClearAlarm { alarm_type, source }),
Some(tedge_alarm_data) => C8yAlarm::Create(C8yCreateAlarm {
alarm_type: alarm_type.clone(),
source,
severity: C8yCreateAlarm::convert_severity(tedge_alarm_data),
text: C8yCreateAlarm::convert_text(tedge_alarm_data, &alarm_type),
time: C8yCreateAlarm::convert_time(tedge_alarm_data),
fragments: C8yCreateAlarm::convert_extras(tedge_alarm_data),
}),
};
Ok(c8y_alarm)
} else {
Err(C8yAlarmError::UnsupportedDeviceTopicId(
alarm.source.to_string(),
))
}
external_id: &EntityExternalId,
entity_type: &EntityType,
) -> Self {
let source = Self::convert_source(external_id, entity_type);
let alarm_type = Self::convert_alarm_type(&alarm.alarm_type);

let c8y_alarm = match alarm.data.as_ref() {
None => C8yAlarm::Clear(C8yClearAlarm { alarm_type, source }),
Some(tedge_alarm_data) => C8yAlarm::Create(C8yCreateAlarm {
alarm_type: alarm_type.clone(),
source,
severity: C8yCreateAlarm::convert_severity(tedge_alarm_data),
text: C8yCreateAlarm::convert_text(tedge_alarm_data, &alarm_type),
time: C8yCreateAlarm::convert_time(tedge_alarm_data),
fragments: C8yCreateAlarm::convert_extras(tedge_alarm_data),
}),
};
c8y_alarm
}

fn convert_source(entity: &EntityMetadata) -> Option<SourceInfo> {
match entity.r#type {
fn convert_source(
external_id: &EntityExternalId,
entity_type: &EntityType,
) -> Option<SourceInfo> {
match entity_type {
EntityType::MainDevice => None,
EntityType::ChildDevice => Some(make_c8y_source_fragment(entity.external_id.as_ref())),
EntityType::Service => Some(make_c8y_source_fragment(entity.external_id.as_ref())),
EntityType::ChildDevice => Some(make_c8y_source_fragment(external_id.as_ref())),
EntityType::Service => Some(make_c8y_source_fragment(external_id.as_ref())),
}
}

Expand Down Expand Up @@ -360,19 +357,12 @@ mod tests {
use crate::json_c8y::AlarmSeverity;
use anyhow::Result;
use assert_matches::assert_matches;
use mqtt_channel::MqttMessage;
use mqtt_channel::Topic;
use serde_json::json;
use std::collections::HashSet;
use tedge_api::alarm::ThinEdgeAlarm;
use tedge_api::alarm::ThinEdgeAlarmData;
use tedge_api::commands::SoftwareListCommandPayload;
use tedge_api::entity_store::EntityExternalId;
use tedge_api::entity_store::EntityRegistrationMessage;
use tedge_api::entity_store::InvalidExternalIdError;
use tedge_api::event::ThinEdgeEventData;
use tedge_api::mqtt_topics::EntityTopicId;
use tedge_api::mqtt_topics::MqttSchema;
use test_case::test_case;
use time::macros::datetime;

Expand Down Expand Up @@ -709,28 +699,13 @@ mod tests {
;"convert to clear alarm"
)]
fn check_alarm_translation(tedge_alarm: ThinEdgeAlarm, expected_c8y_alarm: C8yAlarm) {
let temp_dir = tempfile::tempdir().unwrap();
let main_device = EntityRegistrationMessage::main_device("test-main".into());
let mut entity_store = EntityStore::with_main_device_and_default_service_type(
MqttSchema::default(),
main_device,
"service".into(),
dummy_external_id_mapper,
dummy_external_id_validator,
5,
&temp_dir,
true,
)
.unwrap();

let child_registration = EntityRegistrationMessage::new(&MqttMessage::new(
&Topic::new_unchecked("te/device/external_source//"),
r#"{"@id": "external_source", "@type": "child-device"}"#,
))
.unwrap();
entity_store.update(child_registration).unwrap();

let actual_c8y_alarm = C8yAlarm::try_from(&tedge_alarm, &entity_store).unwrap();
let (external_id, entity_type) = if tedge_alarm.source.is_default_main_device() {
("main_device".into(), EntityType::MainDevice)
} else {
("external_source".into(), EntityType::ChildDevice)
};

let actual_c8y_alarm = C8yAlarm::from(&tedge_alarm, &external_id, &entity_type);
assert_eq!(actual_c8y_alarm, expected_c8y_alarm);
}

Expand All @@ -746,50 +721,13 @@ mod tests {
extras: HashMap::new(),
}),
};
let external_id = "main".into();

let temp_dir = tempfile::tempdir().unwrap();
let main_device = EntityRegistrationMessage::main_device("test-main".into());
let entity_store = EntityStore::with_main_device_and_default_service_type(
MqttSchema::default(),
main_device,
"service".into(),
dummy_external_id_mapper,
dummy_external_id_validator,
5,
&temp_dir,
true,
)
.unwrap();

match C8yAlarm::try_from(&tedge_alarm, &entity_store).unwrap() {
match C8yAlarm::from(&tedge_alarm, &external_id, &EntityType::MainDevice) {
C8yAlarm::Create(value) => {
assert!(value.time.millisecond() > 0);
}
C8yAlarm::Clear(_) => panic!("Must be C8yAlarm::Create"),
};
}

fn dummy_external_id_mapper(
entity_topic_id: &EntityTopicId,
_main_device_xid: &EntityExternalId,
) -> EntityExternalId {
entity_topic_id
.to_string()
.trim_end_matches('/')
.replace('/', ":")
.into()
}

fn dummy_external_id_validator(id: &str) -> Result<EntityExternalId, InvalidExternalIdError> {
let forbidden_chars = HashSet::from(['/', '+', '#']);
for c in id.chars() {
if forbidden_chars.contains(&c) {
return Err(InvalidExternalIdError {
external_id: id.into(),
invalid_char: c,
});
}
}
Ok(id.into())
}
}
4 changes: 2 additions & 2 deletions crates/core/c8y_api/src/smartrest/topic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::json_c8y::C8yAlarm;
use mqtt_channel::MqttError;
use mqtt_channel::Topic;
use mqtt_channel::TopicFilter;
use tedge_api::entity_store::EntityExternalId;
use tedge_api::entity_store::EntityType;
use tedge_api::entity::EntityExternalId;
use tedge_api::entity::EntityType;
use tedge_config::TopicPrefix;

const SMARTREST_PUBLISH_TOPIC: &str = "s/us";
Expand Down
4 changes: 1 addition & 3 deletions crates/core/tedge/src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ impl TEdgeInitCmd {
create_directory(&config.logs.path, &permissions)?;
create_directory(&config.data.path, &permissions)?;

let entity_store_file = config_dir
.join(".tedge-mapper-c8y")
.join("entity_store.jsonl");
let entity_store_file = config_dir.join(".agent").join("entity_store.jsonl");

if entity_store_file.exists() {
change_user_and_group(entity_store_file.as_std_path(), &self.user, &self.group)?;
Expand Down
1 change: 1 addition & 0 deletions crates/core/tedge_agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ tracing = { workspace = true }
which = { workspace = true }

[dev-dependencies]
assert-json-diff = { workspace = true }
axum_tls = { workspace = true, features = ["test-helpers"] }
bytes = { workspace = true }
http-body = { workspace = true }
Expand Down
Loading

0 comments on commit 3de87d1

Please sign in to comment.