diff --git a/editor/icons/parser-yaml.png b/editor/icons/parser-yaml.png
new file mode 100644
index 000000000..e4cb2f20b
Binary files /dev/null and b/editor/icons/parser-yaml.png differ
diff --git a/nodes/core/core/80-template.html b/nodes/core/core/80-template.html
index fc6455ac6..44cf1cc4d 100644
--- a/nodes/core/core/80-template.html
+++ b/nodes/core/core/80-template.html
@@ -37,6 +37,7 @@
+
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"});
+ });
+ });
+
+});