From a11a279c007b32a68c5f5fd9b2527ad2b0af5c17 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Tue, 16 Jan 2018 10:59:44 +0000 Subject: [PATCH 01/12] add msg. select to range and yaml nodes, re-order son node (name to bottom) add common.label.property to messages list --- nodes/core/locales/en-US/messages.json | 5 +++-- nodes/core/logic/16-range.html | 11 +++++++++++ nodes/core/logic/16-range.js | 13 ++++++++----- nodes/core/parsers/70-JSON.html | 17 +++++++---------- nodes/core/parsers/70-YAML.html | 11 +++++++++++ nodes/core/parsers/70-YAML.js | 16 ++++++++++------ 6 files changed, 50 insertions(+), 23 deletions(-) diff --git a/nodes/core/locales/en-US/messages.json b/nodes/core/locales/en-US/messages.json index 6665387ae..62339d81a 100644 --- a/nodes/core/locales/en-US/messages.json +++ b/nodes/core/locales/en-US/messages.json @@ -5,7 +5,8 @@ "topic": "Topic", "name": "Name", "username": "Username", - "password": "Password" + "password": "Password", + "property": "Property" }, "status": { "connected": "connected", @@ -602,7 +603,7 @@ "maxout": "e.g. 255" }, "scale": { - "payload": "Scale msg.payload", + "payload": "Scale the message property", "limit": "Scale and limit to the target range", "wrap": "Scale and wrap within the target range" }, diff --git a/nodes/core/logic/16-range.html b/nodes/core/logic/16-range.html index 321bec7e4..37d35fb85 100644 --- a/nodes/core/logic/16-range.html +++ b/nodes/core/logic/16-range.html @@ -1,5 +1,9 @@ diff --git a/nodes/core/logic/16-range.js b/nodes/core/logic/16-range.js index 1de073fee..b92b155ed 100644 --- a/nodes/core/logic/16-range.js +++ b/nodes/core/logic/16-range.js @@ -24,11 +24,13 @@ module.exports = function(RED) { this.maxin = Number(n.maxin); this.minout = Number(n.minout); this.maxout = Number(n.maxout); + this.property = n.property||"payload"; var node = this; this.on('input', function (msg) { - if (msg.hasOwnProperty("payload")) { - var n = Number(msg.payload); + var value = RED.util.getMessageProperty(msg,node.property); + if (value !== undefined) { + var n = Number(value); if (!isNaN(n)) { if (node.action == "clamp") { if (n < node.minin) { n = node.minin; } @@ -38,11 +40,12 @@ module.exports = function(RED) { var divisor = node.maxin - node.minin; n = ((n - node.minin) % divisor + divisor) % divisor + node.minin; } - msg.payload = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout; - if (node.round) { msg.payload = Math.round(msg.payload); } + value = ((n - node.minin) / (node.maxin - node.minin) * (node.maxout - node.minout)) + node.minout; + if (node.round) { value = Math.round(value); } + RED.util.setMessageProperty(msg,node.property,value); node.send(msg); } - else { node.log(RED._("range.errors.notnumber")+": "+msg.payload); } + else { node.log(RED._("range.errors.notnumber")+": "+value); } } else { node.send(msg); } // If no payload - just pass it on. }); diff --git a/nodes/core/parsers/70-JSON.html b/nodes/core/parsers/70-JSON.html index f473c6039..01ffe42b3 100644 --- a/nodes/core/parsers/70-JSON.html +++ b/nodes/core/parsers/70-JSON.html @@ -1,9 +1,5 @@ diff --git a/nodes/core/parsers/70-YAML.js b/nodes/core/parsers/70-YAML.js index ae4ca1a8c..1a34fdd57 100644 --- a/nodes/core/parsers/70-YAML.js +++ b/nodes/core/parsers/70-YAML.js @@ -4,20 +4,24 @@ module.exports = function(RED) { var yaml = require('js-yaml'); function YAMLNode(n) { RED.nodes.createNode(this,n); + this.property = n.property||"payload"; var node = this; this.on("input", function(msg) { - if (msg.hasOwnProperty("payload")) { - if (typeof msg.payload === "string") { + var value = RED.util.getMessageProperty(msg,node.property); + if (value !== undefined) { + if (typeof value === "string") { try { - msg.payload = yaml.load(msg.payload); + value = yaml.load(value); + RED.util.setMessageProperty(msg,node.property,value); node.send(msg); } catch(e) { node.error(e.message,msg); } } - else if (typeof msg.payload === "object") { - if (!Buffer.isBuffer(msg.payload)) { + else if (typeof value === "object") { + if (!Buffer.isBuffer(value)) { try { - msg.payload = yaml.dump(msg.payload); + value = yaml.dump(value); + RED.util.setMessageProperty(msg,node.property,value); node.send(msg); } catch(e) { From e793a1e1aab506bcdad36e17c34e43b6f61f8abe Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Tue, 16 Jan 2018 21:43:37 +0000 Subject: [PATCH 02/12] add property choice to xml, sentiment nodes add tests --- nodes/core/analysis/72-sentiment.html | 28 ++++----- nodes/core/analysis/72-sentiment.js | 23 ++------ nodes/core/parsers/70-XML.html | 11 ++++ nodes/core/parsers/70-XML.js | 33 ++++------- test/nodes/core/analysis/72-sentiment_spec.js | 59 +++++++++++++++++++ test/nodes/core/parsers/70-XML_spec.js | 30 ++++++++-- test/nodes/core/parsers/70-YAML_spec.js | 33 +++++++++++ 7 files changed, 156 insertions(+), 61 deletions(-) diff --git a/nodes/core/analysis/72-sentiment.html b/nodes/core/analysis/72-sentiment.html index aa35ffb05..0ba1ccade 100644 --- a/nodes/core/analysis/72-sentiment.html +++ b/nodes/core/analysis/72-sentiment.html @@ -1,20 +1,9 @@ - diff --git a/nodes/core/analysis/72-sentiment.js b/nodes/core/analysis/72-sentiment.js index 43a800e2a..ff0cf33e8 100644 --- a/nodes/core/analysis/72-sentiment.js +++ b/nodes/core/analysis/72-sentiment.js @@ -1,18 +1,3 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ module.exports = function(RED) { "use strict"; @@ -20,16 +5,18 @@ module.exports = function(RED) { function SentimentNode(n) { RED.nodes.createNode(this,n); + this.property = n.property||"payload"; var node = this; this.on("input", function(msg) { - if (msg.hasOwnProperty("payload")) { - sentiment(msg.payload, msg.overrides || null, function (err, result) { + var value = RED.util.getMessageProperty(msg,node.property); + if (value !== undefined) { + sentiment(value, msg.overrides || null, function (err, result) { msg.sentiment = result; node.send(msg); }); } - else { node.send(msg); } // If no payload - just pass it on. + else { node.send(msg); } // If no matching property - just pass it on. }); } RED.nodes.registerType("sentiment",SentimentNode); diff --git a/nodes/core/parsers/70-XML.html b/nodes/core/parsers/70-XML.html index f33ef6811..25d17998c 100644 --- a/nodes/core/parsers/70-XML.html +++ b/nodes/core/parsers/70-XML.html @@ -1,5 +1,9 @@ diff --git a/nodes/core/parsers/70-XML.js b/nodes/core/parsers/70-XML.js index 32991f07d..3bfd38101 100644 --- a/nodes/core/parsers/70-XML.js +++ b/nodes/core/parsers/70-XML.js @@ -1,18 +1,3 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ module.exports = function(RED) { "use strict"; @@ -23,35 +8,39 @@ module.exports = function(RED) { RED.nodes.createNode(this,n); this.attrkey = n.attr; this.charkey = n.chr; + this.property = n.property||"payload"; var node = this; this.on("input", function(msg) { - if (msg.hasOwnProperty("payload")) { + var value = RED.util.getMessageProperty(msg,node.property); + if (value !== undefined) { var options; - if (typeof msg.payload === "object") { + if (typeof value === "object") { options = {renderOpts:{pretty:false}}; if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; } options.async = false; var builder = new xml2js.Builder(options); - msg.payload = builder.buildObject(msg.payload, options); + value = builder.buildObject(value, options); + RED.util.setMessageProperty(msg,node.property,value); node.send(msg); } - else if (typeof msg.payload == "string") { + else if (typeof value == "string") { options = {}; if (msg.hasOwnProperty("options") && typeof msg.options === "object") { options = msg.options; } options.async = true; options.attrkey = node.attrkey || options.attrkey || '$'; options.charkey = node.charkey || options.charkey || '_'; - parseString(msg.payload, options, function (err, result) { + parseString(value, options, function (err, result) { if (err) { node.error(err, msg); } else { - msg.payload = result; + value = result; + RED.util.setMessageProperty(msg,node.property,value); node.send(msg); } }); } else { node.warn(RED._("xml.errors.xml_js")); } } - else { node.send(msg); } // If no payload - just pass it on. + else { node.send(msg); } // If no property - just pass it on. }); } RED.nodes.registerType("xml",XMLNode); diff --git a/test/nodes/core/analysis/72-sentiment_spec.js b/test/nodes/core/analysis/72-sentiment_spec.js index 51f06abff..3a6ae07b1 100644 --- a/test/nodes/core/analysis/72-sentiment_spec.js +++ b/test/nodes/core/analysis/72-sentiment_spec.js @@ -75,6 +75,28 @@ describe('sentiment Node', function() { }); }); + it('should add a positive score for good words - alternative property', function(done) { + var flow = [{id:"jn1",type:"sentiment",property:"foo",wires:[["jn2"]]}, + {id:"jn2", type:"helper"}]; + helper.load(sentimentNode, flow, function() { + var jn1 = helper.getNode("jn1"); + var jn2 = helper.getNode("jn2"); + jn2.on("input", function(msg) { + try { + msg.should.have.property('sentiment'); + msg.sentiment.should.have.property('score'); + msg.sentiment.score.should.be.a.Number(); + msg.sentiment.score.should.be.above(10); + done(); + } catch(err) { + done(err); + } + }); + var testString = 'good, great, best, brilliant'; + jn1.receive({foo:testString}); + }); + }); + it('should add a negative score for bad words', function(done) { var flow = [{id:"jn1",type:"sentiment",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; @@ -93,6 +115,24 @@ describe('sentiment Node', function() { }); }); + it('should add a negative score for bad words - alternative property', function(done) { + var flow = [{id:"jn1",type:"sentiment",property:"foo",wires:[["jn2"]]}, + {id:"jn2", type:"helper"}]; + helper.load(sentimentNode, flow, function() { + var jn1 = helper.getNode("jn1"); + var jn2 = helper.getNode("jn2"); + jn2.on("input", function(msg) { + msg.should.have.property('sentiment'); + msg.sentiment.should.have.property('score'); + msg.sentiment.score.should.be.a.Number(); + msg.sentiment.score.should.be.below(-10); + done(); + }); + var testString = 'bad, horrible, negative, awful'; + jn1.receive({foo:testString}); + }); + }); + it('should allow you to override word scoring', function(done) { var flow = [{id:"jn1",type:"sentiment",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; @@ -112,4 +152,23 @@ describe('sentiment Node', function() { }); }); + it('should allow you to override word scoring - alternative property', function(done) { + var flow = [{id:"jn1",type:"sentiment",property:"foo",wires:[["jn2"]]}, + {id:"jn2", type:"helper"}]; + helper.load(sentimentNode, flow, function() { + var jn1 = helper.getNode("jn1"); + var jn2 = helper.getNode("jn2"); + jn2.on("input", function(msg) { + msg.should.have.property('sentiment'); + msg.sentiment.should.have.property('score'); + msg.sentiment.score.should.be.a.Number(); + msg.sentiment.score.should.equal(20); + done(); + }); + var testString = 'sick, wicked'; + var overrides = {'sick': 10, 'wicked': 10 }; + jn1.receive({foo:testString,overrides:overrides}); + }); + }); + }); diff --git a/test/nodes/core/parsers/70-XML_spec.js b/test/nodes/core/parsers/70-XML_spec.js index 68e3ddd52..79c1de30f 100644 --- a/test/nodes/core/parsers/70-XML_spec.js +++ b/test/nodes/core/parsers/70-XML_spec.js @@ -57,6 +57,26 @@ describe('XML node', function() { }); }); + it('should convert a valid xml string to a javascript object - alternative property', function(done) { + var flow = [{id:"n1",type:"xml",property:"foo",wires:[["n2"]],func:"return msg;"}, + {id:"n2", type:"helper"}]; + helper.load(xmlNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + msg.should.have.property('topic', 'bar'); + msg.foo.should.have.property('employees'); + msg.foo.employees.should.have.property('firstName'); + should.equal(msg.foo.employees.firstName[0], 'John'); + msg.foo.employees.should.have.property('lastName'); + should.equal(msg.foo.employees.lastName[0], 'Smith'); + done(); + }); + var string = 'JohnSmith'; + n1.receive({foo:string,topic: "bar"}); + }); + }); + it('should convert a valid xml string to a javascript object with options', function(done) { var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, {id:"n2", type:"helper"}]; @@ -94,20 +114,20 @@ describe('XML node', function() { }); }); - it('should convert a javascript object to an xml string with options', function(done) { - var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, + it('should convert a javascript object to an xml string with options - alternative property', function(done) { + var flow = [{id:"n1",type:"xml",property:"foo",wires:[["n2"]],func:"return msg;"}, {id:"n2", type:"helper"}]; helper.load(xmlNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); n2.on("input", function(msg) { msg.should.have.property('topic', 'bar'); - var index = msg.payload.indexOf('\n John\n Smith\n'); + var index = msg.foo.indexOf('\n John\n Smith\n'); index.should.be.above(-1); done(); }); var obj = {"employees":{"firstName":["John"],"lastName":["Smith"] }}; - n1.receive({payload:obj, topic:"bar", options:{headless:true}}); + n1.receive({foo:obj, topic:"bar", options:{headless:true}}); }); }); @@ -133,7 +153,7 @@ describe('XML node', function() { },200); }); }); - + it('should log an error if asked to parse something thats not xml or js', function(done) { var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, {id:"n2", type:"helper"}]; diff --git a/test/nodes/core/parsers/70-YAML_spec.js b/test/nodes/core/parsers/70-YAML_spec.js index 9055efa3d..9f2fa7ec5 100644 --- a/test/nodes/core/parsers/70-YAML_spec.js +++ b/test/nodes/core/parsers/70-YAML_spec.js @@ -55,6 +55,24 @@ describe('YAML node', function() { }); }); + it('should convert a valid yaml string to a javascript object - using another property', function(done) { + var flow = [{id:"yn1",type:"yaml",property:"foo",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + yn2.on("input", function(msg) { + msg.should.have.property('topic', 'bar'); + msg.foo.should.have.property('employees'); + msg.foo.employees[0].should.have.property('firstName', 'John'); + msg.foo.employees[0].should.have.property('lastName', 'Smith'); + done(); + }); + var yamlString = "employees:\n - firstName: John\n lastName: Smith\n"; + yn1.receive({foo:yamlString,topic: "bar"}); + }); + }); + it('should convert a javascript object to a yaml string', function(done) { var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, {id:"yn2", type:"helper"}]; @@ -70,6 +88,21 @@ describe('YAML node', function() { }); }); + it('should convert a javascript object to a yaml string - using another property', function(done) { + var flow = [{id:"yn1",type:"yaml",property:"foo",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + yn2.on("input", function(msg) { + should.equal(msg.foo, "employees:\n - firstName: John\n lastName: Smith\n"); + done(); + }); + var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; + yn1.receive({foo:obj}); + }); + }); + it('should convert an array to a yaml string', function(done) { var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, {id:"yn2", type:"helper"}]; From 87d847a07405fa34a9aed028166dac170e374e85 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Tue, 23 Jan 2018 08:25:02 +0900 Subject: [PATCH 03/12] delete useless logging code from SWITCH node (#1570) --- nodes/core/logic/10-switch.html | 1 - 1 file changed, 1 deletion(-) diff --git a/nodes/core/logic/10-switch.html b/nodes/core/logic/10-switch.html index 5a39c2bee..77062c7fa 100644 --- a/nodes/core/logic/10-switch.html +++ b/nodes/core/logic/10-switch.html @@ -221,7 +221,6 @@ } } if ((rule.t === 'btwn') || (rule.t === 'index')) { - console.log(`; ${label}/${JSON.stringify(rule)}`); label += " "+getValueLabel(rule.vt,rule.v)+" & "+getValueLabel(rule.v2t,rule.v2); } else if (rule.t !== 'true' && rule.t !== 'false' && rule.t !== 'null' && rule.t !== 'nnull' && rule.t !== 'else' ) { label += " "+getValueLabel(rule.vt,rule.v); From da4a0f09ed3e76cad9eb44ac4ffc4469acb93e6b Mon Sep 17 00:00:00 2001 From: Kazuki Nakanishi Date: Tue, 23 Jan 2018 08:25:43 +0900 Subject: [PATCH 04/12] Add a button to restore user defined icon (#1569) --- editor/js/ui/editor.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/editor/js/ui/editor.js b/editor/js/ui/editor.js index a1a1cd4ea..179556716 100644 --- a/editor/js/ui/editor.js +++ b/editor/js/ui/editor.js @@ -776,6 +776,16 @@ RED.editor = (function() { var fileName = selectIconFile.val(); iconFileHidden.val(fileName); }); + var clear = $('').appendTo(iconForm); + clear.click(function(evt) { + evt.preventDefault(); + var iconPath = RED.utils.getDefaultNodeIcon(node._def, node); + selectIconModule.val(iconPath.module); + moduleChange(selectIconModule, selectIconFile, iconModuleHidden, iconFileHidden, iconSets, true); + selectIconFile.removeClass("input-error"); + selectIconFile.val(iconPath.file); + iconFileHidden.val(iconPath.file); + }); moduleChange(selectIconModule, selectIconFile, iconModuleHidden, iconFileHidden, iconSets, false); var iconFileList = iconSets[selectIconModule.val()]; From a75b8198582ae28fa9ef3a524d05807e5f588128 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Tue, 23 Jan 2018 08:26:17 +0900 Subject: [PATCH 05/12] reduce *MaxKeptMsgsCount property to one maxKeptMsgsCount (#1563) --- nodes/core/logic/10-switch.html | 2 +- nodes/core/logic/10-switch.js | 2 +- nodes/core/logic/17-split.html | 2 +- nodes/core/logic/17-split.js | 2 +- nodes/core/logic/19-batch.html | 2 +- nodes/core/logic/19-batch.js | 2 +- test/nodes/core/logic/10-switch_spec.js | 4 ++-- test/nodes/core/logic/17-split_spec.js | 6 +++--- test/nodes/core/logic/19-batch_spec.js | 8 ++++---- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/nodes/core/logic/10-switch.html b/nodes/core/logic/10-switch.html index 77062c7fa..a76553716 100644 --- a/nodes/core/logic/10-switch.html +++ b/nodes/core/logic/10-switch.html @@ -156,7 +156,7 @@

If repair sequence (reconstruct parts property of outgoing messages) checkbox is selected, msg.parts property are reconstructed for each port to make forks of valid sequences. Otherwise, msg.parts property of incoming messages are passed through.

Note:

-

This node internally keeps messages for its operation if repair sequence checkbox is ON. In order to prevent unexpected memory usage, maximum number of messages kept can be specified by switchMaxKeptMsgsCount property in settings.js.

+

This node internally keeps messages for its operation if repair sequence checkbox is ON. In order to prevent unexpected memory usage, maximum number of messages kept can be specified by maxKeptMsgsCount property in settings.js.

diff --git a/nodes/core/logic/19-batch.js b/nodes/core/logic/19-batch.js index bdd5b5408..cae77ff85 100644 --- a/nodes/core/logic/19-batch.js +++ b/nodes/core/logic/19-batch.js @@ -21,7 +21,7 @@ module.exports = function(RED) { function max_kept_msgs_count(node) { if (_max_kept_msgs_count === undefined) { - var name = "batchMaxKeptMsgsCount"; + var name = "maxKeptMsgsCount"; if (RED.settings.hasOwnProperty(name)) { _max_kept_msgs_count = RED.settings[name]; } diff --git a/test/nodes/core/logic/10-switch_spec.js b/test/nodes/core/logic/10-switch_spec.js index a34f0947e..96b0a4616 100644 --- a/test/nodes/core/logic/10-switch_spec.js +++ b/test/nodes/core/logic/10-switch_spec.js @@ -29,7 +29,7 @@ describe('switch Node', function() { afterEach(function(done) { helper.unload(); helper.stopServer(done); - RED.settings.switchMaxKeptMsgsCount = 0; + RED.settings.maxKeptMsgsCount = 0; }); it('should be loaded with some defaults', function(done) { @@ -692,7 +692,7 @@ describe('switch Node', function() { ]; helper.load(switchNode, flow, function() { var n1 = helper.getNode("n1"); - RED.settings.switchMaxKeptMsgsCount = 2; + RED.settings.maxKeptMsgsCount = 2; setTimeout(function() { var logEvents = helper.log().args.filter(function (evt) { return evt[0].type == "switch"; diff --git a/test/nodes/core/logic/17-split_spec.js b/test/nodes/core/logic/17-split_spec.js index e2c8aaa97..2de6fae06 100644 --- a/test/nodes/core/logic/17-split_spec.js +++ b/test/nodes/core/logic/17-split_spec.js @@ -270,7 +270,7 @@ describe('JOIN node', function() { afterEach(function() { helper.unload(); - RED.settings.joinMaxKeptMsgsCount = 0; + RED.settings.maxKeptMsgsCount = 0; }); it('should be loaded', function(done) { @@ -1072,7 +1072,7 @@ describe('JOIN node', function() { {id:"n2", type:"helper"}]; helper.load(joinNode, flow, function() { var n1 = helper.getNode("n1"); - RED.settings.joinMaxKeptMsgsCount = 2; + RED.settings.maxKeptMsgsCount = 2; setTimeout(function() { var logEvents = helper.log().args.filter(function (evt) { return evt[0].type == "join"; @@ -1101,7 +1101,7 @@ describe('JOIN node', function() { {id:"n2", type:"helper"}]; helper.load(joinNode, flow, function() { var n1 = helper.getNode("n1"); - RED.settings.joinMaxKeptMsgsCount = 2; + RED.settings.maxKeptMsgsCount = 2; setTimeout(function() { var logEvents = helper.log().args.filter(function (evt) { return evt[0].type == "join"; diff --git a/test/nodes/core/logic/19-batch_spec.js b/test/nodes/core/logic/19-batch_spec.js index aa806f5bf..dda49030a 100644 --- a/test/nodes/core/logic/19-batch_spec.js +++ b/test/nodes/core/logic/19-batch_spec.js @@ -28,7 +28,7 @@ describe('BATCH node', function() { afterEach(function() { helper.unload(); - RED.settings.batchMaxKeptMsgsCount = 0; + RED.settings.maxKeptMsgsCount = 0; }); it('should be loaded with defaults', function(done) { @@ -172,7 +172,7 @@ describe('BATCH node', function() { helper.load(batchNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - RED.settings.batchMaxKeptMsgsCount = 2; + RED.settings.maxKeptMsgsCount = 2; setTimeout(function() { var logEvents = helper.log().args.filter(function (evt) { return evt[0].type == "batch"; @@ -239,7 +239,7 @@ describe('BATCH node', function() { helper.load(batchNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - RED.settings.batchMaxKeptMsgsCount = 2; + RED.settings.maxKeptMsgsCount = 2; setTimeout(function() { var logEvents = helper.log().args.filter(function (evt) { return evt[0].type == "batch"; @@ -312,7 +312,7 @@ describe('BATCH node', function() { helper.load(batchNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); - RED.settings.batchMaxKeptMsgsCount = 2; + RED.settings.maxKeptMsgsCount = 2; setTimeout(function() { var logEvents = helper.log().args.filter(function (evt) { return evt[0].type == "batch"; From 1893642187fb2fb0722570964aa6bbea8e9aa0b0 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 23 Jan 2018 15:40:41 +0000 Subject: [PATCH 06/12] Sort typeSearch results based on position of match --- editor/js/ui/typeSearch.js | 50 +++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/editor/js/ui/typeSearch.js b/editor/js/ui/typeSearch.js index e3cb7f1ee..3b068a96a 100644 --- a/editor/js/ui/typeSearch.js +++ b/editor/js/ui/typeSearch.js @@ -19,6 +19,7 @@ RED.typeSearch = (function() { function search(val) { activeFilter = val.toLowerCase(); var visible = searchResults.editableList('filter'); + searchResults.editableList('sort'); setTimeout(function() { selected = 0; searchResults.children().removeClass('selected'); @@ -101,6 +102,23 @@ RED.typeSearch = (function() { } return (activeFilter==="")||(data.index.indexOf(activeFilter) > -1); }, + sort: function(A,B) { + if (activeFilter === "") { + return A.i - B.i; + } + var Ai = A.index.indexOf(activeFilter); + var Bi = B.index.indexOf(activeFilter); + if (Ai === -1) { + return 1; + } + if (Bi === -1) { + return -1; + } + if (Ai === Bi) { + return sortTypeLabels(A,B); + } + return Ai-Bi; + }, addItem: function(container,i,object) { var def = object.def; object.index = object.type.toLowerCase(); @@ -225,7 +243,17 @@ RED.typeSearch = (function() { } return label; } - + function sortTypeLabels(a,b) { + var al = a.label.toLowerCase(); + var bl = b.label.toLowerCase(); + if (al < bl) { + return -1; + } else if (al === bl) { + return 0; + } else { + return 1; + } + } function refreshTypeList() { var i; searchResults.editableList('empty'); @@ -250,27 +278,19 @@ RED.typeSearch = (function() { items.push({type:t,def: def, label:getTypeLabel(t,def)}); } }); - items.sort(function(a,b) { - var al = a.label.toLowerCase(); - var bl = b.label.toLowerCase(); - if (al < bl) { - return -1; - } else if (al === bl) { - return 0; - } else { - return 1; - } - }) + items.sort(sortTypeLabels); var commonCount = 0; var item; + var index = 0; for(i=0;i Date: Tue, 23 Jan 2018 21:28:58 +0000 Subject: [PATCH 07/12] oneditdelete should be available to all node types Closes #1346 --- editor/js/nodes.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/editor/js/nodes.js b/editor/js/nodes.js index b1ee091c3..6e3a251ac 100644 --- a/editor/js/nodes.js +++ b/editor/js/nodes.js @@ -272,10 +272,19 @@ RED.nodes = (function() { if (updatedConfigNode) { RED.workspaces.refresh(); } + try { + if (node._def.oneditdelete) { + node._def.oneditdelete.call(node); + } + } catch(err) { + console.log("oneditdelete",node.id,node.type,err.toString()); + } RED.events.emit('nodes:remove',node); } } if (node && node._def.onremove) { + // Deprecated: never documented but used by some early nodes + console.log("Deprecated API warning: node type ",node.type," has an onremove function - should be oneditremove - please report"); node._def.onremove.call(n); } return {links:removedLinks,nodes:removedNodes}; From 3cb5cbd8d539a2d57d30b988e60f01f5bf4c124e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 23 Jan 2018 23:08:11 +0000 Subject: [PATCH 08/12] Allow adminAuth.user to be a Function Fixes #1461 --- red/api/auth/clients.js | 6 +-- red/api/auth/index.js | 115 +++++++++++++++++++++------------------- red/api/auth/tokens.js | 8 ++- red/api/auth/users.js | 59 +++++++++++---------- 4 files changed, 97 insertions(+), 91 deletions(-) diff --git a/red/api/auth/clients.js b/red/api/auth/clients.js index 4495efba0..63e59ccce 100644 --- a/red/api/auth/clients.js +++ b/red/api/auth/clients.js @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - -var when = require("when"); var clients = [ {id:"node-red-editor",secret:"not_available"}, @@ -25,9 +23,9 @@ module.exports = { get: function(id) { for (var i=0;i 0) { - user.image = arguments[0].photos[0].value; + } else { + // Try to extract common profile information + if (args[0].hasOwnProperty('photos') && args[0].photos.length > 0) { + user.image = args[0].photos[0].value; + } + return cleanUser(user); } - return when.resolve(user); } - } - return when.resolve(null); + return null; + }); } function get(username) { - return when.resolve(users[username]); + return Promise.resolve(users[username]); } function getDefaultUser() { - return when.resolve(null); + return Promise.resolve(null); } var api = { @@ -63,7 +64,6 @@ var api = { function init(config) { users = {}; - passwords = {}; defaultUser = null; if (config.type == "credentials" || config.type == "strategy") { if (config.users) { @@ -77,11 +77,7 @@ function init(config) { } for (var i=0;i Date: Wed, 24 Jan 2018 15:07:43 +0000 Subject: [PATCH 09/12] Handle scoped modules via palette editor --- editor/js/ui/palette.js | 7 ++++--- red/runtime/nodes/registry/installer.js | 3 +-- red/runtime/nodes/registry/localfilesystem.js | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/editor/js/ui/palette.js b/editor/js/ui/palette.js index 99d99a214..d83e3a082 100644 --- a/editor/js/ui/palette.js +++ b/editor/js/ui/palette.js @@ -412,16 +412,17 @@ RED.palette = (function() { for (var j=0;j Date: Wed, 24 Jan 2018 22:01:07 +0000 Subject: [PATCH 10/12] Rewording some of the message sequence nodes (#1564) * Rewording some of the message sequence nodes * Fix batch test for overlap renaming * Finish msg-sequence node help rewording * Rename maxKeptMsgsCount to nodeMessageBufferMaxLength * Rename nodeMessageBufferMaxLength in tests * Remove Join-merge mode for later rework --- nodes/core/locales/en-US/messages.json | 25 ++- nodes/core/logic/10-switch.html | 130 +++----------- nodes/core/logic/10-switch.js | 10 +- nodes/core/logic/17-split.html | 158 +++++------------ nodes/core/logic/17-split.js | 101 +---------- nodes/core/logic/18-sort.html | 2 +- nodes/core/logic/18-sort.js | 2 +- nodes/core/logic/19-batch.html | 91 +++++----- nodes/core/logic/19-batch.js | 16 +- settings.js | 9 +- test/nodes/core/logic/10-switch_spec.js | 4 +- test/nodes/core/logic/17-split_spec.js | 220 +----------------------- test/nodes/core/logic/18-sort_spec.js | 4 +- test/nodes/core/logic/19-batch_spec.js | 54 +++--- 14 files changed, 181 insertions(+), 645 deletions(-) diff --git a/nodes/core/locales/en-US/messages.json b/nodes/core/locales/en-US/messages.json index d8dba5505..dcf639f59 100644 --- a/nodes/core/locales/en-US/messages.json +++ b/nodes/core/locales/en-US/messages.json @@ -543,7 +543,7 @@ "label": { "property": "Property", "rule": "rule", - "repair" : "repair sequence (reconstruct parts property of outgoing messages)" + "repair" : "recreate message sequences" }, "and": "and", "checkall": "checking all rules", @@ -559,7 +559,7 @@ "nnull":"is not null", "head":"head", "tail":"tail", - "index":"is between", + "index":"index between", "exp":"JSONata exp", "else":"otherwise" }, @@ -847,7 +847,7 @@ "mode":{ "mode":"Mode", "auto":"automatic", - "merge":"merge sequence", + "merge":"merge sequences", "reduce":"reduce sequence", "custom":"manual" }, @@ -882,8 +882,8 @@ "exp": "Reduce exp", "exp-value": "exp", "init": "Initial value", - "right": "Evaluate in reverse order (right to left)", - "fixup": "Fixup exp" + "right": "Evaluate in reverse order (last to first)", + "fixup": "Fix-up exp" }, "errors": { "invalid-expr": "Invalid JSONata expression: __error__" @@ -905,20 +905,19 @@ "batch" : { "mode": { "label" : "Mode", - "num-msgs" : "number of messages", - "interval" : "interval in seconds", - "concat" : "concatenate sequences" + "num-msgs" : "Group by number of messages", + "interval" : "Group by time interval", + "concat" : "Concatenate sequences" }, "count": { - "label" : "Number of msgs", - "overwrap" : "Overwrap", + "label" : "Number of messages", + "overlap" : "Overlap", "count" : "count", - "invalid" : "Invalid count and overwrap" + "invalid" : "Invalid count and overlap" }, "interval": { - "label" : "Interval (sec)", + "label" : "Interval", "seconds" : "seconds", - "sec" : "sec", "empty" : "send empty message when no message arrives" }, "concat": { diff --git a/nodes/core/logic/10-switch.html b/nodes/core/logic/10-switch.html index a76553716..7605135cc 100644 --- a/nodes/core/logic/10-switch.html +++ b/nodes/core/logic/10-switch.html @@ -40,7 +40,7 @@ diff --git a/nodes/core/logic/17-split.js b/nodes/core/logic/17-split.js index ad7d48cfd..88a6c3d6f 100644 --- a/nodes/core/logic/17-split.js +++ b/nodes/core/logic/17-split.js @@ -233,10 +233,10 @@ module.exports = function(RED) { var _max_kept_msgs_count = undefined; - + function max_kept_msgs_count(node) { if (_max_kept_msgs_count === undefined) { - var name = "maxKeptMsgsCount"; + var name = "nodeMessageBufferMaxLength"; if (RED.settings.hasOwnProperty(name)) { _max_kept_msgs_count = RED.settings[name]; } @@ -247,89 +247,6 @@ module.exports = function(RED) { return _max_kept_msgs_count; } - function add_to_topic(node, pending, topic, msg) { - var merge_on_change = node.merge_on_change; - if (!pending.hasOwnProperty(topic)) { - pending[topic] = []; - } - var topics = pending[topic]; - topics.push(msg); - if (merge_on_change) { - var counts = node.topic_counts; - if (topics.length > counts[topic]) { - topics.shift(); - node.pending_count--; - } - } - } - - function compute_topic_counts(topics) { - var counts = {}; - for (var topic of topics) { - counts[topic] = (counts.hasOwnProperty(topic) ? counts[topic] : 0) +1; - } - return counts; - } - - function try_merge(node, pending, merge_on_change) { - var topics = node.topics; - var counts = node.topic_counts; - for(var topic of topics) { - if(!pending.hasOwnProperty(topic) || - (pending[topic].length < counts[topic])) { - return; - } - } - var merge_on_change = node.merge_on_change; - var msgs = []; - var vals = []; - var new_msg = {payload: vals}; - for (var topic of topics) { - var pmsgs = pending[topic]; - var msg = pmsgs.shift(); - if (merge_on_change) { - pmsgs.push(msg); - } - var pval = msg.payload; - var val = new_msg[topic]; - msgs.push(msg); - vals.push(pval); - if (val instanceof Array) { - new_msg[topic].push(pval); - } - else if (val === undefined) { - new_msg[topic] = pval; - } - else { - new_msg[topic] = [val, pval] - } - } - node.send(new_msg); - if (!merge_on_change) { - node.pending_count -= topics.length; - } - } - - function merge_msg(node, msg) { - var topics = node.topics; - var topic = msg.topic; - if(node.topics.indexOf(topic) >= 0) { - var pending = node.pending; - if(node.topic_counts == undefined) { - node.topic_counts = compute_topic_counts(topics) - } - add_to_topic(node, pending, topic, msg); - node.pending_count++; - var max_msgs = max_kept_msgs_count(node); - if ((max_msgs > 0) && (node.pending_count > max_msgs)) { - node.pending = {}; - node.pending_count = 0; - node.error(RED._("join.too-many"), msg); - } - try_merge(node, pending); - } - } - function apply_r(exp, accum, msg, index, count) { exp.assign("I", index); exp.assign("N", count); @@ -350,7 +267,7 @@ module.exports = function(RED) { } return exp } - + function reduce_and_send_group(node, group) { var is_right = node.reduce_right; var flag = is_right ? -1 : 1; @@ -374,15 +291,15 @@ module.exports = function(RED) { } node.send({payload: accum}); } - + function reduce_msg(node, msg) { if(msg.hasOwnProperty('parts')) { var parts = msg.parts; var pending = node.pending; var pending_count = node.pending_count; var gid = msg.parts.id; + var count; if(!pending.hasOwnProperty(gid)) { - var count = undefined; if(parts.hasOwnProperty('count')) { count = msg.parts.count; } @@ -568,10 +485,6 @@ module.exports = function(RED) { node.warn("Message missing msg.parts property - cannot join in 'auto' mode") return; } - if (node.mode === 'merge' && !msg.hasOwnProperty("topic")) { - node.warn("Message missing msg.topic property - cannot join in 'merge' mode"); - return; - } if (node.propertyType == "full") { property = msg; @@ -602,10 +515,6 @@ module.exports = function(RED) { arrayLen = msg.parts.len; propertyIndex = msg.parts.index; } - else if (node.mode === 'merge') { - merge_msg(node, msg); - return; - } else if (node.mode === 'reduce') { reduce_msg(node, msg); return; diff --git a/nodes/core/logic/18-sort.html b/nodes/core/logic/18-sort.html index 5ce1a9f82..af66bef84 100644 --- a/nodes/core/logic/18-sort.html +++ b/nodes/core/logic/18-sort.html @@ -81,7 +81,7 @@

Note: This node internally keeps messages for its operation. In order to prevent unexpected memory usage, maximum number of messages kept can be specified. Default is no limit on number of messages.

    -
  • maxKeptMsgsCount property set in settings.js.
  • +
  • nodeMessageBufferMaxLength property set in settings.js.

diff --git a/nodes/core/logic/18-sort.js b/nodes/core/logic/18-sort.js index 65d8e6c45..e30535dc1 100644 --- a/nodes/core/logic/18-sort.js +++ b/nodes/core/logic/18-sort.js @@ -21,7 +21,7 @@ module.exports = function(RED) { function max_kept_msgs_count(node) { if (_max_kept_msgs_count === undefined) { - var name = "maxKeptMsgsCount"; + var name = "nodeMessageBufferMaxLength"; if (RED.settings.hasOwnProperty(name)) { _max_kept_msgs_count = RED.settings[name]; } diff --git a/nodes/core/logic/19-batch.html b/nodes/core/logic/19-batch.html index ae7d00588..371a48bf9 100644 --- a/nodes/core/logic/19-batch.html +++ b/nodes/core/logic/19-batch.html @@ -18,7 +18,7 @@