From 4c47e3311cd9c376561a3ab440d3e5f1339c2c05 Mon Sep 17 00:00:00 2001 From: William Lyles <26171886+wilyle@users.noreply.github.com> Date: Mon, 2 Oct 2023 10:01:15 -0700 Subject: [PATCH] config to in-memory dt adapter --- .../Cargo.toml | 6 +- .../README.md | 19 +++--- .../build.rs | 19 +++--- .../res/config.json | 32 ---------- ...in_memory_digital_twin_config.default.json | 34 +++++++++++ .../src/config.rs | 7 +++ .../in_memory_mock_digital_twin_adapter.rs | 58 ++++++++----------- 7 files changed, 91 insertions(+), 84 deletions(-) delete mode 100644 digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/config.json create mode 100644 digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/in_memory_digital_twin_config.default.json diff --git a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/Cargo.toml b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/Cargo.toml index 12e11f15..275b45ad 100644 --- a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/Cargo.toml +++ b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/Cargo.toml @@ -10,7 +10,11 @@ license = "MIT" [dependencies] async-trait = { workspace = true } +freyja-common = { workspace = true } freyja-contracts = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -tokio = { workspace = true } \ No newline at end of file +tokio = { workspace = true } + +[build-dependencies] +freyja-build-common = { workspace = true } \ No newline at end of file diff --git a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/README.md b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/README.md index 6f009eef..13a94b7d 100644 --- a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/README.md +++ b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/README.md @@ -4,11 +4,14 @@ The In-Memory Mock Digital Twin Adapter mocks the behavior of an in-vehicle digi ## Configuration -The adapter's config is located at `res/config.json` and will be copied to the build output automatically. This file is a list of `EntityConfig` objects with the following properties: - -- `entity`: a digital twin entity that will be exposed to the `find_by_id` API. Entities contain the following properties: - - `id`: this is used as the key when calling `find_by_id`. - - `uri`: the uri that is used to invoke a provider. This is a stand-in for whatever the provider contact info is from Ibeji. This is used as the key when calling `subscribe` and `get` in the [In-Memory Provider Proxy](../../provider_proxies/in_memory_mock_provider_proxy/). - - `operation`: the operation that should be used to access this entity. Supported values are `Get` and `Subscribe`. - - `protocol`: the communication protocol that should be used to access this entity. For this particular adapter, the value should always be `in-memory`. - - `name` and `description`: these are currently unused by Freyja. They are included for completeness and parity with Ibeji's Digital Twin Service contract and may potentially be logged. +This adapter supports the following configuration settings: + +- `values`: a list of entities to use. Each entry in the list is an object with the following properties: + - `entity`: a digital twin entity that will be exposed to the `find_by_id` API. Entities contain the following properties: + - `id`: this is used as the key when calling `find_by_id`. + - `uri`: the uri that is used to invoke a provider. This is a stand-in for whatever the provider contact info is from Ibeji. This is used as the key when calling `subscribe` and `get` in the [In-Memory Provider Proxy](../../provider_proxies/in_memory_mock_provider_proxy/). + - `operation`: the operation that should be used to access this entity. + - `protocol`: the communication protocol that should be used to access this entity. For this particular adapter, the value should always be `in-memory`. + - `name` and `description`: these are currently unused by Freyja. They are included for completeness and parity with Ibeji's Digital Twin Service contract and may potentially be logged. + +This adapter supports [config overrides](../../docs/config-overrides.md). The override filename is `in_memory_digital_twin_config.json`, and the default config is located at `res/in_memory_digital_twin_config.default.json`. \ No newline at end of file diff --git a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/build.rs b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/build.rs index e1d1c175..4d8bcb53 100644 --- a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/build.rs +++ b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/build.rs @@ -2,16 +2,19 @@ // Licensed under the MIT license. // SPDX-License-Identifier: MIT -use std::{env, fs, path::Path}; +use std::env; -fn main() { - // Current directory of the build script is the package's root directory - let config_path = env::current_dir().unwrap().join("res").join("config.json"); +use freyja_build_common::copy_to_build_out_dir; - let target_dir = env::var("OUT_DIR").unwrap(); - let dest_path = Path::new(&target_dir).join("config.json"); +const RES_DIR_NAME: &str = "res"; +const DEFAULT_CONFIG_FILE: &str = "in_memory_digital_twin_config.default.json"; - fs::copy(&config_path, dest_path).unwrap(); +fn main() { + // Current directory of the build script is the package's root directory + let config_path = env::current_dir() + .unwrap() + .join(RES_DIR_NAME) + .join(DEFAULT_CONFIG_FILE); - println!("cargo:rerun-if-changed={}", config_path.to_str().unwrap()); + copy_to_build_out_dir(config_path, DEFAULT_CONFIG_FILE); } diff --git a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/config.json b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/config.json deleted file mode 100644 index 628b7966..00000000 --- a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/config.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "entity": { - "id": "dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1", - "name": "AmbientAirTemperature", - "uri": "http://0.0.0.0:1111", - "description": "The immediate surroundings air temperature (in Fahrenheit).", - "operation": "Get", - "protocol": "in-memory" - } - }, - { - "entity": { - "id": "dtmi:sdv:Vehicle:Cabin:HVAC:IsAirConditioningActive;1", - "name": "IsAirConditioningActive", - "uri": "http://0.0.0.0:1111", - "description": "Is air conditioning active?", - "operation": "Subscribe", - "protocol": "in-memory" - } - }, - { - "entity": { - "id": "dtmi:sdv:Vehicle:OBD:HybridBatteryRemaining;1", - "name": "HybridBatteryRemaining", - "uri": "http://0.0.0.0:1111", - "description": "Percentage of the hybrid battery remaining", - "operation": "Subscribe", - "protocol": "in-memory" - } - } -] \ No newline at end of file diff --git a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/in_memory_digital_twin_config.default.json b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/in_memory_digital_twin_config.default.json new file mode 100644 index 00000000..bd297922 --- /dev/null +++ b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/res/in_memory_digital_twin_config.default.json @@ -0,0 +1,34 @@ +{ + "values": [ + { + "entity": { + "id": "dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1", + "name": "AmbientAirTemperature", + "uri": "http://0.0.0.0:1111", + "description": "The immediate surroundings air temperature (in Fahrenheit).", + "operation": "Get", + "protocol": "in-memory" + } + }, + { + "entity": { + "id": "dtmi:sdv:Vehicle:Cabin:HVAC:IsAirConditioningActive;1", + "name": "IsAirConditioningActive", + "uri": "http://0.0.0.0:1111", + "description": "Is air conditioning active?", + "operation": "Subscribe", + "protocol": "in-memory" + } + }, + { + "entity": { + "id": "dtmi:sdv:Vehicle:OBD:HybridBatteryRemaining;1", + "name": "HybridBatteryRemaining", + "uri": "http://0.0.0.0:1111", + "description": "Percentage of the hybrid battery remaining", + "operation": "Subscribe", + "protocol": "in-memory" + } + } + ] +} \ No newline at end of file diff --git a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/config.rs b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/config.rs index aaf7e230..d86cce36 100644 --- a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/config.rs +++ b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/config.rs @@ -6,6 +6,13 @@ use serde::{Deserialize, Serialize}; use freyja_contracts::entity::Entity; +/// The in-memory mock digital twin's config +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Config { + /// The set of config values + pub values: Vec, +} + /// Configuration for a entity #[derive(Clone, Debug, Serialize, Deserialize)] pub struct EntityConfig { diff --git a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/in_memory_mock_digital_twin_adapter.rs b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/in_memory_mock_digital_twin_adapter.rs index 5a5ca24d..29dddd52 100644 --- a/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/in_memory_mock_digital_twin_adapter.rs +++ b/digital_twin_adapters/in_memory_mock_digital_twin_adapter/src/in_memory_mock_digital_twin_adapter.rs @@ -2,48 +2,31 @@ // Licensed under the MIT license. // SPDX-License-Identifier: MIT -use std::{fs, path::Path}; - use async_trait::async_trait; -use crate::config::EntityConfig; +use crate::config::Config; +use freyja_common::{config_utils, out_dir}; use freyja_contracts::digital_twin_adapter::{ DigitalTwinAdapter, DigitalTwinAdapterError, DigitalTwinAdapterErrorKind, GetDigitalTwinProviderRequest, GetDigitalTwinProviderResponse, }; -const CONFIG_FILE: &str = "config.json"; +const CONFIG_FILE_STEM: &str = "in_memory_digital_twin_config"; /// In-memory mock that mocks finding endpoint info about entities /// through find by id pub struct InMemoryMockDigitalTwinAdapter { /// Stores configs about entities - data: Vec, + config: Config, } impl InMemoryMockDigitalTwinAdapter { - /// Creates a new InMemoryMockDigitalTwinAdapter with config from the specified file - /// - /// # Arguments - /// - `config_path`: the path to the config to use - pub fn from_config_file>( - config_path: P, - ) -> Result { - let config_contents = - fs::read_to_string(config_path).map_err(DigitalTwinAdapterError::io)?; - - let config: Vec = serde_json::from_str(config_contents.as_str()) - .map_err(DigitalTwinAdapterError::deserialize)?; - - Self::from_config(config) - } - /// Creates a new InMemoryMockDigitalTwinAdapter with the specified config /// /// # Arguments /// - `config`: the config to use - pub fn from_config(config: Vec) -> Result { - Ok(Self { data: config }) + pub fn from_config(config: Config) -> Result { + Ok(Self { config }) } } @@ -51,7 +34,15 @@ impl InMemoryMockDigitalTwinAdapter { impl DigitalTwinAdapter for InMemoryMockDigitalTwinAdapter { /// Creates a new instance of a DigitalTwinAdapter with default settings fn create_new() -> Result { - Self::from_config_file(Path::new(env!("OUT_DIR")).join(CONFIG_FILE)) + let config = config_utils::read_from_files( + CONFIG_FILE_STEM, + config_utils::JSON_EXT, + out_dir!(), + DigitalTwinAdapterError::io, + DigitalTwinAdapterError::deserialize, + )?; + + Self::from_config(config) } /// Gets the entity information based on the request @@ -62,7 +53,8 @@ impl DigitalTwinAdapter for InMemoryMockDigitalTwinAdapter { &self, request: GetDigitalTwinProviderRequest, ) -> Result { - self.data + self.config + .values .iter() .find(|entity_config| entity_config.entity.id == request.entity_id) .map(|entity_config| GetDigitalTwinProviderResponse { @@ -76,16 +68,11 @@ impl DigitalTwinAdapter for InMemoryMockDigitalTwinAdapter { mod in_memory_mock_digital_twin_adapter_tests { use super::*; + use crate::config::EntityConfig; use freyja_contracts::{entity::Entity, provider_proxy::OperationKind}; #[test] - fn from_config_file_returns_err_on_nonexistent_file() { - let result = InMemoryMockDigitalTwinAdapter::from_config_file("fake_file.foo"); - assert!(result.is_err()); - } - - #[test] - fn can_get_default_config() { + fn can_create_new() { let result = InMemoryMockDigitalTwinAdapter::create_new(); assert!(result.is_ok()); } @@ -94,7 +81,8 @@ mod in_memory_mock_digital_twin_adapter_tests { async fn find_by_id_test() { const ENTITY_ID: &str = "dtmi:sdv:Vehicle:Cabin:HVAC:AmbientAirTemperature;1"; - let data = vec![EntityConfig { + let config = Config { + values: vec![EntityConfig { entity: Entity { id: String::from(ENTITY_ID), name: None, @@ -103,9 +91,9 @@ mod in_memory_mock_digital_twin_adapter_tests { operation: OperationKind::Subscribe, protocol: String::from("in-memory"), }, - }]; + }]}; - let in_memory_digital_twin_adapter = InMemoryMockDigitalTwinAdapter { data }; + let in_memory_digital_twin_adapter = InMemoryMockDigitalTwinAdapter { config }; let request = GetDigitalTwinProviderRequest { entity_id: String::from(ENTITY_ID), };