diff --git a/README.md b/README.md index 3db5fb7..91f06bc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # Node generator for Node-RED Node generator is a command line tool to generate Node-RED nodes based on various sources such as an OpenAPI (Swagger) document, a Node-RED Function node, or a Web Of Things Thing description. @@ -15,16 +14,17 @@ You may need to run this with `sudo`, or from within an Administrator command sh ## Usage Usage: - node-red-nodegen [-o ] [--prefix ] [--name ] [--module ] [--version ] [--keywords ] [--category ] [--icon ] [--color ] [--tgz] [--help] [--wottd] [--lang ] [-v] - + node-red-nodegen [-o ] [--prefix ] [--name ] [--module ] [--version ] [--keywords ] [--category ] [--icon ] [--color ] [--tgz] [--help] [--wottd] [--encoding ] [--encodekey ] [--lang ] [-v] + Description: Node generator for Node-RED - + Supported source: - OpenAPI document - Function node (js file in library, "~/.node-red/lib/function/") + - Subflow node (json file of subflow) - (Beta) Thing Description of W3C Web of Things (jsonld file or URL that points jsonld file) - + Options: -o : Destination path to save generated node (default: current directory) --prefix : Prefix of npm module (default: "node-red-contrib-") @@ -38,16 +38,16 @@ You may need to run this with `sudo`, or from within an Administrator command sh --tgz : Save node as tgz file --help : Show help --wottd : explicitly instruct source file/URL points a Thing Description + --encoding : Encoding scheme of subflow (none or AES) + --encodekey : Encoding key of subflow --lang : Language negotiation information when retrieve a Thing Description -v : Show node generator version ### Example 1. Create an original node from OpenAPI document - node-red-nodegen http://petstore.swagger.io/v2/swagger.json -- cd node-red-contrib-swagger-petstore -- sudo npm link - cd ~/.node-red -- npm link 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. @@ -56,10 +56,8 @@ 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-contrib-lower-case -- sudo npm link - cd ~/.node-red -- npm link 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. @@ -67,10 +65,8 @@ You may need to run this with `sudo`, or from within an Administrator command sh ### Example 3. Create original node from Thing Description - node-red-nodegen example.jsonld -- cd node-red-contrib-example-thing -- sudo npm link - cd ~/.node-red -- npm link 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. @@ -78,19 +74,30 @@ You may need to run this with `sudo`, or from within an Administrator command sh ### Example 4. Create original node from Thing Description via HTTP - node-red-nodegen http://example.com/td.jsonld --wottd --lang "en-US,en;q=0.5" -- cd node-red-contrib-example-thing -- sudo npm link - cd ~/.node-red -- npm link 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. +### Example 5. Create an original node from SUBFLOW definition + +- In Node-RED flow editor, create the SUBFLOW template with module properties filled. Here, we assume module name for the SUBFLOW template is node-red-contrib-qrcode. +- 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 +- node-red + +-> You can use qrcode node on Node-RED flow editor. + ## Documentation + - [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.1.1/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.1.1/docs/index_ja.md#use-cases)) - [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.1.1/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.1.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.1.1/docs/index.md#generated-files-which-node-package-contains) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.1.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.1.1/docs/index.md#how-to-create-a-node-from-openapi-document) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.1.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.1.1/docs/index.md#how-to-create-a-node-from-function-node) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.1.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.1.1/docs/index.md#how-to-create-a-node-from-subflow) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.1.1/docs/index_ja.md#how-to-create-a-node-from-subflow)) Note: Currently node generator supports GET and POST methods using JSON format without authentication. diff --git a/bin/node-red-nodegen.js b/bin/node-red-nodegen.js index 28622b7..2821e0c 100755 --- a/bin/node-red-nodegen.js +++ b/bin/node-red-nodegen.js @@ -27,6 +27,8 @@ var nodegen = require('../lib/nodegen.js'); var options = {}; options.tgz = argv.tgz; options.obfuscate = argv.obfuscate; +options.encoding = argv.encoding; +options.encodekey = argv.encodekey; var data = { prefix: argv.prefix || argv.p, @@ -57,6 +59,8 @@ function help() { ' [--tgz]' + ' [--help]' + ' [--wottd]' + + ' [--encoding ]' + + ' [--encodekey ]' + ' [--lang ]' + ' [-v]\n' + '\n' + @@ -66,7 +70,7 @@ function help() { 'Supported source:'.bold + '\n' + ' - OpenAPI document\n' + ' - Function node (js file in library, "~/.node-red/lib/function/")\n' + - // ' - Subflow node (json file of subflow)\n' + + ' - Subflow node (json file of subflow)\n' + ' - (Beta) Thing Description of W3C Web of Things (jsonld file or URL that points jsonld file)\n' + '\n' + 'Options:\n'.bold + @@ -82,6 +86,8 @@ function help() { ' --tgz : Save node as tgz file\n' + ' --help : Show help\n' + ' --wottd : explicitly instruct source file/URL points a Thing Description\n' + + ' --encoding : Encoding scheme of subflow (none or AES)\n' + + ' --encodekey : Encoding key of subflow\n' + ' --lang : Language negotiation information when retrieve a Thing Description\n' + ' -v : Show node generator version\n'; console.log(helpText); @@ -102,6 +108,13 @@ function skipBom(body) { } } +function isSubflowDefinition(data) { + return data.find((item) => { + return ((item.type === "subflow") && + (item.hasOwnProperty("meta"))); + }); +} + if (argv.help || argv.h) { help(); } else if (argv.v) { @@ -122,7 +135,12 @@ if (argv.help || argv.h) { var content = JSON.parse(fs.readFileSync(sourcePath)); if (Array.isArray(content)) { data.src = content; - promise = nodegen.FunctionNodeGenerator(data, options); + if (isSubflowDefinition(content)) { + promise = nodegen.SubflowNodeGenerator(data, options); + } + else { + promise = nodegen.FunctionNodeGenerator(data, options); + } } else { promise = nodegen.SwaggerNodeGenerator(data, options); } @@ -145,4 +163,4 @@ if (argv.help || argv.h) { } else { help(); } -} \ No newline at end of file +} diff --git a/docs/index.md b/docs/index.md index 1ff898a..31bc870 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,15 +1,20 @@ Node generator ---- + Node generator is a command line tool to generate Node-RED nodes based on various sources such as an OpenAPI document, a Function node or [Web of Things Thing Description](https://www.w3.org/TR/wot-thing-description/). It helps developers dramatically reduce the time to implement Node-RED nodes. + + ## Use cases + Node-RED is useful for rapid development using existing nodes in the [flow library](https://flows.nodered.org). On the other hand, once Node-RED users develop their original nodes to realize custom processing, JavaScript and HTML coding will be a time-consuming task. To solve the problem, the Node generator generates original nodes without coding and packages it. There are four types of use cases for Node generator. #### (1) Connection to cloud services + The http request node can easily connect to cloud services via REST API. From a marketing perspective, having a dedicated node for a cloud service is more attractive to users because having preset node properties, and targeted information and documents can help improve the usability of the node. In general, cloud service providers publish OpenAPI document with REST API for users to test on Swagger UI. @@ -17,20 +22,23 @@ Node generator can use the OpenAPI document to create nodes for connection to a Therefore, cloud service providers can release their original nodes without node development cost. #### (2) Reuse of function node as original node + Node-RED users use the function node to write basic functionality with JavaScript code. Through trial and error of enhancing a flow, often times too many lines of Javascript code gets added into a function node. If the function node has useful functionality, sharing such a node for other Node-RED users will be useful because other users can just use the shared node without worrying about creating the same functionality and are able to concentrate on the development of their flow. In this case, Node generator supports the development of an original node from an existing function node. -#### (3) Reuse of subflow as original node (future functionality) +#### (3) Reuse of subflow as original node + In terms of reusability, the unit of subflow is suitable for sharing with other Node-RED users. -In the future, Node generator will support functionality for creating an original node from a subflow. +Node generator supports functionality for creating an original node from a subflow. Node-RED users can encapsulate their flow as an original node. For example, both template node which has an authentication header and http request node which has URL is a typical pair which a lot of Node-RED users use to connect to cloud services. Node generator can generate an original node from a subflow that contains the flow. And Node-RED users can easily share their original node with other Node-RED users via flow library. #### (4) Connection to devices + IoT application developers wants to concentrate their effort to create value from connected devices, and not to go into detail of implementation. To abstract out these detail, @@ -40,7 +48,10 @@ described by Thing Description, and Node generator will support the Thing Descri Using Node generator, application developers can handle a node in Node-RED as an avatar of a device. + + ## How to use Node generator + To install Node generator to your local PC, you need to input the following "npm install" command on command prompt (Windows) or terminal (macOS/Linux). Because the command execution needs root permission, "sudo" is required before "npm install" command on macOS or Linux environment. @@ -53,11 +64,15 @@ node-red-nodegen creates a node from the file which is specified in the argument The following documentation explains methods of creating nodes from two types of source files. - - [How to create a node from OpenAPI document](#how-to-create-a-node-from-openapi-document) - - [How to create a node from function node](#how-to-create-a-node-from-function-node) - - [How to create a node from WoT Thing Description](#how-to-create-a-node-from-wot-thing-description) +- [How to create a node from OpenAPI document](#how-to-create-a-node-from-openapi-document) +- [How to create a node from function node](#how-to-create-a-node-from-function-node) +- [How to create a node from subflow](#how-to-create-a-node-from-subflow) +- [How to create a node from WoT Thing Description](#how-to-create-a-node-from-wot-thing-description) + + ## Generated files which node package contains + The following is a typical directory structure of the node package generated by Node generator. After generating the node, you can install it on your local PC or publish it on the flow library. @@ -80,7 +95,10 @@ After generating the node, you can install it on your local PC or publish it on |-de-DE |-node.json <- Message catalog for German language + + ## How to create a node from OpenAPI document + You can specify the URL or file path of an OpenAPI document as the first argument of the node-red-nodegen command. (1) Generate node using node-red-nodegen command @@ -89,49 +107,43 @@ You can specify the URL or file path of an OpenAPI document as the first argumen Node-RED users typically import the generated node to the palette of Node-RED flow editor using the following procedures. -(2) Change directory to the generated node's directory - - cd node-red-contrib-swagger-petstore - -(3) Prepare the symbolic link - - sudo npm link - -(4) Change current directory to Node-RED home directory (Typically, Node-RED home directory is ".node-red" under the home directory) +(2) Change current directory to Node-RED home directory (Typically, Node-RED home directory is ".node-red" under the home directory) cd ~/.node-red -(5) Create a symbolic link +(3) Create a symbolic link - npm link node-red-contrib-swagger-petstore + npm install /node-red-contrib-swagger-petstore -(6) Start Node-RED +(4) Start Node-RED node-red -(7) Access the Node-RED flow editor (http://localhost:1880) +(5) Access the Node-RED flow editor (http://localhost:1880) -> You can see the generated node on the palette of the Node-RED flow editor. -(8) Drag and drop the generated node to the workspace +(6) Drag and drop the generated node to the workspace -(9) Select a method on the node property setting +(7) Select a method on the node property setting (When the OpenAPI document does not contain a hostname or has authentication settings, the node property will have a property to set hostname and authentication settings.) -(10) Create flow on the Node-RED flow editor +(8) Create flow on the Node-RED flow editor -> The flow which consists of inject node, the generated node and debug node are suitable as a first step. (If the generated node uses POST method, you need to set JSON data to msg.payload of the inject node) -(11) Run the flow +(9) Run the flow -> In this example, After clicking the button on the inject node, you can check the received data from the debug tab. ### Command line options + If you want to customize the generated node, the following procedures and command line options will be helpful. #### Module name + Node generator uses "node-red-contrib-" as the default prefix of the module name. Therefore, when the node name is "swagger-petstore", the module name becomes "node-red-contrib-swagger-petstore". If you want to change the default module name, you can specify the module name using --module or --prefix option. @@ -140,10 +152,12 @@ If you want to change the default module name, you can specify the module name u node-red-nodegen http://petstore.swagger.io/v2/swagger.json --prefix node-red-node #### Node name + In the case of the node generated from OpenAPI document, "info.title" value in OpenAPI document is used as the generated node's name. Node generator will replace uppercase characters and spaces with hyphens to convert appropriate name for npm module and Node-RED node. ##### Example of OpenAPI document + ``` { "swagger": "2.0", @@ -171,9 +185,11 @@ If "info.title" value contains a double-byte character instead of alphabet and n node-red-nodegen http://petstore.swagger.io/v2/swagger.json --name new-node-name #### Version + By default, Node generator uses "info.version" value as the module version number. ##### Example of OpenAPI document + ``` { "swagger": "2.0", @@ -202,6 +218,7 @@ In this case, the --version option needs to be specified to update the version n node-red-nodegen http://petstore.swagger.io/v2/swagger.json --version 0.0.2 #### Keywords + --keywords is a useful option for keywords of the module in the flow library. On the flow library website, visitors can search the module using keywords. For example, if you want to use "petstore" as a keyword, you can specify the word using --keywords option. @@ -218,6 +235,7 @@ When "--keywords node-red" is specified before publishing the generated node, yo node-red-nodegen http://petstore.swagger.io/v2/swagger.json --keywords petstore,petshop,node-red #### Category + On the palette of Node-RED flow editor, the generated node is in the function category by default. To change the category or use the product name, you can use --category option. For example, the node generated from the following command can be viewed in the "analysis" category on the Node-RED flow editor. @@ -225,17 +243,20 @@ For example, the node generated from the following command can be viewed in the node-red-nodegen http://petstore.swagger.io/v2/swagger.json --category analysis #### Node icon + Node generator command supports --icon option to specify icon file for the generated node. You can use PNG file path or [file name of stock icons](https://nodered.org/docs/creating-nodes/appearance) for the option. The icon should have white on a transparent background. node-red-nodegen http://petstore.swagger.io/v2/swagger.json --icon #### Node color + By default, Node generator uses default node color defined in the node templates. If you need to change it, you can use the --color option of the command line. The option value should be the sequence of the hexadecimal numbers ("RRGGBB" formats) which represents node color. node-red-nodegen http://petstore.swagger.io/v2/swagger.json --color FFFFFF #### Node information in info tab + Node generator automatically generates the node information in the info tab using the following values in OpenAPI document. - info.description : Node description @@ -243,6 +264,7 @@ Node generator automatically generates the node information in the info tab usin - paths.[path].[http method].operationId : Method name ##### Example of OpenAPI document + ``` { "swagger": "2.0", @@ -291,6 +313,7 @@ If you want to modify node information in info tab, you can manually edit the no ``` #### README + To explain the details of the node, you can write documentation in a README.md file. The documentation will be used in the flow library website if you publish your node on the flow library. The Node generator outputs the template file of README.md so you can just modify it. @@ -314,6 +337,7 @@ Run the following command in your Node-RED home directory, typically `~/.node-re ``` #### Test cases + The set of test cases is important in order to maintain the quality of the generated node in production use. Node generator outputs the template file ("test/node_spec.js") of test cases into the generated directory. You need to modify the following three lines, (1),(2) and (3) in the test case file. @@ -356,6 +380,7 @@ You can run the test cases using the "npm test" command under the generated dire npm test #### Message catalogs + By default, Node generator outputs template files in English, Japanese, Chinese and German languages. If you want to support internationalization of node properties, you need to add language messages of parameters into the files. @@ -391,10 +416,12 @@ If your node does not support some languages, you can delete the language direct (For example, delete "zh-CN" directory, if you do not want to support the Chinese language in the node) ### Configuration node to specify the endpoint + While creating flow, you can use configuration nodes to change the endpoint of REST API which the generated node access. To make the configuration node enable, you need to delete `host`, `basePath` and `schemes` properties from the OpenAPI document before generating node. ##### Example of OpenAPI document + ``` { "swagger": "2.0", @@ -420,6 +447,7 @@ This configuration node is suitable for REST API which have different URLs depen It will also be useful for edge computing use cases to switch from a cloud service endpoint to a local endpoint which has the same functionality as the cloud service. ### OpenAPI Specification 3.0 + If you want to use OpenAPI Specification 3.0 to generate a node, you need to convert data format from 3.0 to 2.0 using [api-spec-converter](https://www.npmjs.com/package/api-spec-converter) command. (1) Install api-spec-converter command @@ -434,7 +462,10 @@ If you want to use OpenAPI Specification 3.0 to generate a node, you need to con node-red-nodegen swagger.json + + ## How to create a node from function node + After writing JavaScript code in a function node, you can export the JavaScript code as js file using "Save to Library..." menu in function node. Because Node generator uses function node name as the generated node name, it is better to input node name before exporting the function node. Node-RED saves the js file to the directory, "/.node-red/lib/functions/". @@ -450,44 +481,38 @@ Therefore, you need to specify the directory and file path as the argument of th Node-RED users typically import generated node to the palette of Node-RED flow editor using the following procedures. -(3) Change the current directory to generated node's directory - - cd node-red-contrib-lower-case - -(4) Prepare a symbolic link - - sudo npm link - -(5) Change directory to Node-RED home directory (Typically, Node-RED home directory is ".node-red" under the home directory) +(3) Change directory to Node-RED home directory (Typically, Node-RED home directory is ".node-red" under the home directory) cd ~/.node-red -(6) Create a symbolic link +(4) Create a symbolic link - npm link node-red-contrib-lower-case + npm install /node-red-contrib-lower-case -(7) Start Node-RED +(5) Start Node-RED node-red -(8) Access the Node-RED flow editor (http://localhost:1880) +(6) Access the Node-RED flow editor (http://localhost:1880) -> You can see the generated node on the palette of the Node-RED flow editor. -(9) Drag and drop the generated node to the workspace +(7) Drag and drop the generated node to the workspace -(10) Create flow on the Node-RED flow editor +(8) Create flow on the Node-RED flow editor -> The flow which consists of inject node, the generated node and debug node are suitable as a first step. -(11) Run the flow +(9) Run the flow -> In this example, After clicking the button on the inject node, you can check the received data from the debug tab. ### Command line options + If you want to customize the generated node, the following procedures and command line options will be helpful. #### Module name + Node generator uses "node-red-contrib-" as default prefix of the module name. Therefore, module name is "node-red-contrib-lower-case" when node name is "lower-case". If you want to change the default module name, you can specify module name using --module or --prefix option. @@ -496,12 +521,14 @@ If you want to change the default module name, you can specify module name using node-red-nodegen ~/.node-red/lib/functions/lower-case.js --prefix node-red-node #### Node name + In the case of function node, node name in function node is used as the generated node's name. If you want to change the default name, you can set node name using --name option. node-red-nodegen ~/.node-red/lib/functions/lower-case.js --name new-node-name #### Version + By default, the version number of the module is always "0.0.1". When you update the version number of the module, you need to specify --version option. A conflict error will occur when you publish a module that has the same version number as the previously published module when using "npm publish" command. @@ -510,6 +537,7 @@ In this case, the --version option needs to be specified to update the version n node-red-nodegen ~/.node-red/lib/functions/lower-case.js --version 0.0.2 #### Keywords + --keywords is a useful option for keywords of the module on flow library. On the flow library website, visitors will search the module using the keywords. For example, if you want to use "lower-case" as a keyword, you can specify the word using --keywords option. @@ -526,6 +554,7 @@ When "--keywords node-red" is specified before publishing the generated node, yo node-red-nodegen ~/.node-red/lib/functions/lower-case.js --keywords lower-case,function,node-red #### Category + On the palette of Node-RED flow editor, the generated node is in the function category as the default. To change the category or use product name, you can use --category option. For example, the node generated from the following command can be viewed in the "analysis" category on the Node-RED flow editor. @@ -533,17 +562,20 @@ For example, the node generated from the following command can be viewed in the node-red-nodegen ~/.node-red/lib/functions/lower-case.js --category analysis #### Node icon + Node generator command supports --icon option to specify icon file for the generated node. You can use PNG file path or [file name of stock icons](https://nodered.org/docs/creating-nodes/appearance) for the option. The icon should have white on a transparent background. node-red-nodegen ~/.node-red/lib/functions/lower-case.js --icon #### Node color + By default, Node generator uses default node color defined in the node templates. If you need to change it, you can use the --color option of the command line. The option value should be hexadecimal numbers ("RRGGBB" formats) which represent node color. node-red-nodegen ~/.node-red/lib/functions/lower-case.js --color FFFFFF #### Node information in info tab + Node generator outputs the template of node information into the node.html file. You need to modify the template along with your node. (Node developer will be able to use node description property to use node information in the future version of Node-RED and Node generator) @@ -579,6 +611,7 @@ Outputs section has properties explanation of outputted messages. Details section will contain additional information about the generated node. #### README + To explain the details of the node, you can write documentation in a README.md file. The documentation will be used in the flow library website if you publish your node on npmjs. The Node generator outputs the template file of README.md so you can just modify it. @@ -600,6 +633,7 @@ Run the following command in your Node-RED home directory, typically `~/.node-re ``` #### Test cases + The set of test cases is important in order to maintain the quality of the generated node in production use. Node generator outputs the template file of test cases into the file, "test/node_spec.js" under the generated directory. You need to modify the following two lines, (1) and (2) in the test case file. @@ -633,6 +667,7 @@ You can run the test cases using "npm test" command under the generated director npm test ### Using external modules + When loading external module to a function node, Node-RED users generally add the module into `functionGlobalContext` section in settings.js file. Current Node generator does not support exporting this setting to the generated node. Therefore, you need to modify node.js file and package.json file before you share the generated node with other Node-RED environments. @@ -664,9 +699,9 @@ The following example is the procedure to generate a node from the function node (4) Write JavaScript code which uses the external module to the function node -| Item | Value in function node property | -|---|---| -| Name | Format date | +| Item | Value in function node property | +| -------- | -------------------------------------------------------------------------------------------------------------- | +| Name | Format date | | Function | var moment = global.get('moment');
msg.payload = moment().format('MMMM Do YYYY, h:mm:ss a');
return msg; | (5) Save the function node as a js file from "Save to Library..." menu on function node property UI @@ -702,14 +737,13 @@ The following example is the procedure to generate a node from the function node "node-red": "0.18.7", ``` -(9) Prepare symbolic link +(9) Change directory to Node-RED home directory (Typically, Node-RED home directory is ".node-red" under the home directory) - sudo npm link + cd ~/.node-red (10) Create symbolic link - cd ~/.node-red/ - npm link node-red-contrib-format-date + npm install /node-red-contrib-format-date (11) Restart Node-RED @@ -717,7 +751,60 @@ The following example is the procedure to generate a node from the function node -> You can use format-date node on your Node-RED flow editor. + + +## How to create a node from subflow + +After creating a subflow, add module properties of the subflow template. Then, download JSON format subflow definition using "Export" menu of Node-RED editor. + +(1) Export JSON format of subflow + + We assume node-red-contrib-qrcode as the module name and SUBFLOW JSON data is downloaded to a file named qrcode.json. + +(2) Generate node using node-red-nodegen command + + node-red-nodegen qrcode.json + +Node-RED users typically import generated node to the palette of Node-RED flow editor using the following procedures. + +(3) Change directory to Node-RED home directory (Typically, Node-RED home directory is ".node-red" under the home directory) + + cd ~/.node-red + +(4) Create a symbolic link + + npm install /node-red-contrib-qrcode + +(5) Start Node-RED + + node-red + +(6) Access the Node-RED flow editor (http://localhost:1880) + +-> You can see the generated node on the palette of the Node-RED flow editor. + +(7) Drag and drop the generated node to the workspace + +(8) Create flow on the Node-RED flow editor + +-> The flow which consists of inject node, the generated node and debug node are suitable as a first step. + +(9) Run the flow + +-> In this example, After clicking the button on the inject node, you can check the received data from the debug tab. + +### Command line options + +If you want to customize the generated node, the following procedures and command line options will be helpful. + +#### Encryption(Experimental) + +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 + You can specify the URL or file path of a Thing Description (TD) as the first argument of the node-red-nodegen command. If you use the URL for retrieve a TD, or the file whose extension isn't ".jsonld", you should specify the `--wottd` option. And, if you get a TD using URL, you can also specify `--lang` option to get a TD of the specific language, if exist. (1) Generate node using node-red-nodegen command @@ -767,9 +854,11 @@ Node-RED users typically import the generated node to the palette of Node-RED fl (9) Run the flow ### Command line options + If you want to customize the generated node, the following procedures and command line options will be helpful. #### Module name + Node generator uses "node-red-contrib-" as the default prefix for the module, and default node name is created from "name" property in TD. If you want to change the default module name, you can specify the module name using `--module` or `--prefix` option. @@ -777,6 +866,7 @@ If you want to change the default module name, you can specify the module name u node-red-nodegen td.jsonld --prefix node-red-wot #### Node name + In the case of the node generated from Thing Description, "name" property in TD is used as the generated node's name. Node generator will replace uppercase characters and spaces with hyphens to convert appropriate name for npm module and Node-RED node. @@ -786,6 +876,7 @@ If "name" property contains a double-byte character instead of alphabet and numb node-red-nodegen td.jsonld --name new-node-name #### Version + By default, Node generator uses "version" property as the module version number. When you update the version number of the module without incrementing the version number in Thing Description, you need to specify `--version` option. @@ -795,6 +886,7 @@ In this case, the `--version` option needs to be specified to update the version node-red-nodegen td.jsonld --version 0.0.2 #### Keywords + `--keywords` is a useful options for keywords of the module in the flow library. On the flow library website, visitors can search the module using keywords. For example, if you want to use "lamp" as a keyword, you can specify the word using `--keyword` option. @@ -811,6 +903,7 @@ If "--keywords node-red" is specified when you publish the generated node, your node-red-nodegen td.jsonld --keyword lamp,led,node-red #### Category + On the palette of Node-RED flow editor, the generated node is in "Web of Things" category by default. To change the category, you can use `--category` option. For example, the node generated from the following command can be viewed in the "analysis" category on the Node-RED flow editor. @@ -818,18 +911,20 @@ For example, the node generated from the following command can be viewed in the node-red-nodegen td.jsonld --category analysis #### Node icon + Node generator command supports `--icon` option to specify icon file for the generated node. You can use PNG file path or [file name of stock icons](https://nodered.org/docs/creating-nodes/appearance) for the option. The icon should have white on a transparent background. node-red-nodegen td.jsonld --icon - #### Node color + By default, Node generator uses default node color defined in the node templates. If you need to change it, you can use the `--color` option of the command line. The option value should be the sequence of the hexadecimal numbers ("RRGGBB" formats) which represents node color. node-red-nodegen td.jsonld --color FFFFFF #### Node information in info tab + Node generator automatically generates the node information in the info tab using the following properties in Thing Description - description: Node description: @@ -840,10 +935,12 @@ Node generator automatically generates the node information in the info tab usin If you want to modify node information in info tab, you can manually edit the generated HTML file. #### README + To explain the details of the node, you can write documentation in a README.md file. The documentation will be used in the flow library website if you publish your node on the flow library. The Node generator outputs the template file of README.md so you can just modify it. ## Known Issues + - In the Node generator command, you cannot use --tgz option and --icon option simultaneously because it has an asynchronous problem. - The value of `info.title` in the OpenAPI document has to start with an alphabet (not a number) because it will be used in the variable name in the generated code. diff --git a/docs/index_ja.md b/docs/index_ja.md index c321bf1..a1a270f 100644 --- a/docs/index_ja.md +++ b/docs/index_ja.md @@ -1,16 +1,20 @@ ノードジェネレータ ---- -ノードジェネレータは、OpenAPIドキュメント、[Web of Things Things Description](https://www.w3.org/TR/wot-thing-description/)やfunctionノードなどのソースコードからNode-REDのノードを生成するためのコマンドラインツールです。 + +ノードジェネレータは、OpenAPIドキュメント、[Web of Things Things Description](https://www.w3.org/TR/wot-thing-description/)、サブフロー、functionノードなどのソースコードからNode-REDのノードを生成するためのコマンドラインツールです。 このツールを使用すると、ノード開発者はNode-REDノードの実装時間を大幅に短縮できます。 + ## 利用ケース + Node-REDには、[フローライブラリ](https://flows.nodered.org)に存在するノードを使用し、コーディングすることなく迅速な開発ができるという利点があります。 しかし、独自の処理を実現するために、Node-REDユーザがノードを開発すると、JavaScriptとHTMLのコーディングに時間がかかり、迅速な開発ができるNode-REDの利点が薄れてしまいます。 この問題を解決するために、ノードジェネレータはコーディングなしで独自のノードを自動生成し、パッケージ化します。 以下の通り、ノードジェネレータには4つの利用ケースがあります。 #### (1) クラウドサービスへの接続 + http requestノードは、REST API経由でクラウドサービスに簡単に接続できます。 しかし、マーケティングの観点から考えると、http requestノードの代替として、クラウドサービス向けに専用に独自に開発したノードを公開することは、クラウドサービスのユーザ数を増やすために効果的です。 なぜなら、クラウドサービスとの接続方法を詳細に解説したノードプロパティ、ノード情報、ドキュメントがユーザビリティに貢献するためです。 @@ -19,21 +23,24 @@ http requestノードは、REST API経由でクラウドサービスに簡単に したがって、クラウドサービスプロバイダは、ノード開発コストなしで独自のノードを公開できる様になります。 #### (2) functionノードを独自のノードとして再利用 + Node-REDユーザは、functionノードにJavaScriptコードを記述し、簡単な処理を動作させています。 しかし、試行錯誤を繰り返す開発を行い、フローを徐々に改善すると、大量のJavaScriptコードをfunctionノードに追加してしまうことがあります。 この時、functionノードに便利な機能が備わっている場合、他のNode-REDユーザにこのfunctionノードを共有し、再利用をしてほしいというニーズがあがってきます。 なぜなら、ノードを再利用することで、他のNode-REDユーザは同じ機能を開発する必要なく、フローの開発に集中できるためです。 ノードジェネレータは、この様なニーズを満たすために、既存のfunctionノードから独自のノードの開発する作業を助けます。 -#### (3) サブフローを独自のノードとして再利用(将来機能) +#### (3) サブフローを独自のノードとして再利用 + 他のNode-REDユーザとフローを共有するには、サブフローの単位で行うのが最適です。 -将来、ノードジェネレータは、サブフローから独自のノードを生成する機能をサポートします。 +ノードジェネレータは、サブフローから独自のノードを生成する機能をサポートします。 Node-REDユーザは、サブフローを独自のノードとしてカプセル化できる様になります。 例えば、認証ヘッダを持つtemplateノードとURLを持つhttp requestノードは、多くのNode-REDユーザがクラウドサービスに接続するために使用する典型的なペアです。 ノードジェネレータは、この様なフローを含むサブフローから独自のノードを生成できます。 そして、Node-REDユーザは、フローライブラリを介して、生成したノードを他のNode-REDユーザと容易に共有できます。 #### (4) デバイスへの接続 + IoTアプリケーションの開発者は、接続したデバイスをつかった価値の創出に 注力したいのであり、その実装の詳細に手間をかけたくありません。 実装の詳細を抽象化するために、W3C Web of Things (WoT)はIoTデバイスがもつ @@ -44,7 +51,9 @@ WoTではデバイスのインタフェースはThing Descriptionとして記述 Node-RED上のノードをデバイスのアバターとして使うことができます。 + ## ノードジェネレータの使い方 + ノードジェネレータをローカル環境にインストールするには、コマンドプロンプト(Windows)又はターミナル(macOS/Linux)で次の「npm install」コマンドを入力します。 コマンド実行にはroot権限が必要なため、macOS又はLinux環境では "npm install"コマンドの前に "sudo"が必要です。 @@ -57,12 +66,15 @@ Node-RED上のノードをデバイスのアバターとして使うことがで 以降のドキュメントでは、2種類のソースファイルからノードを生成する方法の詳細について説明します。 - - [OpenAPIドキュメントからノードを生成する方法](#how-to-create-a-node-from-openapi-document) - - [functionノードからノードを生成する方法](#how-to-create-a-node-from-function-node) - - [Thing Descriptionからノードを生成する方法](#how-to-create-a-node-from-wot-thing-description) +- [OpenAPIドキュメントからノードを生成する方法](#how-to-create-a-node-from-openapi-document) +- [functionノードからノードを生成する方法](#how-to-create-a-node-from-function-node) +- [サブフローからノードを生成する方法](#how-to-create-a-node-from-subflow) +- [Thing Descriptionからノードを生成する方法](#how-to-create-a-node-from-wot-thing-description) + ## ノードパッケージ内のファイル + ノードジェネレータによって自動生成したノードパッケージの典型的なディレクトリ構造は以下の通りです。 Node-REDユーザは、自動生成したノードをローカルNode-RED環境にインストールしたり、追加開発なくフローライブラリに公開したりできます。 @@ -86,7 +98,9 @@ Node-REDユーザは、自動生成したノードをローカルNode-RED環境 |-node.json <- ドイツ語のメッセージカタログ + ## OpenAPIドキュメントからノードを生成する方法 + node-red-nodegenコマンドの最初の引数として、OpenAPIドキュメントのURL又はファイルパスを指定できます。 (1) node-red-nodegenコマンドを使用してノードを生成 @@ -95,48 +109,42 @@ node-red-nodegenコマンドの最初の引数として、OpenAPIドキュメン Node-REDユーザは通常、以下の手順で生成したノードをNode-REDフローエディタのパレットにインポートします。 -(2) 生成したノードのディレクトリにディレクトリを変更 - - cd node-red-contrib-swagger-petstore - -(3) シンボリックリンクを準備 - - sudo npm link - -(4) カレントディレクトリをNode-REDのホームディレクトリに変更(通常、Node-REDのホームディレクトリは、ホームディレクトリの下の".node-red"です) +(2) カレントディレクトリをNode-REDのホームディレクトリに変更(通常、Node-REDのホームディレクトリは、ホームディレクトリの下の".node-red"です) cd ~/.node-red -(5) シンボリックリンクを作成 +(3) シンボリックリンクを作成 - npm link node-red-contrib-swagger-petstore + npm install /node-red-contrib-swagger-petstore -(6) Node-REDを起動 +(4) Node-REDを起動 node-red -(7) Node-REDフローエディタにアクセス (http://localhost:1880) +(5) Node-REDフローエディタにアクセス (http://localhost:1880) -> 生成されたノードがNode-REDフローエディタのパレットに表示されます。 -(8) 生成されたノードをワークスペースにドラッグアンドドロップ +(6) 生成されたノードをワークスペースにドラッグアンドドロップ -(9) ノードプロパティ設定でメソッドを選択 +(7) ノードプロパティ設定でメソッドを選択 (OpenAPIドキュメントにホスト名が含まれていないか、認証設定がある場合、ノードプロパティ設定にてホスト名と認証設定を設定します) -(10) Node-REDフローエディタでフローを作成 +(8) Node-REDフローエディタでフローを作成 -> injectノード、生成されたノード、debugノードからなるフローが、最初のステップに適しています。(生成されたノードがPOSTメソッドを使用する場合は、injectノードのmsg.payloadにJSONデータを設定する必要があります) -(11) フローを実行 +(9) フローを実行 -> この例では、injectノードのボタンをクリックすると、受信したデータをデバッグタブに表示します。 ### コマンドラインオプション + 生成したノードをカスタマイズする場合は、次の手順やコマンドラインオプションが役立ちます。 #### モジュール名 + ノードジェネレータは、モジュール名のデフォルトのプレフィックスとして "node-red-contrib-"を使用します。 したがって、ノード名が "swagger-petstore"の場合、モジュール名は "node-red-contrib-swagger-petstore"となります。 デフォルトのモジュール名を変更したい場合は、--module又は--prefixオプションを使用してモジュール名を指定できます。 @@ -145,10 +153,12 @@ Node-REDユーザは通常、以下の手順で生成したノードをNode-RED node-red-nodegen http://petstore.swagger.io/v2/swagger.json --prefix node-red-node #### ノード名 + OpenAPIドキュメントから生成したノードの場合、OpenAPIドキュメントの "info.title"値が生成ノードの名前として使用します。 ノードジェネレータは、npmモジュールとNode-REDノードで利用できる適切な名前を変換するために、大文字とスペースをハイフンに置き換えます。 ##### OpenAPIドキュメントの例 + ``` { "swagger": "2.0", @@ -176,9 +186,11 @@ OpenAPIドキュメントから生成したノードの場合、OpenAPIドキュ node-red-nodegen http://petstore.swagger.io/v2/swagger.json --name new-node-name #### バージョン + デフォルトでは、ノードジェネレータはモジュールのバージョン番号として "info.version"値を使用します。 ##### OpenAPIドキュメントの例 + ``` { "swagger": "2.0", @@ -207,6 +219,7 @@ OpenAPIドキュメントのバージョン番号をインクリメントせず node-red-nodegen http://petstore.swagger.io/v2/swagger.json --version 0.0.2 #### キーワード + --keywordsは、モジュールのキーワードのために用いる便利なオプションです。 フローライブラリのWebサイトで、訪問者はこのキーワードを使用してモジュールを検索します。 例えば、 "petstore"をキーワードとして使用する場合は、--keywordsオプションを使用して単語を指定できます。 @@ -223,6 +236,7 @@ OpenAPIドキュメントのバージョン番号をインクリメントせず node-red-nodegen http://petstore.swagger.io/v2/swagger.json --keywords petstore,petshop,node-red #### カテゴリ + Node-REDフローエディタのパレットでは、生成したノードはデフォルトとして機能カテゴリに入ります。 カテゴリを変更したり、カテゴリ名に製品名を使用したりしたい場合は、--categoryオプションを用います。 例えば、次のコマンドが出力するノードは、Node-REDフローエディタの「分析」カテゴリに入ります。 @@ -230,17 +244,20 @@ Node-REDフローエディタのパレットでは、生成したノードはデ node-red-nodegen http://petstore.swagger.io/v2/swagger.json --category analysis #### ノードアイコン + ノードジェネレータのコマンドは、生成されるノードのアイコンファイルを指定するための--iconオプションをサポートしています。 オプションにはPNGファイルパス、または[ストックアイコンのファイル名](https://nodered.org/docs/creating-nodes/appearance)を使用できます。アイコンは透明な背景上に白色で表示したPNGファイルである必要があります。 node-red-nodegen http://petstore.swagger.io/v2/swagger.json --icon #### ノードの色 + ノードジェネレータはデフォルトでノードテンプレートで定義されたノードの色を使用します。変更する必要がある場合は、コマンドラインの--colorオプションを使用できます。オプションには、ノードの色を表す16進数("RRGGBB"形式)の文字列を指定できます。 node-red-nodegen http://petstore.swagger.io/v2/swagger.json --color FFFFFF #### 情報タブ内のノードの情報 + ノードジェネレータは、OpenAPIドキュメントの次の値を使用して、情報タブにノードの情報を自動的に生成します。 - info.description : ノードの説明 @@ -248,6 +265,7 @@ Node-REDフローエディタのパレットでは、生成したノードはデ - paths.[path].[http method].operationId : メソッド名 ##### OpenAPIドキュメントの例 + ``` { "swagger": "2.0", @@ -297,6 +315,7 @@ Node-REDフローエディタのパレットでは、生成したノードはデ ``` #### README + ノードの詳細を説明は、README.mdというファイルに書きます。 フローライブラリにノードを公開すると、フローライブラリのWebサイトは、ノードのページで本ファイルを表示します。 ノードジェネレータはREADME.mdのテンプレートを出力するので、ファイルを変更するだけです。 @@ -320,6 +339,7 @@ Run the following command in your Node-RED user directory - typically `~/.node-r ``` #### テストケース + テストケースは、本番環境で利用するノードの品質を維持するために最も重要です。 ノードジェネレータは、生成したディレクトリの下にあるファイル "test/node_spec.js"にテストケースのテンプレートファイルを出力します。 テストケースファイルでは、以下の(1)、(2)、(3)の3行を変更します。 @@ -363,6 +383,7 @@ Run the following command in your Node-RED user directory - typically `~/.node-r npm test #### メッセージカタログ + デフォルトでは、ノードジェネレータは英語、日本語、中国語、ドイツ語のテンプレートファイルを出力します。 ノードプロパティの多言語対応をしたい場合は、パラメータの言語メッセージをこれらのファイルに追加します。 @@ -398,10 +419,12 @@ Run the following command in your Node-RED user directory - typically `~/.node-r (例えば、中国語をサポートしたくない場合は、「zh-CN」ディレクトリごと削除してください) ### エンドポイントを指定するための設定ノード + 設定ノードを使用することで、生成されたノードがアクセスするREST APIのエンドポイントをフローの作成中に変更できるようになります。 設定ノードを有効にするには、ノードを生成する前にOpenAPIドキュメントから`host`、`basePath`、`schemes`の各プロパティを削除する必要があります。 ##### OpenAPIドキュメントの例 + ``` { "swagger": "2.0", @@ -427,6 +450,7 @@ Run the following command in your Node-RED user directory - typically `~/.node-r その他、クラウドサービスのエンドポイントから、クラウドサービスと同じ機能を持つローカルのエンドポイントへ切り替えることができるため、エッジコンピューティングのユースケースでも役立ちます。 ### OpenAPI Specification 3.0 + OpenAPI Specification 3.0を使用してノードを生成する場合は、[api-spec-converter](https://www.npmjs.com/package/api-spec-converter)コマンドを使用してデータ形式を3.0から2.0に変換する必要があります。 (1) api-spec-converterコマンドをインストール @@ -442,7 +466,9 @@ OpenAPI Specification 3.0を使用してノードを生成する場合は、[api node-red-nodegen swagger.json + ## functionノードからノードを生成する方法 + functionノードにJavaScriptコードを記述した後、functionノードの "ライブラリへ保存..."メニューを使用して、JavaScriptコードをjsファイルとして書き出します。 ノードジェネレータはfunctionノード名を生成ノードの名前として使用するため、functionノードをエクスポートする前にノード名を入力する方がよいでしょう。 Node-REDは、jsファイルを"/.node-red/lib/functions/"ディレクトリに保存します。 @@ -458,44 +484,38 @@ Node-REDは、jsファイルを"/.node-red/lib/functions/"ディ Node-REDユーザは通常、以下の手順で生成したノードをNode-REDフローエディタのパレットにインポートします。 -(3) 生成したノードのディレクトリにディレクトリを変更 - - cd node-red-contrib-lower-case - -(4) シンボリックリンクを準備 - - sudo npm link - -(5) カレントディレクトリをNode-REDのホームディレクトリに変更します(通常、Node-REDのホームディレクトリは、ホームディレクトリの下の".node-red"です) +(3) カレントディレクトリをNode-REDのホームディレクトリに変更します(通常、Node-REDのホームディレクトリは、ホームディレクトリの下の".node-red"です) cd ~/.node-red -(6) シンボリックリンクを作成 +(4) シンボリックリンクを作成 - npm link node-red-contrib-lower-case + npm install /node-red-contrib-lower-case -(7) Node-REDを起動 +(5) Node-REDを起動 node-red -(8) Node-REDフローエディタにアクセス (http://localhost:1880) +(6) Node-REDフローエディタにアクセス (http://localhost:1880) -> 生成されたノードがNode-REDフローエディタのパレットに表示されます。 -(9) 生成されたノードをワークスペースにドラッグアンドドロップ +(7) 生成されたノードをワークスペースにドラッグアンドドロップ -(10) Node-REDフローエディタでフローを作成 +(8) Node-REDフローエディタでフローを作成 -> injectノード、生成されたノードおよびdebugノードからなるフローが、最初のステップに適しています。 -(11) フローを実行 +(9) フローを実行 -> この例では、injectノードのボタンをクリックすると、受信したデータをデバッグタブに表示します。 ### コマンドラインオプション + 生成したノードをカスタマイズする場合は、次の手順やコマンドラインオプションが役立ちます。 #### モジュール名 + ノードジェネレータは、モジュール名のデフォルトのプレフィックスとして "node-red-contrib-"を使用します。 したがって、ノード名が "lower-case"の場合、モジュール名は "node-red-contrib-lower-case"になります。 デフォルトのモジュール名を変更したい場合は、--module又は--prefixオプションを使用してモジュール名を指定できます。 @@ -504,12 +524,14 @@ Node-REDユーザは通常、以下の手順で生成したノードをNode-RED node-red-nodegen ~/.node-red/lib/functions/lower-case.js --prefix node-red-node #### ノード名 + functionノードの場合、functionノード内のノード名を、生成されるノードのノード名として使用します。 もしデフォルトのノード名を変更したい場合は、--nameオプションを使用してノード名を設定します。 node-red-nodegen ~/.node-red/lib/functions/lower-case.js --name new-node-name #### バージョン + デフォルトでは、モジュールのバージョン番号は常に"0.0.1"です。 モジュールのバージョン番号を更新するときは、--versionオプションを指定します。 特に、"npm publish"コマンドを使用して、以前公開したモジュールと同じバージョン番号を持つモジュールを公開すると、競合エラーが発生します。 @@ -518,6 +540,7 @@ functionノードの場合、functionノード内のノード名を、生成さ node-red-nodegen ~/.node-red/lib/functions/lower-case.js --version 0.0.2 #### キーワード + --keywordsは、フローライブラリ上のモジュールのキーワードを指定できる便利なオプションです。 フローライブラリのWebサイトでは、訪問者はこのキーワードを使用してモジュールを検索します。 例えば、キーワードとして"lower-case"を使用する場合は、--keywordsオプションを使用してこの単語を指定できます。 @@ -534,6 +557,7 @@ functionノードの場合、functionノード内のノード名を、生成さ node-red-nodegen ~/.node-red/lib/functions/lower-case.js --keywords lower-case,function,node-red #### カテゴリ + Node-REDフローエディタのパレットでは、生成したノードはデフォルトとして機能カテゴリに入ります。 カテゴリを変更したり、カテゴリ名に製品名を使用したりしたい場合は、--categoryオプションを用います。 例えば、次のコマンドが出力するノードは、Node-REDフローエディタの「分析」カテゴリに入ります。 @@ -541,17 +565,20 @@ Node-REDフローエディタのパレットでは、生成したノードはデ node-red-nodegen ~/.node-red/lib/functions/lower-case.js --category analysis #### ノードアイコン + ノードジェネレータのコマンドは、生成されるノードのアイコンファイルを指定するための--iconオプションをサポートしています。 オプションにはPNGファイルパス、または[ストックアイコンのファイル名](https://nodered.org/docs/creating-nodes/appearance)を使用できます。アイコンは透明な背景上に白色で表示したPNGファイルである必要があります。 node-red-nodegen ~/.node-red/lib/functions/lower-case.js --icon #### ノードの色 + ノードジェネレータはデフォルトでノードテンプレートで定義されたノードの色を使用します。変更する必要がある場合は、コマンドラインの--colorオプションを使用できます。オプションには、ノードの色を表す16進数("RRGGBB"形式)の文字列を指定できます。 node-red-nodegen ~/.node-red/lib/functions/lower-case.js --color FFFFFF #### 情報タブ内のノードの情報 + ノードジェネレータはノード情報のテンプレートをnode.htmlファイルに出力します。 ノードとともにテンプレートを変更します。 (将来のバージョンのNode-REDとノードジェネレータでは、ノード開発者はノード記述プロパティを使用して、ノード情報を指定できます) @@ -587,6 +614,7 @@ Outputセクションには、出力したメッセージの情報を記載し Detailsセクションには、生成したノードの追加情報を記載します。 #### README + ノードの詳細を説明は、README.mdというファイルに書きます。 フローライブラリにノードを公開すると、フローライブラリのWebサイトは、ノードのページで本ファイルを表示します。 ノードジェネレータはREADME.mdのテンプレートを出力するので、ファイルを変更するだけです。 @@ -608,6 +636,7 @@ Run the following command in your Node-RED user directory - typically `~/.node-r ``` #### テストケース + テストケースは、本番環境で使用するノードの品質を維持するために最も重要です。 ノードジェネレータは、生成したディレクトリの下にあるファイル"test/node_spec.js"にテストケースのテンプレートファイルを出力します。 テストケースファイルでは、(1)と(2)の2行を修正します。 @@ -641,6 +670,7 @@ Run the following command in your Node-RED user directory - typically `~/.node-r npm test ### 外部モジュールの利用 + functionノードに外部モジュールをロードする場合、Node-REDユーザーは通常、モジュールをsettings.jsファイルの `functionGlobalContext`セクションに追加します。 現在のノードジェネレータは、この設定を生成されたノードにエクスポートする機能をサポートしていません。 したがって、生成されたノードを他のノード-RED環境と共有する前に、node.jsファイルとpackage.jsonファイルを変更する必要があります。 @@ -672,9 +702,9 @@ functionノードに外部モジュールをロードする場合、Node-REDユ (4) 外部モジュールを使用するJavaScriptコードをfunctionノードに記述 -| 項目 | functionノードのプロパティ値 | -|---|---| -| 名前 | Format date | +| 項目 | functionノードのプロパティ値 | +| --- | -------------------------------------------------------------------------------------------------------------- | +| 名前 | Format date | | コード | var moment = global.get('moment');
msg.payload = moment().format('MMMM Do YYYY, h:mm:ss a');
return msg; | (5) functionノードプロパティUIの"ライブラリへ保存"メニューからjsファイルとしてfunctionノードを保存 @@ -710,14 +740,13 @@ functionノードに外部モジュールをロードする場合、Node-REDユ "node-red": "0.18.7", ``` -(9) シンボリックリンクを準備 +(9) カレントディレクトリをNode-REDのホームディレクトリに変更します(通常、Node-REDのホームディレクトリは、ホームディレクトリの下の".node-red"です) - sudo npm link + cd ~/.node-red (10) シンボリックリンクを作成 - cd ~/.node-red/ - npm link node-red-contrib-format-date + npm install /node-red-contrib-format-date (11) Node-REDを再起動 @@ -725,8 +754,60 @@ functionノードに外部モジュールをロードする場合、Node-REDユ -> Node-REDフローエディタ上でformat-dateノードを使用できます。 + + +## サブフローからノードを生成する方法 + +サブフローを作成し、サブフローテンプレートのモジュールプロパティを記入します。サブフローテンプレートを表示した状態で、Node-REDエディタの"書き出し"メニューを使用して、JSON形式のサブフローデータをダウンロードします。 + +(1) サブフローをJSONファイルとしてエクスポート + + ここでは、モジュールプロパティでモジュール名としてnode-red-contrib-qrcodeを指定し、サブフローのJSONデータをqrcode.jsonにダウンロードした場合を想定します。 + +(2) node-red-nodegenコマンドを使用してノードを生成 + + node-red-nodegen qrcode.json + +Node-REDユーザは通常、以下の手順で生成したノードをNode-REDフローエディタのパレットにインポートします。 + +(3) カレントディレクトリをNode-REDのホームディレクトリに変更します(通常、Node-REDのホームディレクトリは、ホームディレクトリの下の".node-red"です) + + cd ~/.node-red + +(4) シンボリックリンクを作成 + + npm install /node-red-contrib-qrcode + +(5) Node-REDを起動 + + node-red + +(6) Node-REDフローエディタにアクセス (http://localhost:1880) + +-> 生成されたノードがNode-REDフローエディタのパレットに表示されます。 + +(7) 生成されたノードをワークスペースにドラッグアンドドロップ + +(8) Node-REDフローエディタでフローを作成 + +-> injectノード、生成されたノードおよびdebugノードからなるフローが、最初のステップに適しています。 + +(11) フローを実行 + +-> この例では、injectノードのボタンをクリックすると、受信したデータをデバッグタブに表示します。 + +### コマンドラインオプション + +生成したノードをカスタマイズする場合は、次の手順やコマンドラインオプションが役立ちます。 + +#### 暗号化(実験機能) + +ノードジェネレータは生成したノードのサブフロー定義を暗号化することができます。暗号化を行うには、--encodingオプションにAESを、-encodekeyに暗号化キーを指定してください。暗号化指定したノードを利用する場合は、環境変数NR_FLOW_DECODE_KEYに暗号化キーを指定します。 + + ## Thing Descriptionからノードを生成する方法 + node-red-nodegenコマンドの最初の引数として、Thing Description(TD)のURL又はファイルパスを指定できます。URL、または拡張子が".jsonld"でないファイルを指定する場合は、`--wottd`オプションをつける必要があります。また、もしURL指定でTDを取得する場合には、`--lang`オプションをつけることで指定した言語のTDを取得することもできます。 (1) node-red-nodegenコマンドを使用してノードを生成 @@ -774,9 +855,11 @@ Node-REDユーザは通常、以下の手順で生成したノードをNode-RED - イベントが発生すると、ノードはイベント内容をmsg.payloadに入れたメッセージを出力します。 ### コマンドラインオプション + 生成したノードをカスタマイズする場合は、次の手順やコマンドラインオプションが役立ちます。 #### モジュール名 + ノードジェネレータは、モジュール名のデフォルトのプレフィックスとして "node-red-contrib-"を使用します。またノード名はTDの"name"プロパティから取られます。 デフォルトのモジュール名を変更したい場合は、`--module`又は`--prefix`オプションを使用してモジュール名を指定できます。 @@ -784,6 +867,7 @@ Node-REDユーザは通常、以下の手順で生成したノードをNode-RED node-red-nodegen td.jsonld --prefix node-red-wot #### ノード名 + Thing Descriptionから生成したノードの場合、Thing Descriptionの"name"プロパティを生成ノードの名前として使用します。 ノードジェネレータは、npmモジュールとNode-REDノードで利用できる適切な名前を変換するために、大文字とスペースをハイフンに置き換えます。 @@ -793,6 +877,7 @@ Thing Descriptionから生成したノードの場合、Thing Descriptionの"nam node-red-nodegen td.jsonld --name new-node-name #### バージョン + デフォルトでは、ノードジェネレータはモジュールのバージョン番号として "version"プロパティを使用します。 Thing Descriptionのバージョン番号をインクリメントせずにモジュールのバージョン番号を更新する場合は、`--version`オプションを指定します。 @@ -802,6 +887,7 @@ Thing Descriptionのバージョン番号をインクリメントせずにモジ node-red-nodegen td.jsonld --version 0.0.2 #### キーワード + `--keywords`は、モジュールのキーワードのために用いる便利なオプションです。 フローライブラリのWebサイトで、訪問者はこのキーワードを使用してモジュールを検索します。 例えば、 "lamp"をキーワードとして使用する場合は、`--keywords`オプションを使用して単語を指定できます。 @@ -818,6 +904,7 @@ Thing Descriptionのバージョン番号をインクリメントせずにモジ node-red-nodegen td.jsonld --keyword lamp,led,node-red #### カテゴリ + Node-REDフローエディタのパレットでは、生成したノードはデフォルトとして「Web of Things」カテゴリに入ります。 カテゴリを変更する場合は、`--category`オプションを用います。 例えば、次のコマンドが出力するノードは、Node-REDフローエディタの「分析」カテゴリに入ります。 @@ -825,17 +912,20 @@ Node-REDフローエディタのパレットでは、生成したノードはデ node-red-nodegen td.jsonld --category analysis #### ノードアイコン + ノードジェネレータのコマンドは、生成されるノードのアイコンファイルを指定するための`--icon`オプションをサポートしています。 オプションにはPNGファイルパス、または[ストックアイコンのファイル名](https://nodered.org/docs/creating-nodes/appearance)を使用できます。アイコンは透明な背景上に白色で表示したPNGファイルである必要があります。 node-red-nodegen td.jsonld --icon #### ノードの色 + ノードジェネレータはデフォルトでノードテンプレートで定義されたノードの色を使用します。変更する必要がある場合は、コマンドラインの`--color`オプションを使用できます。オプションには、ノードの色を表す16進数("RRGGBB"形式)の文字列を指定できます。 node-red-nodegen td.jsonld --color FFFFFF #### 情報タブ内のノードの情報 + ノードジェネレータは、Thing Descriptionの次のプロパティを使用して、情報タブにノードの情報を自動的に生成します。 - description: ノードの説明 @@ -846,10 +936,12 @@ Node-REDフローエディタのパレットでは、生成したノードはデ 情報タブのノード情報を変更したい場合は、生成されたノードのHTMLファイルの最後のセクションを編集します。 #### README + ノードの詳細を説明は、README.mdというファイルに書きます。 フローライブラリにノードを公開すると、フローライブラリのWebサイトは、ノードのページで本ファイルを表示します。 ノードジェネレータはREADME.mdのテンプレートを出力するので、ファイルを変更するだけです。 ## 既知の問題点 + - ノードジェネレータのコマンドでは、非同期の問題があるため、--tgzオプションと--iconオプションを同時に使用することはできません。 - OpenAPIドキュメントの値`info.title`は生成されたコードの変数名として使われるため、アルファベットの文字(数字ではない)で始める必要があります。 diff --git a/lib/nodegen.js b/lib/nodegen.js index 0e86bd3..3aaa620 100644 --- a/lib/nodegen.js +++ b/lib/nodegen.js @@ -14,13 +14,14 @@ * limitations under the License. **/ -const util = require("./util"); const FunctionNodeGenerator = require("./function"); const SwaggerNodeGenerator = require("./swagger"); const WebOfThingsGenerator = require("./webofthings"); +const SubflowNodeGenerator = require("./subflow"); module.exports = { FunctionNodeGenerator: FunctionNodeGenerator, SwaggerNodeGenerator: SwaggerNodeGenerator, - WebOfThingsGenerator: WebOfThingsGenerator + WebOfThingsGenerator: WebOfThingsGenerator, + SubflowNodeGenerator: SubflowNodeGenerator }; diff --git a/lib/subflow/index.js b/lib/subflow/index.js new file mode 100644 index 0000000..b53b8f1 --- /dev/null +++ b/lib/subflow/index.js @@ -0,0 +1,132 @@ +const util = require("../util"); + +const fs = require('fs'); +const path = require('path'); +const mustache = require('mustache'); +const crypt = require("crypto-js"); + +const TEMPLATE_DIR = path.join(__dirname,'../../templates/subflow'); + + +// Extract Subflow definition from JSON data +function getSubflowDef(flow) { + const newFlow = []; + let sf = null; + flow.forEach((item) => { + if (item.hasOwnProperty("meta") && + item.meta.hasOwnProperty("module")) { + if (sf !== null) { + throw new Error("unexpected subflow definition"); + } + sf = item; + } + else { + newFlow.push(item); + } + }); + return [sf, newFlow]; +} + + +// get flow encoding method +function getEncoder(encoding) { + if (encoding === "AES") { + return function (flow, key) { + var data = JSON.stringify(flow); + var enc = crypt.AES.encrypt(data, key); + return enc.toString(); + } + } + throw new Error("encoding not defined:" +encoding); +} + +// Create JSON data file +function createJSON(dstPath, flow, encoding, key) { + const [sf, newFlow] = getSubflowDef(flow); + if (encoding && (encoding !== "none")) { + const encode = getEncoder(encoding); + const encStr = encode(newFlow, key); + sf.flow = { + encoding: encoding, + flow: encStr + }; + } + else { + sf.flow = newFlow; + } + const data = JSON.stringify(sf, null, "\t"); + fs.writeFileSync(dstPath, data); +} + + +module.exports = async function(data, options) { + "use strict"; + + const json = data.src; + + // Get subflow & flow definition + const [sf, newFlow] = getSubflowDef(json); + const meta = sf.meta; + + data.name = meta.type; + data.module = meta.module; + data.version = meta.version; + data.desc = meta.desc; + if (!data.desc || (data.desc === "")) { + data.desc = "Node-RED node for " +data.name; + } + data.license = meta.license; + if (!data.license || (data.license === "")) { + data.license = "unknown"; + } + + var params = { + nodeName: data.name, + projectName: data.module, + projectVersion: data.version, + keywords: util.extractKeywords(data.keywords), + category: data.category || "subflow", + description: data.desc, + licenseName: data.license, + }; + + // Make directory + try { + fs.mkdirSync(path.join(data.dst, data.module)); + } catch (error) { + if (error.code !== "EEXIST") { + throw error; + } + } + + // Create subflow.json + createJSON(path.join(data.dst, data.module, "subflow.json"), + json, (options.encoding || "none"), options.encodekey); + + // Create package.json + var packageTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, "package.json.mustache"), "utf-8"); + var packageSourceCode = mustache.render(packageTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, "package.json"), packageSourceCode); + + // Create subflow.js + var nodeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, "subflow.js.mustache"), "utf-8"); + var nodeSourceCode = mustache.render(nodeTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, "subflow.js"), nodeSourceCode); + + // Create README.md + var readmeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, "README.md.mustache"), "utf-8"); + var readmeSourceCode = mustache.render(readmeTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, "README.md"), readmeSourceCode); + + // Create LICENSE file + var licenseTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, "LICENSE.mustache"), "utf-8"); + var licenseSourceCode = mustache.render(licenseTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, "LICENSE"), licenseSourceCode); + + if (options.tgz) { + util.runNpmPack(data); + return(path.join(data.dst, data.module + "-" + data.version + ".tgz")); + } else { + return(path.join(data.dst, data.module)); + } +} diff --git a/package.json b/package.json index 3e18652..2c7e32e 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "api-spec-converter": "2.12.0", "axios": "0.21.1", "colors": "1.4.0", + "crypto-js": "4.0.0", "csv-string": "4.0.1", "javascript-obfuscator": "1.12.1", "jimp": "0.16.1", diff --git a/samples/qrcode.json b/samples/qrcode.json new file mode 100644 index 0000000..fde6ee2 --- /dev/null +++ b/samples/qrcode.json @@ -0,0 +1 @@ +[{"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 diff --git a/templates/subflow/LICENSE.mustache b/templates/subflow/LICENSE.mustache new file mode 100644 index 0000000..b219080 --- /dev/null +++ b/templates/subflow/LICENSE.mustache @@ -0,0 +1 @@ +{{&licenseName}} diff --git a/templates/subflow/README.md.mustache b/templates/subflow/README.md.mustache new file mode 100644 index 0000000..0ee221f --- /dev/null +++ b/templates/subflow/README.md.mustache @@ -0,0 +1,12 @@ +{{&projectName}} +===================== + +{{&description}} + +Install +------- + +Run the following command in your Node-RED user directory - typically `~/.node-red` + + npm install {{&projectName}} + diff --git a/templates/subflow/package.json.mustache b/templates/subflow/package.json.mustache new file mode 100644 index 0000000..68d3bb4 --- /dev/null +++ b/templates/subflow/package.json.mustache @@ -0,0 +1,18 @@ +{ + "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.0.0" + } +} diff --git a/templates/subflow/subflow.js.mustache b/templates/subflow/subflow.js.mustache new file mode 100644 index 0000000..e36b899 --- /dev/null +++ b/templates/subflow/subflow.js.mustache @@ -0,0 +1,82 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +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); + + function getDecoder(encoding) { + var settings = RED.settings; + if (settings && + settings.encodeSubflow && + settings.encodeSubflow.methods) { + const methods = settings.encodeSubflow.methods; + const method = methods.find((x) => (x.name === encoding)); + if (method) { + return method.decode; + } + } + if (encoding == "AES") { + return function (enc, key) { + var enc = crypt.AES.decrypt(enc, key) + var data = enc.toString(crypt.enc.Utf8); + return JSON.parse(data); + }; + } + // no decoding + return null; + } + + function convFlow(sf) { + const flow = sf.flow; + if (((typeof flow) === "object") && + !Array.isArray(flow)) { + if (flow.hasOwnProperty("encoding") && + flow.hasOwnProperty("flow")) { + const encoding = flow.encoding; + const body = flow.flow; + const decoder = getDecoder(encoding); + if (decoder) { + const key = process.env["NR_FLOW_DECODE_KEY"]; + if (key && (key !== "")) { + try { + RED.log.info("deocde "+encoding+ " encoded flow"); + const flowData = decoder(body, key); + sf.flow = flowData; + } + catch (e) { + throw new Error("flow decode error:"+e.toString()); + } + } + else { + throw new Error("no flow decode key specified"); + } + } + else { + throw new Error("no decoder found:" +encoding); + } + } + } + return sf; + } + + RED.nodes.registerSubflow(convFlow(flow)); +} diff --git a/test/lib/subflow_spec.js b/test/lib/subflow_spec.js new file mode 100644 index 0000000..4592983 --- /dev/null +++ b/test/lib/subflow_spec.js @@ -0,0 +1,74 @@ +const fs = require('fs'); +const path = require('path'); +const should = require('should'); +const del = require('del'); +const subflow2node = require('../../lib/subflow'); + +describe('subflow node', function () { + it('should have node files', function (done) { + var options = {}; + var data = { dst: '.' }; + data.src = JSON.parse(fs.readFileSync('samples/qrcode.json')); + subflow2node(data, options).then(function (result) { + try { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-contrib-qrcode'); + packageSourceCode.version.should.equal('0.1.0'); + fs.statSync(result + '/subflow.json').size.should.be.above(0); + fs.statSync(result + '/subflow.js').size.should.be.above(0); + fs.statSync(result + '/README.md').size.should.be.above(0); + fs.statSync(result + '/LICENSE').size.should.be.above(0); + del.sync(result); + done(); + } + catch (e) { + done(e); + } + }); + }); + it('should handle encoding option', function (done) { + var options = { + encoding: "AES", + encodekey: "Node-RED" + }; + var data = { dst: '.' }; + data.src = JSON.parse(fs.readFileSync('samples/qrcode.json')); + subflow2node(data, options).then(function (result) { + try { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-contrib-qrcode'); + packageSourceCode.version.should.equal('0.1.0'); + fs.statSync(result + '/subflow.json').size.should.be.above(0); + fs.statSync(result + '/subflow.js').size.should.be.above(0); + fs.statSync(result + '/README.md').size.should.be.above(0); + fs.statSync(result + '/LICENSE').size.should.be.above(0); + var sf = JSON.parse(fs.readFileSync(result + "/subflow.json")); + sf.should.have.property("flow"); + sf.flow.should.have.property("encoding", "AES"); + del.sync(result); + done(); + } + catch (e) { + done(e); + } + }); + }); + it('should create tgz', function (done) { + var options = { + tgz: true + }; + var data = { dst: '.' }; + data.src = JSON.parse(fs.readFileSync('samples/qrcode.json')); + subflow2node(data, options).then(function (result) { + try { + fs.statSync(result).isFile().should.be.eql(true); + del.sync(result); + del.sync("./node-red-contrib-qrcode"); + done(); + } + catch (e) { + done(e); + } + }); + }); +});