Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
fa5a531
Support Web of Things Thing Description.
k-toumura Sep 20, 2018
2515075
Fix observe logic.
k-toumura Sep 25, 2018
10be83b
Revert "Fix observe logic."
k-toumura Sep 25, 2018
7e68fe4
Revert "Support Web of Things Thing Description."
k-toumura Sep 25, 2018
1ce47cf
Set default category to Web of Things.
k-toumura Sep 25, 2018
7d73c2e
Fix event handling code
k-toumura Sep 25, 2018
0b9ebc5
Fix bugs discovered in online plugfest
k-toumura Sep 28, 2018
88e303d
support websocket and major refactoring
k-toumura Oct 13, 2018
9e31394
preliminary support of URI template
k-toumura Oct 14, 2018
65989be
set default value for mediaType, and some minor refactoring.
k-toumura Oct 16, 2018
535cc7d
Fix typo.
k-toumura Oct 16, 2018
58d8fbc
UI improvement
k-toumura Oct 17, 2018
45f5c09
Add icon and rel/op normalization
k-toumura Oct 17, 2018
cf7418f
bugfix on handling base-URI.
k-toumura Oct 17, 2018
c342c8d
use mediaType on HTTP request
k-toumura Oct 20, 2018
e9d601b
Merge remote-tracking branch 'upstream/master'
k-toumura Nov 20, 2018
79c5272
merge upstream changes
k-toumura Nov 20, 2018
cdbd563
add test for wottd2node
k-toumura Nov 20, 2018
cf012c6
Adding empty directories
k-toumura Nov 22, 2018
599e3c7
Update based on 20190119 spec.
k-toumura Jan 22, 2019
1777faf
Correct UI issues
k-toumura Jan 28, 2019
161d7e2
Princeton Testfest version
k-toumura Feb 8, 2019
5cf6d1f
Update for recent TD spec.
k-toumura Mar 23, 2019
8806a19
update to CR spec.
k-toumura May 19, 2019
9ba2847
remove debug message
k-toumura May 19, 2019
d90fcb6
remove use of Array.prototype.flat() for better compatibility
k-toumura May 21, 2019
73a5e1d
fix default value handling of PropertyAffordance
k-toumura May 27, 2019
0e52f93
Normalize 'security' as an array of string
k-toumura Jun 1, 2019
4c56ce3
support binary data
k-toumura Jun 1, 2019
95e8acf
support HTTP and language negotiation for retrieve WoT Thing Descript…
k-toumura Jun 2, 2019
3dd7a0e
update README.md (HTTP support for retrieve TD)
k-toumura Jun 2, 2019
55895a6
when use --wottd, regard .json as TD
k-toumura Jun 2, 2019
af06697
editorial fix
k-toumura Jun 2, 2019
dbf58f2
skip BOM when exist
k-toumura Jun 6, 2019
0bcc629
dir='auto' for heuristic directionality detection
k-toumura Jun 10, 2019
59a9022
infer script direction from @language
k-toumura Jun 10, 2019
3dff5b6
add normalization of 'security' in interactions
k-toumura Jun 16, 2019
581ca2a
Fix for decoding URI
k-toumura Dec 4, 2019
a1e3457
incorporate upstream changes
k-toumura Dec 4, 2019
8aacf0b
update Thing Description schema
k-toumura Dec 4, 2019
5ca9a38
minor fixes
k-toumura Dec 5, 2019
8185de9
Update assertion code of Swagger Petstore (version 1.0.0 -> 1.0.3)
k-toumura Dec 5, 2019
67dfdab
Merge branch 'webofthings-2' into webofthings
k-toumura Dec 5, 2019
b068aba
don't track .eslintrc.js
k-toumura Dec 5, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# Node generator for Node-RED

Node generator is a command line tool to generate Node-RED nodes based on various sources such as an Open API document or a Function node.
Expand All @@ -22,6 +23,8 @@ You may need to run this with `sudo`, or from within an Administrator command sh
Supported source:
- Open API document
- Function node (js file in library, "~/.node-red/lib/function/")
- Swagger definition
- (Beta) Thing Description of W3C Web of Things (jsonld file or URL that points jsonld file)

Options:
-o : Destination path to save generated node (default: current directory)
Expand All @@ -35,6 +38,8 @@ You may need to run this with `sudo`, or from within an Administrator command sh
--color : Color for node appearance (format: color hexadecimal numbers like "A6BBCF")
--tgz : Save node as tgz file
--help : Show help
--wottd : explicitly instruct that source file/URL points a Thing Description
--lang : Language negotiation information when retrieve a Thing Description
-v : Show node generator version

### Example 1. Create an original node from Open API document
Expand All @@ -60,6 +65,28 @@ You may need to run this with `sudo`, or from within an Administrator command sh

-> You can use lower-case node on Node-RED flow editor.

### Example 3. Create original node from Thing Description

- node-red-nodegen example.jsonld
- cd node-red-contrib-example-thing
- sudo npm link
- cd ~/.node-red
- npm link node-red-contrib-example-thing
- node-red

-> You can use Example Thing node on Node-RED flow editor.

### Example 4. Create original node from Thing Description via HTTP

- node-red-nodegen http://example.com/td.jsonld --wottd --lang "en-US,en;q=0.5"
- cd node-red-contrib-example-thing
- sudo npm link
- cd ~/.node-red
- npm link node-red-contrib-example-thing
- node-red

-> You can use Example Thing node on Node-RED flow editor.

## Documentation
- [Use cases](https://github.com/node-red/node-red-nodegen/blob/0.0.4/docs/index.md#use-cases) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.0.4/docs/index_ja.md#use-cases))
- [How to use Node generator](https://github.com/node-red/node-red-nodegen/blob/0.0.4/docs/index.md#how-to-use-node-generator) ([Japanese](https://github.com/node-red/node-red-nodegen/blob/0.0.4/docs/index_ja.md#how-to-use-node-generator))
Expand Down
48 changes: 46 additions & 2 deletions bin/node-red-nodegen.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ function help() {
' [--color <node color>]' +
' [--tgz]' +
' [--help]' +
' [--wottd]' +
' [--lang <accept-language>]' +
' [-v]\n' +
'\n' +
'Description:'.bold + '\n' +
Expand All @@ -64,6 +66,8 @@ function help() {
'Supported source:'.bold + '\n' +
' - Open API document\n' +
// ' - Subflow node (json file of subflow)\n' +
' - Swagger definition\n' +
' - Thing Description (jsonld file or URL that points jsonld file)\n' +
' - Function node (js file in library, "~/.node-red/lib/function/")\n' +
'\n' +
'Options:\n'.bold +
Expand All @@ -78,6 +82,8 @@ function help() {
' --color : Color for node appearance (format: color hexadecimal numbers like "A6BBCF")\n' +
' --tgz : Save node as tgz file\n' +
' --help : Show help\n' +
' --wottd : explicitly instruct source file/URL points a Thing Description\n' +
' --lang : Language negotiation information when retrieve a Thing Description\n' +
' -v : Show node generator version\n';
console.log(helpText);
}
Expand All @@ -87,14 +93,24 @@ function version() {
console.log(packageJson.version);
}

function skipBom(body) {
if (body[0]===0xEF &&
body[1]===0xBB &&
body[2]===0xBF) {
return body.slice(3);
} else {
return body;
}
}

if (argv.help || argv.h) {
help();
} else if (argv.v) {
version();
} else {
var sourcePath = argv._[0];
if (sourcePath) {
if (sourcePath.startsWith('http://') || sourcePath.startsWith('https://')) {
if (!argv.wottd && (sourcePath.startsWith('http://') || sourcePath.startsWith('https://'))) {
request(sourcePath, function (error, response, body) {
if (!error) {
data.src = JSON.parse(body);
Expand All @@ -107,7 +123,28 @@ if (argv.help || argv.h) {
console.error(error);
}
});
} else if (sourcePath.endsWith('.json')) {
} 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));
nodegen.swagger2node(data, options).then(function (result) {
console.log('Success: ' + result);
Expand All @@ -128,6 +165,13 @@ if (argv.help || argv.h) {
}).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) {
console.log('Success: ' + result);
}).catch(function (error) {
console.log('Error: ' + error);
});
} else {
console.error('error: Unsupported file type');
}
Expand Down
191 changes: 190 additions & 1 deletion lib/nodegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ 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
Expand Down Expand Up @@ -566,7 +568,194 @@ function swagger2node(data, options) {
});
}


function wottd2node(data, options) {
return when.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('error: 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' });
}
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));
}
});
}

module.exports = {
function2node: function2node,
swagger2node: swagger2node
swagger2node: swagger2node,
wottd2node: wottd2node
};
Loading