let split node specify property to split on

and let join auto join the correct property
or manually the specified one.
This commit is contained in:
Dave Conway-Jones 2023-10-17 21:12:13 +01:00
parent eb940d6d57
commit 9fd929ac1e
No known key found for this signature in database
GPG Key ID: 1DDB0E91A28C2643
4 changed files with 161 additions and 40 deletions

View File

@ -15,7 +15,11 @@
--> -->
<script type="text/html" data-template-name="split"> <script type="text/html" data-template-name="split">
<div class="form-row"><span data-i18n="[html]split.intro"></span></div> <!-- <div class="form-row"><span data-i18n="[html]split.intro"></span></div> -->
<div class="form-row">
<label for="node-input-property"><i class="fa fa-forward"></i> <span data-i18n="split.split"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row"><span data-i18n="[html]split.strBuff"></span></div> <div class="form-row"><span data-i18n="[html]split.strBuff"></span></div>
<div class="form-row"> <div class="form-row">
<label for="node-input-splt" style="padding-left:10px; margin-right:-10px;" data-i18n="split.splitUsing"></label> <label for="node-input-splt" style="padding-left:10px; margin-right:-10px;" data-i18n="split.splitUsing"></label>
@ -39,10 +43,9 @@
<label for="node-input-addname-cb" style="width:auto;" data-i18n="split.addname"></label> <label for="node-input-addname-cb" style="width:auto;" data-i18n="split.addname"></label>
<input type="text" id="node-input-addname" style="width:70%"> <input type="text" id="node-input-addname" style="width:70%">
</div> </div>
<hr/>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
</div> </div>
</script> </script>
@ -57,7 +60,8 @@
arraySplt: {value:1}, arraySplt: {value:1},
arraySpltType: {value:"len"}, arraySpltType: {value:"len"},
stream: {value:false}, stream: {value:false},
addname: {value:""} addname: {value:""},
property: {value:"payload",required:true}
}, },
inputs:1, inputs:1,
outputs:1, outputs:1,
@ -69,6 +73,10 @@
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";
}, },
oneditprepare: function() { oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
$("#node-input-splt").typedInput({ $("#node-input-splt").typedInput({
default: 'str', default: 'str',
typeField: $("#node-input-spltType"), typeField: $("#node-input-spltType"),

View File

@ -19,13 +19,13 @@ module.exports = function(RED) {
function sendArray(node,msg,array,send) { function sendArray(node,msg,array,send) {
for (var i = 0; i < array.length-1; i++) { for (var i = 0; i < array.length-1; i++) {
msg.payload = array[i]; RED.util.setMessageProperty(msg,node.property,array[i]);
msg.parts.index = node.c++; msg.parts.index = node.c++;
if (node.stream !== true) { msg.parts.count = array.length; } if (node.stream !== true) { msg.parts.count = array.length; }
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
} }
if (node.stream !== true) { if (node.stream !== true) {
msg.payload = array[i]; RED.util.setMessageProperty(msg,node.property,array[i]);
msg.parts.index = node.c++; msg.parts.index = node.c++;
msg.parts.count = array.length; msg.parts.count = array.length;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
@ -40,10 +40,12 @@ module.exports = function(RED) {
node.stream = n.stream; node.stream = n.stream;
node.spltType = n.spltType || "str"; node.spltType = n.spltType || "str";
node.addname = n.addname || ""; node.addname = n.addname || "";
node.property = n.property||"payload";
try { try {
if (node.spltType === "str") { if (node.spltType === "str") {
this.splt = (n.splt || "\\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0"); this.splt = (n.splt || "\\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0");
} else if (node.spltType === "bin") { }
else if (node.spltType === "bin") {
var spltArray = JSON.parse(n.splt); var spltArray = JSON.parse(n.splt);
if (Array.isArray(spltArray)) { if (Array.isArray(spltArray)) {
this.splt = Buffer.from(spltArray); this.splt = Buffer.from(spltArray);
@ -51,7 +53,8 @@ module.exports = function(RED) {
throw new Error("not an array"); throw new Error("not an array");
} }
this.spltBuffer = spltArray; this.spltBuffer = spltArray;
} else if (node.spltType === "len") { }
else if (node.spltType === "len") {
this.splt = parseInt(n.splt); this.splt = parseInt(n.splt);
if (isNaN(this.splt) || this.splt < 1) { if (isNaN(this.splt) || this.splt < 1) {
throw new Error("invalid split length: "+n.splt); throw new Error("invalid split length: "+n.splt);
@ -69,18 +72,23 @@ module.exports = function(RED) {
node.buffer = Buffer.from([]); node.buffer = Buffer.from([]);
node.pendingDones = []; node.pendingDones = [];
this.on("input", function(msg, send, done) { this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("payload")) { var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
RED.util.setMessageProperty(msg,node.property,undefined);
if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack
else { msg.parts = {}; } else { msg.parts = {}; }
msg.parts.id = RED.util.generateId(); // generate a random id msg.parts.id = RED.util.generateId(); // generate a random id
if (node.property !== "payload") {
msg.parts.property = node.property;
}
delete msg._msgid; delete msg._msgid;
if (typeof msg.payload === "string") { // Split String into array if (typeof value === "string") { // Split String into array
msg.payload = (node.remainder || "") + msg.payload; value = (node.remainder || "") + value;
msg.parts.type = "string"; msg.parts.type = "string";
if (node.spltType === "len") { if (node.spltType === "len") {
msg.parts.ch = ""; msg.parts.ch = "";
msg.parts.len = node.splt; msg.parts.len = node.splt;
var count = msg.payload.length/node.splt; var count = value.length/node.splt;
if (Math.floor(count) !== count) { if (Math.floor(count) !== count) {
count = Math.ceil(count); count = Math.ceil(count);
} }
@ -89,9 +97,9 @@ module.exports = function(RED) {
node.c = 0; node.c = 0;
} }
var pos = 0; var pos = 0;
var data = msg.payload; var data = value;
for (var i=0; i<count-1; i++) { for (var i=0; i<count-1; i++) {
msg.payload = data.substring(pos,pos+node.splt); RED.util.setMessageProperty(msg,node.property,data.substring(pos,pos+node.splt));
msg.parts.index = node.c++; msg.parts.index = node.c++;
pos += node.splt; pos += node.splt;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
@ -102,7 +110,7 @@ module.exports = function(RED) {
} }
node.remainder = data.substring(pos); node.remainder = data.substring(pos);
if ((node.stream !== true) || (node.remainder.length === node.splt)) { if ((node.stream !== true) || (node.remainder.length === node.splt)) {
msg.payload = node.remainder; RED.util.setMessageProperty(msg,node.property,node.remainder);
msg.parts.index = node.c++; msg.parts.index = node.c++;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d()); node.pendingDones.forEach(d => d());
@ -119,47 +127,48 @@ module.exports = function(RED) {
if (!node.spltBufferString) { if (!node.spltBufferString) {
node.spltBufferString = node.splt.toString(); node.spltBufferString = node.splt.toString();
} }
a = msg.payload.split(node.spltBufferString); a = value.split(node.spltBufferString);
msg.parts.ch = node.spltBuffer; // pass the split char to other end for rejoin msg.parts.ch = node.spltBuffer; // pass the split char to other end for rejoin
} else if (node.spltType === "str") { } else if (node.spltType === "str") {
a = msg.payload.split(node.splt); a = value.split(node.splt);
msg.parts.ch = node.splt; // pass the split char to other end for rejoin msg.parts.ch = node.splt; // pass the split char to other end for rejoin
} }
sendArray(node,msg,a,send); sendArray(node,msg,a,send);
done(); done();
} }
} }
else if (Array.isArray(msg.payload)) { // then split array into messages else if (Array.isArray(value)) { // then split array into messages
msg.parts.type = "array"; msg.parts.type = "array";
var count = msg.payload.length/node.arraySplt; var count = value.length/node.arraySplt;
if (Math.floor(count) !== count) { if (Math.floor(count) !== count) {
count = Math.ceil(count); count = Math.ceil(count);
} }
msg.parts.count = count; msg.parts.count = count;
var pos = 0; var pos = 0;
var data = msg.payload; var data = value;
msg.parts.len = node.arraySplt; msg.parts.len = node.arraySplt;
for (var i=0; i<count; i++) { for (var i=0; i<count; i++) {
msg.payload = data.slice(pos,pos+node.arraySplt); var m = data.slice(pos,pos+node.arraySplt);
if (node.arraySplt === 1) { if (node.arraySplt === 1) {
msg.payload = msg.payload[0]; m = m[0];
} }
RED.util.setMessageProperty(msg,node.property,m);
msg.parts.index = i; msg.parts.index = i;
pos += node.arraySplt; pos += node.arraySplt;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
} }
done(); done();
} }
else if ((typeof msg.payload === "object") && !Buffer.isBuffer(msg.payload)) { else if ((typeof value === "object") && !Buffer.isBuffer(value)) {
var j = 0; var j = 0;
var l = Object.keys(msg.payload).length; var l = Object.keys(value).length;
var pay = msg.payload; var pay = value;
msg.parts.type = "object"; msg.parts.type = "object";
for (var p in pay) { for (var p in pay) {
if (pay.hasOwnProperty(p)) { if (pay.hasOwnProperty(p)) {
msg.payload = pay[p]; RED.util.setMessageProperty(msg,node.property,pay[p]);
if (node.addname !== "") { if (node.addname !== "") {
msg[node.addname] = p; RED.util.setMessageProperty(msg,node.addname,p);
} }
msg.parts.key = p; msg.parts.key = p;
msg.parts.index = j; msg.parts.index = j;
@ -170,9 +179,9 @@ module.exports = function(RED) {
} }
done(); done();
} }
else if (Buffer.isBuffer(msg.payload)) { else if (Buffer.isBuffer(value)) {
var len = node.buffer.length + msg.payload.length; var len = node.buffer.length + value.length;
var buff = Buffer.concat([node.buffer, msg.payload], len); var buff = Buffer.concat([node.buffer, value], len);
msg.parts.type = "buffer"; msg.parts.type = "buffer";
if (node.spltType === "len") { if (node.spltType === "len") {
var count = buff.length/node.splt; var count = buff.length/node.splt;
@ -186,7 +195,7 @@ module.exports = function(RED) {
var pos = 0; var pos = 0;
msg.parts.len = node.splt; msg.parts.len = node.splt;
for (var i=0; i<count-1; i++) { for (var i=0; i<count-1; i++) {
msg.payload = buff.slice(pos,pos+node.splt); RED.util.setMessageProperty(msg,node.property,buff.slice(pos,pos+node.splt));
msg.parts.index = node.c++; msg.parts.index = node.c++;
pos += node.splt; pos += node.splt;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
@ -197,7 +206,7 @@ module.exports = function(RED) {
} }
node.buffer = buff.slice(pos); node.buffer = buff.slice(pos);
if ((node.stream !== true) || (node.buffer.length === node.splt)) { if ((node.stream !== true) || (node.buffer.length === node.splt)) {
msg.payload = node.buffer; RED.util.setMessageProperty(msg,node.property,node.buffer);
msg.parts.index = node.c++; msg.parts.index = node.c++;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d()); node.pendingDones.forEach(d => d());
@ -230,7 +239,7 @@ module.exports = function(RED) {
var i = 0, p = 0; var i = 0, p = 0;
pos = buff.indexOf(node.splt); pos = buff.indexOf(node.splt);
while (pos > -1) { while (pos > -1) {
msg.payload = buff.slice(p,pos); RED.util.setMessageProperty(msg,node.property,buff.slice(p,pos));
msg.parts.index = node.c++; msg.parts.index = node.c++;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
i++; i++;
@ -242,7 +251,7 @@ module.exports = function(RED) {
node.pendingDones = []; node.pendingDones = [];
} }
if ((node.stream !== true) && (p < buff.length)) { if ((node.stream !== true) && (p < buff.length)) {
msg.payload = buff.slice(p,buff.length); RED.util.setMessageProperty(msg,node.property,buff.slice(p,buff.length));
msg.parts.index = node.c++; msg.parts.index = node.c++;
msg.parts.count = node.c++; msg.parts.count = node.c++;
send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
@ -298,7 +307,6 @@ module.exports = function(RED) {
return exp return exp
} }
function reduceMessageGroup(node,msgInfos,exp,fixup,count,accumulator,done) { function reduceMessageGroup(node,msgInfos,exp,fixup,count,accumulator,done) {
var msgInfo = msgInfos.shift(); var msgInfo = msgInfos.shift();
exp.assign("I", msgInfo.msg.parts.index); exp.assign("I", msgInfo.msg.parts.index);
@ -515,13 +523,13 @@ module.exports = function(RED) {
if (typeof group.joinChar !== 'string') { if (typeof group.joinChar !== 'string') {
groupJoinChar = group.joinChar.toString(); groupJoinChar = group.joinChar.toString();
} }
RED.util.setMessageProperty(group.msg,node.property,group.payload.join(groupJoinChar)); RED.util.setMessageProperty(group.msg,group?.prop||"payload",group.payload.join(groupJoinChar));
} }
else { else {
if (node.propertyType === 'full') { if (node.propertyType === 'full') {
group.msg = RED.util.cloneMessage(group.msg); group.msg = RED.util.cloneMessage(group.msg);
} }
RED.util.setMessageProperty(group.msg,node.property,group.payload); RED.util.setMessageProperty(group.msg,group?.prop||"payload",group.payload);
} }
if (group.msg.hasOwnProperty('parts') && group.msg.parts.hasOwnProperty('parts')) { if (group.msg.hasOwnProperty('parts') && group.msg.parts.hasOwnProperty('parts')) {
group.msg.parts = group.msg.parts.parts; group.msg.parts = group.msg.parts.parts;
@ -589,7 +597,7 @@ module.exports = function(RED) {
} }
if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) { if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) {
// if a blank reset messag erest it all. // if a blank reset message reset it all.
if (msg.hasOwnProperty("reset")) { if (msg.hasOwnProperty("reset")) {
if (inflight && inflight.hasOwnProperty("partId") && inflight[partId].timeout) { if (inflight && inflight.hasOwnProperty("partId") && inflight[partId].timeout) {
clearTimeout(inflight[partId].timeout); clearTimeout(inflight[partId].timeout);
@ -618,6 +626,7 @@ module.exports = function(RED) {
propertyKey = msg.parts.key; propertyKey = msg.parts.key;
arrayLen = msg.parts.len; arrayLen = msg.parts.len;
propertyIndex = msg.parts.index; propertyIndex = msg.parts.index;
property = RED.util.getMessageProperty(msg,msg.parts.property||"payload");
} }
else if (node.mode === 'reduce') { else if (node.mode === 'reduce') {
return processReduceMessageQueue({msg, send, done}); return processReduceMessageQueue({msg, send, done});
@ -719,6 +728,8 @@ module.exports = function(RED) {
completeSend(partId) completeSend(partId)
}, node.timer) }, node.timer)
} }
if (node.mode === "auto") { inflight[partId].prop = msg.parts.property; }
else { inflight[partId].prop = node.property; }
} }
inflight[partId].dones.push(done); inflight[partId].dones.push(done);

View File

@ -1001,7 +1001,7 @@
"tip": "Tip: The filename should be an absolute path, otherwise it will be relative to the working directory of the Node-RED process." "tip": "Tip: The filename should be an absolute path, otherwise it will be relative to the working directory of the Node-RED process."
}, },
"split": { "split": {
"split": "split", "split": "Split",
"intro": "Split <code>msg.payload</code> based on type:", "intro": "Split <code>msg.payload</code> based on type:",
"object": "<b>Object</b>", "object": "<b>Object</b>",
"objectSend": "Send a message for each key/value pair", "objectSend": "Send a message for each key/value pair",

View File

@ -66,6 +66,27 @@ describe('SPLIT node', function() {
}); });
}); });
it('should split an array on a sub-property into multiple messages', function(done) {
var flow = [{id:"sn1", type:"split", property:"foo", wires:[["sn2"]]},
{id:"sn2", type:"helper"}];
helper.load(splitNode, flow, function() {
var sn1 = helper.getNode("sn1");
var sn2 = helper.getNode("sn2");
sn2.on("input", function(msg) {
msg.should.have.property("parts");
msg.parts.should.have.property("count",4);
msg.parts.should.have.property("type","array");
msg.parts.should.have.property("index");
msg.parts.should.have.property("property","foo");
if (msg.parts.index === 0) { msg.foo.should.equal(1); }
if (msg.parts.index === 1) { msg.foo.should.equal(2); }
if (msg.parts.index === 2) { msg.foo.should.equal(3); }
if (msg.parts.index === 3) { msg.foo.should.equal(4); done(); }
});
sn1.receive({foo:[1,2,3,4]});
});
});
it('should split an array into multiple messages of a specified size', function(done) { it('should split an array into multiple messages of a specified size', function(done) {
var flow = [{id:"sn1", type:"split", wires:[["sn2"]], arraySplt:3, arraySpltType:"len"}, var flow = [{id:"sn1", type:"split", wires:[["sn2"]], arraySplt:3, arraySpltType:"len"},
{id:"sn2", type:"helper"}]; {id:"sn2", type:"helper"}];
@ -108,6 +129,31 @@ describe('SPLIT node', function() {
}); });
}); });
it('should split an object sub property into pieces', function(done) {
var flow = [{id:"sn1", type:"split", property:"foo.bar",wires:[["sn2"]]},
{id:"sn2", type:"helper"}];
helper.load(splitNode, flow, function() {
var sn1 = helper.getNode("sn1");
var sn2 = helper.getNode("sn2");
var count = 0;
sn2.on("input", function(msg) {
msg.should.have.property("foo");
msg.foo.should.have.property("bar");
msg.should.have.property("parts");
msg.parts.should.have.property("type","object");
msg.parts.should.have.property("key");
msg.parts.should.have.property("count");
msg.parts.should.have.property("index");
msg.parts.should.have.property("property","foo.bar");
msg.topic.should.equal("foo");
if (msg.parts.index === 0) { msg.foo.bar.should.equal(1); }
if (msg.parts.index === 1) { msg.foo.bar.should.equal("2"); }
if (msg.parts.index === 2) { msg.foo.bar.should.equal(true); done(); }
});
sn1.receive({topic:"foo",foo:{bar:{a:1,b:"2",c:true}}});
});
});
it('should split an object into pieces and overwrite their topics', function(done) { it('should split an object into pieces and overwrite their topics', function(done) {
var flow = [{id:"sn1", type:"split", addname:"topic", wires:[["sn2"]]}, var flow = [{id:"sn1", type:"split", addname:"topic", wires:[["sn2"]]},
{id:"sn2", type:"helper"}]; {id:"sn2", type:"helper"}];
@ -516,6 +562,7 @@ describe('JOIN node', function() {
n1.receive({payload:{a:1}}); n1.receive({payload:{a:1}});
}); });
}); });
it('should join things into an array ignoring msg.parts.index in manual mode', function(done) { it('should join things into an array ignoring msg.parts.index in manual mode', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"}, var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",",mode:"custom"},
{id:"n2", type:"helper"}]; {id:"n2", type:"helper"}];
@ -562,6 +609,32 @@ describe('JOIN node', function() {
}); });
}); });
it('should join things into an array on a sub property in auto mode', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:3, joiner:",", mode:"auto"},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property("foo");
msg.foo.should.have.property("bar");
msg.foo.bar.should.be.an.Array();
msg.foo.bar[0].should.equal("A");
msg.foo.bar[1].should.equal("B");
//msg.payload[2].a.should.equal(1);
done();
}
catch(e) {done(e);}
});
n1.receive({foo:{bar:"A"}, parts:{id:1, type:"array", len:1, index:0, count:4, property:"foo.bar"}});
n1.receive({foo:{bar:"B"}, parts:{id:1, type:"array", len:1, index:1, count:4, property:"foo.bar"}});
n1.receive({foo:{bar:"C"}, parts:{id:1, type:"array", len:1, index:2, count:4, property:"foo.bar"}});
n1.receive({foo:{bar:"D"}, parts:{id:1, type:"array", len:1, index:3, count:4, property:"foo.bar"}});
});
});
it('should join strings into a buffer after a count', function(done) { it('should join strings into a buffer after a count', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:2, build:"buffer", joinerType:"bin", joiner:"", mode:"custom"}, var flow = [{id:"n1", type:"join", wires:[["n2"]], count:2, build:"buffer", joinerType:"bin", joiner:"", mode:"custom"},
{id:"n2", type:"helper"}]; {id:"n2", type:"helper"}];
@ -639,6 +712,35 @@ describe('JOIN node', function() {
}); });
}); });
it('should merge sub property objects', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:5, property:"foo.bar", build:"merged", mode:"custom"},
{id:"n2", type:"helper"}];
helper.load(joinNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
n2.on("input", function(msg) {
try {
msg.should.have.property("foo");
msg.foo.should.have.property("bar");
msg.foo.bar.should.have.property("a",1);
msg.foo.bar.should.have.property("b",2);
msg.foo.bar.should.have.property("c",3);
msg.foo.bar.should.have.property("d",4);
msg.foo.bar.should.have.property("e",5);
done();
}
catch(e) { done(e)}
});
n1.receive({foo:{bar:{a:9}, topic:"f"}});
n1.receive({foo:{bar:{a:1}, topic:"a"}});
n1.receive({foo:{bar:{b:9}, topic:"b"}});
n1.receive({foo:{bar:{b:2}, topic:"b"}});
n1.receive({foo:{bar:{c:3}, topic:"c"}});
n1.receive({foo:{bar:{d:4}, topic:"d"}});
n1.receive({foo:{bar:{e:5}, topic:"e"}});
});
});
it('should merge full msg objects', function(done) { it('should merge full msg objects', function(done) {
var flow = [{id:"n1", type:"join", wires:[["n2"]], count:6, build:"merged", mode:"custom", propertyType:"full", property:""}, var flow = [{id:"n1", type:"join", wires:[["n2"]], count:6, build:"merged", mode:"custom", propertyType:"full", property:""},
{id:"n2", type:"helper"}]; {id:"n2", type:"helper"}];