mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
50d7e16365 | ||
|
ef7bc931b7 | ||
|
a713c92530 | ||
|
1db1ec7b5e | ||
|
a6ef755139 | ||
|
7b80ae42e1 | ||
|
06a1f30350 | ||
|
2f93bb969b | ||
|
e094ea3d2a | ||
|
c685a31056 | ||
|
c32ce3bb7b | ||
|
4f87ebdf0a | ||
|
19b6cba398 | ||
|
5cf9c07b73 | ||
|
211eeea05d | ||
|
51a3521834 | ||
|
6a9575e9f4 | ||
|
62088259ae | ||
|
925ebcc06e | ||
|
673a6bbe2c | ||
|
cf32a33984 | ||
|
98c1bc276d | ||
|
629536b562 | ||
|
1441042458 | ||
|
58c8311d56 | ||
|
e03a0fffa9 | ||
|
8e2c12f8d9 | ||
|
7cec7ae608 | ||
|
c49f722e4f | ||
|
d4d95a43b6 | ||
|
a345089c8b | ||
|
67c268e13d | ||
|
ce85c8d986 | ||
|
cb35604ef5 | ||
|
61681bb1d6 | ||
|
1a226c4dc6 | ||
|
08fccc4e77 | ||
|
c1d50e82e1 | ||
|
9777af7cb5 | ||
|
fd86035865 | ||
|
a8ec032553 | ||
|
66ee27c5fa | ||
|
17a737ca88 | ||
|
75e7c0e50d | ||
|
fc0cf1ff51 | ||
|
0f4d46671f | ||
|
048f9c0294 | ||
|
ca77842b5b | ||
|
6fa8b7f5f1 | ||
|
a2d03c14ae | ||
|
c667a0e74c | ||
|
8123828113 | ||
|
72b8dbb45b | ||
|
7703875740 | ||
|
6442bb8a13 | ||
|
f29d7c9252 | ||
|
2f7f53ed96 | ||
|
94031a52a5 | ||
|
d67f91e7ed | ||
|
f37697c4fb | ||
|
69448c7329 | ||
|
8e9815fb91 | ||
|
4cdd7978cf | ||
|
40d81358f4 | ||
|
c7b62aed91 | ||
|
c0e7d6d826 | ||
|
f809377de8 | ||
|
9767bd9697 | ||
|
3a55528552 | ||
|
56197ffe3a | ||
|
0f0d0c046c | ||
|
ecc4973645 | ||
|
3169f93cc2 | ||
|
c1a1a73599 | ||
|
db1b0ccb79 | ||
|
df161ce672 | ||
|
72fe30892e | ||
|
d373105b32 | ||
|
28b311b7ed | ||
|
dcda513901 | ||
|
72c400794c | ||
|
4374506981 | ||
|
695873d35a | ||
|
15da19dcea | ||
|
d5bdc1600b |
71
CHANGELOG.md
71
CHANGELOG.md
@@ -1,3 +1,74 @@
|
||||
#### 0.19.6: Maintenance Release
|
||||
|
||||
- Fix encoding of file node from binary to utf8 - #2051
|
||||
|
||||
#### 0.19.5: Maintenance Release
|
||||
|
||||
- Recognize pip installs of RPi.GPIO (#1934)
|
||||
- Merge pull request #1941 from node-red-hitachi/master-batch
|
||||
- Merge pull request #1931 from node-red-hitachi/master-typedinput
|
||||
- Set min value of properties and spinners for batch
|
||||
- Fix that unnecessary optionMenu remains
|
||||
- Merge pull request #1894 from node-red-hitachi/fix-overlapping-file-node-execution
|
||||
- Merge pull request #1924 from imZack/patch-1
|
||||
- Add missing comma
|
||||
- Do not disable context sidebar during node edit Fixes #1921
|
||||
- Don't allow virtual links to be spliced Fixes #1920
|
||||
- Merge project package changes to avoid overwritten changes
|
||||
- Handle manually added project deps that are unused Fixes #1908
|
||||
- update close & input handling of File node
|
||||
- make close handler argument only one
|
||||
- Merge pull request #1907 from amilajack/patch-2
|
||||
- Change repo badge to point to master branch
|
||||
- invoke callbacks if async handler is specified
|
||||
- Merge pull request #1891 from camlow325/resolve-example-path-for-windows-support
|
||||
- Merge pull request #1900 from kazuhitoyokoi/master-addtestcases4settings.js
|
||||
- wait closing while pending messages exist
|
||||
- Add test cases for red/api/editor/settings.js
|
||||
- Ensure all palette categories are opened properly Closes #1893
|
||||
- Resolve path when sending example file for Windows support
|
||||
- fix multiple input message processing of file node
|
||||
|
||||
#### 0.19.4: Maintenance Release
|
||||
|
||||
- Fix race condition in non-cache lfs context Fixes #1888
|
||||
- LocalFileSystem Context: Remove extra flush code
|
||||
- Prevent race condition in caching mode of lfs context (#1889)
|
||||
- Allow context store name to be provided in the key
|
||||
- Switch node: only use promises when absolutely necessary
|
||||
- Fix dbl-click handling on webkit-based browsers
|
||||
- Ensure context.flow/global cannot be deleted or enumerated
|
||||
- Handle context.get with multiple levels of unknown key Fixes #1883
|
||||
- Fix global.get("foo.bar") for functionGlobalContext set values
|
||||
- Fix node color bug (#1877)
|
||||
- Merge pull request #1857 from cclauss/patch-1
|
||||
- Define raw_input() in Python 3 & fix time.sleep()
|
||||
|
||||
#### 0.19.3: Maintenance Release
|
||||
|
||||
- Split node - fix complete to send msg for k/v object
|
||||
- Remove unused Join node merged object key typed input
|
||||
- Set the JavaScript editor to full-screen
|
||||
- Filter global modules installed locally
|
||||
- Add svg to permitted icon extension list
|
||||
- Debug node - indicate status all the time if selected to do so
|
||||
- pi nodes - increase test coverage slightly
|
||||
- TCP-request node - only write payload
|
||||
- JSON schema: perform validation when obj -> obj or str -> str
|
||||
- JSON schema: add draft-06 support (via $schema keyword)
|
||||
- Mqtt proxy configuration for websocket connection, #1651.
|
||||
- Allows MQTT Shared Subscriptions for MQTT-In core node
|
||||
- Fix use of HTML tag or CSS class specification as icon of typedInput
|
||||
|
||||
#### 0.19.2: Maintenance Release
|
||||
|
||||
- Ensure node default colour is used if palette.theme has no match
|
||||
- fix lost messages / properties in TCPRequest Node; closes #1863 (#1864)
|
||||
- Fix typo in template.html
|
||||
- Improve error reporting from context plugin loading
|
||||
- Prevent no-op edit of node marking as changed due to icon
|
||||
- Change node must handle empty rule set
|
||||
|
||||
#### 0.19.1: Maintenance Release
|
||||
|
||||
- Pull in latest twitter node
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
http://nodered.org
|
||||
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
A visual tool for wiring the Internet of Things.
|
||||
|
@@ -522,13 +522,25 @@
|
||||
this.selectLabel.empty();
|
||||
var image;
|
||||
if (opt.icon) {
|
||||
image = new Image();
|
||||
image.name = opt.icon;
|
||||
image.src = opt.icon;
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
|
||||
if (opt.icon.indexOf("<") === 0) {
|
||||
$(opt.icon).prependTo(this.selectLabel);
|
||||
}
|
||||
else if (opt.icon.indexOf("/") !== -1) {
|
||||
image = new Image();
|
||||
image.name = opt.icon;
|
||||
image.src = opt.icon;
|
||||
$('<img>',{src:opt.icon,style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
|
||||
}
|
||||
else {
|
||||
$('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(this.selectLabel);
|
||||
}
|
||||
} else {
|
||||
this.selectLabel.text(opt.label);
|
||||
}
|
||||
if (this.optionMenu) {
|
||||
this.optionMenu.remove();
|
||||
this.optionMenu = null;
|
||||
}
|
||||
if (opt.options) {
|
||||
if (this.optionExpandButton) {
|
||||
this.optionExpandButton.hide();
|
||||
@@ -619,10 +631,6 @@
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.optionMenu) {
|
||||
this.optionMenu.remove();
|
||||
this.optionMenu = null;
|
||||
}
|
||||
if (this.optionSelectTrigger) {
|
||||
this.optionSelectTrigger.hide();
|
||||
}
|
||||
|
@@ -881,7 +881,6 @@ RED.diff = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
|
||||
if (def.defaults) {
|
||||
properties = properties.concat(Object.keys(def.defaults));
|
||||
@@ -889,6 +888,13 @@ RED.diff = (function() {
|
||||
if (node.type !== 'tab') {
|
||||
properties = properties.concat(['inputLabels','outputLabels']);
|
||||
}
|
||||
if ( ((localNode && localNode.hasOwnProperty('icon')) || (remoteNode && remoteNode.hasOwnProperty('icon'))) &&
|
||||
properties.indexOf('icon') === -1
|
||||
) {
|
||||
properties.unshift('icon');
|
||||
}
|
||||
|
||||
|
||||
properties.forEach(function(d) {
|
||||
localChanged = false;
|
||||
remoteChanged = false;
|
||||
|
@@ -1110,7 +1110,7 @@ RED.editor = (function() {
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
if (icon !== defaultIcon) {
|
||||
if (icon !== "" && icon !== defaultIcon) {
|
||||
changes.icon = editing_node.icon;
|
||||
editing_node.icon = icon;
|
||||
changed = true;
|
||||
|
@@ -32,7 +32,7 @@ RED.editor.types._js = (function() {
|
||||
|
||||
var trayOptions = {
|
||||
title: options.title,
|
||||
width: "inherit",
|
||||
width: options.width||"inherit",
|
||||
buttons: [
|
||||
{
|
||||
id: "node-dialog-cancel",
|
||||
|
@@ -447,7 +447,10 @@ RED.library = (function() {
|
||||
click: function() {
|
||||
//TODO: move this to RED.library
|
||||
var flowName = $("#node-input-library-filename").val();
|
||||
if (!/^\s*$/.test(flowName)) {
|
||||
flowName = flowName.trim();
|
||||
if(flowName === "" || flowName.endsWith("/")) {
|
||||
RED.notify(RED._("library.invalidFilename"),"warning");
|
||||
} else {
|
||||
$.ajax({
|
||||
url:'library/flows/'+flowName,
|
||||
type: "POST",
|
||||
|
@@ -275,7 +275,8 @@ RED.palette = (function() {
|
||||
}
|
||||
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
if (d3.select(nodes[i]).classed('link_background')) {
|
||||
var node = d3.select(nodes[i]);
|
||||
if (node.classed('link_background') && !node.classed('link_link')) {
|
||||
var length = nodes[i].getTotalLength();
|
||||
for (var j=0;j<length;j+=10) {
|
||||
var p = nodes[i].getPointAtLength(j);
|
||||
@@ -320,9 +321,9 @@ RED.palette = (function() {
|
||||
}
|
||||
setLabel(nt,$(d),label,nodeInfo);
|
||||
|
||||
var categoryNode = $("#palette-container-"+category);
|
||||
var categoryNode = $("#palette-container-"+rootCategory);
|
||||
if (categoryNode.find(".palette_node").length === 1) {
|
||||
categoryContainers[category].open();
|
||||
categoryContainers[rootCategory].open();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -459,7 +460,6 @@ RED.palette = (function() {
|
||||
}
|
||||
});
|
||||
RED.events.on('registry:node-set-disabled', function(nodeSet) {
|
||||
console.log(nodeSet);
|
||||
for (var j=0;j<nodeSet.types.length;j++) {
|
||||
hideNodeType(nodeSet.types[j]);
|
||||
var def = RED.nodes.getType(nodeSet.types[j]);
|
||||
|
@@ -461,7 +461,11 @@ RED.projects.settings = (function() {
|
||||
setTimeout(function() {
|
||||
depsList.editableList('removeItem',entry);
|
||||
refreshModuleInUseCounts();
|
||||
entry.count = modulesInUse[entry.id].count;
|
||||
if (modulesInUse.hasOwnProperty(entry.id)) {
|
||||
entry.count = modulesInUse[entry.id].count;
|
||||
} else {
|
||||
entry.count = 0;
|
||||
}
|
||||
depsList.editableList('addItem',entry);
|
||||
},500);
|
||||
}
|
||||
|
@@ -132,7 +132,7 @@ RED.sidebar.context = (function() {
|
||||
content: content,
|
||||
toolbar: footerToolbar,
|
||||
// pinned: true,
|
||||
enableOnEdit: false
|
||||
enableOnEdit: true
|
||||
});
|
||||
|
||||
// var toggleLiveButton = $("#sidebar-context-toggle-live");
|
||||
|
@@ -800,8 +800,9 @@ RED.utils = (function() {
|
||||
var paletteTheme = RED.settings.theme('palette.theme') || [];
|
||||
if (paletteTheme.length > 0) {
|
||||
if (!nodeColorCache.hasOwnProperty(type)) {
|
||||
nodeColorCache[type] = def.color;
|
||||
var l = paletteTheme.length;
|
||||
for (var i=0;i<l;i++ ){
|
||||
for (var i = 0; i < l; i++ ){
|
||||
var themeRule = paletteTheme[i];
|
||||
if (themeRule.hasOwnProperty('category')) {
|
||||
if (!themeRule.hasOwnProperty('_category')) {
|
||||
@@ -825,7 +826,11 @@ RED.utils = (function() {
|
||||
}
|
||||
result = nodeColorCache[type];
|
||||
}
|
||||
return result;
|
||||
if (result) {
|
||||
return result;
|
||||
} else {
|
||||
return "#ddd";
|
||||
}
|
||||
}
|
||||
|
||||
function addSpinnerOverlay(container,contain) {
|
||||
|
@@ -1766,7 +1766,7 @@ RED.view = (function() {
|
||||
clickTime = now;
|
||||
|
||||
dblClickPrimed = (lastClickNode == mousedown_node &&
|
||||
d3.event.buttons === 1 &&
|
||||
d3.event.button === 0 &&
|
||||
!d3.event.shiftKey && !d3.event.metaKey && !d3.event.altKey && !d3.event.ctrlKey);
|
||||
lastClickNode = mousedown_node;
|
||||
|
||||
@@ -2415,6 +2415,7 @@ RED.view = (function() {
|
||||
var l = d3.select(this);
|
||||
d.added = true;
|
||||
l.append("svg:path").attr("class","link_background link_path")
|
||||
.classed("link_link", function(d) { return d.link })
|
||||
.on("mousedown",function(d) {
|
||||
mousedown_link = d;
|
||||
clearSelection();
|
||||
|
@@ -67,6 +67,7 @@
|
||||
},
|
||||
inputs:1, // set the number of inputs - only 0 or 1
|
||||
outputs:1, // set the number of outputs - 0 to n
|
||||
color: "#ddd", // set icon color
|
||||
// set the icon (held in icons dir below where you save the node)
|
||||
icon: "myicon.png", // saved in icons/myicon.png
|
||||
label: function() { // sets the default label contents
|
||||
|
@@ -19,7 +19,11 @@ module.exports = function(RED) {
|
||||
if (this.tosidebar === undefined) { this.tosidebar = true; }
|
||||
this.severity = n.severity || 40;
|
||||
this.active = (n.active === null || typeof n.active === "undefined") || n.active;
|
||||
this.status({});
|
||||
if (this.tostatus) {
|
||||
this.oldStatus = {fill:"grey", shape:"ring"};
|
||||
this.status(this.oldStatus);
|
||||
}
|
||||
else { this.status({}); }
|
||||
|
||||
var node = this;
|
||||
var levels = {
|
||||
@@ -122,12 +126,12 @@ module.exports = function(RED) {
|
||||
if (state === "enable") {
|
||||
node.active = true;
|
||||
res.sendStatus(200);
|
||||
if (node.tostatus) { node.status({}); }
|
||||
if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); }
|
||||
} else if (state === "disable") {
|
||||
node.active = false;
|
||||
res.sendStatus(201);
|
||||
if (node.tostatus && node.hasOwnProperty("oldStatus")) {
|
||||
node.oldStatus.shape = "ring";
|
||||
node.oldStatus.shape = "dot";
|
||||
node.status(node.oldStatus);
|
||||
}
|
||||
} else {
|
||||
|
@@ -126,6 +126,7 @@
|
||||
var value = that.editor.getValue();
|
||||
RED.editor.editJavaScript({
|
||||
value: value,
|
||||
width: "Infinity",
|
||||
cursor: that.editor.getCursorPosition(),
|
||||
complete: function(v,cursor) {
|
||||
that.editor.setValue(v, -1);
|
||||
|
@@ -42,7 +42,7 @@ module.exports = function(RED) {
|
||||
if (type === 'object') {
|
||||
type = Buffer.isBuffer(msg)?'Buffer':(util.isArray(msg)?'Array':'Date');
|
||||
}
|
||||
node.error(RED._("function.error.non-message-returned",{ type: type }))
|
||||
node.error(RED._("function.error.non-message-returned",{ type: type }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,9 +203,9 @@ module.exports = function(RED) {
|
||||
if (util.hasOwnProperty('promisify')) {
|
||||
sandbox.setTimeout[util.promisify.custom] = function(after, value) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
sandbox.setTimeout(function(){ resolve(value) }, after);
|
||||
sandbox.setTimeout(function(){ resolve(value); }, after);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
var context = vm.createContext(sandbox);
|
||||
try {
|
||||
@@ -241,7 +241,6 @@ module.exports = function(RED) {
|
||||
|
||||
var line = 0;
|
||||
var errorMessage;
|
||||
var stack = err.stack.split(/\r?\n/);
|
||||
if (stack.length > 0) {
|
||||
while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
|
||||
line++;
|
||||
@@ -265,13 +264,13 @@ module.exports = function(RED) {
|
||||
});
|
||||
this.on("close", function() {
|
||||
while (node.outstandingTimers.length > 0) {
|
||||
clearTimeout(node.outstandingTimers.pop())
|
||||
clearTimeout(node.outstandingTimers.pop());
|
||||
}
|
||||
while (node.outstandingIntervals.length > 0) {
|
||||
clearInterval(node.outstandingIntervals.pop())
|
||||
clearInterval(node.outstandingIntervals.pop());
|
||||
}
|
||||
this.status({});
|
||||
})
|
||||
});
|
||||
} catch(err) {
|
||||
// eg SyntaxError - which v8 doesn't include line number information
|
||||
// so we can't do better than this
|
||||
@@ -280,4 +279,4 @@ module.exports = function(RED) {
|
||||
}
|
||||
RED.nodes.registerType("function",FunctionNode);
|
||||
RED.library.register("functions");
|
||||
}
|
||||
};
|
||||
|
@@ -77,7 +77,9 @@
|
||||
}</pre>
|
||||
<p>The resulting property will be:
|
||||
<pre>Hello Fred. Today is Monday</pre>
|
||||
<p>It is possible to use a property from the flow context or global context. Just use <code>{{flow.name}}</code> or <code>{{global.name}}</code>, or for persistable store <code>store</code> use <code>{{flow[store].name}}</code> or <code>{{flobal[store].name}}</code>.
|
||||
<p>It is possible to use a property from the flow context or global context. Just use <code>{{flow.name}}</code> or
|
||||
<code>{{global.name}}</code>, or for persistable store <code>store</code> use <code>{{flow[store].name}}</code> or
|
||||
<code>{{global[store].name}}</code>.
|
||||
<p><b>Note: </b>By default, <i>mustache</i> will escape any HTML entities in the values it substitutes.
|
||||
To prevent this, use <code>{{{triple}}}</code> braces.
|
||||
</script>
|
||||
|
@@ -24,8 +24,12 @@ module.exports = function(RED) {
|
||||
try {
|
||||
fs.statSync("/usr/lib/python2.7/dist-packages/RPi/GPIO"); // test on Hypriot
|
||||
} catch(err) {
|
||||
RED.log.warn("rpi-gpio : "+RED._("rpi-gpio.errors.libnotfound"));
|
||||
allOK = false;
|
||||
try {
|
||||
fs.statSync("/usr/local/lib/python2.7/dist-packages/RPi/GPIO"); // installed with pip
|
||||
} catch(err) {
|
||||
RED.log.warn("rpi-gpio : "+RED._("rpi-gpio.errors.libnotfound"));
|
||||
allOK = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,12 @@ import os
|
||||
import subprocess
|
||||
from time import sleep
|
||||
|
||||
bounce = 25;
|
||||
try:
|
||||
raw_input # Python 2
|
||||
except NameError:
|
||||
raw_input = input # Python 3
|
||||
|
||||
bounce = 25
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
cmd = sys.argv[1].lower()
|
||||
@@ -198,7 +203,7 @@ if len(sys.argv) > 2:
|
||||
elif cmd == "kbd": # catch keyboard button events
|
||||
try:
|
||||
while not os.path.isdir("/dev/input/by-path"):
|
||||
time.sleep(10)
|
||||
sleep(10)
|
||||
infile = subprocess.check_output("ls /dev/input/by-path/ | grep -m 1 'kbd'", shell=True).strip()
|
||||
infile_path = "/dev/input/by-path/" + infile
|
||||
EVENT_SIZE = struct.calcsize('llHHI')
|
||||
|
@@ -19,11 +19,26 @@ module.exports = function(RED) {
|
||||
var mqtt = require("mqtt");
|
||||
var util = require("util");
|
||||
var isUtf8 = require('is-utf8');
|
||||
var HttpsProxyAgent = require('https-proxy-agent');
|
||||
var url = require('url');
|
||||
|
||||
function matchTopic(ts,t) {
|
||||
if (ts == "#") {
|
||||
return true;
|
||||
}
|
||||
/* The following allows shared subscriptions (as in MQTT v5)
|
||||
http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.html#_Toc514345522
|
||||
|
||||
4.8.2 describes shares like:
|
||||
$share/{ShareName}/{filter}
|
||||
$share is a literal string that marks the Topic Filter as being a Shared Subscription Topic Filter.
|
||||
{ShareName} is a character string that does not include "/", "+" or "#"
|
||||
{filter} The remainder of the string has the same syntax and semantics as a Topic Filter in a non-shared subscription. Refer to section 4.7.
|
||||
*/
|
||||
else if(ts.startsWith("$share")){
|
||||
ts = ts.replace(/^\$share\/[^#+/]+\/(.*)/g,"$1");
|
||||
|
||||
}
|
||||
var re = new RegExp("^"+ts.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
return re.test(t);
|
||||
}
|
||||
@@ -96,12 +111,29 @@ module.exports = function(RED) {
|
||||
if (typeof this.cleansession === 'undefined') {
|
||||
this.cleansession = true;
|
||||
}
|
||||
var prox;
|
||||
if (process.env.http_proxy != null) { prox = process.env.http_proxy; }
|
||||
if (process.env.HTTP_PROXY != null) { prox = process.env.HTTP_PROXY; }
|
||||
|
||||
// Create the URL to pass in to the MQTT.js library
|
||||
if (this.brokerurl === "") {
|
||||
// if the broker may be ws:// or wss:// or even tcp://
|
||||
if (this.broker.indexOf("://") > -1) {
|
||||
this.brokerurl = this.broker;
|
||||
// Only for ws or wss, check if proxy env var for additional configuration
|
||||
if (this.brokerurl.indexOf("wss://") > -1 || this.brokerurl.indexOf("ws://") > -1 )
|
||||
// check if proxy is set in env
|
||||
if (prox) {
|
||||
var parsedUrl = url.parse(this.brokerurl);
|
||||
var proxyOpts = url.parse(prox);
|
||||
// true for wss
|
||||
proxyOpts.secureEndpoint = parsedUrl.protocol ? parsedUrl.protocol === 'wss:' : true;
|
||||
// Set Agent for wsOption in MQTT
|
||||
var agent = new HttpsProxyAgent(proxyOpts);
|
||||
this.options.wsOptions = {
|
||||
agent: agent
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// construct the std mqtt:// url
|
||||
if (this.usetls) {
|
||||
@@ -435,4 +467,4 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
RED.nodes.registerType("mqtt out",MQTTOutNode);
|
||||
};
|
||||
};
|
@@ -33,9 +33,7 @@ module.exports = function(RED) {
|
||||
*/
|
||||
const enqueue = (queue, item) => {
|
||||
// drop msgs from front of queue if size is going to be exceeded
|
||||
if (queue.size() === msgQueueSize) {
|
||||
queue.shift();
|
||||
}
|
||||
if (queue.size() === msgQueueSize) { queue.shift(); }
|
||||
queue.push(item);
|
||||
return queue;
|
||||
};
|
||||
@@ -467,6 +465,7 @@ module.exports = function(RED) {
|
||||
connecting: false
|
||||
};
|
||||
enqueue(clients[connection_id].msgQueue, msg);
|
||||
clients[connection_id].lastMsg = msg;
|
||||
|
||||
if (!clients[connection_id].connecting && !clients[connection_id].connected) {
|
||||
var buf;
|
||||
@@ -507,8 +506,7 @@ module.exports = function(RED) {
|
||||
clients[connection_id].client.on('data', function(data) {
|
||||
if (node.out === "sit") { // if we are staying connected just send the buffer
|
||||
if (clients[connection_id]) {
|
||||
let msg = dequeue(clients[connection_id].msgQueue) || {};
|
||||
clients[connection_id].msgQueue.unshift(msg);
|
||||
const msg = clients[connection_id].lastMsg || {};
|
||||
msg.payload = data;
|
||||
node.send(RED.util.cloneMessage(msg));
|
||||
}
|
||||
@@ -530,8 +528,7 @@ module.exports = function(RED) {
|
||||
clients[connection_id].timeout = setTimeout(function () {
|
||||
if (clients[connection_id]) {
|
||||
clients[connection_id].timeout = null;
|
||||
let msg = dequeue(clients[connection_id].msgQueue) || {};
|
||||
clients[connection_id].msgQueue.unshift(msg);
|
||||
const msg = clients[connection_id].lastMsg || {};
|
||||
msg.payload = Buffer.alloc(i+1);
|
||||
buf.copy(msg.payload,0,0,i+1);
|
||||
node.send(msg);
|
||||
@@ -553,8 +550,7 @@ module.exports = function(RED) {
|
||||
i += 1;
|
||||
if ( i >= node.splitc) {
|
||||
if (clients[connection_id]) {
|
||||
let msg = dequeue(clients[connection_id].msgQueue) || {};
|
||||
clients[connection_id].msgQueue.unshift(msg);
|
||||
const msg = clients[connection_id].lastMsg || {};
|
||||
msg.payload = Buffer.alloc(i);
|
||||
buf.copy(msg.payload,0,0,i);
|
||||
node.send(msg);
|
||||
@@ -573,8 +569,7 @@ module.exports = function(RED) {
|
||||
i += 1;
|
||||
if (data[j] == node.splitc) {
|
||||
if (clients[connection_id]) {
|
||||
let msg = dequeue(clients[connection_id].msgQueue) || {};
|
||||
clients[connection_id].msgQueue.unshift(msg);
|
||||
const msg = clients[connection_id].lastMsg || {};
|
||||
msg.payload = Buffer.alloc(i);
|
||||
buf.copy(msg.payload,0,0,i);
|
||||
node.send(msg);
|
||||
@@ -649,7 +644,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
else if (!clients[connection_id].connecting && clients[connection_id].connected) {
|
||||
if (clients[connection_id] && clients[connection_id].client) {
|
||||
clients[connection_id].client.write(dequeue(clients[connection_id].msgQueue));
|
||||
clients[connection_id].client.write(dequeue(clients[connection_id].msgQueue).payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -92,31 +92,80 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
function getProperty(node,msg) {
|
||||
return new Promise((resolve,reject) => {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise((resolve,reject) => {
|
||||
if (node.propertyType === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (node.propertyType === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return RED.util.evaluateJSONataExpression(node.property,msg);
|
||||
} catch(err) {
|
||||
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
|
||||
}
|
||||
} else {
|
||||
RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg);
|
||||
} catch(err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getV1(node,msg,rule,hasParts) {
|
||||
return new Promise( (resolve,reject) => {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise( (resolve,reject) => {
|
||||
if (rule.vt === 'prev') {
|
||||
resolve(node.previousValue);
|
||||
} else if (rule.vt === 'jsonata') {
|
||||
var exp = rule.v;
|
||||
if (rule.t === 'jsonata_exp') {
|
||||
if (hasParts) {
|
||||
exp.assign("I", msg.parts.index);
|
||||
exp.assign("N", msg.parts.count);
|
||||
}
|
||||
}
|
||||
RED.util.evaluateJSONataExpression(exp,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
} else if (rule.vt === 'json') {
|
||||
resolve("json"); // TODO: ?! invalid case
|
||||
} else if (rule.vt === 'null') {
|
||||
resolve("null");
|
||||
} else {
|
||||
RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (rule.vt === 'prev') {
|
||||
resolve(node.previousValue);
|
||||
return node.previousValue;
|
||||
} else if (rule.vt === 'jsonata') {
|
||||
var exp = rule.v;
|
||||
if (rule.t === 'jsonata_exp') {
|
||||
@@ -125,83 +174,120 @@ module.exports = function(RED) {
|
||||
exp.assign("N", msg.parts.count);
|
||||
}
|
||||
}
|
||||
RED.util.evaluateJSONataExpression(exp,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return RED.util.evaluateJSONataExpression(exp,msg);
|
||||
} catch(err) {
|
||||
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
|
||||
}
|
||||
} else if (rule.vt === 'json') {
|
||||
resolve("json");
|
||||
return "json"; // TODO: ?! invalid case
|
||||
} else if (rule.vt === 'null') {
|
||||
resolve("null");
|
||||
return "null";
|
||||
} else {
|
||||
RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
|
||||
} catch(err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getV2(node,msg,rule) {
|
||||
return new Promise((resolve,reject) => {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise((resolve,reject) => {
|
||||
var v2 = rule.v2;
|
||||
if (rule.v2t === 'prev') {
|
||||
resolve(node.previousValue);
|
||||
} else if (rule.v2t === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
} else if (typeof v2 !== 'undefined') {
|
||||
RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve(v2);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var v2 = rule.v2;
|
||||
if (rule.v2t === 'prev') {
|
||||
resolve(node.previousValue);
|
||||
return node.previousValue;
|
||||
} else if (rule.v2t === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return RED.util.evaluateJSONataExpression(rule.v2,msg);
|
||||
} catch(err) {
|
||||
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
|
||||
}
|
||||
} else if (typeof v2 !== 'undefined') {
|
||||
RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
try {
|
||||
return RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg);
|
||||
} catch(err) {
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
resolve(v2);
|
||||
return v2;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function applyRule(node, msg, property, state) {
|
||||
return new Promise((resolve,reject) => {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise((resolve,reject) => {
|
||||
|
||||
var rule = node.rules[state.currentRule];
|
||||
var v1,v2;
|
||||
var rule = node.rules[state.currentRule];
|
||||
var v1,v2;
|
||||
|
||||
getV1(node,msg,rule,state.hasParts).then(value => {
|
||||
v1 = value;
|
||||
}).then(()=>getV2(node,msg,rule)).then(value => {
|
||||
v2 = value;
|
||||
}).then(() => {
|
||||
if (rule.t == "else") {
|
||||
property = state.elseflag;
|
||||
state.elseflag = true;
|
||||
}
|
||||
if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) {
|
||||
state.onward.push(msg);
|
||||
state.elseflag = false;
|
||||
if (node.checkall == "false") {
|
||||
return resolve(false);
|
||||
getV1(node,msg,rule,state.hasParts).then(value => {
|
||||
v1 = value;
|
||||
}).then(()=>getV2(node,msg,rule)).then(value => {
|
||||
v2 = value;
|
||||
}).then(() => {
|
||||
if (rule.t == "else") {
|
||||
property = state.elseflag;
|
||||
state.elseflag = true;
|
||||
}
|
||||
} else {
|
||||
state.onward.push(null);
|
||||
if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) {
|
||||
state.onward.push(msg);
|
||||
state.elseflag = false;
|
||||
if (node.checkall == "false") {
|
||||
return resolve(false);
|
||||
}
|
||||
} else {
|
||||
state.onward.push(null);
|
||||
}
|
||||
resolve(state.currentRule < node.rules.length - 1);
|
||||
});
|
||||
})
|
||||
} else {
|
||||
var rule = node.rules[state.currentRule];
|
||||
var v1 = getV1(node,msg,rule,state.hasParts);
|
||||
var v2 = getV2(node,msg,rule);
|
||||
if (rule.t == "else") {
|
||||
property = state.elseflag;
|
||||
state.elseflag = true;
|
||||
}
|
||||
if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) {
|
||||
state.onward.push(msg);
|
||||
state.elseflag = false;
|
||||
if (node.checkall == "false") {
|
||||
return false;
|
||||
}
|
||||
resolve(state.currentRule < node.rules.length - 1);
|
||||
});
|
||||
})
|
||||
} else {
|
||||
state.onward.push(null);
|
||||
}
|
||||
return state.currentRule < node.rules.length - 1
|
||||
}
|
||||
}
|
||||
|
||||
function applyRules(node, msg, property,state) {
|
||||
@@ -215,7 +301,18 @@ module.exports = function(RED) {
|
||||
msg.parts.hasOwnProperty("index")
|
||||
}
|
||||
}
|
||||
return applyRule(node,msg,property,state).then(hasMore => {
|
||||
if (node.useAsyncRules) {
|
||||
return applyRule(node,msg,property,state).then(hasMore => {
|
||||
if (hasMore) {
|
||||
state.currentRule++;
|
||||
return applyRules(node,msg,property,state);
|
||||
} else {
|
||||
node.previousValue = property;
|
||||
return state.onward;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var hasMore = applyRule(node,msg,property,state);
|
||||
if (hasMore) {
|
||||
state.currentRule++;
|
||||
return applyRules(node,msg,property,state);
|
||||
@@ -223,7 +320,7 @@ module.exports = function(RED) {
|
||||
node.previousValue = property;
|
||||
return state.onward;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -248,6 +345,14 @@ module.exports = function(RED) {
|
||||
var valid = true;
|
||||
var repair = n.repair;
|
||||
var needsCount = repair;
|
||||
this.useAsyncRules = (
|
||||
this.propertyType === 'flow' ||
|
||||
this.propertyType === 'global' || (
|
||||
this.propertyType === 'jsonata' &&
|
||||
/\$(flow|global)Context/.test(this.property)
|
||||
)
|
||||
);
|
||||
|
||||
for (var i=0; i<this.rules.length; i+=1) {
|
||||
var rule = this.rules[i];
|
||||
needsCount = needsCount || ((rule.t === "tail") || (rule.t === "jsonata_exp"));
|
||||
@@ -258,6 +363,13 @@ module.exports = function(RED) {
|
||||
rule.vt = 'str';
|
||||
}
|
||||
}
|
||||
this.useAsyncRules = this.useAsyncRules || (
|
||||
rule.vt === 'flow' ||
|
||||
rule.vt === 'global' || (
|
||||
rule.vt === 'jsonata' &&
|
||||
/\$(flow|global)Context/.test(rule.v)
|
||||
)
|
||||
);
|
||||
if (rule.vt === 'num') {
|
||||
if (!isNaN(Number(rule.v))) {
|
||||
rule.v = Number(rule.v);
|
||||
@@ -270,6 +382,9 @@ module.exports = function(RED) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (rule.vt === 'flow' || rule.vt === 'global' || rule.vt === 'jsonata') {
|
||||
this.useAsyncRules = true;
|
||||
}
|
||||
if (typeof rule.v2 !== 'undefined') {
|
||||
if (!rule.v2t) {
|
||||
if (!isNaN(Number(rule.v2))) {
|
||||
@@ -278,6 +393,13 @@ module.exports = function(RED) {
|
||||
rule.v2t = 'str';
|
||||
}
|
||||
}
|
||||
this.useAsyncRules = this.useAsyncRules || (
|
||||
rule.v2t === 'flow' ||
|
||||
rule.v2t === 'global' || (
|
||||
rule.v2t === 'jsonata' &&
|
||||
/\$(flow|global)Context/.test(rule.v2)
|
||||
)
|
||||
);
|
||||
if (rule.v2t === 'num') {
|
||||
rule.v2 = Number(rule.v2);
|
||||
} else if (rule.v2t === 'jsonata') {
|
||||
@@ -290,7 +412,6 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
@@ -420,18 +541,32 @@ module.exports = function(RED) {
|
||||
if (needsCount && checkParts && hasParts) {
|
||||
return addMessageToPending(msg);
|
||||
}
|
||||
return getProperty(node,msg)
|
||||
.then(property => applyRules(node,msg,property))
|
||||
.then(onward => {
|
||||
if (!repair || !hasParts) {
|
||||
node.send(onward);
|
||||
}
|
||||
else {
|
||||
sendGroupMessages(onward, msg);
|
||||
}
|
||||
}).catch(err => {
|
||||
node.warn(err);
|
||||
});
|
||||
if (node.useAsyncRules) {
|
||||
return getProperty(node,msg)
|
||||
.then(property => applyRules(node,msg,property))
|
||||
.then(onward => {
|
||||
if (!repair || !hasParts) {
|
||||
node.send(onward);
|
||||
}
|
||||
else {
|
||||
sendGroupMessages(onward, msg);
|
||||
}
|
||||
}).catch(err => {
|
||||
node.warn(err);
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
var property = getProperty(node,msg);
|
||||
var onward = applyRules(node,msg,property);
|
||||
if (!repair || !hasParts) {
|
||||
node.send(onward);
|
||||
} else {
|
||||
sendGroupMessages(onward, msg);
|
||||
}
|
||||
} catch(err) {
|
||||
node.warn(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearPending() {
|
||||
@@ -473,7 +608,11 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
this.on('input', function(msg) {
|
||||
processMessageQueue(msg);
|
||||
if (node.useAsyncRules) {
|
||||
processMessageQueue(msg);
|
||||
} else {
|
||||
processMessage(msg,true);
|
||||
}
|
||||
});
|
||||
|
||||
this.on('close', function() {
|
||||
|
@@ -283,6 +283,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
function applyRules(msg, currentRule) {
|
||||
if (currentRule >= node.rules.length) {
|
||||
return Promise.resolve(msg);
|
||||
}
|
||||
var r = node.rules[currentRule];
|
||||
var rulePromise;
|
||||
if (r.t === "move") {
|
||||
|
@@ -295,7 +295,8 @@
|
||||
For object outputs, once this count has been reached, the node can be configured to send a message for each subsequent message
|
||||
received.</p>
|
||||
<p>A <i>timeout</i> can be set to trigger sending the new message using whatever has been received so far.</p>
|
||||
<p>If a message is received with the <b>msg.complete</b> property set, the output message is sent.</p>
|
||||
<p>If a message is received with the <b>msg.complete</b> property set, the output message is finalised and sent.
|
||||
This resets any part counts.</p>
|
||||
|
||||
<h4>Reduce Sequence mode</h4>
|
||||
<p>When configured to join in reduce mode, an expression is applied to each
|
||||
@@ -439,10 +440,7 @@
|
||||
$("#node-input-joiner").typedInput({
|
||||
default: 'str',
|
||||
typeField: $("#node-input-joinerType"),
|
||||
types:[
|
||||
'str',
|
||||
'bin'
|
||||
]
|
||||
types:['str', 'bin']
|
||||
});
|
||||
|
||||
$("#node-input-property").typedInput({
|
||||
@@ -451,7 +449,7 @@
|
||||
});
|
||||
|
||||
$("#node-input-key").typedInput({
|
||||
types:['msg', {value:"merge", label:"", hasValue:false}]
|
||||
types:['msg']
|
||||
});
|
||||
|
||||
$("#node-input-build").change();
|
||||
|
@@ -586,7 +586,10 @@ module.exports = function(RED) {
|
||||
}
|
||||
else {
|
||||
if (msg.hasOwnProperty('complete')) {
|
||||
completeSend(partId);
|
||||
if (inflight[partId]) {
|
||||
inflight[partId].msg.complete = msg.complete;
|
||||
completeSend(partId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object")
|
||||
@@ -594,6 +597,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inflight.hasOwnProperty(partId)) {
|
||||
if (payloadType === 'object' || payloadType === 'merged') {
|
||||
inflight[partId] = {
|
||||
@@ -604,19 +608,6 @@ module.exports = function(RED) {
|
||||
msg:RED.util.cloneMessage(msg)
|
||||
};
|
||||
}
|
||||
else if (node.accumulate === true) {
|
||||
if (msg.hasOwnProperty("reset")) { delete inflight[partId]; }
|
||||
inflight[partId] = inflight[partId] || {
|
||||
currentCount:0,
|
||||
payload:{},
|
||||
targetCount:targetCount,
|
||||
type:payloadType,
|
||||
msg:RED.util.cloneMessage(msg)
|
||||
}
|
||||
if (payloadType === 'string' || payloadType === 'array' || payloadType === 'buffer') {
|
||||
inflight[partId].payload = [];
|
||||
}
|
||||
}
|
||||
else {
|
||||
inflight[partId] = {
|
||||
currentCount:0,
|
||||
|
@@ -196,9 +196,10 @@ module.exports = function(RED) {
|
||||
}).catch(err => {
|
||||
node.error(err,msg);
|
||||
});
|
||||
return;
|
||||
}
|
||||
var parts = msg.parts;
|
||||
if (!parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) {
|
||||
if (!parts || !parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) {
|
||||
return;
|
||||
}
|
||||
var gid = parts.id;
|
||||
@@ -242,7 +243,8 @@ module.exports = function(RED) {
|
||||
delete pending[key];
|
||||
}
|
||||
}
|
||||
pending_count = 0; })
|
||||
pending_count = 0;
|
||||
});
|
||||
}
|
||||
|
||||
RED.nodes.registerType("sort", SortNode);
|
||||
|
@@ -100,9 +100,9 @@
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
mode: {value:"count"},
|
||||
count: {value:10},
|
||||
overlap: {value:0},
|
||||
interval: {value:10},
|
||||
count: {value:10,validate:function(v) { return RED.validators.number(v) && (v >= 1); }},
|
||||
overlap: {value:0,validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
|
||||
interval: {value:10,validate:function(v) { return RED.validators.number(v) && (v >= 1); }},
|
||||
allowEmptySequence: {value:false},
|
||||
topics: {value:[{topic:""}]}
|
||||
},
|
||||
@@ -149,12 +149,9 @@
|
||||
removable: true
|
||||
});
|
||||
|
||||
$("#node-input-count").spinner({
|
||||
});
|
||||
$("#node-input-overlap").spinner({
|
||||
});
|
||||
$("#node-input-interval").spinner({
|
||||
});
|
||||
$("#node-input-count").spinner({min:1});
|
||||
$("#node-input-overlap").spinner({min:0});
|
||||
$("#node-input-interval").spinner({min:1});
|
||||
$("#node-input-mode").change(function(e) {
|
||||
var val = $(this).val();
|
||||
$(".node-row-msg-count").toggle(val==="count");
|
||||
|
@@ -19,6 +19,7 @@ module.exports = function(RED) {
|
||||
const Ajv = require('ajv');
|
||||
const ajv = new Ajv({allErrors: true, schemaId: 'auto'});
|
||||
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
|
||||
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
|
||||
|
||||
function JSONNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
@@ -29,6 +30,7 @@ module.exports = function(RED) {
|
||||
this.compiledSchema = null;
|
||||
|
||||
var node = this;
|
||||
|
||||
this.on("input", function(msg) {
|
||||
var validate = false;
|
||||
if (msg.schema) {
|
||||
@@ -65,7 +67,17 @@ module.exports = function(RED) {
|
||||
}
|
||||
catch(e) { node.error(e.message,msg); }
|
||||
} else {
|
||||
node.send(msg);
|
||||
// If node.action is str and value is str
|
||||
if (validate) {
|
||||
if (this.compiledSchema(JSON.parse(msg[node.property]))) {
|
||||
node.send(msg);
|
||||
} else {
|
||||
msg.schemaError = this.compiledSchema.errors;
|
||||
node.error(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`, msg);
|
||||
}
|
||||
} else {
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (typeof value === "object") {
|
||||
@@ -84,13 +96,22 @@ module.exports = function(RED) {
|
||||
RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent));
|
||||
node.send(msg);
|
||||
}
|
||||
|
||||
}
|
||||
catch(e) { node.error(RED._("json.errors.dropped-error")); }
|
||||
}
|
||||
else { node.warn(RED._("json.errors.dropped-object")); }
|
||||
} else {
|
||||
node.send(msg);
|
||||
// If node.action is obj and value is object
|
||||
if (validate) {
|
||||
if (this.compiledSchema(value)) {
|
||||
node.send(msg);
|
||||
} else {
|
||||
msg.schemaError = this.compiledSchema.errors;
|
||||
node.error(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`, msg);
|
||||
}
|
||||
} else {
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
else { node.warn(RED._("json.errors.dropped")); }
|
||||
|
@@ -28,9 +28,11 @@ module.exports = function(RED) {
|
||||
this.createDir = n.createDir || false;
|
||||
var node = this;
|
||||
node.wstream = null;
|
||||
node.data = [];
|
||||
node.msgQueue = [];
|
||||
node.closing = false;
|
||||
node.closeCallback = null;
|
||||
|
||||
this.on("input",function(msg) {
|
||||
function processMsg(msg, done) {
|
||||
var filename = node.filename || msg.filename || "";
|
||||
if ((!node.filename) && (!node.tout)) {
|
||||
node.tout = setTimeout(function() {
|
||||
@@ -41,6 +43,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
if (filename === "") {
|
||||
node.warn(RED._("file.errors.nofilename"));
|
||||
done();
|
||||
} else if (node.overwriteFile === "delete") {
|
||||
fs.unlink(filename, function (err) {
|
||||
if (err) {
|
||||
@@ -51,6 +54,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
node.send(msg);
|
||||
}
|
||||
done();
|
||||
});
|
||||
} else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) {
|
||||
var dir = path.dirname(filename);
|
||||
@@ -59,6 +63,7 @@ module.exports = function(RED) {
|
||||
fs.ensureDirSync(dir);
|
||||
} catch(err) {
|
||||
node.error(RED._("file.errors.createfail",{error:err.toString()}),msg);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -70,85 +75,143 @@ module.exports = function(RED) {
|
||||
if (typeof data === "boolean") { data = data.toString(); }
|
||||
if (typeof data === "number") { data = data.toString(); }
|
||||
if ((node.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; }
|
||||
node.data.push({msg:msg,data:Buffer.from(data)});
|
||||
|
||||
while (node.data.length > 0) {
|
||||
if (node.overwriteFile === "true") {
|
||||
(function(packet) {
|
||||
node.wstream = fs.createWriteStream(filename, { encoding:'binary', flags:'w', autoClose:true });
|
||||
node.wstream.on("error", function(err) {
|
||||
node.error(RED._("file.errors.writefail",{error:err.toString()}),msg);
|
||||
});
|
||||
node.wstream.on("open", function() {
|
||||
node.wstream.end(packet.data, function() {
|
||||
node.send(packet.msg);
|
||||
});
|
||||
})
|
||||
})(node.data.shift());
|
||||
}
|
||||
else {
|
||||
// Append mode
|
||||
var recreateStream = !node.wstream || !node.filename;
|
||||
if (node.wstream && node.wstreamIno) {
|
||||
// There is already a stream open and we have the inode
|
||||
// of the file. Check the file hasn't been deleted
|
||||
// or deleted and recreated.
|
||||
try {
|
||||
var stat = fs.statSync(filename);
|
||||
// File exists - check the inode matches
|
||||
if (stat.ino !== node.wstreamIno) {
|
||||
// The file has been recreated. Close the current
|
||||
// stream and recreate it
|
||||
recreateStream = true;
|
||||
node.wstream.end();
|
||||
delete node.wstream;
|
||||
delete node.wstreamIno;
|
||||
}
|
||||
} catch(err) {
|
||||
// File does not exist
|
||||
var buf = Buffer.from(data);
|
||||
if (node.overwriteFile === "true") {
|
||||
var wstream = fs.createWriteStream(filename, { encoding:'binary', flags:'w', autoClose:true });
|
||||
node.wstream = wstream;
|
||||
wstream.on("error", function(err) {
|
||||
node.error(RED._("file.errors.writefail",{error:err.toString()}),msg);
|
||||
done();
|
||||
});
|
||||
wstream.on("open", function() {
|
||||
wstream.end(buf, function() {
|
||||
node.send(msg);
|
||||
done();
|
||||
});
|
||||
})
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// Append mode
|
||||
var recreateStream = !node.wstream || !node.filename;
|
||||
if (node.wstream && node.wstreamIno) {
|
||||
// There is already a stream open and we have the inode
|
||||
// of the file. Check the file hasn't been deleted
|
||||
// or deleted and recreated.
|
||||
try {
|
||||
var stat = fs.statSync(filename);
|
||||
// File exists - check the inode matches
|
||||
if (stat.ino !== node.wstreamIno) {
|
||||
// The file has been recreated. Close the current
|
||||
// stream and recreate it
|
||||
recreateStream = true;
|
||||
node.wstream.end();
|
||||
delete node.wstream;
|
||||
delete node.wstreamIno;
|
||||
}
|
||||
}
|
||||
if (recreateStream) {
|
||||
node.wstream = fs.createWriteStream(filename, { encoding:'binary', flags:'a', autoClose:true });
|
||||
node.wstream.on("open", function(fd) {
|
||||
try {
|
||||
var stat = fs.statSync(filename);
|
||||
node.wstreamIno = stat.ino;
|
||||
} catch(err) {
|
||||
}
|
||||
});
|
||||
node.wstream.on("error", function(err) {
|
||||
node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg);
|
||||
});
|
||||
}
|
||||
if (node.filename) {
|
||||
// Static filename - write and reuse the stream next time
|
||||
var packet = node.data.shift()
|
||||
node.wstream.write(packet.data, function() {
|
||||
node.send(packet.msg);
|
||||
});
|
||||
|
||||
} else {
|
||||
// Dynamic filename - write and close the stream
|
||||
var packet = node.data.shift()
|
||||
node.wstream.end(packet.data, function() {
|
||||
node.send(packet.msg);
|
||||
});
|
||||
} catch(err) {
|
||||
// File does not exist
|
||||
recreateStream = true;
|
||||
node.wstream.end();
|
||||
delete node.wstream;
|
||||
delete node.wstreamIno;
|
||||
}
|
||||
}
|
||||
if (recreateStream) {
|
||||
node.wstream = fs.createWriteStream(filename, { encoding:'binary', flags:'a', autoClose:true });
|
||||
node.wstream.on("open", function(fd) {
|
||||
try {
|
||||
var stat = fs.statSync(filename);
|
||||
node.wstreamIno = stat.ino;
|
||||
} catch(err) {
|
||||
}
|
||||
});
|
||||
node.wstream.on("error", function(err) {
|
||||
node.error(RED._("file.errors.appendfail",{error:err.toString()}),msg);
|
||||
done();
|
||||
});
|
||||
}
|
||||
if (node.filename) {
|
||||
// Static filename - write and reuse the stream next time
|
||||
node.wstream.write(buf, function() {
|
||||
node.send(msg);
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
// Dynamic filename - write and close the stream
|
||||
node.wstream.end(buf, function() {
|
||||
node.send(msg);
|
||||
delete node.wstream;
|
||||
delete node.wstreamIno;
|
||||
done();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function processQ(queue) {
|
||||
var msg = queue[0];
|
||||
processMsg(msg, function() {
|
||||
queue.shift();
|
||||
if (queue.length > 0) {
|
||||
processQ(queue);
|
||||
}
|
||||
else if (node.closing) {
|
||||
closeNode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.on("input", function(msg) {
|
||||
var msgQueue = node.msgQueue;
|
||||
if (msgQueue.push(msg) > 1) {
|
||||
// pending write exists
|
||||
return;
|
||||
}
|
||||
try {
|
||||
processQ(msgQueue);
|
||||
}
|
||||
catch (e) {
|
||||
node.msgQueue = [];
|
||||
if (node.closing) {
|
||||
closeNode();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
this.on('close', function() {
|
||||
|
||||
function closeNode() {
|
||||
if (node.wstream) { node.wstream.end(); }
|
||||
if (node.tout) { clearTimeout(node.tout); }
|
||||
node.status({});
|
||||
var cb = node.closeCallback;
|
||||
node.closeCallback = null;
|
||||
node.closing = false;
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
this.on('close', function(done) {
|
||||
if (node.closing) {
|
||||
// already closing
|
||||
return;
|
||||
}
|
||||
node.closing = true;
|
||||
if (done) {
|
||||
node.closeCallback = done;
|
||||
}
|
||||
if (node.msgQueue.length > 0) {
|
||||
// close after queue processed
|
||||
return;
|
||||
}
|
||||
else {
|
||||
closeNode();
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("file",FileNode);
|
||||
|
33
package.json
33
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "0.19.1",
|
||||
"version": "0.19.6",
|
||||
"description": "A visual tool for wiring the Internet of Things",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -33,8 +33,8 @@
|
||||
"flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "6.5.2",
|
||||
"basic-auth": "2.0.0",
|
||||
"ajv": "6.5.4",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.18.3",
|
||||
"cheerio": "0.22.0",
|
||||
@@ -42,13 +42,14 @@
|
||||
"cookie": "0.3.1",
|
||||
"cookie-parser": "1.4.3",
|
||||
"cors": "2.8.4",
|
||||
"cron": "1.3.0",
|
||||
"cron": "1.5.0",
|
||||
"denque": "1.3.0",
|
||||
"express": "4.16.3",
|
||||
"express": "4.16.4",
|
||||
"express-session": "1.15.6",
|
||||
"fs-extra": "5.0.0",
|
||||
"fs.notify": "0.0.4",
|
||||
"hash-sum": "1.0.2",
|
||||
"https-proxy-agent": "2.2.1",
|
||||
"i18next": "11.6.0",
|
||||
"is-utf8": "0.2.1",
|
||||
"js-yaml": "3.12.0",
|
||||
@@ -56,9 +57,9 @@
|
||||
"jsonata": "1.5.4",
|
||||
"media-typer": "0.3.0",
|
||||
"memorystore": "1.6.0",
|
||||
"mqtt": "2.18.3",
|
||||
"multer": "1.3.1",
|
||||
"mustache": "2.3.1",
|
||||
"mqtt": "2.18.8",
|
||||
"multer": "1.4.1",
|
||||
"mustache": "2.3.2",
|
||||
"node-red-node-email": "0.1.*",
|
||||
"node-red-node-feedparser": "^0.1.12",
|
||||
"node-red-node-rbe": "0.2.*",
|
||||
@@ -71,9 +72,9 @@
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
"raw-body": "2.3.3",
|
||||
"request": "2.88.0",
|
||||
"semver": "5.5.0",
|
||||
"semver": "5.6.0",
|
||||
"sentiment": "2.1.0",
|
||||
"uglify-js": "3.4.7",
|
||||
"uglify-js": "3.4.9",
|
||||
"when": "3.7.8",
|
||||
"ws": "1.1.5",
|
||||
"xml2js": "0.4.19"
|
||||
@@ -82,10 +83,10 @@
|
||||
"bcrypt": "~2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chromedriver": "^2.41.0",
|
||||
"chromedriver": "2.41.0",
|
||||
"grunt": "~1.0.3",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-cli": "~1.2.0",
|
||||
"grunt-cli": "~1.3.1",
|
||||
"grunt-concurrent": "~2.3.1",
|
||||
"grunt-contrib-clean": "~1.1.0",
|
||||
"grunt-contrib-compress": "~1.4.0",
|
||||
@@ -103,15 +104,15 @@
|
||||
"http-proxy": "^1.16.2",
|
||||
"istanbul": "0.4.5",
|
||||
"mocha": "^5.2.0",
|
||||
"node-red-node-test-helper": "0.1.7",
|
||||
"should": "^8.4.0",
|
||||
"sinon": "1.17.7",
|
||||
"stoppable": "^1.0.6",
|
||||
"supertest": "3.1.0",
|
||||
"stoppable": "^1.0.7",
|
||||
"supertest": "3.3.0",
|
||||
"wdio-chromedriver-service": "^0.1.3",
|
||||
"wdio-mocha-framework": "^0.6.2",
|
||||
"wdio-spec-reporter": "^0.1.5",
|
||||
"webdriverio": "^4.13.1",
|
||||
"node-red-node-test-helper": "^0.1.7"
|
||||
"webdriverio": "^4.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
2
red.js
2
red.js
@@ -240,7 +240,7 @@ function basicAuthMiddleware(user,pass) {
|
||||
}
|
||||
var requestUser = basicAuth(req);
|
||||
if (!requestUser || requestUser.name !== user || !checkPasswordAndCache(requestUser.pass)) {
|
||||
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
|
||||
res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
next();
|
||||
|
@@ -102,9 +102,10 @@ module.exports = {
|
||||
var fullPath = redNodes.getNodeExampleFlowPath(module,path);
|
||||
if (fullPath) {
|
||||
try {
|
||||
fs.statSync(fullPath);
|
||||
var resolvedPath = fspath.resolve(fullPath);
|
||||
fs.statSync(resolvedPath);
|
||||
log.audit({event: "library.get",type:"flow",path:req.params[0]},req);
|
||||
return res.sendFile(fullPath,{
|
||||
return res.sendFile(resolvedPath,{
|
||||
headers:{
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
@@ -162,12 +162,10 @@ function start() {
|
||||
if (settings.httpStatic) {
|
||||
log.info(log._("runtime.paths.httpStatic",{path:path.resolve(settings.httpStatic)}));
|
||||
}
|
||||
redNodes.loadContextsPlugin().then(function () {
|
||||
return redNodes.loadContextsPlugin().then(function () {
|
||||
redNodes.loadFlows().then(redNodes.startFlows).catch(function(err) {});
|
||||
started = true;
|
||||
});
|
||||
}).catch(function(err) {
|
||||
console.log(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -163,7 +163,12 @@
|
||||
"error-module-not-defined": "Context store '__storage__' missing 'module' option",
|
||||
"error-invalid-module-name": "Invalid context store name: '__name__'",
|
||||
"error-invalid-default-module": "Default context store unknown: '__storage__'",
|
||||
"unknown-store": "Unknown context store '__name__' specified. Using default store."
|
||||
"unknown-store": "Unknown context store '__name__' specified. Using default store.",
|
||||
"error-loading-module": "Error loading context store: __message__",
|
||||
"localfilesystem": {
|
||||
"error-circular": "Context __scope__ contains a circular reference that cannot be persisted",
|
||||
"error-write": "Error writing context: __message__"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@
|
||||
var clone = require("clone");
|
||||
var log = require("../../log");
|
||||
var memory = require("./memory");
|
||||
var util = require("../../util");
|
||||
|
||||
var settings;
|
||||
|
||||
@@ -170,6 +171,8 @@ function load() {
|
||||
defaultStore = "memory";
|
||||
}
|
||||
return resolve(Promise.all(promises));
|
||||
}).catch(function(err) {
|
||||
throw new Error(log._("context.error-loading-module",{message:err.toString()}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -207,104 +210,131 @@ function createContext(id,seed) {
|
||||
insertSeedValues = function(keys,values) {
|
||||
if (!Array.isArray(keys)) {
|
||||
if (values[0] === undefined) {
|
||||
values[0] = seed[keys];
|
||||
values[0] = util.getObjectProperty(seed,keys);
|
||||
}
|
||||
} else {
|
||||
for (var i=0;i<keys.length;i++) {
|
||||
if (values[i] === undefined) {
|
||||
values[i] = seed[keys[i]];
|
||||
values[i] = util.getObjectProperty(seed,keys[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Object.defineProperties(obj, {
|
||||
get: {
|
||||
value: function(key, storage, callback) {
|
||||
var context;
|
||||
|
||||
obj.get = function(key, storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = stores["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "_";
|
||||
}
|
||||
if (callback && typeof callback !== 'function'){
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
if (callback) {
|
||||
if (!seed) {
|
||||
context.get(scope,key,callback);
|
||||
} else {
|
||||
context.get(scope,key,function() {
|
||||
if (arguments[0]) {
|
||||
callback(arguments[0]);
|
||||
return;
|
||||
if (!callback && typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = undefined;
|
||||
}
|
||||
if (callback && typeof callback !== 'function'){
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
|
||||
if (!Array.isArray(key)) {
|
||||
var keyParts = util.parseContextStore(key);
|
||||
key = keyParts.key;
|
||||
if (!storage) {
|
||||
storage = keyParts.store || "_";
|
||||
}
|
||||
var results = Array.prototype.slice.call(arguments,[1]);
|
||||
insertSeedValues(key,results);
|
||||
// Put the err arg back
|
||||
results.unshift(undefined);
|
||||
callback.apply(null,results);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// No callback, attempt to do this synchronously
|
||||
var results = context.get(scope,key);
|
||||
if (seed) {
|
||||
if (Array.isArray(key)) {
|
||||
insertSeedValues(key,results);
|
||||
} else if (results === undefined){
|
||||
results = seed[key];
|
||||
} else {
|
||||
if (!storage) {
|
||||
storage = "_";
|
||||
}
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
|
||||
if (callback) {
|
||||
if (!seed) {
|
||||
context.get(scope,key,callback);
|
||||
} else {
|
||||
context.get(scope,key,function() {
|
||||
if (arguments[0]) {
|
||||
callback(arguments[0]);
|
||||
return;
|
||||
}
|
||||
var results = Array.prototype.slice.call(arguments,[1]);
|
||||
insertSeedValues(key,results);
|
||||
// Put the err arg back
|
||||
results.unshift(undefined);
|
||||
callback.apply(null,results);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// No callback, attempt to do this synchronously
|
||||
var results = context.get(scope,key);
|
||||
if (seed) {
|
||||
if (Array.isArray(key)) {
|
||||
insertSeedValues(key,results);
|
||||
} else if (results === undefined){
|
||||
results = util.getObjectProperty(seed,key);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
},
|
||||
set: {
|
||||
value: function(key, value, storage, callback) {
|
||||
var context;
|
||||
|
||||
if (!callback && typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = undefined;
|
||||
}
|
||||
if (callback && typeof callback !== 'function'){
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
|
||||
if (!Array.isArray(key)) {
|
||||
var keyParts = util.parseContextStore(key);
|
||||
key = keyParts.key;
|
||||
if (!storage) {
|
||||
storage = keyParts.store || "_";
|
||||
}
|
||||
} else {
|
||||
if (!storage) {
|
||||
storage = "_";
|
||||
}
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
|
||||
context.set(scope, key, value, callback);
|
||||
}
|
||||
},
|
||||
keys: {
|
||||
value: function(storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = stores["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "_";
|
||||
}
|
||||
if (callback && typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
if (seed) {
|
||||
if (callback) {
|
||||
context.keys(scope, function(err,keys) {
|
||||
callback(err,Array.from(new Set(seedKeys.concat(keys)).keys()));
|
||||
});
|
||||
} else {
|
||||
var keys = context.keys(scope);
|
||||
return Array.from(new Set(seedKeys.concat(keys)).keys())
|
||||
}
|
||||
} else {
|
||||
return context.keys(scope, callback);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
};
|
||||
obj.set = function(key, value, storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = stores["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "_";
|
||||
}
|
||||
if (callback && typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
context.set(scope, key, value, callback);
|
||||
};
|
||||
obj.keys = function(storage, callback) {
|
||||
var context;
|
||||
if (!storage && !callback) {
|
||||
context = stores["_"];
|
||||
} else {
|
||||
if (typeof storage === 'function') {
|
||||
callback = storage;
|
||||
storage = "_";
|
||||
}
|
||||
if (callback && typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
}
|
||||
context = getContextStorage(storage);
|
||||
}
|
||||
if (seed) {
|
||||
if (callback) {
|
||||
context.keys(scope, function(err,keys) {
|
||||
callback(err,Array.from(new Set(seedKeys.concat(keys)).keys()));
|
||||
});
|
||||
} else {
|
||||
var keys = context.keys(scope);
|
||||
return Array.from(new Set(seedKeys.concat(keys)).keys())
|
||||
}
|
||||
} else {
|
||||
return context.keys(scope, callback);
|
||||
}
|
||||
};
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -318,9 +348,13 @@ function getContext(localId,flowId) {
|
||||
}
|
||||
var newContext = createContext(contextId);
|
||||
if (flowId) {
|
||||
newContext.flow = getContext(flowId);
|
||||
Object.defineProperty(newContext, 'flow', {
|
||||
value: getContext(flowId)
|
||||
});
|
||||
}
|
||||
newContext.global = contexts['global'];
|
||||
Object.defineProperty(newContext, 'global', {
|
||||
value: contexts['global']
|
||||
})
|
||||
contexts[contextId] = newContext;
|
||||
return newContext;
|
||||
}
|
||||
|
@@ -104,8 +104,6 @@ function loadFile(storagePath){
|
||||
}else{
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}).catch(function(err){
|
||||
throw Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -113,9 +111,21 @@ function listFiles(storagePath) {
|
||||
var promises = [];
|
||||
return fs.readdir(storagePath).then(function(files) {
|
||||
files.forEach(function(file) {
|
||||
promises.push(fs.readdir(path.join(storagePath,file)).then(function(subdirFiles) {
|
||||
return subdirFiles.map(subfile => path.join(file,subfile));
|
||||
}))
|
||||
if (!/^\./.test(file)) {
|
||||
var fullPath = path.join(storagePath,file);
|
||||
var stats = fs.statSync(fullPath);
|
||||
if (stats.isDirectory()) {
|
||||
promises.push(fs.readdir(fullPath).then(function(subdirFiles) {
|
||||
var result = [];
|
||||
subdirFiles.forEach(subfile => {
|
||||
if (/\.json$/.test(subfile)) {
|
||||
result.push(path.join(file,subfile))
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}).then(dirs => dirs.reduce((acc, val) => acc.concat(val), []));
|
||||
@@ -130,6 +140,7 @@ function stringify(value) {
|
||||
function LocalFileSystem(config){
|
||||
this.config = config;
|
||||
this.storageBaseDir = getBasePath(this.config);
|
||||
this.writePromise = Promise.resolve();
|
||||
if (config.hasOwnProperty('cache')?config.cache:true) {
|
||||
this.cache = MemoryStore({});
|
||||
}
|
||||
@@ -172,7 +183,7 @@ LocalFileSystem.prototype.open = function(){
|
||||
if(err.code == 'ENOENT') {
|
||||
return fs.ensureDir(self.storageBaseDir);
|
||||
}else{
|
||||
return Promise.reject(err);
|
||||
throw err;
|
||||
}
|
||||
}).then(function() {
|
||||
self._flushPendingWrites = function() {
|
||||
@@ -185,7 +196,7 @@ LocalFileSystem.prototype.open = function(){
|
||||
var context = newContext[scope];
|
||||
var stringifiedContext = stringify(context);
|
||||
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
|
||||
log.warn("Context "+scope+" contains a circular reference that cannot be persisted");
|
||||
log.warn(log._("error-circular",{scope:scope}));
|
||||
self.knownCircularRefs[scope] = true;
|
||||
} else {
|
||||
delete self.knownCircularRefs[scope];
|
||||
@@ -203,12 +214,13 @@ LocalFileSystem.prototype.open = function(){
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.close = function(){
|
||||
if (this.cache && this._flushPendingWrites) {
|
||||
var self = this;
|
||||
if (this.cache && this._pendingWriteTimeout) {
|
||||
clearTimeout(this._pendingWriteTimeout);
|
||||
delete this._pendingWriteTimeout;
|
||||
return this._flushPendingWrites();
|
||||
this.flushInterval = 0;
|
||||
}
|
||||
return Promise.resolve();
|
||||
return this.writePromise;
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype.get = function(scope, key, callback) {
|
||||
@@ -220,14 +232,31 @@ LocalFileSystem.prototype.get = function(scope, key, callback) {
|
||||
}
|
||||
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
||||
loadFile(storagePath + ".json").then(function(data){
|
||||
var value;
|
||||
if(data){
|
||||
data = JSON.parse(data);
|
||||
if (!Array.isArray(key)) {
|
||||
callback(null, util.getObjectProperty(data,key));
|
||||
try {
|
||||
value = util.getObjectProperty(data,key);
|
||||
} catch(err) {
|
||||
if (err.code === "INVALID_EXPR") {
|
||||
throw err;
|
||||
}
|
||||
value = undefined;
|
||||
}
|
||||
callback(null, value);
|
||||
} else {
|
||||
var results = [undefined];
|
||||
for (var i=0;i<key.length;i++) {
|
||||
results.push(util.getObjectProperty(data,key[i]))
|
||||
try {
|
||||
value = util.getObjectProperty(data,key[i]);
|
||||
} catch(err) {
|
||||
if (err.code === "INVALID_EXPR") {
|
||||
throw err;
|
||||
}
|
||||
value = undefined;
|
||||
}
|
||||
results.push(value)
|
||||
}
|
||||
callback.apply(null,results);
|
||||
}
|
||||
@@ -249,12 +278,18 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) {
|
||||
// there's a pending write which will handle this
|
||||
return;
|
||||
} else {
|
||||
this._pendingWriteTimeout = setTimeout(function() { self._flushPendingWrites.call(self)}, this.flushInterval);
|
||||
this._pendingWriteTimeout = setTimeout(function() {
|
||||
self.writePromise = self.writePromise.then(function(){
|
||||
return self._flushPendingWrites.call(self).catch(function(err) {
|
||||
log.error(log._("context.localfilesystem.error-write",{message:err.toString()}));
|
||||
});
|
||||
});
|
||||
}, this.flushInterval);
|
||||
}
|
||||
} else if (callback && typeof callback !== 'function') {
|
||||
throw new Error("Callback must be a function");
|
||||
} else {
|
||||
loadFile(storagePath + ".json").then(function(data){
|
||||
self.writePromise = self.writePromise.then(function() { return loadFile(storagePath + ".json") }).then(function(data){
|
||||
var obj = data ? JSON.parse(data) : {}
|
||||
if (!Array.isArray(key)) {
|
||||
key = [key];
|
||||
@@ -272,7 +307,7 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) {
|
||||
}
|
||||
var stringifiedContext = stringify(obj);
|
||||
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
|
||||
log.warn("Context "+scope+" contains a circular reference that cannot be persisted");
|
||||
log.warn(log._("error-circular",{scope:scope}));
|
||||
self.knownCircularRefs[scope] = true;
|
||||
} else {
|
||||
delete self.knownCircularRefs[scope];
|
||||
|
@@ -32,7 +32,14 @@ Memory.prototype._getOne = function(scope, key) {
|
||||
var value;
|
||||
var error;
|
||||
if(this.data[scope]){
|
||||
value = util.getObjectProperty(this.data[scope], key);
|
||||
try {
|
||||
value = util.getObjectProperty(this.data[scope], key);
|
||||
} catch(err) {
|
||||
if (err.code === "INVALID_EXPR") {
|
||||
throw err;
|
||||
}
|
||||
value = undefined;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ var i18n;
|
||||
|
||||
var settings;
|
||||
var disableNodePathScan = false;
|
||||
var iconFileExtensions = [".png", ".gif"];
|
||||
var iconFileExtensions = [".png", ".gif", ".svg"];
|
||||
|
||||
function init(runtime) {
|
||||
settings = runtime.settings;
|
||||
@@ -290,6 +290,33 @@ function getNodeFiles(disableNodePathScan) {
|
||||
|
||||
if (!disableNodePathScan) {
|
||||
var moduleFiles = scanTreeForNodesModules();
|
||||
|
||||
// Filter the module list to ignore global modules
|
||||
// that have also been installed locally - allowing the user to
|
||||
// update a module they may not otherwise be able to touch
|
||||
|
||||
moduleFiles.sort(function(A,B) {
|
||||
if (A.local && !B.local) {
|
||||
return -1
|
||||
} else if (!A.local && B.local) {
|
||||
return 1
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
var knownModules = {};
|
||||
moduleFiles = moduleFiles.filter(function(mod) {
|
||||
var result;
|
||||
if (!knownModules[mod.package.name]) {
|
||||
knownModules[mod.package.name] = true;
|
||||
result = true;
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
log.debug("Module: "+mod.package.name+" "+mod.package.version+(result?"":" *ignored due to local copy*"));
|
||||
log.debug(" "+mod.dir);
|
||||
return result;
|
||||
});
|
||||
|
||||
moduleFiles.forEach(function(moduleFile) {
|
||||
var nodeModuleFiles = getModuleNodeFiles(moduleFile);
|
||||
nodeList[moduleFile.package.name] = {
|
||||
|
@@ -358,7 +358,15 @@ Project.prototype.update = function (user, data) {
|
||||
promises.push(util.writeFile(this.paths['README.md'], this.description));
|
||||
}
|
||||
if (savePackage) {
|
||||
promises.push(util.writeFile(this.paths['package.json'], JSON.stringify(this.package,"",4)));
|
||||
promises.push(fs.readFile(project.paths['package.json'],"utf8").then(content => {
|
||||
var currentPackage = {};
|
||||
try {
|
||||
currentPackage = util.parseJSON(content);
|
||||
} catch(err) {
|
||||
}
|
||||
this.package = Object.assign(currentPackage,this.package);
|
||||
return util.writeFile(this.paths['package.json'], JSON.stringify(this.package,"",4));
|
||||
}));
|
||||
}
|
||||
return when.settle(promises).then(function(res) {
|
||||
return {
|
||||
|
@@ -130,13 +130,19 @@ function compareObjects(obj1,obj2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function createError(code, message) {
|
||||
var e = new Error(message);
|
||||
e.code = code;
|
||||
return e;
|
||||
}
|
||||
|
||||
function normalisePropertyExpression(str) {
|
||||
// This must be kept in sync with validatePropertyExpression
|
||||
// in editor/js/ui/utils.js
|
||||
|
||||
var length = str.length;
|
||||
if (length === 0) {
|
||||
throw new Error("Invalid property expression: zero-length");
|
||||
throw createError("INVALID_EXPR","Invalid property expression: zero-length");
|
||||
}
|
||||
var parts = [];
|
||||
var start = 0;
|
||||
@@ -149,14 +155,14 @@ function normalisePropertyExpression(str) {
|
||||
if (!inString) {
|
||||
if (c === "'" || c === '"') {
|
||||
if (i != start) {
|
||||
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
|
||||
}
|
||||
inString = true;
|
||||
quoteChar = c;
|
||||
start = i+1;
|
||||
} else if (c === '.') {
|
||||
if (i===0) {
|
||||
throw new Error("Invalid property expression: unexpected . at position 0");
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected . at position 0");
|
||||
}
|
||||
if (start != i) {
|
||||
v = str.substring(start,i);
|
||||
@@ -167,57 +173,57 @@ function normalisePropertyExpression(str) {
|
||||
}
|
||||
}
|
||||
if (i===length-1) {
|
||||
throw new Error("Invalid property expression: unterminated expression");
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
|
||||
}
|
||||
// Next char is first char of an identifier: a-z 0-9 $ _
|
||||
if (!/[a-z0-9\$\_]/i.test(str[i+1])) {
|
||||
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
|
||||
}
|
||||
start = i+1;
|
||||
} else if (c === '[') {
|
||||
if (i === 0) {
|
||||
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
|
||||
}
|
||||
if (start != i) {
|
||||
parts.push(str.substring(start,i));
|
||||
}
|
||||
if (i===length-1) {
|
||||
throw new Error("Invalid property expression: unterminated expression");
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
|
||||
}
|
||||
// Next char is either a quote or a number
|
||||
if (!/["'\d]/.test(str[i+1])) {
|
||||
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
|
||||
}
|
||||
start = i+1;
|
||||
inBox = true;
|
||||
} else if (c === ']') {
|
||||
if (!inBox) {
|
||||
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
|
||||
}
|
||||
if (start != i) {
|
||||
v = str.substring(start,i);
|
||||
if (/^\d+$/.test(v)) {
|
||||
parts.push(parseInt(v));
|
||||
} else {
|
||||
throw new Error("Invalid property expression: unexpected array expression at position "+start);
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
|
||||
}
|
||||
}
|
||||
start = i+1;
|
||||
inBox = false;
|
||||
} else if (c === ' ') {
|
||||
throw new Error("Invalid property expression: unexpected ' ' at position "+i);
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected ' ' at position "+i);
|
||||
}
|
||||
} else {
|
||||
if (c === quoteChar) {
|
||||
if (i-start === 0) {
|
||||
throw new Error("Invalid property expression: zero-length string at position "+start);
|
||||
throw createError("INVALID_EXPR","Invalid property expression: zero-length string at position "+start);
|
||||
}
|
||||
parts.push(str.substring(start,i));
|
||||
// If inBox, next char must be a ]. Otherwise it may be [ or .
|
||||
if (inBox && !/\]/.test(str[i+1])) {
|
||||
throw new Error("Invalid property expression: unexpected array expression at position "+start);
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
|
||||
} else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) {
|
||||
throw new Error("Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
|
||||
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
|
||||
}
|
||||
start = i+1;
|
||||
inString = false;
|
||||
@@ -226,7 +232,7 @@ function normalisePropertyExpression(str) {
|
||||
|
||||
}
|
||||
if (inBox || inString) {
|
||||
throw new Error("Invalid property expression: unterminated expression");
|
||||
throw new createError("INVALID_EXPR","Invalid property expression: unterminated expression");
|
||||
}
|
||||
if (start < length) {
|
||||
parts.push(str.substring(start));
|
||||
|
@@ -148,7 +148,7 @@ module.exports = {
|
||||
|
||||
// The following property can be used to cause insecure HTTP connections to
|
||||
// be redirected to HTTPS.
|
||||
//requireHttps: true
|
||||
//requireHttps: true,
|
||||
|
||||
// The following property can be used to disable the editor. The admin API
|
||||
// is not affected by this option. To disable both the editor and the admin
|
||||
|
@@ -24,7 +24,7 @@ function injectNode(id) {
|
||||
|
||||
util.inherits(injectNode, nodePage);
|
||||
|
||||
var payloadType = {
|
||||
var payloadTypeList = {
|
||||
"flow": 1,
|
||||
"global": 2,
|
||||
"str": 3,
|
||||
@@ -36,54 +36,43 @@ var payloadType = {
|
||||
"env": 9,
|
||||
};
|
||||
|
||||
var timeType = {
|
||||
var repeatTypeList = {
|
||||
"none": 1,
|
||||
"interval": 2,
|
||||
"intervalBetweenTimes": 3,
|
||||
"atASpecificTime": 4,
|
||||
};
|
||||
|
||||
var timeType = {
|
||||
"none": 1,
|
||||
"interval": 2,
|
||||
"intervalBetweenTimes": 3,
|
||||
"atASpecificTime": 4,
|
||||
};
|
||||
|
||||
var timeType = {
|
||||
"none": 1,
|
||||
"interval": 2,
|
||||
"intervalBetweenTimes": 3,
|
||||
"atASpecificTime": 4,
|
||||
};
|
||||
|
||||
injectNode.prototype.setPayload = function(type, value) {
|
||||
injectNode.prototype.setPayload = function(payloadType, payload) {
|
||||
// Open a payload type list.
|
||||
browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-container")]');
|
||||
// Select a payload type.
|
||||
var payloadTypeXPath = '//*[@class="red-ui-typedInput-options"]/a[' + payloadType[type] + ']';
|
||||
var payloadTypeXPath = '//*[@class="red-ui-typedInput-options"]/a[' + payloadTypeList[payloadType] + ']';
|
||||
browser.clickWithWait(payloadTypeXPath);
|
||||
if (value) {
|
||||
if (payload) {
|
||||
// Input a value.
|
||||
browser.setValue('//*[@class="red-ui-typedInput-input"]/input', value);
|
||||
browser.setValue('//*[@class="red-ui-typedInput-input"]/input', payload);
|
||||
}
|
||||
}
|
||||
|
||||
injectNode.prototype.setTopic = function(value) {
|
||||
browser.setValue('#node-input-topic', value);
|
||||
injectNode.prototype.setTopic = function(topic) {
|
||||
browser.setValue('#node-input-topic', topic);
|
||||
}
|
||||
|
||||
injectNode.prototype.setOnce = function(value) {
|
||||
browser.clickWithWait('#node-input-once');
|
||||
injectNode.prototype.setOnce = function(once) {
|
||||
var isChecked = browser.isSelected('#node-input-once');
|
||||
if (isChecked !== once) {
|
||||
browser.clickWithWait('#node-input-once');
|
||||
}
|
||||
}
|
||||
|
||||
injectNode.prototype.setTimeType = function(type) {
|
||||
var timeTypeXPath = '//*[@id="inject-time-type-select"]/option[' + timeType[type] + ']';
|
||||
browser.clickWithWait(timeTypeXPath);
|
||||
injectNode.prototype.setRepeat = function(repeatType) {
|
||||
var repeatTypeXPath = '//*[@id="inject-time-type-select"]/option[' + repeatTypeList[repeatType] + ']';
|
||||
browser.clickWithWait(repeatTypeXPath);
|
||||
}
|
||||
|
||||
injectNode.prototype.setRepeat = function(sec) {
|
||||
browser.setValue('#inject-time-interval-count', sec);
|
||||
injectNode.prototype.setRepeatInterval = function(repeat) {
|
||||
browser.setValue('#inject-time-interval-count', repeat);
|
||||
}
|
||||
|
||||
module.exports = injectNode;
|
||||
|
@@ -24,22 +24,20 @@ function debugNode(id) {
|
||||
|
||||
util.inherits(debugNode, nodePage);
|
||||
|
||||
var target = {
|
||||
"msg": 1,
|
||||
"full": 2
|
||||
};
|
||||
|
||||
debugNode.prototype.setTarget = function(type, value) {
|
||||
debugNode.prototype.setOutput = function(complete) {
|
||||
// Open a payload type list.
|
||||
browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-container")]/button');
|
||||
// Select a payload type.
|
||||
var xPath = '/html/body/div[11]/a[' + target[type] + ']';
|
||||
browser.clickWithWait(xPath);
|
||||
if (value) {
|
||||
if (complete !== 'true') {
|
||||
// Select the "msg" type.
|
||||
browser.clickWithWait('/html/body/div[11]/a[1]');
|
||||
// Input the path in msg.
|
||||
browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-input")]/input');
|
||||
browser.keys(['Control', 'a', 'Control']);
|
||||
browser.keys(['Delete']);
|
||||
browser.setValue('//*[contains(@class, "red-ui-typedInput-input")]/input', value);
|
||||
browser.setValue('//*[contains(@class, "red-ui-typedInput-input")]/input', complete);
|
||||
} else {
|
||||
// Select the "complete msg object" type.
|
||||
browser.clickWithWait('/html/body/div[11]/a[2]');
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,12 +24,17 @@ function functionNode(id) {
|
||||
|
||||
util.inherits(functionNode, nodePage);
|
||||
|
||||
functionNode.prototype.setCode = function(value) {
|
||||
functionNode.prototype.setFunction = function(func) {
|
||||
browser.click('#node-input-func-editor');
|
||||
browser.keys(['Control', 'Home', 'Control']);
|
||||
for (var i=0; i<value.length; i++) {
|
||||
browser.keys([value.substr(i, 1)]);
|
||||
for (var i = 0; i < func.length; i++) {
|
||||
browser.keys([func.charAt(i)]);
|
||||
}
|
||||
// Delete the unnecessary code that ace editor does the autocompletion.
|
||||
browser.keys(['Control', 'Shift', 'End', 'Shift', 'Control']);
|
||||
browser.keys(['Delete']);
|
||||
// Need to wait until ace editor correctly checks the syntax.
|
||||
browser.pause(50);
|
||||
}
|
||||
|
||||
module.exports = functionNode;
|
||||
|
@@ -24,29 +24,20 @@ function templateNode(id) {
|
||||
|
||||
util.inherits(templateNode, nodePage);
|
||||
|
||||
var syntaxType = {
|
||||
"mustache": 1,
|
||||
"plain": 2
|
||||
};
|
||||
|
||||
templateNode.prototype.setSyntax = function(type) {
|
||||
// Open a method type list.
|
||||
browser.clickWithWait('#node-input-syntax');
|
||||
// Select a method type.
|
||||
var syntaxTypeXPath = '//*[@id="node-input-syntax"]/option[' + syntaxType[type] + ']';
|
||||
browser.clickWithWait(syntaxTypeXPath);
|
||||
templateNode.prototype.setSyntax = function(syntax) {
|
||||
browser.selectWithWait('#node-input-syntax', syntax);
|
||||
}
|
||||
|
||||
templateNode.prototype.setFormat = function(type) {
|
||||
browser.selectByValue('#node-input-format', type);
|
||||
templateNode.prototype.setFormat = function(format) {
|
||||
browser.selectWithWait('#node-input-format', format);
|
||||
}
|
||||
|
||||
templateNode.prototype.setTemplate = function(value) {
|
||||
templateNode.prototype.setTemplate = function(template) {
|
||||
browser.click('#node-input-template-editor');
|
||||
browser.keys(['Control', 'a', 'Control']); // call twice to release the keys.
|
||||
// Need to add a character one by one since some words such as 'Control' are treated as a special word.
|
||||
for (var i=0; i<value.length; i++) {
|
||||
browser.keys([value.charAt(i)]);
|
||||
for (var i = 0; i < template.length; i++) {
|
||||
browser.keys([template.charAt(i)]);
|
||||
}
|
||||
browser.keys(['Control', 'Shift', 'End', 'Shift', 'Control']);
|
||||
browser.keys(['Delete']);
|
||||
|
@@ -22,30 +22,14 @@ function httpinNode(id) {
|
||||
nodePage.call(this, id);
|
||||
}
|
||||
|
||||
function setMethod(type) {
|
||||
browser.selectByValue('#node-input-method', type);
|
||||
}
|
||||
|
||||
util.inherits(httpinNode, nodePage);
|
||||
|
||||
var methodType = {
|
||||
"get": 1,
|
||||
"post": 2,
|
||||
"put": 3,
|
||||
"delete": 4,
|
||||
"patch": 5,
|
||||
};
|
||||
|
||||
httpinNode.prototype.setMethod = function(type) {
|
||||
// Open a method type list.
|
||||
browser.clickWithWait('#node-input-method');
|
||||
// Select a method type.
|
||||
var methodTypeXPath = '//*[@id="node-input-method"]/option[' + methodType[type] + ']';
|
||||
browser.clickWithWait(methodTypeXPath);
|
||||
httpinNode.prototype.setMethod = function(method) {
|
||||
browser.selectWithWait('#node-input-method', method);
|
||||
}
|
||||
|
||||
httpinNode.prototype.setUrl = function(value) {
|
||||
browser.setValue('#node-input-url', value);
|
||||
httpinNode.prototype.setUrl = function(url) {
|
||||
browser.setValue('#node-input-url', url);
|
||||
}
|
||||
|
||||
module.exports = httpinNode;
|
||||
|
@@ -24,36 +24,16 @@ function httpRequestNode(id) {
|
||||
|
||||
util.inherits(httpRequestNode, nodePage);
|
||||
|
||||
var methodType = {
|
||||
"get": 1,
|
||||
"post": 2,
|
||||
"put": 3,
|
||||
"delete": 4,
|
||||
"setByMsgMethod": 5,
|
||||
};
|
||||
|
||||
var retType = {
|
||||
"txt": 1,
|
||||
"bin": 2,
|
||||
"obj": 3,
|
||||
};
|
||||
|
||||
httpRequestNode.prototype.setUrl = function(value) {
|
||||
browser.setValue('#node-input-url', value);
|
||||
httpRequestNode.prototype.setUrl = function(url) {
|
||||
browser.setValue('#node-input-url', url);
|
||||
}
|
||||
|
||||
httpRequestNode.prototype.setMethod = function(type) {
|
||||
// Open a method type list.
|
||||
browser.clickWithWait('#node-input-method');
|
||||
// Select a method type.
|
||||
var methodTypeXPath = '//*[@id="node-input-method"]/option[' + methodType[type] + ']';
|
||||
browser.clickWithWait(methodTypeXPath);
|
||||
httpRequestNode.prototype.setMethod = function(method) {
|
||||
browser.selectWithWait('#node-input-method', method);
|
||||
}
|
||||
|
||||
httpRequestNode.prototype.setRet = function(type) {
|
||||
browser.clickWithWait('#node-input-ret');
|
||||
var retTypeXPath = '//*[@id="node-input-ret"]/option[' + retType[type] + ']';
|
||||
browser.clickWithWait(retTypeXPath);
|
||||
httpRequestNode.prototype.setReturn = function(ret) {
|
||||
browser.selectWithWait('#node-input-ret', ret);
|
||||
}
|
||||
|
||||
module.exports = httpRequestNode;
|
||||
|
@@ -24,13 +24,6 @@ function changeNode(id) {
|
||||
|
||||
util.inherits(changeNode, nodePage);
|
||||
|
||||
var tType = {
|
||||
"set": 1,
|
||||
"change": 2,
|
||||
"delete": 3,
|
||||
"move": 4,
|
||||
};
|
||||
|
||||
var totType = {
|
||||
"msg": 1,
|
||||
"flow": 2,
|
||||
@@ -42,6 +35,7 @@ var totType = {
|
||||
"bin": 8,
|
||||
"date": 9,
|
||||
"jsonata": 10,
|
||||
"env": 11,
|
||||
};
|
||||
|
||||
var ptType = {
|
||||
@@ -50,8 +44,8 @@ var ptType = {
|
||||
"global": 3,
|
||||
};
|
||||
|
||||
function setT(rule, index) {
|
||||
browser.selectByValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', rule);
|
||||
function setT(t, index) {
|
||||
browser.selectWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', t);
|
||||
}
|
||||
|
||||
// It is better to create a function whose input value is the object type in the future,
|
||||
|
@@ -24,8 +24,8 @@ function rangeNode(id) {
|
||||
|
||||
util.inherits(rangeNode, nodePage);
|
||||
|
||||
rangeNode.prototype.setAction = function(value) {
|
||||
browser.selectByValue('#node-input-action', value);
|
||||
rangeNode.prototype.setAction = function(action) {
|
||||
browser.selectWithWait('#node-input-action', action);
|
||||
}
|
||||
|
||||
rangeNode.prototype.setRange = function(minin, maxin, minout, maxout) {
|
||||
|
@@ -24,8 +24,8 @@ function htmlNode(id) {
|
||||
|
||||
util.inherits(htmlNode, nodePage);
|
||||
|
||||
htmlNode.prototype.setTag = function(value) {
|
||||
browser.setValue('#node-input-tag', value);
|
||||
htmlNode.prototype.setSelector = function(tag) {
|
||||
browser.setValue('#node-input-tag', tag);
|
||||
}
|
||||
|
||||
module.exports = htmlNode;
|
||||
|
@@ -31,13 +31,13 @@ var formatType = {
|
||||
"stream": 4
|
||||
};
|
||||
|
||||
fileinNode.prototype.setFilename = function(value) {
|
||||
browser.setValue('#node-input-filename', value);
|
||||
fileinNode.prototype.setFilename = function(filename) {
|
||||
browser.setValue('#node-input-filename', filename);
|
||||
}
|
||||
|
||||
fileinNode.prototype.setFormat = function(type) {
|
||||
fileinNode.prototype.setOutput = function(format) {
|
||||
browser.clickWithWait('#node-input-format');
|
||||
var formatTypeXPath = '//*[@id="node-input-format"]/option[' + formatType[type] + ']';
|
||||
var formatTypeXPath = '//*[@id="node-input-format"]/option[' + formatType[format] + ']';
|
||||
browser.clickWithWait(formatTypeXPath);
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,12 @@ function init() {
|
||||
var ret = browser.getText(selector);
|
||||
return ret;
|
||||
}, false);
|
||||
|
||||
browser.addCommand("selectWithWait", function(selector, value) {
|
||||
browser.waitForVisible(selector, 5000);
|
||||
var ret = browser.selectByValue(selector, value);
|
||||
return ret;
|
||||
}, false);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@@ -19,8 +19,8 @@ var should = require("should");
|
||||
var fs = require('fs-extra');
|
||||
|
||||
var helper = require("../../editor_helper");
|
||||
var debugTab = require('../../pageobjects/workspace/debugTab_page');
|
||||
var workspace = require('../../pageobjects/workspace/workspace_page');
|
||||
var debugTab = require('../../pageobjects/editor/debugTab_page');
|
||||
var workspace = require('../../pageobjects/editor/workspace_page');
|
||||
|
||||
var nodeWidth = 200;
|
||||
|
@@ -17,8 +17,8 @@
|
||||
var should = require("should");
|
||||
|
||||
var helper = require("../../editor_helper");
|
||||
var debugTab = require('../../pageobjects/workspace/debugTab_page');
|
||||
var workspace = require('../../pageobjects/workspace/workspace_page');
|
||||
var debugTab = require('../../pageobjects/editor/debugTab_page');
|
||||
var workspace = require('../../pageobjects/editor/workspace_page');
|
||||
|
||||
var nodeWidth = 200;
|
||||
var nodeHeight = 100;
|
||||
@@ -65,7 +65,7 @@ describe('cookbook', function() {
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello');
|
||||
httpRequestNode.setMethod("get");
|
||||
httpRequestNode.setMethod("GET");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequestNode);
|
||||
@@ -105,7 +105,7 @@ describe('cookbook', function() {
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-query?name=Nick');
|
||||
httpRequestNode.setMethod("get");
|
||||
httpRequestNode.setMethod("GET");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequestNode);
|
||||
@@ -145,7 +145,7 @@ describe('cookbook', function() {
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-param/Dave');
|
||||
httpRequestNode.setMethod("get");
|
||||
httpRequestNode.setMethod("GET");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequestNode);
|
||||
@@ -185,12 +185,12 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 3, nodeHeight);
|
||||
|
||||
changeNode.edit();
|
||||
changeNode.ruleSet("headers", "msg", "{\"user-agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64)\"}", "json");
|
||||
changeNode.ruleSet("headers", "msg", '{"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}', "json");
|
||||
changeNode.clickOk();
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-headers');
|
||||
httpRequestNode.setMethod("get");
|
||||
httpRequestNode.setMethod("GET");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(changeNode);
|
||||
@@ -249,7 +249,7 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 2, nodeHeight * 2);
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setMethod("get");
|
||||
httpRequestNode.setMethod("GET");
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-data');
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
@@ -280,7 +280,7 @@ describe('cookbook', function() {
|
||||
templateNode.edit();
|
||||
templateNode.setSyntax("mustache");
|
||||
templateNode.setFormat("handlebars");
|
||||
templateNode.setTemplate("{ \"Hello\": \"World\" }");
|
||||
templateNode.setTemplate('{ "Hello": "World" }');
|
||||
templateNode.clickOk();
|
||||
|
||||
changeNode.edit();
|
||||
@@ -299,12 +299,12 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 2, nodeHeight);
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setMethod("get");
|
||||
httpRequestNode.setMethod("GET");
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-json');
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
debugNode.edit();
|
||||
debugNode.setTarget("msg", "headers");
|
||||
debugNode.setOutput("headers");
|
||||
debugNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequestNode);
|
||||
@@ -332,7 +332,7 @@ describe('cookbook', function() {
|
||||
|
||||
fileinNode.edit();
|
||||
fileinNode.setFilename("test/resources/file-in-node/test.txt");
|
||||
fileinNode.setFormat("");
|
||||
fileinNode.setOutput("");
|
||||
fileinNode.clickOk();
|
||||
|
||||
changeNode.edit();
|
||||
@@ -352,7 +352,7 @@ describe('cookbook', function() {
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-file');
|
||||
httpRequestNode.setMethod("get");
|
||||
httpRequestNode.setMethod("GET");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequestNode);
|
||||
@@ -396,7 +396,7 @@ describe('cookbook', function() {
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-raw');
|
||||
httpRequestNode.setMethod("post");
|
||||
httpRequestNode.setMethod("POST");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequestNode);
|
||||
@@ -440,12 +440,12 @@ describe('cookbook', function() {
|
||||
injectNode.clickOk();
|
||||
|
||||
changeNode.edit();
|
||||
changeNode.ruleSet("headers", "msg", "{\"content-type\":\"application/x-www-form-urlencoded\"}", "json");
|
||||
changeNode.ruleSet("headers", "msg", '{"content-type":"application/x-www-form-urlencoded"}', "json");
|
||||
changeNode.clickOk();
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-form');
|
||||
httpRequestNode.setMethod("post");
|
||||
httpRequestNode.setMethod("POST");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(changeNode);
|
||||
@@ -486,16 +486,16 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 3, nodeHeight);
|
||||
|
||||
injectNode.edit()
|
||||
injectNode.setPayload("json", "{\"name\":\"Nick\"}");
|
||||
injectNode.setPayload("json", '{"name":"Nick"}');
|
||||
injectNode.clickOk();
|
||||
|
||||
changeNode.edit();
|
||||
changeNode.ruleSet("headers", "msg", "{\"content-type\":\"application/json\"}", "json");
|
||||
changeNode.ruleSet("headers", "msg", '{"content-type":"application/json"}', "json");
|
||||
changeNode.clickOk();
|
||||
|
||||
httpRequestNode.edit();
|
||||
httpRequestNode.setUrl(helper.url() + httpNodeRoot + '/hello-json');
|
||||
httpRequestNode.setMethod("post");
|
||||
httpRequestNode.setMethod("POST");
|
||||
httpRequestNode.clickOk();
|
||||
|
||||
injectNode.connect(changeNode);
|
||||
@@ -531,13 +531,13 @@ describe('cookbook', function() {
|
||||
httpinNodeFormat.clickOk();
|
||||
|
||||
functionNodeFormat.edit();
|
||||
functionNodeFormat.setCode("msg.payload = JSON.stringify(msg.req.cookies,null,4);");
|
||||
functionNodeFormat.setFunction("msg.payload = JSON.stringify(msg.req.cookies,null,4);\nreturn msg;");
|
||||
functionNodeFormat.clickOk();
|
||||
|
||||
templateNode.edit();
|
||||
templateNode.setSyntax("mustache");
|
||||
templateNode.setFormat("handlebars");
|
||||
templateNode.setTemplate("<html>\n<head></head>\n<body>\n<h1>Cookies</h1>\n<p></p><a href=\"hello-cookie/add\">Add a cookie</a> • <a href=\"hello-cookie/clear\">Clear cookies</a></p>\n<pre>{{ payload }}</pre>\n</body>\n</html>");
|
||||
templateNode.setTemplate('<html>\n<head></head>\n<body>\n<h1>Cookies</h1>\n<p></p><a href="hello-cookie/add">Add a cookie</a> • <a href="hello-cookie/clear">Clear cookies</a></p>\n<pre>{{ payload }}</pre>\n</body>\n</html>');
|
||||
templateNode.clickOk();
|
||||
|
||||
httpinNodeFormat.connect(functionNodeFormat);
|
||||
@@ -550,7 +550,7 @@ describe('cookbook', function() {
|
||||
httpinNodeAdd.clickOk();
|
||||
|
||||
functionNodeAdd.edit();
|
||||
functionNodeAdd.setCode("msg.cookies = { };\n msg.cookies[\"demo-\"+(Math.floor(Math.random()*1000))] = Date.now();");
|
||||
functionNodeAdd.setFunction('msg.cookies = { };\n msg.cookies["demo-"+(Math.floor(Math.random()*1000))] = Date.now();\nreturn msg;');
|
||||
functionNodeAdd.clickOk();
|
||||
|
||||
changeNode.edit();
|
||||
@@ -571,7 +571,7 @@ describe('cookbook', function() {
|
||||
httpinNodeClear.clickOk();
|
||||
|
||||
functionNodeClear.edit();
|
||||
functionNodeClear.setCode("var cookieNames = Object.keys(msg.req.cookies).filter(function(cookieName) { return /^demo-/.test(cookieName);});\nmsg.cookies = {};\n\ncookieNames.forEach(function(cookieName) {\n msg.cookies[cookieName] = null;\n});\n\n");
|
||||
functionNodeClear.setFunction("var cookieNames = Object.keys(msg.req.cookies).filter(function(cookieName) { return /^demo-/.test(cookieName);});\nmsg.cookies = {};\n\ncookieNames.forEach(function(cookieName) {\n msg.cookies[cookieName] = null;\n});\nreturn msg;\n");
|
||||
functionNodeClear.clickOk();
|
||||
|
||||
httpinNodeClear.connect(functionNodeClear);
|
||||
|
@@ -19,8 +19,8 @@ var should = require("should");
|
||||
var fs = require('fs-extra');
|
||||
|
||||
var helper = require("../../editor_helper");
|
||||
var debugTab = require('../../pageobjects/workspace/debugTab_page');
|
||||
var workspace = require('../../pageobjects/workspace/workspace_page');
|
||||
var debugTab = require('../../pageobjects/editor/debugTab_page');
|
||||
var workspace = require('../../pageobjects/editor/workspace_page');
|
||||
var specUtil = require('../../pageobjects/util/spec_util_page');
|
||||
|
||||
var nodeWidth = 200;
|
||||
@@ -168,8 +168,8 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 2);
|
||||
|
||||
injectNode.edit();
|
||||
injectNode.setTimeType("interval");
|
||||
injectNode.setRepeat(1);
|
||||
injectNode.setRepeat("interval");
|
||||
injectNode.setRepeatInterval(1);
|
||||
injectNode.clickOk();
|
||||
injectNode.connect(debugNode);
|
||||
|
||||
@@ -196,12 +196,12 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 3);
|
||||
|
||||
httpRequetNode.edit();
|
||||
httpRequetNode.setMethod("get");
|
||||
httpRequetNode.setMethod("GET");
|
||||
httpRequetNode.setUrl(helper.url());
|
||||
httpRequetNode.clickOk();
|
||||
|
||||
htmlNode.edit();
|
||||
htmlNode.setTag("title");
|
||||
htmlNode.setSelector("title");
|
||||
htmlNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequetNode);
|
||||
@@ -336,14 +336,14 @@ describe('cookbook', function() {
|
||||
changeNodeSetPost.clickOk();
|
||||
|
||||
httpRequetNode.edit();
|
||||
httpRequetNode.setMethod("get");
|
||||
httpRequetNode.setMethod("GET");
|
||||
var url = helper.url() + httpNodeRoot + "/{{post}}";
|
||||
httpRequetNode.setUrl(url);
|
||||
httpRequetNode.setRet("obj");
|
||||
httpRequetNode.setReturn("obj");
|
||||
httpRequetNode.clickOk();
|
||||
|
||||
debugNode.edit();
|
||||
debugNode.setTarget("msg", "payload.title");
|
||||
debugNode.setOutput("payload.title");
|
||||
debugNode.clickOk();
|
||||
|
||||
injectNode.connect(changeNodeSetPost);
|
||||
@@ -364,11 +364,11 @@ describe('cookbook', function() {
|
||||
templateNode.edit();
|
||||
templateNode.setSyntax("mustache");
|
||||
templateNode.setFormat("handlebars");
|
||||
templateNode.setTemplate("{\"title\": \"Hello\"}");
|
||||
templateNode.setTemplate('{"title": "Hello"}');
|
||||
templateNode.clickOk();
|
||||
|
||||
changeNodeSetHeader.edit();
|
||||
changeNodeSetHeader.ruleSet("headers", "msg", "{\"content-type\":\"application/json\"}", "json");
|
||||
changeNodeSetHeader.ruleSet("headers", "msg", '{"content-type":"application/json"}', "json");
|
||||
changeNodeSetHeader.clickOk();
|
||||
|
||||
httpinNode.connect(templateNode);
|
||||
@@ -389,9 +389,9 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 2);
|
||||
|
||||
httpRequetNode.edit();
|
||||
httpRequetNode.setMethod("get");
|
||||
httpRequetNode.setMethod("GET");
|
||||
httpRequetNode.setUrl(helper.url() + "/settings");
|
||||
httpRequetNode.setRet("bin");
|
||||
httpRequetNode.setReturn("bin");
|
||||
httpRequetNode.clickOk();
|
||||
|
||||
injectNode.connect(httpRequetNode);
|
||||
@@ -413,11 +413,11 @@ describe('cookbook', function() {
|
||||
var debugNode = workspace.addNode("debug", nodeWidth * 3);
|
||||
|
||||
functionNode.edit();
|
||||
functionNode.setCode("msg.payload = \"data to post\";");
|
||||
functionNode.setFunction('msg.payload = "data to post";\nreturn msg;');
|
||||
functionNode.clickOk();
|
||||
|
||||
httpRequetNode.edit();
|
||||
httpRequetNode.setMethod("post");
|
||||
httpRequetNode.setMethod("POST");
|
||||
var url = helper.url() + httpNodeRoot + "/set-header";
|
||||
httpRequetNode.setUrl(url);
|
||||
httpRequetNode.clickOk();
|
||||
|
@@ -1216,7 +1216,7 @@ describe('function node', function() {
|
||||
msg.should.have.property('payload', n1.id);
|
||||
done();
|
||||
});
|
||||
n1.receive({payload:"foo",topicb: "bar"});
|
||||
n1.receive({payload:"foo",topic: "bar"});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1230,7 +1230,7 @@ describe('function node', function() {
|
||||
msg.should.have.property('payload', n1.name);
|
||||
done();
|
||||
});
|
||||
n1.receive({payload:"foo",topicb: "bar"});
|
||||
n1.receive({payload:"foo",topic: "bar"});
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -15,7 +15,8 @@
|
||||
**/
|
||||
|
||||
var should = require("should");
|
||||
var rpi = require("../../../../nodes/core/hardware/36-rpi-gpio.js");
|
||||
var rpiNode = require("../../../../nodes/core/hardware/36-rpi-gpio.js");
|
||||
var statusNode = require("../../../../nodes/core/core/25-status.js");
|
||||
var helper = require("node-red-node-test-helper");
|
||||
var fs = require("fs");
|
||||
|
||||
@@ -50,7 +51,7 @@ describe('RPI GPIO Node', function() {
|
||||
|
||||
it('should load Input node', function(done) {
|
||||
var flow = [{id:"n1", type:"rpi-gpio in", name:"rpi-gpio in" }];
|
||||
helper.load(rpi, flow, function() {
|
||||
helper.load(rpiNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
n1.should.have.property('name', 'rpi-gpio in');
|
||||
try {
|
||||
@@ -69,7 +70,7 @@ describe('RPI GPIO Node', function() {
|
||||
|
||||
it('should load Output node', function(done) {
|
||||
var flow = [{id:"n1", type:"rpi-gpio out", name:"rpi-gpio out" }];
|
||||
helper.load(rpi, flow, function() {
|
||||
helper.load(rpiNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
n1.should.have.property('name', 'rpi-gpio out');
|
||||
try {
|
||||
@@ -86,4 +87,62 @@ describe('RPI GPIO Node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should read a dummy value high (not on Pi)', function(done) {
|
||||
var flow = [{id:"n1", type:"rpi-gpio in", pin:"7", intype:"up", debounce:"25", read:true, wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(rpiNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('topic', 'pi/7');
|
||||
msg.should.have.property('payload', 1);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should read a dummy value low (not on Pi)', function(done) {
|
||||
var flow = [{id:"n1", type:"rpi-gpio in", pin:"11", intype:"down", debounce:"25", read:true, wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(rpiNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('topic', 'pi/11');
|
||||
msg.should.have.property('payload', 0);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able preset out to a dummy value (not on Pi)', function(done) {
|
||||
var flow = [{id:"n1", type:"rpi-gpio out", pin:"7", out:"out", level:"0", set:true, freq:"", wires:[], z:"1"},
|
||||
{id:"n2", type:"status", scope:null, wires:[["n3"]], z:"1"},
|
||||
{id:"n3", type:"helper", z:"1"}];
|
||||
helper.load([rpiNode,statusNode], flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var n3 = helper.getNode("n3");
|
||||
n3.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('status');
|
||||
msg.status.should.have.property('text', "rpi-gpio.status.na");
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"1"});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -59,7 +59,11 @@ describe('TCP Request Node', function() {
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('payload', Buffer(val1));
|
||||
if (typeof val1 === 'object') {
|
||||
msg.should.have.properties(Object.assign({}, val1, {payload: Buffer(val1.payload)}));
|
||||
} else {
|
||||
msg.should.have.property('payload', Buffer(val1));
|
||||
}
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
@@ -79,7 +83,11 @@ describe('TCP Request Node', function() {
|
||||
const n2 = helper.getNode("n2");
|
||||
n2.on("input", msg => {
|
||||
try {
|
||||
msg.should.have.property('payload', Buffer(result));
|
||||
if (typeof result === 'object') {
|
||||
msg.should.have.properties(Object.assign({}, result, {payload: Buffer(result.payload)}));
|
||||
} else {
|
||||
msg.should.have.property('payload', Buffer(result));
|
||||
}
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
@@ -95,31 +103,75 @@ describe('TCP Request Node', function() {
|
||||
it('should send & recv data', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCP(flow, "foo", "ACK:foo", done)
|
||||
testTCP(flow, {
|
||||
payload: 'foo',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'ACK:foo',
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should retain complete message', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCP(flow, {
|
||||
payload: 'foo',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'ACK:foo',
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should send & recv data when specified character received', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCP(flow, "foo0bar0", "ACK:foo0", done);
|
||||
testTCP(flow, {
|
||||
payload: 'foo0bar0',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'ACK:foo0',
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should send & recv data after fixed number of chars received', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCP(flow, "foo bar", "ACK:foo", done);
|
||||
testTCP(flow, {
|
||||
payload: 'foo bar',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'ACK:foo',
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should send & receive, then keep connection', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCP(flow, "foo", "ACK:foo", done);
|
||||
testTCP(flow, {
|
||||
payload: 'foo',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'ACK:foo',
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should send & recv data to/from server:port from msg', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCP(flow, {payload:"foo", host:"localhost", port:port}, "ACK:foo", done)
|
||||
testTCP(flow, {
|
||||
payload: "foo",
|
||||
host: "localhost",
|
||||
port: port
|
||||
}, {
|
||||
payload: "ACK:foo",
|
||||
host: 'localhost',
|
||||
port: port
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -127,36 +179,95 @@ describe('TCP Request Node', function() {
|
||||
it('should send & recv data', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
|
||||
testTCPMany(flow, ['f', 'o', 'o'], 'ACK:foo', done);
|
||||
testTCPMany(flow, [{
|
||||
payload: 'f',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'o',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'o',
|
||||
topic: 'bar'
|
||||
}], {
|
||||
payload: 'ACK:foo',
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should send & recv data when specified character received', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"char", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCPMany(flow, ["foo0","bar0"], "ACK:foo0", done);
|
||||
testTCPMany(flow, [{
|
||||
payload: "foo0",
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: "bar0",
|
||||
topic: 'bar'
|
||||
}], {
|
||||
payload: "ACK:foo0",
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should send & recv data after fixed number of chars received', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"count", splitc: "7", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCPMany(flow, ["fo", "ob", "ar"], "ACK:foo", done);
|
||||
testTCPMany(flow, [{
|
||||
payload: "fo",
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: "ob",
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: "ar",
|
||||
topic: 'bar'
|
||||
}], {
|
||||
payload: "ACK:foo",
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
|
||||
it('should send & receive, then keep connection', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"sit", splitc: "5", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCPMany(flow, ["foo", "bar", "baz"], "ACK:foobarbaz", done);
|
||||
testTCPMany(flow, [{
|
||||
payload: "foo",
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: "bar",
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: "baz",
|
||||
topic: 'bar'
|
||||
}], {
|
||||
payload: "ACK:foobarbaz",
|
||||
topic: 'bar'
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should send & recv data to/from server:port from msg', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"", port:"", out:"time", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCPMany(flow, [
|
||||
{payload:"f", host:"localhost", port:port},
|
||||
{payload:"o", host:"localhost", port:port},
|
||||
{payload:"o", host:"localhost", port:port}], "ACK:foo", done);
|
||||
testTCPMany(flow, [{
|
||||
payload: "f",
|
||||
host: "localhost",
|
||||
port: port
|
||||
},
|
||||
{
|
||||
payload: "o",
|
||||
host: "localhost",
|
||||
port: port
|
||||
},
|
||||
{
|
||||
payload: "o",
|
||||
host: "localhost",
|
||||
port: port
|
||||
}
|
||||
], {
|
||||
payload: "ACK:foo",
|
||||
host: 'localhost',
|
||||
port: port
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('should limit the queue size', function (done) {
|
||||
@@ -168,5 +279,23 @@ describe('TCP Request Node', function() {
|
||||
const expected = msgs.slice(0, -1);
|
||||
testTCPMany(flow, msgs, "ACK:" + expected.join(''), done);
|
||||
});
|
||||
|
||||
it('should only retain the latest message', function(done) {
|
||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] },
|
||||
{id:"n2", type:"helper"}];
|
||||
testTCPMany(flow, [{
|
||||
payload: 'f',
|
||||
topic: 'bar'
|
||||
}, {
|
||||
payload: 'o',
|
||||
topic: 'baz'
|
||||
}, {
|
||||
payload: 'o',
|
||||
topic: 'quux'
|
||||
}], {
|
||||
payload: 'ACK:foo',
|
||||
topic: 'quux'
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -78,6 +78,24 @@ describe('change Node', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should no-op if there are no rules', function(done) {
|
||||
var flow = [{"id":"changeNode1","type":"change","rules":[],"action":"","property":"","from":"","to":"","reg":false,"name":"changeNode","wires":[["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
helper.load(changeNode, flow, function() {
|
||||
var changeNode1 = helper.getNode("changeNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.eql(sentMsg);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
var sentMsg = {payload:"leaveMeAlong"};
|
||||
changeNode1.receive(sentMsg);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#set' , function() {
|
||||
|
||||
|
@@ -603,14 +603,14 @@ describe('JOIN node', function() {
|
||||
});
|
||||
|
||||
it('should accumulate a merged object', function(done) {
|
||||
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true, count:1},
|
||||
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true, count:3},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(joinNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var c = 0;
|
||||
n2.on("input", function(msg) {
|
||||
if (c === 5) {
|
||||
if (c === 3) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.have.property("a",3);
|
||||
@@ -632,14 +632,14 @@ describe('JOIN node', function() {
|
||||
});
|
||||
|
||||
it('should be able to reset an accumulation', function(done) {
|
||||
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",accumulate:true,mode:"custom", count:1},
|
||||
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",accumulate:true,mode:"custom", count:3},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(joinNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
var c = 0;
|
||||
n2.on("input", function(msg) {
|
||||
if (c === 3) {
|
||||
if (c === 1) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.have.property("a",1);
|
||||
@@ -649,11 +649,20 @@ describe('JOIN node', function() {
|
||||
}
|
||||
catch(e) { done(e) }
|
||||
}
|
||||
if (c === 5) {
|
||||
if (c === 2) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.have.property("b",2);
|
||||
msg.payload.should.have.property("c",1);
|
||||
msg.payload.should.have.property("e",2);
|
||||
msg.payload.should.have.property("f",1);
|
||||
}
|
||||
catch(e) { done(e) }
|
||||
}
|
||||
if (c === 3) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
msg.payload.should.have.property("g",2);
|
||||
msg.payload.should.have.property("h",1);
|
||||
msg.payload.should.have.property("i",3);
|
||||
done();
|
||||
}
|
||||
catch(e) { done(e) }
|
||||
@@ -664,8 +673,11 @@ describe('JOIN node', function() {
|
||||
n1.receive({payload:{b:2}, topic:"b"});
|
||||
n1.receive({payload:{c:3}, topic:"c"});
|
||||
n1.receive({payload:{d:4}, topic:"d", complete:true});
|
||||
n1.receive({payload:{b:2}, topic:"e"});
|
||||
n1.receive({payload:{c:1}, topic:"f"});
|
||||
n1.receive({payload:{e:2}, topic:"e"});
|
||||
n1.receive({payload:{f:1}, topic:"f", complete:true});
|
||||
n1.receive({payload:{g:2}, topic:"g"});
|
||||
n1.receive({payload:{h:1}, topic:"h"});
|
||||
n1.receive({payload:{i:3}, topic:"i"});
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -66,13 +66,25 @@ describe('SORT node', function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
msg.should.have.property(target);
|
||||
var data = msg[target];
|
||||
data.length.should.equal(data_out.length);
|
||||
for(var i = 0; i < data_out.length; i++) {
|
||||
data[i].should.equal(data_out[i]);
|
||||
try {
|
||||
msg.should.have.property(target);
|
||||
var data = msg[target];
|
||||
data.length.should.equal(data_out.length);
|
||||
for(var i = 0; i < data_out.length; i++) {
|
||||
var data0 = data[i];
|
||||
var data1 = data_out[i];
|
||||
if (typeof data0 === "object") {
|
||||
data0.should.deepEqual(data1);
|
||||
}
|
||||
else {
|
||||
data0.should.equal(data1);
|
||||
}
|
||||
}
|
||||
done();
|
||||
}
|
||||
catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
done();
|
||||
});
|
||||
var msg = {};
|
||||
msg[target] = data_in;
|
||||
@@ -93,6 +105,34 @@ describe('SORT node', function() {
|
||||
}
|
||||
|
||||
function check_sort1(flow, key, key_type, data_in, data_out, done) {
|
||||
function equals(v0, v1) {
|
||||
var k0 = Object.keys(v0);
|
||||
var k1 = Object.keys(v1);
|
||||
|
||||
if (k0.length === k1.length) {
|
||||
for (var i = 0; i < k0.length; i++) {
|
||||
var k = k0[i];
|
||||
if (!v1.hasOwnProperty(k) ||
|
||||
(v0[k] !== v1[k])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function indexOf(a, v) {
|
||||
for(var i = 0; i < a.length; i++) {
|
||||
var av = a[i];
|
||||
if ((typeof v === 'object') && equals(v, av)) {
|
||||
return i;
|
||||
}
|
||||
else if (v === av) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
var sort = flow[0];
|
||||
var prop = (key_type === "msg") ? key : "payload";
|
||||
sort.targetType = "seq";
|
||||
@@ -107,7 +147,7 @@ describe('SORT node', function() {
|
||||
msg.should.have.property("parts");
|
||||
msg.parts.should.have.property("count", data_out.length);
|
||||
var data = msg[prop];
|
||||
var index = data_out.indexOf(data);
|
||||
var index = indexOf(data_out, data);
|
||||
msg.parts.should.have.property("index", index);
|
||||
count++;
|
||||
if (count === data_out.length) {
|
||||
@@ -136,7 +176,6 @@ describe('SORT node', function() {
|
||||
check_sort1(flow, exp, "jsonata", data_in, data_out, done);
|
||||
}
|
||||
|
||||
|
||||
(function() {
|
||||
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]},
|
||||
{id:"n2", type:"helper"}];
|
||||
@@ -239,6 +278,19 @@ describe('SORT node', function() {
|
||||
});
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, wires:[["n2"]]},
|
||||
{id:"n2", type:"helper"}];
|
||||
var conv = function(x) {
|
||||
return x.map(function(v) { return { val:v }; });
|
||||
};
|
||||
var data_in = conv([ "200", "4", "30", "1000" ]);
|
||||
var data_out = conv([ "4", "30", "200", "1000" ]);
|
||||
it('should sort payload of objects', function(done) {
|
||||
check_sort0C(flow, "val", data_in, data_out, done);
|
||||
});
|
||||
})();
|
||||
|
||||
it('should sort payload by context (exp, not number, ascending)', function(done) {
|
||||
var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$flowContext($)", msgKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"},
|
||||
{id:"n2", type:"helper",z:"flow"},
|
||||
@@ -268,7 +320,7 @@ describe('SORT node', function() {
|
||||
});
|
||||
|
||||
it('should sort message group by context (exp, not number, ascending)', function(done) {
|
||||
var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$globalContext(payload)", msgKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"},
|
||||
var flow = [{id:"n1", type:"sort", target:"data", targetType:"seq", seqKey:"$globalContext(payload)", seqKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"},
|
||||
{id:"n2", type:"helper",z:"flow"},
|
||||
{id:"flow", type:"tab"}];
|
||||
var data_in = [ "first", "second", "third", "fourth" ];
|
||||
@@ -282,15 +334,20 @@ describe('SORT node', function() {
|
||||
n1.context()["global"].set("third","3");
|
||||
n1.context()["global"].set("fourth","2");
|
||||
n2.on("input", function(msg) {
|
||||
msg.should.have.property("payload");
|
||||
msg.should.have.property("parts");
|
||||
msg.parts.should.have.property("count", data_out.length);
|
||||
var data = msg["payload"];
|
||||
var index = data_out.indexOf(data);
|
||||
msg.parts.should.have.property("index", index);
|
||||
count++;
|
||||
if (count === data_out.length) {
|
||||
done();
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
msg.should.have.property("parts");
|
||||
msg.parts.should.have.property("count", data_out.length);
|
||||
var data = msg["payload"];
|
||||
var index = data_out.indexOf(data);
|
||||
msg.parts.should.have.property("index", index);
|
||||
count++;
|
||||
if (count === data_out.length) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
var len = data_in.length;
|
||||
@@ -332,7 +389,7 @@ describe('SORT node', function() {
|
||||
});
|
||||
|
||||
it('should sort message group by persistable context (exp, not number, descending)', function(done) {
|
||||
var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$flowContext(payload,\"memory\")", msgKeyType:"jsonata", order:"descending", as_num:false, wires:[["n2"]],z:"flow"},
|
||||
var flow = [{id:"n1", type:"sort", target:"data", targetType:"seq", seqKey:"$flowContext(payload,\"memory\")", seqKeyType:"jsonata", order:"descending", as_num:false, wires:[["n2"]],z:"flow"},
|
||||
{id:"n2", type:"helper",z:"flow"},
|
||||
{id:"flow", type:"tab"}];
|
||||
var data_in = [ "first", "second", "third", "fourth" ];
|
||||
|
@@ -265,6 +265,23 @@ describe('JSON node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass an object if provided a valid object and schema and action is object', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
helper.load(jsonNode, flow, function() {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
jn2.on("input", function(msg) {
|
||||
should.equal(msg.payload.number, 3);
|
||||
should.equal(msg.payload.string, "allo");
|
||||
done();
|
||||
});
|
||||
var obj = {"number": 3, "string": "allo"};
|
||||
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||
jn1.receive({payload:obj, schema:schema});
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass a string if provided a valid object and schema', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
@@ -281,6 +298,22 @@ describe('JSON node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass a string if provided a valid JSON string and schema and action is string', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
helper.load(jsonNode, flow, function() {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
jn2.on("input", function(msg) {
|
||||
should.equal(msg.payload, '{"number":3,"string":"allo"}');
|
||||
done();
|
||||
});
|
||||
var jsonString = '{"number":3,"string":"allo"}';
|
||||
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||
jn1.receive({payload:jsonString, schema:schema});
|
||||
});
|
||||
});
|
||||
|
||||
it('should log an error if passed an invalid object and valid schema', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
@@ -305,6 +338,78 @@ describe('JSON node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should log an error if passed an invalid object and valid schema and action is object', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",action:"obj",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
helper.load(jsonNode, flow, function() {
|
||||
try {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||
var obj = {"number": "foo", "string": 3};
|
||||
jn1.receive({payload:obj, schema:schema});
|
||||
var logEvents = helper.log().args.filter(function(evt) {
|
||||
return evt[0].type == "json";
|
||||
});
|
||||
logEvents.should.have.length(1);
|
||||
logEvents[0][0].should.have.a.property('msg');
|
||||
logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
|
||||
logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should log an error if passed an invalid JSON string and valid schema', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
helper.load(jsonNode, flow, function() {
|
||||
try {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||
var jsonString = '{"number":"Hello","string":3}';
|
||||
jn1.receive({payload:jsonString, schema:schema});
|
||||
var logEvents = helper.log().args.filter(function(evt) {
|
||||
return evt[0].type == "json";
|
||||
});
|
||||
logEvents.should.have.length(1);
|
||||
logEvents[0][0].should.have.a.property('msg');
|
||||
logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
|
||||
logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should log an error if passed an invalid JSON string and valid schema and action is string', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
helper.load(jsonNode, flow, function() {
|
||||
try {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||
var jsonString = '{"number":"Hello","string":3}';
|
||||
jn1.receive({payload:jsonString, schema:schema});
|
||||
var logEvents = helper.log().args.filter(function(evt) {
|
||||
return evt[0].type == "json";
|
||||
});
|
||||
logEvents.should.have.length(1);
|
||||
logEvents[0][0].should.have.a.property('msg');
|
||||
logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
|
||||
logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should log an error if passed a valid object and invalid schema', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
|
@@ -46,14 +46,14 @@ describe('file Nodes', function() {
|
||||
it('should be loaded', function(done) {
|
||||
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":true}];
|
||||
helper.load(fileNode, flow, function() {
|
||||
try {
|
||||
try {
|
||||
var fileNode1 = helper.getNode("fileNode1");
|
||||
fileNode1.should.have.property('name', 'fileNode');
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,21 +64,44 @@ describe('file Nodes', function() {
|
||||
var n1 = helper.getNode("fileNode1");
|
||||
var n2 = helper.getNode("helperNode1");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
var f = fs.readFileSync(fileToTest);
|
||||
f.should.have.length(4);
|
||||
fs.unlinkSync(fileToTest);
|
||||
msg.should.have.property("payload", "test");
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
try {
|
||||
var f = fs.readFileSync(fileToTest);
|
||||
f.should.have.length(4);
|
||||
fs.unlinkSync(fileToTest);
|
||||
msg.should.have.property("payload", "test");
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"test"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should write multi-byte string to a file', function(done) {
|
||||
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":true, wires: [["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper"}];
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileNode1");
|
||||
var n2 = helper.getNode("helperNode1");
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
f.should.have.length(2);
|
||||
f.should.equal("試験");
|
||||
fs.unlinkSync(fileToTest);
|
||||
msg.should.have.property("payload", "試験");
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"試験"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should append to a file and add newline', function(done) {
|
||||
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false, wires: [["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper"}];
|
||||
@@ -93,26 +116,26 @@ describe('file Nodes', function() {
|
||||
var data = ["test2", true, 999, [2]];
|
||||
|
||||
n2.on("input", function (msg) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
data.should.containDeep([msg.payload]);
|
||||
if (count === 3) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
data.should.containDeep([msg.payload]);
|
||||
if (count === 3) {
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
if (os.type() !== "Windows_NT") {
|
||||
f.should.have.length(19);
|
||||
f.should.equal("test2\ntrue\n999\n[2]\n");
|
||||
f.should.have.length(19);
|
||||
f.should.equal("test2\ntrue\n999\n[2]\n");
|
||||
}
|
||||
else {
|
||||
f.should.have.length(23);
|
||||
f.should.equal("test2\r\ntrue\r\n999\r\n[2]\r\n");
|
||||
f.should.have.length(23);
|
||||
f.should.equal("test2\r\ntrue\r\n999\r\n[2]\r\n");
|
||||
}
|
||||
done();
|
||||
}
|
||||
count++;
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
n1.receive({payload:"test2"}); // string
|
||||
@@ -142,37 +165,37 @@ describe('file Nodes', function() {
|
||||
var count = 0;
|
||||
|
||||
n2.on("input", function (msg) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
data.should.containDeep([msg.payload]);
|
||||
try {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
data.should.containDeep([msg.payload]);
|
||||
try {
|
||||
if (count === 1) {
|
||||
// Check they got appended as expected
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
f.should.equal("onetwo");
|
||||
// Check they got appended as expected
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
f.should.equal("onetwo");
|
||||
|
||||
// Delete the file
|
||||
fs.unlinkSync(fileToTest);
|
||||
setTimeout(function() {
|
||||
// Delete the file
|
||||
fs.unlinkSync(fileToTest);
|
||||
setTimeout(function() {
|
||||
// Send two more messages to the file
|
||||
n1.receive({payload:"three"});
|
||||
n1.receive({payload:"four"});
|
||||
}, wait);
|
||||
}, wait);
|
||||
}
|
||||
if (count === 3) {
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
f.should.equal("threefour");
|
||||
fs.unlinkSync(fileToTest);
|
||||
done();
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
f.should.equal("threefour");
|
||||
fs.unlinkSync(fileToTest);
|
||||
done();
|
||||
}
|
||||
} catch(err) {
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Send two messages to the file
|
||||
@@ -197,7 +220,7 @@ describe('file Nodes', function() {
|
||||
n2.on("input", function (msg) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
data.should.containDeep([msg.payload]);
|
||||
data.should.containDeep([msg.payload]);
|
||||
if (count == 1) {
|
||||
// Check they got appended as expected
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
@@ -256,25 +279,25 @@ describe('file Nodes', function() {
|
||||
var n2 = helper.getNode("helperNode1");
|
||||
|
||||
n2.on("input", function (msg) {
|
||||
try {
|
||||
msg.should.have.property("payload", "fine");
|
||||
msg.should.have.property("filename", fileToTest);
|
||||
try {
|
||||
msg.should.have.property("payload", "fine");
|
||||
msg.should.have.property("filename", fileToTest);
|
||||
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
if (os.type() !== "Windows_NT") {
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
if (os.type() !== "Windows_NT") {
|
||||
f.should.have.length(5);
|
||||
f.should.equal("fine\n");
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
f.should.have.length(6);
|
||||
f.should.equal("fine\r\n");
|
||||
}
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
n1.receive({payload:"fine", filename:fileToTest});
|
||||
});
|
||||
@@ -540,6 +563,93 @@ describe('file Nodes', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should write to multiple files', function(done) {
|
||||
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, "createDir":true, wires: [["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper"}];
|
||||
var tmp_path = path.join(resourcesDir, "tmp");
|
||||
var len = 1024*1024*10;
|
||||
var file_count = 5;
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileNode1");
|
||||
var n2 = helper.getNode("helperNode1");
|
||||
var count = 0;
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
count++;
|
||||
if (count == file_count) {
|
||||
for(var i = 0; i < file_count; i++) {
|
||||
var name = path.join(tmp_path, String(i));
|
||||
var f = fs.readFileSync(name);
|
||||
f.should.have.length(len);
|
||||
f[0].should.have.equal(i);
|
||||
}
|
||||
fs.removeSync(tmp_path);
|
||||
done();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
try {
|
||||
fs.removeSync(tmp_path);
|
||||
}
|
||||
catch (e1) {
|
||||
}
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
for(var i = 0; i < file_count; i++) {
|
||||
var data = new Buffer(len);
|
||||
data.fill(i);
|
||||
var name = path.join(tmp_path, String(i));
|
||||
var msg = {payload:data, filename:name};
|
||||
n1.receive(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should write to multiple files if node is closed', function(done) {
|
||||
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "appendNewline":true, "overwriteFile":true, "createDir":true, wires: [["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper"}];
|
||||
var tmp_path = path.join(resourcesDir, "tmp");
|
||||
var len = 1024*1024*10;
|
||||
var file_count = 5;
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileNode1");
|
||||
var n2 = helper.getNode("helperNode1");
|
||||
var count = 0;
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
count++;
|
||||
if (count == file_count) {
|
||||
for(var i = 0; i < file_count; i++) {
|
||||
var name = path.join(tmp_path, String(i));
|
||||
var f = fs.readFileSync(name);
|
||||
f.should.have.length(len);
|
||||
f[0].should.have.equal(i);
|
||||
}
|
||||
fs.removeSync(tmp_path);
|
||||
done();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
try {
|
||||
fs.removeSync(tmp_path);
|
||||
}
|
||||
catch (e1) {
|
||||
}
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
for(var i = 0; i < file_count; i++) {
|
||||
var data = new Buffer(len);
|
||||
data.fill(i);
|
||||
var name = path.join(tmp_path, String(i));
|
||||
var msg = {payload:data, filename:name};
|
||||
n1.receive(msg);
|
||||
}
|
||||
n1.close();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -572,7 +682,7 @@ describe('file Nodes', function() {
|
||||
|
||||
it('should read in a file and output a buffer', function(done) {
|
||||
var flow = [{id:"fileInNode1", type:"file in", name:"fileInNode", "filename":fileToTest, "format":"", wires:[["n2"]]},
|
||||
{id:"n2", type:"helper"}];
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileInNode1");
|
||||
var n2 = helper.getNode("n2");
|
||||
@@ -589,7 +699,7 @@ describe('file Nodes', function() {
|
||||
|
||||
it('should read in a file and output a utf8 string', function(done) {
|
||||
var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", "filename":fileToTest, "format":"utf8", wires:[["n2"]]},
|
||||
{id:"n2", type:"helper"}];
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileInNode1");
|
||||
var n2 = helper.getNode("n2");
|
||||
@@ -610,7 +720,7 @@ describe('file Nodes', function() {
|
||||
|
||||
it('should read in a file and output split lines with parts', function(done) {
|
||||
var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", wires:[["n2"]]},
|
||||
{id:"n2", type:"helper"}];
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileInNode1");
|
||||
var n2 = helper.getNode("n2");
|
||||
@@ -648,7 +758,7 @@ describe('file Nodes', function() {
|
||||
var line = data.join("\n");
|
||||
fs.writeFileSync(fileToTest, line);
|
||||
var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"lines", wires:[["n2"]]},
|
||||
{id:"n2", type:"helper"}];
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileInNode1");
|
||||
var n2 = helper.getNode("n2");
|
||||
@@ -681,7 +791,7 @@ describe('file Nodes', function() {
|
||||
|
||||
it('should read in a file and output a buffer with parts', function(done) {
|
||||
var flow = [{id:"fileInNode1", type:"file in", name: "fileInNode", filename:fileToTest, format:"stream", wires:[["n2"]]},
|
||||
{id:"n2", type:"helper"}];
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileInNode1");
|
||||
var n2 = helper.getNode("n2");
|
||||
|
@@ -225,7 +225,7 @@ describe("api/editor/library", function() {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property('sendFile',
|
||||
'node-module:example-one');
|
||||
fspath.resolve('node-module') + ':example-one');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -243,7 +243,8 @@ describe("api/editor/library", function() {
|
||||
throw err;
|
||||
}
|
||||
res.body.should.have.property('sendFile',
|
||||
'@org_scope/node_package:example-one');
|
||||
fspath.resolve('@org_scope/node_package') +
|
||||
':example-one');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@@ -19,6 +19,7 @@ var request = require('supertest');
|
||||
var express = require('express');
|
||||
var sinon = require('sinon');
|
||||
var when = require('when');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
var app = express();
|
||||
var info = require("../../../../red/api/editor/settings");
|
||||
@@ -29,6 +30,7 @@ describe("api/editor/settings", function() {
|
||||
before(function() {
|
||||
sinon.stub(theme,"settings",function() { return { test: 456 };});
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.get("/settings",info.runtimeSettings);
|
||||
app.get("/settingsWithUser",function(req,res,next) {
|
||||
req.user = {
|
||||
@@ -40,12 +42,13 @@ describe("api/editor/settings", function() {
|
||||
}
|
||||
next();
|
||||
},info.runtimeSettings);
|
||||
app.get("/settings/user", info.userSettings);
|
||||
app.post("/settings/user", info.updateUserSettings);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
theme.settings.restore();
|
||||
});
|
||||
|
||||
it('returns the filtered settings', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
@@ -120,6 +123,83 @@ describe("api/editor/settings", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns user settings', function (done) {
|
||||
info.init({
|
||||
settings: {
|
||||
getUserSettings: function () {
|
||||
return {
|
||||
"editor": {
|
||||
"view": {
|
||||
"view-grid-size": "20",
|
||||
"view-node-status": true,
|
||||
"view-show-tips": true,
|
||||
"view-snap-grid": true,
|
||||
"view-show-grid": true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.get("/settings/user")
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
res.body.should.have.property("editor");
|
||||
res.body.editor.should.have.property("view");
|
||||
res.body.editor.view.should.have.property("view-grid-size", "20");
|
||||
res.body.editor.view.should.have.property("view-node-status", true);
|
||||
res.body.editor.view.should.have.property("view-show-tips", true);
|
||||
res.body.editor.view.should.have.property("view-snap-grid", true);
|
||||
res.body.editor.view.should.have.property("view-show-grid", true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('sets user settings', function (done) {
|
||||
info.init({
|
||||
settings: {
|
||||
getUserSettings: function () {
|
||||
return {};
|
||||
},
|
||||
setUserSettings: function (username, currentSettings) {
|
||||
currentSettings.should.have.property("editor");
|
||||
currentSettings.editor.should.have.property("view");
|
||||
currentSettings.editor.view.should.have.property("view-grid-size", "21");
|
||||
currentSettings.editor.view.should.have.property("view-node-status", false);
|
||||
currentSettings.editor.view.should.have.property("view-show-tips", false);
|
||||
currentSettings.editor.view.should.have.property("view-snap-grid", false);
|
||||
currentSettings.editor.view.should.have.property("view-show-grid", false);
|
||||
return when.resolve();
|
||||
}
|
||||
},
|
||||
log: {
|
||||
audit: function () {}
|
||||
}
|
||||
});
|
||||
request(app)
|
||||
.post("/settings/user")
|
||||
.send({
|
||||
"editor": {
|
||||
"view": {
|
||||
"view-grid-size": "21",
|
||||
"view-node-status": false,
|
||||
"view-show-tips": false,
|
||||
"view-snap-grid": false,
|
||||
"view-show-grid": false
|
||||
}
|
||||
}
|
||||
})
|
||||
.expect(204)
|
||||
.end(function (err, res) {
|
||||
res.should.have.property("status");
|
||||
res.status.should.equal(204);
|
||||
res.should.have.property("text", "");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('includes project settings if projects available', function(done) {
|
||||
info.init({
|
||||
settings: {
|
||||
@@ -277,7 +357,7 @@ describe("api/editor/settings", function() {
|
||||
res.body.editorTheme.should.have.property("palette",{editable:false});
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -113,6 +113,21 @@ describe('context', function() {
|
||||
context2.global.get("foo").should.equal("test");
|
||||
});
|
||||
|
||||
it('context.flow/global are not enumerable', function() {
|
||||
var context1 = Context.get("1","flowA");
|
||||
Object.keys(context1).length.should.equal(0);
|
||||
Object.keys(context1.flow).length.should.equal(0);
|
||||
Object.keys(context1.global).length.should.equal(0);
|
||||
})
|
||||
|
||||
it('context.flow/global cannot be deleted', function() {
|
||||
var context1 = Context.get("1","flowA");
|
||||
delete context1.flow;
|
||||
should.exist(context1.flow);
|
||||
delete context1.global;
|
||||
should.exist(context1.global);
|
||||
})
|
||||
|
||||
it('deletes context',function() {
|
||||
var context = Context.get("1","flowA");
|
||||
should.not.exist(context.get("foo"));
|
||||
@@ -191,15 +206,26 @@ describe('context', function() {
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('returns functionGlobalContext value if store value undefined', function() {
|
||||
Context.init({functionGlobalContext: {foo:"bar"}});
|
||||
Context.load().then(function(){
|
||||
return Context.load().then(function(){
|
||||
var context = Context.get("1","flowA");
|
||||
var v = context.global.get('foo');
|
||||
v.should.equal('bar');
|
||||
});
|
||||
})
|
||||
|
||||
it('returns functionGlobalContext sub-value if store value undefined', function() {
|
||||
Context.init({functionGlobalContext: {foo:{bar:123}}});
|
||||
return Context.load().then(function(){
|
||||
var context = Context.get("1","flowA");
|
||||
var v = context.global.get('foo.bar');
|
||||
should.equal(v,123);
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('external context storage',function() {
|
||||
@@ -473,6 +499,23 @@ describe('context', function() {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('should allow the store name to be provide in the key', function(done) {
|
||||
Context.init({contextStorage:contextDefaultStorage});
|
||||
Context.load().then(function(){
|
||||
var context = Context.get("1","flow");
|
||||
var cb = function(){done("An error occurred")}
|
||||
context.set("#:(test)::foo","bar");
|
||||
context.get("#:(test)::foo");
|
||||
stubGet2.called.should.be.false();
|
||||
stubSet2.called.should.be.false();
|
||||
stubSet.calledWithExactly("1:flow","foo","bar",undefined).should.be.true();
|
||||
stubGet.calledWith("1:flow","foo").should.be.true();
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
|
||||
it('should use default as the alias of other context', function(done) {
|
||||
Context.init({contextStorage:contextAlias});
|
||||
Context.load().then(function(){
|
||||
@@ -579,16 +622,16 @@ describe('context', function() {
|
||||
});
|
||||
|
||||
it('should return multiple functionGlobalContext values if key is an array', function(done) {
|
||||
var fGC = { "foo1": 456, "foo2": 789 };
|
||||
var fGC = { "foo1": 456, "foo2": {"bar":789} };
|
||||
Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC });
|
||||
Context.load().then(function(){
|
||||
var context = Context.get("1","flow");
|
||||
context.global.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){
|
||||
context.global.get(["foo1","foo2.bar","foo3"], "memory", function(err,foo1,foo2,foo3){
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
foo1.should.be.equal(456);
|
||||
foo2.should.be.equal(789);
|
||||
should.equal(foo1, 456);
|
||||
should.equal(foo2, 789);
|
||||
should.not.exist(foo3);
|
||||
done();
|
||||
}
|
||||
|
@@ -672,7 +672,7 @@ describe('localfilesystem',function() {
|
||||
|
||||
it('should enumerate context keys in the cache',function() {
|
||||
var globalData = {foo:"bar"};
|
||||
fs.outputFile(path.join(resourcesDir,defaultContextBase,"global","global.json"), JSON.stringify(globalData,null,4), "utf8").then(function(){
|
||||
return fs.outputFile(path.join(resourcesDir,defaultContextBase,"global","global.json"), JSON.stringify(globalData,null,4), "utf8").then(function(){
|
||||
context = LocalFileSystem({dir: resourcesDir, cache: true, flushInterval: 2});
|
||||
return context.open()
|
||||
}).then(function(){
|
||||
|
@@ -96,7 +96,7 @@ describe('memory',function() {
|
||||
context.set("nodeX","three","test3");
|
||||
context.set("nodeX","four","test4");
|
||||
|
||||
var values = context.get("nodeX",["one","unknown"]);
|
||||
var values = context.get("nodeX",["one","unknown.with.multiple.levels"]);
|
||||
values.should.eql(["test1",undefined])
|
||||
})
|
||||
it('should throw error if bad key included in multiple keys', function() {
|
||||
|
@@ -128,7 +128,7 @@ describe("red/nodes/registry/localfilesystem",function() {
|
||||
}
|
||||
return _join.apply(null,arguments);
|
||||
}));
|
||||
localfilesystem.init({i18n:{registerMessageCatalog:function(){}},events:{emit:function(){}},settings:{coreNodesDir:moduleDir}});
|
||||
localfilesystem.init({log:{debug:function(){}},i18n:{registerMessageCatalog:function(){}},events:{emit:function(){}},settings:{coreNodesDir:moduleDir}});
|
||||
var nodeList = localfilesystem.getNodeFiles();
|
||||
nodeList.should.have.a.property("node-red");
|
||||
var nm = nodeList['node-red'];
|
||||
|
Reference in New Issue
Block a user