1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge branch 'pr_2990' into dev

This commit is contained in:
Nick O'Leary 2021-06-08 10:56:57 +01:00
commit 4edea59ab1
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
6 changed files with 207 additions and 88 deletions

View File

@ -18,7 +18,7 @@
/**
* options:
* - addButton : boolean|string - text for add label, default 'add'
* - buttons : array - list of custom buttons (objects with fields 'label', 'icon', 'title', 'click')
* - buttons : array - list of custom buttons (objects with fields 'id', 'label', 'icon', 'title', 'click')
* - height : number|'auto'
* - resize : function - called when list as a whole is resized
* - resizeItem : function(item) - called to resize individual item
@ -94,7 +94,7 @@
}
buttons.forEach(function(button) {
var element = $('<a href="#" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></a>')
var element = $('<button type="button" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></button>')
.appendTo(that.topContainer)
.on("click", function(evt) {
evt.preventDefault();
@ -103,6 +103,9 @@
}
});
if (button.id) {
element.attr("id", button.id);
}
if (button.title) {
element.attr("title", button.title);
}

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.tray = (function() {
RED.tray = (function() {
var stack = [];
var editorStack;
@ -288,9 +288,9 @@ RED.tray = (function() {
right: -(tray.tray.width()+10)+"px"
});
setTimeout(function() {
if (tray.options.close) {
tray.options.close();
}
try {
if (tray.options.close) { tray.options.close(); }
} catch (ex) { }
tray.tray.remove();
if (stack.length > 0) {
var oldTray = stack[stack.length-1];

View File

@ -162,7 +162,62 @@
height += 16;
$("#node-input-property-container").editableList('height',height);
}
/** Retrieve editableList items (refactored for re-use in the form inject button)*/
function getProps(el, legacy) {
var result = {
props: []
}
el.each(function(i) {
var prop = $(this);
var p = {
p:prop.find(".node-input-prop-property-name").typedInput('value')
};
if (p.p) {
p.v = prop.find(".node-input-prop-property-value").typedInput('value');
p.vt = prop.find(".node-input-prop-property-value").typedInput('type');
if(legacy) {
if (p.p === "payload") { // save payload to old "legacy" property
result.payloadType = p.vt;
result.payload = p.v;
delete p.v;
delete p.vt;
} else if (p.p === "topic" && p.vt === "str") {
result.topic = p.v;
delete p.v;
}
}
result.props.push(p);
}
});
return result;
}
/** Perform inject, optionally sending a custom msg (refactored for re-use in the form inject button)*/
function doInject(node, customMsg) {
var label = node._def.label.call(node);
if (label.length > 30) {
label = label.substring(0, 50) + "...";
}
label = label.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
$.ajax({
url: "inject/" + node.id,
type: "POST",
data: customMsg,
success: function (resp) {
RED.notify(node._("inject.success", { label: label }), { type: "success", id: "inject", timeout: 2000 });
},
error: function (jqXHR, textStatus, errorThrown) {
if (jqXHR.status == 404) {
RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.not-deployed") }), "error");
} else if (jqXHR.status == 500) {
RED.notify(node._("common.notification.error", { message: node._("inject.errors.failed") }), "error");
} else if (jqXHR.status == 0) {
RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.no-response") }), "error");
} else {
RED.notify(node._("common.notification.error", { message: node._("common.notification.errors.unexpected", { status: jqXHR.status, message: textStatus }) }), "error");
}
}
});
}
RED.nodes.registerType('inject',{  
category: 'common',
color:"#a6bbcf",
@ -283,14 +338,15 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.payloadType == null) {
if (this.payload == "") {
this.payloadType = "date";
var node = this;
if (node.payloadType == null) {
if (node.payload == "") {
node.payloadType = "date";
} else {
this.payloadType = "str";
node.payloadType = "str";
}
} else if (this.payloadType === 'string' || this.payloadType === 'none') {
this.payloadType = "str";
} else if (node.payloadType === 'string' || node.payloadType === 'none') {
node.payloadType = "str";
}
$("#inject-time-type-select").on("change", function() {
@ -345,17 +401,17 @@
});
var repeattype = "none";
if (this.repeat != "" && this.repeat != 0) {
if (node.repeat != "" && node.repeat != 0) {
repeattype = "interval";
var r = "s";
var c = this.repeat;
if (this.repeat % 60 === 0) { r = "m"; c = c/60; }
if (this.repeat % 1440 === 0) { r = "h"; c = c/60; }
var c = node.repeat;
if (node.repeat % 60 === 0) { r = "m"; c = c/60; }
if (node.repeat % 1440 === 0) { r = "h"; c = c/60; }
$("#inject-time-interval-count").val(c);
$("#inject-time-interval-units").val(r);
$("#inject-time-interval-days").prop("disabled","disabled");
} else if (this.crontab) {
var cronparts = this.crontab.split(" ");
} else if (node.crontab) {
var cronparts = node.crontab.split(" ");
var days = cronparts[4];
if (!isNaN(cronparts[0]) && !isNaN(cronparts[1])) {
repeattype = "time";
@ -431,7 +487,24 @@
/* */
$('#node-input-property-container').css('min-height','120px').css('min-width','450px').editableList({
var eList = $('#node-input-property-container').css('min-height','120px').css('min-width','450px');
eList.editableList({
buttons: [
{
id: "node-inject-test-inject-button",
label: node._("inject.injectNow"),
click: function(e) {
var items = eList.editableList('items');
var result = getProps(items);
var m = {__user_inject_props__: []};
if (result && result.props && result.props.length) {
m.__user_inject_props__ = result.props;
}
doInject(node, m);
}
}
],
addItem: function(container,i,opt) {
var prop = opt;
if (!prop.hasOwnProperty('p')) {
@ -465,33 +538,38 @@
removable: true,
sortable: true
});
$('#node-inject-test-inject-button').css("float", "right").css("margin-right", "unset");
if (!this.props) {
if (RED.nodes.subflow(node.z)) {
$('#node-inject-test-inject-button').attr("disabled",true);
}
if (!node.props) {
var payload = {
p:'payload',
v: this.payload ? this.payload : '',
vt:this.payloadType ? this.payloadType : 'date'
v: node.payload ? node.payload : '',
vt:node.payloadType ? node.payloadType : 'date'
};
var topic = {
p:'topic',
v: this.topic ? this.topic : '',
v: node.topic ? node.topic : '',
vt:'string'
}
this.props = [payload,topic];
node.props = [payload,topic];
}
for (var i=0; i<this.props.length; i++) {
var prop = this.props[i];
for (var i=0; i<node.props.length; i++) {
var prop = node.props[i];
var newProp = { p: prop.p, v: prop.v, vt: prop.vt };
if (newProp.v === undefined) {
if (prop.p === 'payload') {
newProp.v = this.payload ? this.payload : '';
newProp.vt = this.payloadType ? this.payloadType : 'date';
newProp.v = node.payload ? node.payload : '';
newProp.vt = node.payloadType ? node.payloadType : 'date';
} else if (prop.p === 'topic' && prop.vt === "str") {
newProp.v = this.topic ? this.topic : '';
newProp.v = node.topic ? node.topic : '';
}
}
$("#node-input-property-container").editableList('addItem',newProp);
eList.editableList('addItem',newProp);
}
$("#inject-time-type-select").trigger("change");
@ -589,67 +667,26 @@
$("#node-input-repeat").val(repeat);
$("#node-input-crontab").val(crontab);
/* Gather the injected properties of the msg object */
var props = $("#node-input-property-container").editableList('items');
var node = this;
node.props= [];
delete node.payloadType;
delete node.payload;
node.topic = "";
props.each(function(i) {
var prop = $(this);
var p = {
p:prop.find(".node-input-prop-property-name").typedInput('value')
};
if (p.p) {
p.v = prop.find(".node-input-prop-property-value").typedInput('value');
p.vt = prop.find(".node-input-prop-property-value").typedInput('type');
if (p.p === "payload") { // save payload to old "legacy" property
node.payloadType = p.vt;
node.payload = p.v;
delete p.v;
delete p.vt;
} else if (p.p === "topic" && p.vt === "str") {
node.topic = p.v;
delete p.v;
}
node.props.push(p);
}
});
/* Gather the properties */
var items = $("#node-input-property-container").editableList('items');
delete this.payloadType;
delete this.payload;
this.topic = "";
var result = getProps(items, true);
this.props = result.props;
if(result.payloadType) { this.payloadType = result.payloadType; };
if(result.payload) { this.payload = result.payload; };
if(result.topic) { this.topic = result.topic; };
},
button: {
enabled: function() {
return !this.changed
},
onclick: function() {
onclick: function () {
if (this.changed) {
return RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.undeployedChanges")}),"warning");
return RED.notify(RED._("notification.warning", { message: RED._("notification.warnings.undeployedChanges") }), "warning");
}
var label = this._def.label.call(this);
if (label.length > 30) {
label = label.substring(0,50)+"...";
}
label = label.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
var node = this;
$.ajax({
url: "inject/"+this.id,
type:"POST",
success: function(resp) {
RED.notify(node._("inject.success",{label:label}),{type:"success",id:"inject", timeout: 2000});
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status == 404) {
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.not-deployed")}),"error");
} else if (jqXHR.status == 500) {
RED.notify(node._("common.notification.error",{message:node._("inject.errors.failed")}),"error");
} else if (jqXHR.status == 0) {
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.no-response")}),"error");
} else {
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:jqXHR.status,message:textStatus})}),"error");
}
}
});
doInject(this);
}
},
oneditresize: resizeDialog

View File

@ -100,8 +100,12 @@ module.exports = function(RED) {
this.on("input", function(msg, send, done) {
var errors = [];
this.props.forEach(p => {
var props = this.props;
if(msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
props = msg.__user_inject_props__;
}
delete msg.__user_inject_props__;
props.forEach(p => {
var property = p.p;
var value = p.v ? p.v : '';
var valueType = p.vt ? p.vt : 'str';
@ -156,7 +160,11 @@ module.exports = function(RED) {
var node = RED.nodes.getNode(req.params.id);
if (node != null) {
try {
node.receive();
if (req.body && req.body.__user_inject_props__) {
node.receive(req.body);
} else {
node.receive();
}
res.sendStatus(200);
} catch(err) {
res.sendStatus(500);

View File

@ -32,6 +32,7 @@
},
"inject": {
"inject": "inject",
"injectNow": "inject now",
"repeat": "repeat = __repeat__",
"crontab": "crontab = __crontab__",
"stopped": "stopped",

View File

@ -510,6 +510,36 @@ describe('inject node', function() {
});
});
it('should inject custom properties in message', function (done) {
//n1: inject node with { topic:"static", payload:"static", bool1:true, str1:"1" }
var flow = [{id: "n1", type: "inject", props: [{p:"payload", v:"static", vt:"str"}, {p:"topic", v:"static", vt:"str"}, {p:"bool1", v:"true", vt:"bool"}, {p:"str1", v:"1", vt:"str"}], wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
helper.load(injectNode, flow, function () {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function (msg) {
try {
msg.should.not.have.property("payload"); //payload removed
msg.should.have.property("topic", "t_override"); //changed value to t_override
msg.should.have.property("str1", 1);//changed type from str to num
msg.should.have.property("num1", 1);//new prop
msg.should.have.property("bool1", false);//changed value to false
done();
} catch (err) {
done(err);
}
});
n1.receive({ __user_inject_props__: [
{p:"topic", v:"t_override", vt:"str"}, //change value to t_override
{p:"str1", v:"1", vt:"num"}, //change type
{p:"num1", v:"1", vt:"num"}, //new prop
{p:"bool1", v:"false", vt:"bool"}, //change value to false
]});
});
});
it('should inject multiple properties using legacy props if needed', function (done) {
var flow = [{id: "n1", type: "inject", payload:"123", payloadType:"num", topic:"foo", props: [{p:"topic", vt:"str"}, {p:"payload"}], wires: [["n2"]], z: "flow"},
{id: "n2", type: "helper"}];
@ -592,6 +622,46 @@ describe('inject node', function() {
});
});
it('should inject custom properties in posted message', function(done) {
var flow = [{id:"n1", type:"inject", payloadType:"str", topic: "t4",payload:"hello", wires:[["n4"]] },
{ id:"n4", type:"helper"}];
helper.load(injectNode, flow, function() {
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.not.have.property("payload"); //payload removed
msg.should.have.property("topic", "t_override"); //changed value to t_override
msg.should.have.property("str1", "1"); //injected prop
msg.should.have.property("num1", 1); //injected prop
msg.should.have.property("bool1", true); //injected prop
helper.clearFlows().then(function() {
done();
});
});
try {
helper.request()
.post('/inject/n1')
.send({ __user_inject_props__: [
{p:"topic", v:"t_override", vt:"str"}, //change value to t_override
{p:"str1", v:"1", vt:"str"}, //new prop
{p:"num1", v:"1", vt:"num"}, //new prop
{p:"bool1", v:"true", vt:"bool"}, //new prop
]})
.expect(200).end(function(err) {
if (err) {
console.log(err);
return helper.clearFlows()
.then(function () {
done(err);
});
}
});
} catch(err) {
done(err);
}
});
});
it('should fail for invalid node', function(done) {
helper.request().post('/inject/invalid').expect(404).end(done);
});