From 378daf33e913818c9d89022ee909c8892412b6e4 Mon Sep 17 00:00:00 2001 From: lukesmolo Date: Tue, 29 Mar 2022 12:56:47 +0200 Subject: [PATCH 1/4] thing-model: add json-placeholder-replacer library for handling placeholder mapping, issue #680 --- packages/td-tools/package.json | 1 + packages/td-tools/src/thing-model-helpers.ts | 27 +++----------- .../td-tools/test/ThingModelHelperTest.ts | 35 +++++++++++++++++++ 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/packages/td-tools/package.json b/packages/td-tools/package.json index 3fc203b62..8c4d8f682 100644 --- a/packages/td-tools/package.json +++ b/packages/td-tools/package.json @@ -41,6 +41,7 @@ }, "dependencies": { "is-absolute-url": "3.0.3", + "json-placeholder-replacer": "^1.0.35", "url-toolkit": "2.1.6", "wot-thing-description-types": "^1.1.0-09-February-2022", "wot-thing-model-types": "^1.1.0-3-February-2022", diff --git a/packages/td-tools/src/thing-model-helpers.ts b/packages/td-tools/src/thing-model-helpers.ts index e3f20146f..870b63bdc 100644 --- a/packages/td-tools/src/thing-model-helpers.ts +++ b/packages/td-tools/src/thing-model-helpers.ts @@ -17,6 +17,7 @@ import Ajv, { ValidateFunction, ErrorObject } from "ajv"; import * as http from "http"; import * as https from "https"; import * as fs from "fs"; +import { JsonPlaceholderReplacer } from "json-placeholder-replacer"; import { LinkElement } from "wot-thing-description-types"; import { DataSchema, ExposedThingInit } from "wot-typescript-definitions"; import { ThingModel } from "wot-thing-model-types"; @@ -146,6 +147,7 @@ export class ThingModelHelpers { * @experimental */ public static validateThingModel(data: ThingModel): { valid: boolean; errors: string } { + console.log(data) const isValid = ThingModelHelpers.tsSchemaValidator(data); let errors; if (!isValid) { @@ -552,28 +554,9 @@ export class ThingModelHelpers { } private fillPlaceholder(data: Record, map: Record): ThingModel { - let dataString = JSON.stringify(data); - for (const key in map) { - const value = map[key]; - let word = `{{${key}}}`; - const instances = (dataString.match(new RegExp(word, "g")) || []).length; - for (let i = 0; i < instances; i++) { - word = `{{${key}}}`; - const re = `"(${word})"`; - const match = dataString.match(re); - if (match === null) { - // word is included in another string/number/element. Keep that type - dataString = dataString.replace(word, value as string); - } else { - // keep the new value type - if (typeof value !== "string") { - word = `"{{${key}}}"`; - } - dataString = dataString.replace(word, value as string); - } - } - } - return JSON.parse(dataString); + const placeHolderReplacer = new JsonPlaceholderReplacer(); + placeHolderReplacer.addVariableMap(map); + return placeHolderReplacer.replace(data) as ThingModel; } private checkPlaceholderMap(model: ThingModel, map: Record): { valid: boolean; errors: string } { diff --git a/packages/td-tools/test/ThingModelHelperTest.ts b/packages/td-tools/test/ThingModelHelperTest.ts index a3d8716b5..3003e259f 100644 --- a/packages/td-tools/test/ThingModelHelperTest.ts +++ b/packages/td-tools/test/ThingModelHelperTest.ts @@ -350,6 +350,41 @@ class ThingModelHelperTest { expect(partialTd).to.be.deep.equal(finalJSON); } + @test async "should correctly fill placeholders with composed types"() { + const thing = { + "@context": ["http://www.w3.org/ns/td"], + "@type": "tm:ThingModel", + "arrayField": "{{ARRAY}}", + title: "Thermostate No. 4", + "versionInfo": "{{VERSION_INFO}}" + + } as unknown as ThingModel; + const map = { + ARRAY: ["random", "random1", "random2"], + VERSION_INFO: { "instance": "xyz", "model": "ABC" }, + }; + const finalJSON = { + "@context": ["http://www.w3.org/ns/td"], + "@type": "Thing", + title: "Thermostate No. 4", + "arrayField": ["random", "random1", "random2"], + "versionInfo": { "instance": "xyz", "model": "ABC" }, + links: [ + { + href: "./ThermostateNo.4.tm.jsonld", + rel: "type", + type: "application/tm+json", + }, + ], + }; + const options: CompositionOptions = { + map, + selfComposition: false, + }; + const [partialTd] = await this.thingModelHelpers.getPartialTDs(thing, options); + expect(partialTd).to.be.deep.equal(finalJSON); + } + @test async "should reject fill placeholders because of missing fields in map"() { const thing = { "@context": ["http://www.w3.org/ns/td"], From 07b87ca4029fe506024c51e62c39736e16103540 Mon Sep 17 00:00:00 2001 From: lukesmolo Date: Tue, 29 Mar 2022 13:04:32 +0200 Subject: [PATCH 2/4] thing-model: fix format --- packages/td-tools/src/thing-model-helpers.ts | 1 - packages/td-tools/test/ThingModelHelperTest.ts | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/td-tools/src/thing-model-helpers.ts b/packages/td-tools/src/thing-model-helpers.ts index 870b63bdc..4a42df268 100644 --- a/packages/td-tools/src/thing-model-helpers.ts +++ b/packages/td-tools/src/thing-model-helpers.ts @@ -147,7 +147,6 @@ export class ThingModelHelpers { * @experimental */ public static validateThingModel(data: ThingModel): { valid: boolean; errors: string } { - console.log(data) const isValid = ThingModelHelpers.tsSchemaValidator(data); let errors; if (!isValid) { diff --git a/packages/td-tools/test/ThingModelHelperTest.ts b/packages/td-tools/test/ThingModelHelperTest.ts index 3003e259f..ee3a95b45 100644 --- a/packages/td-tools/test/ThingModelHelperTest.ts +++ b/packages/td-tools/test/ThingModelHelperTest.ts @@ -354,21 +354,20 @@ class ThingModelHelperTest { const thing = { "@context": ["http://www.w3.org/ns/td"], "@type": "tm:ThingModel", - "arrayField": "{{ARRAY}}", + arrayField: "{{ARRAY}}", title: "Thermostate No. 4", - "versionInfo": "{{VERSION_INFO}}" - + versionInfo: "{{VERSION_INFO}}", } as unknown as ThingModel; const map = { ARRAY: ["random", "random1", "random2"], - VERSION_INFO: { "instance": "xyz", "model": "ABC" }, + VERSION_INFO: { instance: "xyz", model: "ABC" }, }; const finalJSON = { "@context": ["http://www.w3.org/ns/td"], "@type": "Thing", title: "Thermostate No. 4", - "arrayField": ["random", "random1", "random2"], - "versionInfo": { "instance": "xyz", "model": "ABC" }, + arrayField: ["random", "random1", "random2"], + versionInfo: { instance: "xyz", model: "ABC" }, links: [ { href: "./ThermostateNo.4.tm.jsonld", From 9c917a2e961915c604c82edcc0d8f64772ba06d8 Mon Sep 17 00:00:00 2001 From: lukesmolo Date: Wed, 30 Mar 2022 14:14:23 +0200 Subject: [PATCH 3/4] thing-model: package-lock.json updated --- package-lock.json | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c47bb20eb..4abcf617a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6194,6 +6194,15 @@ "dev": true, "license": "MIT" }, + "node_modules/json-placeholder-replacer": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-1.0.35.tgz", + "integrity": "sha512-edlSWqcFVUpKPshaIcJfXpQ8eu0//gk8iU6XHWkCZIp5QEp4hoCFR7uk+LrIzhLTSqmBQ9VBs+EYK8pvWGEpRg==", + "bin": { + "jpr": "dist/index.js", + "json-placeholder-replacer": "dist/index.js" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "license": "MIT" @@ -11957,9 +11966,9 @@ }, "node_modules/wot-thing-model-types": { "version": "1.1.0-3-February-2022", - "license": "W3C-20150513", "resolved": "https://registry.npmjs.org/wot-thing-model-types/-/wot-thing-model-types-1.1.0-3-February-2022.tgz", - "integrity": "sha512-IYetSr4Z7DmXESMg2OKDyIPTRwGSciIMuJBPyABNptNHodbwcMHx/48GO0kx6TpsDy9gBYkGO1SFXvt1/NZoMw==" + "integrity": "sha512-IYetSr4Z7DmXESMg2OKDyIPTRwGSciIMuJBPyABNptNHodbwcMHx/48GO0kx6TpsDy9gBYkGO1SFXvt1/NZoMw==", + "license": "W3C-20150513" }, "node_modules/wot-typescript-definitions": { "version": "0.8.0-SNAPSHOT.22", @@ -12670,6 +12679,7 @@ "license": "EPL-2.0 OR W3C-20150513", "dependencies": { "is-absolute-url": "3.0.3", + "json-placeholder-replacer": "^1.0.35", "url-toolkit": "2.1.6", "wot-thing-description-types": "^1.1.0-09-February-2022", "wot-thing-model-types": "^1.1.0-3-February-2022", @@ -13326,9 +13336,10 @@ "eslint-plugin-promise": "^5.1.0", "eslint-plugin-unused-imports": "^1.1.4", "is-absolute-url": "3.0.3", + "json-placeholder-replacer": "^1.0.35", "mocha": "^9.1.1", "prettier": "^2.3.2", - "stream-http": "*", + "stream-http": "^3.2.0", "ts-loader": "8", "ts-node": "10.1.0", "typescript": "4.4.3", @@ -17306,6 +17317,11 @@ "version": "1.0.2", "dev": true }, + "json-placeholder-replacer": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/json-placeholder-replacer/-/json-placeholder-replacer-1.0.35.tgz", + "integrity": "sha512-edlSWqcFVUpKPshaIcJfXpQ8eu0//gk8iU6XHWkCZIp5QEp4hoCFR7uk+LrIzhLTSqmBQ9VBs+EYK8pvWGEpRg==" + }, "json-schema-traverse": { "version": "1.0.0" }, From 238f4f09d82074e4ff2634186d7bd7da77f0267e Mon Sep 17 00:00:00 2001 From: reluc Date: Wed, 30 Mar 2022 14:38:24 +0200 Subject: [PATCH 4/4] test(td-tools): add a test for composed types in strings Signed-off-by: reluc --- .../td-tools/test/ThingModelHelperTest.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/td-tools/test/ThingModelHelperTest.ts b/packages/td-tools/test/ThingModelHelperTest.ts index ee3a95b45..2849a097f 100644 --- a/packages/td-tools/test/ThingModelHelperTest.ts +++ b/packages/td-tools/test/ThingModelHelperTest.ts @@ -384,6 +384,40 @@ class ThingModelHelperTest { expect(partialTd).to.be.deep.equal(finalJSON); } + @test async "should correctly fill placeholders with composed types in strings"() { + const thing = { + "@context": ["http://www.w3.org/ns/td"], + "@type": "tm:ThingModel", + data: "data: {{ARRAY}}", + title: "Thermostate No. 4", + versionInfo: "version: {{VERSION_INFO}}", + } as unknown as ThingModel; + const map = { + ARRAY: [1, 2, 3], + VERSION_INFO: { instance: "xyz", model: "ABC" }, + }; + const finalJSON = { + "@context": ["http://www.w3.org/ns/td"], + "@type": "Thing", + title: "Thermostate No. 4", + data: "data: [1,2,3]", + versionInfo: 'version: {"instance":"xyz","model":"ABC"}', + links: [ + { + href: "./ThermostateNo.4.tm.jsonld", + rel: "type", + type: "application/tm+json", + }, + ], + }; + const options: CompositionOptions = { + map, + selfComposition: false, + }; + const [partialTd] = await this.thingModelHelpers.getPartialTDs(thing, options); + expect(partialTd).to.be.deep.equal(finalJSON); + } + @test async "should reject fill placeholders because of missing fields in map"() { const thing = { "@context": ["http://www.w3.org/ns/td"],