Skip to content

Commit 8f84327

Browse files
author
Luiz Aoqui
committed
Content forked
1 parent 650fa87 commit 8f84327

File tree

9 files changed

+409
-0
lines changed

9 files changed

+409
-0
lines changed

.cfignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
launchConfigurations/
2+
.git/

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# EditorConfig is awesome: http://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Unix-style newlines with a newline ending every file
7+
[*]
8+
end_of_line = lf
9+
insert_final_newline = true
10+
indent_style = space
11+
indent_size = 4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

77-cloudant.html

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
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+
<script type="text/x-red" data-template-name="cloudant">
18+
<div class="form-row">
19+
<label for="node-config-input-hostname"><i class="fa fa-bookmark"></i> Host</label>
20+
<input class="input-append-left" type="text" id="node-config-input-hostname"
21+
placeholder="https://[username].cloudant.com">
22+
</div>
23+
24+
<div class="form-row">
25+
<label for="node-config-input-user"><i class="fa fa-user"></i> Username</label>
26+
<input type="text" id="node-config-input-user">
27+
</div>
28+
29+
<div class="form-row">
30+
<label for="node-config-input-pass"><i class="fa fa-lock"></i> Password</label>
31+
<input type="password" id="node-config-input-pass">
32+
</div>
33+
34+
<div class="form-row">
35+
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
36+
<input type="text" id="node-config-input-name" placeholder="Name">
37+
</div>
38+
</script>
39+
40+
<script type="text/javascript">
41+
RED.nodes.registerType("cloudant",{
42+
category: "config",
43+
color: "rgb(114, 199, 231)",
44+
defaults: {
45+
hostname: { value: "", required: true },
46+
name: { value: "" },
47+
//user -> credentials
48+
//pass -> credentials
49+
},
50+
label: function() {
51+
return this.name || this.hostname;
52+
},
53+
oneditprepare: function() {
54+
$.getJSON("cloudant/"+this.id, function(data) {
55+
if (data.user) {
56+
$("#node-config-input-user").val(data.user);
57+
}
58+
if (data.hasPassword) {
59+
$("#node-config-input-pass").val("__PWRD__");
60+
} else {
61+
$("#node-config-input-pass").val("");
62+
}
63+
});
64+
},
65+
oneditsave: function() {
66+
var newUser = $("#node-config-input-user").val();
67+
var newPass = $("#node-config-input-pass").val();
68+
var credentials = {};
69+
credentials.user = newUser;
70+
if (newPass != "__PWRD__") {
71+
credentials.password = newPass;
72+
}
73+
$.ajax({
74+
url: "cloudant/"+this.id,
75+
type: "POST",
76+
data: credentials,
77+
success: function(result) {}
78+
});
79+
},
80+
ondelete: function() {
81+
$.ajax({
82+
url: "cloudant/"+this.id,
83+
type: "DELETE",
84+
success: function(result) {}
85+
});
86+
}
87+
});
88+
</script>
89+
90+
<script type="text/x-red" data-template-name="cloudant out">
91+
<div class="form-row">
92+
<label for="node-input-cloudant"><i class="fa fa-tag"></i> Account</label>
93+
<input type="text" id="node-input-cloudant">
94+
</div>
95+
96+
<div class="form-row">
97+
<label for="node-input-db"><i class="fa fa-briefcase"></i> Database</label>
98+
<input type="text" id="node-input-db" placeholder="database">
99+
</div>
100+
101+
</div>
102+
<div class="form-row">
103+
<label for="node-input-operation"><i class="fa fa-wrench"></i> Operation</label>
104+
<select type="text" id="node-input-operation"
105+
style="display: inline-block; vertical-align: top;">
106+
<option value="insert">insert</option>
107+
<option value="delete">remove</option>
108+
</select>
109+
</div>
110+
111+
<div class="form-row node-input-payonly">
112+
<label>&nbsp;</label>
113+
<input type="checkbox" id="node-input-payonly" placeholder="Only"
114+
style="display: inline-block; width: auto; vertical-align: top;">
115+
<label for="node-input-payonly" style="width: 70%;">Only store msg.payload object?</label>
116+
</div>
117+
118+
<div class="form-row">
119+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
120+
<input type="text" id="node-input-name" placeholder="Name">
121+
</div>
122+
123+
<script>
124+
$("#node-input-operation").change(function() {
125+
var id = $("#node-input-operation option:selected").val();
126+
if (id == "delete") $(".node-input-payonly").hide();
127+
else $(".node-input-payonly").show();
128+
});
129+
</script>
130+
</script>
131+
132+
<script type="text/x-red" data-help-name="cloudant out">
133+
<p>
134+
A simple Cloudant output node. Stores <b>msg</b> in a chosen database.
135+
</p>
136+
<p>
137+
Cloudant generates a random id for the <b>msg._id</b> property if one
138+
is not specified and a new document will be <b>inserted</b> each time.
139+
If you want to <b>update</b> an existing document you must specify a
140+
value for <b>msg._id</b> in your object.
141+
</p>
142+
<p>
143+
You can <b>insert</b> only the <b>payload</b> by checking the checkbox
144+
in the configuration window.
145+
</p>
146+
<p>
147+
It is also possible to <b>delete</b> documents from the database by
148+
providing values for <b>msg.payload._id</b> and <b>msg.payload._rev</b>
149+
and selecting the <b>remove</b> option for the node.
150+
</p>
151+
</script>
152+
153+
<script type="text/javascript">
154+
RED.nodes.registerType("cloudant out", {
155+
category: "storage-output",
156+
color: "rgb(114, 199, 231)",
157+
defaults: {
158+
cloudant: { type: "cloudant", required: true },
159+
name: { value: "" },
160+
db: { value: "", required: true },
161+
payonly: { value: false },
162+
operation: { value: "insert" }
163+
},
164+
inputs: 1,
165+
outputs: 0,
166+
icon: "cloudant.png",
167+
align: "right",
168+
label: function() {
169+
return this.name || "cloudant";
170+
},
171+
labelStyle: function() {
172+
return this.name ? "node_label_italic" : "";
173+
}
174+
});
175+
</script>

77-cloudant.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/**
2+
* Copyright 2013 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+
module.exports = function(RED) {
18+
"use strict";
19+
var url = require('url');
20+
var querystring = require('querystring');
21+
22+
function CloudantNode(n) {
23+
RED.nodes.createNode(this, n);
24+
25+
this.name = n.name;
26+
this.hostname = n.hostname;
27+
28+
var credentials = RED.nodes.getCredentials(n.id);
29+
if (credentials) {
30+
this.username = credentials.user;
31+
this.password = credentials.password;
32+
}
33+
34+
var parsedUrl = url.parse(this.hostname);
35+
var authUrl = parsedUrl.protocol+'//';
36+
37+
if (this.username && this.password) {
38+
authUrl += this.username + ":" + encodeURIComponent(this.password) + "@";
39+
}
40+
authUrl += parsedUrl.hostname;
41+
42+
this.url = authUrl;
43+
}
44+
45+
RED.nodes.registerType("cloudant", CloudantNode);
46+
47+
RED.httpAdmin.get('/cloudant/:id', function(req,res) {
48+
var credentials = RED.nodes.getCredentials(req.params.id);
49+
50+
if (credentials) {
51+
res.send(JSON.stringify(
52+
{
53+
user: credentials.user,
54+
hasPassword: (credentials.password && credentials.password !== "")
55+
}
56+
));
57+
} else {
58+
res.send(JSON.stringify({}));
59+
}
60+
});
61+
62+
RED.httpAdmin.delete('/cloudant/:id', function(req,res) {
63+
RED.nodes.deleteCredentials(req.params.id);
64+
res.send(200);
65+
});
66+
67+
RED.httpAdmin.post('/cloudant/:id', function(req,res) {
68+
var body = "";
69+
70+
req.on('data', function(chunk) {
71+
body += chunk;
72+
});
73+
74+
req.on('end', function() {
75+
var newCreds = querystring.parse(body);
76+
var credentials = RED.nodes.getCredentials(req.params.id) || {};
77+
78+
if (newCreds.user == null || newCreds.user == "") {
79+
delete credentials.user;
80+
} else {
81+
credentials.user = newCreds.user;
82+
}
83+
84+
if (newCreds.password == "") {
85+
delete credentials.password;
86+
} else {
87+
credentials.password = newCreds.password || credentials.password;
88+
}
89+
90+
RED.nodes.addCredentials(req.params.id, credentials);
91+
res.send(200);
92+
});
93+
});
94+
95+
function CloudantOutNode(n) {
96+
RED.nodes.createNode(this,n);
97+
98+
this.operation = n.operation;
99+
this.payonly = n.payonly || false;
100+
this.database = n.db;
101+
this.cloudant = n.cloudant;
102+
this.cloudantConfig = RED.nodes.getNode(this.cloudant);
103+
104+
if (this.cloudantConfig) {
105+
var node = this;
106+
107+
var nano = require('nano')(node.cloudantConfig.url);
108+
var db = nano.use(node.database);
109+
110+
// check if the database exists and create it if it doesn't
111+
nano.db.list(function(err, body) {
112+
if (err) { node.error(err); }
113+
else {
114+
if (body && body.indexOf(node.database) < 0) {
115+
nano.db.create(node.database, function(err, body) {
116+
if (err) { node.error(err); }
117+
});
118+
}
119+
}
120+
});
121+
122+
node.on("input", function(msg) {
123+
if (node.operation === "insert") {
124+
var msg = node.payonly ? msg.payload : msg;
125+
var root = node.payonly ? "payload" : "msg";
126+
var doc = parseMessage(msg, root);
127+
128+
db.insert(doc, function(err, body) {
129+
if (err) { node.error(err); }
130+
});
131+
}
132+
else if (node.operation === "delete") {
133+
var doc = msg.payload;
134+
135+
if ("_rev" in doc && "_id" in doc) {
136+
db.destroy(doc._id, doc._rev, function(err, body) {
137+
if (err) { node.error(err); }
138+
});
139+
} else {
140+
node.error("_rev and _id are required to delete a document");
141+
}
142+
}
143+
});
144+
145+
} else {
146+
this.error("missing cloudant configuration");
147+
}
148+
149+
function parseMessage(msg, root) {
150+
if (typeof msg !== "object") {
151+
try {
152+
msg = JSON.parse(msg);
153+
154+
// JSON.parse accepts numbers, so make sure that an
155+
// object is return, otherwise create a new one
156+
if (typeof msg !== "object") {
157+
msg = JSON.parse('{"' + root + '":"' + msg + '"}');
158+
}
159+
} catch (e) {
160+
// payload is not in JSON format
161+
msg = JSON.parse('{"' + root + '":"' + msg + '"}');
162+
}
163+
}
164+
return msg;
165+
}
166+
};
167+
168+
RED.nodes.registerType("cloudant out", CloudantOutNode);
169+
}

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
node-red-node-cloudant
2+
======================
3+
A [Node-RED](http://nodered.org) node to write (and read soon...) to a
4+
[Cloudant](http://cloudant.com) database.
5+
6+
Install
7+
-------
8+
Place these files inside your `nodes/` folder.
9+
10+
This node will soon be published to [npm](https://www.npmjs.org/).
11+
12+
Usage
13+
-----
14+
Allows basic access to a [Cloudant](http://cloudant.com) database. Currently
15+
it only have one node that supports `insert` and `delete`
16+
operations.
17+
18+
To **insert** a new document into the database you have the option to store
19+
the entire `msg` object or just the `msg.payload`. If the input value is not
20+
in JSON format, it will transformed before being stored.
21+
22+
For **update** and **delete**, you must pass the `_id` and the `_rev`as part
23+
of the input `msg` object.

icons/cloudant.png

1.3 KB
Loading

0 commit comments

Comments
 (0)