diff --git a/nodes/core/parsers/70-JSON.js b/nodes/core/parsers/70-JSON.js index eb0bec63c..4e1e51e47 100644 --- a/nodes/core/parsers/70-JSON.js +++ b/nodes/core/parsers/70-JSON.js @@ -19,6 +19,7 @@ module.exports = function(RED) { const Ajv = require('ajv'); const ajv = new Ajv({allErrors: true, schemaId: 'auto'}); ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); + ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); function JSONNode(n) { RED.nodes.createNode(this,n); @@ -29,6 +30,7 @@ module.exports = function(RED) { this.compiledSchema = null; var node = this; + this.on("input", function(msg) { var validate = false; if (msg.schema) { @@ -65,7 +67,17 @@ module.exports = function(RED) { } catch(e) { node.error(e.message,msg); } } else { - node.send(msg); + // If node.action is str and value is str + if (validate) { + if (this.compiledSchema(JSON.parse(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); + } } } else if (typeof value === "object") { @@ -84,13 +96,22 @@ module.exports = function(RED) { RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent)); node.send(msg); } - } catch(e) { node.error(RED._("json.errors.dropped-error")); } } else { node.warn(RED._("json.errors.dropped-object")); } } else { - node.send(msg); + // If node.action is obj and value is object + if (validate) { + if (this.compiledSchema(value)) { + 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); + } } } else { node.warn(RED._("json.errors.dropped")); } diff --git a/test/nodes/core/parsers/70-JSON_spec.js b/test/nodes/core/parsers/70-JSON_spec.js index ba913b1e7..4f6e4beb2 100644 --- a/test/nodes/core/parsers/70-JSON_spec.js +++ b/test/nodes/core/parsers/70-JSON_spec.js @@ -265,6 +265,23 @@ describe('JSON node', function() { }); }); + it('should pass an object if provided a valid object and schema and action is object', function(done) { + var flow = [{id:"jn1",type:"json",action:"obj",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 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 pass a string if provided a valid object and schema', function(done) { var flow = [{id:"jn1",type:"json",wires:[["jn2"]]}, {id:"jn2", type:"helper"}]; @@ -281,6 +298,22 @@ describe('JSON node', function() { }); }); + it('should pass a string if provided a valid JSON string and schema and action is string', function(done) { + var flow = [{id:"jn1",type:"json",action:"str",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 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 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"}]; @@ -305,6 +338,78 @@ describe('JSON node', function() { }); }); + it('should log an error if passed an invalid object and valid schema and action is object', function(done) { + var flow = [{id:"jn1",type:"json",action:"obj",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 an invalid JSON string 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 jsonString = '{"number":"Hello","string":3}'; + jn1.receive({payload:jsonString, 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 an invalid JSON string and valid schema and action is string', function(done) { + var flow = [{id:"jn1",type:"json",action:"str",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 jsonString = '{"number":"Hello","string":3}'; + jn1.receive({payload:jsonString, 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"}];