From 1392d7b8b49880e18d8021f57007743c3733555b Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 1 Sep 2020 11:24:20 +0100 Subject: [PATCH 1/7] Update copyright statements --- bin/getswagger.js | 2 +- bin/node-red-nodegen.js | 2 +- lib/nodegen.js | 26 +++++++++++++------------- lib/wotutils.js | 24 ++++++++++++------------ templates/function/LICENSE.mustache | 6 ++---- templates/function/node.js.mustache | 16 ---------------- 6 files changed, 29 insertions(+), 47 deletions(-) diff --git a/bin/getswagger.js b/bin/getswagger.js index d7d5de9..afd2796 100644 --- a/bin/getswagger.js +++ b/bin/getswagger.js @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * Copyright JS Foundation and other contributors, http://js.foundation + * Copyright OpenJS Foundation and other contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/bin/node-red-nodegen.js b/bin/node-red-nodegen.js index 3d28161..b163c73 100755 --- a/bin/node-red-nodegen.js +++ b/bin/node-red-nodegen.js @@ -1,7 +1,7 @@ #!/usr/bin/env node /** - * Copyright JS Foundation and other contributors, http://js.foundation + * Copyright OpenJS Foundation and other contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/nodegen.js b/lib/nodegen.js index 4a30f0b..e11cc5b 100644 --- a/lib/nodegen.js +++ b/lib/nodegen.js @@ -1,5 +1,5 @@ /** - * Copyright JS Foundation and other contributors, http://js.foundation + * Copyright OpenJS Foundation and other contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -460,7 +460,7 @@ function swagger2node(data, options) { nodeName: data.name, isBodyParam: isBodyParam, isNotBodyParam: isNotBodyParam, - hasServiceParams: hasServiceParams + hasServiceParams: hasServiceParams }, lint: false, beautify: false @@ -492,7 +492,7 @@ function swagger2node(data, options) { color: data.color || '#89bf04', isNotBodyParam: isNotBodyParam, hasOptionalParams: hasOptionalParams, - hasServiceParams: hasServiceParams + hasServiceParams: hasServiceParams }, lint: false, beautify: false @@ -532,7 +532,7 @@ function swagger2node(data, options) { mustache: { nodeName: data.name, projectName: data.module, - hasServiceParams: hasServiceParams + hasServiceParams: hasServiceParams }, lint: false, beautify: false @@ -642,7 +642,7 @@ function wottd2node(data, options) { data.module = 'node-red-contrib-' + data.name; } } - + if (!data.version || data.version === '') { if (td.version && td.version.instance) { data.version = td.version.instance; @@ -679,7 +679,7 @@ function wottd2node(data, options) { } } data.actions = []; - for (const a in td.actions) { + for (const a in td.actions) { if (td.actions.hasOwnProperty(a)) { const q = td.actions[a]; q.name = a; @@ -712,21 +712,21 @@ function wottd2node(data, options) { } const wotmeta = []; - if (td.hasOwnProperty('lastModified')) { - wotmeta.push({name: "lastModified", value: td.lastModified}); + if (td.hasOwnProperty('lastModified')) { + wotmeta.push({name: "lastModified", value: td.lastModified}); } - if (td.hasOwnProperty('created')) { - wotmeta.push({name: "created", value: td.created}); + if (td.hasOwnProperty('created')) { + wotmeta.push({name: "created", value: td.created}); } - if (td.hasOwnProperty('support')) { - wotmeta.push({name: "support", value: JSON.stringify(td.support)}); + if (td.hasOwnProperty('support')) { + wotmeta.push({name: "support", value: JSON.stringify(td.support)}); } if (td.hasOwnProperty("id")) { wotmeta.push({name: "id", value: td.id, last: true}); } const formsel = wotutils.makeformsel(td); - + data.genformsel = JSON.stringify(formsel); data.genproprwo = JSON.stringify(rwo); diff --git a/lib/wotutils.js b/lib/wotutils.js index 51c31cd..fcebadb 100644 --- a/lib/wotutils.js +++ b/lib/wotutils.js @@ -1,5 +1,5 @@ /** - * Copyright JS Foundation and other contributors, http://js.foundation + * Copyright OpenJS Foundation and other contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ function validateTd(td) { JSON.parse(fs.readFileSync(path.join(__dirname, './td-json-schema-validation.json'), 'utf-8')); const ajv = new Ajv({allErrors: true}); const valid = ajv.validate(TDSchema, td); - + return { result: valid, errorText: valid?"":ajv.errorsText()}; } @@ -128,7 +128,7 @@ function normalizeTd(td) { } } // Set default values in properties - for (const p in td.properties) { + for (const p in td.properties) { const pdef = td.properties[p]; if (pdef.hasOwnProperty("security") && typeof pdef.security === 'string') { pdef.security = [pdef.security]; @@ -137,7 +137,7 @@ function normalizeTd(td) { pdef.forms = pdef.forms.map((f) => formconv(pdef, f, "PropertyAffordance")); // no filtering based on protocol } - + // if there is forms which have readproperty in op, writeOnly is false, otherwise true pdef.writeOnly = setdefault(pdef, "writeOnly", !isOpInForms("readproperty", pdef.forms)); // if there is forms which have writeproperty in op, readOnly is false, otherwise true @@ -147,7 +147,7 @@ function normalizeTd(td) { // in any cases, if it explicitly stated by writeOnly/readOnly/observable, use it. } - + // Set default values in actions for (const a in td.actions) { const adef = td.actions[a]; @@ -206,7 +206,7 @@ function normalizeTd(td) { td.properties['__ALLPROPERTIES'] = { title: "All Properties", description: "all properties of this Thing", - forms: convforms, + forms: convforms, type: "object", writeOnly: !isOpInForms("readproperty", convforms), readOnly: !isOpInForms("writeproperty", convforms), @@ -221,15 +221,15 @@ function filterFormTd(td) { for (const p in td.properties) { 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?:/)))); + forms = forms.filter((f) => (f.hasOwnProperty("href") && + (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/)))); } td.properties[p].forms = forms; } for (const a in td.actions) { let forms = td.actions[a].forms; if (forms) { - forms = forms.filter((f) => (f.hasOwnProperty("href") && + forms = forms.filter((f) => (f.hasOwnProperty("href") && (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/)))); } td.actions[a].forms = forms; @@ -237,7 +237,7 @@ function filterFormTd(td) { for (const e in td.events) { let forms = td.events[e].forms; if (forms) { - forms = forms.filter((f) => (f.hasOwnProperty("href") && + forms = forms.filter((f) => (f.hasOwnProperty("href") && (f.href.match(/^https?:/) || f.href.match(/^wss?:/) || f.href.match(/^coaps?:/)))); } td.events[e].forms = forms; @@ -309,13 +309,13 @@ function woticon(td) { "Illuminance": "font-awesome/fa-sun-o", "IlluminanceSensing": "font-awesome/fa-sun-o", "MotionControl": "font-awesome/fa-arrows-alt", - "Temperature": "font-awesome/fa-thermometer-half", + "Temperature": "font-awesome/fa-thermometer-half", "TemperatureSensing": "font-awesome/fa-thermometer-half", "TemperatureData": "font-awesome/fa-thermometer-half", "Thermostat": "font-awesome/fa-thermometer-half", "Pump": "font-awesome/fa-tint", "AirConditioner": "font-awesome/fa-snowflake-o", - "UltrasonicSensing": "font-awesome/fa-rss", + "UltrasonicSensing": "font-awesome/fa-rss", "HumiditySensing": "font-awesome/fa-umbrella", "SoundPressure": "font-awesome/fa-volume-up", "Valve": "font-awesome/fa-wrench", diff --git a/templates/function/LICENSE.mustache b/templates/function/LICENSE.mustache index a548602..290620d 100644 --- a/templates/function/LICENSE.mustache +++ b/templates/function/LICENSE.mustache @@ -1,7 +1,5 @@ -This node is under Apache-2.0 licence because it was created from the following -fuction node code using [node-red-nodegen](https://github.com/node-red/node-red-nodegen). -https://github.com/node-red/node-red/blob/master/nodes/core/core/80-function.html -https://github.com/node-red/node-red/blob/master/nodes/core/core/80-function.js +This module was generated by node-red-nodegen: https://github.com/node-red/node-red-nodegen +using code templates made available under the Apache-2.0 licence. --------------------------------------------------------------------------- diff --git a/templates/function/node.js.mustache b/templates/function/node.js.mustache index 3dea553..e848181 100644 --- a/templates/function/node.js.mustache +++ b/templates/function/node.js.mustache @@ -1,19 +1,3 @@ -/** - * 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) { "use strict"; var util = require("util"); From 5e1a059062136b32be973c2ad189af39e0b1b3b6 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 1 Sep 2020 12:09:26 +0100 Subject: [PATCH 2/7] Remove when library for native promises --- lib/nodegen.js | 7 +++---- package.json | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/nodegen.js b/lib/nodegen.js index e11cc5b..80522af 100644 --- a/lib/nodegen.js +++ b/lib/nodegen.js @@ -14,7 +14,6 @@ * limitations under the License. **/ -var when = require("when"); var fs = require('fs'); var path = require('path'); var child_process = require('child_process'); @@ -151,7 +150,7 @@ function function2node(data, options) { // console.log("OPT",options); // console.log("DATA",data); "use strict"; - return when.promise(function (resolve, reject) { + return new Promise(function (resolve, reject) { // Read meta data in js file var meta = {}; var parts = new String(data.src).split('\n'); @@ -290,7 +289,7 @@ function function2node(data, options) { function swagger2node(data, options) { "use strict"; - return when.promise(function (resolve, reject) { + return new Promise(function (resolve, reject) { // Modify swagger data var swagger = JSON.parse(JSON.stringify(data.src), function (key, value) { if (key === 'consumes' || key === 'produces') { // Filter type of 'Content-Type' and 'Accept' in request header @@ -613,7 +612,7 @@ function swagger2node(data, options) { function wottd2node(data, options) { - return when.promise(function (resolve, reject) { + return new Promise(function (resolve, reject) { let td = data.src; // validate TD diff --git a/package.json b/package.json index bf641e0..f370387 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "mustache": "4.0.1", "request": "2.88.2", "swagger-js-codegen-formdata": "0.15.5", - "when": "3.7.8", "yamljs": "0.3.0" }, "devDependencies": { From 6a31ff9f105ca1887d084ba214998c7d88169f9f Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 1 Sep 2020 15:04:23 +0100 Subject: [PATCH 3/7] Split each mode into its own src/test files --- .gitignore | 1 - Gruntfile.js | 3 - bin/getswagger.js | 35 - lib/function/index.js | 150 ++++ lib/nodegen.js | 785 +----------------- lib/swagger/index.js | 333 ++++++++ lib/util.js | 133 +++ lib/webofthings/index.js | 195 +++++ .../td-json-schema-validation.json | 0 lib/{ => webofthings}/wotutils.js | 0 package.json | 1 - samples/swagger.json | 1 + test/lib/function_spec.js | 106 +++ test/lib/nodegen_spec.js | 198 +---- test/lib/swagger_spec.js | 52 ++ test/lib/webofthings_spec.js | 48 ++ 16 files changed, 1023 insertions(+), 1018 deletions(-) delete mode 100644 bin/getswagger.js create mode 100644 lib/function/index.js create mode 100644 lib/swagger/index.js create mode 100644 lib/util.js create mode 100644 lib/webofthings/index.js rename lib/{ => webofthings}/td-json-schema-validation.json (100%) rename lib/{ => webofthings}/wotutils.js (100%) create mode 100644 samples/swagger.json create mode 100644 test/lib/function_spec.js create mode 100644 test/lib/swagger_spec.js create mode 100644 test/lib/webofthings_spec.js diff --git a/.gitignore b/.gitignore index 534be16..0ea1ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ node-red-contrib-* node-red-node-* ./nodegen -samples # Dependency directories node_modules diff --git a/Gruntfile.js b/Gruntfile.js index 2590cd9..a993350 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,9 +4,6 @@ module.exports = function (grunt) { generateNode_lowerCase: { command: 'node bin/node-red-nodegen.js samples/lower-case.js -o ./nodegen' }, - getSwagger_swaggerPetstore: { - command: 'node bin/getswagger.js' - }, generateNode_swaggerPetstore: { command: 'node bin/node-red-nodegen.js samples/swagger.json -o ./nodegen' } diff --git a/bin/getswagger.js b/bin/getswagger.js deleted file mode 100644 index afd2796..0000000 --- a/bin/getswagger.js +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env node - -/** - * Copyright OpenJS Foundation and other contributors - * - * 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. - **/ - -var fs = require('fs'); -var request = require('request'); - -request("http://petstore.swagger.io/v2/swagger.json", function (error, response, body) { - "use strict"; - if (!error) { - try { - var swagger = JSON.parse(body); - swagger.schemes = ["http"]; - fs.writeFileSync(__dirname + "/../samples/swagger.json", JSON.stringify(swagger)); - } catch (error2) { - console.error(error2); - } - } else { - console.error(error); - } -}); diff --git a/lib/function/index.js b/lib/function/index.js new file mode 100644 index 0000000..87090da --- /dev/null +++ b/lib/function/index.js @@ -0,0 +1,150 @@ +const util = require("../util"); + +const fs = require('fs'); +const path = require('path'); +const mustache = require('mustache'); +const jsStringEscape = require('js-string-escape'); +const obfuscator = require('javascript-obfuscator'); + +const TEMPLATE_DIR = path.join(__dirname,'../../templates/function'); + +module.exports = function(data, options) { + // console.log("OPT",options); + // console.log("DATA",data); + "use strict"; + return new Promise(function (resolve, reject) { + // Read meta data in js file + var meta = {}; + var parts = new String(data.src).split('\n'); + parts.forEach(function (part) { + var match = /^\/\/ (\w+): (.*)/.exec(part.toString()); + if (match) { + if (match[1] === 'name') { + meta.name = match[2].replace(/([A-Z])/g, ' $1').toLowerCase().replace(/[^ a-z0-9]+/g, '').replace(/^ | $/, '').replace(/ +/g, '-'); + } else { + meta[match[1]] = match[2]; + } + } + }); + + if (!data.name || data.name === '') { + data.name = meta.name; + } + + if (data.module) { + if (data.prefix) { + reject('module name and prefix are conflicted.'); + return; + } + } else { + if (data.prefix) { + data.module = data.prefix + data.name; + } else { + data.module = 'node-red-contrib-' + data.name; + } + } + + if (!data.version || data.version === '') { + data.version = '0.0.1'; + } + + if (data.icon) { + if (!data.icon.match(/\.(png|gif)$/) && !data.icon.match(/^(node-red|font-awesome)/)) { + data.icon = data.icon + '.png'; + } + if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { + reject('invalid icon file name'); + return; + } + } + + if (data.color) { + if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { + data.color = '#' + data.color; + } else { + reject('invalid color'); + return; + } + } + + if (data.name === 'function') { + reject('\'function\' is duplicated node name. Use another name.'); + return; + } else if (!data.name.match(/^[a-z0-9\-]+$/)) { + reject('invalid node name'); + return; + } else { + var params = { + nodeName: data.name, + projectName: data.module, + projectVersion: data.version, + keywords: util.extractKeywords(data.keywords), + category: data.category || 'function', + icon: function () { + if (data.icon ) { + if (!data.icon.match(/^(node-red|font-awesome)/)) { + return path.basename(data.icon); + } + else { return data.icon; } + } else { + return 'icon.png'; + } + }, + color: data.color || '#C0DEED', + func: jsStringEscape(data.src), + outputs: meta.outputs || data.outputs, + inputLabels: JSON.stringify(data.inputLabels || []), + outputLabels: JSON.stringify(data.outputLabels || []), + nodeInfo: jsStringEscape(data.info || ""), + nodeRead: data.info || "" + }; + + util.createCommonFiles(TEMPLATE_DIR, data); + + // 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 node.js + var nodeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.js.mustache'), 'utf-8'); + var nodeSourceCode = mustache.render(nodeTemplate, params); + if (options.obfuscate) { + nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); + } + fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); + + // Create node.html + var htmlTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.html.mustache'), 'utf-8'); + var htmlSourceCode = mustache.render(htmlTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); + + // Create flow.json + var flowTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'examples/flow.json.mustache'), 'utf-8'); + var flowSourceCode = mustache.render(flowTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, 'examples/flow.json'), flowSourceCode); + + // Create node_spec.js + var nodeSpecTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'test/node_spec.js.mustache'), 'utf-8'); + var nodeSpecSourceCode = mustache.render(nodeSpecTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); + + // 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); + resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); + } else { + resolve(path.join(data.dst, data.module)); + } + } + }); +} diff --git a/lib/nodegen.js b/lib/nodegen.js index 80522af..d46d30f 100644 --- a/lib/nodegen.js +++ b/lib/nodegen.js @@ -14,787 +14,10 @@ * limitations under the License. **/ -var fs = require('fs'); -var path = require('path'); -var child_process = require('child_process'); -var request = require('request'); -var mustache = require('mustache'); -var jsStringEscape = require('js-string-escape'); -var obfuscator = require('javascript-obfuscator'); -var csv = require('csv-string'); -var CodeGen = require('swagger-js-codegen-formdata').CodeGen; -var jimp = require("jimp"); - -var wotutils = require('./wotutils'); - -function createCommonFiles(templateDirectory, data) { - "use strict"; - // Make directories - try { - fs.mkdirSync(path.join(data.dst, data.module)); - } catch (error) { - if (error.code !== 'EEXIST') { - console.error(error); - } - } - - var isStockIcon = data.icon && (data.icon.match(/^(alert|arduino|arrow-in|batch|bluetooth|bridge-dash|bridge|cog|comment|db|debug|envelope|feed|file-in|file-out|file|function|hash|inject|join|leveldb|light|link-out|mongodb|mouse|node-changed|node-error|parser-csv|parser-html|parser-json|parser-xml|parser-yaml|range|redis|rpi|serial|sort|split|subflow|swap|switch|template|timer|trigger|twitter|watch|white-globe)\.png$/) || data.icon.match(/^(node-red|font-awesome)/)); - if (!isStockIcon) { - try { - fs.mkdirSync(path.join(data.dst, data.module, 'icons')); - } catch (error) { - if (error.code !== 'EEXIST') { - console.error(error); - } - } - } - if (data.icon) { - if (!isStockIcon) { - try { - jimp.read(data.icon, function (error2, image) { - if (!error2) { - var outputPath = path.join(data.dst, data.module, 'icons', path.basename(data.icon)); - if (image.bitmap.width === 40 && image.bitmap.height === 60) { - var buf = fs.readFileSync(data.icon); - fs.writeFileSync(outputPath, buf); - } else { - image.background(0xFFFFFFFF).resize(40, 60).write(outputPath); - } - } else { - console.log('error occurs while converting icon file.'); - } - }); - } catch (error) { - console.error(error); - } - } - } else { - var icons = fs.readdirSync(path.join(templateDirectory, 'icons')); - icons.forEach(function (icon) { - try { - var buf = fs.readFileSync(path.join(templateDirectory, 'icons', icon)); - fs.writeFileSync(path.join(data.dst, data.module, 'icons', icon), buf); - } catch (error) { - console.error(error); - } - }); - } - - try { - fs.mkdirSync(path.join(data.dst, data.module, 'locales')); - } catch (error) { - if (error.code !== 'EEXIST') { - console.error(error); - } - } - try { - var languages = fs.readdirSync(path.join(templateDirectory, 'locales')); - languages.forEach(function (language) { - try { - fs.mkdirSync(path.join(data.dst, data.module, 'locales', language)); - } catch (error) { - if (error.code !== 'EEXIST') { - console.error(error); - } - } - }); - } catch (error) { - if (error.code !== 'ENOENT') { - console.error(error); - } - } - try { - fs.mkdirSync(path.join(data.dst, data.module, 'examples')); - } catch (error) { - if (error.code !== 'EEXIST') { - console.error(error); - } - } - try { - fs.mkdirSync(path.join(data.dst, data.module, 'test')); - } catch (error) { - if (error.code !== 'EEXIST') { - console.error(error); - } - } - try { - var buf = fs.readFileSync(path.join(templateDirectory, '.travis.yml.mustache')); - fs.writeFileSync(path.join(data.dst, data.module, '.travis.yml'), buf); - } catch (error) { - if (error.code !== 'EEXIST') { - console.error(error); - } - } -} - -function runNpmPack(data) { - "use strict"; - var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; - try { - child_process.execFileSync(npmCommand, ['pack', './' + data.module], { cwd: data.dst }); - } catch (error) { - console.error(error); - } -} - -function extractKeywords(keywordsStr) { - "use strict"; - var keywords = ["node-red-nodegen"]; - keywords = keywordsStr ? keywords.concat(csv.parse(keywordsStr)[0]) : keywords; - keywords = keywords.map(k => ({ name: k })); - keywords[keywords.length - 1].last = true; - return keywords; -} - -function function2node(data, options) { - // console.log("OPT",options); - // console.log("DATA",data); - "use strict"; - return new Promise(function (resolve, reject) { - // Read meta data in js file - var meta = {}; - var parts = new String(data.src).split('\n'); - parts.forEach(function (part) { - var match = /^\/\/ (\w+): (.*)/.exec(part.toString()); - if (match) { - if (match[1] === 'name') { - meta.name = match[2].replace(/([A-Z])/g, ' $1').toLowerCase().replace(/[^ a-z0-9]+/g, '').replace(/^ | $/, '').replace(/ +/g, '-'); - } else { - meta[match[1]] = match[2]; - } - } - }); - - if (!data.name || data.name === '') { - data.name = meta.name; - } - - if (data.module) { - if (data.prefix) { - reject('module name and prefix are conflicted.'); - return; - } - } else { - if (data.prefix) { - data.module = data.prefix + data.name; - } else { - data.module = 'node-red-contrib-' + data.name; - } - } - - if (!data.version || data.version === '') { - data.version = '0.0.1'; - } - - if (data.icon) { - if (!data.icon.match(/\.(png|gif)$/) && !data.icon.match(/^(node-red|font-awesome)/)) { - data.icon = data.icon + '.png'; - } - if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { - reject('invalid icon file name'); - return; - } - } - - if (data.color) { - if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { - data.color = '#' + data.color; - } else { - reject('invalid color'); - return; - } - } - - if (data.name === 'function') { - reject('\'function\' is duplicated node name. Use another name.'); - return; - } else if (!data.name.match(/^[a-z0-9\-]+$/)) { - reject('invalid node name'); - return; - } else { - var params = { - nodeName: data.name, - projectName: data.module, - projectVersion: data.version, - keywords: extractKeywords(data.keywords), - category: data.category || 'function', - icon: function () { - if (data.icon ) { - if (!data.icon.match(/^(node-red|font-awesome)/)) { - return path.basename(data.icon); - } - else { return data.icon; } - } else { - return 'icon.png'; - } - }, - color: data.color || '#C0DEED', - func: jsStringEscape(data.src), - outputs: meta.outputs || data.outputs, - inputLabels: JSON.stringify(data.inputLabels || []), - outputLabels: JSON.stringify(data.outputLabels || []), - nodeInfo: jsStringEscape(data.info || ""), - nodeRead: data.info || "" - }; - - createCommonFiles(__dirname + '/../templates/function', data); - - // Create package.json - var packageTemplate = fs.readFileSync(path.join(__dirname, '../templates/function/package.json.mustache'), 'utf-8'); - var packageSourceCode = mustache.render(packageTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); - - // Create node.js - var nodeTemplate = fs.readFileSync(path.join(__dirname, '../templates/function/node.js.mustache'), 'utf-8'); - var nodeSourceCode = mustache.render(nodeTemplate, params); - if (options.obfuscate) { - nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); - - // Create node.html - var htmlTemplate = fs.readFileSync(path.join(__dirname, '../templates/function/node.html.mustache'), 'utf-8'); - var htmlSourceCode = mustache.render(htmlTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); - - // Create flow.json - var flowTemplate = fs.readFileSync(path.join(__dirname, '../templates/function/examples/flow.json.mustache'), 'utf-8'); - var flowSourceCode = mustache.render(flowTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'examples/flow.json'), flowSourceCode); - - // Create node_spec.js - var nodeSpecTemplate = fs.readFileSync(path.join(__dirname, '../templates/function/test/node_spec.js.mustache'), 'utf-8'); - var nodeSpecSourceCode = mustache.render(nodeSpecTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); - - // Create README.md - var readmeTemplate = fs.readFileSync(path.join(__dirname, '../templates/function/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(__dirname, '../templates/function/LICENSE.mustache'), 'utf-8'); - var licenseSourceCode = mustache.render(licenseTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'LICENSE'), licenseSourceCode); - - if (options.tgz) { - runNpmPack(data); - resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); - } else { - resolve(path.join(data.dst, data.module)); - } - } - }); -} - -function swagger2node(data, options) { - "use strict"; - return new Promise(function (resolve, reject) { - // Modify swagger data - var swagger = JSON.parse(JSON.stringify(data.src), function (key, value) { - if (key === 'consumes' || key === 'produces') { // Filter type of 'Content-Type' and 'Accept' in request header - if (value.indexOf('application/json') >= 0) { - return ['application/json']; - } else if (value.indexOf('application/xml') >= 0) { - return ['application/xml']; - } else if (value.indexOf('text/csv') >= 0) { - return ['text/csv']; - } else if (value.indexOf('text/plain') >= 0) { - return ['text/plain']; - } else if (value.indexOf('multipart/form-data') >= 0) { - return ['multipart/form-data']; - } else if (value.indexOf('application/octet-stream') >= 0) { - return ['application/octet-stream']; - } else { - return undefined; - } - } else if (key === 'default') { // Handle swagger-js-codegen bug - return undefined; - } else if (key === 'operationId') { // Sanitize 'operationId' (remove special chars) - return value.replace(/(?!\w|\s)./g, ''); - } else { - return value; - } - }); - - if (swagger.swagger !== '2.0') { - reject('unsupported version of OpenAPI document'); - return; - } - - if (!swagger.info.title) { - reject('No title is specified in OpenAPI document'); - return; - } - - var className = swagger.info.title.toLowerCase().replace(/(^|[^a-z0-9]+)[a-z0-9]/g, - function (str) { - return str.replace(/^[^a-z0-9]+/, '').toUpperCase(); - }); - - if (!data.name || data.name === '') { - data.name = className.replace(/([A-Z])/g, '-$1').slice(1).toLowerCase(); - } - - if (data.module) { - if (data.prefix) { - reject('module name and prefix are conflicted.'); - return; - } - } else { - if (data.prefix) { - data.module = data.prefix + data.name; - } else { - data.module = 'node-red-contrib-' + data.name; - } - } - - if (!data.version || data.version === '') { - if (swagger.info.version) { - var version = swagger.info.version.replace(/[^0-9\.]/g, ''); - if (version.match(/^[0-9]+$/)) { - data.version = version + '.0.0'; - } else if (version.match(/^[0-9]+\.[0-9]+$/)) { - data.version = version + '.0'; - } else if (version.match(/^[0-9]+\.[0-9]+\.[0-9]+$/)) { - data.version = version; - } else { - data.version = '0.0.1'; - } - } else { - data.version = '0.0.1'; - } - } - - if (data.icon) { - if (!data.icon.match(/\.(png|gif)$/)) { - data.icon = data.icon + '.png'; - } - if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { - reject('invalid icon file name'); - return; - } - } - - if (data.color) { - if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { - data.color = '#' + data.color; - } else { - reject('invalid color'); - return; - } - } - - createCommonFiles(path.join(__dirname, '../templates/swagger'), data); - - // Create Node.js SDK - var nodejsSourceCode = CodeGen.getNodeCode({ - className: className, - swagger: swagger, - lint: false, - beautify: false - }); - if (options.obfuscate) { - nodejsSourceCode = obfuscator.obfuscate(nodejsSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'lib.js'), nodejsSourceCode); - - // Create package.json - var packageSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(__dirname, '../templates/swagger/package.json.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - projectName: data.module, - projectVersion: data.version, - keywords: extractKeywords(data.keywords), - licenseName: function () { - if (swagger.info.license && swagger.info.license.name) { - return swagger.info.license.name; - } else { - return 'Apache-2.0'; - } - }, - projectAuthor: swagger.info.contact && swagger.info.contact.name ? swagger.info.contact.name : '' - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); - - // Mustache helpers - var isNotBodyParam = function () { - return function (content, render) { - return render('{{camelCaseName}}') !== 'body' ? render(content) : ''; - }; - }; - var isBodyParam = function () { - return function (content, render) { - return render('{{camelCaseName}}') === 'body' ? render(content) : ''; - }; - }; - var hasOptionalParams = function () { - return function (content, render) { - var params = render('{{#parameters}}{{^required}}{{camelCaseName}},{{/required}}{{/parameters}}'); - return params.split(',').filter(p => p).some(p => p !== 'body') ? render(content) : ''; - }; - }; - var hasServiceParams = swagger.host === undefined || swagger.securityDefinitions !== undefined; - - // Create node.js - var nodeSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(__dirname, '../templates/swagger/node.js.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - isBodyParam: isBodyParam, - isNotBodyParam: isNotBodyParam, - hasServiceParams: hasServiceParams - }, - lint: false, - beautify: false - }); - if (options.obfuscate) { - nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); - - // Create node.html - var htmlSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(__dirname, '../templates/swagger/node.html.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - category: data.category || 'function', - icon: function () { - if (data.icon) { - return path.basename(data.icon); - } else { - return 'icon.png'; - } - }, - color: data.color || '#89bf04', - isNotBodyParam: isNotBodyParam, - hasOptionalParams: hasOptionalParams, - hasServiceParams: hasServiceParams - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); - - // Create language files - var languages = fs.readdirSync(path.join(__dirname, '../templates/swagger/locales')); - languages.forEach(function (language) { - var languageFileSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(__dirname, '../templates/swagger/locales', language, 'node.json.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'locales', language, 'node.json'), - JSON.stringify(JSON.parse(languageFileSourceCode), null, 4)); - }); - - // Create node_spec.js - var nodeSpecSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(__dirname, '../templates/swagger/test/node_spec.js.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - projectName: data.module, - hasServiceParams: hasServiceParams - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); - - // Create README.md - var readmeSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(__dirname, '../templates/swagger/README.md.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - projectName: data.module, - licenseName: function () { - if (swagger.info.license && swagger.info.license.name) { - return swagger.info.license.name; - } else { - return 'Apache-2.0'; - } - }, - licenseUrl: function () { - if (swagger.info.license && swagger.info.license.url) { - return swagger.info.license.url; - } else { - return ''; - } - } - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); - - // Create LICENSE file - var licenseSourceCode = CodeGen.getCustomCode({ - projectName: data.module, - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(__dirname, '../templates/swagger/LICENSE.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - licenseName: function () { - if (swagger.info.license && swagger.info.license.name) { - return swagger.info.license.name; - } else { - return 'Apache-2.0'; - } - }, - licenseUrl: function () { - if (swagger.info.license && swagger.info.license.url) { - return swagger.info.license.url; - } else { - return ''; - } - } - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'LICENSE'), licenseSourceCode); - - if (options.tgz) { - runNpmPack(data); - resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); - } else { - resolve(path.join(data.dst, data.module)); - } - }); -} - - -function wottd2node(data, options) { - return new Promise(function (resolve, reject) { - let td = data.src; - - // validate TD - const validateResult = wotutils.validateTd(td); - if (validateResult.result === false) { - console.warn(`Invalid Thing Description:\n${validateResult.errorText}`); - } else { - console.info(`Schema validation succeeded.`); - } - - // if name is not specified, use td.title for module name. - if (!data.name || data.name === '') { - // filtering out special characters - data.name = 'wot' + td.title.replace(/[^A-Za-z0-9]/g, '').toLowerCase(); - } - - if (data.module) { - if (data.prefix) { - reject('module name and prefix are conflicted'); - return; - } - } else { - if (data.prefix) { - data.module = data.prefix + data.name; - } else { - data.module = 'node-red-contrib-' + data.name; - } - } - - if (!data.version || data.version === '') { - if (td.version && td.version.instance) { - data.version = td.version.instance; - } else { - data.version = '0.0.1'; - } - } - - data.tdstr = JSON.stringify(td); - td = wotutils.normalizeTd(td); - td = wotutils.filterFormTd(td); - data.normtd = JSON.stringify(td); - data.properties = []; - const rwo = {}; - for (const p in td.properties) { // convert to array - if (td.properties.hasOwnProperty(p)) { - const q = td.properties[p]; - q.name = p; - if (!q.title || q.title === '') { - q.title = q.name; - } - if (q.forms) { - for (var i = 0; i < q.forms.length; i++) { - q.forms[i].index = i; - } - } - data.properties.push(q); - rwo[p] = { - readable: !q.writeOnly, - writable: !q.readOnly, - observable: q.observable - }; - - } - } - data.actions = []; - for (const a in td.actions) { - if (td.actions.hasOwnProperty(a)) { - const q = td.actions[a]; - q.name = a; - if (!q.title || q.title === '') { - q.title = q.name; - } - if (q.forms) { - for (var i = 0; i < q.forms.length; i++) { - q.forms[i].index = i; - } - } - data.actions.push(q); - } - } - data.events = []; - for (const e in td.events) { - if (td.events.hasOwnProperty(e)) { - const q = td.events[e]; - q.name = e; - if (!q.title || q.title === '') { - q.title = q.name; - } - if (q.forms) { - for (var i = 0; i < q.forms.length; i++) { - q.forms[i].index = i; - } - } - data.events.push(q); - } - } - - const wotmeta = []; - if (td.hasOwnProperty('lastModified')) { - wotmeta.push({name: "lastModified", value: td.lastModified}); - } - if (td.hasOwnProperty('created')) { - wotmeta.push({name: "created", value: td.created}); - } - if (td.hasOwnProperty('support')) { - wotmeta.push({name: "support", value: JSON.stringify(td.support)}); - } - if (td.hasOwnProperty("id")) { - wotmeta.push({name: "id", value: td.id, last: true}); - } - - const formsel = wotutils.makeformsel(td); - - data.genformsel = JSON.stringify(formsel); - data.genproprwo = JSON.stringify(rwo); - - data.ufName = td.title.replace(/[^A-Za-z0-9]/g, ' ').trim(); - data.nodeName = data.name; - data.projectName = data.module; - data.projectVersion = data.version; - data.keywords = extractKeywords(data.keywords); - data.category = data.category || 'Web of Things'; - data.description = td.description; - data.licenseName = 'Apache-2.0'; - data.licenseUrl = ''; - data.links = td.links; - data.support = td.support; - data.iconpath = wotutils.woticon(td); - data.wotmeta = wotmeta; - - let lang = null; - if (td.hasOwnProperty('@context') && Array.isArray(td['@context'])) { - td['@context'].forEach(e => { - if (e.hasOwnProperty("@language")) { - lang = e['@language']; - } - }); - } - if (lang === null) { - data.textdir = "auto"; - } else { - data.textdir = wotutils.textDirection(lang); - } - - createCommonFiles(path.join(__dirname, '../templates/webofthings'), data); - - // Create package.json - const packageTemplate = fs.readFileSync(path.join(__dirname, '../templates/webofthings/package.json.mustache'), 'utf-8'); - const packageSourceCode = mustache.render(packageTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); - - // Create node.js - const nodeTemplate = fs.readFileSync(path.join(__dirname, '../templates/webofthings/node.js.mustache'), 'utf-8'); - let nodeSourceCode = mustache.render(nodeTemplate, data); - if (options.obfuscate) { - nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); - - // Create node.html - const htmlTemplate = fs.readFileSync(path.join(__dirname, '../templates/webofthings/node.html.mustache'), 'utf-8'); - const htmlSourceCode = mustache.render(htmlTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); - - // Create README.html - const readmeTemplate = fs.readFileSync(path.join(__dirname, '../templates/webofthings/README.md.mustache'), 'utf-8'); - const readmeSourceCode = mustache.render(readmeTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); - - // Create LICENSE - const licenseTemplate = fs.readFileSync(path.join(__dirname, '../templates/webofthings/LICENSE.mustache'), 'utf-8'); - const licenseSourceCode = mustache.render(licenseTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'LICENSE'), licenseSourceCode); - - if (options.tgz) { - runNpmPack(data); - resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); - } else { - resolve(path.join(data.dst, data.module)); - } - }); -} +const util = require("./util"); +const function2node = require("./function"); +const swagger2node = require("./swagger"); +const wottd2node = require("./webofthings"); module.exports = { function2node: function2node, diff --git a/lib/swagger/index.js b/lib/swagger/index.js new file mode 100644 index 0000000..5eb540b --- /dev/null +++ b/lib/swagger/index.js @@ -0,0 +1,333 @@ +const util = require("../util"); + +const fs = require('fs'); +const path = require('path'); +const mustache = require('mustache'); +const obfuscator = require('javascript-obfuscator'); +const CodeGen = require('swagger-js-codegen-formdata').CodeGen; + +const TEMPLATE_DIR = path.join(__dirname,'../../templates/swagger'); + +module.exports = function(data, options) { + "use strict"; + return new Promise(function (resolve, reject) { + // Modify swagger data + var swagger = JSON.parse(JSON.stringify(data.src), function (key, value) { + if (key === 'consumes' || key === 'produces') { // Filter type of 'Content-Type' and 'Accept' in request header + if (value.indexOf('application/json') >= 0) { + return ['application/json']; + } else if (value.indexOf('application/xml') >= 0) { + return ['application/xml']; + } else if (value.indexOf('text/csv') >= 0) { + return ['text/csv']; + } else if (value.indexOf('text/plain') >= 0) { + return ['text/plain']; + } else if (value.indexOf('multipart/form-data') >= 0) { + return ['multipart/form-data']; + } else if (value.indexOf('application/octet-stream') >= 0) { + return ['application/octet-stream']; + } else { + return undefined; + } + } else if (key === 'default') { // Handle swagger-js-codegen bug + return undefined; + } else if (key === 'operationId') { // Sanitize 'operationId' (remove special chars) + return value.replace(/(?!\w|\s)./g, ''); + } else { + return value; + } + }); + + if (swagger.swagger !== '2.0') { + reject('unsupported version of OpenAPI document'); + return; + } + + if (!swagger.info.title) { + reject('No title is specified in OpenAPI document'); + return; + } + + var className = swagger.info.title.toLowerCase().replace(/(^|[^a-z0-9]+)[a-z0-9]/g, + function (str) { + return str.replace(/^[^a-z0-9]+/, '').toUpperCase(); + }); + + if (!data.name || data.name === '') { + data.name = className.replace(/([A-Z])/g, '-$1').slice(1).toLowerCase(); + } + + if (data.module) { + if (data.prefix) { + reject('module name and prefix are conflicted.'); + return; + } + } else { + if (data.prefix) { + data.module = data.prefix + data.name; + } else { + data.module = 'node-red-contrib-' + data.name; + } + } + + if (!data.version || data.version === '') { + if (swagger.info.version) { + var version = swagger.info.version.replace(/[^0-9\.]/g, ''); + if (version.match(/^[0-9]+$/)) { + data.version = version + '.0.0'; + } else if (version.match(/^[0-9]+\.[0-9]+$/)) { + data.version = version + '.0'; + } else if (version.match(/^[0-9]+\.[0-9]+\.[0-9]+$/)) { + data.version = version; + } else { + data.version = '0.0.1'; + } + } else { + data.version = '0.0.1'; + } + } + + if (data.icon) { + if (!data.icon.match(/\.(png|gif)$/)) { + data.icon = data.icon + '.png'; + } + if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { + reject('invalid icon file name'); + return; + } + } + + if (data.color) { + if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { + data.color = '#' + data.color; + } else { + reject('invalid color'); + return; + } + } + + util.createCommonFiles(TEMPLATE_DIR, data); + + // Create Node.js SDK + var nodejsSourceCode = CodeGen.getNodeCode({ + className: className, + swagger: swagger, + lint: false, + beautify: false + }); + if (options.obfuscate) { + nodejsSourceCode = obfuscator.obfuscate(nodejsSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); + } + fs.writeFileSync(path.join(data.dst, data.module, 'lib.js'), nodejsSourceCode); + + // Create package.json + var packageSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'package.json.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + projectName: data.module, + projectVersion: data.version, + keywords: util.extractKeywords(data.keywords), + licenseName: function () { + if (swagger.info.license && swagger.info.license.name) { + return swagger.info.license.name; + } else { + return 'Apache-2.0'; + } + }, + projectAuthor: swagger.info.contact && swagger.info.contact.name ? swagger.info.contact.name : '' + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); + + // Mustache helpers + var isNotBodyParam = function () { + return function (content, render) { + return render('{{camelCaseName}}') !== 'body' ? render(content) : ''; + }; + }; + var isBodyParam = function () { + return function (content, render) { + return render('{{camelCaseName}}') === 'body' ? render(content) : ''; + }; + }; + var hasOptionalParams = function () { + return function (content, render) { + var params = render('{{#parameters}}{{^required}}{{camelCaseName}},{{/required}}{{/parameters}}'); + return params.split(',').filter(p => p).some(p => p !== 'body') ? render(content) : ''; + }; + }; + var hasServiceParams = swagger.host === undefined || swagger.securityDefinitions !== undefined; + + // Create node.js + var nodeSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'node.js.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + isBodyParam: isBodyParam, + isNotBodyParam: isNotBodyParam, + hasServiceParams: hasServiceParams + }, + lint: false, + beautify: false + }); + if (options.obfuscate) { + nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); + } + fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); + + // Create node.html + var htmlSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'node.html.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + category: data.category || 'function', + icon: function () { + if (data.icon) { + return path.basename(data.icon); + } else { + return 'icon.png'; + } + }, + color: data.color || '#89bf04', + isNotBodyParam: isNotBodyParam, + hasOptionalParams: hasOptionalParams, + hasServiceParams: hasServiceParams + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); + + // Create language files + var languages = fs.readdirSync(path.join(TEMPLATE_DIR, 'locales')); + languages.forEach(function (language) { + var languageFileSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'locales', language, 'node.json.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'locales', language, 'node.json'), + JSON.stringify(JSON.parse(languageFileSourceCode), null, 4)); + }); + + // Create node_spec.js + var nodeSpecSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'test/node_spec.js.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + projectName: data.module, + hasServiceParams: hasServiceParams + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); + + // Create README.md + var readmeSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'README.md.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + projectName: data.module, + licenseName: function () { + if (swagger.info.license && swagger.info.license.name) { + return swagger.info.license.name; + } else { + return 'Apache-2.0'; + } + }, + licenseUrl: function () { + if (swagger.info.license && swagger.info.license.url) { + return swagger.info.license.url; + } else { + return ''; + } + } + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); + + // Create LICENSE file + var licenseSourceCode = CodeGen.getCustomCode({ + projectName: data.module, + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'LICENSE.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + licenseName: function () { + if (swagger.info.license && swagger.info.license.name) { + return swagger.info.license.name; + } else { + return 'Apache-2.0'; + } + }, + licenseUrl: function () { + if (swagger.info.license && swagger.info.license.url) { + return swagger.info.license.url; + } else { + return ''; + } + } + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'LICENSE'), licenseSourceCode); + + if (options.tgz) { + util.runNpmPack(data); + resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); + } else { + resolve(path.join(data.dst, data.module)); + } + }); +} + diff --git a/lib/util.js b/lib/util.js new file mode 100644 index 0000000..4fe5f95 --- /dev/null +++ b/lib/util.js @@ -0,0 +1,133 @@ +const fs = require('fs'); +const path = require('path'); + +const csv = require('csv-string'); +const child_process = require('child_process'); +const jimp = require("jimp"); + + +function createCommonFiles(templateDirectory, data) { + "use strict"; + // Make directories + try { + fs.mkdirSync(path.join(data.dst, data.module)); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error(error); + } + } + + var isStockIcon = data.icon && (data.icon.match(/^(alert|arduino|arrow-in|batch|bluetooth|bridge-dash|bridge|cog|comment|db|debug|envelope|feed|file-in|file-out|file|function|hash|inject|join|leveldb|light|link-out|mongodb|mouse|node-changed|node-error|parser-csv|parser-html|parser-json|parser-xml|parser-yaml|range|redis|rpi|serial|sort|split|subflow|swap|switch|template|timer|trigger|twitter|watch|white-globe)\.png$/) || data.icon.match(/^(node-red|font-awesome)/)); + if (!isStockIcon) { + try { + fs.mkdirSync(path.join(data.dst, data.module, 'icons')); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error(error); + } + } + } + if (data.icon) { + if (!isStockIcon) { + try { + jimp.read(data.icon, function (error2, image) { + if (!error2) { + var outputPath = path.join(data.dst, data.module, 'icons', path.basename(data.icon)); + if (image.bitmap.width === 40 && image.bitmap.height === 60) { + var buf = fs.readFileSync(data.icon); + fs.writeFileSync(outputPath, buf); + } else { + image.background(0xFFFFFFFF).resize(40, 60).write(outputPath); + } + } else { + console.log('error occurs while converting icon file.'); + } + }); + } catch (error) { + console.error(error); + } + } + } else { + var icons = fs.readdirSync(path.join(templateDirectory, 'icons')); + icons.forEach(function (icon) { + try { + var buf = fs.readFileSync(path.join(templateDirectory, 'icons', icon)); + fs.writeFileSync(path.join(data.dst, data.module, 'icons', icon), buf); + } catch (error) { + console.error(error); + } + }); + } + + try { + fs.mkdirSync(path.join(data.dst, data.module, 'locales')); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error(error); + } + } + try { + var languages = fs.readdirSync(path.join(templateDirectory, 'locales')); + languages.forEach(function (language) { + try { + fs.mkdirSync(path.join(data.dst, data.module, 'locales', language)); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error(error); + } + } + }); + } catch (error) { + if (error.code !== 'ENOENT') { + console.error(error); + } + } + try { + fs.mkdirSync(path.join(data.dst, data.module, 'examples')); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error(error); + } + } + try { + fs.mkdirSync(path.join(data.dst, data.module, 'test')); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error(error); + } + } + try { + var buf = fs.readFileSync(path.join(templateDirectory, '.travis.yml.mustache')); + fs.writeFileSync(path.join(data.dst, data.module, '.travis.yml'), buf); + } catch (error) { + if (error.code !== 'EEXIST') { + console.error(error); + } + } +} + +function runNpmPack(data) { + "use strict"; + var npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + try { + child_process.execFileSync(npmCommand, ['pack', './' + data.module], { cwd: data.dst }); + } catch (error) { + console.error(error); + } +} + +function extractKeywords(keywordsStr) { + "use strict"; + var keywords = ["node-red-nodegen"]; + keywords = keywordsStr ? keywords.concat(csv.parse(keywordsStr)[0]) : keywords; + keywords = keywords.map(k => ({ name: k })); + keywords[keywords.length - 1].last = true; + return keywords; +} + + +module.exports = { + createCommonFiles, + runNpmPack, + extractKeywords +} \ No newline at end of file diff --git a/lib/webofthings/index.js b/lib/webofthings/index.js new file mode 100644 index 0000000..f660894 --- /dev/null +++ b/lib/webofthings/index.js @@ -0,0 +1,195 @@ +const util = require("../util"); + +const fs = require('fs'); +const path = require('path'); +const mustache = require('mustache'); +const obfuscator = require('javascript-obfuscator'); + +const wotutils = require('./wotutils'); + +const TEMPLATE_DIR = path.join(__dirname,'../../templates/webofthings'); + +module.exports = function(data, options) { + return new Promise(function (resolve, reject) { + let td = data.src; + + // validate TD + const validateResult = wotutils.validateTd(td); + if (validateResult.result === false) { + console.warn(`Invalid Thing Description:\n${validateResult.errorText}`); + } else { + console.info(`Schema validation succeeded.`); + } + + // if name is not specified, use td.title for module name. + if (!data.name || data.name === '') { + // filtering out special characters + data.name = 'wot' + td.title.replace(/[^A-Za-z0-9]/g, '').toLowerCase(); + } + + if (data.module) { + if (data.prefix) { + reject('module name and prefix are conflicted'); + return; + } + } else { + if (data.prefix) { + data.module = data.prefix + data.name; + } else { + data.module = 'node-red-contrib-' + data.name; + } + } + + if (!data.version || data.version === '') { + if (td.version && td.version.instance) { + data.version = td.version.instance; + } else { + data.version = '0.0.1'; + } + } + + data.tdstr = JSON.stringify(td); + td = wotutils.normalizeTd(td); + td = wotutils.filterFormTd(td); + data.normtd = JSON.stringify(td); + data.properties = []; + const rwo = {}; + for (const p in td.properties) { // convert to array + if (td.properties.hasOwnProperty(p)) { + const q = td.properties[p]; + q.name = p; + if (!q.title || q.title === '') { + q.title = q.name; + } + if (q.forms) { + for (var i = 0; i < q.forms.length; i++) { + q.forms[i].index = i; + } + } + data.properties.push(q); + rwo[p] = { + readable: !q.writeOnly, + writable: !q.readOnly, + observable: q.observable + }; + + } + } + data.actions = []; + for (const a in td.actions) { + if (td.actions.hasOwnProperty(a)) { + const q = td.actions[a]; + q.name = a; + if (!q.title || q.title === '') { + q.title = q.name; + } + if (q.forms) { + for (var i = 0; i < q.forms.length; i++) { + q.forms[i].index = i; + } + } + data.actions.push(q); + } + } + data.events = []; + for (const e in td.events) { + if (td.events.hasOwnProperty(e)) { + const q = td.events[e]; + q.name = e; + if (!q.title || q.title === '') { + q.title = q.name; + } + if (q.forms) { + for (var i = 0; i < q.forms.length; i++) { + q.forms[i].index = i; + } + } + data.events.push(q); + } + } + + const wotmeta = []; + if (td.hasOwnProperty('lastModified')) { + wotmeta.push({name: "lastModified", value: td.lastModified}); + } + if (td.hasOwnProperty('created')) { + wotmeta.push({name: "created", value: td.created}); + } + if (td.hasOwnProperty('support')) { + wotmeta.push({name: "support", value: JSON.stringify(td.support)}); + } + if (td.hasOwnProperty("id")) { + wotmeta.push({name: "id", value: td.id, last: true}); + } + + const formsel = wotutils.makeformsel(td); + + data.genformsel = JSON.stringify(formsel); + data.genproprwo = JSON.stringify(rwo); + + data.ufName = td.title.replace(/[^A-Za-z0-9]/g, ' ').trim(); + data.nodeName = data.name; + data.projectName = data.module; + data.projectVersion = data.version; + data.keywords = util.extractKeywords(data.keywords); + data.category = data.category || 'Web of Things'; + data.description = td.description; + data.licenseName = 'Apache-2.0'; + data.licenseUrl = ''; + data.links = td.links; + data.support = td.support; + data.iconpath = wotutils.woticon(td); + data.wotmeta = wotmeta; + + let lang = null; + if (td.hasOwnProperty('@context') && Array.isArray(td['@context'])) { + td['@context'].forEach(e => { + if (e.hasOwnProperty("@language")) { + lang = e['@language']; + } + }); + } + if (lang === null) { + data.textdir = "auto"; + } else { + data.textdir = wotutils.textDirection(lang); + } + + util.createCommonFiles(TEMPLATE_DIR, data); + + // Create package.json + const packageTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/package.json.mustache'), 'utf-8'); + const packageSourceCode = mustache.render(packageTemplate, data); + fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); + + // Create node.js + const nodeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/node.js.mustache'), 'utf-8'); + let nodeSourceCode = mustache.render(nodeTemplate, data); + if (options.obfuscate) { + nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); + } + fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); + + // Create node.html + const htmlTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/node.html.mustache'), 'utf-8'); + const htmlSourceCode = mustache.render(htmlTemplate, data); + fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); + + // Create README.html + const readmeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/README.md.mustache'), 'utf-8'); + const readmeSourceCode = mustache.render(readmeTemplate, data); + fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); + + // Create LICENSE + const licenseTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/LICENSE.mustache'), 'utf-8'); + const licenseSourceCode = mustache.render(licenseTemplate, data); + fs.writeFileSync(path.join(data.dst, data.module, 'LICENSE'), licenseSourceCode); + + if (options.tgz) { + util.runNpmPack(data); + resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); + } else { + resolve(path.join(data.dst, data.module)); + } + }); +} \ No newline at end of file diff --git a/lib/td-json-schema-validation.json b/lib/webofthings/td-json-schema-validation.json similarity index 100% rename from lib/td-json-schema-validation.json rename to lib/webofthings/td-json-schema-validation.json diff --git a/lib/wotutils.js b/lib/webofthings/wotutils.js similarity index 100% rename from lib/wotutils.js rename to lib/webofthings/wotutils.js diff --git a/package.json b/package.json index f370387..b37afb7 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "js-string-escape": "1.0.1", "minimist": "1.2.5", "mustache": "4.0.1", - "request": "2.88.2", "swagger-js-codegen-formdata": "0.15.5", "yamljs": "0.3.0" }, diff --git a/samples/swagger.json b/samples/swagger.json new file mode 100644 index 0000000..48b1e64 --- /dev/null +++ b/samples/swagger.json @@ -0,0 +1 @@ +{"swagger":"2.0","info":{"description":"This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.","version":"1.0.5","title":"Swagger Petstore","termsOfService":"http://swagger.io/terms/","contact":{"email":"apiteam@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}},"host":"petstore.swagger.io","basePath":"/v2","tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io"}},{"name":"store","description":"Access to Petstore orders"},{"name":"user","description":"Operations about user","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io"}}],"schemes":["https","http"],"paths":{"/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"uploads an image","description":"","operationId":"uploadFile","consumes":["multipart/form-data"],"produces":["application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"type":"integer","format":"int64"},{"name":"additionalMetadata","in":"formData","description":"Additional data to pass to server","required":false,"type":"string"},{"name":"file","in":"formData","description":"file to upload","required":false,"type":"file"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/ApiResponse"}}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet":{"post":{"tags":["pet"],"summary":"Add a new pet to the store","description":"","operationId":"addPet","consumes":["application/json","application/xml"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"put":{"tags":["pet"],"summary":"Update an existing pet","description":"","operationId":"updatePet","consumes":["application/json","application/xml"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"405":{"description":"Validation exception"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByStatus":{"get":{"tags":["pet"],"summary":"Finds Pets by status","description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","produces":["application/json","application/xml"],"parameters":[{"name":"status","in":"query","description":"Status values that need to be considered for filter","required":true,"type":"array","items":{"type":"string","enum":["available","pending","sold"],"default":"available"},"collectionFormat":"multi"}],"responses":{"200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},"400":{"description":"Invalid status value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags","description":"Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","produces":["application/json","application/xml"],"parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},"400":{"description":"Invalid tag value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}],"deprecated":true}},"/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID","description":"Returns a single pet","operationId":"getPetById","produces":["application/json","application/xml"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Pet"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"api_key":[]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data","description":"","operationId":"updatePetWithForm","consumes":["application/x-www-form-urlencoded"],"produces":["application/json","application/xml"],"parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"type":"integer","format":"int64"},{"name":"name","in":"formData","description":"Updated name of the pet","required":false,"type":"string"},{"name":"status","in":"formData","description":"Updated status of the pet","required":false,"type":"string"}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet","description":"","operationId":"deletePet","produces":["application/json","application/xml"],"parameters":[{"name":"api_key","in":"header","required":false,"type":"string"},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"type":"integer","format":"int64"}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet","description":"","operationId":"placeOrder","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"order placed for purchasing the pet","required":true,"schema":{"$ref":"#/definitions/Order"}}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid Order"}}}},"/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID","description":"For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions","operationId":"getOrderById","produces":["application/json","application/xml"],"parameters":[{"name":"orderId","in":"path","description":"ID of pet that needs to be fetched","required":true,"type":"integer","maximum":10,"minimum":1,"format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by ID","description":"For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors","operationId":"deleteOrder","produces":["application/json","application/xml"],"parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"type":"integer","minimum":1,"format":"int64"}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}}},"/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status","description":"Returns a map of status codes to quantities","operationId":"getInventory","produces":["application/json"],"parameters":[],"responses":{"200":{"description":"successful operation","schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"security":[{"api_key":[]}]}},"/user/createWithArray":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithArrayInput","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}},"/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithListInput","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}},"/user/{username}":{"get":{"tags":["user"],"summary":"Get user by user name","description":"","operationId":"getUserByName","produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing. ","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/User"}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}},"put":{"tags":["user"],"summary":"Updated user","description":"This can only be done by the logged in user.","operationId":"updateUser","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"path","description":"name that need to be updated","required":true,"type":"string"},{"in":"body","name":"body","description":"Updated user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"400":{"description":"Invalid user supplied"},"404":{"description":"User not found"}}},"delete":{"tags":["user"],"summary":"Delete user","description":"This can only be done by the logged in user.","operationId":"deleteUser","produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"type":"string"}],"responses":{"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}}},"/user/login":{"get":{"tags":["user"],"summary":"Logs user into the system","description":"","operationId":"loginUser","produces":["application/json","application/xml"],"parameters":[{"name":"username","in":"query","description":"The user name for login","required":true,"type":"string"},{"name":"password","in":"query","description":"The password for login in clear text","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","headers":{"X-Expires-After":{"type":"string","format":"date-time","description":"date in UTC when token expires"},"X-Rate-Limit":{"type":"integer","format":"int32","description":"calls per hour allowed by the user"}},"schema":{"type":"string"}},"400":{"description":"Invalid username/password supplied"}}}},"/user/logout":{"get":{"tags":["user"],"summary":"Logs out current logged in user session","description":"","operationId":"logoutUser","produces":["application/json","application/xml"],"parameters":[],"responses":{"default":{"description":"successful operation"}}}},"/user":{"post":{"tags":["user"],"summary":"Create user","description":"This can only be done by the logged in user.","operationId":"createUser","consumes":["application/json"],"produces":["application/json","application/xml"],"parameters":[{"in":"body","name":"body","description":"Created user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"default":{"description":"successful operation"}}}}},"securityDefinitions":{"api_key":{"type":"apiKey","name":"api_key","in":"header"},"petstore_auth":{"type":"oauth2","authorizationUrl":"https://petstore.swagger.io/oauth/authorize","flow":"implicit","scopes":{"read:pets":"read your pets","write:pets":"modify pets in your account"}}},"definitions":{"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}}},"Category":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Category"}},"Pet":{"type":"object","required":["name","photoUrls"],"properties":{"id":{"type":"integer","format":"int64"},"category":{"$ref":"#/definitions/Category"},"name":{"type":"string","example":"doggie"},"photoUrls":{"type":"array","xml":{"wrapped":true},"items":{"type":"string","xml":{"name":"photoUrl"}}},"tags":{"type":"array","xml":{"wrapped":true},"items":{"xml":{"name":"tag"},"$ref":"#/definitions/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"Pet"}},"Tag":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Tag"}},"Order":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"petId":{"type":"integer","format":"int64"},"quantity":{"type":"integer","format":"int32"},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","enum":["placed","approved","delivered"]},"complete":{"type":"boolean"}},"xml":{"name":"Order"}},"User":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"username":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"phone":{"type":"string"},"userStatus":{"type":"integer","format":"int32","description":"User Status"}},"xml":{"name":"User"}}},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"}} \ No newline at end of file diff --git a/test/lib/function_spec.js b/test/lib/function_spec.js new file mode 100644 index 0000000..1229019 --- /dev/null +++ b/test/lib/function_spec.js @@ -0,0 +1,106 @@ +const fs = require('fs'); +const path = require('path'); +const should = require('should'); +const del = require('del'); +const function2node = require('../../lib/function'); + +describe('Function node', function () { + it('should have node files', function (done) { + var options = {}; + var data = { dst: '.' }; + data.src = fs.readFileSync('samples/lower-case.js'); + function2node(data, options).then(function (result) { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-contrib-lowercase'); + packageSourceCode.version.should.equal('0.0.1'); + fs.statSync(result + '/node.html').size.should.be.above(0); + fs.statSync(result + '/node.js').size.should.be.above(0); + fs.statSync(result + '/test/node_spec.js').size.should.be.above(0); + fs.statSync(result + '/icons/icon.svg').size.should.be.above(0); + fs.statSync(result + '/README.md').size.should.be.above(0); + fs.statSync(result + '/LICENSE').size.should.be.above(0); + fs.statSync(result + '/.travis.yml').size.should.be.above(0); + del.sync(result); + done(); + }); + }); + it('should handle parameters (node and module name)', function (done) { + var options = {}; + var data = { + name: 'nodename', + module: 'node-red-node-function-node', + dst: '.' + }; + data.src = fs.readFileSync('samples/lower-case.js'); + function2node(data, options).then(function (result) { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-node-function-node'); + packageSourceCode.version.should.equal('0.0.1'); + del.sync(result); + done(); + }); + }); + it('should handle parameters (prefix and node name)', function (done) { + var options = {}; + var data = { + prefix: 'node-red-prefix-', + name: 'nodename', + dst: '.' + }; + data.src = fs.readFileSync('samples/lower-case.js'); + function2node(data, options).then(function (result) { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-prefix-nodename'); + packageSourceCode.version.should.equal('0.0.1'); + del.sync(result); + done(); + }); + }); + it('should handle parameters (version)', function (done) { + var options = {}; + var data = { + version: '4.5.1', + dst: '.' + }; + data.src = fs.readFileSync('samples/lower-case.js'); + function2node(data, options).then(function (result) { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-contrib-lowercase'); + packageSourceCode.version.should.equal('4.5.1'); + del.sync(result); + done(); + }); + }); + it('should handle parameters (keywords)', function (done) { + var options = {}; + var data = { + keywords: 'node-red,function,lowercase', + dst: '.' + }; + data.src = fs.readFileSync('samples/lower-case.js'); + function2node(data, options).then(function (result) { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-contrib-lowercase'); + packageSourceCode.keywords.should.eql(['node-red-nodegen', 'node-red', 'function', 'lowercase']); + del.sync(result); + done(); + }); + }); + it('should handle options', function (done) { + var options = { + tgz: true, + obfuscate: true + }; + var data = { dst: '.' }; + data.src = fs.readFileSync('samples/lower-case.js'); + function2node(data, options).then(function (result) { + fs.statSync(result).isFile().should.be.eql(true); + del.sync(result); + var jsfile = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, '/node.js'); + fs.readFileSync(jsfile).toString().split('\n').length.should.be.eql(1); + result = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, ''); + del.sync(result); + done(); + }); + }); +}); diff --git a/test/lib/nodegen_spec.js b/test/lib/nodegen_spec.js index 343d998..f1cf745 100644 --- a/test/lib/nodegen_spec.js +++ b/test/lib/nodegen_spec.js @@ -1,201 +1,5 @@ -var fs = require('fs'); -var request = require('request'); -var should = require('should'); -var del = require('del'); -var nodegen = require('../../lib/nodegen'); +const nodegen = require('../../lib/nodegen'); describe('nodegen library', function () { - describe('function node', function () { - it('should have node files', function (done) { - var options = {}; - var data = { dst: '.' }; - data.src = fs.readFileSync('samples/lower-case.js'); - nodegen.function2node(data, options).then(function (result) { - var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); - packageSourceCode.name.should.equal('node-red-contrib-lowercase'); - packageSourceCode.version.should.equal('0.0.1'); - fs.statSync(result + '/node.html').size.should.be.above(0); - fs.statSync(result + '/node.js').size.should.be.above(0); - fs.statSync(result + '/test/node_spec.js').size.should.be.above(0); - fs.statSync(result + '/icons/icon.svg').size.should.be.above(0); - fs.statSync(result + '/README.md').size.should.be.above(0); - fs.statSync(result + '/LICENSE').size.should.be.above(0); - fs.statSync(result + '/.travis.yml').size.should.be.above(0); - del.sync(result); - done(); - }); - }); - it('should handle parameters (node and module name)', function (done) { - var options = {}; - var data = { - name: 'nodename', - module: 'node-red-node-function-node', - dst: '.' - }; - data.src = fs.readFileSync('samples/lower-case.js'); - nodegen.function2node(data, options).then(function (result) { - var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); - packageSourceCode.name.should.equal('node-red-node-function-node'); - packageSourceCode.version.should.equal('0.0.1'); - del.sync(result); - done(); - }); - }); - it('should handle parameters (prefix and node name)', function (done) { - var options = {}; - var data = { - prefix: 'node-red-prefix-', - name: 'nodename', - dst: '.' - }; - data.src = fs.readFileSync('samples/lower-case.js'); - nodegen.function2node(data, options).then(function (result) { - var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); - packageSourceCode.name.should.equal('node-red-prefix-nodename'); - packageSourceCode.version.should.equal('0.0.1'); - del.sync(result); - done(); - }); - }); - it('should handle parameters (version)', function (done) { - var options = {}; - var data = { - version: '4.5.1', - dst: '.' - }; - data.src = fs.readFileSync('samples/lower-case.js'); - nodegen.function2node(data, options).then(function (result) { - var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); - packageSourceCode.name.should.equal('node-red-contrib-lowercase'); - packageSourceCode.version.should.equal('4.5.1'); - del.sync(result); - done(); - }); - }); - it('should handle parameters (keywords)', function (done) { - var options = {}; - var data = { - keywords: 'node-red,function,lowercase', - dst: '.' - }; - data.src = fs.readFileSync('samples/lower-case.js'); - nodegen.function2node(data, options).then(function (result) { - var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); - packageSourceCode.name.should.equal('node-red-contrib-lowercase'); - packageSourceCode.keywords.should.eql(['node-red-nodegen', 'node-red', 'function', 'lowercase']); - del.sync(result); - done(); - }); - }); - it('should handle options', function (done) { - var options = { - tgz: true, - obfuscate: true - }; - var data = { dst: '.' }; - data.src = fs.readFileSync('samples/lower-case.js'); - nodegen.function2node(data, options).then(function (result) { - fs.statSync(result).isFile().should.be.eql(true); - del.sync(result); - var jsfile = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, '/node.js'); - fs.readFileSync(jsfile).toString().split('\n').length.should.be.eql(1); - result = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, ''); - del.sync(result); - done(); - }); - }); - }); - - describe('swagger node', function () { - it('should have node files', function (done) { - var options = {}; - var data = { dst: '.' }; - var sourcePath = 'http://petstore.swagger.io/v2/swagger.json'; - request(sourcePath, function (error, response, body) { - data.src = JSON.parse(body); - nodegen.swagger2node(data, options).then(function (result) { - var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); - packageSourceCode.name.should.equal('node-red-contrib-swagger-petstore'); - packageSourceCode.version.should.equal('1.0.5'); - packageSourceCode.license.should.equal('Apache 2.0'); - fs.statSync(result + '/node.html').size.should.be.above(0); - fs.statSync(result + '/node.js').size.should.be.above(0); - fs.statSync(result + '/lib.js').size.should.be.above(0); - fs.statSync(result + '/test/node_spec.js').size.should.be.above(0); - fs.statSync(result + '/icons/icon.png').size.should.be.above(0); - fs.statSync(result + '/locales/en-US/node.json').size.should.be.above(0); - fs.statSync(result + '/locales/ja/node.json').size.should.be.above(0); - fs.statSync(result + '/locales/zh-CN/node.json').size.should.be.above(0); - fs.statSync(result + '/README.md').size.should.be.above(0); - fs.statSync(result + '/LICENSE').size.should.be.above(0); - fs.statSync(result + '/.travis.yml').size.should.be.above(0); - del.sync(result); - done(); - }); - }); - }); - it('should handle options', function (done) { - var options = { - tgz: true, - obfuscate: true - }; - var data = { dst: '.' }; - var sourcePath = 'http://petstore.swagger.io/v2/swagger.json'; - request(sourcePath, function (error, response, body) { - data.src = JSON.parse(body); - nodegen.swagger2node(data, options).then(function (result) { - fs.statSync(result).isFile().should.be.eql(true); - del.sync(result); - var jsfile = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, '/node.js'); - fs.readFileSync(jsfile).toString().split('\n').length.should.be.eql(1); - result = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, ''); - del.sync(result); - done(); - }); - }); - }); - }); - - describe('Web of Things node', function () { - it('should have node files', function (done) { - const sourcePath = 'samples/MyLampThing.jsonld'; - const data = { - src: JSON.parse(fs.readFileSync(sourcePath)), - dst: '.' - }; - const options = {}; - nodegen.wottd2node(data, options).then(function (result) { - const packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); - packageSourceCode.name.should.equal('node-red-contrib-wotmylampthing'); - packageSourceCode.version.should.equal('0.0.1'); - fs.statSync(result + '/node.html').size.should.be.above(0); - fs.statSync(result + '/node.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(); - }); - }); - it('should handle options', function (done) { - const sourcePath = 'samples/MyLampThing.jsonld'; - const data = { - src: JSON.parse(fs.readFileSync(sourcePath)), - dst: '.' - }; - const options = { - tgz: true, - obfuscate: true - }; - nodegen.wottd2node(data, options).then(function (result) { - fs.statSync(result).isFile().should.be.eql(true); - del.sync(result); - var jsfile = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, '/node.js'); - fs.readFileSync(jsfile).toString().split('\n').length.should.be.eql(1); - result = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, ''); - del.sync(result); - done(); - }); - }); - }); }); diff --git a/test/lib/swagger_spec.js b/test/lib/swagger_spec.js new file mode 100644 index 0000000..0bc417d --- /dev/null +++ b/test/lib/swagger_spec.js @@ -0,0 +1,52 @@ +var fs = require('fs'); +var path = require('path'); +var should = require('should'); +var del = require('del'); +var swagger2node = require('../../lib/swagger'); + +describe('swagger node', function () { + var swaggerDoc; + before(function() { + swaggerDoc = JSON.parse(fs.readFileSync(path.join(__dirname,"../../samples/swagger.json"))); + }); + + it('should have node files', function (done) { + var options = {}; + var data = { src: swaggerDoc, dst: '.' }; + swagger2node(data, options).then(function (result) { + var packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-contrib-swagger-petstore'); + packageSourceCode.version.should.equal('1.0.5'); + packageSourceCode.license.should.equal('Apache 2.0'); + fs.statSync(result + '/node.html').size.should.be.above(0); + fs.statSync(result + '/node.js').size.should.be.above(0); + fs.statSync(result + '/lib.js').size.should.be.above(0); + fs.statSync(result + '/test/node_spec.js').size.should.be.above(0); + fs.statSync(result + '/icons/icon.png').size.should.be.above(0); + fs.statSync(result + '/locales/en-US/node.json').size.should.be.above(0); + fs.statSync(result + '/locales/ja/node.json').size.should.be.above(0); + fs.statSync(result + '/locales/zh-CN/node.json').size.should.be.above(0); + fs.statSync(result + '/README.md').size.should.be.above(0); + fs.statSync(result + '/LICENSE').size.should.be.above(0); + fs.statSync(result + '/.travis.yml').size.should.be.above(0); + del.sync(result); + done(); + }); + }); + it('should handle options', function (done) { + var options = { + tgz: true, + obfuscate: true + }; + var data = { src: swaggerDoc, dst: '.' }; + swagger2node(data, options).then(function (result) { + fs.statSync(result).isFile().should.be.eql(true); + del.sync(result); + var jsfile = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, '/node.js'); + fs.readFileSync(jsfile).toString().split('\n').length.should.be.eql(1); + result = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, ''); + del.sync(result); + done(); + }); + }); +}); \ No newline at end of file diff --git a/test/lib/webofthings_spec.js b/test/lib/webofthings_spec.js new file mode 100644 index 0000000..7ad32d5 --- /dev/null +++ b/test/lib/webofthings_spec.js @@ -0,0 +1,48 @@ +var fs = require('fs'); +var path = require('path'); +var should = require('should'); +var del = require('del'); +var wottd2node = require('../../lib/webofthings'); + +describe('Web of Things node', function () { + it('should have node files', function (done) { + const sourcePath = 'samples/MyLampThing.jsonld'; + const data = { + src: JSON.parse(fs.readFileSync(sourcePath)), + dst: '.' + }; + const options = {}; + wottd2node(data, options).then(function (result) { + const packageSourceCode = JSON.parse(fs.readFileSync(result + '/package.json')); + packageSourceCode.name.should.equal('node-red-contrib-wotmylampthing'); + packageSourceCode.version.should.equal('0.0.1'); + fs.statSync(result + '/node.html').size.should.be.above(0); + fs.statSync(result + '/node.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(); + }); + }); + it('should handle options', function (done) { + const sourcePath = 'samples/MyLampThing.jsonld'; + const data = { + src: JSON.parse(fs.readFileSync(sourcePath)), + dst: '.' + }; + const options = { + tgz: true, + obfuscate: true + }; + wottd2node(data, options).then(function (result) { + fs.statSync(result).isFile().should.be.eql(true); + del.sync(result); + var jsfile = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, '/node.js'); + fs.readFileSync(jsfile).toString().split('\n').length.should.be.eql(1); + result = result.replace(/-[0-9]+\.[0-9]+\.[0-9]+\.tgz$/, ''); + del.sync(result); + done(); + }); + }); +}); + From a6f226a46019d0d765080187a1cfd31378341b4c Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 1 Sep 2020 17:31:27 +0100 Subject: [PATCH 4/7] Remove mocha-istanbul as it does not support es2015 --- Gruntfile.js | 3 +-- package.json | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index a993350..6a278d8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,7 +28,6 @@ module.exports = function (grunt) { grunt.file.mkdir('nodegen'); grunt.loadNpmTasks('grunt-shell'); grunt.loadNpmTasks('grunt-simple-mocha'); - grunt.loadNpmTasks('grunt-mocha-istanbul'); - grunt.registerTask('default', ['shell', 'mocha_istanbul:all']); + grunt.registerTask('default', ['shell', 'simplemocha']); grunt.registerTask('coverage', 'Run Istanbul code test coverage task', ['shell', 'mocha_istanbul:all']); }; diff --git a/package.json b/package.json index b37afb7..5e414c9 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "devDependencies": { "del": "5.1.0", "grunt": "1.0.4", - "grunt-mocha-istanbul": "5.0.2", "grunt-shell": "3.0.1", "grunt-simple-mocha": "0.4.1", "istanbul": "0.4.5", From ca9e5770dccca6c7104b76ea8adfb2652179c369 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 1 Sep 2020 17:38:12 +0100 Subject: [PATCH 5/7] Move more generator specific logic into individual modules --- .gitignore | 4 +- Gruntfile.js | 8 +- bin/node-red-nodegen.js | 135 +++------ lib/function/index.js | 260 +++++++++--------- lib/swagger/index.js | 576 +++++++++++++++++++++------------------ lib/util.js | 12 +- lib/webofthings/index.js | 346 +++++++++++------------ package.json | 1 + 8 files changed, 669 insertions(+), 673 deletions(-) diff --git a/.gitignore b/.gitignore index 0ea1ba5..756e971 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ # Generated files -node-red-contrib-* -node-red-node-* -./nodegen +nodegen/* # Dependency directories node_modules diff --git a/Gruntfile.js b/Gruntfile.js index 6a278d8..09c9ac5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,11 +1,14 @@ module.exports = function (grunt) { grunt.initConfig({ shell: { - generateNode_lowerCase: { + generateNode_Function: { command: 'node bin/node-red-nodegen.js samples/lower-case.js -o ./nodegen' }, - generateNode_swaggerPetstore: { + generateNode_Swagger: { command: 'node bin/node-red-nodegen.js samples/swagger.json -o ./nodegen' + }, + generateNode_WebOfThings: { + command: 'node bin/node-red-nodegen.js samples/MyLampThing.jsonld -o ./nodegen' } }, simplemocha: { @@ -29,5 +32,4 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-shell'); grunt.loadNpmTasks('grunt-simple-mocha'); grunt.registerTask('default', ['shell', 'simplemocha']); - grunt.registerTask('coverage', 'Run Istanbul code test coverage task', ['shell', 'mocha_istanbul:all']); }; diff --git a/bin/node-red-nodegen.js b/bin/node-red-nodegen.js index b163c73..d9ff2bf 100755 --- a/bin/node-red-nodegen.js +++ b/bin/node-red-nodegen.js @@ -19,10 +19,9 @@ var fs = require('fs'); var path = require('path'); var request = require('request'); -var yamljs = require('yamljs'); + var argv = require('minimist')(process.argv.slice(2)); var colors = require('colors'); -var Converter = require('api-spec-converter'); var nodegen = require('../lib/nodegen.js'); // Command options @@ -39,7 +38,8 @@ var data = { category: argv.category || argv.c, icon: argv.icon, color: argv.color, - dst: argv.output || argv.o || '.' + dst: argv.output || argv.o || '.', + lang: argv.lang }; function help() { @@ -108,115 +108,42 @@ if (argv.help || argv.h) { } else if (argv.v) { version(); } else { + var promise; var sourcePath = argv._[0]; if (sourcePath) { - if (!argv.wottd && (sourcePath.startsWith('http://') || sourcePath.startsWith('https://'))) { - request(sourcePath, function (error, response, body) { - if (!error) { - data.src = JSON.parse(body); - Converter.convert({ - from: data.src.openapi && data.src.openapi.startsWith('3.0') ? 'openapi_3' : 'swagger_2', - to: 'swagger_2', - source: data.src, - }).then(function (convertedData) { - data.src = convertedData.spec; - nodegen.swagger2node(data, options).then(function (result) { - console.log('Success: ' + result); - }).catch(function (error) { - console.log('Error: ' + error); - }); - }); - } else { - console.error(error); - } - }); - } else if (argv.wottd && (sourcePath.startsWith('http://') || sourcePath.startsWith('https://'))) { - const req = { - url: sourcePath, - } - if (argv.lang) { - req.headers = { - 'Accept-Language': argv.lang - } - } - request(req, function (error, response, body) { - if (!error) { - data.src = JSON.parse(skipBom(body)); - nodegen.wottd2node(data, options).then(function (result) { - console.log('Success: ' + result); - }).catch(function (error) { - console.log('Error: ' + error); - }); - } else { - console.error(error); - } - }); - } else if (sourcePath.endsWith('.json') && !argv.wottd) { - data.src = JSON.parse(fs.readFileSync(sourcePath)); - // if it's a .json flow file with one function node in... - if (Array.isArray(data.src) && data.src[0].hasOwnProperty("type") && data.src[0].type == "function") { - var f = data.src[0]; - if (!f.name || f.name.length ==0) { console.log('Error: No function name supplied.'); return; } - data.name = f.name.toLowerCase(); - data.icon = f.icon; - data.info = f.info; - data.outputs = f.outputs; - data.inputLabels = f.inputLabels; - data.outputLabels = f.outputLabels; - data.src = Buffer.from(f.func); - nodegen.function2node(data, options).then(function (result) { - console.log('Success: ' + result); - }).catch(function (error) { - console.log('Error: ' + error); - }); + data.src = sourcePath; + if (argv.wottd || /\.jsonld$/.test(sourcePath)) { + // Explicitly a Web Of Things request + promise = nodegen.wottd2node(data, options); + } else if (/^https?:/.test(sourcePath) || /.yaml$/.test(sourcePath)) { + // URL/yaml -> swagger + promise = nodegen.swagger2node(data, options); + } else if (/\.json$/.test(sourcePath)) { + // JSON could be a Function node, or Swagger + var content = JSON.parse(fs.readFileSync(sourcePath)); + if (Array.isArray(content)) { + data.src = content; + promise = nodegen.function2node(data, options); + } else { + promise = nodegen.swagger2node(data, options); } - else { - Converter.convert({ - from: data.src.openapi && data.src.openapi.startsWith('3.0') ? 'openapi_3' : 'swagger_2', - to: 'swagger_2', - source: data.src, - }).then(function (convertedData) { - data.src = convertedData.spec; - nodegen.swagger2node(data, options).then(function (result) { - console.log('Success: ' + result); - }).catch(function (error) { - console.log('Error: ' + error); - }); - }); - } - } else if (sourcePath.endsWith('.yaml')) { - data.src = yamljs.load(sourcePath); - console.log(JSON.stringify(data.src, null, 4)); // hoge - Converter.convert({ - from: data.src.openapi && data.src.openapi.startsWith('3.0') ? 'openapi_3' : 'swagger_2', - to: 'swagger_2', - source: data.src, - }).then(function (convertedData) { - data.src = convertedData.spec; - nodegen.swagger2node(data, options).then(function (result) { - console.log('Success: ' + result); - }).catch(function (error) { - console.log('Error: ' + error); - }); - }); - } else if (sourcePath.endsWith('.js')) { - data.src = fs.readFileSync(sourcePath); - nodegen.function2node(data, options).then(function (result) { - console.log('Success: ' + result); - }).catch(function (error) { - console.log('Error: ' + error); - }); - } else if (sourcePath.endsWith('.jsonld') || argv.wottd) { - data.src = JSON.parse(skipBom(fs.readFileSync(sourcePath))); - nodegen.wottd2node(data, options).then(function (result) { + } else if (/\.js$/.test(sourcePath)) { + // .js -> Function node + promise = nodegen.function2node(data, options); + } else { + console.error('error: Unsupported file type'); + help(); + return; + } + if (promise) { + promise.then(function (result) { console.log('Success: ' + result); }).catch(function (error) { console.log('Error: ' + error); + console.log(error.stack); }); - } else { - console.error('error: Unsupported file type'); } } else { help(); } -} +} \ No newline at end of file diff --git a/lib/function/index.js b/lib/function/index.js index 87090da..f9212ae 100644 --- a/lib/function/index.js +++ b/lib/function/index.js @@ -8,143 +8,155 @@ const obfuscator = require('javascript-obfuscator'); const TEMPLATE_DIR = path.join(__dirname,'../../templates/function'); -module.exports = function(data, options) { - // console.log("OPT",options); - // console.log("DATA",data); + +module.exports = async function(data, options) { "use strict"; - return new Promise(function (resolve, reject) { - // Read meta data in js file - var meta = {}; - var parts = new String(data.src).split('\n'); - parts.forEach(function (part) { - var match = /^\/\/ (\w+): (.*)/.exec(part.toString()); - if (match) { - if (match[1] === 'name') { - meta.name = match[2].replace(/([A-Z])/g, ' $1').toLowerCase().replace(/[^ a-z0-9]+/g, '').replace(/^ | $/, '').replace(/ +/g, '-'); - } else { - meta[match[1]] = match[2]; - } - } - }); - if (!data.name || data.name === '') { - data.name = meta.name; + if (Array.isArray(data.src)) { + if (data.src.length !== 1) { + throw new Error("Invalid flow json - expected a single Function node"); } - - if (data.module) { - if (data.prefix) { - reject('module name and prefix are conflicted.'); - return; - } - } else { - if (data.prefix) { - data.module = data.prefix + data.name; + var f = data.src[0]; + if (!f.name || f.name.length ==0) { + throw new Error('Error: No function name supplied.'); + } + data.name = f.name.toLowerCase(); + data.icon = f.icon; + data.info = f.info; + data.outputs = f.outputs; + data.inputLabels = f.inputLabels; + data.outputLabels = f.outputLabels; + data.src = Buffer.from(f.func); + } else if (typeof data.src === "string" && /\.js$/.test(data.src)) { + data.src = await util.skipBom(await fs.promises.readFile(data.src)); + } + + // Read meta data in js file + var meta = {}; + var parts = new String(data.src).split('\n'); + parts.forEach(function (part) { + var match = /^\/\/ (\w+): (.*)/.exec(part.toString()); + if (match) { + if (match[1] === 'name') { + meta.name = match[2].replace(/([A-Z])/g, ' $1').toLowerCase().replace(/[^ a-z0-9]+/g, '').replace(/^ | $/, '').replace(/ +/g, '-'); } else { - data.module = 'node-red-contrib-' + data.name; + meta[match[1]] = match[2]; } } + }); - if (!data.version || data.version === '') { - data.version = '0.0.1'; - } + if (!data.name || data.name === '') { + data.name = meta.name; + } - if (data.icon) { - if (!data.icon.match(/\.(png|gif)$/) && !data.icon.match(/^(node-red|font-awesome)/)) { - data.icon = data.icon + '.png'; - } - if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { - reject('invalid icon file name'); - return; - } + if (data.module) { + if (data.prefix) { + throw new Error('module name and prefix are conflicted.'); + } + } else { + if (data.prefix) { + data.module = data.prefix + data.name; + } else { + data.module = 'node-red-contrib-' + data.name; } + } - if (data.color) { - if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { - data.color = '#' + data.color; - } else { - reject('invalid color'); - return; - } + if (!data.version || data.version === '') { + data.version = '0.0.1'; + } + + if (data.icon) { + if (!data.icon.match(/\.(png|gif)$/) && !data.icon.match(/^(node-red|font-awesome)/)) { + data.icon = data.icon + '.png'; + } + if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { + throw new Error('invalid icon file name'); } + } - if (data.name === 'function') { - reject('\'function\' is duplicated node name. Use another name.'); - return; - } else if (!data.name.match(/^[a-z0-9\-]+$/)) { - reject('invalid node name'); - return; + if (data.color) { + if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { + data.color = '#' + data.color; } else { - var params = { - nodeName: data.name, - projectName: data.module, - projectVersion: data.version, - keywords: util.extractKeywords(data.keywords), - category: data.category || 'function', - icon: function () { - if (data.icon ) { - if (!data.icon.match(/^(node-red|font-awesome)/)) { - return path.basename(data.icon); - } - else { return data.icon; } - } else { - return 'icon.png'; + throw new Error('invalid color'); + } + } + + if (data.name === 'function') { + throw new Error('\'function\' is duplicated node name. Use another name.'); + } else if (!data.name.match(/^[a-z0-9\-]+$/)) { + throw new Error('invalid node name'); + } else { + var params = { + nodeName: data.name, + projectName: data.module, + projectVersion: data.version, + keywords: util.extractKeywords(data.keywords), + category: data.category || 'function', + icon: function () { + if (data.icon ) { + if (!data.icon.match(/^(node-red|font-awesome)/)) { + return path.basename(data.icon); } - }, - color: data.color || '#C0DEED', - func: jsStringEscape(data.src), - outputs: meta.outputs || data.outputs, - inputLabels: JSON.stringify(data.inputLabels || []), - outputLabels: JSON.stringify(data.outputLabels || []), - nodeInfo: jsStringEscape(data.info || ""), - nodeRead: data.info || "" - }; - - util.createCommonFiles(TEMPLATE_DIR, data); - - // 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 node.js - var nodeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.js.mustache'), 'utf-8'); - var nodeSourceCode = mustache.render(nodeTemplate, params); - if (options.obfuscate) { - nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); - - // Create node.html - var htmlTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.html.mustache'), 'utf-8'); - var htmlSourceCode = mustache.render(htmlTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); - - // Create flow.json - var flowTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'examples/flow.json.mustache'), 'utf-8'); - var flowSourceCode = mustache.render(flowTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'examples/flow.json'), flowSourceCode); - - // Create node_spec.js - var nodeSpecTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'test/node_spec.js.mustache'), 'utf-8'); - var nodeSpecSourceCode = mustache.render(nodeSpecTemplate, params); - fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); - - // 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); - resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); - } else { - resolve(path.join(data.dst, data.module)); - } + else { return data.icon; } + } else { + return 'icon.png'; + } + }, + color: data.color || '#C0DEED', + func: jsStringEscape(data.src), + outputs: meta.outputs || data.outputs, + inputLabels: JSON.stringify(data.inputLabels || []), + outputLabels: JSON.stringify(data.outputLabels || []), + nodeInfo: jsStringEscape(data.info || ""), + nodeRead: data.info || "" + }; + + util.createCommonFiles(TEMPLATE_DIR, data); + + // 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 node.js + var nodeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.js.mustache'), 'utf-8'); + var nodeSourceCode = mustache.render(nodeTemplate, params); + if (options.obfuscate) { + nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); } - }); + fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); + + // Create node.html + var htmlTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.html.mustache'), 'utf-8'); + var htmlSourceCode = mustache.render(htmlTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); + + // Create flow.json + var flowTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'examples/flow.json.mustache'), 'utf-8'); + var flowSourceCode = mustache.render(flowTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, 'examples/flow.json'), flowSourceCode); + + // Create node_spec.js + var nodeSpecTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'test/node_spec.js.mustache'), 'utf-8'); + var nodeSpecSourceCode = mustache.render(nodeSpecTemplate, params); + fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); + + // 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/lib/swagger/index.js b/lib/swagger/index.js index 5eb540b..e7cbd39 100644 --- a/lib/swagger/index.js +++ b/lib/swagger/index.js @@ -5,329 +5,363 @@ const path = require('path'); const mustache = require('mustache'); const obfuscator = require('javascript-obfuscator'); const CodeGen = require('swagger-js-codegen-formdata').CodeGen; +const axios = require('axios'); +const yamljs = require('yamljs'); +const Converter = require('api-spec-converter'); const TEMPLATE_DIR = path.join(__dirname,'../../templates/swagger'); -module.exports = function(data, options) { - "use strict"; - return new Promise(function (resolve, reject) { - // Modify swagger data - var swagger = JSON.parse(JSON.stringify(data.src), function (key, value) { - if (key === 'consumes' || key === 'produces') { // Filter type of 'Content-Type' and 'Accept' in request header - if (value.indexOf('application/json') >= 0) { - return ['application/json']; - } else if (value.indexOf('application/xml') >= 0) { - return ['application/xml']; - } else if (value.indexOf('text/csv') >= 0) { - return ['text/csv']; - } else if (value.indexOf('text/plain') >= 0) { - return ['text/plain']; - } else if (value.indexOf('multipart/form-data') >= 0) { - return ['multipart/form-data']; - } else if (value.indexOf('application/octet-stream') >= 0) { - return ['application/octet-stream']; - } else { - return undefined; +async function getSpec(src, data) { + let spec; + if (typeof src === "string") { + if (/^https?:/.test(src)) { + let requestOptions = { + url: src + }; + if (data.lang) { + requestOptions.headers = { + 'Accept-Language': data.lang } - } else if (key === 'default') { // Handle swagger-js-codegen bug - return undefined; - } else if (key === 'operationId') { // Sanitize 'operationId' (remove special chars) - return value.replace(/(?!\w|\s)./g, ''); - } else { - return value; } - }); - - if (swagger.swagger !== '2.0') { - reject('unsupported version of OpenAPI document'); - return; + let response = await axios.get(requestOptions); + spec = response.data; + } else if (/\.yaml$/.test(src)) { + spec = yamljs.load(util.skipBom(await fs.promises.readFile(src))); + } else if (/\.json/.test(src)) { + spec = JSON.parse(util.skipBom(await fs.promises.readFile(src))); + } else { + throw new Error("Unsupported file type: "+src) } + } else { + spec = src; + } + return Converter.convert({ + from: spec.openapi && spec.openapi.startsWith('3.0') ? 'openapi_3' : 'swagger_2', + to: 'swagger_2', + source: spec, + }).then(res => res.spec) +} + +/* + * data.src : String : url or path to json/yaml file + * : Spec JSON + * Object : Spec Object + */ +module.exports = async function(data, options) { + "use strict"; - if (!swagger.info.title) { - reject('No title is specified in OpenAPI document'); - return; + let spec = await getSpec(data.src, data); + // Modify swagger data + var swagger = JSON.parse(JSON.stringify(spec), function (key, value) { + if (key === 'consumes' || key === 'produces') { // Filter type of 'Content-Type' and 'Accept' in request header + if (value.indexOf('application/json') >= 0) { + return ['application/json']; + } else if (value.indexOf('application/xml') >= 0) { + return ['application/xml']; + } else if (value.indexOf('text/csv') >= 0) { + return ['text/csv']; + } else if (value.indexOf('text/plain') >= 0) { + return ['text/plain']; + } else if (value.indexOf('multipart/form-data') >= 0) { + return ['multipart/form-data']; + } else if (value.indexOf('application/octet-stream') >= 0) { + return ['application/octet-stream']; + } else { + return undefined; + } + } else if (key === 'default') { // Handle swagger-js-codegen bug + return undefined; + } else if (key === 'operationId') { // Sanitize 'operationId' (remove special chars) + return value.replace(/(?!\w|\s)./g, ''); + } else { + return value; } + }); - var className = swagger.info.title.toLowerCase().replace(/(^|[^a-z0-9]+)[a-z0-9]/g, - function (str) { - return str.replace(/^[^a-z0-9]+/, '').toUpperCase(); - }); + if (swagger.swagger !== '2.0') { + throw new Error('unsupported version of OpenAPI document'); + } - if (!data.name || data.name === '') { - data.name = className.replace(/([A-Z])/g, '-$1').slice(1).toLowerCase(); - } + if (!swagger.info.title) { + throw new Error('No title is specified in OpenAPI document'); + } - if (data.module) { - if (data.prefix) { - reject('module name and prefix are conflicted.'); - return; - } + var className = swagger.info.title.toLowerCase().replace(/(^|[^a-z0-9]+)[a-z0-9]/g, + function (str) { + return str.replace(/^[^a-z0-9]+/, '').toUpperCase(); + }); + + if (!data.name || data.name === '') { + data.name = className.replace(/([A-Z])/g, '-$1').slice(1).toLowerCase(); + } + + if (data.module) { + if (data.prefix) { + throw new Error('module name and prefix are conflicted.'); + } + } else { + if (data.prefix) { + data.module = data.prefix + data.name; } else { - if (data.prefix) { - data.module = data.prefix + data.name; - } else { - data.module = 'node-red-contrib-' + data.name; - } + data.module = 'node-red-contrib-' + data.name; } + } - if (!data.version || data.version === '') { - if (swagger.info.version) { - var version = swagger.info.version.replace(/[^0-9\.]/g, ''); - if (version.match(/^[0-9]+$/)) { - data.version = version + '.0.0'; - } else if (version.match(/^[0-9]+\.[0-9]+$/)) { - data.version = version + '.0'; - } else if (version.match(/^[0-9]+\.[0-9]+\.[0-9]+$/)) { - data.version = version; - } else { - data.version = '0.0.1'; - } + if (!data.version || data.version === '') { + if (swagger.info.version) { + var version = swagger.info.version.replace(/[^0-9\.]/g, ''); + if (version.match(/^[0-9]+$/)) { + data.version = version + '.0.0'; + } else if (version.match(/^[0-9]+\.[0-9]+$/)) { + data.version = version + '.0'; + } else if (version.match(/^[0-9]+\.[0-9]+\.[0-9]+$/)) { + data.version = version; } else { data.version = '0.0.1'; } + } else { + data.version = '0.0.1'; } + } - if (data.icon) { - if (!data.icon.match(/\.(png|gif)$/)) { - data.icon = data.icon + '.png'; - } - if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { - reject('invalid icon file name'); - return; - } + if (data.icon) { + if (!data.icon.match(/\.(png|gif)$/)) { + data.icon = data.icon + '.png'; + } + if (!data.icon.match(/^[a-zA-Z0-9\-\./]+$/)) { + throw new Error('invalid icon file name'); } + } - if (data.color) { - if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { - data.color = '#' + data.color; - } else { - reject('invalid color'); - return; - } + if (data.color) { + if (data.color.match(/^[a-zA-Z0-9]{6}$/)) { + data.color = '#' + data.color; + } else { + throw new Error('invalid color'); } + } - util.createCommonFiles(TEMPLATE_DIR, data); + util.createCommonFiles(TEMPLATE_DIR, data); - // Create Node.js SDK - var nodejsSourceCode = CodeGen.getNodeCode({ - className: className, - swagger: swagger, - lint: false, - beautify: false - }); - if (options.obfuscate) { - nodejsSourceCode = obfuscator.obfuscate(nodejsSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'lib.js'), nodejsSourceCode); + // Create Node.js SDK + var nodejsSourceCode = CodeGen.getNodeCode({ + className: className, + swagger: swagger, + lint: false, + beautify: false + }); + if (options.obfuscate) { + nodejsSourceCode = obfuscator.obfuscate(nodejsSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); + } + fs.writeFileSync(path.join(data.dst, data.module, 'lib.js'), nodejsSourceCode); - // Create package.json - var packageSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(TEMPLATE_DIR, 'package.json.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - projectName: data.module, - projectVersion: data.version, - keywords: util.extractKeywords(data.keywords), - licenseName: function () { - if (swagger.info.license && swagger.info.license.name) { - return swagger.info.license.name; - } else { - return 'Apache-2.0'; - } - }, - projectAuthor: swagger.info.contact && swagger.info.contact.name ? swagger.info.contact.name : '' + // Create package.json + var packageSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'package.json.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + projectName: data.module, + projectVersion: data.version, + keywords: util.extractKeywords(data.keywords), + licenseName: function () { + if (swagger.info.license && swagger.info.license.name) { + return swagger.info.license.name; + } else { + return 'Apache-2.0'; + } }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); + projectAuthor: swagger.info.contact && swagger.info.contact.name ? swagger.info.contact.name : '' + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); - // Mustache helpers - var isNotBodyParam = function () { - return function (content, render) { - return render('{{camelCaseName}}') !== 'body' ? render(content) : ''; - }; + // Mustache helpers + var isNotBodyParam = function () { + return function (content, render) { + return render('{{camelCaseName}}') !== 'body' ? render(content) : ''; }; - var isBodyParam = function () { - return function (content, render) { - return render('{{camelCaseName}}') === 'body' ? render(content) : ''; - }; + }; + var isBodyParam = function () { + return function (content, render) { + return render('{{camelCaseName}}') === 'body' ? render(content) : ''; }; - var hasOptionalParams = function () { - return function (content, render) { - var params = render('{{#parameters}}{{^required}}{{camelCaseName}},{{/required}}{{/parameters}}'); - return params.split(',').filter(p => p).some(p => p !== 'body') ? render(content) : ''; - }; + }; + var hasOptionalParams = function () { + return function (content, render) { + var params = render('{{#parameters}}{{^required}}{{camelCaseName}},{{/required}}{{/parameters}}'); + return params.split(',').filter(p => p).some(p => p !== 'body') ? render(content) : ''; }; - var hasServiceParams = swagger.host === undefined || swagger.securityDefinitions !== undefined; + }; + var hasServiceParams = swagger.host === undefined || swagger.securityDefinitions !== undefined; - // Create node.js - var nodeSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(TEMPLATE_DIR, 'node.js.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - isBodyParam: isBodyParam, - isNotBodyParam: isNotBodyParam, - hasServiceParams: hasServiceParams - }, - lint: false, - beautify: false - }); - if (options.obfuscate) { - nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); + // Create node.js + var nodeSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'node.js.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + isBodyParam: isBodyParam, + isNotBodyParam: isNotBodyParam, + hasServiceParams: hasServiceParams + }, + lint: false, + beautify: false + }); + if (options.obfuscate) { + nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); + } + fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); - // Create node.html - var htmlSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(TEMPLATE_DIR, 'node.html.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - category: data.category || 'function', - icon: function () { - if (data.icon) { - return path.basename(data.icon); - } else { - return 'icon.png'; - } - }, - color: data.color || '#89bf04', - isNotBodyParam: isNotBodyParam, - hasOptionalParams: hasOptionalParams, - hasServiceParams: hasServiceParams + // Create node.html + var htmlSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'node.html.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + category: data.category || 'function', + icon: function () { + if (data.icon) { + return path.basename(data.icon); + } else { + return 'icon.png'; + } }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); - - // Create language files - var languages = fs.readdirSync(path.join(TEMPLATE_DIR, 'locales')); - languages.forEach(function (language) { - var languageFileSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(TEMPLATE_DIR, 'locales', language, 'node.json.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'locales', language, 'node.json'), - JSON.stringify(JSON.parse(languageFileSourceCode), null, 4)); - }); + color: data.color || '#89bf04', + isNotBodyParam: isNotBodyParam, + hasOptionalParams: hasOptionalParams, + hasServiceParams: hasServiceParams + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); - // Create node_spec.js - var nodeSpecSourceCode = CodeGen.getCustomCode({ + // Create language files + var languages = fs.readdirSync(path.join(TEMPLATE_DIR, 'locales')); + languages.forEach(function (language) { + var languageFileSourceCode = CodeGen.getCustomCode({ className: className, swagger: swagger, template: { - class: fs.readFileSync(path.join(TEMPLATE_DIR, 'test/node_spec.js.mustache'), 'utf-8'), + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'locales', language, 'node.json.mustache'), 'utf-8'), method: '', type: '' }, mustache: { nodeName: data.name, - projectName: data.module, - hasServiceParams: hasServiceParams }, lint: false, beautify: false }); - fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); + fs.writeFileSync(path.join(data.dst, data.module, 'locales', language, 'node.json'), + JSON.stringify(JSON.parse(languageFileSourceCode), null, 4)); + }); - // Create README.md - var readmeSourceCode = CodeGen.getCustomCode({ - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(TEMPLATE_DIR, 'README.md.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - nodeName: data.name, - projectName: data.module, - licenseName: function () { - if (swagger.info.license && swagger.info.license.name) { - return swagger.info.license.name; - } else { - return 'Apache-2.0'; - } - }, - licenseUrl: function () { - if (swagger.info.license && swagger.info.license.url) { - return swagger.info.license.url; - } else { - return ''; - } - } - }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); + // Create node_spec.js + var nodeSpecSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'test/node_spec.js.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, + projectName: data.module, + hasServiceParams: hasServiceParams + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'test/node_spec.js'), nodeSpecSourceCode); - // Create LICENSE file - var licenseSourceCode = CodeGen.getCustomCode({ + // Create README.md + var readmeSourceCode = CodeGen.getCustomCode({ + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'README.md.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + nodeName: data.name, projectName: data.module, - className: className, - swagger: swagger, - template: { - class: fs.readFileSync(path.join(TEMPLATE_DIR, 'LICENSE.mustache'), 'utf-8'), - method: '', - type: '' - }, - mustache: { - licenseName: function () { - if (swagger.info.license && swagger.info.license.name) { - return swagger.info.license.name; - } else { - return 'Apache-2.0'; - } - }, - licenseUrl: function () { - if (swagger.info.license && swagger.info.license.url) { - return swagger.info.license.url; - } else { - return ''; - } + licenseName: function () { + if (swagger.info.license && swagger.info.license.name) { + return swagger.info.license.name; + } else { + return 'Apache-2.0'; } }, - lint: false, - beautify: false - }); - fs.writeFileSync(path.join(data.dst, data.module, 'LICENSE'), licenseSourceCode); + licenseUrl: function () { + if (swagger.info.license && swagger.info.license.url) { + return swagger.info.license.url; + } else { + return ''; + } + } + }, + lint: false, + beautify: false + }); + fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); - if (options.tgz) { - util.runNpmPack(data); - resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); - } else { - resolve(path.join(data.dst, data.module)); - } + // Create LICENSE file + var licenseSourceCode = CodeGen.getCustomCode({ + projectName: data.module, + className: className, + swagger: swagger, + template: { + class: fs.readFileSync(path.join(TEMPLATE_DIR, 'LICENSE.mustache'), 'utf-8'), + method: '', + type: '' + }, + mustache: { + licenseName: function () { + if (swagger.info.license && swagger.info.license.name) { + return swagger.info.license.name; + } else { + return 'Apache-2.0'; + } + }, + licenseUrl: function () { + if (swagger.info.license && swagger.info.license.url) { + return swagger.info.license.url; + } else { + return ''; + } + } + }, + lint: false, + beautify: false }); + 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/lib/util.js b/lib/util.js index 4fe5f95..b25b2fc 100644 --- a/lib/util.js +++ b/lib/util.js @@ -125,9 +125,19 @@ function extractKeywords(keywordsStr) { return keywords; } +function skipBom(body) { + if (body[0]===0xEF && + body[1]===0xBB && + body[2]===0xBF) { + return body.slice(3); + } else { + return body; + } +} module.exports = { createCommonFiles, runNpmPack, - extractKeywords + extractKeywords, + skipBom } \ No newline at end of file diff --git a/lib/webofthings/index.js b/lib/webofthings/index.js index f660894..c6a1c92 100644 --- a/lib/webofthings/index.js +++ b/lib/webofthings/index.js @@ -9,187 +9,199 @@ const wotutils = require('./wotutils'); const TEMPLATE_DIR = path.join(__dirname,'../../templates/webofthings'); -module.exports = function(data, options) { - return new Promise(function (resolve, reject) { - let td = data.src; - - // validate TD - const validateResult = wotutils.validateTd(td); - if (validateResult.result === false) { - console.warn(`Invalid Thing Description:\n${validateResult.errorText}`); +async function getSpec(src) { + let spec; + if (typeof src === "string") { + if (/^https?:/.test(src)) { + spec = JSON.parse(util.skipBom(await axios.get(src))); } else { - console.info(`Schema validation succeeded.`); + spec = JSON.parse(util.skipBom(await fs.promises.readFile(src))); } - - // if name is not specified, use td.title for module name. - if (!data.name || data.name === '') { - // filtering out special characters - data.name = 'wot' + td.title.replace(/[^A-Za-z0-9]/g, '').toLowerCase(); + } else { + spec = src; + } + return spec; +} + +module.exports = async function(data, options) { + + let td = await getSpec(data.src); + + // validate TD + const validateResult = wotutils.validateTd(td); + if (validateResult.result === false) { + console.warn(`Invalid Thing Description:\n${validateResult.errorText}`); + } else { + console.info(`Schema validation succeeded.`); + } + + // if name is not specified, use td.title for module name. + if (!data.name || data.name === '') { + // filtering out special characters + data.name = 'wot' + td.title.replace(/[^A-Za-z0-9]/g, '').toLowerCase(); + } + + if (data.module) { + if (data.prefix) { + throw new Error('module name and prefix are conflicted'); } - - if (data.module) { - if (data.prefix) { - reject('module name and prefix are conflicted'); - return; - } + } else { + if (data.prefix) { + data.module = data.prefix + data.name; } else { - if (data.prefix) { - data.module = data.prefix + data.name; - } else { - data.module = 'node-red-contrib-' + data.name; - } + data.module = 'node-red-contrib-' + data.name; } + } - if (!data.version || data.version === '') { - if (td.version && td.version.instance) { - data.version = td.version.instance; - } else { - data.version = '0.0.1'; - } + if (!data.version || data.version === '') { + if (td.version && td.version.instance) { + data.version = td.version.instance; + } else { + data.version = '0.0.1'; } - - data.tdstr = JSON.stringify(td); - td = wotutils.normalizeTd(td); - td = wotutils.filterFormTd(td); - data.normtd = JSON.stringify(td); - data.properties = []; - const rwo = {}; - for (const p in td.properties) { // convert to array - if (td.properties.hasOwnProperty(p)) { - const q = td.properties[p]; - q.name = p; - if (!q.title || q.title === '') { - q.title = q.name; - } - if (q.forms) { - for (var i = 0; i < q.forms.length; i++) { - q.forms[i].index = i; - } - } - data.properties.push(q); - rwo[p] = { - readable: !q.writeOnly, - writable: !q.readOnly, - observable: q.observable - }; - + } + + data.tdstr = JSON.stringify(td); + td = wotutils.normalizeTd(td); + td = wotutils.filterFormTd(td); + data.normtd = JSON.stringify(td); + data.properties = []; + const rwo = {}; + for (const p in td.properties) { // convert to array + if (td.properties.hasOwnProperty(p)) { + const q = td.properties[p]; + q.name = p; + if (!q.title || q.title === '') { + q.title = q.name; } - } - data.actions = []; - for (const a in td.actions) { - if (td.actions.hasOwnProperty(a)) { - const q = td.actions[a]; - q.name = a; - if (!q.title || q.title === '') { - q.title = q.name; - } - if (q.forms) { - for (var i = 0; i < q.forms.length; i++) { - q.forms[i].index = i; - } + if (q.forms) { + for (var i = 0; i < q.forms.length; i++) { + q.forms[i].index = i; } - data.actions.push(q); } + data.properties.push(q); + rwo[p] = { + readable: !q.writeOnly, + writable: !q.readOnly, + observable: q.observable + }; + } - data.events = []; - for (const e in td.events) { - if (td.events.hasOwnProperty(e)) { - const q = td.events[e]; - q.name = e; - if (!q.title || q.title === '') { - q.title = q.name; - } - if (q.forms) { - for (var i = 0; i < q.forms.length; i++) { - q.forms[i].index = i; - } + } + data.actions = []; + for (const a in td.actions) { + if (td.actions.hasOwnProperty(a)) { + const q = td.actions[a]; + q.name = a; + if (!q.title || q.title === '') { + q.title = q.name; + } + if (q.forms) { + for (var i = 0; i < q.forms.length; i++) { + q.forms[i].index = i; } - data.events.push(q); } + data.actions.push(q); } - - const wotmeta = []; - if (td.hasOwnProperty('lastModified')) { - wotmeta.push({name: "lastModified", value: td.lastModified}); - } - if (td.hasOwnProperty('created')) { - wotmeta.push({name: "created", value: td.created}); - } - if (td.hasOwnProperty('support')) { - wotmeta.push({name: "support", value: JSON.stringify(td.support)}); - } - if (td.hasOwnProperty("id")) { - wotmeta.push({name: "id", value: td.id, last: true}); - } - - const formsel = wotutils.makeformsel(td); - - data.genformsel = JSON.stringify(formsel); - data.genproprwo = JSON.stringify(rwo); - - data.ufName = td.title.replace(/[^A-Za-z0-9]/g, ' ').trim(); - data.nodeName = data.name; - data.projectName = data.module; - data.projectVersion = data.version; - data.keywords = util.extractKeywords(data.keywords); - data.category = data.category || 'Web of Things'; - data.description = td.description; - data.licenseName = 'Apache-2.0'; - data.licenseUrl = ''; - data.links = td.links; - data.support = td.support; - data.iconpath = wotutils.woticon(td); - data.wotmeta = wotmeta; - - let lang = null; - if (td.hasOwnProperty('@context') && Array.isArray(td['@context'])) { - td['@context'].forEach(e => { - if (e.hasOwnProperty("@language")) { - lang = e['@language']; + } + data.events = []; + for (const e in td.events) { + if (td.events.hasOwnProperty(e)) { + const q = td.events[e]; + q.name = e; + if (!q.title || q.title === '') { + q.title = q.name; + } + if (q.forms) { + for (var i = 0; i < q.forms.length; i++) { + q.forms[i].index = i; } - }); - } - if (lang === null) { - data.textdir = "auto"; - } else { - data.textdir = wotutils.textDirection(lang); - } - - util.createCommonFiles(TEMPLATE_DIR, data); - - // Create package.json - const packageTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/package.json.mustache'), 'utf-8'); - const packageSourceCode = mustache.render(packageTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); - - // Create node.js - const nodeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/node.js.mustache'), 'utf-8'); - let nodeSourceCode = mustache.render(nodeTemplate, data); - if (options.obfuscate) { - nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); - } - fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); - - // Create node.html - const htmlTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/node.html.mustache'), 'utf-8'); - const htmlSourceCode = mustache.render(htmlTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); - - // Create README.html - const readmeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/README.md.mustache'), 'utf-8'); - const readmeSourceCode = mustache.render(readmeTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); - - // Create LICENSE - const licenseTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, '/LICENSE.mustache'), 'utf-8'); - const licenseSourceCode = mustache.render(licenseTemplate, data); - fs.writeFileSync(path.join(data.dst, data.module, 'LICENSE'), licenseSourceCode); - - if (options.tgz) { - util.runNpmPack(data); - resolve(path.join(data.dst, data.module + '-' + data.version + '.tgz')); - } else { - resolve(path.join(data.dst, data.module)); + } + data.events.push(q); } - }); + } + + const wotmeta = []; + if (td.hasOwnProperty('lastModified')) { + wotmeta.push({name: "lastModified", value: td.lastModified}); + } + if (td.hasOwnProperty('created')) { + wotmeta.push({name: "created", value: td.created}); + } + if (td.hasOwnProperty('support')) { + wotmeta.push({name: "support", value: JSON.stringify(td.support)}); + } + if (td.hasOwnProperty("id")) { + wotmeta.push({name: "id", value: td.id, last: true}); + } + + const formsel = wotutils.makeformsel(td); + + data.genformsel = JSON.stringify(formsel); + data.genproprwo = JSON.stringify(rwo); + + data.ufName = td.title.replace(/[^A-Za-z0-9]/g, ' ').trim(); + data.nodeName = data.name; + data.projectName = data.module; + data.projectVersion = data.version; + data.keywords = util.extractKeywords(data.keywords); + data.category = data.category || 'Web of Things'; + data.description = td.description; + data.licenseName = 'Apache-2.0'; + data.licenseUrl = ''; + data.links = td.links; + data.support = td.support; + data.iconpath = wotutils.woticon(td); + data.wotmeta = wotmeta; + + let lang = null; + if (td.hasOwnProperty('@context') && Array.isArray(td['@context'])) { + td['@context'].forEach(e => { + if (e.hasOwnProperty("@language")) { + lang = e['@language']; + } + }); + } + if (lang === null) { + data.textdir = "auto"; + } else { + data.textdir = wotutils.textDirection(lang); + } + + util.createCommonFiles(TEMPLATE_DIR, data); + + // Create package.json + const packageTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'package.json.mustache'), 'utf-8'); + const packageSourceCode = mustache.render(packageTemplate, data); + fs.writeFileSync(path.join(data.dst, data.module, 'package.json'), packageSourceCode); + + // Create node.js + const nodeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.js.mustache'), 'utf-8'); + let nodeSourceCode = mustache.render(nodeTemplate, data); + if (options.obfuscate) { + nodeSourceCode = obfuscator.obfuscate(nodeSourceCode, { stringArrayEncoding: 'rc4' }).getObfuscatedCode(); + } + fs.writeFileSync(path.join(data.dst, data.module, 'node.js'), nodeSourceCode); + + // Create node.html + const htmlTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'node.html.mustache'), 'utf-8'); + const htmlSourceCode = mustache.render(htmlTemplate, data); + fs.writeFileSync(path.join(data.dst, data.module, 'node.html'), htmlSourceCode); + + // Create README.html + const readmeTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'README.md.mustache'), 'utf-8'); + const readmeSourceCode = mustache.render(readmeTemplate, data); + fs.writeFileSync(path.join(data.dst, data.module, 'README.md'), readmeSourceCode); + + // Create LICENSE + const licenseTemplate = fs.readFileSync(path.join(TEMPLATE_DIR, 'LICENSE.mustache'), 'utf-8'); + const licenseSourceCode = mustache.render(licenseTemplate, data); + 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); + } } \ No newline at end of file diff --git a/package.json b/package.json index 5e414c9..822ee68 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dependencies": { "ajv": "6.12.0", "api-spec-converter": "2.11.0", + "axios": "0.20.0", "cldr": "5.5.4", "colors": "1.4.0", "csv-string": "3.1.8", From 6e1f5b378834755e27a2e64e3843ddd52448c7cf Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 1 Sep 2020 17:45:34 +0100 Subject: [PATCH 6/7] Rename core lib APIs --- bin/node-red-nodegen.js | 11 +++++------ lib/nodegen.js | 12 ++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/bin/node-red-nodegen.js b/bin/node-red-nodegen.js index d9ff2bf..28622b7 100755 --- a/bin/node-red-nodegen.js +++ b/bin/node-red-nodegen.js @@ -18,7 +18,6 @@ var fs = require('fs'); var path = require('path'); -var request = require('request'); var argv = require('minimist')(process.argv.slice(2)); var colors = require('colors'); @@ -114,22 +113,22 @@ if (argv.help || argv.h) { data.src = sourcePath; if (argv.wottd || /\.jsonld$/.test(sourcePath)) { // Explicitly a Web Of Things request - promise = nodegen.wottd2node(data, options); + promise = nodegen.WebOfThingsGenerator(data, options); } else if (/^https?:/.test(sourcePath) || /.yaml$/.test(sourcePath)) { // URL/yaml -> swagger - promise = nodegen.swagger2node(data, options); + promise = nodegen.SwaggerNodeGenerator(data, options); } else if (/\.json$/.test(sourcePath)) { // JSON could be a Function node, or Swagger var content = JSON.parse(fs.readFileSync(sourcePath)); if (Array.isArray(content)) { data.src = content; - promise = nodegen.function2node(data, options); + promise = nodegen.FunctionNodeGenerator(data, options); } else { - promise = nodegen.swagger2node(data, options); + promise = nodegen.SwaggerNodeGenerator(data, options); } } else if (/\.js$/.test(sourcePath)) { // .js -> Function node - promise = nodegen.function2node(data, options); + promise = nodegen.FunctionNodeGenerator(data, options); } else { console.error('error: Unsupported file type'); help(); diff --git a/lib/nodegen.js b/lib/nodegen.js index d46d30f..0e86bd3 100644 --- a/lib/nodegen.js +++ b/lib/nodegen.js @@ -15,12 +15,12 @@ **/ const util = require("./util"); -const function2node = require("./function"); -const swagger2node = require("./swagger"); -const wottd2node = require("./webofthings"); +const FunctionNodeGenerator = require("./function"); +const SwaggerNodeGenerator = require("./swagger"); +const WebOfThingsGenerator = require("./webofthings"); module.exports = { - function2node: function2node, - swagger2node: swagger2node, - wottd2node: wottd2node + FunctionNodeGenerator: FunctionNodeGenerator, + SwaggerNodeGenerator: SwaggerNodeGenerator, + WebOfThingsGenerator: WebOfThingsGenerator }; From 6e53d62537d8214619b73f1f28395e65a97fad54 Mon Sep 17 00:00:00 2001 From: Kazuhito Yokoi Date: Mon, 14 Sep 2020 18:00:18 +0900 Subject: [PATCH 7/7] Add handling to modify swagger.json for Travis CI --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 365d8e3..c7e5b4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,3 +16,5 @@ before_script: - docker pull swaggerapi/petstore - docker run -d -e SWAGGER_URL=http://petstore.swagger.io -e SWAGGER_BASE_PATH=/v2 -p 80:8080 swaggerapi/petstore - npm install -g istanbul coveralls + - jq '.schemes=["http"]' samples/swagger.json > samples/tmp + - mv samples/tmp samples/swagger.json