From 760b5bee2ef662918b7dcacc0417e996077a92ab Mon Sep 17 00:00:00 2001 From: danielpeintner Date: Wed, 23 Aug 2023 17:11:48 +0200 Subject: [PATCH] refactor: add transformation code to TODO: properties etc --- .../src/util/asset-interface-description.ts | 111 +++++++++++++++++- .../test/AssetInterfaceDescriptionTest.ts | 93 ++++++++++++++- 2 files changed, 194 insertions(+), 10 deletions(-) diff --git a/packages/td-tools/src/util/asset-interface-description.ts b/packages/td-tools/src/util/asset-interface-description.ts index 36743a782..f9d62dc2c 100644 --- a/packages/td-tools/src/util/asset-interface-description.ts +++ b/packages/td-tools/src/util/asset-interface-description.ts @@ -19,6 +19,7 @@ import { SecurityScheme } from "wot-thing-description-types"; import * as TDParser from "../td-parser"; import debug from "debug"; +import { ThingDescription } from "wot-typescript-definitions"; const namespace = "node-wot:td-tools:asset-interface-description-util"; const logDebug = debug(`${namespace}:debug`); const logInfo = debug(`${namespace}:info`); @@ -53,6 +54,17 @@ interface SubmodelInformation { endpointMetadataArray: Array>; } +// TODO can/shall we define als AAS/AID contructs? Difficult since idShort and value array is used *everywhere* +interface EndpointMetadata { + idShort: "EndpointMetadata"; + value: Array; + modelType: "SubmodelElementCollection"; +} + +interface EndpointMetadataValue { + idShort: string; // base, contentType, securityDefinitions +} + const noSecSS: SecurityScheme = { scheme: "nosec" }; const noSecName = 0 + "_sc"; @@ -565,9 +577,13 @@ export class AssetInterfaceDescriptionUtil { * @param td input TD * @returns transformed AID submodel definition in JSON format */ - public transformTD2SM(td: string): string { - const thing = TDParser.parseTD(td); - console.log("TD " + thing.title + " parsed..."); + public transformTD2SM(tdAsString: string): string { + const td: ThingDescription = TDParser.parseTD(tdAsString); + + console.log("TD " + td.title + " parsed..."); + + // configuration + const submodelElementIdShort = "InterfaceHTTP"; // TODO // value entry "idShort": "EndpointMetadata" @@ -581,12 +597,97 @@ export class AssetInterfaceDescriptionUtil { // TODO does this need to be an array or can it simply be a value { language: "en", - text: thing.title, // TODO should be description, where does title go to? later on in submodel? + text: td.title, // TODO should be description, where does title go to? later on in submodel? }, ], - submodels: [], + submodelElements: [ + { + idShort: submodelElementIdShort, + // semanticId needed? + // embeddedDataSpecifications needed? + value: [ + // support + this.createEndpointMetadata(td), // EndpointMetadata + this.createInterfaceMetadata(td), // InterfaceMetadata + // externalDescriptor ? + ], + modelType: "SubmodelElementCollection", + }, + ], + modelType: "Submodel", }; return JSON.stringify(aidObject); } + + private createEndpointMetadata(td: ThingDescription): Record { + const values: Array = []; + + // base ? + if (td.base) { + values.push({ + idShort: "base", + valueType: "xs:anyURI", + value: td.base, // TODO + modelType: "Property", + }); + } + + // TODO wrong place.. not allowed in TD spec? + /* + { + idShort: "contentType", + valueType: "xs:string", + value: "application/json", // TODO + modelType: "Property", + }, + */ + + // securityDefinitions + const secValues: Array = []; + for (const secKey in td.securityDefinitions) { + const secValue: SecurityScheme = td.securityDefinitions[secKey]; + secValues.push({ + idShort: secValue.scheme, + modelType: "SubmodelElementCollection", // TODO correct or Property ? + // modelType: "Property" + }); + } + + values.push({ + idShort: "securityDefinitions", + value: [ + { + idShort: "scheme", + value: secValues, + modelType: "SubmodelElementCollection", + }, + ], + modelType: "SubmodelElementCollection", + }); + + const endpointMetadata: Record = { + idShort: "EndpointMetadata", + // semanticId ? + // embeddedDataSpecifications ? + value: values, + modelType: "SubmodelElementCollection", + }; + + return endpointMetadata; + } + + private createInterfaceMetadata(td: ThingDescription): Record { + const interfaceMetadata: Record = { + idShort: "InterfaceMetadata", + // semanticId ? + // embeddedDataSpecifications ? + value: [ + // TODO + ], + modelType: "SubmodelElementCollection", + }; + + return interfaceMetadata; + } } diff --git a/packages/td-tools/test/AssetInterfaceDescriptionTest.ts b/packages/td-tools/test/AssetInterfaceDescriptionTest.ts index 65130c590..d1b38c1a6 100644 --- a/packages/td-tools/test/AssetInterfaceDescriptionTest.ts +++ b/packages/td-tools/test/AssetInterfaceDescriptionTest.ts @@ -18,6 +18,7 @@ import { expect } from "chai"; import { AssetInterfaceDescriptionUtil } from "../src/util/asset-interface-description"; import { promises as fs } from "fs"; +import { ThingDescription } from "wot-typescript-definitions"; @suite("tests to verify the Asset Interface Description Utils") class AssetInterfaceDescriptionUtilTest { @@ -363,13 +364,95 @@ class AssetInterfaceDescriptionUtilTest { } @test async "should correctly transform sample TD into JSON submodel"() { - const td = `{"title": "testTD"}`; - const sm = this.assetInterfaceDescriptionUtil.transformTD2SM(td); + const base = "https://www.example.com/"; + const td: ThingDescription = { + "@context": "https://www.w3.org/2022/wot/td/v1.1", + title: "testTD", + securityDefinitions: { + basic_sc: { + scheme: "basic", + in: "header", + }, + }, + security: "basic_sc", + base: base, + properties: { + status: { + type: "string", + observable: true, + forms: [ + { + href: "stat", + }, + ], + }, + }, + }; + const sm = this.assetInterfaceDescriptionUtil.transformTD2SM(JSON.stringify(td)); const smObj = JSON.parse(sm); expect(smObj).to.have.property("idShort").that.equals("AssetInterfacesDescription"); - - // TODO EndpointMetadata and InterfaceMetadata + expect(smObj).to.have.property("submodelElements").to.be.an("array").to.have.lengthOf.greaterThan(0); + const smInterface = smObj.submodelElements[0]; + expect(smInterface).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0); + let hasEndpointMetadata = false; + for (const smValue of smInterface.value) { + if (smValue.idShort === "EndpointMetadata") { + hasEndpointMetadata = true; + const endpointMetadata = smValue; + expect(endpointMetadata).to.have.property("value").to.be.an("array").to.have.lengthOf.greaterThan(0); + let hasBase = false; + let hasContentType = false; + let hasSecurityDefinitions = false; + for (const endpointMetadataValue of endpointMetadata.value) { + if (endpointMetadataValue.idShort === "base") { + hasBase = true; + expect(endpointMetadataValue.value).to.equal(base); + } else if (endpointMetadataValue.idShort === "contentType") { + hasContentType = true; + } else if (endpointMetadataValue.idShort === "securityDefinitions") { + hasSecurityDefinitions = true; + expect(endpointMetadataValue) + .to.have.property("value") + .to.be.an("array") + .to.have.lengthOf.greaterThan(0); + let hasScheme = false; + for (const securityDefinitionValue of endpointMetadataValue.value) { + if (securityDefinitionValue.idShort === "scheme") { + hasScheme = true; + expect(securityDefinitionValue) + .to.have.property("value") + .to.be.an("array") + .to.have.lengthOf.greaterThan(0); + let hasBasic = false; + for (const sec of securityDefinitionValue.value) { + if (sec.idShort === "basic") { + hasBasic = true; + } + } + expect(hasBasic).to.equal(true); + } + } + expect(hasScheme).to.equal(true); + + // expect(endpointMetadataValue.value).to.equal("basic"); + } + } + expect(hasBase).to.equal(true); + expect(hasContentType).to.equal(false); + expect(hasBase).to.equal(hasSecurityDefinitions); + } + } + expect(hasEndpointMetadata, "No EndpointMetadata").to.equal(true); + + // TODO InterfaceMetadata with properties etc + let hasInterfaceMetadata = false; + for (const smValue of smInterface.value) { + if (smValue.idShort === "InterfaceMetadata") { + hasInterfaceMetadata = true; + } + } + expect(hasInterfaceMetadata, "No InterfaceMetadata").to.equal(true); } @test async "should correctly transform sample TD into JSON AAS"() { @@ -378,7 +461,7 @@ class AssetInterfaceDescriptionUtilTest { const aasObj = JSON.parse(sm); expect(aasObj).to.have.property("assetAdministrationShells").to.be.an("array"); - expect(aasObj).to.have.property("submodels").to.be.an("array"); + expect(aasObj).to.have.property("submodels").to.be.an("array").to.have.lengthOf(1); // TODO more checks }