Compare commits

..

2 Commits

Author SHA1 Message Date
Dave Conway-Jones
376db60acc fix error messages for readonly 2018-08-30 09:32:50 +01:00
Dave Conway-Jones
ffa65afbb2 Catch readonly write errors more cleanly
Fail more cleanly when run from a readonly files system without setting readOnly true.
2018-08-26 10:49:53 +01:00
66 changed files with 662 additions and 1501 deletions

View File

@@ -1,65 +1,3 @@
#### 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

View File

@@ -2,7 +2,7 @@
http://nodered.org
[![Build Status](https://travis-ci.org/node-red/node-red.svg?branch=master)](https://travis-ci.org/node-red/node-red)
[![Build Status](https://travis-ci.org/node-red/node-red.svg)](https://travis-ci.org/node-red/node-red)
[![Coverage Status](https://coveralls.io/repos/node-red/node-red/badge.svg?branch=master)](https://coveralls.io/r/node-red/node-red?branch=master)
A visual tool for wiring the Internet of Things.

View File

@@ -522,25 +522,13 @@
this.selectLabel.empty();
var image;
if (opt.icon) {
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);
}
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 {
this.selectLabel.text(opt.label);
}
if (this.optionMenu) {
this.optionMenu.remove();
this.optionMenu = null;
}
if (opt.options) {
if (this.optionExpandButton) {
this.optionExpandButton.hide();
@@ -631,6 +619,10 @@
}
}
} else {
if (this.optionMenu) {
this.optionMenu.remove();
this.optionMenu = null;
}
if (this.optionSelectTrigger) {
this.optionSelectTrigger.hide();
}

View File

@@ -32,7 +32,7 @@ RED.editor.types._js = (function() {
var trayOptions = {
title: options.title,
width: options.width||"inherit",
width: "inherit",
buttons: [
{
id: "node-dialog-cancel",

View File

@@ -447,10 +447,7 @@ RED.library = (function() {
click: function() {
//TODO: move this to RED.library
var flowName = $("#node-input-library-filename").val();
flowName = flowName.trim();
if(flowName === "" || flowName.endsWith("/")) {
RED.notify(RED._("library.invalidFilename"),"warning");
} else {
if (!/^\s*$/.test(flowName)) {
$.ajax({
url:'library/flows/'+flowName,
type: "POST",

View File

@@ -275,8 +275,7 @@ RED.palette = (function() {
}
for (var i=0;i<nodes.length;i++) {
var node = d3.select(nodes[i]);
if (node.classed('link_background') && !node.classed('link_link')) {
if (d3.select(nodes[i]).classed('link_background')) {
var length = nodes[i].getTotalLength();
for (var j=0;j<length;j+=10) {
var p = nodes[i].getPointAtLength(j);
@@ -321,9 +320,9 @@ RED.palette = (function() {
}
setLabel(nt,$(d),label,nodeInfo);
var categoryNode = $("#palette-container-"+rootCategory);
var categoryNode = $("#palette-container-"+category);
if (categoryNode.find(".palette_node").length === 1) {
categoryContainers[rootCategory].open();
categoryContainers[category].open();
}
}
@@ -460,6 +459,7 @@ 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]);

View File

@@ -461,11 +461,7 @@ RED.projects.settings = (function() {
setTimeout(function() {
depsList.editableList('removeItem',entry);
refreshModuleInUseCounts();
if (modulesInUse.hasOwnProperty(entry.id)) {
entry.count = modulesInUse[entry.id].count;
} else {
entry.count = 0;
}
entry.count = modulesInUse[entry.id].count;
depsList.editableList('addItem',entry);
},500);
}

View File

@@ -132,7 +132,7 @@ RED.sidebar.context = (function() {
content: content,
toolbar: footerToolbar,
// pinned: true,
enableOnEdit: true
enableOnEdit: false
});
// var toggleLiveButton = $("#sidebar-context-toggle-live");

View File

@@ -826,11 +826,7 @@ RED.utils = (function() {
}
result = nodeColorCache[type];
}
if (result) {
return result;
} else {
return "#ddd";
}
return result;
}
function addSpinnerOverlay(container,contain) {

View File

@@ -1766,7 +1766,7 @@ RED.view = (function() {
clickTime = now;
dblClickPrimed = (lastClickNode == mousedown_node &&
d3.event.button === 0 &&
d3.event.buttons === 1 &&
!d3.event.shiftKey && !d3.event.metaKey && !d3.event.altKey && !d3.event.ctrlKey);
lastClickNode = mousedown_node;
@@ -2415,7 +2415,6 @@ 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();

View File

@@ -67,7 +67,6 @@
},
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

View File

@@ -19,11 +19,7 @@ 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;
if (this.tostatus) {
this.oldStatus = {fill:"grey", shape:"ring"};
this.status(this.oldStatus);
}
else { this.status({}); }
this.status({});
var node = this;
var levels = {
@@ -126,12 +122,12 @@ module.exports = function(RED) {
if (state === "enable") {
node.active = true;
res.sendStatus(200);
if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); }
if (node.tostatus) { node.status({}); }
} else if (state === "disable") {
node.active = false;
res.sendStatus(201);
if (node.tostatus && node.hasOwnProperty("oldStatus")) {
node.oldStatus.shape = "dot";
node.oldStatus.shape = "ring";
node.status(node.oldStatus);
}
} else {

View File

@@ -126,7 +126,6 @@
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);

View File

@@ -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,6 +241,7 @@ 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++;
@@ -264,13 +265,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
@@ -279,4 +280,4 @@ module.exports = function(RED) {
}
RED.nodes.registerType("function",FunctionNode);
RED.library.register("functions");
};
}

View File

@@ -24,12 +24,8 @@ module.exports = function(RED) {
try {
fs.statSync("/usr/lib/python2.7/dist-packages/RPi/GPIO"); // test on Hypriot
} catch(err) {
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;
}
RED.log.warn("rpi-gpio : "+RED._("rpi-gpio.errors.libnotfound"));
allOK = false;
}
}
}

View File

@@ -21,12 +21,7 @@ import os
import subprocess
from time import sleep
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
bounce = 25
bounce = 25;
if len(sys.argv) > 2:
cmd = sys.argv[1].lower()
@@ -203,7 +198,7 @@ if len(sys.argv) > 2:
elif cmd == "kbd": # catch keyboard button events
try:
while not os.path.isdir("/dev/input/by-path"):
sleep(10)
time.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')

View File

@@ -19,26 +19,11 @@ 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);
}
@@ -111,29 +96,12 @@ 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) {
@@ -467,4 +435,4 @@ module.exports = function(RED) {
}
}
RED.nodes.registerType("mqtt out",MQTTOutNode);
};
};

View File

@@ -33,7 +33,9 @@ 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;
};
@@ -644,7 +646,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).payload);
clients[connection_id].client.write(dequeue(clients[connection_id].msgQueue));
}
}
});

View File

@@ -92,80 +92,31 @@ module.exports = function(RED) {
}
function getProperty(node,msg) {
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 {
return new Promise((resolve,reject) => {
if (node.propertyType === 'jsonata') {
try {
return RED.util.evaluateJSONataExpression(node.property,msg);
} catch(err) {
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
}
RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => {
if (err) {
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
} else {
resolve(value);
}
});
} else {
try {
return RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg);
} catch(err) {
return undefined;
}
RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => {
if (err) {
resolve(undefined);
} else {
resolve(value);
}
});
}
}
});
}
function getV1(node,msg,rule,hasParts) {
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 {
return new Promise( (resolve,reject) => {
if (rule.vt === 'prev') {
return node.previousValue;
resolve(node.previousValue);
} else if (rule.vt === 'jsonata') {
var exp = rule.v;
if (rule.t === 'jsonata_exp') {
@@ -174,120 +125,83 @@ module.exports = function(RED) {
exp.assign("N", msg.parts.count);
}
}
try {
return RED.util.evaluateJSONataExpression(exp,msg);
} catch(err) {
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
}
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') {
return "json"; // TODO: ?! invalid case
resolve("json");
} else if (rule.vt === 'null') {
return "null";
resolve("null");
} else {
try {
return RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
} catch(err) {
return undefined;
}
RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) {
if (err) {
resolve(undefined);
} else {
resolve(value);
}
});
}
}
});
}
function getV2(node,msg,rule) {
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 {
return new Promise((resolve,reject) => {
var v2 = rule.v2;
if (rule.v2t === 'prev') {
return node.previousValue;
resolve(node.previousValue);
} else if (rule.v2t === 'jsonata') {
try {
return RED.util.evaluateJSONataExpression(rule.v2,msg);
} catch(err) {
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
}
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') {
try {
return RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg);
} catch(err) {
return undefined;
}
RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) {
if (err) {
resolve(undefined);
} else {
resolve(value);
}
});
} else {
return v2;
resolve(v2);
}
}
})
}
function applyRule(node, msg, property, state) {
if (node.useAsyncRules) {
return new Promise((resolve,reject) => {
return new Promise((resolve,reject) => {
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);
}
} 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;
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;
}
} else {
state.onward.push(null);
}
return state.currentRule < node.rules.length - 1
}
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);
});
})
}
function applyRules(node, msg, property,state) {
@@ -301,18 +215,7 @@ module.exports = function(RED) {
msg.parts.hasOwnProperty("index")
}
}
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);
return applyRule(node,msg,property,state).then(hasMore => {
if (hasMore) {
state.currentRule++;
return applyRules(node,msg,property,state);
@@ -320,7 +223,7 @@ module.exports = function(RED) {
node.previousValue = property;
return state.onward;
}
}
});
}
@@ -345,14 +248,6 @@ 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"));
@@ -363,13 +258,6 @@ 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);
@@ -382,9 +270,6 @@ 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))) {
@@ -393,13 +278,6 @@ 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') {
@@ -412,6 +290,7 @@ module.exports = function(RED) {
}
}
}
if (!valid) {
return;
}
@@ -541,32 +420,18 @@ module.exports = function(RED) {
if (needsCount && checkParts && hasParts) {
return addMessageToPending(msg);
}
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);
}
}
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);
});
}
function clearPending() {
@@ -608,11 +473,7 @@ module.exports = function(RED) {
}
this.on('input', function(msg) {
if (node.useAsyncRules) {
processMessageQueue(msg);
} else {
processMessage(msg,true);
}
processMessageQueue(msg);
});
this.on('close', function() {

View File

@@ -295,8 +295,7 @@
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 finalised and sent.
This resets any part counts.</p>
<p>If a message is received with the <b>msg.complete</b> property set, the output message is sent.</p>
<h4>Reduce Sequence mode</h4>
<p>When configured to join in reduce mode, an expression is applied to each
@@ -440,7 +439,10 @@
$("#node-input-joiner").typedInput({
default: 'str',
typeField: $("#node-input-joinerType"),
types:['str', 'bin']
types:[
'str',
'bin'
]
});
$("#node-input-property").typedInput({
@@ -449,7 +451,7 @@
});
$("#node-input-key").typedInput({
types:['msg']
types:['msg', {value:"merge", label:"", hasValue:false}]
});
$("#node-input-build").change();

View File

@@ -586,10 +586,7 @@ module.exports = function(RED) {
}
else {
if (msg.hasOwnProperty('complete')) {
if (inflight[partId]) {
inflight[partId].msg.complete = msg.complete;
completeSend(partId);
}
completeSend(partId);
}
else {
node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object")
@@ -597,7 +594,6 @@ module.exports = function(RED) {
}
return;
}
if (!inflight.hasOwnProperty(partId)) {
if (payloadType === 'object' || payloadType === 'merged') {
inflight[partId] = {
@@ -608,6 +604,19 @@ 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,

View File

@@ -196,10 +196,9 @@ module.exports = function(RED) {
}).catch(err => {
node.error(err,msg);
});
return;
}
var parts = msg.parts;
if (!parts || !parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) {
if (!parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) {
return;
}
var gid = parts.id;
@@ -243,8 +242,7 @@ module.exports = function(RED) {
delete pending[key];
}
}
pending_count = 0;
});
pending_count = 0; })
}
RED.nodes.registerType("sort", SortNode);

View File

@@ -100,9 +100,9 @@
defaults: {
name: {value:""},
mode: {value:"count"},
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); }},
count: {value:10},
overlap: {value:0},
interval: {value:10},
allowEmptySequence: {value:false},
topics: {value:[{topic:""}]}
},
@@ -149,9 +149,12 @@
removable: true
});
$("#node-input-count").spinner({min:1});
$("#node-input-overlap").spinner({min:0});
$("#node-input-interval").spinner({min:1});
$("#node-input-count").spinner({
});
$("#node-input-overlap").spinner({
});
$("#node-input-interval").spinner({
});
$("#node-input-mode").change(function(e) {
var val = $(this).val();
$(".node-row-msg-count").toggle(val==="count");

View File

@@ -19,7 +19,6 @@ 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);
@@ -30,7 +29,6 @@ module.exports = function(RED) {
this.compiledSchema = null;
var node = this;
this.on("input", function(msg) {
var validate = false;
if (msg.schema) {
@@ -67,17 +65,7 @@ module.exports = function(RED) {
}
catch(e) { node.error(e.message,msg); }
} else {
// 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);
}
node.send(msg);
}
}
else if (typeof value === "object") {
@@ -96,22 +84,13 @@ 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 {
// 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);
}
node.send(msg);
}
}
else { node.warn(RED._("json.errors.dropped")); }

View File

@@ -28,11 +28,9 @@ module.exports = function(RED) {
this.createDir = n.createDir || false;
var node = this;
node.wstream = null;
node.msgQueue = [];
node.closing = false;
node.closeCallback = null;
node.data = [];
function processMsg(msg, done) {
this.on("input",function(msg) {
var filename = node.filename || msg.filename || "";
if ((!node.filename) && (!node.tout)) {
node.tout = setTimeout(function() {
@@ -43,7 +41,6 @@ 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) {
@@ -54,7 +51,6 @@ module.exports = function(RED) {
}
node.send(msg);
}
done();
});
} else if (msg.hasOwnProperty("payload") && (typeof msg.payload !== "undefined")) {
var dir = path.dirname(filename);
@@ -63,7 +59,6 @@ module.exports = function(RED) {
fs.ensureDirSync(dir);
} catch(err) {
node.error(RED._("file.errors.createfail",{error:err.toString()}),msg);
done();
return;
}
}
@@ -75,143 +70,85 @@ 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; }
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
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
recreateStream = true;
node.wstream.end();
delete node.wstream;
delete node.wstreamIno;
}
} catch(err) {
// File does not exist
recreateStream = true;
node.wstream.end();
}
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);
});
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;
}
});
function closeNode() {
this.on('close', function() {
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);

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "0.19.6",
"version": "0.19.2",
"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.4",
"basic-auth": "2.0.1",
"ajv": "6.5.3",
"basic-auth": "2.0.0",
"bcryptjs": "2.4.3",
"body-parser": "1.18.3",
"cheerio": "0.22.0",
@@ -42,14 +42,13 @@
"cookie": "0.3.1",
"cookie-parser": "1.4.3",
"cors": "2.8.4",
"cron": "1.5.0",
"cron": "1.3.0",
"denque": "1.3.0",
"express": "4.16.4",
"express": "4.16.3",
"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",
@@ -57,9 +56,9 @@
"jsonata": "1.5.4",
"media-typer": "0.3.0",
"memorystore": "1.6.0",
"mqtt": "2.18.8",
"multer": "1.4.1",
"mustache": "2.3.2",
"mqtt": "2.18.5",
"multer": "1.3.1",
"mustache": "2.3.1",
"node-red-node-email": "0.1.*",
"node-red-node-feedparser": "^0.1.12",
"node-red-node-rbe": "0.2.*",
@@ -72,9 +71,9 @@
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.3.3",
"request": "2.88.0",
"semver": "5.6.0",
"semver": "5.5.1",
"sentiment": "2.1.0",
"uglify-js": "3.4.9",
"uglify-js": "3.4.8",
"when": "3.7.8",
"ws": "1.1.5",
"xml2js": "0.4.19"
@@ -83,10 +82,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.3.1",
"grunt-cli": "~1.2.0",
"grunt-concurrent": "~2.3.1",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-compress": "~1.4.0",
@@ -107,12 +106,12 @@
"node-red-node-test-helper": "0.1.7",
"should": "^8.4.0",
"sinon": "1.17.7",
"stoppable": "^1.0.7",
"supertest": "3.3.0",
"stoppable": "^1.0.6",
"supertest": "3.1.0",
"wdio-chromedriver-service": "^0.1.3",
"wdio-mocha-framework": "^0.6.2",
"wdio-spec-reporter": "^0.1.5",
"webdriverio": "^4.14.0"
"webdriverio": "^4.13.1"
},
"engines": {
"node": ">=4"

16
red.js
View File

@@ -101,8 +101,18 @@ if (parsedArgs.settings) {
var settingsStat = fs.statSync(defaultSettings);
if (settingsStat.mtime.getTime() <= settingsStat.ctime.getTime()) {
// Default settings file has not been modified - safe to copy
fs.copySync(defaultSettings,userSettingsFile);
settingsFile = userSettingsFile;
try {
fs.copySync(defaultSettings,userSettingsFile);
settingsFile = userSettingsFile;
}
catch (err) {
console.log("Failed to copy settings file to "+userSettingsFile);
console.log("Error: "+err.toString());
if (err.code == "EACCES") {
console.log("You may need to set readOnly: true, in settings.js");
}
process.exit(1);
}
} else {
// Use default settings.js as it has been modified
settingsFile = defaultSettings;
@@ -240,7 +250,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();

View File

@@ -102,10 +102,9 @@ module.exports = {
var fullPath = redNodes.getNodeExampleFlowPath(module,path);
if (fullPath) {
try {
var resolvedPath = fspath.resolve(fullPath);
fs.statSync(resolvedPath);
fs.statSync(fullPath);
log.audit({event: "library.get",type:"flow",path:req.params[0]},req);
return res.sendFile(resolvedPath,{
return res.sendFile(fullPath,{
headers:{
'Content-Type': 'application/json'
}

View File

@@ -142,6 +142,7 @@
"restore": "Restoring __type__ file backup : __path__",
"restore-fail": "Restoring __type__ file backup failed : __message__",
"fsync-fail": "Flushing file __path__ to disk failed : __message__",
"fwrite-fail": "Writing backup file __path__ to disk failed : __message__",
"projects": {
"changing-project": "Setting active project : __project__",
"active-project": "Active project : __project__",

View File

@@ -17,7 +17,6 @@
var clone = require("clone");
var log = require("../../log");
var memory = require("./memory");
var util = require("../../util");
var settings;
@@ -210,131 +209,104 @@ function createContext(id,seed) {
insertSeedValues = function(keys,values) {
if (!Array.isArray(keys)) {
if (values[0] === undefined) {
values[0] = util.getObjectProperty(seed,keys);
values[0] = seed[keys];
}
} else {
for (var i=0;i<keys.length;i++) {
if (values[i] === undefined) {
values[i] = util.getObjectProperty(seed,keys[i]);
values[i] = seed[keys[i]];
}
}
}
}
}
Object.defineProperties(obj, {
get: {
value: function(key, 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);
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;
}
obj.get = function(key, storage, callback) {
var context;
if (!storage && !callback) {
context = stores["_"];
} else {
if (typeof storage === 'function') {
callback = storage;
storage = "_";
}
},
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);
}
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;
}
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];
}
}
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;
}
@@ -348,13 +320,9 @@ function getContext(localId,flowId) {
}
var newContext = createContext(contextId);
if (flowId) {
Object.defineProperty(newContext, 'flow', {
value: getContext(flowId)
});
newContext.flow = getContext(flowId);
}
Object.defineProperty(newContext, 'global', {
value: contexts['global']
})
newContext.global = contexts['global'];
contexts[contextId] = newContext;
return newContext;
}

View File

@@ -140,7 +140,6 @@ 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({});
}
@@ -214,13 +213,12 @@ LocalFileSystem.prototype.open = function(){
}
LocalFileSystem.prototype.close = function(){
var self = this;
if (this.cache && this._pendingWriteTimeout) {
if (this.cache && this._flushPendingWrites) {
clearTimeout(this._pendingWriteTimeout);
delete this._pendingWriteTimeout;
this.flushInterval = 0;
return this._flushPendingWrites();
}
return this.writePromise;
return Promise.resolve();
}
LocalFileSystem.prototype.get = function(scope, key, callback) {
@@ -232,31 +230,14 @@ 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)) {
try {
value = util.getObjectProperty(data,key);
} catch(err) {
if (err.code === "INVALID_EXPR") {
throw err;
}
value = undefined;
}
callback(null, value);
callback(null, util.getObjectProperty(data,key));
} else {
var results = [undefined];
for (var i=0;i<key.length;i++) {
try {
value = util.getObjectProperty(data,key[i]);
} catch(err) {
if (err.code === "INVALID_EXPR") {
throw err;
}
value = undefined;
}
results.push(value)
results.push(util.getObjectProperty(data,key[i]))
}
callback.apply(null,results);
}
@@ -279,17 +260,15 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) {
return;
} else {
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()}));
});
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 {
self.writePromise = self.writePromise.then(function() { return loadFile(storagePath + ".json") }).then(function(data){
loadFile(storagePath + ".json").then(function(data){
var obj = data ? JSON.parse(data) : {}
if (!Array.isArray(key)) {
key = [key];

View File

@@ -32,14 +32,7 @@ Memory.prototype._getOne = function(scope, key) {
var value;
var error;
if(this.data[scope]){
try {
value = util.getObjectProperty(this.data[scope], key);
} catch(err) {
if (err.code === "INVALID_EXPR") {
throw err;
}
value = undefined;
}
value = util.getObjectProperty(this.data[scope], key);
}
return value;
}

View File

@@ -23,7 +23,7 @@ var i18n;
var settings;
var disableNodePathScan = false;
var iconFileExtensions = [".png", ".gif", ".svg"];
var iconFileExtensions = [".png", ".gif"];
function init(runtime) {
settings = runtime.settings;
@@ -290,33 +290,6 @@ 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] = {

View File

@@ -358,15 +358,7 @@ Project.prototype.update = function (user, data) {
promises.push(util.writeFile(this.paths['README.md'], this.description));
}
if (savePackage) {
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));
}));
promises.push(util.writeFile(this.paths['package.json'], JSON.stringify(this.package,"",4)));
}
return when.settle(promises).then(function(res) {
return {

View File

@@ -81,7 +81,11 @@ module.exports = {
writeFile: function(path,content,backupPath) {
if (backupPath) {
if (fs.existsSync(path)) {
fs.renameSync(path,backupPath);
try {
fs.renameSync(path,backupPath);
} catch(e) {
log.warn(log._("storage.localfilesystem.fwrite-fail",{path:path, message:e.toString()}));
}
}
}
return when.promise(function(resolve,reject) {

View File

@@ -130,19 +130,13 @@ 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 createError("INVALID_EXPR","Invalid property expression: zero-length");
throw new Error("Invalid property expression: zero-length");
}
var parts = [];
var start = 0;
@@ -155,14 +149,14 @@ function normalisePropertyExpression(str) {
if (!inString) {
if (c === "'" || c === '"') {
if (i != start) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
}
inString = true;
quoteChar = c;
start = i+1;
} else if (c === '.') {
if (i===0) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected . at position 0");
throw new Error("Invalid property expression: unexpected . at position 0");
}
if (start != i) {
v = str.substring(start,i);
@@ -173,57 +167,57 @@ function normalisePropertyExpression(str) {
}
}
if (i===length-1) {
throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
throw new Error("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 createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
}
start = i+1;
} else if (c === '[') {
if (i === 0) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
throw new Error("Invalid property expression: unexpected "+c+" at position "+i);
}
if (start != i) {
parts.push(str.substring(start,i));
}
if (i===length-1) {
throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
throw new Error("Invalid property expression: unterminated expression");
}
// Next char is either a quote or a number
if (!/["'\d]/.test(str[i+1])) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
}
start = i+1;
inBox = true;
} else if (c === ']') {
if (!inBox) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
throw new Error("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 createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
throw new Error("Invalid property expression: unexpected array expression at position "+start);
}
}
start = i+1;
inBox = false;
} else if (c === ' ') {
throw createError("INVALID_EXPR","Invalid property expression: unexpected ' ' at position "+i);
throw new Error("Invalid property expression: unexpected ' ' at position "+i);
}
} else {
if (c === quoteChar) {
if (i-start === 0) {
throw createError("INVALID_EXPR","Invalid property expression: zero-length string at position "+start);
throw new Error("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 createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
throw new Error("Invalid property expression: unexpected array expression at position "+start);
} else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) {
throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
throw new Error("Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
}
start = i+1;
inString = false;
@@ -232,7 +226,7 @@ function normalisePropertyExpression(str) {
}
if (inBox || inString) {
throw new createError("INVALID_EXPR","Invalid property expression: unterminated expression");
throw new Error("Invalid property expression: unterminated expression");
}
if (start < length) {
parts.push(str.substring(start));

View File

@@ -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

View File

@@ -24,7 +24,7 @@ function injectNode(id) {
util.inherits(injectNode, nodePage);
var payloadTypeList = {
var payloadType = {
"flow": 1,
"global": 2,
"str": 3,
@@ -36,43 +36,54 @@ var payloadTypeList = {
"env": 9,
};
var repeatTypeList = {
var timeType = {
"none": 1,
"interval": 2,
"intervalBetweenTimes": 3,
"atASpecificTime": 4,
};
injectNode.prototype.setPayload = function(payloadType, payload) {
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) {
// 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[' + payloadTypeList[payloadType] + ']';
var payloadTypeXPath = '//*[@class="red-ui-typedInput-options"]/a[' + payloadType[type] + ']';
browser.clickWithWait(payloadTypeXPath);
if (payload) {
if (value) {
// Input a value.
browser.setValue('//*[@class="red-ui-typedInput-input"]/input', payload);
browser.setValue('//*[@class="red-ui-typedInput-input"]/input', value);
}
}
injectNode.prototype.setTopic = function(topic) {
browser.setValue('#node-input-topic', topic);
injectNode.prototype.setTopic = function(value) {
browser.setValue('#node-input-topic', value);
}
injectNode.prototype.setOnce = function(once) {
var isChecked = browser.isSelected('#node-input-once');
if (isChecked !== once) {
browser.clickWithWait('#node-input-once');
}
injectNode.prototype.setOnce = function(value) {
browser.clickWithWait('#node-input-once');
}
injectNode.prototype.setRepeat = function(repeatType) {
var repeatTypeXPath = '//*[@id="inject-time-type-select"]/option[' + repeatTypeList[repeatType] + ']';
browser.clickWithWait(repeatTypeXPath);
injectNode.prototype.setTimeType = function(type) {
var timeTypeXPath = '//*[@id="inject-time-type-select"]/option[' + timeType[type] + ']';
browser.clickWithWait(timeTypeXPath);
}
injectNode.prototype.setRepeatInterval = function(repeat) {
browser.setValue('#inject-time-interval-count', repeat);
injectNode.prototype.setRepeat = function(sec) {
browser.setValue('#inject-time-interval-count', sec);
}
module.exports = injectNode;

View File

@@ -24,20 +24,22 @@ function debugNode(id) {
util.inherits(debugNode, nodePage);
debugNode.prototype.setOutput = function(complete) {
var target = {
"msg": 1,
"full": 2
};
debugNode.prototype.setTarget = function(type, value) {
// Open a payload type list.
browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-container")]/button');
if (complete !== 'true') {
// Select the "msg" type.
browser.clickWithWait('/html/body/div[11]/a[1]');
// Input the path in msg.
// Select a payload type.
var xPath = '/html/body/div[11]/a[' + target[type] + ']';
browser.clickWithWait(xPath);
if (value) {
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', complete);
} else {
// Select the "complete msg object" type.
browser.clickWithWait('/html/body/div[11]/a[2]');
browser.setValue('//*[contains(@class, "red-ui-typedInput-input")]/input', value);
}
}

View File

@@ -24,17 +24,12 @@ function functionNode(id) {
util.inherits(functionNode, nodePage);
functionNode.prototype.setFunction = function(func) {
functionNode.prototype.setCode = function(value) {
browser.click('#node-input-func-editor');
browser.keys(['Control', 'Home', 'Control']);
for (var i = 0; i < func.length; i++) {
browser.keys([func.charAt(i)]);
for (var i=0; i<value.length; i++) {
browser.keys([value.substr(i, 1)]);
}
// 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;

View File

@@ -24,20 +24,29 @@ function templateNode(id) {
util.inherits(templateNode, nodePage);
templateNode.prototype.setSyntax = function(syntax) {
browser.selectWithWait('#node-input-syntax', syntax);
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.setFormat = function(format) {
browser.selectWithWait('#node-input-format', format);
templateNode.prototype.setFormat = function(type) {
browser.selectByValue('#node-input-format', type);
}
templateNode.prototype.setTemplate = function(template) {
templateNode.prototype.setTemplate = function(value) {
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 < template.length; i++) {
browser.keys([template.charAt(i)]);
for (var i=0; i<value.length; i++) {
browser.keys([value.charAt(i)]);
}
browser.keys(['Control', 'Shift', 'End', 'Shift', 'Control']);
browser.keys(['Delete']);

View File

@@ -22,14 +22,30 @@ function httpinNode(id) {
nodePage.call(this, id);
}
util.inherits(httpinNode, nodePage);
httpinNode.prototype.setMethod = function(method) {
browser.selectWithWait('#node-input-method', method);
function setMethod(type) {
browser.selectByValue('#node-input-method', type);
}
httpinNode.prototype.setUrl = function(url) {
browser.setValue('#node-input-url', url);
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.setUrl = function(value) {
browser.setValue('#node-input-url', value);
}
module.exports = httpinNode;

View File

@@ -24,16 +24,36 @@ function httpRequestNode(id) {
util.inherits(httpRequestNode, nodePage);
httpRequestNode.prototype.setUrl = function(url) {
browser.setValue('#node-input-url', url);
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.setMethod = function(method) {
browser.selectWithWait('#node-input-method', method);
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.setReturn = function(ret) {
browser.selectWithWait('#node-input-ret', ret);
httpRequestNode.prototype.setRet = function(type) {
browser.clickWithWait('#node-input-ret');
var retTypeXPath = '//*[@id="node-input-ret"]/option[' + retType[type] + ']';
browser.clickWithWait(retTypeXPath);
}
module.exports = httpRequestNode;

View File

@@ -24,6 +24,13 @@ function changeNode(id) {
util.inherits(changeNode, nodePage);
var tType = {
"set": 1,
"change": 2,
"delete": 3,
"move": 4,
};
var totType = {
"msg": 1,
"flow": 2,
@@ -35,7 +42,6 @@ var totType = {
"bin": 8,
"date": 9,
"jsonata": 10,
"env": 11,
};
var ptType = {
@@ -44,8 +50,8 @@ var ptType = {
"global": 3,
};
function setT(t, index) {
browser.selectWithWait('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', t);
function setT(rule, index) {
browser.selectByValue('//*[@id="node-input-rule-container"]/li[' + index + ']/div/div[1]/select', rule);
}
// It is better to create a function whose input value is the object type in the future,

View File

@@ -24,8 +24,8 @@ function rangeNode(id) {
util.inherits(rangeNode, nodePage);
rangeNode.prototype.setAction = function(action) {
browser.selectWithWait('#node-input-action', action);
rangeNode.prototype.setAction = function(value) {
browser.selectByValue('#node-input-action', value);
}
rangeNode.prototype.setRange = function(minin, maxin, minout, maxout) {

View File

@@ -24,8 +24,8 @@ function htmlNode(id) {
util.inherits(htmlNode, nodePage);
htmlNode.prototype.setSelector = function(tag) {
browser.setValue('#node-input-tag', tag);
htmlNode.prototype.setTag = function(value) {
browser.setValue('#node-input-tag', value);
}
module.exports = htmlNode;

View File

@@ -31,13 +31,13 @@ var formatType = {
"stream": 4
};
fileinNode.prototype.setFilename = function(filename) {
browser.setValue('#node-input-filename', filename);
fileinNode.prototype.setFilename = function(value) {
browser.setValue('#node-input-filename', value);
}
fileinNode.prototype.setOutput = function(format) {
fileinNode.prototype.setFormat = function(type) {
browser.clickWithWait('#node-input-format');
var formatTypeXPath = '//*[@id="node-input-format"]/option[' + formatType[format] + ']';
var formatTypeXPath = '//*[@id="node-input-format"]/option[' + formatType[type] + ']';
browser.clickWithWait(formatTypeXPath);
}

View File

@@ -38,12 +38,6 @@ 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 = {

View File

@@ -17,8 +17,8 @@
var should = require("should");
var helper = require("../../editor_helper");
var debugTab = require('../../pageobjects/editor/debugTab_page');
var workspace = require('../../pageobjects/editor/workspace_page');
var debugTab = require('../../pageobjects/workspace/debugTab_page');
var workspace = require('../../pageobjects/workspace/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.setOutput("headers");
debugNode.setTarget("msg", "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.setOutput("");
fileinNode.setFormat("");
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.setFunction("msg.payload = JSON.stringify(msg.req.cookies,null,4);\nreturn msg;");
functionNodeFormat.setCode("msg.payload = JSON.stringify(msg.req.cookies,null,4);");
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> &bull; <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> &bull; <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.setFunction('msg.cookies = { };\n msg.cookies["demo-"+(Math.floor(Math.random()*1000))] = Date.now();\nreturn msg;');
functionNodeAdd.setCode("msg.cookies = { };\n msg.cookies[\"demo-\"+(Math.floor(Math.random()*1000))] = Date.now();");
functionNodeAdd.clickOk();
changeNode.edit();
@@ -571,7 +571,7 @@ describe('cookbook', function() {
httpinNodeClear.clickOk();
functionNodeClear.edit();
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.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.clickOk();
httpinNodeClear.connect(functionNodeClear);

View File

@@ -19,8 +19,8 @@ var should = require("should");
var fs = require('fs-extra');
var helper = require("../../editor_helper");
var debugTab = require('../../pageobjects/editor/debugTab_page');
var workspace = require('../../pageobjects/editor/workspace_page');
var debugTab = require('../../pageobjects/workspace/debugTab_page');
var workspace = require('../../pageobjects/workspace/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.setRepeat("interval");
injectNode.setRepeatInterval(1);
injectNode.setTimeType("interval");
injectNode.setRepeat(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.setSelector("title");
htmlNode.setTag("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.setReturn("obj");
httpRequetNode.setRet("obj");
httpRequetNode.clickOk();
debugNode.edit();
debugNode.setOutput("payload.title");
debugNode.setTarget("msg", "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.setReturn("bin");
httpRequetNode.setRet("bin");
httpRequetNode.clickOk();
injectNode.connect(httpRequetNode);
@@ -413,11 +413,11 @@ describe('cookbook', function() {
var debugNode = workspace.addNode("debug", nodeWidth * 3);
functionNode.edit();
functionNode.setFunction('msg.payload = "data to post";\nreturn msg;');
functionNode.setCode("msg.payload = \"data to post\";");
functionNode.clickOk();
httpRequetNode.edit();
httpRequetNode.setMethod("POST");
httpRequetNode.setMethod("post");
var url = helper.url() + httpNodeRoot + "/set-header";
httpRequetNode.setUrl(url);
httpRequetNode.clickOk();

View File

@@ -19,8 +19,8 @@ var should = require("should");
var fs = require('fs-extra');
var helper = require("../../editor_helper");
var debugTab = require('../../pageobjects/editor/debugTab_page');
var workspace = require('../../pageobjects/editor/workspace_page');
var debugTab = require('../../pageobjects/workspace/debugTab_page');
var workspace = require('../../pageobjects/workspace/workspace_page');
var nodeWidth = 200;

View File

@@ -1216,7 +1216,7 @@ describe('function node', function() {
msg.should.have.property('payload', n1.id);
done();
});
n1.receive({payload:"foo",topic: "bar"});
n1.receive({payload:"foo",topicb: "bar"});
});
});
@@ -1230,7 +1230,7 @@ describe('function node', function() {
msg.should.have.property('payload', n1.name);
done();
});
n1.receive({payload:"foo",topic: "bar"});
n1.receive({payload:"foo",topicb: "bar"});
});
});

View File

@@ -15,8 +15,7 @@
**/
var should = require("should");
var rpiNode = require("../../../../nodes/core/hardware/36-rpi-gpio.js");
var statusNode = require("../../../../nodes/core/core/25-status.js");
var rpi = require("../../../../nodes/core/hardware/36-rpi-gpio.js");
var helper = require("node-red-node-test-helper");
var fs = require("fs");
@@ -51,7 +50,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(rpiNode, flow, function() {
helper.load(rpi, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'rpi-gpio in');
try {
@@ -70,7 +69,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(rpiNode, flow, function() {
helper.load(rpi, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property('name', 'rpi-gpio out');
try {
@@ -87,62 +86,4 @@ 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"});
});
});
});

View File

@@ -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:3},
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",mode:"custom",accumulate:true, count:1},
{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 === 5) {
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:3},
var flow = [{id:"n1", type:"join", wires:[["n2"]], build:"merged",accumulate:true,mode:"custom", count:1},
{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 === 1) {
if (c === 3) {
try {
msg.should.have.property("payload");
msg.payload.should.have.property("a",1);
@@ -649,20 +649,11 @@ describe('JOIN node', function() {
}
catch(e) { done(e) }
}
if (c === 2) {
if (c === 5) {
try {
msg.should.have.property("payload");
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);
msg.payload.should.have.property("b",2);
msg.payload.should.have.property("c",1);
done();
}
catch(e) { done(e) }
@@ -673,11 +664,8 @@ 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:{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"});
n1.receive({payload:{b:2}, topic:"e"});
n1.receive({payload:{c:1}, topic:"f"});
});
});

View File

@@ -66,25 +66,13 @@ describe('SORT node', function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
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);
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]);
}
done();
});
var msg = {};
msg[target] = data_in;
@@ -105,34 +93,6 @@ 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";
@@ -147,7 +107,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 = indexOf(data_out, data);
var index = data_out.indexOf(data);
msg.parts.should.have.property("index", index);
count++;
if (count === data_out.length) {
@@ -176,6 +136,7 @@ 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"}];
@@ -278,19 +239,6 @@ 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"},
@@ -320,7 +268,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:"seq", seqKey:"$globalContext(payload)", seqKeyType:"jsonata", order:"ascending", as_num:false, wires:[["n2"]],z:"flow"},
var flow = [{id:"n1", type:"sort", target:"data", targetType:"msg", msgKey:"$globalContext(payload)", msgKeyType:"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" ];
@@ -334,20 +282,15 @@ describe('SORT node', function() {
n1.context()["global"].set("third","3");
n1.context()["global"].set("fourth","2");
n2.on("input", function(msg) {
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);
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();
}
});
var len = data_in.length;
@@ -389,7 +332,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:"seq", seqKey:"$flowContext(payload,\"memory\")", seqKeyType:"jsonata", order:"descending", as_num:false, wires:[["n2"]],z:"flow"},
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"},
{id:"n2", type:"helper",z:"flow"},
{id:"flow", type:"tab"}];
var data_in = [ "first", "second", "third", "fourth" ];

View File

@@ -265,23 +265,6 @@ 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"}];
@@ -298,22 +281,6 @@ 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"}];
@@ -338,78 +305,6 @@ 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"}];

View File

@@ -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,44 +64,21 @@ 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"}];
@@ -116,26 +93,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
@@ -165,37 +142,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
@@ -220,7 +197,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();
@@ -279,25 +256,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});
});
@@ -563,93 +540,6 @@ 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();
});
});
});
@@ -682,7 +572,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");
@@ -699,7 +589,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");
@@ -720,7 +610,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");
@@ -758,7 +648,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");
@@ -791,7 +681,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");

View File

@@ -225,7 +225,7 @@ describe("api/editor/library", function() {
throw err;
}
res.body.should.have.property('sendFile',
fspath.resolve('node-module') + ':example-one');
'node-module:example-one');
done();
});
});
@@ -243,8 +243,7 @@ describe("api/editor/library", function() {
throw err;
}
res.body.should.have.property('sendFile',
fspath.resolve('@org_scope/node_package') +
':example-one');
'@org_scope/node_package:example-one');
done();
});
});

View File

@@ -19,7 +19,6 @@ 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");
@@ -30,7 +29,6 @@ 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 = {
@@ -42,13 +40,12 @@ 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: {
@@ -123,83 +120,6 @@ 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: {
@@ -357,7 +277,7 @@ describe("api/editor/settings", function() {
res.body.editorTheme.should.have.property("palette",{editable:false});
done();
});
});
})
});
});

View File

@@ -113,21 +113,6 @@ 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"));
@@ -206,26 +191,15 @@ describe('context', function() {
});
it('returns functionGlobalContext value if store value undefined', function() {
Context.init({functionGlobalContext: {foo:"bar"}});
return Context.load().then(function(){
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() {
@@ -499,23 +473,6 @@ 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(){
@@ -622,16 +579,16 @@ describe('context', function() {
});
it('should return multiple functionGlobalContext values if key is an array', function(done) {
var fGC = { "foo1": 456, "foo2": {"bar":789} };
var fGC = { "foo1": 456, "foo2": 789 };
Context.init({contextStorage:memoryStorage, functionGlobalContext:fGC });
Context.load().then(function(){
var context = Context.get("1","flow");
context.global.get(["foo1","foo2.bar","foo3"], "memory", function(err,foo1,foo2,foo3){
context.global.get(["foo1","foo2","foo3"], "memory", function(err,foo1,foo2,foo3){
if (err) {
done(err);
} else {
should.equal(foo1, 456);
should.equal(foo2, 789);
foo1.should.be.equal(456);
foo2.should.be.equal(789);
should.not.exist(foo3);
done();
}

View File

@@ -672,7 +672,7 @@ describe('localfilesystem',function() {
it('should enumerate context keys in the cache',function() {
var globalData = {foo:"bar"};
return fs.outputFile(path.join(resourcesDir,defaultContextBase,"global","global.json"), JSON.stringify(globalData,null,4), "utf8").then(function(){
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(){

View File

@@ -96,7 +96,7 @@ describe('memory',function() {
context.set("nodeX","three","test3");
context.set("nodeX","four","test4");
var values = context.get("nodeX",["one","unknown.with.multiple.levels"]);
var values = context.get("nodeX",["one","unknown"]);
values.should.eql(["test1",undefined])
})
it('should throw error if bad key included in multiple keys', function() {

View File

@@ -128,7 +128,7 @@ describe("red/nodes/registry/localfilesystem",function() {
}
return _join.apply(null,arguments);
}));
localfilesystem.init({log:{debug:function(){}},i18n:{registerMessageCatalog:function(){}},events:{emit:function(){}},settings:{coreNodesDir:moduleDir}});
localfilesystem.init({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'];