mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #1780 from natcl/json-schema
Add JSON schema validation to JSON node
This commit is contained in:
commit
9bf87697fd
@ -63,7 +63,7 @@ module.exports = function(RED) {
|
|||||||
udpInputPortsInUse[this.port] = server;
|
udpInputPortsInUse[this.port] = server;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.warn(RED._("udp.errors.alreadyused",{port:node.port}));
|
node.log(RED._("udp.errors.alreadyused",{port:node.port}));
|
||||||
server = udpInputPortsInUse[this.port]; // re-use existing
|
server = udpInputPortsInUse[this.port]; // re-use existing
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,8 +172,7 @@ module.exports = function(RED) {
|
|||||||
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
||||||
|
|
||||||
var sock;
|
var sock;
|
||||||
var p = this.port;
|
var p = this.outport || this.port || "0";
|
||||||
if (node.multicast != "false") { p = this.outport||"0"; }
|
|
||||||
if (udpInputPortsInUse[p]) {
|
if (udpInputPortsInUse[p]) {
|
||||||
sock = udpInputPortsInUse[p];
|
sock = udpInputPortsInUse[p];
|
||||||
node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port}));
|
node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port}));
|
||||||
|
@ -697,7 +697,9 @@
|
|||||||
"errors": {
|
"errors": {
|
||||||
"dropped-object": "Ignored non-object payload",
|
"dropped-object": "Ignored non-object payload",
|
||||||
"dropped": "Ignored unsupported payload type",
|
"dropped": "Ignored unsupported payload type",
|
||||||
"dropped-error": "Failed to convert payload"
|
"dropped-error": "Failed to convert payload",
|
||||||
|
"schema-error": "JSON Schema error",
|
||||||
|
"schema-error-compile": "JSON Schema error: failed to compile schema"
|
||||||
},
|
},
|
||||||
"label": {
|
"label": {
|
||||||
"o2j": "Object to JSON options",
|
"o2j": "Object to JSON options",
|
||||||
|
@ -686,10 +686,13 @@ module.exports = function(RED) {
|
|||||||
} else {
|
} else {
|
||||||
if (!isNaN(propertyIndex)) {
|
if (!isNaN(propertyIndex)) {
|
||||||
group.payload[propertyIndex] = property;
|
group.payload[propertyIndex] = property;
|
||||||
|
group.currentCount++;
|
||||||
} else {
|
} else {
|
||||||
group.payload.push(property);
|
if (property !== undefined) {
|
||||||
|
group.payload.push(property);
|
||||||
|
group.currentCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
group.currentCount++;
|
|
||||||
}
|
}
|
||||||
group.msg = Object.assign(group.msg, msg);
|
group.msg = Object.assign(group.msg, msg);
|
||||||
var tcnt = group.targetCount;
|
var tcnt = group.targetCount;
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
<dl class="message-properties">
|
<dl class="message-properties">
|
||||||
<dt>payload<span class="property-type">object | string</span></dt>
|
<dt>payload<span class="property-type">object | string</span></dt>
|
||||||
<dd>A JavaScript object or JSON string.</dd>
|
<dd>A JavaScript object or JSON string.</dd>
|
||||||
|
<dt>schema<span class="property-type">object</span></dt>
|
||||||
|
<dd>An optional JSON Schema object to validate the payload against.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<h3>Outputs</h3>
|
<h3>Outputs</h3>
|
||||||
<dl class="message-properties">
|
<dl class="message-properties">
|
||||||
@ -41,6 +43,9 @@
|
|||||||
<li>If the input is a JavaScript object it creates a JSON string. The string can optionally be well-formatted.</li>
|
<li>If the input is a JavaScript object it creates a JSON string. The string can optionally be well-formatted.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt>schemaError<span class="property-type">array</span></dt>
|
||||||
|
<dd>If JSON schema validation fails, the catch node will have a <code>schemaError</code> property
|
||||||
|
containing an array of errors.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<h3>Details</h3>
|
<h3>Details</h3>
|
||||||
<p>By default, the node operates on <code>msg.payload</code>, but can be configured
|
<p>By default, the node operates on <code>msg.payload</code>, but can be configured
|
||||||
@ -53,6 +58,8 @@
|
|||||||
receives a String, no further checks will be made of the property. It will
|
receives a String, no further checks will be made of the property. It will
|
||||||
not check the String is valid JSON nor will it reformat it if the format option
|
not check the String is valid JSON nor will it reformat it if the format option
|
||||||
is selected.</p>
|
is selected.</p>
|
||||||
|
<p>For more details about JSON Schema you can consult the specification
|
||||||
|
<a href="http://json-schema.org/latest/json-schema-validation.html">here</a>.</p>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
@ -16,21 +16,52 @@
|
|||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
const Ajv = require('ajv');
|
||||||
|
const ajv = new Ajv({allErrors: true, schemaId: 'auto'});
|
||||||
|
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
|
||||||
|
|
||||||
function JSONNode(n) {
|
function JSONNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
RED.nodes.createNode(this,n);
|
||||||
this.indent = n.pretty ? 4 : 0;
|
this.indent = n.pretty ? 4 : 0;
|
||||||
this.action = n.action||"";
|
this.action = n.action||"";
|
||||||
this.property = n.property||"payload";
|
this.property = n.property||"payload";
|
||||||
|
this.schema = null;
|
||||||
|
this.compiledSchema = null;
|
||||||
|
|
||||||
var node = this;
|
var node = this;
|
||||||
this.on("input", function(msg) {
|
this.on("input", function(msg) {
|
||||||
|
var validate = false;
|
||||||
|
if (msg.schema) {
|
||||||
|
// If input schema is different, re-compile it
|
||||||
|
if (JSON.stringify(this.schema) != JSON.stringify(msg.schema)) {
|
||||||
|
try {
|
||||||
|
this.compiledSchema = ajv.compile(msg.schema);
|
||||||
|
this.schema = msg.schema;
|
||||||
|
} catch(e) {
|
||||||
|
this.schema = null;
|
||||||
|
this.compiledSchema = null;
|
||||||
|
node.error(RED._("json.errors.schema-error-compile"), msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validate = true;
|
||||||
|
}
|
||||||
var value = RED.util.getMessageProperty(msg,node.property);
|
var value = RED.util.getMessageProperty(msg,node.property);
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
if (node.action === "" || node.action === "obj") {
|
if (node.action === "" || node.action === "obj") {
|
||||||
try {
|
try {
|
||||||
RED.util.setMessageProperty(msg,node.property,JSON.parse(value));
|
RED.util.setMessageProperty(msg,node.property,JSON.parse(value));
|
||||||
node.send(msg);
|
if (validate) {
|
||||||
|
if (this.compiledSchema(msg[node.property])) {
|
||||||
|
node.send(msg);
|
||||||
|
} else {
|
||||||
|
msg.schemaError = this.compiledSchema.errors;
|
||||||
|
node.error(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`, msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.send(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(e) { node.error(e.message,msg); }
|
catch(e) { node.error(e.message,msg); }
|
||||||
} else {
|
} else {
|
||||||
@ -41,8 +72,19 @@ module.exports = function(RED) {
|
|||||||
if (node.action === "" || node.action === "str") {
|
if (node.action === "" || node.action === "str") {
|
||||||
if (!Buffer.isBuffer(value)) {
|
if (!Buffer.isBuffer(value)) {
|
||||||
try {
|
try {
|
||||||
RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent));
|
if (validate) {
|
||||||
node.send(msg);
|
if (this.compiledSchema(value)) {
|
||||||
|
RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent));
|
||||||
|
node.send(msg);
|
||||||
|
} else {
|
||||||
|
msg.schemaError = this.compiledSchema.errors;
|
||||||
|
node.error(`${RED._("json.errors.schema-error")}: ${ajv.errorsText(this.compiledSchema.errors)}`, msg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent));
|
||||||
|
node.send(msg);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(e) { node.error(RED._("json.errors.dropped-error")); }
|
catch(e) { node.error(RED._("json.errors.dropped-error")); }
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"flow"
|
"flow"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"ajv": "6.5.1",
|
||||||
"basic-auth": "2.0.0",
|
"basic-auth": "2.0.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
|
@ -20,6 +20,8 @@ var joinNode = require("../../../../nodes/core/logic/17-split.js");
|
|||||||
var helper = require("node-red-node-test-helper");
|
var helper = require("node-red-node-test-helper");
|
||||||
var RED = require("../../../../red/red.js");
|
var RED = require("../../../../red/red.js");
|
||||||
|
|
||||||
|
var TimeoutForErrorCase = 20;
|
||||||
|
|
||||||
describe('SPLIT node', function() {
|
describe('SPLIT node', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
@ -264,6 +266,126 @@ describe('SPLIT node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle invalid spltType (not an array)', function (done) {
|
||||||
|
var flow = [{ id: "sn1", type: "split", splt: "1", spltType: "bin", wires: [["sn2"]] },
|
||||||
|
{ id: "sn2", type: "helper" }];
|
||||||
|
helper.load(splitNode, flow, function () {
|
||||||
|
var sn1 = helper.getNode("sn1");
|
||||||
|
var sn2 = helper.getNode("sn2");
|
||||||
|
setTimeout(function () {
|
||||||
|
done();
|
||||||
|
}, TimeoutForErrorCase);
|
||||||
|
sn2.on("input", function (msg) {
|
||||||
|
done(new Error("This path does not go through."));
|
||||||
|
});
|
||||||
|
sn1.receive({ payload: "123" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle invalid splt length', function (done) {
|
||||||
|
var flow = [{ id: "sn1", type: "split", splt: 0, spltType: "len", wires: [["sn2"]] },
|
||||||
|
{ id: "sn2", type: "helper" }];
|
||||||
|
helper.load(splitNode, flow, function () {
|
||||||
|
var sn1 = helper.getNode("sn1");
|
||||||
|
var sn2 = helper.getNode("sn2");
|
||||||
|
setTimeout(function () {
|
||||||
|
done();
|
||||||
|
}, TimeoutForErrorCase);
|
||||||
|
sn2.on("input", function (msg) {
|
||||||
|
done(new Error("This path does not go through."));
|
||||||
|
});
|
||||||
|
sn1.receive({ payload: "123" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle invalid array splt length', function (done) {
|
||||||
|
var flow = [{ id: "sn1", type: "split", arraySplt: 0, arraySpltType: "len", wires: [["sn2"]] },
|
||||||
|
{ id: "sn2", type: "helper" }];
|
||||||
|
helper.load(splitNode, flow, function () {
|
||||||
|
var sn1 = helper.getNode("sn1");
|
||||||
|
var sn2 = helper.getNode("sn2");
|
||||||
|
setTimeout(function () {
|
||||||
|
done();
|
||||||
|
}, TimeoutForErrorCase);
|
||||||
|
sn2.on("input", function (msg) {
|
||||||
|
done(new Error("This path does not go through."));
|
||||||
|
});
|
||||||
|
sn1.receive({ payload: "123" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ceil count value when msg.payload type is string', function (done) {
|
||||||
|
var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "len", 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", 2);
|
||||||
|
msg.parts.should.have.property("index");
|
||||||
|
if (msg.parts.index === 0) { msg.payload.length.should.equal(2); }
|
||||||
|
if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); }
|
||||||
|
});
|
||||||
|
sn1.receive({ payload: "123" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle spltBufferString value of undefined', function (done) {
|
||||||
|
var flow = [{ id: "sn1", type: "split", wires: [["sn2"]], splt: "[52]", spltType: "bin" },
|
||||||
|
{ id: "sn2", type: "helper" }];
|
||||||
|
helper.load(splitNode, flow, function () {
|
||||||
|
var sn1 = helper.getNode("sn1");
|
||||||
|
var sn2 = helper.getNode("sn2");
|
||||||
|
sn2.on("input", function (msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property("parts");
|
||||||
|
msg.parts.should.have.property("index");
|
||||||
|
if (msg.parts.index === 0) { msg.payload.toString().should.equal("123"); done(); }
|
||||||
|
} catch (err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sn1.receive({ payload: "123" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ceil count value when msg.payload type is Buffer', function (done) {
|
||||||
|
var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "len", 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", 2);
|
||||||
|
msg.parts.should.have.property("index");
|
||||||
|
if (msg.parts.index === 0) { msg.payload.length.should.equal(2); }
|
||||||
|
if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); }
|
||||||
|
});
|
||||||
|
var b = new Buffer.from("123");
|
||||||
|
sn1.receive({ payload: b });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set msg.parts.ch when node.spltType is str', function (done) {
|
||||||
|
var flow = [{ id: "sn1", type: "split", splt: "2", spltType: "str", stream: false, 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", 2);
|
||||||
|
msg.parts.should.have.property("index");
|
||||||
|
if (msg.parts.index === 0) { msg.payload.length.should.equal(2); }
|
||||||
|
if (msg.parts.index === 1) { msg.payload.length.should.equal(1); done(); }
|
||||||
|
});
|
||||||
|
var b = new Buffer.from("123");
|
||||||
|
sn1.receive({ payload: b });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('JOIN node', function() {
|
describe('JOIN node', function() {
|
||||||
@ -696,6 +818,57 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should manually join things into an array, send when told complete', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:1, mode:"custom", build:"array"},
|
||||||
|
{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("payload");
|
||||||
|
msg.payload.should.be.an.Array();
|
||||||
|
msg.payload.length.should.equal(3);
|
||||||
|
msg.payload[0].should.equal(1);
|
||||||
|
msg.payload[1].should.equal(2);
|
||||||
|
msg.payload[2].should.equal(3);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch(e) { done(e); }
|
||||||
|
});
|
||||||
|
n1.receive({payload:1, topic:"A"});
|
||||||
|
n1.receive({payload:2, topic:"B"});
|
||||||
|
n1.receive({payload:3, topic:"C"});
|
||||||
|
n1.receive({complete:true});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should manually join things into an object, send when told complete', function(done) {
|
||||||
|
var flow = [{id:"n1", type:"join", wires:[["n2"]], timeout:1, mode:"custom", build:"object"},
|
||||||
|
{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("payload");
|
||||||
|
msg.payload.should.be.an.Object();
|
||||||
|
Object.keys(msg.payload).length.should.equal(3);
|
||||||
|
msg.payload.A.should.equal(1);
|
||||||
|
msg.payload.B.should.equal(2);
|
||||||
|
msg.payload.C.should.equal(3);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch(e) { done(e); }
|
||||||
|
});
|
||||||
|
n1.receive({payload:1, topic:"A"});
|
||||||
|
n1.receive({payload:2, topic:"B"});
|
||||||
|
n1.receive({payload:3, topic:"C"});
|
||||||
|
n1.receive({complete:true});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should join split strings back into a word', function(done) {
|
it('should join split strings back into a word', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", mode:"auto", wires:[["n2"]]},
|
var flow = [{id:"n1", type:"join", mode:"auto", wires:[["n2"]]},
|
||||||
{id:"n2", type:"helper"}];
|
{id:"n2", type:"helper"}];
|
||||||
@ -739,7 +912,7 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redece messages', function(done) {
|
it('should reduce messages', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", mode:"reduce",
|
var flow = [{id:"n1", type:"join", mode:"reduce",
|
||||||
reduceRight:false,
|
reduceRight:false,
|
||||||
reduceExp:"$A+payload",
|
reduceExp:"$A+payload",
|
||||||
@ -767,7 +940,7 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redece messages using $I', function(done) {
|
it('should reduce messages using $I', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", mode:"reduce",
|
var flow = [{id:"n1", type:"join", mode:"reduce",
|
||||||
reduceRight:false,
|
reduceRight:false,
|
||||||
reduceExp:"$A+$I",
|
reduceExp:"$A+$I",
|
||||||
@ -795,7 +968,7 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redece messages with fixup', function(done) {
|
it('should reduce messages with fixup', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", mode:"reduce",
|
var flow = [{id:"n1", type:"join", mode:"reduce",
|
||||||
reduceRight:false,
|
reduceRight:false,
|
||||||
reduceExp:"$A+payload",
|
reduceExp:"$A+payload",
|
||||||
@ -824,7 +997,7 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redece messages (left)', function(done) {
|
it('should reduce messages (left)', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", mode:"reduce",
|
var flow = [{id:"n1", type:"join", mode:"reduce",
|
||||||
reduceRight:false,
|
reduceRight:false,
|
||||||
reduceExp:"'(' & $A & '+' & payload & ')'",
|
reduceExp:"'(' & $A & '+' & payload & ')'",
|
||||||
@ -853,7 +1026,7 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redece messages (right)', function(done) {
|
it('should reduce messages (right)', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", mode:"reduce",
|
var flow = [{id:"n1", type:"join", mode:"reduce",
|
||||||
reduceRight:true,
|
reduceRight:true,
|
||||||
reduceExp:"'(' & $A & '+' & payload & ')'",
|
reduceExp:"'(' & $A & '+' & payload & ')'",
|
||||||
@ -882,7 +1055,7 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redece messages with array result', function(done) {
|
it('should reduce messages with array result', function(done) {
|
||||||
var flow = [{id:"n1", type:"join", mode:"reduce",
|
var flow = [{id:"n1", type:"join", mode:"reduce",
|
||||||
reduceRight:false,
|
reduceRight:false,
|
||||||
reduceExp:"$append($A,[payload])",
|
reduceExp:"$append($A,[payload])",
|
||||||
@ -942,7 +1115,7 @@ describe('JOIN node', function() {
|
|||||||
evt.should.have.property('type', "join");
|
evt.should.have.property('type', "join");
|
||||||
evt.should.have.property('msg', "join.too-many");
|
evt.should.have.property('msg', "join.too-many");
|
||||||
done();
|
done();
|
||||||
}, 150);
|
}, TimeoutForErrorCase);
|
||||||
n1.receive({payload:3, parts:{index:2, count:4, id:222}});
|
n1.receive({payload:3, parts:{index:2, count:4, id:222}});
|
||||||
n1.receive({payload:2, parts:{index:1, count:4, id:222}});
|
n1.receive({payload:2, parts:{index:1, count:4, id:222}});
|
||||||
n1.receive({payload:4, parts:{index:3, count:4, id:222}});
|
n1.receive({payload:4, parts:{index:3, count:4, id:222}});
|
||||||
@ -950,4 +1123,109 @@ describe('JOIN node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle invalid JSON expression"', function (done) {
|
||||||
|
var flow = [{
|
||||||
|
id: "n1", type: "join", mode: "reduce",
|
||||||
|
reduceRight: false,
|
||||||
|
reduceExp: "invalid expr",
|
||||||
|
reduceInit: "0",
|
||||||
|
reduceInitType: "num",
|
||||||
|
reduceFixup: undefined,
|
||||||
|
wires: [["n2"]]
|
||||||
|
},
|
||||||
|
{ id: "n2", type: "helper" }];
|
||||||
|
helper.load(joinNode, flow, function () {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
setTimeout(function () {
|
||||||
|
done();
|
||||||
|
}, TimeoutForErrorCase);
|
||||||
|
n2.on("input", function (msg) {
|
||||||
|
done(new Error("This path does not go through."));
|
||||||
|
});
|
||||||
|
n1.receive({ payload: "A", parts: { id: 1, type: "string", ch: ",", index: 0, count: 1 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should concat payload when group.type is array', function (done) {
|
||||||
|
var flow = [{ id: "n1", type: "join", wires: [["n2"]], build: "array", 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("payload");
|
||||||
|
msg.payload.should.be.an.Array();
|
||||||
|
msg.payload[0].should.equal("ab");
|
||||||
|
msg.payload[1].should.equal("cd");
|
||||||
|
msg.payload[2].should.equal("ef");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) { done(e); }
|
||||||
|
});
|
||||||
|
n1.receive({ payload: "ab", parts: { id: 1, type: "array", ch: ",", index: 0, count: 3, len:2}});
|
||||||
|
n1.receive({ payload: "cd", parts: { id: 1, type: "array", ch: ",", index: 1, count: 3, len:2}});
|
||||||
|
n1.receive({ payload: "ef", parts: { id: 1, type: "array", ch: ",", index: 2, count: 3, len:2}});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should concat payload when group.type is buffer and group.joinChar is undefined', function (done) {
|
||||||
|
var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: ",", build: "buffer", 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("payload");
|
||||||
|
Buffer.isBuffer(msg.payload).should.be.true();
|
||||||
|
msg.payload.toString().should.equal("ABC");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) { done(e); }
|
||||||
|
});
|
||||||
|
n1.receive({ payload: Buffer.from("A"), parts: { id: 1, type: "buffer", index: 0, count: 3 } });
|
||||||
|
n1.receive({ payload: Buffer.from("B"), parts: { id: 1, type: "buffer", index: 1, count: 3 } });
|
||||||
|
n1.receive({ payload: Buffer.from("C"), parts: { id: 1, type: "buffer", index: 2, count: 3 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should concat payload when group.type is string and group.joinChar is not string', function (done) {
|
||||||
|
var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: ",", build: "buffer", 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("payload");
|
||||||
|
msg.payload.toString().should.equal("A0B0C");
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch (e) { done(e); }
|
||||||
|
});
|
||||||
|
n1.receive({ payload: Buffer.from("A"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 0, count: 3 } });
|
||||||
|
n1.receive({ payload: Buffer.from("B"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 1, count: 3 } });
|
||||||
|
n1.receive({ payload: Buffer.from("C"), parts: { id: 1, type: "string", ch: Buffer.from("0"), index: 2, count: 3 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle msg.parts property when mode is auto and parts or id are missing', function (done) {
|
||||||
|
var flow = [{ id: "n1", type: "join", wires: [["n2"]], joiner: "[44]", joinerType: "bin", build: "string", 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) {
|
||||||
|
done(new Error("This path does not go through."));
|
||||||
|
});
|
||||||
|
n1.receive({ payload: "A", parts: { type: "string", ch: ",", index: 0, count: 2 } });
|
||||||
|
n1.receive({ payload: "B", parts: { type: "string", ch: ",", index: 1, count: 2 } });
|
||||||
|
setTimeout(function () {
|
||||||
|
done();
|
||||||
|
}, TimeoutForErrorCase);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -213,8 +213,6 @@ describe('SORT node', function() {
|
|||||||
check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done);
|
check_sort1C(flow, "$substring(payload,1)", data_in, data_out, done);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]},
|
var flow = [{id:"n1", type:"sort", order:"descending", as_num:false, wires:[["n2"]]},
|
||||||
@ -250,8 +248,6 @@ describe('SORT node', function() {
|
|||||||
n1.receive(msg1);
|
n1.receive(msg1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
it('should handle too many pending messages', function(done) {
|
it('should handle too many pending messages', function(done) {
|
||||||
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]},
|
var flow = [{id:"n1", type:"sort", order:"ascending", as_num:false, target:"payload", targetType:"seq", seqKey:"payload", seqKeyType:"msg", wires:[["n2"]]},
|
||||||
|
@ -247,4 +247,85 @@ describe('JSON node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should pass an object if provided a valid JSON string and schema', function(done) {
|
||||||
|
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||||
|
{id:"jn2", type:"helper"}];
|
||||||
|
helper.load(jsonNode, flow, function() {
|
||||||
|
var jn1 = helper.getNode("jn1");
|
||||||
|
var jn2 = helper.getNode("jn2");
|
||||||
|
jn2.on("input", function(msg) {
|
||||||
|
should.equal(msg.payload.number, 3);
|
||||||
|
should.equal(msg.payload.string, "allo");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var jsonString = '{"number": 3, "string": "allo"}';
|
||||||
|
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||||
|
jn1.receive({payload:jsonString, schema:schema});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass a string if provided a valid object and schema', function(done) {
|
||||||
|
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||||
|
{id:"jn2", type:"helper"}];
|
||||||
|
helper.load(jsonNode, flow, function() {
|
||||||
|
var jn1 = helper.getNode("jn1");
|
||||||
|
var jn2 = helper.getNode("jn2");
|
||||||
|
jn2.on("input", function(msg) {
|
||||||
|
should.equal(msg.payload, '{"number":3,"string":"allo"}');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var obj = {"number": 3, "string": "allo"};
|
||||||
|
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||||
|
jn1.receive({payload:obj, schema:schema});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log an error if passed an invalid object and valid schema', function(done) {
|
||||||
|
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||||
|
{id:"jn2", type:"helper"}];
|
||||||
|
helper.load(jsonNode, flow, function() {
|
||||||
|
try {
|
||||||
|
var jn1 = helper.getNode("jn1");
|
||||||
|
var jn2 = helper.getNode("jn2");
|
||||||
|
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||||
|
var obj = {"number": "foo", "string": 3};
|
||||||
|
jn1.receive({payload:obj, schema:schema});
|
||||||
|
var logEvents = helper.log().args.filter(function(evt) {
|
||||||
|
return evt[0].type == "json";
|
||||||
|
});
|
||||||
|
logEvents.should.have.length(1);
|
||||||
|
logEvents[0][0].should.have.a.property('msg');
|
||||||
|
logEvents[0][0].msg.should.equal("json.errors.schema-error: data.number should be number, data.string should be string");
|
||||||
|
logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should log an error if passed a valid object and invalid schema', function(done) {
|
||||||
|
var flow = [{id:"jn1",type:"json",wires:[["jn2"]]},
|
||||||
|
{id:"jn2", type:"helper"}];
|
||||||
|
helper.load(jsonNode, flow, function() {
|
||||||
|
try {
|
||||||
|
var jn1 = helper.getNode("jn1");
|
||||||
|
var jn2 = helper.getNode("jn2");
|
||||||
|
var schema = "garbage";
|
||||||
|
var obj = {"number": "foo", "string": 3};
|
||||||
|
jn1.receive({payload:obj, schema:schema});
|
||||||
|
var logEvents = helper.log().args.filter(function(evt) {
|
||||||
|
return evt[0].type == "json";
|
||||||
|
});
|
||||||
|
logEvents.should.have.length(1);
|
||||||
|
logEvents[0][0].should.have.a.property('msg');
|
||||||
|
logEvents[0][0].msg.should.equal("json.errors.schema-error-compile");
|
||||||
|
logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user