From ea8c6d5ccef1adb3440878baba373f1e31c6a6a9 Mon Sep 17 00:00:00 2001 From: Antoine Aflalo Date: Fri, 16 Sep 2016 09:27:14 -0400 Subject: [PATCH] Add number of units to the delay node (rate) (#994) * Add possibility to set the value for the rate unit Backward compatible, if the new nbRateUnits is not set, default to 1. This way we can delay messages to 1 msg per X seconds/minutes/hours days instead of always 1. Useful when interacting with API that have a uncommon rate limiting like 1req per 2 seconds. * Fix existing testing for delay * Add new test for the nbRateUnits * Fix label for timed and topic for delay node * Schrink width of Units delay rate * pluralisation of labels * Dynamic pluralisation respecting i18n * Remove debug data left --- nodes/core/core/89-delay.html | 51 ++++++++++++++++++++++---- nodes/core/core/89-delay.js | 2 + nodes/core/locales/en-US/messages.json | 20 +++++++++- test/nodes/core/core/89-delay_spec.js | 38 ++++++++++++------- 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/nodes/core/core/89-delay.html b/nodes/core/core/89-delay.html index 937727c76..6c17da49c 100644 --- a/nodes/core/core/89-delay.html +++ b/nodes/core/core/89-delay.html @@ -42,11 +42,12 @@ - +
@@ -98,6 +99,7 @@ timeout: {value:"5", required:true, validate:RED.validators.number()}, timeoutUnits: {value:"seconds"}, rate: {value:"1", required:true, validate:RED.validators.number()}, + nbRateUnits: {value:"1", required:true, validate:RED.validators.number()}, rateUnits: {value: "second"}, randomFirst: {value:"1", required:true, validate:RED.validators.number()}, randomLast: {value:"5", required:true, validate:RED.validators.number()}, @@ -113,16 +115,22 @@ if (this.timeoutUnits == "milliseconds") { units = "ms"; } return this.name||this._("delay.label.delay")+" "+this.timeout+" "+units; } else if (this.pauseType == "rate") { - var units = this.rateUnits ? this.rateUnits.charAt(0) : "s"; + var units = this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits : '') + this.rateUnits.charAt(0) : "s"; return this.name||this._("delay.label.limit")+" "+this.rate+" msg/"+units; } else if (this.pauseType == "random") { return this.name || this._("delay.label.random"); } else if (this.pauseType == "timed") { - return this.name || this.rate+" "+this._("delay.label.timed")+" "+this.rateUnits; + var units = ''; + if (this.nbRateUnits > 1) { + units = this.nbRateUnits + ' ' + this._("delay.label.units." + this.rateUnits + ".plural"); + } else { + units = this._("delay.label.units." + this.rateUnits + ".singular"); + } + return this.name || this.rate + " " + this._("delay.label.timed") + ' ' + units; } else { - var units = this.rateUnits ? this.rateUnits.charAt(0) : "s"; + var units = this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits : '') + this.rateUnits.charAt(0) : "s"; return this.name || this._("delay.label.queue")+" "+this.rate+" msg/"+units; } }, @@ -130,12 +138,39 @@ return this.name?"node_label_italic":""; }, oneditprepare: function() { + var node = this; $( "#node-input-timeout" ).spinner({min:1}); $( "#node-input-rate" ).spinner({min:1}); + $( "#node-input-nbRateUnits" ).spinner({min:1}); $( "#node-input-randomFirst" ).spinner({min:0}); $( "#node-input-randomLast" ).spinner({min:1}); + $('.ui-spinner-button').click(function() { + $(this).siblings('input').change(); + }); + + $( "#node-input-nbRateUnits" ).on('change keyup', function() { + var $this = $(this); + var val = parseInt($this.val()); + var type = "singular"; + if(val > 1) { + type = "plural"; + } + if($this.attr("data-type") == type) { + return; + } + $this.attr("data-type", type); + $("#node-input-rateUnits option").each(function () { + var $option = $(this); + var key = "delay.label.units." + $option.val() + "." + type; + $option.attr('data-i18n', 'node-red:' + key); + $option.html(node._(key)); + }) + }); + + + if (this.pauseType == "delay") { $("#delay-details").show(); $("#rate-details").hide(); diff --git a/nodes/core/core/89-delay.js b/nodes/core/core/89-delay.js index e153e18f9..dfaba3764 100644 --- a/nodes/core/core/89-delay.js +++ b/nodes/core/core/89-delay.js @@ -51,6 +51,8 @@ module.exports = function(RED) { this.rate = 1000/n.rate; } + this.rate *= (n.nbRateUnits > 0 ? n.nbRateUnits : 1); + if (n.randomUnits === "milliseconds") { this.randomFirst = n.randomFirst * 1; this.randomLast = n.randomLast * 1; diff --git a/nodes/core/locales/en-US/messages.json b/nodes/core/locales/en-US/messages.json index 8f324e94f..35473c275 100644 --- a/nodes/core/locales/en-US/messages.json +++ b/nodes/core/locales/en-US/messages.json @@ -200,7 +200,25 @@ "limit": "limit", "random": "random", "queue": "queue", - "timed": "releases per" + "timed": "releases per", + "units" : { + "second": { + "plural" : "Seconds", + "singular": "Second" + }, + "minute": { + "plural" : "Minutes", + "singular": "Minute" + }, + "hour": { + "plural" : "Hours", + "singular": "Hour" + }, + "day": { + "plural" : "Days", + "singular": "Day" + } + } }, "error": { "buffer": "buffer exceeded 1000 messages", diff --git a/test/nodes/core/core/89-delay_spec.js b/test/nodes/core/core/89-delay_spec.js index f3b4d9303..1b6d80c16 100644 --- a/test/nodes/core/core/89-delay_spec.js +++ b/test/nodes/core/core/89-delay_spec.js @@ -40,7 +40,7 @@ describe('delay Node', function() { }); it('should be loaded', function(done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"day","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; + var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"day","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; helper.load(delayNode, flow, function() { var delayNode1 = helper.getNode("delayNode1"); delayNode1.should.have.property('name', 'delayNode'); @@ -50,7 +50,7 @@ describe('delay Node', function() { }); it('should be able to set rate to hour', function(done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"hour","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; + var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"hour","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; helper.load(delayNode, flow, function() { var delayNode1 = helper.getNode("delayNode1"); delayNode1.should.have.property('name', 'delayNode'); @@ -60,7 +60,7 @@ describe('delay Node', function() { }); it('should be able to set rate to minute', function(done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; + var flow = [{"id":"delayNode1","type":"delay", "nbRateUnits":"1", "name":"delayNode","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[[]]}]; helper.load(delayNode, flow, function() { var delayNode1 = helper.getNode("delayNode1"); delayNode1.should.have.property('name', 'delayNode'); @@ -173,10 +173,11 @@ describe('delay Node', function() { /** * Runs a rate limit test - only testing seconds! * @param aLimit - the message limit count + * @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds * @param runtimeInMillis - when to terminate run and count messages received */ - function genericRateLimitSECONDSTest(aLimit, runtimeInMillis, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, + function genericRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, done) { + var flow = [{"id":"delayNode1","type":"delay","nbRateUnits":nbUnit,"name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, {id:"helperNode1", type:"helper", wires:[]}]; helper.load(delayNode, flow, function() { var delayNode1 = helper.getNode("delayNode1"); @@ -223,21 +224,27 @@ describe('delay Node', function() { } it('limits the message rate to 1 per second', function(done) { - genericRateLimitSECONDSTest(1, 1500, done); + genericRateLimitSECONDSTest(1, 1, 1500, done); }); - it('limits the message rate to 2 per second, 2 seconds', function(done) { + it('limits the message rate to 1 per 2 seconds', function(done) { this.timeout(6000); - genericRateLimitSECONDSTest(2, 2100, done); + genericRateLimitSECONDSTest(1, 2, 3000, done); + }); + + it('limits the message rate to 2 per seconds, 2 seconds', function(done) { + this.timeout(6000); + genericRateLimitSECONDSTest(2, 1, 2100, done); }); /** * Runs a rate limit test with drop support - only testing seconds! * @param aLimit - the message limit count + * @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds * @param runtimeInMillis - when to terminate run and count messages received */ - function dropRateLimitSECONDSTest(aLimit, runtimeInMillis, done) { - var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"wires":[["helperNode1"]]}, + function dropRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, done) { + var flow = [{"id":"delayNode1","type":"delay","name":"delayNode","pauseType":"rate","timeout":5,"nbRateUnits":nbUnit,"timeoutUnits":"seconds","rate":aLimit,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"wires":[["helperNode1"]]}, {id:"helperNode1", type:"helper", wires:[]}]; helper.load(delayNode, flow, function() { var delayNode1 = helper.getNode("delayNode1"); @@ -298,12 +305,17 @@ describe('delay Node', function() { it('limits the message rate to 1 per second, 4 seconds, with drop', function(done) { this.timeout(6000); - dropRateLimitSECONDSTest(1, 4000, done); + dropRateLimitSECONDSTest(1, 1, 4000, done); + }); + + it('limits the message rate to 1 per 2 seconds, 4 seconds, with drop', function(done) { + this.timeout(6000); + dropRateLimitSECONDSTest(1, 2, 4500, done); }); it('limits the message rate to 2 per second, 5 seconds, with drop', function(done) { this.timeout(6000); - dropRateLimitSECONDSTest(2, 5000, done); + dropRateLimitSECONDSTest(2, 1, 5000, done); }); /** @@ -436,7 +448,7 @@ describe('delay Node', function() { it('handles delay queue', function(done) { this.timeout(2000); - var flow = [{id:"delayNode1", type :"delay","name":"delayNode","pauseType":"queue","timeout":1,"timeoutUnits":"seconds","rate":4,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, + var flow = [{id:"delayNode1", type :"delay","name":"delayNode","nbRateUnits":"1","pauseType":"queue","timeout":1,"timeoutUnits":"seconds","rate":4,"rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"wires":[["helperNode1"]]}, {id:"helperNode1", type:"helper", wires:[]}]; helper.load(delayNode, flow, function() { var delayNode1 = helper.getNode("delayNode1");