diff --git a/package-lock.json b/package-lock.json index 7a086857f..07a87b0a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6202,6 +6202,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", @@ -12673,6 +12682,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", @@ -13332,9 +13342,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", @@ -17321,6 +17332,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" }, 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..4a42df268 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"; @@ -552,28 +553,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..2849a097f 100644 --- a/packages/td-tools/test/ThingModelHelperTest.ts +++ b/packages/td-tools/test/ThingModelHelperTest.ts @@ -350,6 +350,74 @@ 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 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"],