From 89d375a97892aad6b4cf976a38581cb0d7a14936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Rodriguez?= Date: Tue, 31 Oct 2023 13:39:50 +0100 Subject: [PATCH] feat(digitalTwin): allow to modify mapping from application (#322) * feat(digitalTwin): add support modify mapping * test: fix erratic bug in functional tests --- jest.config.ts | 1 + .../device/collections/deviceMappings.ts | 4 +- lib/modules/plugin/DeviceManagerEngine.ts | 20 ++++----- lib/modules/plugin/DeviceManagerPlugin.ts | 10 ++++- .../types/DeviceManagerConfiguration.ts | 10 ++++- tests/application/app.ts | 11 +++++ tests/scenario/custom-mapping.test.ts | 43 +++++++++++++++++++ .../devices/action-export-measures.test.ts | 35 +++++++++------ 8 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 tests/scenario/custom-mapping.test.ts diff --git a/jest.config.ts b/jest.config.ts index 8cb6371b..25b3c88d 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -2,6 +2,7 @@ import type { Config } from "@jest/types"; // Sync object const config: Config.InitialOptions = { + testTimeout: 20000, transform: { "^.+\\.tsx?$": ["ts-jest", { tsconfig: "./tests/tsconfig.json" }], }, diff --git a/lib/modules/device/collections/deviceMappings.ts b/lib/modules/device/collections/deviceMappings.ts index 3fb8bb5f..b6e81918 100644 --- a/lib/modules/device/collections/deviceMappings.ts +++ b/lib/modules/device/collections/deviceMappings.ts @@ -1,9 +1,11 @@ +import { CollectionMappings } from "kuzzle"; + /** * Base mappings for the "devices" collection. * * Those mappings does not contains the `measures` and `metadata` mappings. */ -export const devicesMappings = { +export const devicesMappings: CollectionMappings = { dynamic: "strict", properties: { model: { diff --git a/lib/modules/plugin/DeviceManagerEngine.ts b/lib/modules/plugin/DeviceManagerEngine.ts index 847640bd..007f8e56 100644 --- a/lib/modules/plugin/DeviceManagerEngine.ts +++ b/lib/modules/plugin/DeviceManagerEngine.ts @@ -4,18 +4,13 @@ import { JSONObject } from "kuzzle-sdk"; import { AbstractEngine, ConfigManager } from "kuzzle-plugin-commons"; import { EngineContent } from "kuzzle-plugin-commons"; -import { - assetsMappings, - assetsHistoryMappings, - assetGroupsMappings, -} from "../asset"; +import { assetsHistoryMappings, assetGroupsMappings } from "../asset"; import { AssetModelContent, DeviceModelContent, MeasureModelContent, } from "../model"; import { getEmbeddedMeasureMappings, measuresMappings } from "../measure"; -import { devicesMappings } from "../device"; import { onAsk } from "../shared"; import { NamedMeasures } from "../decoder"; @@ -23,11 +18,6 @@ import { DeviceManagerConfiguration } from "./types/DeviceManagerConfiguration"; import { DeviceManagerPlugin } from "./DeviceManagerPlugin"; import { InternalCollection } from "./types/InternalCollection"; -const digitalTwinMappings = { - asset: assetsMappings, - device: devicesMappings, -} as const; - export type AskEngineList = { name: "ask:device-manager:engine:list"; @@ -211,6 +201,12 @@ export class DeviceManagerEngine extends AbstractEngine { private async getDigitalTwinMappings< TDigitalTwinModelContent extends AssetModelContent | DeviceModelContent >(digitalTwinType: "asset" | "device", engineGroup?: string) { + if ( + this.config.engineCollections[digitalTwinType] === undefined || + this.config.engineCollections[digitalTwinType].mappings === undefined + ) { + throw new InternalError(`Cannot find mapping for "${digitalTwinType}"`); + } const models = await this.getModels( this.config.adminIndex, digitalTwinType, @@ -223,7 +219,7 @@ export class DeviceManagerEngine extends AbstractEngine { ); const mappings = JSON.parse( - JSON.stringify(digitalTwinMappings[digitalTwinType]) + JSON.stringify(this.config.engineCollections[digitalTwinType].mappings) ); for (const model of models) { diff --git a/lib/modules/plugin/DeviceManagerPlugin.ts b/lib/modules/plugin/DeviceManagerPlugin.ts index 79fa568c..e2b748cc 100644 --- a/lib/modules/plugin/DeviceManagerPlugin.ts +++ b/lib/modules/plugin/DeviceManagerPlugin.ts @@ -20,7 +20,7 @@ import { import { DeviceModule, devicesMappings } from "../device"; import { MeasureModule } from "../measure"; -import { AssetModule } from "../asset"; +import { AssetModule, assetsMappings } from "../asset"; import { DecoderModule, NamedMeasures, @@ -217,6 +217,14 @@ export class DeviceManagerPlugin extends Plugin { }, settings: {}, }, + asset: { + name: InternalCollection.ASSETS, + mappings: assetsMappings, + }, + device: { + name: InternalCollection.DEVICES, + mappings: devicesMappings, + }, }, }; /* eslint-enable sort-keys */ diff --git a/lib/modules/plugin/types/DeviceManagerConfiguration.ts b/lib/modules/plugin/types/DeviceManagerConfiguration.ts index 651e0dc6..cb04593a 100644 --- a/lib/modules/plugin/types/DeviceManagerConfiguration.ts +++ b/lib/modules/plugin/types/DeviceManagerConfiguration.ts @@ -1,4 +1,4 @@ -import { JSONObject } from "kuzzle-sdk"; +import { CollectionMappings, JSONObject } from "kuzzle-sdk"; export type DeviceManagerConfiguration = { /** @@ -52,5 +52,13 @@ export type DeviceManagerConfiguration = { mappings: JSONObject; settings: JSONObject; }; + asset: { + name: string; + mappings: CollectionMappings; + }; + device: { + name: string; + mappings: CollectionMappings; + }; }; }; diff --git a/tests/application/app.ts b/tests/application/app.ts index a30a7afc..d9dfa945 100644 --- a/tests/application/app.ts +++ b/tests/application/app.ts @@ -15,6 +15,17 @@ const app = new Backend("kuzzle"); const deviceManager = new DeviceManagerPlugin(); +//? Add custom mapping properties +deviceManager.config.engineCollections.asset.mappings.properties["softTenant"] = { + type: "keyword", + fields: { text: { type: "text" } }, +}; +deviceManager.config.engineCollections.device.mappings.properties["softTenant"] = { + type: "keyword", + fields: { text: { type: "text" } }, +}; + + deviceManager.models.registerDevice("DummyTempPosition", { decoder: new DummyTempPositionDecoder(), metadataMappings: { diff --git a/tests/scenario/custom-mapping.test.ts b/tests/scenario/custom-mapping.test.ts new file mode 100644 index 00000000..3531ceeb --- /dev/null +++ b/tests/scenario/custom-mapping.test.ts @@ -0,0 +1,43 @@ +import { JSONObject } from "kuzzle-sdk"; +import { useSdk } from "../helpers"; +import { beforeAllCreateEngines } from "../hooks/engines"; + +describe("Assets mapping", () => { + const sdk = useSdk(); + + beforeAll(async () => { + await sdk.connect(); + + await beforeAllCreateEngines(sdk); + }); + + afterAll(async () => { + sdk.disconnect(); + }); + + it("asset mapping can be customize by application", async () => { + const mapping: JSONObject = await sdk.collection.getMapping( + "engine-ayse", + "assets" + ); + + expect(mapping.properties?.softTenant).toBeDefined(); + expect(mapping.properties.softTenant).toMatchObject({ + type: "keyword", + fields: { text: { type: "text" } }, + }); + }); + + it("device mapping can be customize by application", async () => { + const mapping: JSONObject = await sdk.collection.getMapping( + "engine-ayse", + "devices" + ); + + expect(mapping.properties?.softTenant).toBeDefined(); + expect(mapping.properties.softTenant).toMatchObject({ + type: "keyword", + fields: { text: { type: "text" } }, + }); + }); +}); diff --git a/tests/scenario/modules/devices/action-export-measures.test.ts b/tests/scenario/modules/devices/action-export-measures.test.ts index 8f528397..447bce18 100644 --- a/tests/scenario/modules/devices/action-export-measures.test.ts +++ b/tests/scenario/modules/devices/action-export-measures.test.ts @@ -80,20 +80,31 @@ describe("DevicesController:exportMeasures", () => { { deviceEUI: "linked1", temperature: 38 }, { deviceEUI: "linked1", - temperature: 37, + temperature: { + value: 37, + /** + * Here we specify a measuredAt, because of a side effect in the decoder. + * Sometimes the acceleration measurement would be registered earlier than the temperature's one. + * + * The +10sec is to ensure that those two measures will always remain the last ones. + */ + measuredAt: Date.now() + 10000, + }, acceleration: { - x: 1, - y: 2.5, - z: -1, - accuracy: 0.1, + value: { + x: 1, + y: 2.5, + z: -1, + accuracy: 0.1, + }, + /** + * Here we specify a measuredAt, because of a side effect in the decoder. + * Sometimes the battery measurement would be registered earlier than the acceleration's one. + * + * The +5sec is to ensure that those two measures will always remain the last ones. + */ + measuredAt: Date.now() + 5000, }, - /** - * Here we specify a measuredAt, because of a side effect in the decoder. - * Sometimes the acceleration measurement would be registered earlier than the temperature's one. - * - * The +10sec is to ensure that those two measures will always remain the last ones. - */ - measuredAt: Date.now() + 10000, }, ]); await sdk.collection.refresh("engine-ayse", "measures");