Skip to content

Commit 924b9a9

Browse files
committed
Merge branch 'develop'
* develop: review code and prepare to publish on npm registry pushing new working version
2 parents c43aecd + 6cdd12e commit 924b9a9

File tree

5 files changed

+296
-199
lines changed

5 files changed

+296
-199
lines changed

78-cloudant-cf.html renamed to 77-cloudant-cf.html

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,23 @@
8888
</script>
8989

9090
<script type="text/x-red" data-template-name="cloudant out">
91+
9192
<div class="form-row">
92-
<label for="node-input-cloudant"><i class="fa fa-tag"></i> Account</label>
93+
<label for="node-input-service"><i class="fa fa-folder-close"></i> Service</label>
94+
<select id="node-input-service">
95+
<option value="" disabled></option>
96+
<option value="_ext_"> External service</option>
97+
</select>
98+
</div>
99+
100+
<div class="form-row hide" id="node-input-external-details">
101+
<label for="node-input-cloudant"><i class=" fa fa-bookmark"></i> Server</label>
93102
<input type="text" id="node-input-cloudant">
94103
</div>
95104

96105
<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">
106+
<label for="node-input-database"><i class="fa fa-briefcase"></i> Database</label>
107+
<input type="text" id="node-input-database" placeholder="database">
99108
</div>
100109

101110
</div>
@@ -127,6 +136,7 @@
127136
else $(".node-input-payonly").show();
128137
});
129138
</script>
139+
130140
</script>
131141

132142
<script type="text/x-red" data-help-name="cloudant out">
@@ -150,26 +160,76 @@
150160
</p>
151161
</script>
152162

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" : "";
163+
<script type="text/javascript"> //js functions
164+
(function() {
165+
166+
function oneditprepare() {
167+
var select = $('#node-input-service');
168+
var node = this;
169+
170+
$.getJSON('cloudant/vcap/',function(data) {
171+
var last = select.children().last();
172+
var opts = [];
173+
174+
for (var i=0; i < data.length; i++) {
175+
var selected = node.service == data[i].name;
176+
opts.push(
177+
'<option value="' + data[i].name + '"' + (selected ? " selected":"") + '>' +
178+
data[i].name +
179+
'</option>'
180+
);
181+
}
182+
183+
if (opts.length == 0) {
184+
node.service = "_ext_";
185+
select.find("option").filter(function() {
186+
return $(this).val() == node.service;
187+
}).attr('selected', true);
188+
} else {
189+
last.before(opts.join(""));
190+
}
191+
192+
select.change();
193+
});
194+
195+
select.change(function() {
196+
var service = select.val();
197+
if (service == "_ext_") {
198+
$("#node-input-external-details").show();
199+
} else {
200+
$("#node-input-external-details").hide();
201+
}
202+
});
173203
}
174-
});
204+
205+
function label() {
206+
return this.name || this.database || "cloudant";
207+
}
208+
209+
function validateServer(v) {
210+
return this.service != "_ext_" || v != "_ADD_";
211+
}
212+
213+
RED.nodes.registerType("cloudant out", {
214+
category: "storage-output",
215+
color: "rgb(114, 199, 231)",
216+
defaults: {
217+
service: { value: "", required: true },
218+
cloudant: { type: "cloudant", validate: validateServer},
219+
name: { value: "" },
220+
database: { value: "", required: true },
221+
payonly: { value: false },
222+
operation: { value: "insert" }
223+
},
224+
inputs: 1,
225+
outputs: 0,
226+
icon: "cloudant.png",
227+
align: "right",
228+
label: label,
229+
labelStyle: function() {
230+
return this.name?"node_label_italic":"";
231+
},
232+
oneditprepare: oneditprepare
233+
});
234+
})();
175235
</script>

77-cloudant-cf.js

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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+
var url = require('url');
17+
var querystring = require('querystring');
18+
var cfEnv = require("cf-env");
19+
var RED = require(process.env.NODE_RED_HOME + "/red/red");
20+
21+
var cfCore = cfEnv.getCore();
22+
var services = [];
23+
24+
// load the services bindded to this application
25+
for (var i in cfCore.services) {
26+
// filter the services to include only the Cloudant ones
27+
if (i.match(/^(cloudant)/i)) {
28+
services = services.concat(cfCore.services[i].map(function(v) {
29+
return { name: v.name, label: v.label };
30+
}));
31+
}
32+
}
33+
34+
//
35+
// HTTP endpoints that will be accessed from the HTML file
36+
//
37+
RED.httpAdmin.get('/cloudant/vcap', function(req,res) {
38+
res.send(JSON.stringify(services));
39+
});
40+
41+
// REMINDER: routes are order dependent
42+
RED.httpAdmin.get('/cloudant/:id', function(req,res) {
43+
var credentials = RED.nodes.getCredentials(req.params.id);
44+
45+
if (credentials) {
46+
res.send(JSON.stringify(
47+
{
48+
user: credentials.user,
49+
hasPassword: (credentials.password && credentials.password !== "")
50+
}
51+
));
52+
} else {
53+
res.send(JSON.stringify({}));
54+
}
55+
});
56+
57+
RED.httpAdmin.delete('/cloudant/:id', function(req,res) {
58+
RED.nodes.deleteCredentials(req.params.id);
59+
res.send(200);
60+
});
61+
62+
RED.httpAdmin.post('/cloudant/:id', function(req,res) {
63+
var body = "";
64+
65+
req.on('data', function(chunk) {
66+
body += chunk;
67+
});
68+
69+
req.on('end', function() {
70+
var newCreds = querystring.parse(body);
71+
var credentials = RED.nodes.getCredentials(req.params.id) || {};
72+
73+
if (newCreds.user == null || newCreds.user == "") {
74+
delete credentials.user;
75+
} else {
76+
credentials.user = newCreds.user;
77+
}
78+
79+
if (newCreds.password == "") {
80+
delete credentials.password;
81+
} else {
82+
credentials.password = newCreds.password || credentials.password;
83+
}
84+
85+
RED.nodes.addCredentials(req.params.id, credentials);
86+
res.send(200);
87+
});
88+
});
89+
90+
//
91+
// Create and register nodes
92+
//
93+
function CloudantNode(n) {
94+
RED.nodes.createNode(this, n);
95+
96+
this.name = n.name;
97+
this.hostname = n.hostname;
98+
99+
var credentials = RED.nodes.getCredentials(n.id);
100+
if (credentials) {
101+
this.username = credentials.user;
102+
this.password = credentials.password;
103+
}
104+
105+
var parsedUrl = url.parse(this.hostname);
106+
var authUrl = parsedUrl.protocol+'//';
107+
108+
if (this.username && this.password) {
109+
authUrl += this.username + ":" + encodeURIComponent(this.password) + "@";
110+
}
111+
authUrl += parsedUrl.hostname;
112+
113+
this.url = authUrl;
114+
}
115+
RED.nodes.registerType("cloudant", CloudantNode);
116+
117+
function CloudantOutNode(n) {
118+
RED.nodes.createNode(this,n);
119+
120+
this.operation = n.operation;
121+
this.payonly = n.payonly || false;
122+
this.database = n.database;
123+
this.cloudant = n.cloudant;
124+
125+
if (n.service == "_ext_") {
126+
var cloudantConfig = RED.nodes.getNode(this.cloudant);
127+
if (cloudantConfig) {
128+
this.url = cloudantConfig.url;
129+
}
130+
}
131+
else if (n.service != "") {
132+
var cloudantConfig = cfEnv.getService(n.service);
133+
if (cloudantConfig) {
134+
this.url = cloudantConfig.credentials.url;
135+
}
136+
}
137+
138+
if (this.url) {
139+
var node = this;
140+
141+
var nano = require('nano')(this.url);
142+
var db = nano.use(node.database);
143+
144+
// check if the database exists and create it if it doesn't
145+
nano.db.list(function(err, body) {
146+
if (err) { node.error(err); }
147+
else {
148+
if (body && body.indexOf(node.database) < 0) {
149+
nano.db.create(node.database, function(err, body) {
150+
if (err) { node.error(err); }
151+
});
152+
}
153+
}
154+
});
155+
156+
node.on("input", function(msg) {
157+
if (node.operation === "insert") {
158+
var msg = node.payonly ? msg.payload : msg;
159+
var root = node.payonly ? "payload" : "msg";
160+
var doc = parseMessage(msg, root);
161+
162+
db.insert(doc, function(err, body) {
163+
if (err) { node.error(err); }
164+
});
165+
}
166+
else if (node.operation === "delete") {
167+
var doc = parseMessage(msg.payload || msg, "");
168+
169+
if ("_rev" in doc && "_id" in doc) {
170+
db.destroy(doc._id, doc._rev, function(err, body) {
171+
if (err) { node.error(err); }
172+
});
173+
} else {
174+
node.error("_rev and _id are required to delete a document");
175+
}
176+
}
177+
});
178+
179+
} else {
180+
this.error("missing cloudant configuration");
181+
}
182+
183+
function parseMessage(msg, root) {
184+
if (typeof msg !== "object") {
185+
try {
186+
msg = JSON.parse(msg);
187+
188+
// JSON.parse accepts numbers, so make sure that an
189+
// object is return, otherwise create a new one
190+
if (typeof msg !== "object") {
191+
msg = JSON.parse('{"' + root + '":"' + msg + '"}');
192+
}
193+
} catch (e) {
194+
// payload is not in JSON format
195+
msg = JSON.parse('{"' + root + '":"' + msg + '"}');
196+
}
197+
}
198+
return msg;
199+
}
200+
};
201+
RED.nodes.registerType("cloudant out", CloudantOutNode);

0 commit comments

Comments
 (0)