From 9bbc8eda9dcfdff43d7d118d9b27ee842e0e10d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20L=C3=A9caud=C3=A9?= Date: Fri, 11 Nov 2016 05:22:05 +0800 Subject: [PATCH] Added YAML parser node (#1034) Thanks @natcl - (sorry pressed closed by mistake !) * Added YAML parser node * Added YAML error strings in messages.json * Change location of YAML library import * Remove copyright * Remove copyright * Change order of yaml in Template node * Add YAML test * Add working test --- editor/icons/parser-yaml.png | Bin 0 -> 423 bytes nodes/core/core/80-template.html | 1 + nodes/core/locales/en-US/messages.json | 7 ++ nodes/core/parsers/70-YAML.html | 31 +++++ nodes/core/parsers/70-YAML.js | 34 ++++++ package.json | 1 + test/nodes/core/parsers/70-YAML_spec.js | 156 ++++++++++++++++++++++++ 7 files changed, 230 insertions(+) create mode 100644 editor/icons/parser-yaml.png create mode 100644 nodes/core/parsers/70-YAML.html create mode 100644 nodes/core/parsers/70-YAML.js create mode 100644 test/nodes/core/parsers/70-YAML_spec.js diff --git a/editor/icons/parser-yaml.png b/editor/icons/parser-yaml.png new file mode 100644 index 0000000000000000000000000000000000000000..e4cb2f20bab3b820c2ca532ba323b3b3950d1727 GIT binary patch literal 423 zcmV;Y0a*TtP)Px#32;bRa{vGf6951U69E94oEQKA0Zd6mK~zW$V_+C@fDz>5|NsB90I4>p@4;d~ zHaZQG28sOw;x^r2N072jsq3*VKodS2$Uq%K#=9Q4JFe++=fzMAcmn# zIKzQ>2@9aoNigN0*hL2?fdZ$Yg5E${5lz8fApbg4ED}fy1F-;<524V!`~b-30?7br zH;|u@zzZN79J@f;0ZDuVJ_AAOT3`x*>~%;Q!Q(i4!#NCs45}RI>t*{SAmg z$poC(fpiHFBLjj4f)wvSa%C-$ZvwSI8p!?(Qh)@AG7zLb2xCSS + diff --git a/nodes/core/locales/en-US/messages.json b/nodes/core/locales/en-US/messages.json index ab733c45d..23d0a94bb 100644 --- a/nodes/core/locales/en-US/messages.json +++ b/nodes/core/locales/en-US/messages.json @@ -614,6 +614,13 @@ "dropped-error": "Failed to convert payload" } }, + "yaml": { + "errors": { + "dropped-object": "Ignored non-object payload", + "dropped": "Ignored unsupported payload type", + "dropped-error": "Failed to convert payload" + } + }, "xml": { "label": { "represent": "Represent XML tag attributes as a property named", diff --git a/nodes/core/parsers/70-YAML.html b/nodes/core/parsers/70-YAML.html new file mode 100644 index 000000000..ef0cc52fa --- /dev/null +++ b/nodes/core/parsers/70-YAML.html @@ -0,0 +1,31 @@ + + + + + diff --git a/nodes/core/parsers/70-YAML.js b/nodes/core/parsers/70-YAML.js new file mode 100644 index 000000000..0c8e0d034 --- /dev/null +++ b/nodes/core/parsers/70-YAML.js @@ -0,0 +1,34 @@ +module.exports = function(RED) { + "use strict"; + var yaml = require('js-yaml'); + function YAMLNode(n) { + RED.nodes.createNode(this,n); + var node = this; + this.on("input", function(msg) { + if (msg.hasOwnProperty("payload")) { + if (typeof msg.payload === "string") { + try { + msg.payload = yaml.load(msg.payload); + node.send(msg); + } + catch(e) { node.error(e.message,msg); } + } + else if (typeof msg.payload === "object") { + if (!Buffer.isBuffer(msg.payload)) { + try { + msg.payload = yaml.dump(msg.payload); + node.send(msg); + } + catch(e) { + node.error(RED._("yaml.errors.dropped-error")); + } + } + else { node.warn(RED._("yaml.errors.dropped-object")); } + } + else { node.warn(RED._("yaml.errors.dropped")); } + } + else { node.send(msg); } // If no payload - just pass it on. + }); + } + RED.nodes.registerType("yaml",YAMLNode); +}; diff --git a/package.json b/package.json index ac20ba7e0..735584f0f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "fs.notify":"0.0.4", "i18next":"1.10.6", "is-utf8":"0.2.1", + "js-yaml": "3.6.1", "media-typer": "0.3.0", "mqtt": "1.14.1", "mustache": "2.2.1", diff --git a/test/nodes/core/parsers/70-YAML_spec.js b/test/nodes/core/parsers/70-YAML_spec.js new file mode 100644 index 000000000..fac2931cf --- /dev/null +++ b/test/nodes/core/parsers/70-YAML_spec.js @@ -0,0 +1,156 @@ +/** + * Copyright 2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var should = require("should"); +var yamlNode = require("../../../../nodes/core/parsers/70-YAML.js"); +var helper = require("../../helper.js"); + +describe('YAML node', function() { + + before(function(done) { + helper.startServer(done); + }); + + afterEach(function() { + helper.unload(); + }); + + it('should be loaded', function(done) { + var flow = [{id:"yamlNode1", type:"yaml", name: "yamlNode" }]; + helper.load(yamlNode, flow, function() { + var yamlNode1 = helper.getNode("yamlNode1"); + yamlNode1.should.have.property('name', 'yamlNode'); + done(); + }); + }); + + it('should convert a valid yaml string to a javascript object', function(done) { + var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + yn2.on("input", function(msg) { + console.log('msg', msg); + console.log('payload', msg.payload.employees[0]); + msg.should.have.property('topic', 'bar'); + msg.payload.should.have.property('employees'); + msg.payload.employees[0].should.have.property('firstName', 'John'); + msg.payload.employees[0].should.have.property('lastName', 'Smith'); + done(); + }); + var yamlString = "employees:\n - firstName: John\n lastName: Smith\n"; + yn1.receive({payload:yamlString,topic: "bar"}); + }); + }); + + it('should convert a javascript object to a yaml string', function(done) { + var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + yn2.on("input", function(msg) { + should.equal(msg.payload, "employees:\n - firstName: John\n lastName: Smith\n"); + done(); + }); + var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; + yn1.receive({payload:obj}); + }); + }); + + it('should convert an array to a yaml string', function(done) { + var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + yn2.on("input", function(msg) { + should.equal(msg.payload, "- 1\n- 2\n- 3\n"); + done(); + }); + var obj = [1,2,3]; + yn1.receive({payload:obj}); + }); + }); + + it('should log an error if asked to parse an invalid yaml string', function(done) { + var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + try { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + yn1.receive({payload:'employees:\n-firstName: John\n- lastName: Smith\n',topic: "bar"}); + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "yaml"; + }); + logEvents.should.have.length(1); + logEvents[0][0].should.have.a.property('msg'); + logEvents[0][0].msg.should.startWith("end of the stream"); + logEvents[0][0].should.have.a.property('level',helper.log().ERROR); + done(); + } catch(err) { + done(err); + } + }); + }); + + it('should log an error if asked to parse something thats not yaml or js', function(done) { + var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + setTimeout(function() { + try { + var logEvents = helper.log().args.filter(function(evt) { + return evt[0].type == "yaml"; + }); + logEvents.should.have.length(3); + logEvents[0][0].should.have.a.property('msg'); + logEvents[0][0].msg.toString().should.eql('yaml.errors.dropped'); + logEvents[1][0].should.have.a.property('msg'); + logEvents[1][0].msg.toString().should.eql('yaml.errors.dropped'); + logEvents[2][0].should.have.a.property('msg'); + logEvents[2][0].msg.toString().should.eql('yaml.errors.dropped-object'); + done(); + } catch(err) { + done(err); + } + },150); + yn1.receive({payload:true}); + yn1.receive({payload:1}); + yn1.receive({payload:new Buffer("a")}); + }); + }); + + it('should pass straight through if no payload set', function(done) { + var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, + {id:"yn2", type:"helper"}]; + helper.load(yamlNode, flow, function() { + var yn1 = helper.getNode("yn1"); + var yn2 = helper.getNode("yn2"); + yn2.on("input", function(msg) { + msg.should.have.property('topic', 'bar'); + msg.should.not.have.property('payload'); + done(); + }); + yn1.receive({topic: "bar"}); + }); + }); + +});