Skip to content

Commit 9bbc8ed

Browse files
natcldceejay
authored andcommitted
Added YAML parser node (node-red#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
1 parent b1ab26e commit 9bbc8ed

7 files changed

Lines changed: 230 additions & 0 deletions

File tree

editor/icons/parser-yaml.png

423 Bytes
Loading

nodes/core/core/80-template.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<option value="css">CSS</option>
3838
<option value="markdown">Markdown</option>
3939
<option value="text">none</option>
40+
<option value="yaml">YAML</option>
4041
</select>
4142
</div>
4243
</div>

nodes/core/locales/en-US/messages.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,13 @@
614614
"dropped-error": "Failed to convert payload"
615615
}
616616
},
617+
"yaml": {
618+
"errors": {
619+
"dropped-object": "Ignored non-object payload",
620+
"dropped": "Ignored unsupported payload type",
621+
"dropped-error": "Failed to convert payload"
622+
}
623+
},
617624
"xml": {
618625
"label": {
619626
"represent": "Represent XML tag attributes as a property named",

nodes/core/parsers/70-YAML.html

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script type="text/x-red" data-template-name="yaml">
2+
<div class="form-row">
3+
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
4+
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
5+
</div>
6+
</script>
7+
8+
<script type="text/x-red" data-help-name="yaml">
9+
<p>A function that parses the <code>msg.payload</code> to convert a YAML string to/from a javascript object. Places the result back into the payload.</p>
10+
<p>If the input is a YAML string it tries to parse it to a javascript object.</p>
11+
<p>If the input is a javascript object it creates a YAML string.</p>
12+
</script>
13+
14+
<script type="text/javascript">
15+
RED.nodes.registerType('yaml',{
16+
category: 'function',
17+
color:"#DEBD5C",
18+
defaults: {
19+
name: {value:""}
20+
},
21+
inputs:1,
22+
outputs:1,
23+
icon: "parser-yaml.png",
24+
label: function() {
25+
return this.name||"yaml";
26+
},
27+
labelStyle: function() {
28+
return this.name?"node_label_italic":"";
29+
}
30+
});
31+
</script>

nodes/core/parsers/70-YAML.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module.exports = function(RED) {
2+
"use strict";
3+
var yaml = require('js-yaml');
4+
function YAMLNode(n) {
5+
RED.nodes.createNode(this,n);
6+
var node = this;
7+
this.on("input", function(msg) {
8+
if (msg.hasOwnProperty("payload")) {
9+
if (typeof msg.payload === "string") {
10+
try {
11+
msg.payload = yaml.load(msg.payload);
12+
node.send(msg);
13+
}
14+
catch(e) { node.error(e.message,msg); }
15+
}
16+
else if (typeof msg.payload === "object") {
17+
if (!Buffer.isBuffer(msg.payload)) {
18+
try {
19+
msg.payload = yaml.dump(msg.payload);
20+
node.send(msg);
21+
}
22+
catch(e) {
23+
node.error(RED._("yaml.errors.dropped-error"));
24+
}
25+
}
26+
else { node.warn(RED._("yaml.errors.dropped-object")); }
27+
}
28+
else { node.warn(RED._("yaml.errors.dropped")); }
29+
}
30+
else { node.send(msg); } // If no payload - just pass it on.
31+
});
32+
}
33+
RED.nodes.registerType("yaml",YAMLNode);
34+
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"fs.notify":"0.0.4",
4141
"i18next":"1.10.6",
4242
"is-utf8":"0.2.1",
43+
"js-yaml": "3.6.1",
4344
"media-typer": "0.3.0",
4445
"mqtt": "1.14.1",
4546
"mustache": "2.2.1",
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/**
2+
* Copyright 2014 IBM Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
**/
16+
17+
var should = require("should");
18+
var yamlNode = require("../../../../nodes/core/parsers/70-YAML.js");
19+
var helper = require("../../helper.js");
20+
21+
describe('YAML node', function() {
22+
23+
before(function(done) {
24+
helper.startServer(done);
25+
});
26+
27+
afterEach(function() {
28+
helper.unload();
29+
});
30+
31+
it('should be loaded', function(done) {
32+
var flow = [{id:"yamlNode1", type:"yaml", name: "yamlNode" }];
33+
helper.load(yamlNode, flow, function() {
34+
var yamlNode1 = helper.getNode("yamlNode1");
35+
yamlNode1.should.have.property('name', 'yamlNode');
36+
done();
37+
});
38+
});
39+
40+
it('should convert a valid yaml string to a javascript object', function(done) {
41+
var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"},
42+
{id:"yn2", type:"helper"}];
43+
helper.load(yamlNode, flow, function() {
44+
var yn1 = helper.getNode("yn1");
45+
var yn2 = helper.getNode("yn2");
46+
yn2.on("input", function(msg) {
47+
console.log('msg', msg);
48+
console.log('payload', msg.payload.employees[0]);
49+
msg.should.have.property('topic', 'bar');
50+
msg.payload.should.have.property('employees');
51+
msg.payload.employees[0].should.have.property('firstName', 'John');
52+
msg.payload.employees[0].should.have.property('lastName', 'Smith');
53+
done();
54+
});
55+
var yamlString = "employees:\n - firstName: John\n lastName: Smith\n";
56+
yn1.receive({payload:yamlString,topic: "bar"});
57+
});
58+
});
59+
60+
it('should convert a javascript object to a yaml string', function(done) {
61+
var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"},
62+
{id:"yn2", type:"helper"}];
63+
helper.load(yamlNode, flow, function() {
64+
var yn1 = helper.getNode("yn1");
65+
var yn2 = helper.getNode("yn2");
66+
yn2.on("input", function(msg) {
67+
should.equal(msg.payload, "employees:\n - firstName: John\n lastName: Smith\n");
68+
done();
69+
});
70+
var obj = {employees:[{firstName:"John", lastName:"Smith"}]};
71+
yn1.receive({payload:obj});
72+
});
73+
});
74+
75+
it('should convert an array to a yaml string', function(done) {
76+
var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"},
77+
{id:"yn2", type:"helper"}];
78+
helper.load(yamlNode, flow, function() {
79+
var yn1 = helper.getNode("yn1");
80+
var yn2 = helper.getNode("yn2");
81+
yn2.on("input", function(msg) {
82+
should.equal(msg.payload, "- 1\n- 2\n- 3\n");
83+
done();
84+
});
85+
var obj = [1,2,3];
86+
yn1.receive({payload:obj});
87+
});
88+
});
89+
90+
it('should log an error if asked to parse an invalid yaml string', function(done) {
91+
var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"},
92+
{id:"yn2", type:"helper"}];
93+
helper.load(yamlNode, flow, function() {
94+
try {
95+
var yn1 = helper.getNode("yn1");
96+
var yn2 = helper.getNode("yn2");
97+
yn1.receive({payload:'employees:\n-firstName: John\n- lastName: Smith\n',topic: "bar"});
98+
var logEvents = helper.log().args.filter(function(evt) {
99+
return evt[0].type == "yaml";
100+
});
101+
logEvents.should.have.length(1);
102+
logEvents[0][0].should.have.a.property('msg');
103+
logEvents[0][0].msg.should.startWith("end of the stream");
104+
logEvents[0][0].should.have.a.property('level',helper.log().ERROR);
105+
done();
106+
} catch(err) {
107+
done(err);
108+
}
109+
});
110+
});
111+
112+
it('should log an error if asked to parse something thats not yaml or js', function(done) {
113+
var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"},
114+
{id:"yn2", type:"helper"}];
115+
helper.load(yamlNode, flow, function() {
116+
var yn1 = helper.getNode("yn1");
117+
var yn2 = helper.getNode("yn2");
118+
setTimeout(function() {
119+
try {
120+
var logEvents = helper.log().args.filter(function(evt) {
121+
return evt[0].type == "yaml";
122+
});
123+
logEvents.should.have.length(3);
124+
logEvents[0][0].should.have.a.property('msg');
125+
logEvents[0][0].msg.toString().should.eql('yaml.errors.dropped');
126+
logEvents[1][0].should.have.a.property('msg');
127+
logEvents[1][0].msg.toString().should.eql('yaml.errors.dropped');
128+
logEvents[2][0].should.have.a.property('msg');
129+
logEvents[2][0].msg.toString().should.eql('yaml.errors.dropped-object');
130+
done();
131+
} catch(err) {
132+
done(err);
133+
}
134+
},150);
135+
yn1.receive({payload:true});
136+
yn1.receive({payload:1});
137+
yn1.receive({payload:new Buffer("a")});
138+
});
139+
});
140+
141+
it('should pass straight through if no payload set', function(done) {
142+
var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"},
143+
{id:"yn2", type:"helper"}];
144+
helper.load(yamlNode, flow, function() {
145+
var yn1 = helper.getNode("yn1");
146+
var yn2 = helper.getNode("yn2");
147+
yn2.on("input", function(msg) {
148+
msg.should.have.property('topic', 'bar');
149+
msg.should.not.have.property('payload');
150+
done();
151+
});
152+
yn1.receive({topic: "bar"});
153+
});
154+
});
155+
156+
});

0 commit comments

Comments
 (0)