From de3bac9a2fb24b309726f8bc399ac0d6bb63ce65 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Wed, 23 Mar 2022 17:28:58 +0900 Subject: [PATCH 01/20] Escape special characters --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 070fddb..21eeb59 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ You may need to run this with `sudo`, or from within an Administrator command sh - node-red-nodegen http://petstore.swagger.io/v2/swagger.json - cd ~/.node-red -- npm install **/node-red-contrib-swagger-petstore +- npm install \/node-red-contrib-swagger-petstore - node-red -> You can use swagger-petstore node on Node-RED flow editor. @@ -57,7 +57,7 @@ You may need to run this with `sudo`, or from within an Administrator command sh - In Node-RED flow editor, edit the function node and to the right of the 'name' option, click on the book icon and select 'Save to library'. Then fill in the 'Export as' with the file name (lower-case.js). - node-red-nodegen ~/.node-red/lib/functions/lower-case.js - cd ~/.node-red -- npm install **/node-red-contrib-lower-case +- npm install \/node-red-contrib-lower-case - node-red -> You can use lower-case node on Node-RED flow editor. @@ -66,7 +66,7 @@ You may need to run this with `sudo`, or from within an Administrator command sh - node-red-nodegen example.jsonld - cd ~/.node-red -- npm install **/node-red-contrib-example-thing +- npm install \/node-red-contrib-example-thing - node-red -> You can use Example Thing node on Node-RED flow editor. @@ -75,7 +75,7 @@ You may need to run this with `sudo`, or from within an Administrator command sh - node-red-nodegen http://example.com/td.jsonld --wottd --lang "en-US,en;q=0.5" - cd ~/.node-red -- npm install **/node-red-contrib-example-thing +- npm install \/node-red-contrib-example-thing - node-red -> You can use Example Thing node on Node-RED flow editor. @@ -86,7 +86,7 @@ You may need to run this with `sudo`, or from within an Administrator command sh - Export JSON definition of the SUBFLOW template from Export menu. We assume it is saved to a file named qrcode.json. - node-red-nodegen qrcode.json - cd ~/.node-red -- npm install **/node-red-contrib-qrcode +- npm install \/node-red-contrib-qrcode - node-red -> You can use qrcode node on Node-RED flow editor. From 76836ef2d271e2ca75296594f5851fbdeb9e3663 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Wed, 23 Mar 2022 17:50:11 +0900 Subject: [PATCH 02/20] Fix links for documents --- README.md | 13 +++++++------ docs/index.md | 2 +- docs/index_ja.md | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 21eeb59..51c7616 100644 --- a/README.md +++ b/README.md @@ -93,11 +93,12 @@ You may need to run this with `sudo`, or from within an Administrator command sh ## Documentation -- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index_ja.md#use-cases)) -- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index_ja.md#how-to-use-node-generator)) -- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index_ja.md#generated-files-which-node-package-contains)) -- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) -- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index_ja.md#how-to-create-a-node-from-function-node)) -- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.0/docs/index_ja.md#how-to-create-a-node-from-subflow)) +- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#use-cases)) +- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-use-node-generator)) +- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#generated-files-which-node-package-contains)) +- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) +- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-function-node)) +- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-subflow)) +- [How to create a node from WoT Thing Description](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-wot-thing-description) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-wot-thing-description)) Note: Currently node generator supports GET and POST methods using JSON format without authentication. diff --git a/docs/index.md b/docs/index.md index 31bc870..5cbfb46 100644 --- a/docs/index.md +++ b/docs/index.md @@ -801,7 +801,7 @@ If you want to customize the generated node, the following procedures and comman Node generater can encrypt subflow definition included in generated code. Specify AES to --encoding option and encoding key to --encodekey option. When using the node with encrypted subflow definition, specify the encoding key to OS environment variable `NR_FLOW_DECODE_KEY` before starting Node-RED. - + ## How to create a node from WoT Thing Description diff --git a/docs/index_ja.md b/docs/index_ja.md index a1a270f..e18cd7c 100644 --- a/docs/index_ja.md +++ b/docs/index_ja.md @@ -804,7 +804,7 @@ Node-REDユーザは通常、以下の手順で生成したノードをNode-RED ノードジェネレータは生成したノードのサブフロー定義を暗号化することができます。暗号化を行うには、--encodingオプションにAESを、-encodekeyに暗号化キーを指定してください。暗号化指定したノードを利用する場合は、環境変数NR_FLOW_DECODE_KEYに暗号化キーを指定します。 - + ## Thing Descriptionからノードを生成する方法 From 63b2417d2f4538d65bae7487da998d2565666dc5 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Wed, 23 Mar 2022 17:52:50 +0900 Subject: [PATCH 03/20] Bump for 0.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 63ecf8a..347627e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-nodegen", - "version": "0.2.0", + "version": "0.2.1", "description": "Node generator for Node-RED", "homepage": "http://nodered.org", "main": "./lib/nodegen.js", From ff819d74178e0f416a6e1fed6ea0d4018ecd795b Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 28 Mar 2022 18:48:47 +0900 Subject: [PATCH 04/20] Change subflow file to formatted JSON --- samples/qrcode.json | 67 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/samples/qrcode.json b/samples/qrcode.json index fde6ee2..d0daa8e 100644 --- a/samples/qrcode.json +++ b/samples/qrcode.json @@ -1 +1,66 @@ -[{"id":"921a4b1d.07bb68","type":"subflow","name":"QRcode","info":"","category":"","in":[{"x":140,"y":120,"wires":[{"id":"e563f491.e443f8"}]}],"out":[{"x":380,"y":120,"wires":[{"id":"e563f491.e443f8","port":0}]}],"env":[],"meta":{"module":"node-red-contrib-qrcode","type":"qrcode","version":"0.1.0","author":"hiroyasu.nishiyama.uq@hitachi.com","desc":"Node-RED node for converting string to QRcode","keywords":"Node-RED, subflow, QRcode","license":"Apache-2.0"},"color":"#87A980","icon":"font-awesome/fa-qrcode"},{"id":"e563f491.e443f8","type":"function","z":"921a4b1d.07bb68","name":"","func":"qrcode.toString(msg.payload, (err,str) => {\n if (err) {\n node.error(err);\n return;\n }\n node.send({payload: str});\n});","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"qrcode","module":"qrcode"}],"x":260,"y":120,"wires":[[]]}] \ No newline at end of file +[ + { + "id": "921a4b1d.07bb68", + "type": "subflow", + "name": "QRcode", + "info": "", + "category": "", + "in": [ + { + "x": 140, + "y": 120, + "wires": [ + { + "id": "e563f491.e443f8" + } + ] + } + ], + "out": [ + { + "x": 380, + "y": 120, + "wires": [ + { + "id": "e563f491.e443f8", + "port": 0 + } + ] + } + ], + "env": [], + "meta": { + "module": "node-red-contrib-qrcode", + "type": "qrcode", + "version": "0.1.0", + "author": "hiroyasu.nishiyama.uq@hitachi.com", + "desc": "Node-RED node for converting string to QRcode", + "keywords": "Node-RED, subflow, QRcode", + "license": "Apache-2.0" + }, + "color": "#87A980", + "icon": "font-awesome/fa-qrcode" + }, + { + "id": "e563f491.e443f8", + "type": "function", + "z": "921a4b1d.07bb68", + "name": "", + "func": "qrcode.toString(msg.payload, (err,str) => {\n if (err) {\n node.error(err);\n return;\n }\n node.send({payload: str});\n});", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [ + { + "var": "qrcode", + "module": "qrcode" + } + ], + "x": 260, + "y": 120, + "wires": [ + [] + ] + } +] From 04230bafd82fd2d95a801d2a0d615821d8a3c4f0 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 28 Mar 2022 18:50:17 +0900 Subject: [PATCH 05/20] Add node information into subflow file --- samples/qrcode.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/qrcode.json b/samples/qrcode.json index d0daa8e..44bdd89 100644 --- a/samples/qrcode.json +++ b/samples/qrcode.json @@ -3,7 +3,7 @@ "id": "921a4b1d.07bb68", "type": "subflow", "name": "QRcode", - "info": "", + "info": "This node generates a QR code from the text data in the `msg.payload`. The QR code in the outputted `msg.payload` is text data (not image data) described by text which consists of black rectangles and white spaces. Therefore, you can use the debug node to see the QR code.\n\n### Inputs\n- `payload` - _string_\n\n Text data which is converted to QR code\n\n### Outputs\n- `payload` - _string_\n\n Text data which contains QR code\n\n**Note**: This node uses the [node-qrcode](https://www.npmjs.com/package/qrcode) module to generate QR codes.", "category": "", "in": [ { @@ -63,4 +63,4 @@ [] ] } -] +] \ No newline at end of file From 3955106d80f0488a319e54ddd872f9de82d13bcd Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 28 Mar 2022 18:56:06 +0900 Subject: [PATCH 06/20] Output subflow information to README.md --- lib/subflow/index.js | 2 ++ templates/subflow/README.md.mustache | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/subflow/index.js b/lib/subflow/index.js index b53b8f1..8eab738 100644 --- a/lib/subflow/index.js +++ b/lib/subflow/index.js @@ -79,6 +79,7 @@ module.exports = async function(data, options) { if (!data.license || (data.license === "")) { data.license = "unknown"; } + data.info = meta.info; var params = { nodeName: data.name, @@ -88,6 +89,7 @@ module.exports = async function(data, options) { category: data.category || "subflow", description: data.desc, licenseName: data.license, + nodeRead: sf.info || "" }; // Make directory diff --git a/templates/subflow/README.md.mustache b/templates/subflow/README.md.mustache index 0ee221f..cae8709 100644 --- a/templates/subflow/README.md.mustache +++ b/templates/subflow/README.md.mustache @@ -3,10 +3,12 @@ {{&description}} -Install -------- +## Install Run the following command in your Node-RED user directory - typically `~/.node-red` npm install {{&projectName}} +## Information + +{{&nodeRead}} From 90b9ba61e64ed209ab0730c401ce8f60ada5ae43 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 28 Mar 2022 19:10:29 +0900 Subject: [PATCH 07/20] Use encoding code when the option specified only --- lib/subflow/index.js | 3 ++- templates/subflow/package.json.mustache | 33 ++++++++++++++----------- templates/subflow/subflow.js.mustache | 10 +++++--- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/subflow/index.js b/lib/subflow/index.js index 8eab738..8a3d0a4 100644 --- a/lib/subflow/index.js +++ b/lib/subflow/index.js @@ -89,7 +89,8 @@ module.exports = async function(data, options) { category: data.category || "subflow", description: data.desc, licenseName: data.license, - nodeRead: sf.info || "" + nodeRead: sf.info || "", + encoding: options.encoding }; // Make directory diff --git a/templates/subflow/package.json.mustache b/templates/subflow/package.json.mustache index 4c86ef6..9902cc9 100644 --- a/templates/subflow/package.json.mustache +++ b/templates/subflow/package.json.mustache @@ -1,18 +1,21 @@ { - "name": "{{&projectName}}", - "version": "{{&projectVersion}}", - "description": "Node-RED node for {{&nodeName}}", - "node-red": { - "nodes": { - "{{&nodeName}}": "subflow.js" - } - }, - "keywords": [ - {{#keywords}} - "{{name}}"{{^last}}, {{/last}} - {{/keywords}} - ], - "dependencies": { - "crypto-js": "4.1.1" + "name": "{{&projectName}}", + "version": "{{&projectVersion}}", + "description": "Node-RED node for {{&nodeName}}", + "keywords": [ + {{#keywords}} + "{{name}}"{{^last}}, {{/last}} + {{/keywords}} + ], + "node-red": { + "nodes": { + "{{&nodeName}}": "subflow.js" } + }, + {{#encoding}} + "dependencies": { + "crypto-js": "4.1.1" + }, + {{/encoding}} + "license": "{{&licenseName}}" } diff --git a/templates/subflow/subflow.js.mustache b/templates/subflow/subflow.js.mustache index 669f264..4a6dd58 100644 --- a/templates/subflow/subflow.js.mustache +++ b/templates/subflow/subflow.js.mustache @@ -1,11 +1,11 @@ -module.exports = function(RED) { +module.exports = function (RED) { const fs = require('fs'); const path = require("path"); - const crypt = require("crypto-js"); - const file = path.join(__dirname, "subflow.json"); const text = fs.readFileSync(file); const flow = JSON.parse(text); + {{#encoding}} + const crypt = require("crypto-js"); function getDecoder(encoding) { var settings = RED.settings; @@ -63,4 +63,8 @@ module.exports = function(RED) { } RED.nodes.registerSubflow(convFlow(flow)); + {{/encoding}} + {{^encoding}} + RED.nodes.registerSubflow(flow); + {{/encoding}} } From 0bcbfd80c0dfe9f439c56a48b97b7260f651047f Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 28 Mar 2022 19:15:50 +0900 Subject: [PATCH 08/20] Bump for 0.2.2 --- README.md | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 51c7616..af99b32 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,12 @@ You may need to run this with `sudo`, or from within an Administrator command sh ## Documentation -- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#use-cases)) -- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-use-node-generator)) -- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#generated-files-which-node-package-contains)) -- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) -- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-function-node)) -- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-subflow)) -- [How to create a node from WoT Thing Description](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index.md#how-to-create-a-node-from-wot-thing-description) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.1/docs/index_ja.md#how-to-create-a-node-from-wot-thing-description)) +- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#use-cases)) +- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-use-node-generator)) +- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#generated-files-which-node-package-contains)) +- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) +- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-function-node)) +- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-subflow)) +- [How to create a node from WoT Thing Description](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-wot-thing-description) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-wot-thing-description)) Note: Currently node generator supports GET and POST methods using JSON format without authentication. diff --git a/package.json b/package.json index 347627e..18b4bd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-nodegen", - "version": "0.2.1", + "version": "0.2.2", "description": "Node generator for Node-RED", "homepage": "http://nodered.org", "main": "./lib/nodegen.js", From 9498c47552e9aa6d92eb3812f1f0b8bd836a0a6e Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 11:32:27 +0900 Subject: [PATCH 09/20] Add subflow information to README.md if node infomation exists --- templates/subflow/README.md.mustache | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/subflow/README.md.mustache b/templates/subflow/README.md.mustache index cae8709..a3b2239 100644 --- a/templates/subflow/README.md.mustache +++ b/templates/subflow/README.md.mustache @@ -9,6 +9,8 @@ Run the following command in your Node-RED user directory - typically `~/.node-r npm install {{&projectName}} +{{#nodeRead}} ## Information {{&nodeRead}} +{{/nodeRead}} From 9bd0152c92e65c04e9bae1ccfdddbef3012d62f9 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 11:34:25 +0900 Subject: [PATCH 10/20] Change JSON indents from tabs to spaces --- lib/subflow/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/subflow/index.js b/lib/subflow/index.js index 8a3d0a4..daf2fe5 100644 --- a/lib/subflow/index.js +++ b/lib/subflow/index.js @@ -54,7 +54,7 @@ function createJSON(dstPath, flow, encoding, key) { else { sf.flow = newFlow; } - const data = JSON.stringify(sf, null, "\t"); + const data = JSON.stringify(sf, null, 4); fs.writeFileSync(dstPath, data); } From 07e6af68cf7c9c5b2b99c983e6dfd63de28dc8d3 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 11:41:08 +0900 Subject: [PATCH 11/20] Change package.json indents from 4 spaces to 2 spaces --- templates/webofthings/package.json.mustache | 68 ++++++++++----------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/templates/webofthings/package.json.mustache b/templates/webofthings/package.json.mustache index a134e96..bf9f73d 100644 --- a/templates/webofthings/package.json.mustache +++ b/templates/webofthings/package.json.mustache @@ -1,37 +1,37 @@ { - "name": "{{&projectName}}", - "version": "{{&projectVersion}}", - "description": "Node-RED node for {{&nodeName}}", - "main": "node.js", - "scripts": { - "test": "mocha \"test/**/*_spec.js\"" - }, - "node-red": { - "nodes": { - "{{&nodeName}}": "node.js" - } - }, - "keywords": [ - {{#keywords}} - "{{name}}"{{^last}}, {{/last}} - {{/keywords}} - ], - "dependencies": { - "https-proxy-agent": "5.0.0", - "request": "2.88.2", - "ws": "8.5.0", - "url-template": "2.0.8", - "ajv": "8.10.0", - "coap": "1.0.3" - }, - "devDependencies": { - "node-red": "2.2.2", - "node-red-node-test-helper": "0.2.7" - }, - "license": "{{&licenseName}}", - "wot": { - {{#wotmeta}} - "{{name}}": "{{value}}"{{^last}}, {{/last}} - {{/wotmeta}} + "name": "{{&projectName}}", + "version": "{{&projectVersion}}", + "description": "Node-RED node for {{&nodeName}}", + "main": "node.js", + "scripts": { + "test": "mocha \"test/**/*_spec.js\"" + }, + "node-red": { + "nodes": { + "{{&nodeName}}": "node.js" } + }, + "keywords": [ + {{#keywords}} + "{{name}}"{{^last}}, {{/last}} + {{/keywords}} + ], + "dependencies": { + "https-proxy-agent": "5.0.0", + "request": "2.88.2", + "ws": "8.5.0", + "url-template": "2.0.8", + "ajv": "8.10.0", + "coap": "1.0.3" + }, + "devDependencies": { + "node-red": "2.2.2", + "node-red-node-test-helper": "0.2.7" + }, + "license": "{{&licenseName}}", + "wot": { + {{#wotmeta}} + "{{name}}": "{{value}}"{{^last}}, {{/last}} + {{/wotmeta}} + } } From 76c7b2b8c507a5f5418e3cfd68147343f8be9b1b Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 11:44:09 +0900 Subject: [PATCH 12/20] Add the supported version information of Node.js and Node-RED --- templates/function/package.json.mustache | 4 ++++ templates/swagger/package.json.mustache | 4 ++++ templates/webofthings/package.json.mustache | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/templates/function/package.json.mustache b/templates/function/package.json.mustache index 095a498..bf1f3cb 100644 --- a/templates/function/package.json.mustache +++ b/templates/function/package.json.mustache @@ -6,7 +6,11 @@ "scripts": { "test": "mocha \"test/**/*_spec.js\" --timeout 3000" }, + "engines": { + "node": ">=12.0.0" + }, "node-red": { + "version": ">=1.3.7", "nodes": { "{{&nodeName}}": "node.js" } diff --git a/templates/swagger/package.json.mustache b/templates/swagger/package.json.mustache index 67049b0..a15d634 100644 --- a/templates/swagger/package.json.mustache +++ b/templates/swagger/package.json.mustache @@ -6,7 +6,11 @@ "scripts": { "test": "mocha \"test/**/*_spec.js\" --timeout 3000" }, + "engines": { + "node": ">=12.0.0" + }, "node-red": { + "version": ">=1.3.7", "nodes": { "{{&nodeName}}": "node.js" } diff --git a/templates/webofthings/package.json.mustache b/templates/webofthings/package.json.mustache index bf9f73d..4b18e4b 100644 --- a/templates/webofthings/package.json.mustache +++ b/templates/webofthings/package.json.mustache @@ -6,7 +6,11 @@ "scripts": { "test": "mocha \"test/**/*_spec.js\"" }, + "engines": { + "node": ">=12.0.0" + }, "node-red": { + "version": ">=1.3.7", "nodes": { "{{&nodeName}}": "node.js" } From c46251c2bdd02306da8a7b07c68ba72783d45565 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 11:46:40 +0900 Subject: [PATCH 13/20] Add the supported version information of Node.js and Node-RED --- templates/subflow/package.json.mustache | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/subflow/package.json.mustache b/templates/subflow/package.json.mustache index 9902cc9..a492866 100644 --- a/templates/subflow/package.json.mustache +++ b/templates/subflow/package.json.mustache @@ -7,7 +7,11 @@ "{{name}}"{{^last}}, {{/last}} {{/keywords}} ], + "engines": { + "node": ">=12.0.0" + }, "node-red": { + "version": ">=1.3.7", "nodes": { "{{&nodeName}}": "subflow.js" } From abffe424d28a165d798cc912bafca5c5a73a24d8 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 18:51:31 +0900 Subject: [PATCH 14/20] Use keywords in subflow for package.json --- lib/subflow/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/subflow/index.js b/lib/subflow/index.js index daf2fe5..bd9e7c5 100644 --- a/lib/subflow/index.js +++ b/lib/subflow/index.js @@ -79,6 +79,7 @@ module.exports = async function(data, options) { if (!data.license || (data.license === "")) { data.license = "unknown"; } + data.keywords = data.keywords || meta.keywords; data.info = meta.info; var params = { From bc65f0ce8e54047f96f286b751d03b1270f508ad Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 21:13:47 +0900 Subject: [PATCH 15/20] Use file-type@16.5.3 in swagger node to avoid loading error --- templates/swagger/package.json.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/swagger/package.json.mustache b/templates/swagger/package.json.mustache index a15d634..5d7b4f3 100644 --- a/templates/swagger/package.json.mustache +++ b/templates/swagger/package.json.mustache @@ -23,7 +23,7 @@ "dependencies": { "q": "1.5.1", "request": "2.88.2", - "file-type": "17.1.1" + "file-type": "16.5.3" }, "devDependencies": { "mocha": "9.2.1", From b77eca1a59e903da64c9083fc6ec5346bfd1f100 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Tue, 29 Mar 2022 21:59:59 +0900 Subject: [PATCH 16/20] Fix parse error when generating node from yaml file --- lib/swagger/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/swagger/index.js b/lib/swagger/index.js index 2c6d0f5..360802f 100644 --- a/lib/swagger/index.js +++ b/lib/swagger/index.js @@ -24,7 +24,7 @@ async function getSpec(src, data) { let response = await axios.get(src, requestOptions); spec = response.data; } else if (/\.yaml$/.test(src)) { - spec = yamljs.load(util.skipBom(await fs.promises.readFile(src))); + spec = yamljs.load(src); } else if (/\.json/.test(src)) { spec = JSON.parse(util.skipBom(await fs.promises.readFile(src))); } else { From db319eb10124060906a41a28f40c5c2fa91ec3fa Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Wed, 30 Mar 2022 15:52:15 +0900 Subject: [PATCH 17/20] Bump for 0.2.3 --- README.md | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index af99b32..de2b055 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,12 @@ You may need to run this with `sudo`, or from within an Administrator command sh ## Documentation -- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#use-cases)) -- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-use-node-generator)) -- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#generated-files-which-node-package-contains)) -- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) -- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-function-node)) -- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-subflow)) -- [How to create a node from WoT Thing Description](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index.md#how-to-create-a-node-from-wot-thing-description) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.2/docs/index_ja.md#how-to-create-a-node-from-wot-thing-description)) +- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#use-cases)) +- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-use-node-generator)) +- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#generated-files-which-node-package-contains)) +- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) +- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-function-node)) +- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-subflow)) +- [How to create a node from WoT Thing Description](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-wot-thing-description) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-wot-thing-description)) Note: Currently node generator supports GET and POST methods using JSON format without authentication. diff --git a/package.json b/package.json index 18b4bd3..2bf801e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-nodegen", - "version": "0.2.2", + "version": "0.2.3", "description": "Node generator for Node-RED", "homepage": "http://nodered.org", "main": "./lib/nodegen.js", From da7669cfd5083abd069f523e80f5130ae3e27b1b Mon Sep 17 00:00:00 2001 From: Kunihiko Toumura Date: Thu, 31 Mar 2022 13:53:57 +0900 Subject: [PATCH 18/20] Add MQTT binding support, etc. --- lib/webofthings/index.js | 13 + .../td-json-schema-validation.json | 2327 +++++++++-------- lib/webofthings/wotutils.js | 12 +- package.json | 4 +- templates/webofthings/node.js.mustache | 187 +- templates/webofthings/package.json.mustache | 5 +- 6 files changed, 1426 insertions(+), 1122 deletions(-) diff --git a/lib/webofthings/index.js b/lib/webofthings/index.js index 66e79a2..2845637 100644 --- a/lib/webofthings/index.js +++ b/lib/webofthings/index.js @@ -37,6 +37,19 @@ module.exports = async function(data, options) { console.info(`Schema validation succeeded.`); } + // if there is no title but titles, put one of titles to title. + if (!td.title) { + if (td.titles) { + if (td.titles.en) { + td.title = td.titles.en + } else { + td.title = Object.values(td.titles)[0]; + } + } else { + td.title = "notitle"; + } + } + // if name is not specified, use td.title for module name. if (!data.name || data.name === '') { // filtering out special characters diff --git a/lib/webofthings/td-json-schema-validation.json b/lib/webofthings/td-json-schema-validation.json index 5751ca5..f02ddc0 100644 --- a/lib/webofthings/td-json-schema-validation.json +++ b/lib/webofthings/td-json-schema-validation.json @@ -1,1153 +1,1316 @@ { - "title": "WoT TD Schema - 16 October 2019", - "description": "JSON Schema for validating TD instances against the TD model. TD instances can be with or without terms that have default values", + "title": "Thing Description", + "version": "1.1-28-January-2022", + "description": "JSON Schema for validating TD instances against the TD information model. TD instances can be with or without terms that have default values", "$schema ": "http://json-schema.org/draft-07/schema#", + "$id":"https://raw.githubusercontent.com/w3c/wot-thing-description/main/validation/td-json-schema-validation.json", "definitions": { - "thing-context-w3c-uri": { - "type": "string", - "enum": [ - "https://www.w3.org/2019/wot/td/v1" - ] - }, - "thing-context": { - "oneOf": [{ - "type": "array", - "items": { - "anyOf": [{ - "$ref": "#/definitions/anyUri" - }, - { - "type": "object" - } - ] - }, - "contains": { - "$ref": "#/definitions/thing-context-w3c-uri" - } + "anyUri": { + "type": "string", + "format": "iri-reference" + }, + "description": { + "type": "string" + }, + "descriptions": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "title": { + "type": "string" + }, + "titles": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "security": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "scopes": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "subprotocol": { + "type": "string", + "examples": [ + "longpoll", + "websub", + "sse" + ] + }, + "thing-context-w3c-uri": { + "type": "string", + "enum": [ + "https://www.w3.org/2019/wot/td/v1", + "http://www.w3.org/ns/td", + "https://www.w3.org/2022/wot/td/v1.1" + ] + }, + "thing-context": { + "oneOf": [ + { + "type": "array", + "items": [ + { + "$ref": "#/definitions/thing-context-w3c-uri" + } + ], + "additionalItems": { + "anyOf": [ + { + "$ref": "#/definitions/anyUri" }, { - "$ref": "#/definitions/thing-context-w3c-uri" + "type": "object" + } + ] + } + }, + { + "$ref": "#/definitions/thing-context-w3c-uri" + } + ] + }, + "type_declaration": { + "oneOf": [ + { + "type": "string", + "not": { + "const": "tm:ThingModel" + } + }, + { + "type": "array", + "items": { + "type": "string", + "not": { + "const": "tm:ThingModel" + } + } + } + ] + }, + "dataSchema-type": { + "type": "string", + "enum": [ + "boolean", + "integer", + "number", + "string", + "object", + "array", + "null" + ] + }, + "dataSchema": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "title": { + "$ref": "#/definitions/title" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "titles": { + "$ref": "#/definitions/titles" + }, + "writeOnly": { + "type": "boolean" + }, + "readOnly": { + "type": "boolean" + }, + "oneOf": { + "type": "array", + "items": { + "$ref": "#/definitions/dataSchema" + } + }, + "unit": { + "type": "string" + }, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "format": { + "type": "string" + }, + "const": {}, + "default": {}, + "contentEncoding": { + "type": "string" + }, + "contentMediaType": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/dataSchema-type" + }, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/dataSchema" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/dataSchema" } + } ] - }, - "type_declaration": { - "oneOf": [{ - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } + }, + "maxItems": { + "type": "integer", + "minimum": 0 + }, + "minItems": { + "type": "integer", + "minimum": 0 + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minLength": { + "type": "integer", + "minimum": 0 + }, + "maxLength": { + "type": "integer", + "minimum": 0 + }, + "multipleOf": { + "$ref": "#/definitions/multipleOfDefinition" + }, + "properties": { + "additionalProperties": { + "$ref": "#/definitions/dataSchema" + } + }, + "required": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "additionalResponsesDefinition": { + "type": "array", + "items": { + "type": "object", + "properties": { + "contentType": { + "type": "string" + }, + "schema": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + } + }, + "multipleOfDefinition": { + "type": [ + "integer", + "number" + ], + "exclusiveMinimum": 0 + }, + "expectedResponse": { + "type": "object", + "properties": { + "contentType": { + "type": "string" + } + } + }, + "form_element_base": { + "type": "object", + "properties": { + "op": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" } + } ] + }, + "href": { + "$ref": "#/definitions/anyUri" + }, + "contentType": { + "type": "string" + }, + "contentCoding": { + "type": "string" + }, + "subprotocol": { + "$ref": "#/definitions/subprotocol" + }, + "security": { + "$ref": "#/definitions/security" + }, + "scopes": { + "$ref": "#/definitions/scopes" + }, + "response": { + "$ref": "#/definitions/expectedResponse" + }, + "additionalResponses": { + "$ref": "#/definitions/additionalResponsesDefinition" + } }, - "property_element": { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "uriVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "forms": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/form_element_property" - } - }, - "observable": { - "type": "boolean" - }, - "writeOnly": { - "type": "boolean" - }, - "readOnly": { - "type": "boolean" - }, - "oneOf": { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - }, - "unit": { - "type": "string" - }, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "format": { - "type": "string" - }, - "const": {}, - "type": { - "type": "string", - "enum": [ - "boolean", - "integer", - "number", - "string", - "object", - "array", - "null" - ] - }, + "required": [ + "href" + ], + "additionalProperties": true + }, + "form_element_property": { + "allOf":[{"$ref":"#/definitions/form_element_base"}], + "type": "object", + "properties": { + "op": { + "oneOf": [ + { + "type": "string", + "enum": [ + "readproperty", + "writeproperty", + "observeproperty", + "unobserveproperty" + ] + }, + { + "type": "array", "items": { - "oneOf": [{ - "$ref": "#/definitions/dataSchema" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - } - ] - }, - "maxItems": { - "type": "integer", - "minimum": 0 - }, - "minItems": { - "type": "integer", - "minimum": 0 - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "properties": { - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "required": { - "type": "array", - "items": { - "type": "string" - } + "type": "string", + "enum": [ + "readproperty", + "writeproperty", + "observeproperty", + "unobserveproperty" + ] } - }, - "required": [ - "forms" - ], - "additionalProperties": true + } + ] + } }, - "action_element": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "uriVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "forms": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/form_element_action" - } - }, - "input": { - "$ref": "#/definitions/dataSchema" - }, - "output": { - "$ref": "#/definitions/dataSchema" - }, - "safe": { - "type": "boolean" - }, - "idempotent": { - "type": "boolean" + "additionalProperties": true + }, + "form_element_action": { + "allOf":[{"$ref":"#/definitions/form_element_base"}], + "type": "object", + "properties": { + "op": { + "oneOf": [ + { + "type": "string", + "enum": [ + "invokeaction", + "queryaction", + "cancelaction" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "invokeaction", + "queryaction", + "cancelaction" + ] } - }, - "required": [ - "forms" - ], - "additionalProperties": true + } + ] + } }, - "event_element": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "uriVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "forms": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/form_element_event" - } - }, - "subscription": { - "$ref": "#/definitions/dataSchema" - }, - "data": { - "$ref": "#/definitions/dataSchema" - }, - "cancellation": { - "$ref": "#/definitions/dataSchema" - }, - "type": { - "not": {} - }, - "enum": { - "not": {} - }, - "const": { - "not": {} + "additionalProperties": true + }, + "form_element_event": { + "allOf":[{"$ref":"#/definitions/form_element_base"}], + "type": "object", + "properties": { + "op": { + "oneOf": [ + { + "type": "string", + "enum": [ + "subscribeevent", + "unsubscribeevent" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "subscribeevent", + "unsubscribeevent" + ] } - }, - "required": [ - "forms" - ], - "additionalProperties": true + } + ] + } }, - "form_element_property": { - "type": "object", - "properties": { - "href": { - "$ref": "#/definitions/anyUri" - }, - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "readproperty", - "writeproperty", - "observeproperty", - "unobserveproperty" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "readproperty", - "writeproperty", - "observeproperty", - "unobserveproperty" - ] - } - } - ] - }, - "contentType": { - "type": "string" - }, - "security": { - "type": "array", - "items": { - "type": "string" - } - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "subProtocol": { - "type": "string", - "enum": [ - "longpoll", - "websub", - "sse" - ] - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } + "additionalProperties": true + }, + "form_element_root": { + "allOf":[{"$ref":"#/definitions/form_element_base"}], + "type": "object", + "properties": { + "op": { + "oneOf": [ + { + "type": "string", + "enum": [ + "readallproperties", + "writeallproperties", + "readmultipleproperties", + "writemultipleproperties", + "observeallproperties", + "unobserveallproperties", + "queryallactions", + "subscribeallevents", + "unsubscribeallevents" + ] + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ + "readallproperties", + "writeallproperties", + "readmultipleproperties", + "writemultipleproperties", + "observeallproperties", + "unobserveallproperties", + "queryallactions", + "subscribeallevents", + "unsubscribeallevents" + ] } - }, - "required": [ - "href" - ], - "additionalProperties": true + } + ] + } }, - "form_element_action": { + "additionalProperties": true + }, + "form" : { + "$comment":"This is NOT for validation purposes but for automatic generation of TS types. For more info, please see: https://github.com/w3c/wot-thing-description/pull/1319#issuecomment-994950057", + "oneOf": [ + { "$ref" : "#/definitions/form_element_property" }, + { "$ref" : "#/definitions/form_element_action" }, + { "$ref" : "#/definitions/form_element_event" }, + { "$ref" : "#/definitions/form_element_root" } + ] + }, + "property_element": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "title": { + "$ref": "#/definitions/title" + }, + "titles": { + "$ref": "#/definitions/titles" + }, + "forms": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/form_element_property" + } + }, + "uriVariables": { "type": "object", - "properties": { - "href": { - "$ref": "#/definitions/anyUri" - }, - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "invokeaction" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "invokeaction" - ] - } - } - ] - }, - "contentType": { - "type": "string" - }, - "security": { - "type": "array", - "items": { - "type": "string" - } - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "subProtocol": { - "type": "string", - "enum": [ - "longpoll", - "websub", - "sse" - ] - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } + "additionalProperties": { + "$ref": "#/definitions/dataSchema" + } + }, + "observable": { + "type": "boolean" + }, + "writeOnly": { + "type": "boolean" + }, + "readOnly": { + "type": "boolean" + }, + "oneOf": { + "type": "array", + "items": { + "$ref": "#/definitions/dataSchema" + } + }, + "unit": { + "type": "string" + }, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "format": { + "type": "string" + }, + "const": {}, + "default": {}, + "type": { + "$ref": "#/definitions/dataSchema-type" + }, + "items": { + "oneOf": [ + { + "$ref": "#/definitions/dataSchema" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/dataSchema" } - }, - "required": [ - "href" - ], - "additionalProperties": true + } + ] + }, + "maxItems": { + "type": "integer", + "minimum": 0 + }, + "minItems": { + "type": "integer", + "minimum": 0 + }, + "minimum": { + "type": "number" + }, + "maximum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minLength": { + "type": "integer", + "minimum": 0 + }, + "maxLength": { + "type": "integer", + "minimum": 0 + }, + "multipleOf": { + "$ref": "#/definitions/multipleOfDefinition" + }, + "properties": { + "additionalProperties": { + "$ref": "#/definitions/dataSchema" + } + }, + "required": { + "type": "array", + "items": { + "type": "string" + } + } }, - "form_element_event": { + "required": [ + "forms" + ], + "additionalProperties": true + }, + "action_element": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "title": { + "$ref": "#/definitions/title" + }, + "titles": { + "$ref": "#/definitions/titles" + }, + "forms": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/form_element_action" + } + }, + "uriVariables": { "type": "object", - "properties": { - "href": { - "$ref": "#/definitions/anyUri" - }, - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] - } - } - ] - }, - "contentType": { - "type": "string" - }, - "security": { - "type": "array", - "items": { - "type": "string" - } - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "subProtocol": { - "type": "string", - "enum": [ - "longpoll", - "websub", - "sse" - ] - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } - } - }, - "required": [ - "href" - ], - "additionalProperties": true + "additionalProperties": { + "$ref": "#/definitions/dataSchema" + } + }, + "input": { + "$ref": "#/definitions/dataSchema" + }, + "output": { + "$ref": "#/definitions/dataSchema" + }, + "safe": { + "type": "boolean" + }, + "idempotent": { + "type": "boolean" + } }, - "form_element_root": { + "required": [ + "forms" + ], + "additionalProperties": true + }, + "event_element": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "title": { + "$ref": "#/definitions/title" + }, + "titles": { + "$ref": "#/definitions/titles" + }, + "forms": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/form_element_event" + } + }, + "uriVariables": { "type": "object", - "properties": { - "href": { - "$ref": "#/definitions/anyUri" - }, - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "readallproperties", - "writeallproperties", - "readmultipleproperties", - "writemultipleproperties" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "readallproperties", - "writeallproperties", - "readmultipleproperties", - "writemultipleproperties" - ] - } - } - ] - }, - "contentType": { - "type": "string" - }, - "security": { - "type": "array", - "items": { - "type": "string" - } - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "subProtocol": { - "type": "string", - "enum": [ - "longpoll", - "websub", - "sse" - ] - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } - } - }, - "required": [ - "href" - ], - "additionalProperties": true + "additionalProperties": { + "$ref": "#/definitions/dataSchema" + } + }, + "subscription": { + "$ref": "#/definitions/dataSchema" + }, + "data": { + "$ref": "#/definitions/dataSchema" + }, + "dataResponse": { + "$ref": "#/definitions/dataSchema" + }, + "cancellation": { + "$ref": "#/definitions/dataSchema" + } }, - "description": { + "required": [ + "forms" + ], + "additionalProperties": true + }, + "base_link_element": { + "type": "object", + "properties": { + "href": { + "$ref": "#/definitions/anyUri" + }, + "type": { "type": "string" - }, - "title": { + }, + "rel": { "type": "string" + }, + "anchor": { + "$ref": "#/definitions/anyUri" + } }, - "descriptions": { - "type": "object" + "required": [ + "href" + ], + "additionalProperties": true + }, + "link_element": { + "allOf": [ + { + "$ref": "#/definitions/base_link_element" + }, + { + "not": { + "description": "A basic link element should not contain sizes", + "type": "object", + "properties": { + "sizes": {} + }, + "required": [ + "sizes" + ] + } + }, + { + "not": { + "description": "A basic link element should not contain icon or tm:extends", + "properties": { + "rel": { + "enum": [ + "icon", + "tm:extends" + ] + } + }, + "required": [ + "rel" + ] + } + } + ] + }, + "icon_link_element": { + "allOf": [ + { + "$ref": "#/definitions/base_link_element" + }, + { + "properties": { + "rel": { + "const": "icon" + }, + "sizes": { + "type": "string", + "pattern": "[0-9]*x[0-9]+" + } + }, + "required": [ + "rel" + ] + } + ] + }, + "noSecurityScheme": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "nosec" + ] + } }, - "titles": { - "type": "object" + "required": [ + "scheme" + ] + }, + "autoSecurityScheme": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "auto" + ] + } }, - "dataSchema": { + "required": [ + "scheme" + ] + }, + "comboSecurityScheme": { + "oneOf": [ + { "type": "object", "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "title": { - "$ref": "#/definitions/title" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "writeOnly": { - "type": "boolean" - }, - "readOnly": { - "type": "boolean" - }, - "oneOf": { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - }, - "unit": { - "type": "string" - }, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "format": { - "type": "string" - }, - "const": {}, - "type": { - "type": "string", - "enum": [ - "boolean", - "integer", - "number", - "string", - "object", - "array", - "null" - ] - }, + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "combo" + ] + }, + "oneOf": { + "type": "array", + "minItems": 2, "items": { - "oneOf": [{ - "$ref": "#/definitions/dataSchema" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - } - ] - }, - "maxItems": { - "type": "integer", - "minimum": 0 - }, - "minItems": { - "type": "integer", - "minimum": 0 - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "properties": { - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "required": { - "type": "array", - "items": { - "type": "string" - } + "type": "string" } - } - }, - "link_element": { + } + }, + "required": [ + "scheme", + "oneOf" + ] + }, + { "type": "object", "properties": { - "anchor": { - "$ref": "#/definitions/anyUri" - }, - "href": { - "$ref": "#/definitions/anyUri" - }, - "rel": { - "type": "string" - }, - "type": { - "type": "string" + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "combo" + ] + }, + "allOf": { + "type": "array", + "minItems": 2, + "items": { + "type": "string" } + } }, "required": [ - "href" - ], - "additionalProperties": true - }, - "securityScheme": { - "oneOf": [{ - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "nosec" - ] - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "basic" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "cert" - ] - }, - "identity": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "digest" - ] - }, - "qop": { - "type": "string", - "enum": [ - "auth", - "auth-int" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "bearer" - ] - }, - "authorization": { - "$ref": "#/definitions/anyUri" - }, - "alg": { - "type": "string", - "enum": [ - "MD5", - "ES256", - "ES512-256" - ] - }, - "format": { - "type": "string", - "enum": [ - "jwt", - "jwe", - "jws" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "psk" - ] - }, - "identity": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "public" - ] - }, - "identity": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "oauth2" - ] - }, - "authorization": { - "$ref": "#/definitions/anyUri" - }, - "token": { - "$ref": "#/definitions/anyUri" - }, - "refresh": { - "$ref": "#/definitions/anyUri" - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "flow": { - "type": "string", - "enum": [ - "implicit", - "password", - "client", - "code" - ] - } - }, - "required": [ - "scheme", - "flow" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "apikey" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "pop" - ] - }, - "authorization": { - "$ref": "#/definitions/anyUri" - }, - "format": { - "type": "string", - "enum": [ - "jwt", - "jwe", - "jws" - ] - }, - "alg": { - "type": "string", - "enum": [ - "MD5", - "ES256", - "ES512-256" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - } + "scheme", + "allOf" ] - }, - "anyUri": { + } + ] + }, + "basicSecurityScheme": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { "type": "string", - "format": "uri-reference" - } - }, - "type": "object", - "properties": { - "id": { + "enum": [ + "basic" + ] + }, + "in": { "type": "string", - "format": "uri" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" + "enum": [ + "header", + "query", + "body", + "cookie", + "auto" + ] + }, + "name": { + "type": "string" + } }, + "required": [ + "scheme" + ] + }, + "digestSecurityScheme": { + "type": "object", "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/property_element" - } - }, - "actions": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/action_element" - } + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "digest" + ] + }, + "qop": { + "type": "string", + "enum": [ + "auth", + "auth-int" + ] + }, + "in": { + "type": "string", + "enum": [ + "header", + "query", + "body", + "cookie", + "auto" + ] + }, + "name": { + "type": "string" + } }, - "events": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/event_element" - } + "required": [ + "scheme" + ] + }, + "apiKeySecurityScheme": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "apikey" + ] + }, + "in": { + "type": "string", + "enum": [ + "header", + "query", + "body", + "cookie" + ] + }, + "name": { + "type": "string" + } }, - "description": { + "required": [ + "scheme" + ] + }, + "bearerSecurityScheme": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "bearer" + ] + }, + "authorization": { + "$ref": "#/definitions/anyUri" + }, + "alg": { + "type": "string" + }, + "format": { + "type": "string" + }, + "in": { + "type": "string", + "enum": [ + "header", + "query", + "body", + "cookie", + "auto" + ] + }, + "name": { + "type": "string" + } }, - "descriptions": { + "required": [ + "scheme" + ] + }, + "pskSecurityScheme": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "psk" + ] + }, + "identity": { + "type": "string" + } }, - "version": { - "type": "object", - "properties": { - "instance": { - "type": "string" + "required": [ + "scheme" + ] + }, + "oAuth2SecurityScheme": { + "type": "object", + "properties": { + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "proxy": { + "$ref": "#/definitions/anyUri" + }, + "scheme": { + "type": "string", + "enum": [ + "oauth2" + ] + }, + "authorization": { + "$ref": "#/definitions/anyUri" + }, + "token": { + "$ref": "#/definitions/anyUri" + }, + "refresh": { + "$ref": "#/definitions/anyUri" + }, + "scopes": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" } - }, - "required": [ - "instance" + }, + { + "type": "string" + } ] + }, + "flow": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "code", + "client", + "device" + ] + } + ] + } }, - "links": { - "type": "array", - "items": { - "$ref": "#/definitions/link_element" - } + "required": [ + "scheme" + ] + }, + "securityScheme": { + "oneOf": [ + { + "$ref": "#/definitions/noSecurityScheme" + }, + { + "$ref": "#/definitions/autoSecurityScheme" + }, + { + "$ref": "#/definitions/comboSecurityScheme" + }, + { + "$ref": "#/definitions/basicSecurityScheme" + }, + { + "$ref": "#/definitions/digestSecurityScheme" + }, + { + "$ref": "#/definitions/apiKeySecurityScheme" + }, + { + "$ref": "#/definitions/bearerSecurityScheme" + }, + { + "$ref": "#/definitions/pskSecurityScheme" + }, + { + "$ref": "#/definitions/oAuth2SecurityScheme" + } + ] + } + }, + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri" + }, + "title": { + "$ref": "#/definitions/title" + }, + "titles": { + "$ref": "#/definitions/titles" + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/property_element" + } + }, + "actions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/action_element" + } + }, + "events": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/event_element" + } + }, + "description": { + "$ref": "#/definitions/description" + }, + "descriptions": { + "$ref": "#/definitions/descriptions" + }, + "version": { + "type": "object", + "properties": { + "instance": { + "type": "string" + } }, - "forms": { + "required": [ + "instance" + ] + }, + "links": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/definitions/link_element" + }, + { + "$ref": "#/definitions/icon_link_element" + } + ] + } + }, + "forms": { "type": "array", "minItems": 1, "items": { - "$ref": "#/definitions/form_element_root" + "$ref": "#/definitions/form_element_root" } - }, - "base": { + }, + "base": { + "$ref": "#/definitions/anyUri" + }, + "securityDefinitions": { + "type": "object", + "minProperties": 1, + "additionalProperties": { + "$ref": "#/definitions/securityScheme" + } + }, + "schemaDefinitions": { + "type": "object", + "minProperties": 1, + "additionalProperties": { + "$ref": "#/definitions/dataSchema" + } + }, + "support": { + "$ref": "#/definitions/anyUri" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "modified": { + "type": "string", + "format": "date-time" + }, + "profile": { + "oneOf": [ + { "$ref": "#/definitions/anyUri" - }, - "securityDefinitions": { - "type": "object", - "minProperties": 1, - "additionalProperties": { - "$ref": "#/definitions/securityScheme" + }, + { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/definitions/anyUri" } - }, - "support": { - "$ref": "#/definitions/anyUri" - }, - "created": { + } + ] + }, + "security": { + "oneOf": [ + { "type": "string" - }, - "modified": { - "type": "string" - }, - "security": { + }, + { "type": "array", "minItems": 1, "items": { - "type": "string" + "type": "string" } - }, - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "@context": { - "$ref": "#/definitions/thing-context" + } + ] + }, + "uriVariables": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/dataSchema" } + }, + "@type": { + "$ref": "#/definitions/type_declaration" + }, + "@context": { + "$ref": "#/definitions/thing-context" + } }, "required": [ - "title", - "security", - "securityDefinitions", - "@context" + "title", + "security", + "securityDefinitions", + "@context" ], "additionalProperties": true -} + } diff --git a/lib/webofthings/wotutils.js b/lib/webofthings/wotutils.js index 0e95a4c..d9b35b8 100644 --- a/lib/webofthings/wotutils.js +++ b/lib/webofthings/wotutils.js @@ -15,6 +15,8 @@ **/ const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); +const addFormatsDraft2019 = require('ajv-formats-draft2019'); const url = require('url'); const fs = require('fs'); const path = require('path'); @@ -22,7 +24,9 @@ const path = require('path'); function validateTd(td) { const TDSchema = JSON.parse(fs.readFileSync(path.join(__dirname, './td-json-schema-validation.json'), 'utf-8')); - const ajv = new Ajv({allErrors: true}); + const ajv = new Ajv({allErrors: true, strict: false, strictSchema: false}); + addFormats(ajv); + addFormatsDraft2019(ajv); const valid = ajv.validate(TDSchema, td); return { result: valid, errorText: valid?"":ajv.errorsText()}; @@ -237,7 +241,7 @@ function filterFormTd(td) { let forms = td.properties[p].forms; if (forms) { forms = forms.filter((f) => (f.hasOwnProperty("href") && - (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/)))); + (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/) || f.href.match(/^mqtt:/)))); } td.properties[p].forms = forms; } @@ -245,7 +249,7 @@ function filterFormTd(td) { let forms = td.actions[a].forms; if (forms) { forms = forms.filter((f) => (f.hasOwnProperty("href") && - (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/)))); + (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/) || f.href.match(/^mqtt:/)))); } td.actions[a].forms = forms; } @@ -253,7 +257,7 @@ function filterFormTd(td) { let forms = td.events[e].forms; if (forms) { forms = forms.filter((f) => (f.hasOwnProperty("href") && - (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/)))); + (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/) || f.href.match(/^mqtt:/)))); } td.events[e].forms = forms; } diff --git a/package.json b/package.json index 2bf801e..18cdb67 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,9 @@ "web of things" ], "dependencies": { - "ajv": "6.12.6", + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "ajv-formats-draft2019": "1.6.1", "api-spec-converter": "2.12.0", "axios": "0.26.0", "colors": "1.4.0", diff --git a/templates/webofthings/node.js.mustache b/templates/webofthings/node.js.mustache index a388c71..66d8e78 100644 --- a/templates/webofthings/node.js.mustache +++ b/templates/webofthings/node.js.mustache @@ -7,6 +7,7 @@ module.exports = function (RED) { const urltemplate = require('url-template'); const Ajv = require('ajv').default; const nodecoap = require('coap'); + const mqtt = require('mqtt'); function extractTemplate(href, context={}) { return urltemplate.parse(href).expand(context); @@ -15,9 +16,9 @@ module.exports = function (RED) { function getResType(form) { if (form) { if (form.response && form.response.contentType) { - return form.response.contentType; + return form.response.contentType.split(';')[0]; } else if (form.contentType) { - return form.contentType; + return form.contentType.split(';')[0]; } } return "application/json"; @@ -25,7 +26,7 @@ module.exports = function (RED) { function isBinaryType(contentType) { let result; - switch (contentType) { + switch (contentType.split(';')[0]) { case "image/jpeg": case "application/octet-stream": result = true; @@ -37,6 +38,101 @@ module.exports = function (RED) { return result; } + function bindingMqtt(node, send, done, form, options={}) { // options.interaction, options.reqbody, options.msg + let jobqueue = []; // [{topic, op: "publish"/"subscribe"/"subscribeonce", msg, callback(recvmsg)}] + const mqttcli = options.mqttcli; + mqttcli.on('connect', () => { + node.trace('connected to broker'); + flushJobQueue(); + }); + function flushJobQueue() { + jobqueue.forEach((e) => { + processMqttOp(e); + }); + jobqueue = []; + } + function processMqttOp({ topic, op, msg, callback }) { + switch (op) { + case "publish": + mqttcli.publish(topic, msg); + break; + case "subscribe": + mqttcli.on('message', (t, m, _) => { + if (t !== topic) { + node.trace(`wrong topic: ${t}`); + } + callback(m); + }) + mqttcli.subscribe(topic); + break; + case "subscribeonce": + mqttcli.once('message', (t, m, _) => { + if (t !== topic) { + node.trace(`wrong topic: ${t}`); + } + mqttcli.unsubscribe(topic); + callback(m); + }) + mqttcli.subscribe(topic); + default: + // unknown op + break; + } + } + function mkmessage(form, origmessage) { + let retval; + if (getResType(form) == "application/json") { + try { + retval = {payload: JSON.parse(origmessage.toString())}; + } catch (_) { + retval = {payload: origmessage}; + } + } else { + retval = {payload: origmessage}; + } + return retval; + } + + const msg = options.msg || {}; + const topic = (new URL(extractTemplate(form.href, options.urivars))).pathname.slice(1); + node.trace(`interaction: ${options.interaction}`); + switch (options.interaction) { + case "property-read": + node.trace(`MQTT subscribe (property-read)`); + jobqueue.push({topic, op: "subscribeonce", callback: (m) => { + send(mkmessage(form, m)); + done(); + }}); + break; + case "property-write": + node.trace(`MQTT publish (property-write)`); + jobqueue.push({topic, op: "publish", msg: JSON.stringify(options.reqbody), callback: (_) => {done()}}); + break; + case "property-observe": + node.trace(`MQTT subscribe (property-observe)`); + jobqueue.push({topic, op: "subscribe", callback: (m) => { + node.send(mkmessage(form, m)); + }}); + break; + case "action": + node.trace(`MQTT publish (action)`); + jobqueue.push({topic, op: "publish", msg: JSON.stringify(options.reqbody), callback: (_) => {done()}}); + break; + case "event": + node.trace(`MQTT subscribe (event)`); + jobqueue.push({topic, op: "subscribe", callback: (m) => { + node.send(mkmessage(form, m)); + }}); + break; + default: + node.trace(`Unknown interaction`); + break; + } + if (mqttcli.connected) { + flushJobQueue(); + } + } + function coapMethodCodeToName(methodCode) { switch (methodCode) { case '1': @@ -451,6 +547,26 @@ module.exports = function (RED) { return auth; } + function setupMqttConnection(form, node) { + const u = new URL(form.href); + const mqtturi = `${u.protocol}//${u.host}`; + const cli = mqtt.connect(mqtturi); + cli.on('connect', ()=> { + node.status({fill:"green", shape:"dot", text:"connected"}); + }); + cli.on('reconnect', () => { + node.status({fill:"yellow", shape:"dot", text:"reconnecting..."}); + }); + cli.on('offline', () => { + node.status({fill:"red", shape:"ring", text:"disconnected"}); + }); + cli.on('error', () => { + node.status({fill:"red", shape:"dot", text:"error"}); + }); + return cli; + } + + function Node(config) { RED.nodes.createNode(this, config); const node = this; @@ -465,16 +581,20 @@ module.exports = function (RED) { const username = node.credentials.username; const password = node.credentials.password; const token = node.credentials.token; + let mqttcli = null; node.td = {{{tdstr}}}; const normTd = {{{normtd}}}; if (node.interactiontype === "property") { + const prop = normTd.properties[node.propname]; + const form = prop.forms[node.formindex];// formSelection("property-read", prop.forms); + const auth = makeauth(normTd, form, username, password, token); + if (form.href.match(/^mqtt:/)) { + mqttcli = setupMqttConnection(form, node); + } if (node.proptype === "read") { node.on("input", (msg, send, done) => { send = send || function() { node.send.apply(node,arguments) }; - const prop = normTd.properties[node.propname]; - const form = prop.forms[node.formindex];// formSelection("property-read", prop.forms); - const auth = makeauth(normTd, form, username, password, token); const urivars = prop.hasOwnProperty("uriVariables") ? msg.payload : {}; if (prop.uriVariables) { const ajv = new Ajv({allErrors: true, strict: false, validateFormats: false}); @@ -487,14 +607,13 @@ module.exports = function (RED) { bindingHttp(node, send, done, form, {interaction:"property-read", auth, msg, urivars, outschema: prop}); } else if (form.href.match(/^coaps?:/)) { bindingCoap(node, send, done, form, {interaction:"property-read", auth, msg, urivars, outschema: prop}); + } else if (form.href.match(/^mqtt:/)) { + bindingMqtt(node, send, done, form, {interaction:"property-read", auth, msg, urivars, outschema: prop, mqttcli}); } }); } else if (node.proptype === "write") { node.on("input", (msg, send, done) => { send = send || function() { node.send.apply(node,arguments) }; - const prop = normTd.properties[node.propname]; - const form = prop.forms[node.formindex];// formSelection("property-write", prop.forms); - const auth = makeauth(normTd, form, username, password, token); const ajv = new Ajv({allErrors: true, strict: false, validateFormats: false}); if (!ajv.validate(prop, msg.payload)) { node.warn(`input schema validation error: ${ajv.errorsText()}`, msg); @@ -504,34 +623,31 @@ module.exports = function (RED) { bindingHttp(node, send, done, form, {interaction: "property-write", auth, msg, reqbody: msg.payload}); } else if (form.href.match(/^coaps?:/)) { bindingCoap(node, send, done, form, {interaction: "property-write", auth, msg, reqbody: msg.payload}); + } else if (form.href.match(/^mqtt:/)) { + bindingMqtt(node, send, done, form, {interaction: "property-write", auth, msg, reqbody: msg.payload, mqttcli}); } }); } else if (node.proptype === "observe") { - const prop = normTd.properties[node.propname]; - const form = prop.forms[node.formindex];// formSelection("property-observe", prop.forms); - const auth = makeauth(normTd, form, username, password, token); - const urivars = prop.hasOwnProperty("uriVariables") ? msg.payload : {}; - if (prop.uriVariables) { - const ajv = new Ajv({allErrors: true, strict: false, validateFormats: false}); - const uvschema = { type: "object", properties: prop.uriVariables }; - if (!ajv.validate(uvschema, urivars)) { - node.warn(`input schema validation error: ${ajv.errorsText()}`, msg); - } - } + const urivars = {}; if (form.href.match(/^wss?:/)) { // websocket bindingWebSocket(node, form, {auth, urivars, outschema: prop}); } else if (form.href.match(/^coaps?:/)) { // CoAP observe bindingCoapObserve(node, form, {auth, urivars, outschema: prop}); - } else { // long polling + } else if (form.href.match(/^https?:/)) { // long polling bindingLongPoll(node, form, {auth, urivars, outschema: prop}); + } else if (form.href.match(/^mqtt:/)) { + bindingMqtt(node, undefined, undefined, form, {interaction: "property-observe", auth, urivars, outschema: prop, mqttcli}); } } } else if (node.interactiontype === "action") { + const act = normTd.actions[node.actname]; + const form = act.forms[node.formindex];// formSelection("action", act.forms); + const auth = makeauth(normTd, form, username, password, token); + if (form.href.match(/^mqtt:/)) { + mqttcli = setupMqttConnection(form, node); + } node.on("input", (msg, send, done) => { send = send || function() { node.send.apply(node,arguments) }; - const act = normTd.actions[node.actname]; - const form = act.forms[node.formindex];// formSelection("action", act.forms); - const auth = makeauth(normTd, form, username, password, token); const urivars = act.hasOwnProperty("uriVariables") ? msg.payload : {}; if (act.uriVariables) { const ajv = new Ajv({allErrors: true, strict: false, validateFormats: false}); @@ -550,28 +666,33 @@ module.exports = function (RED) { bindingHttp(node, send, done, form, {interaction: "action", auth, msg, urivars, reqbody:msg.payload}); } else if (form.href.match(/^coaps?/)) { bindingCoap(node, send, done, form, {interaction: "action", auth, msg, urivars, reqbody:msg.payload}); + } else if (form.href.match(/^mqtt:/)) { + bindingMqtt(node, send, done, form, {interaction: "action", auth, msg, urivars, reqbody:msg.payload, mqttcli}); } }); } else if (node.interactiontype === "event") { const ev = normTd.events[node.evname]; const form = ev.forms[node.formindex];// formSelection("event", ev.forms); const auth = makeauth(normTd, form, username, password, token); - const urivars = ev.hasOwnProperty("uriVariables") ? msg.payload : {}; - if (ev.uriVariables) { - const ajv = new Ajv({allErrors: true, strict: false, validateFormats: false}); - const uvschema = { type: "object", properties: ev.uriVariables }; - if (!ajv.validate(uvschema, urivars)) { - node.warn(`input schema validation error: ${ajv.errorsText()}`, msg); - } + if (form.href.match(/^mqtt:/)) { + mqttcli = setupMqttConnection(form, node); } + const urivars = {}; if (form.href.match(/^wss?:/)) { // websocket bindingWebSocket(node, form, {auth, urivars}); } else if (form.href.match(/^coaps?:/)) { bindingCoapObserve(node, form, {auth, urivars}); - } else { // long polling + } else if (form.href.match(/^https?:/)) { // long polling bindingLongPoll(node, form, {auth, urivars}); - } + } else if (form.href.match(/^mqtt:/)) { + bindingMqtt(node, undefined, undefined, form, {interaction: "event", auth, urivars, mqttcli }); + } } + node.on('close', () => { + if (mqttcli) { + mqttcli.end(true); + } + }); } RED.nodes.registerType("{{&nodeName}}", Node, { diff --git a/templates/webofthings/package.json.mustache b/templates/webofthings/package.json.mustache index 4b18e4b..3d21096 100644 --- a/templates/webofthings/package.json.mustache +++ b/templates/webofthings/package.json.mustache @@ -25,8 +25,9 @@ "request": "2.88.2", "ws": "8.5.0", "url-template": "2.0.8", - "ajv": "8.10.0", - "coap": "1.0.3" + "ajv": "8.11.0", + "coap": "1.0.3", + "mqtt": "4.3.7" }, "devDependencies": { "node-red": "2.2.2", From 5b719eb992f18bc515e9834515784fa2c06a2621 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Thu, 31 Mar 2022 20:21:29 +0900 Subject: [PATCH 19/20] Bump for 0.2.4 --- README.md | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index de2b055..e6bb8b4 100644 --- a/README.md +++ b/README.md @@ -93,12 +93,12 @@ You may need to run this with `sudo`, or from within an Administrator command sh ## Documentation -- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#use-cases)) -- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-use-node-generator)) -- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#generated-files-which-node-package-contains)) -- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) -- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-function-node)) -- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-subflow)) -- [How to create a node from WoT Thing Description](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index.md#how-to-create-a-node-from-wot-thing-description) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.3/docs/index_ja.md#how-to-create-a-node-from-wot-thing-description)) +- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index_ja.md#use-cases)) +- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index_ja.md#how-to-use-node-generator)) +- [Generated files which node package contains](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index_ja.md#generated-files-which-node-package-contains)) +- [How to create a node from OpenAPI document](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index_ja.md#how-to-create-a-node-from-openapi-document)) +- [How to create a node from function node](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index_ja.md#how-to-create-a-node-from-function-node)) +- [How to create a node from subflow](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index_ja.md#how-to-create-a-node-from-subflow)) +- [How to create a node from WoT Thing Description](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index.md#how-to-create-a-node-from-wot-thing-description) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.2.4/docs/index_ja.md#how-to-create-a-node-from-wot-thing-description)) Note: Currently node generator supports GET and POST methods using JSON format without authentication. diff --git a/package.json b/package.json index 18cdb67..9eaef0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-nodegen", - "version": "0.2.3", + "version": "0.2.4", "description": "Node generator for Node-RED", "homepage": "http://nodered.org", "main": "./lib/nodegen.js", From f5bdc79150188ade9babcb8565e447570ce31cad Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Wed, 15 Jun 2022 00:13:30 +0900 Subject: [PATCH 20/20] Add error handling for no module property in subflow --- bin/node-red-nodegen.js | 4 +--- lib/subflow/index.js | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/bin/node-red-nodegen.js b/bin/node-red-nodegen.js index 2821e0c..824b129 100755 --- a/bin/node-red-nodegen.js +++ b/bin/node-red-nodegen.js @@ -110,8 +110,7 @@ function skipBom(body) { function isSubflowDefinition(data) { return data.find((item) => { - return ((item.type === "subflow") && - (item.hasOwnProperty("meta"))); + return item.type === "subflow"; }); } @@ -156,7 +155,6 @@ if (argv.help || argv.h) { promise.then(function (result) { console.log('Success: ' + result); }).catch(function (error) { - console.log('Error: ' + error); console.log(error.stack); }); } diff --git a/lib/subflow/index.js b/lib/subflow/index.js index bd9e7c5..c70ea80 100644 --- a/lib/subflow/index.js +++ b/lib/subflow/index.js @@ -7,7 +7,6 @@ const crypt = require("crypto-js"); const TEMPLATE_DIR = path.join(__dirname,'../../templates/subflow'); - // Extract Subflow definition from JSON data function getSubflowDef(flow) { const newFlow = []; @@ -24,10 +23,12 @@ function getSubflowDef(flow) { newFlow.push(item); } }); + if (sf == null) { + throw new Error("No module properties in subflow"); + } return [sf, newFlow]; } - // get flow encoding method function getEncoder(encoding) { if (encoding === "AES") { @@ -58,7 +59,6 @@ function createJSON(dstPath, flow, encoding, key) { fs.writeFileSync(dstPath, data); } - module.exports = async function(data, options) { "use strict";