diff --git a/node-red-node-wot/package-lock.json b/node-red-node-wot/package-lock.json
index 61d5608..37cb19a 100644
--- a/node-red-node-wot/package-lock.json
+++ b/node-red-node-wot/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@thingweb/node-red-node-wot",
- "version": "1.0.0",
+ "version": "1.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@thingweb/node-red-node-wot",
- "version": "1.0.0",
+ "version": "1.0.1",
"license": "MIT",
"dependencies": {
"@node-wot/binding-coap": "0.8.12",
@@ -18,7 +18,7 @@
"@node-wot/core": "0.8.12"
},
"devDependencies": {
- "@types/chai": "^4.3.11",
+ "@types/chai": "^4.3.12",
"@types/chai-as-promised": "^7.1.8",
"@types/mocha": "^10.0.6",
"@types/node-red": "^1.3.4",
diff --git a/node-red-node-wot/package.json b/node-red-node-wot/package.json
index 68cad38..d94d03e 100644
--- a/node-red-node-wot/package.json
+++ b/node-red-node-wot/package.json
@@ -25,6 +25,7 @@
"wot-property": "dist/wot-property.js",
"wot-action": "dist/wot-action.js",
"wot-event": "dist/wot-event.js",
+ "wot-update-td": "dist/wot-update-td.js",
"wot-server-config": "dist/wot-server-config.js",
"wot-thing-config": "dist/wot-thing-config.js",
"wot-server-end": "dist/wot-server-end.js",
@@ -33,7 +34,7 @@
"wot-server-event": "dist/wot-server-event.js",
"wot-server-td": "dist/wot-server-td.js"
},
- "version":">=2.0.0"
+ "version": ">=2.0.0"
},
"scripts": {
"build": "npm run copy:src2dist && tsc",
@@ -52,7 +53,7 @@
"@node-wot/core": "0.8.12"
},
"devDependencies": {
- "@types/chai": "^4.3.11",
+ "@types/chai": "^4.3.12",
"@types/chai-as-promised": "^7.1.8",
"@types/mocha": "^10.0.6",
"@types/node-red": "^1.3.4",
diff --git a/node-red-node-wot/src/wot-action.js b/node-red-node-wot/src/wot-action.js
index 0714161..69ed103 100644
--- a/node-red-node-wot/src/wot-action.js
+++ b/node-red-node-wot/src/wot-action.js
@@ -4,6 +4,9 @@ module.exports = function (RED) {
function invokeActionNode(config) {
RED.nodes.createNode(this, config)
let node = this
+ let consumedThing
+
+ this.status({})
if (!config.thing) {
this.status({ fill: "red", shape: "dot", text: "Error: Thing undefined" })
@@ -17,15 +20,26 @@ module.exports = function (RED) {
return
}
+ const thingNode = RED.nodes.getNode(config.thing)
+ thingNode.addUpdateTDListener(async (_consumedThing) => {
+ consumedThing = _consumedThing
+ })
+
this.on("input", function (msg, send, done) {
- RED.nodes.getNode(config.thing).consumedThing.then((consumedThing) => {
- const uriVariables = config.uriVariables ? JSON.parse(config.uriVariables) : undefined
- consumedThing
- .invokeAction(config.action, msg.payload, {
- uriVariables: uriVariables,
- })
- .then(async (resp) => {
- const payload = resp ? await resp.value() : ""
+ if (!consumedThing) {
+ node.error("[error] consumedThing is not defined.")
+ done("consumedThing is not defined.")
+ return
+ }
+ const uriVariables = config.uriVariables ? JSON.parse(config.uriVariables) : undefined
+ consumedThing
+ .invokeAction(config.action, msg.payload, {
+ uriVariables: uriVariables,
+ })
+ .then(async (resp) => {
+ let payload
+ try {
+ payload = await resp.value()
node.send({ payload: payload, topic: config.topic })
node.status({
fill: "green",
@@ -33,17 +47,20 @@ module.exports = function (RED) {
text: "invoked",
})
done()
+ } catch (err) {
+ console.error(`[error] failed to get return value. err: `, err)
+ done(`[error] failed to get return value. err: ${err.toString()}`)
+ }
+ })
+ .catch((err) => {
+ node.warn(err)
+ node.status({
+ fill: "red",
+ shape: "ring",
+ text: err.message,
})
- .catch((err) => {
- node.warn(err)
- node.status({
- fill: "red",
- shape: "ring",
- text: err.message,
- })
- done(err)
- })
- })
+ done(err)
+ })
})
this.on("close", function (removed, done) {
diff --git a/node-red-node-wot/src/wot-event.js b/node-red-node-wot/src/wot-event.js
index 89a0f53..2900eca 100644
--- a/node-red-node-wot/src/wot-event.js
+++ b/node-red-node-wot/src/wot-event.js
@@ -4,8 +4,9 @@ module.exports = function (RED) {
function subscribeEventNode(config) {
RED.nodes.createNode(this, config)
let node = this
+ let consumedThing
+ let subscription
- this.subscription = undefined
this.status({})
if (!config.thing) {
@@ -16,95 +17,84 @@ module.exports = function (RED) {
return
}
- RED.nodes
- .getNode(config.thing)
- .consumedThing.then(async (consumedThing) => {
- let subscription
- // Repeat until event subscription succeeds.
- try {
- while (true) {
- subscription = await consumedThing
- .subscribeEvent(
- config.event,
- async (resp) => {
- if (resp) {
- let payload
- try {
- payload = await resp.value()
- } catch (err) {
- node.error(`[error] failed to get event. err: ${err.toString()}`)
- console.error(`[error] failed to get event. err: `, err)
- }
- node.send({ payload, topic: config.topic })
+ const thingNode = RED.nodes.getNode(config.thing)
+ thingNode.addUpdateTDListener(async (_consumedThing) => {
+ if (subscription) {
+ // Stop if already subscribed
+ await subscription.stop()
+ }
+ consumedThing = _consumedThing
+ // Repeat until event subscription succeeds.
+ try {
+ while (true) {
+ subscription = await consumedThing
+ .subscribeEvent(
+ config.event,
+ async (resp) => {
+ if (resp) {
+ let payload
+ try {
+ payload = await resp.value()
+ } catch (err) {
+ node.error(`[error] failed to get event. err: ${err.toString()}`)
+ console.error(`[error] failed to get event. err: `, err)
}
- node.status({
- fill: "green",
- shape: "dot",
- text: "Subscribed",
- })
- },
- (err) => {
- console.error("[error] subscribe events.", err)
- node.error(`[error] subscribe events. err: ${err.toString()}`)
- node.status({
- fill: "red",
- shape: "ring",
- text: "Subscription error",
- })
- },
- () => {
- console.error("[warn] Subscription ended.")
- node.warn("[warn] Subscription ended.")
- node.status({})
- node.subscription = undefined
+ node.send({ payload, topic: config.topic })
}
- )
- .catch((err) => {
- console.warn("[warn] event subscribe error. try again. error: " + err)
- })
- if (subscription) {
- break
- }
- await (() => {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve()
- }, 500)
- })
- })()
+ node.status({
+ fill: "green",
+ shape: "dot",
+ text: "Subscribed",
+ })
+ },
+ (err) => {
+ console.error("[error] subscribe events.", err)
+ node.error(`[error] subscribe events. err: ${err.toString()}`)
+ node.status({
+ fill: "red",
+ shape: "ring",
+ text: "Subscription error",
+ })
+ },
+ () => {
+ console.error("[warn] Subscription ended.")
+ node.warn("[warn] Subscription ended.")
+ node.status({})
+ subscription = undefined
+ }
+ )
+ .catch((err) => {
+ console.warn("[warn] event subscribe error. try again. error: " + err)
+ })
+ if (subscription) {
+ break
}
- } catch (err) {
- node.status({
- fill: "red",
- shape: "ring",
- text: "Subscription error",
+ await new Promise((resolve) => {
+ setTimeout(resolve, 500)
})
- node.error(`[error] failed to subscribe events. error: ${err.toString()}`)
}
- node.subscription = subscription
-
- if (node.subscription) {
- node.status({
- fill: "green",
- shape: "dot",
- text: "Subscribed",
- })
- }
- })
- .catch((err) => {
+ } catch (err) {
node.status({
fill: "red",
shape: "ring",
text: "Subscription error",
})
- node.error(`[error] Failed to create consumed thing for enents. err: ${err.toString()}`)
- })
+ node.error(`[error] failed to subscribe events. error: ${err.toString()}`)
+ }
+
+ if (subscription) {
+ node.status({
+ fill: "green",
+ shape: "dot",
+ text: "Subscribed",
+ })
+ }
+ })
- this.on("close", function (removed, done) {
- if (removed) {
- // This node has been deleted
- } else {
- // This node is being restarted
+ this.on("close", async function (removed, done) {
+ if (subscription) {
+ // Stop if already subscribed
+ await subscription.stop()
}
done()
})
diff --git a/node-red-node-wot/src/wot-property.js b/node-red-node-wot/src/wot-property.js
index e35097d..7acda80 100644
--- a/node-red-node-wot/src/wot-property.js
+++ b/node-red-node-wot/src/wot-property.js
@@ -5,6 +5,7 @@ module.exports = function (RED) {
RED.nodes.createNode(this, config)
let node = this
let consumedThing
+ let subscription
this.status({})
@@ -20,16 +21,20 @@ module.exports = function (RED) {
return
}
- RED.nodes.getNode(config.thing).consumedThing.then(async (thing) => {
- consumedThing = thing
+ const thingNode = RED.nodes.getNode(config.thing)
+ thingNode.addUpdateTDListener(async (_consumedThing) => {
+ if (subscription) {
+ // Stop if already subscribed
+ await subscription.stop()
+ }
+ consumedThing = _consumedThing
if (config.observe === false) {
return
}
// Repeat until observeProperty succeeds.
- let ob
while (true) {
try {
- ob = await consumedThing.observeProperty(
+ subscription = await consumedThing.observeProperty(
config.property,
async (resp) => {
let payload
@@ -59,7 +64,7 @@ module.exports = function (RED) {
text: "Observe error",
})
}
- if (ob) {
+ if (subscription) {
node.status({
fill: "green",
shape: "dot",
@@ -113,11 +118,10 @@ module.exports = function (RED) {
})
})
- node.on("close", function (removed, done) {
- if (removed) {
- // This node has been deleted
- } else {
- // This node is being restarted
+ node.on("close", async function (removed, done) {
+ if (subscription) {
+ // Stop if already subscribed
+ await subscription.stop()
}
done()
})
@@ -127,6 +131,7 @@ module.exports = function (RED) {
function writePropertyNode(config) {
RED.nodes.createNode(this, config)
let node = this
+ let consumedThing
this.status({})
@@ -142,32 +147,49 @@ module.exports = function (RED) {
return
}
- RED.nodes.getNode(config.thing).consumedThing.then((consumedThing) => {
- node.on("input", function (msg, send, done) {
- const uriVariables = config.uriVariables ? JSON.parse(config.uriVariables) : undefined
- consumedThing
- .writeProperty(config.property, msg.payload, {
- uriVariables: uriVariables,
- })
- .then((resp) => {
- if (resp) node.send({ payload: resp, topic: config.topic })
- node.status({
- fill: "green",
- shape: "dot",
- text: "connected",
- })
- done()
+ const thingNode = RED.nodes.getNode(config.thing)
+ thingNode.addUpdateTDListener(async (_consumedThing) => {
+ consumedThing = _consumedThing
+ })
+
+ node.on("input", function (msg, send, done) {
+ if (!consumedThing) {
+ node.error("[error] consumedThing is not defined.")
+ done("consumedThing is not defined.")
+ return
+ }
+ const uriVariables = config.uriVariables ? JSON.parse(config.uriVariables) : undefined
+ consumedThing
+ .writeProperty(config.property, msg.payload, {
+ uriVariables: uriVariables,
+ })
+ .then((resp) => {
+ if (resp) node.send({ payload: resp, topic: config.topic })
+ node.status({
+ fill: "green",
+ shape: "dot",
+ text: "connected",
})
- .catch((err) => {
- node.warn(err)
- node.status({
- fill: "red",
- shape: "ring",
- text: err.message,
- })
- done(err)
+ done()
+ })
+ .catch((err) => {
+ node.warn(err)
+ node.status({
+ fill: "red",
+ shape: "ring",
+ text: err.message,
})
- })
+ done(err)
+ })
+ })
+
+ this.on("close", function (removed, done) {
+ if (removed) {
+ // This node has been deleted
+ } else {
+ // This node is being restarted
+ }
+ done()
})
}
RED.nodes.registerType("write-property", writePropertyNode)
diff --git a/node-red-node-wot/src/wot-server-td.ts b/node-red-node-wot/src/wot-server-td.ts
index 3579fda..47002ff 100644
--- a/node-red-node-wot/src/wot-server-td.ts
+++ b/node-red-node-wot/src/wot-server-td.ts
@@ -5,8 +5,6 @@ module.exports = function (RED) {
RED.nodes.createNode(this, config)
const node = this
this.status({ fill: "red", shape: "dot", text: "not prepared" })
- console.log("*** wot server td node: ", node)
- console.log("*** wot server td config: ", config)
node.setServientStatus = (running: boolean) => {
if (running) {
diff --git a/node-red-node-wot/src/wot-thing.js b/node-red-node-wot/src/wot-thing.js
index 3e759cb..fe653ab 100644
--- a/node-red-node-wot/src/wot-thing.js
+++ b/node-red-node-wot/src/wot-thing.js
@@ -13,16 +13,22 @@ module.exports = function (RED) {
function consumedThingNode(config) {
RED.nodes.createNode(this, config)
const node = this
+ let consumedThing
+ let tdListeners = []
- this.tdLink = config.tdLink
- this.td = JSON.parse(config.td)
+ this.addUpdateTDListener = (listener) => {
+ tdListeners.push(listener)
+ if (consumedThing) {
+ listener(consumedThing)
+ }
+ }
- this.consumedThing = new Promise((resolve, reject) => {
+ this.createConsumedThing = async (td) => {
let servient = new Servient()
if (config.basicAuth) {
servient.addCredentials({
- [this.td.id]: { username: config.username.trim(), password: config.password },
+ [td.id]: { username: config.username.trim(), password: config.password },
})
}
@@ -47,16 +53,21 @@ module.exports = function (RED) {
servient.addClientFactory(new ModbusClientFactory())
}
- servient
- .start()
- .then((thingFactory) => {
- let consumedThing = thingFactory.consume(this.td)
- resolve(consumedThing)
- })
- .catch((err) => {
- node.error(`[error] failed to start servient. err: ${err.toString()}`)
- reject(err)
- })
+ const thingFactory = await servient.start()
+ consumedThing = await thingFactory.consume(td)
+ tdListeners.forEach((listener) => {
+ listener(consumedThing)
+ })
+ }
+
+ const td = JSON.parse(config.td)
+ this.createConsumedThing(td).catch((err) => {
+ node.error(`[error] failed to start servient. err: ${err.toString()}`)
+ })
+
+ this.on("close", function (removed, done) {
+ tdListeners = []
+ done()
})
}
RED.nodes.registerType("consumed-thing", consumedThingNode)
diff --git a/node-red-node-wot/src/wot-update-td.html b/node-red-node-wot/src/wot-update-td.html
new file mode 100644
index 0000000..ae04e7d
--- /dev/null
+++ b/node-red-node-wot/src/wot-update-td.html
@@ -0,0 +1,58 @@
+
+
+
+
+
diff --git a/node-red-node-wot/src/wot-update-td.js b/node-red-node-wot/src/wot-update-td.js
new file mode 100644
index 0000000..e978173
--- /dev/null
+++ b/node-red-node-wot/src/wot-update-td.js
@@ -0,0 +1,51 @@
+"use strict"
+
+module.exports = function (RED) {
+ function UpdateTDNode(config) {
+ RED.nodes.createNode(this, config)
+ let node = this
+
+ this.status({})
+
+ if (!config.thing) {
+ this.status({ fill: "red", shape: "dot", text: "Error: Thing undefined" })
+ return
+ } else if (!config.tdSource) {
+ this.status({
+ fill: "red",
+ shape: "dot",
+ text: "Error: Choose a td source",
+ })
+ return
+ }
+
+ const thingNode = RED.nodes.getNode(config.thing)
+
+ this.on("input", async function (msg, send, done) {
+ let td
+ if (config.tdSource && config.tdSourceType) {
+ try {
+ td = await RED.util.evaluateNodeProperty(config.tdSource, config.tdSourceType, node, msg)
+ } catch (err) {
+ return done("cannot evaluate td source")
+ }
+ }
+ try {
+ await thingNode.createConsumedThing(td)
+ done()
+ } catch (err) {
+ done(err)
+ }
+ })
+
+ this.on("close", function (removed, done) {
+ if (removed) {
+ // This node has been deleted
+ } else {
+ // This node is being restarted
+ }
+ done()
+ })
+ }
+ RED.nodes.registerType("update-td", UpdateTDNode)
+}
diff --git a/node-red-node-wot/test/server-td-test.ts b/node-red-node-wot/test/server-td-test.ts
new file mode 100644
index 0000000..5062d56
--- /dev/null
+++ b/node-red-node-wot/test/server-td-test.ts
@@ -0,0 +1,125 @@
+/**
+ * test for server-td
+ */
+import "mocha"
+import * as chai from "chai"
+import chaiAsPromised from "chai-as-promised"
+import helper from "node-red-node-test-helper"
+import { startFlow, endFlow, getNodeAfterStartFlow } from "./util"
+
+helper.init(require.resolve("node-red"))
+
+chai.use(chaiAsPromised)
+const assert = chai.assert
+
+/*
+ Flow Summary
+ [Server-side]
+ 1a. wot-server-event:id.serverevent01 (id.serverconfig01, id.thingconfig01')
+ 1b. wot-server-td:id.servertd01 (id.serverconfig01, id.thingconfig01')
+ 2b. helper:id.gettdhelper01
+ */
+const targetFlow = [
+ // Server-side
+ {
+ id: "id.serverevent01",
+ type: "wot-server-event",
+ name: "",
+ eventName: "testEvent",
+ eventDescription: "test event",
+ eventDataType: "string",
+ inParams_eventValueType: "msg",
+ inParams_eventValueConstValue: "payload",
+ woTServerConfig: "id.serverconfig01",
+ woTThingConfig: "id.thingconfig01",
+ wires: [],
+ },
+ {
+ id: "id.servertd01",
+ type: "wot-server-td",
+ name: "",
+ outParams1_tdType: "msg",
+ outParams1_tdConstValue: "payload",
+ woTServerConfig: "id.serverconfig01",
+ woTThingConfig: "id.thingconfig01",
+ outputTDAfterServerStartFlag: true,
+ wires: [["id.gettdhelper01"]],
+ },
+ {
+ id: "id.serverconfig01",
+ type: "wot-server-config",
+ name: "httpserver",
+ bindingType: "http",
+ bindingConfigType: "json",
+ bindingConfigConstValue: '{"port":8383}',
+ },
+ {
+ id: "id.thingconfig01",
+ type: "wot-thing-config",
+ name: "thing01",
+ description: "thing01 for test",
+ },
+ { id: "id.gettdhelper01", type: "helper" },
+]
+
+let eventContent = "test-content"
+
+describe("Tests for Server TD", function () {
+ this.timeout(15 * 1000)
+ before(async function () {
+ await startFlow(targetFlow, helper, 0)
+ })
+
+ after(async function () {
+ await endFlow("id.serverconfig01", helper)
+ })
+
+ beforeEach(function (done) {
+ done()
+ })
+
+ afterEach(function (done) {
+ done()
+ })
+
+ it("get td after server start", function (done) {
+ getNodeAfterStartFlow("id.gettdhelper01", helper).then((helperNode) => {
+ helperNode.removeAllListeners("input")
+ helperNode.on("input", function (msg) {
+ try {
+ //@ts-ignore
+ assert.equal(msg.payload?.title, "thing01")
+ done()
+ } catch (err) {
+ done(err)
+ }
+ })
+ })
+ })
+
+ it("get td by input", function (done) {
+ // wait for servient start
+ new Promise((resolve, reject) => {
+ setTimeout(resolve, 2000)
+ }).then(() => {
+ const helperNode = helper.getNode("id.gettdhelper01")
+ let sentFlg = false
+ helperNode.removeAllListeners("input")
+ helperNode.on("input", function (msg) {
+ try {
+ // check after send trigger or not
+ if (sentFlg) {
+ //@ts-ignore
+ assert.equal(msg.payload?.title, "thing01")
+ done()
+ }
+ } catch (err) {
+ done(err)
+ }
+ })
+ const serverTDNode = helper.getNode("id.servertd01")
+ serverTDNode.receive({})
+ sentFlg = true
+ })
+ })
+})
diff --git a/node-red-node-wot/test/update-td-test.ts b/node-red-node-wot/test/update-td-test.ts
new file mode 100644
index 0000000..001d998
--- /dev/null
+++ b/node-red-node-wot/test/update-td-test.ts
@@ -0,0 +1,226 @@
+/**
+ * test for update-td
+ */
+import "mocha"
+import * as chai from "chai"
+import chaiAsPromised from "chai-as-promised"
+import helper from "node-red-node-test-helper"
+import { startFlow, endFlow } from "./util"
+
+helper.init(require.resolve("node-red"))
+
+chai.use(chaiAsPromised)
+const assert = chai.assert
+
+/*
+ Flow Summary
+ [Server-side]
+ 1a. wot-server-event:id.serverevent01 (id.serverconfig01, id.thingconfig01')
+ 1b. wot-server-event:id.serverevent02 (id.serverconfig02, id.thingconfig01')
+ [Client-side]
+ 1a. subscribe-event:id.subscribeevent01 (id.consumedthing01)
+ 2a. helper:id.subscribeeventhelper01
+ 1b. update-td:id.updatetd01 (id.consumedthing01)
+ */
+const targetFlow = [
+ // Server-side
+ {
+ id: "id.serverevent01",
+ type: "wot-server-event",
+ name: "",
+ eventName: "testEvent",
+ eventDescription: "test event",
+ eventDataType: "string",
+ inParams_eventValueType: "msg",
+ inParams_eventValueConstValue: "payload",
+ woTServerConfig: "id.serverconfig01",
+ woTThingConfig: "id.thingconfig01",
+ wires: [],
+ },
+ {
+ id: "id.serverconfig01",
+ type: "wot-server-config",
+ name: "httpserver",
+ bindingType: "http",
+ bindingConfigType: "json",
+ bindingConfigConstValue: '{"port":8181}',
+ },
+ {
+ id: "id.thingconfig01",
+ type: "wot-thing-config",
+ name: "thing01",
+ description: "thing01 for test",
+ },
+ {
+ id: "id.serverevent02",
+ type: "wot-server-event",
+ name: "",
+ eventName: "testEvent",
+ eventDescription: "test event",
+ eventDataType: "string",
+ inParams_eventValueType: "msg",
+ inParams_eventValueConstValue: "payload",
+ woTServerConfig: "id.serverconfig02",
+ woTThingConfig: "id.thingconfig01",
+ wires: [],
+ },
+ {
+ id: "id.serverconfig02",
+ type: "wot-server-config",
+ name: "httpserver",
+ bindingType: "http",
+ bindingConfigType: "json",
+ bindingConfigConstValue: '{"port":8282}',
+ },
+ // Client-side
+ {
+ id: "id.subscribeevent01",
+ type: "subscribe-event",
+ name: "",
+ topic: "",
+ thing: "id.consumedthing01",
+ event: "testEvent",
+ uriVariables: "",
+ wires: [["id.subscribeeventhelper01"]],
+ },
+ { id: "id.subscribeeventhelper01", type: "helper" },
+ {
+ id: "id.consumedthing01",
+ type: "consumed-thing",
+ tdLink: "",
+ td: JSON.stringify({
+ "@context": [
+ "https://www.w3.org/2019/wot/td/v1",
+ "https://www.w3.org/2022/wot/td/v1.1",
+ { "@language": "en" },
+ ],
+ "@type": "Thing",
+ title: "thing01",
+ securityDefinitions: { nosec: { scheme: "nosec" } },
+ security: ["nosec"],
+ events: {
+ testEvent: {
+ description: "",
+ data: { type: "string" },
+ forms: [
+ {
+ href: "http://localhost:8181/thing01/events/testEvent",
+ contentType: "application/json",
+ subprotocol: "longpoll",
+ op: ["subscribeevent", "unsubscribeevent"],
+ },
+ {
+ href: "http://localhost:8181/thing01/events/testEvent",
+ contentType: "application/cbor",
+ subprotocol: "longpoll",
+ op: ["subscribeevent", "unsubscribeevent"],
+ },
+ ],
+ },
+ },
+ id: "urn:uuid:cf950521-8eaf-4e1c-9277-758930e47246",
+ description: "",
+ }),
+ http: true,
+ ws: false,
+ coap: false,
+ mqtt: false,
+ opcua: false,
+ modbus: false,
+ basicAuth: false,
+ username: "",
+ password: "",
+ },
+ {
+ id: "id.updatetd01",
+ type: "update-td",
+ name: "",
+ thing: "id.consumedthing01",
+ tdSourceType: "msg",
+ tdSource: "payload",
+ wires: [],
+ },
+]
+
+describe("Tests for Update TD", function () {
+ this.timeout(15 * 1000)
+ before(async function () {
+ await startFlow(targetFlow, helper)
+ })
+
+ after(async function () {
+ await endFlow("id.serverconfig01", helper)
+ })
+
+ beforeEach(function (done) {
+ done()
+ })
+
+ afterEach(function (done) {
+ done()
+ })
+
+ it("update td", function (done) {
+ const clientHelperNode = helper.getNode("id.subscribeeventhelper01")
+ clientHelperNode.removeAllListeners("input")
+ let expectedEvent
+ clientHelperNode.on("input", function (msg) {
+ try {
+ assert.equal(msg.payload, expectedEvent)
+ if (expectedEvent === "event from server02") {
+ done()
+ }
+ } catch (err) {
+ done(err)
+ }
+ })
+ const serverEventNode01 = helper.getNode("id.serverevent01")
+ const serverEventNode02 = helper.getNode("id.serverevent02")
+ expectedEvent = "event from server01"
+ serverEventNode01.receive({ payload: "event from server01" })
+ serverEventNode02.receive({ payload: "event from server02" })
+ new Promise((resolve) => setTimeout(resolve, 500)).then(() => {
+ const updateTDNode = helper.getNode("id.updatetd01")
+ updateTDNode.receive({
+ payload: {
+ "@context": [
+ "https://www.w3.org/2019/wot/td/v1",
+ "https://www.w3.org/2022/wot/td/v1.1",
+ { "@language": "en" },
+ ],
+ "@type": "Thing",
+ title: "thing01",
+ securityDefinitions: { nosec: { scheme: "nosec" } },
+ security: ["nosec"],
+ events: {
+ testEvent: {
+ description: "",
+ data: { type: "string" },
+ forms: [
+ {
+ href: "http://localhost:8282/thing01/events/testEvent",
+ contentType: "application/json",
+ subprotocol: "longpoll",
+ op: ["subscribeevent", "unsubscribeevent"],
+ },
+ {
+ href: "http://localhost:8282/thing01/events/testEvent",
+ contentType: "application/cbor",
+ subprotocol: "longpoll",
+ op: ["subscribeevent", "unsubscribeevent"],
+ },
+ ],
+ },
+ },
+ id: "urn:uuid:cf950521-8eaf-4e1c-9277-758930e47246",
+ description: "",
+ },
+ })
+ new Promise((resolve) => setTimeout(resolve, 500)).then(() => {
+ expectedEvent = "event from server02"
+ serverEventNode01.receive({ payload: "event from server01" })
+ serverEventNode02.receive({ payload: "event from server02" })
+ })
+ })
+ })
+})
diff --git a/node-red-node-wot/test/util.ts b/node-red-node-wot/test/util.ts
index 09a40f6..0170b6c 100644
--- a/node-red-node-wot/test/util.ts
+++ b/node-red-node-wot/test/util.ts
@@ -6,10 +6,12 @@ const serverPropertyNode = require("../src/wot-server-property.ts")
const serverActionNode = require("../src/wot-server-action.ts")
const serverEventNode = require("../src/wot-server-event.ts")
const serverEndNode = require("../src/wot-server-end.ts")
+const serverTDNode = require("../src/wot-server-td.ts")
const thingConfigNode = require("../src/wot-thing.js")
const propertyNode = require("../src/wot-property.js")
const actionNode = require("../src/wot-action.js")
const eventNode = require("../src/wot-event.js")
+const updateTDNode = require("../src/wot-update-td.js")
const USE_NODES = [
serverConfigNode,
@@ -18,28 +20,30 @@ const USE_NODES = [
serverActionNode,
serverEventNode,
serverEndNode,
+ serverTDNode,
thingConfigNode,
propertyNode,
actionNode,
eventNode,
+ updateTDNode,
]
-const launchFlow = async (flow: any[], helper) => {
+const launchFlow = async (flow: any[], helper, waitForServientStart = 2000) => {
return new Promise((resolve, reject) => {
helper.load(USE_NODES, flow, function () {
// Wait for the servient to start.
setTimeout(function () {
resolve()
- }, 2000)
+ }, waitForServientStart)
})
})
}
-export const startFlow = async (targetFlow, helper) => {
+export const startFlow = async (targetFlow, helper, waitForServientStart = 2000) => {
return new Promise((resolve, reject) => {
helper.startServer(async function () {
try {
- await launchFlow(targetFlow, helper)
+ await launchFlow(targetFlow, helper, waitForServientStart)
resolve()
} catch (err) {
reject(err)
@@ -58,3 +62,17 @@ export const endFlow = async (id: string, helper) => {
}
})
}
+
+export const getNodeAfterStartFlow = async (id: string, helper, wait = 50, maxCount = 50) => {
+ for (let i = 0; i < maxCount; i++) {
+ let node = helper.getNode(id)
+ if (node) {
+ return node
+ } else {
+ await new Promise((resolve, reject) => {
+ setTimeout(resolve, wait)
+ })
+ }
+ }
+ throw new Error("timeout for getting node")
+}