diff --git a/node/td-utils/index.js b/node/td-utils/index.js deleted file mode 100644 index c8ad2e5..0000000 --- a/node/td-utils/index.js +++ /dev/null @@ -1,2 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/node/td-utils/package-lock.json b/node/td-utils/package-lock.json index 26233e3..60f9575 100644 --- a/node/td-utils/package-lock.json +++ b/node/td-utils/package-lock.json @@ -8,15 +8,11 @@ "name": "@thingweb/td-utils", "version": "1.0.0", "license": "EPL-2.0 OR W3C-20150513", - "dependencies": { - "lodash": "^4.17.21" - }, "devDependencies": { "@babel/core": "^7.24.7", "@babel/preset-env": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@types/jest": "^29.5.12", - "@types/lodash": "^4.17.6", "babel-jest": "^29.7.0", "browserify": "^17.0.0", "jest": "^28.1.3", @@ -2528,12 +2524,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@types/lodash": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.6.tgz", - "integrity": "sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==", - "dev": true - }, "node_modules/@types/node": { "version": "20.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", @@ -5359,11 +5349,6 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", diff --git a/node/td-utils/package.json b/node/td-utils/package.json index 10e9068..2e53cf2 100644 --- a/node/td-utils/package.json +++ b/node/td-utils/package.json @@ -21,7 +21,6 @@ "@babel/preset-env": "^7.24.7", "@babel/preset-typescript": "^7.24.7", "@types/jest": "^29.5.12", - "@types/lodash": "^4.17.6", "babel-jest": "^29.7.0", "browserify": "^17.0.0", "jest": "^28.1.3", @@ -33,8 +32,5 @@ "bugs": { "url": "https://github.com/eclipse-thingweb/td-tools/issues" }, - "homepage": "https://github.com/eclipse-thingweb/td-tools#readme", - "dependencies": { - "lodash": "^4.17.21" - } + "homepage": "https://github.com/eclipse-thingweb/td-tools#readme" } diff --git a/node/td-utils/src/detectProtocolSchemes.ts b/node/td-utils/src/detectProtocolSchemes.ts index 952f925..36d1453 100644 --- a/node/td-utils/src/detectProtocolSchemes.ts +++ b/node/td-utils/src/detectProtocolSchemes.ts @@ -1,4 +1,3 @@ -import { uniqWith, isEqual } from "lodash"; import { AnyUri, Form, @@ -8,10 +7,18 @@ import { EventElement, } from "wot-thing-description-types"; +export interface ProtocolSchemeMap { + [k: string]: ProtocolScheme[]; +} + export interface ProtocolScheme { + uri: string; + subprotocol?: string; +} + +export interface HrefInfo { protocol: string; - hostname: string; - port?: number; + uri: string; } type AffordanceElement = { [k: string]: PropertyElement | ActionElement | EventElement }; @@ -21,75 +28,63 @@ type AffordanceElement = { [k: string]: PropertyElement | ActionElement | EventE * @param {string} td TD string to detect protocols of * return List of available protocol schemes */ -export const detectProtocolSchemes = (td: string): ProtocolScheme[] => { +export const detectProtocolSchemes = (td: string): ProtocolSchemeMap => { let tdJson: ThingDescription; + const protocolSchemes: ProtocolSchemeMap = {}; + try { tdJson = JSON.parse(td); } catch (err) { console.error("Could not parse the TD string."); - return []; + return protocolSchemes; } - const baseUriProtocol: ProtocolScheme | undefined = tdJson.base ? getHrefData(tdJson.base) : undefined; - const thingProtocols: ProtocolScheme[] = tdJson.forms ? detectProtocolInForms(tdJson.forms) : []; - const actionsProtocols: ProtocolScheme[] = tdJson.actions ? detectProtocolInAffordance(tdJson.actions) : []; - const eventsProtocols: ProtocolScheme[] = tdJson.events ? detectProtocolInAffordance(tdJson.events) : []; - const propertiesProtocols: ProtocolScheme[] = tdJson.properties - ? detectProtocolInAffordance(tdJson.properties) - : []; - const protocolSchemes: ProtocolScheme[] = [ - ...thingProtocols, - ...actionsProtocols, - ...eventsProtocols, - ...propertiesProtocols, - ]; - - if (baseUriProtocol) protocolSchemes.push(baseUriProtocol); - - return uniqWith(protocolSchemes, isEqual); + const baseHrefInfo = getHrefInfo(tdJson.base); + + addProtocolScheme(protocolSchemes, baseHrefInfo); + detectProtocolInForms(protocolSchemes, tdJson.forms); + detectProtocolInAffordance(protocolSchemes, tdJson.actions); + detectProtocolInAffordance(protocolSchemes, tdJson.events); + detectProtocolInAffordance(protocolSchemes, tdJson.properties); + + return protocolSchemes; }; /** * Detect protocols in a TD affordance + * @param protocolSchemes Protocol scheme map that the protocol scheme will be added * @param {object} affordance That belongs to a TD * @returns List of protocol schemes */ -const detectProtocolInAffordance = (affordance: AffordanceElement): ProtocolScheme[] => { +const detectProtocolInAffordance = (protocolSchemes: ProtocolSchemeMap, affordance?: AffordanceElement) => { if (!affordance) { - return []; + return; } - let protocolSchemes: ProtocolScheme[] = []; - for (const key in affordance) { if (key) { - protocolSchemes = protocolSchemes.concat(detectProtocolInForms(affordance[key].forms)); + detectProtocolInForms(protocolSchemes, affordance[key].forms); } } - - return protocolSchemes; }; /** * Detect protocols in a TD forms or a TD affordance forms + * @param protocolSchemes Protocol scheme map that the protocol scheme will be added * @param {object} forms Forms field of a TD or a TD affordance * @returns List of protocol schemes */ -const detectProtocolInForms = (forms: Form[]): ProtocolScheme[] => { +const detectProtocolInForms = (protocolSchemes: ProtocolSchemeMap, forms?: Form[]) => { if (!forms) { - return []; + return; } - const protocolSchemes: ProtocolScheme[] = []; - forms.forEach((form) => { - const hrefData = getHrefData(form.href); + const hrefInfo = getHrefInfo(form.href); - if (hrefData) protocolSchemes.push(hrefData); + addProtocolScheme(protocolSchemes, hrefInfo, form.subprotocol); }); - - return protocolSchemes; }; /** @@ -97,21 +92,43 @@ const detectProtocolInForms = (forms: Form[]): ProtocolScheme[] => { * @param {string} href URI string * @returns an object with protocol, hostname and port */ -const getHrefData = (href: AnyUri): ProtocolScheme | undefined => { +const getHrefInfo = (href?: AnyUri): HrefInfo | undefined => { if (!href) { return; } const splitHref = href.split(":"); - const hostAndPort = href.split("/")[2]; - const splitHostAndPort = hostAndPort.split(":"); + const uri = href.split("/")[2]; const protocol = splitHref[0]; - const hostname = splitHostAndPort[0]; - const port = splitHostAndPort[1] ? Number(splitHostAndPort[1]) : undefined; return { protocol, - hostname, - port, + uri: protocol + "://" + uri, }; }; + +/** + * + * @param protocolSchemes Protocol scheme map that the protocol scheme will be added + * @param hrefInfo Protocol scheme's information that is extracted from the href + * @param subprotocol Subprotocol of the protocol scheme + */ +const addProtocolScheme = (protocolSchemes: ProtocolSchemeMap, hrefInfo?: HrefInfo, subprotocol?: string) => { + if (hrefInfo) { + const protocolScheme = { + uri: hrefInfo.uri, + subprotocol: subprotocol, + }; + + if (protocolSchemes[hrefInfo.protocol]) { + const schemeExists = + protocolSchemes[hrefInfo.protocol].filter( + (s) => s.uri === protocolScheme.uri && s.subprotocol === protocolScheme.subprotocol + ).length > 0; + + if (!schemeExists) protocolSchemes[hrefInfo.protocol].push(protocolScheme); + } else { + protocolSchemes[hrefInfo.protocol] = [protocolScheme]; + } + } +}; diff --git a/node/td-utils/test/protocol-detection.test.suite.ts b/node/td-utils/test/protocol-detection.test.suite.ts index 59231d5..237dd07 100644 --- a/node/td-utils/test/protocol-detection.test.suite.ts +++ b/node/td-utils/test/protocol-detection.test.suite.ts @@ -8,62 +8,39 @@ export const testSuite = [ { name: "httpAndMqtt", input: httpAndMqtt, - expected: [ - { - protocol: "http", - hostname: "mylamp.example.com", - port: 1234, - }, - { - protocol: "mqtt", - hostname: "mylamp.example.com", - port: 1234, - }, - ], + expected: { + http: [{ uri: "http://mylamp.example.com:1234" }], + mqtt: [{ uri: "mqtt://mylamp.example.com:1234" }], + }, }, { name: "noProtocol", input: noProtocol, - expected: [], + expected: {}, }, { name: "onlyHttp", input: onlyHttp, - expected: [ - { - protocol: "http", - hostname: "mylamp.example.com", - port: 4212, - }, - { - protocol: "http", - hostname: "mylamp.example.com", - port: 4214, - }, - ], + expected: { + http: [ + { uri: "http://mylamp.example.com:4212" }, + { uri: "http://mylamp.example.com:4214", subprotocol: "longpoll" }, + ], + }, }, { name: "onlyMqtt", input: onlyMqtt, - expected: [ - { - protocol: "mqtt", - hostname: "mylamp.example.com", - }, - ], + expected: { + mqtt: [{ uri: "mqtt://mylamp.example.com" }, { uri: "mqtt://mylamp.example.com", subprotocol: "longpoll" }], + }, }, { name: "secureProtocols", input: secureProtocols, - expected: [ - { - protocol: "https", - hostname: "mylamp.example.com", - }, - { - protocol: "mqtts", - hostname: "mylamp.example.com", - }, - ], + expected: { + https: [{ uri: "https://mylamp.example.com", subprotocol: "longpoll" }], + mqtts: [{ uri: "mqtts://mylamp.example.com" }], + }, }, ]; diff --git a/node/td-utils/test/protocol-detection.test.ts b/node/td-utils/test/protocol-detection.test.ts index 22b4a1e..5ac2ced 100644 --- a/node/td-utils/test/protocol-detection.test.ts +++ b/node/td-utils/test/protocol-detection.test.ts @@ -12,11 +12,11 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 */ -import { detectProtocolSchemes, ProtocolScheme } from "../src/detectProtocolSchemes"; +import { detectProtocolSchemes, ProtocolSchemeMap } from "../src/detectProtocolSchemes"; import { ThingDescription } from "wot-thing-description-types"; import { testSuite } from "./protocol-detection.test.suite"; -export type ThingDescriptionTest = ThingDescription & { protocolSchemes: ProtocolScheme[] }; +export type ThingDescriptionTest = ThingDescription & { protocolSchemes: ProtocolSchemeMap[] }; describe("test examples", () => { testSuite.forEach((t) => { @@ -26,11 +26,8 @@ describe("test examples", () => { }); }); -const testTD = (td: any, expected: ProtocolScheme[]) => { +const testTD = (td: any, expected: ProtocolSchemeMap[]) => { const detectedProtocolSchemes = detectProtocolSchemes(JSON.stringify(td)); - expect(detectedProtocolSchemes.length).toBe(expected.length); - expected.forEach((s: ProtocolScheme) => { - expect(detectedProtocolSchemes).toContainEqual(s); - }); + expect(detectedProtocolSchemes).toMatchObject(expected); };