From 6b466d217aeaf8adfe7db01ccd3cbfdd38244840 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Mon, 22 Jan 2018 09:23:22 +0900 Subject: [PATCH] update UI for SORT node (#1567) * update UI of SORT node * fix maxKeptMsgsCount of SORT node --- nodes/core/locales/en-US/messages.json | 64 +++---- nodes/core/logic/18-sort.html | 81 ++++++--- nodes/core/logic/18-sort.js | 38 +++-- settings.js | 5 +- test/nodes/core/logic/18-sort_spec.js | 224 ++++++++++++++++--------- 5 files changed, 258 insertions(+), 154 deletions(-) diff --git a/nodes/core/locales/en-US/messages.json b/nodes/core/locales/en-US/messages.json index 3d07b0c33..e524b78f3 100644 --- a/nodes/core/locales/en-US/messages.json +++ b/nodes/core/locales/en-US/messages.json @@ -871,55 +871,55 @@ "complete":"After a message with the msg.complete property set", "tip":"This mode assumes this node is either paired with a split node or the received messages will have a properly configured msg.parts property.", "too-many" : "too many pending messages in join node", - "merge": { - "topics-label":"Merged Topics", - "topics":"topics", + "merge": { + "topics-label":"Merged Topics", + "topics":"topics", "topic" : "topic", "on-change":"Send merged message on arrival of a new topic" - }, + }, "reduce": { - "exp": "Reduce exp", - "exp-value": "exp", - "init": "Initial value", - "right": "Evaluate in reverse order (right to left)", - "fixup": "Fixup exp" + "exp": "Reduce exp", + "exp-value": "exp", + "init": "Initial value", + "right": "Evaluate in reverse order (right to left)", + "fixup": "Fixup exp" }, "errors": { "invalid-expr": "Invalid JSONata expression: __error__" } }, "sort" : { - "key-type" : "Key type", - "payload" : "payload or element", - "exp" : "expression", - "key-exp" : "Key exp.", - "order" : "Order", - "ascending" : "ascending", - "descending" : "descending", - "as-number" : "as number", + "target" : "Sort", + "seq" : "message sequence", + "key" : "Key", + "elem" : "element value", + "order" : "Order", + "ascending" : "ascending", + "descending" : "descending", + "as-number" : "as number", "invalid-exp" : "invalid JSONata expression in sort node", "too-many" : "too many pending messages in sort node", "clear" : "clear pending message in sort node" }, "batch" : { - "mode": { - "label" : "Mode", - "num-msgs" : "number of messages", - "interval" : "interval in seconds", + "mode": { + "label" : "Mode", + "num-msgs" : "number of messages", + "interval" : "interval in seconds", "concat" : "concatenate sequences" - }, - "count": { - "label" : "Number of msgs", - "overwrap" : "Overwrap", - "count" : "count", + }, + "count": { + "label" : "Number of msgs", + "overwrap" : "Overwrap", + "count" : "count", "invalid" : "Invalid count and overwrap" - }, - "interval": { - "label" : "Interval (sec)", - "seconds" : "seconds", - "sec" : "sec", + }, + "interval": { + "label" : "Interval (sec)", + "seconds" : "seconds", + "sec" : "sec", "empty" : "send empty message when no message arrives" - }, + }, "concat": { "topics-label": "Topics", "topic" : "topic" diff --git a/nodes/core/logic/18-sort.html b/nodes/core/logic/18-sort.html index 3aacd46a1..5ce1a9f82 100644 --- a/nodes/core/logic/18-sort.html +++ b/nodes/core/logic/18-sort.html @@ -19,17 +19,24 @@ @@ -86,9 +93,13 @@ defaults: { name: { value:"" }, order: { value:"ascending" }, - as_num : { value:false }, - keyType : { value:"payload" }, - key : { value:"" } + as_num: { value:false }, + target: { value:"payload" }, + targetType: { value:"msg" }, + msgKey: { value:"payload" }, + msgKeyType: { value:"elem" }, + seqKey: { value:"payload" }, + seqKeyType: { value:"msg" } }, inputs:1, outputs:1, @@ -100,13 +111,37 @@ return this.name ? "node_label_italic" : ""; }, oneditprepare: function() { - $("#node-input-key").typedInput({default:'jsonata', types:['jsonata']}); - $("#node-input-keyType").change(function(e) { - var val = $(this).val(); - $(".node-row-sort-key").toggle(val === 'exp'); + var seq = { + value: "seq", + label: RED._("node-red:sort.seq"), + hasValue: false + }; + var elem = { + value: "elem", + label: RED._("node-red:sort.elem"), + hasValue: false + }; + $("#node-input-target").typedInput({ + default:'msg', + typeField: $("#node-input-targetType"), + types:['msg', seq] }); - $("#node-input-keyType").change(); - $("#node-input-order").change(); + $("#node-input-msgKey").typedInput({ + default:'elem', + typeField: $("#node-input-msgKeyType"), + types:[elem, 'jsonata'] + }); + $("#node-input-seqKey").typedInput({ + default:'msg', + typeField: $("#node-input-seqKeyType"), + types:['msg', 'jsonata'] + }); + $("#node-input-target").change(function(e) { + var val = $("#node-input-target").typedInput('type'); + $(".node-row-sort-msg-key").toggle(val === "msg"); + $(".node-row-sort-seq-key").toggle(val === "seq"); + }); + $("#node-input-target").change(); } }); diff --git a/nodes/core/logic/18-sort.js b/nodes/core/logic/18-sort.js index 529e83f37..65d8e6c45 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 = "sortMaxKeptMsgsCount"; + var name = "maxKeptMsgsCount"; if (RED.settings.hasOwnProperty(name)) { _max_kept_msgs_count = RED.settings[name]; } @@ -60,11 +60,15 @@ module.exports = function(RED) { var pending_id = 0; var order = n.order || "ascending"; var as_num = n.as_num || false; - var key_is_payload = (n.keyType === 'payload'); - var key_exp = undefined; - if (!key_is_payload) { + var target_prop = n.target || "payload"; + var target_is_prop = (n.targetType === 'msg'); + var key_is_exp = target_is_prop ? (n.msgKeyType === "jsonata") : (n.seqKeyType === "jsonata"); + var key_prop = n.seqKey || "payload"; + var key_exp = target_is_prop ? n.msgKey : n.seqKey; + + if (key_is_exp) { try { - key_exp = RED.util.prepareJSONataExpression(n.key, this); + key_exp = RED.util.prepareJSONataExpression(key_exp, this); } catch (e) { node.error(RED._("sort.invalid-exp")); @@ -87,10 +91,12 @@ module.exports = function(RED) { } function send_group(group) { - var key = key_is_payload - ? function(msg) { return msg.payload; } - : function(msg) { + var key = key_is_exp + ? function(msg) { return eval_jsonata(node, key_exp, msg); + } + : function(msg) { + return RED.util.getMessageProperty(msg, key_prop); }; var comp = gen_comp(key); var msgs = group.msgs; @@ -108,16 +114,16 @@ module.exports = function(RED) { } function sort_payload(msg) { - var payload = msg.payload; - if (Array.isArray(payload)) { - var key = key_is_payload - ? function(elem) { return elem; } - : function(elem) { + var data = RED.util.getMessageProperty(msg, target_prop); + if (Array.isArray(data)) { + var key = key_is_exp + ? function(elem) { return eval_jsonata(node, key_exp, elem); - }; + } + : function(elem) { return elem; }; var comp = gen_comp(key); try { - payload.sort(comp); + data.sort(comp); } catch (e) { return false; @@ -162,7 +168,7 @@ module.exports = function(RED) { } function process_msg(msg) { - if (!msg.hasOwnProperty("parts")) { + if (target_is_prop) { if (sort_payload(msg)) { node.send(msg); } diff --git a/settings.js b/settings.js index a3eef19e2..eca108e96 100644 --- a/settings.js +++ b/settings.js @@ -49,10 +49,7 @@ module.exports = { // The maximum number of messages kept internally in nodes. // Zero or undefined value means not restricting number of messages. - //sortMaxKeptMsgsCount: 0, - //switchMaxKeptMsgsCount: 0, - //joinMaxKeptMsgsCount: 0, - //batchMaxKeptMsgsCount: 0, + //maxKeptMsgsCount: 0, // To disable the option for using local files for storing keys and certificates in the TLS configuration // node, set this to true diff --git a/test/nodes/core/logic/18-sort_spec.js b/test/nodes/core/logic/18-sort_spec.js index 1695a7439..98e299f6f 100644 --- a/test/nodes/core/logic/18-sort_spec.js +++ b/test/nodes/core/logic/18-sort_spec.js @@ -27,10 +27,11 @@ describe('SORT node', function() { afterEach(function() { helper.unload(); + RED.settings.maxKeptMsgsCount = 0; }); it('should be loaded', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", name: "SortNode", wires:[["n2"]]}, + var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, name: "SortNode", wires:[["n2"]]}, {id:"n2", type:"helper"}]; helper.load(sortNode, flow, function() { var n1 = helper.getNode("n1"); @@ -39,16 +40,58 @@ describe('SORT node', function() { }); }); - function check_sort0(flow, data_in, data_out, done) { + function check_sort0(flow, target, key, key_type, data_in, data_out, done) { + var sort = flow[0]; + sort.target = target; + sort.targetType = "msg"; + sort.msgKey = key; + sort.msgKeyType = key_type; + helper.load(sortNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + msg.should.have.property(target); + var data = msg[target]; + data.length.should.equal(data_out.length); + for(var i = 0; i < data_out.length; i++) { + data[i].should.equal(data_out[i]); + } + done(); + }); + var msg = {}; + msg[target] = data_in; + n1.receive(msg); + }); + } + + function check_sort0A(flow, data_in, data_out, done) { + check_sort0(flow, "payload", "", "elem", data_in, data_out, done); + } + + function check_sort0B(flow, data_in, data_out, done) { + check_sort0(flow, "data", "", "elem", data_in, data_out, done); + } + + function check_sort0C(flow, exp, data_in, data_out, done) { + check_sort0(flow, "data", exp, "jsonata", data_in, data_out, done); + } + + function check_sort1(flow, key, key_type, data_in, data_out, done) { + var sort = flow[0]; + var prop = (key_type === "msg") ? key : "payload"; + sort.targetType = "seq"; + sort.seqKey = key; + sort.seqKeyType = key_type; helper.load(sortNode, flow, function() { var n1 = helper.getNode("n1"); var n2 = helper.getNode("n2"); var count = 0; n2.on("input", function(msg) { - msg.should.have.property("payload"); + msg.should.have.property(prop); msg.should.have.property("parts"); msg.parts.should.have.property("count", data_out.length); - var index = data_out.indexOf(msg.payload); + var data = msg[prop]; + var index = data_out.indexOf(data); msg.parts.should.have.property("index", index); count++; if (count === data_out.length) { @@ -58,111 +101,132 @@ describe('SORT node', function() { var len = data_in.length; for(var i = 0; i < len; i++) { var parts = { id: "X", index: i, count: len }; - n1.receive({payload:data_in[i], parts: parts}); + var msg = {parts: parts}; + msg[prop] = data_in[i]; + n1.receive(msg); } }); } - function check_sort1(flow, data_in, data_out, done) { - helper.load(sortNode, flow, function() { - var n1 = helper.getNode("n1"); - var n2 = helper.getNode("n2"); - n2.on("input", function(msg) { - msg.should.have.property("payload"); - msg.payload.length.should.equal(data_out.length); - for(var i = 0; i < data_out.length; i++) { - msg.payload[i].should.equal(data_out[i]); - } - done(); - }); - n1.receive({payload:data_in}); - }); + function check_sort1A(flow, data_in, data_out, done) { + check_sort1(flow, "payload", "msg", data_in, data_out, done); } + function check_sort1B(flow, data_in, data_out, done) { + check_sort1(flow, "data", "msg", data_in, data_out, done); + } + + function check_sort1C(flow, exp, data_in, data_out, done) { + check_sort1(flow, exp, "jsonata", data_in, data_out, done); + } + + (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]}, + var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]}, {id:"n2", type:"helper"}]; var data_in = [ "200", "4", "30", "1000" ]; var data_out = [ "1000", "200", "30", "4" ]; - it('should sort message group (payload, not number, ascending)', function(done) { - check_sort0(flow, data_in, data_out, done); + it('should sort payload (elem, not number, ascending)', function(done) { + check_sort0A(flow, data_in, data_out, done); }); - it('should sort payload (payload, not number, ascending)', function(done) { - check_sort1(flow, data_in, data_out, done); + it('should sort msg prop (elem, not number, ascending)', function(done) { + check_sort0B(flow, data_in, data_out, done); }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"payload", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "4", "30", "200", "1000" ]; - it('should sort message group (payload, not number, descending)', function(done) { - check_sort0(flow, data_in, data_out, done); + it('should sort message group/payload (not number, ascending)', function(done) { + check_sort1A(flow, data_in, data_out, done); }); - it('should sort payload (payload, not number, descending)', function(done) { - check_sort1(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, keyType:"payload", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "4", "30", "200", "1000" ]; - it('should sort message group (payload, number, ascending)', function(done) { - check_sort0(flow, data_in, data_out, done); - }); - it('should sort payload (payload, number, ascending)', function(done) { - check_sort1(flow, data_in, data_out, done); - }); - })(); - - (function() { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:true, keyType:"payload", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - var data_in = [ "200", "4", "30", "1000" ]; - var data_out = [ "1000", "200", "30", "4" ]; - it('should sort message group (payload, number, descending)', function(done) { - check_sort0(flow, data_in, data_out, done); - }); - it('should sort payload (payload, number, descending)', function(done) { - check_sort1(flow, data_in, data_out, done); + it('should sort message group/prop (not number, ascending)', function(done) { + check_sort1B(flow, data_in, data_out, done); }); })(); (function() { + var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]}, + {id:"n2", type:"helper"}]; + var data_in = [ "200", "4", "30", "1000" ]; + var data_out = [ "4", "30", "200", "1000" ]; + it('should sort payload (elem, not number, descending)', function(done) { + check_sort0A(flow, data_in, data_out, done); + }); + it('should sort msg prop (elem, not number, descending)', function(done) { + check_sort0B(flow, data_in, data_out, done); + }); + it('should sort message group/payload (not number, descending)', function(done) { + check_sort1A(flow, data_in, data_out, done); + }); + it('should sort message group/prop (not number, descending)', function(done) { + check_sort1B(flow, data_in, data_out, done); + }); + })(); + + (function() { + var flow = [{id:"n1", type:"sort", order:"ascending", as_num:true, wires:[["n2"]]}, + {id:"n2", type:"helper"}]; + var data_in = [ "200", "4", "30", "1000" ]; + var data_out = [ "4", "30", "200", "1000" ]; + it('should sort payload (elem, number, ascending)', function(done) { + check_sort0A(flow, data_in, data_out, done); + }); + it('should sort msg prop (elem, number, ascending)', function(done) { + check_sort0B(flow, data_in, data_out, done); + }); + it('should sort message group/payload (number, ascending)', function(done) { + check_sort1A(flow, data_in, data_out, done); + }); + it('should sort message group/prop (number, ascending)', function(done) { + check_sort1B(flow, data_in, data_out, done); + }); + })(); + + (function() { + var flow = [{id:"n1", type:"sort", order:"descending", as_num:true, wires:[["n2"]]}, + {id:"n2", type:"helper"}]; + var data_in = [ "200", "4", "30", "1000" ]; + var data_out = [ "1000", "200", "30", "4" ]; + it('should sort payload (elem, number, descending)', function(done) { + check_sort0A(flow, data_in, data_out, done); + }); + it('should sort msg prop (elem, number, descending)', function(done) { + check_sort0B(flow, data_in, data_out, done); + }); + it('should sort message group/payload (number, descending)', function(done) { + check_sort1A(flow, data_in, data_out, done); + }); + it('should sort message group/prop (number, descending)', function(done) { + check_sort1B(flow, data_in, data_out, done); + }); + })(); + + (function() { + var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, wires:[["n2"]]}, + {id:"n2", type:"helper"}]; var data_in = [ "C200", "A4", "B30", "D1000" ]; var data_out = [ "D1000", "C200", "B30", "A4" ]; - it('should sort message group (exp, not number, ascending)', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$substring(payload, 1)", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - check_sort0(flow, data_in, data_out, done); - }); it('should sort payload (exp, not number, ascending)', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$substring($, 1)", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - check_sort1(flow, data_in, data_out, done); + check_sort0C(flow, "$substring($,1)", data_in, data_out, done); + }); + it('should sort message group (exp, not number, ascending)', function(done) { + check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done); }); })(); + return; + (function() { + var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]}, + {id:"n2", type:"helper"}]; var data_in = [ "C200", "A4", "B30", "D1000" ]; var data_out = [ "A4", "B30", "C200", "D1000" ]; it('should sort message group (exp, not number, descending)', function(done) { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"exp", key:"$substring(payload, 1)", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - check_sort0(flow, data_in, data_out, done); + check_sort0C(flow, "$substring($,1)", data_in, data_out, done); }); it('should sort payload (exp, not number, descending)', function(done) { - var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, keyType:"exp", key:"$substring($, 1)", wires:[["n2"]]}, - {id:"n2", type:"helper"}]; - check_sort1(flow, data_in, data_out, done); + check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done); }); })(); it('should handle JSONata script error', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"exp", key:"$unknown()", wires:[["n2"]]}, + var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"$unknown()", seqKeyType:"jsonata", wires:[["n2"]]}, {id:"n2", type:"helper"}]; helper.load(sortNode, flow, function() { var n1 = helper.getNode("n1"); @@ -183,12 +247,14 @@ describe('SORT node', function() { }); }); + return; + it('should handle too many pending messages', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]}, + var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]}, {id:"n2", type:"helper"}]; helper.load(sortNode, flow, function() { var n1 = helper.getNode("n1"); - RED.settings.sortMaxKeptMsgsCount = 2; + RED.settings.maxKeptMsgsCount = 2; setTimeout(function() { var logEvents = helper.log().args.filter(function (evt) { return evt[0].type == "sort"; @@ -208,7 +274,7 @@ describe('SORT node', function() { }); it('should clear pending messages on close', function(done) { - var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, keyType:"payload", wires:[["n2"]]}, + var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]}, {id:"n2", type:"helper"}]; helper.load(sortNode, flow, function() { var n1 = helper.getNode("n1");