1
0
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:
Nick O'Leary 2018-07-10 17:06:17 +01:00 committed by GitHub
commit 9bf87697fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 429 additions and 20 deletions

View File

@ -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}));

View File

@ -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",

View File

@ -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;

View File

@ -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">

View File

@ -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")); }
} }

View File

@ -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",

View File

@ -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);
});
});
}); });

View File

@ -214,8 +214,6 @@ describe('SORT node', function() {
}); });
})(); })();
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"]]},
{id:"n2", type:"helper"}]; {id:"n2", type:"helper"}];
@ -251,8 +249,6 @@ describe('SORT node', function() {
}); });
}); });
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"]]},
{id:"n2", type:"helper"}]; {id:"n2", type:"helper"}];

View File

@ -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);
}
});
});
}); });