Skip to content

Commit ea8c6d5

Browse files
Belphemurknolleary
authored andcommitted
Add number of units to the delay node (rate) (node-red#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
1 parent e4c9519 commit ea8c6d5

4 files changed

Lines changed: 89 additions & 22 deletions

File tree

nodes/core/core/89-delay.html

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@
4242
<label for="node-input-rate"><i class="fa fa-clock-o"></i> <span data-i18n="delay.rate"></span></label>
4343
<input type="text" id="node-input-rate" placeholder="1" style="text-align:end; width:30px !important">
4444
<label for="node-input-rateUnits"><span data-i18n="delay.msgper"></span></label>
45-
<select id="node-input-rateUnits" style="width:140px !important">
46-
<option value="second" data-i18n="delay.sec"></option>
47-
<option value="minute" data-i18n="delay.min"></option>
48-
<option value="hour" data-i18n="delay.hour"></option>
49-
<option value="day" data-i18n="delay.day"></option>
45+
<input type="text" id="node-input-nbRateUnits" placeholder="1" style="text-align:end; width:30px !important">
46+
<select id="node-input-rateUnits" style="width:110px !important">
47+
<option value="second" data-i18n="delay.label.units.second.singular"></option>
48+
<option value="minute" data-i18n="delay.label.units.minute.singular"></option>
49+
<option value="hour" data-i18n="delay.label.units.hour.singular"></option>
50+
<option value="day" data-i18n="delay.label.units.day.singular"></option>
5051
</select>
5152
<br/>
5253
<div id="node-input-dr"><input style="margin: 20px 0 20px 100px; width: 30px;" type="checkbox" id="node-input-drop"><label style="width: 250px;" for="node-input-drop"><span data-i18n="delay.dropmsg"></span></label></div>
@@ -98,6 +99,7 @@
9899
timeout: {value:"5", required:true, validate:RED.validators.number()},
99100
timeoutUnits: {value:"seconds"},
100101
rate: {value:"1", required:true, validate:RED.validators.number()},
102+
nbRateUnits: {value:"1", required:true, validate:RED.validators.number()},
101103
rateUnits: {value: "second"},
102104
randomFirst: {value:"1", required:true, validate:RED.validators.number()},
103105
randomLast: {value:"5", required:true, validate:RED.validators.number()},
@@ -113,29 +115,62 @@
113115
if (this.timeoutUnits == "milliseconds") { units = "ms"; }
114116
return this.name||this._("delay.label.delay")+" "+this.timeout+" "+units;
115117
} else if (this.pauseType == "rate") {
116-
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s";
118+
var units = this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits : '') + this.rateUnits.charAt(0) : "s";
117119
return this.name||this._("delay.label.limit")+" "+this.rate+" msg/"+units;
118120
} else if (this.pauseType == "random") {
119121
return this.name || this._("delay.label.random");
120122
}
121123
else if (this.pauseType == "timed") {
122-
return this.name || this.rate+" "+this._("delay.label.timed")+" "+this.rateUnits;
124+
var units = '';
125+
if (this.nbRateUnits > 1) {
126+
units = this.nbRateUnits + ' ' + this._("delay.label.units." + this.rateUnits + ".plural");
127+
} else {
128+
units = this._("delay.label.units." + this.rateUnits + ".singular");
129+
}
130+
return this.name || this.rate + " " + this._("delay.label.timed") + ' ' + units;
123131
}
124132
else {
125-
var units = this.rateUnits ? this.rateUnits.charAt(0) : "s";
133+
var units = this.rateUnits ? (this.nbRateUnits > 1 ? this.nbRateUnits : '') + this.rateUnits.charAt(0) : "s";
126134
return this.name || this._("delay.label.queue")+" "+this.rate+" msg/"+units;
127135
}
128136
},
129137
labelStyle: function() {
130138
return this.name?"node_label_italic":"";
131139
},
132140
oneditprepare: function() {
141+
var node = this;
133142
$( "#node-input-timeout" ).spinner({min:1});
134143
$( "#node-input-rate" ).spinner({min:1});
144+
$( "#node-input-nbRateUnits" ).spinner({min:1});
135145

136146
$( "#node-input-randomFirst" ).spinner({min:0});
137147
$( "#node-input-randomLast" ).spinner({min:1});
138148

149+
$('.ui-spinner-button').click(function() {
150+
$(this).siblings('input').change();
151+
});
152+
153+
$( "#node-input-nbRateUnits" ).on('change keyup', function() {
154+
var $this = $(this);
155+
var val = parseInt($this.val());
156+
var type = "singular";
157+
if(val > 1) {
158+
type = "plural";
159+
}
160+
if($this.attr("data-type") == type) {
161+
return;
162+
}
163+
$this.attr("data-type", type);
164+
$("#node-input-rateUnits option").each(function () {
165+
var $option = $(this);
166+
var key = "delay.label.units." + $option.val() + "." + type;
167+
$option.attr('data-i18n', 'node-red:' + key);
168+
$option.html(node._(key));
169+
})
170+
});
171+
172+
173+
139174
if (this.pauseType == "delay") {
140175
$("#delay-details").show();
141176
$("#rate-details").hide();

nodes/core/core/89-delay.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ module.exports = function(RED) {
5151
this.rate = 1000/n.rate;
5252
}
5353

54+
this.rate *= (n.nbRateUnits > 0 ? n.nbRateUnits : 1);
55+
5456
if (n.randomUnits === "milliseconds") {
5557
this.randomFirst = n.randomFirst * 1;
5658
this.randomLast = n.randomLast * 1;

nodes/core/locales/en-US/messages.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,25 @@
200200
"limit": "limit",
201201
"random": "random",
202202
"queue": "queue",
203-
"timed": "releases per"
203+
"timed": "releases per",
204+
"units" : {
205+
"second": {
206+
"plural" : "Seconds",
207+
"singular": "Second"
208+
},
209+
"minute": {
210+
"plural" : "Minutes",
211+
"singular": "Minute"
212+
},
213+
"hour": {
214+
"plural" : "Hours",
215+
"singular": "Hour"
216+
},
217+
"day": {
218+
"plural" : "Days",
219+
"singular": "Day"
220+
}
221+
}
204222
},
205223
"error": {
206224
"buffer": "buffer exceeded 1000 messages",

test/nodes/core/core/89-delay_spec.js

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('delay Node', function() {
4040
});
4141

4242
it('should be loaded', function(done) {
43-
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":[[]]}];
43+
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":[[]]}];
4444
helper.load(delayNode, flow, function() {
4545
var delayNode1 = helper.getNode("delayNode1");
4646
delayNode1.should.have.property('name', 'delayNode');
@@ -50,7 +50,7 @@ describe('delay Node', function() {
5050
});
5151

5252
it('should be able to set rate to hour', function(done) {
53-
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":[[]]}];
53+
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":[[]]}];
5454
helper.load(delayNode, flow, function() {
5555
var delayNode1 = helper.getNode("delayNode1");
5656
delayNode1.should.have.property('name', 'delayNode');
@@ -60,7 +60,7 @@ describe('delay Node', function() {
6060
});
6161

6262
it('should be able to set rate to minute', function(done) {
63-
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":[[]]}];
63+
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":[[]]}];
6464
helper.load(delayNode, flow, function() {
6565
var delayNode1 = helper.getNode("delayNode1");
6666
delayNode1.should.have.property('name', 'delayNode');
@@ -173,10 +173,11 @@ describe('delay Node', function() {
173173
/**
174174
* Runs a rate limit test - only testing seconds!
175175
* @param aLimit - the message limit count
176+
* @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds
176177
* @param runtimeInMillis - when to terminate run and count messages received
177178
*/
178-
function genericRateLimitSECONDSTest(aLimit, runtimeInMillis, done) {
179-
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"]]},
179+
function genericRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, done) {
180+
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"]]},
180181
{id:"helperNode1", type:"helper", wires:[]}];
181182
helper.load(delayNode, flow, function() {
182183
var delayNode1 = helper.getNode("delayNode1");
@@ -223,21 +224,27 @@ describe('delay Node', function() {
223224
}
224225

225226
it('limits the message rate to 1 per second', function(done) {
226-
genericRateLimitSECONDSTest(1, 1500, done);
227+
genericRateLimitSECONDSTest(1, 1, 1500, done);
227228
});
228229

229-
it('limits the message rate to 2 per second, 2 seconds', function(done) {
230+
it('limits the message rate to 1 per 2 seconds', function(done) {
230231
this.timeout(6000);
231-
genericRateLimitSECONDSTest(2, 2100, done);
232+
genericRateLimitSECONDSTest(1, 2, 3000, done);
233+
});
234+
235+
it('limits the message rate to 2 per seconds, 2 seconds', function(done) {
236+
this.timeout(6000);
237+
genericRateLimitSECONDSTest(2, 1, 2100, done);
232238
});
233239

234240
/**
235241
* Runs a rate limit test with drop support - only testing seconds!
236242
* @param aLimit - the message limit count
243+
* @param nbUnit - the multiple of the unit, aLimit Message for nbUnit Seconds
237244
* @param runtimeInMillis - when to terminate run and count messages received
238245
*/
239-
function dropRateLimitSECONDSTest(aLimit, runtimeInMillis, done) {
240-
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"]]},
246+
function dropRateLimitSECONDSTest(aLimit, nbUnit, runtimeInMillis, done) {
247+
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"]]},
241248
{id:"helperNode1", type:"helper", wires:[]}];
242249
helper.load(delayNode, flow, function() {
243250
var delayNode1 = helper.getNode("delayNode1");
@@ -298,12 +305,17 @@ describe('delay Node', function() {
298305

299306
it('limits the message rate to 1 per second, 4 seconds, with drop', function(done) {
300307
this.timeout(6000);
301-
dropRateLimitSECONDSTest(1, 4000, done);
308+
dropRateLimitSECONDSTest(1, 1, 4000, done);
309+
});
310+
311+
it('limits the message rate to 1 per 2 seconds, 4 seconds, with drop', function(done) {
312+
this.timeout(6000);
313+
dropRateLimitSECONDSTest(1, 2, 4500, done);
302314
});
303315

304316
it('limits the message rate to 2 per second, 5 seconds, with drop', function(done) {
305317
this.timeout(6000);
306-
dropRateLimitSECONDSTest(2, 5000, done);
318+
dropRateLimitSECONDSTest(2, 1, 5000, done);
307319
});
308320

309321
/**
@@ -436,7 +448,7 @@ describe('delay Node', function() {
436448

437449
it('handles delay queue', function(done) {
438450
this.timeout(2000);
439-
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"]]},
451+
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"]]},
440452
{id:"helperNode1", type:"helper", wires:[]}];
441453
helper.load(delayNode, flow, function() {
442454
var delayNode1 = helper.getNode("delayNode1");

0 commit comments

Comments
 (0)