mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #476 from knolleary/auth
Reorganise how adminApp is setup
This commit is contained in:
commit
def93214de
@ -236,7 +236,12 @@ RED.editor = (function() {
|
|||||||
//TODO: move this to RED.library
|
//TODO: move this to RED.library
|
||||||
var flowName = $("#node-input-filename").val();
|
var flowName = $("#node-input-filename").val();
|
||||||
if (!/^\s*$/.test(flowName)) {
|
if (!/^\s*$/.test(flowName)) {
|
||||||
$.post('library/flows/'+flowName,$("#node-input-filename").attr('nodes'),function() {
|
$.ajax({
|
||||||
|
url:'library/flows/'+flowName,
|
||||||
|
type: "POST",
|
||||||
|
data: $("#node-input-filename").attr('nodes'),
|
||||||
|
contentType: "application/json; charset=utf-8"
|
||||||
|
}).done(function() {
|
||||||
RED.library.loadFlowLibrary();
|
RED.library.loadFlowLibrary();
|
||||||
RED.notify("Saved nodes","success");
|
RED.notify("Saved nodes","success");
|
||||||
});
|
});
|
||||||
|
38
red/api/flows.js
Normal file
38
red/api/flows.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* 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 express = require('express');
|
||||||
|
var fs = require("fs");
|
||||||
|
var events = require("../events");
|
||||||
|
var path = require("path");
|
||||||
|
var util = require("util");
|
||||||
|
|
||||||
|
var redNodes = require("../nodes");
|
||||||
|
var settings = require("../settings");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
get: function(req,res) {
|
||||||
|
res.json(redNodes.getFlows());
|
||||||
|
},
|
||||||
|
post: function(req,res) {
|
||||||
|
var flows = req.body;
|
||||||
|
redNodes.setFlows(flows).then(function() {
|
||||||
|
res.send(204);
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
util.log("[red] Error saving flows : "+err);
|
||||||
|
res.send(500,err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
70
red/api/index.js
Normal file
70
red/api/index.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* 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 express = require("express");
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
var ui = require("./ui");
|
||||||
|
var nodes = require("./nodes");
|
||||||
|
var flows = require("./flows");
|
||||||
|
var library = require("./library");
|
||||||
|
|
||||||
|
var settings = require("../settings");
|
||||||
|
|
||||||
|
var errorHandler = function(err,req,res,next) {
|
||||||
|
//TODO: standardize json response
|
||||||
|
res.send(400,err.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(adminApp) {
|
||||||
|
|
||||||
|
adminApp.use(express.json());
|
||||||
|
|
||||||
|
library.init(adminApp);
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
if (!settings.disableEditor) {
|
||||||
|
adminApp.get("/",ui.ensureSlash);
|
||||||
|
adminApp.get("/icons/:icon",ui.icon);
|
||||||
|
adminApp.get("/settings",ui.settings);
|
||||||
|
adminApp.use("/",ui.editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flows
|
||||||
|
adminApp.get("/flows",flows.get);
|
||||||
|
adminApp.post("/flows",flows.post);
|
||||||
|
|
||||||
|
// Nodes
|
||||||
|
adminApp.get("/nodes",nodes.getAll);
|
||||||
|
adminApp.post("/nodes",nodes.post);
|
||||||
|
|
||||||
|
adminApp.get("/nodes/:id",nodes.get);
|
||||||
|
adminApp.put("/nodes/:id",nodes.put);
|
||||||
|
adminApp.delete("/nodes/:id",nodes.delete);
|
||||||
|
|
||||||
|
// Library
|
||||||
|
adminApp.post(new RegExp("/library/flows\/(.*)"),library.post);
|
||||||
|
adminApp.get("/library/flows",library.getAll);
|
||||||
|
adminApp.get(new RegExp("/library/flows\/(.*)"),library.get);
|
||||||
|
|
||||||
|
|
||||||
|
// Error Handler
|
||||||
|
adminApp.use(errorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
init: init
|
||||||
|
}
|
106
red/api/library.js
Normal file
106
red/api/library.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2013, 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 util = require("util");
|
||||||
|
|
||||||
|
var redApp = null;
|
||||||
|
var storage = require("../storage");
|
||||||
|
|
||||||
|
function createLibrary(type) {
|
||||||
|
if (redApp) {
|
||||||
|
redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),function(req,res) {
|
||||||
|
var path = req.params[1]||"";
|
||||||
|
storage.getLibraryEntry(type,path).then(function(result) {
|
||||||
|
if (typeof result === "string") {
|
||||||
|
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||||
|
res.write(result);
|
||||||
|
res.end();
|
||||||
|
} else {
|
||||||
|
res.json(result);
|
||||||
|
}
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
if (err) {
|
||||||
|
util.log("[red] Error loading library entry '"+path+"' : "+err);
|
||||||
|
if (err.message.indexOf('forbidden') === 0) {
|
||||||
|
res.send(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.send(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
redApp.post(new RegExp("/library/"+type+"\/(.*)"),function(req,res) {
|
||||||
|
var path = req.params[0];
|
||||||
|
var fullBody = '';
|
||||||
|
req.on('data', function(chunk) {
|
||||||
|
fullBody += chunk.toString();
|
||||||
|
});
|
||||||
|
req.on('end', function() {
|
||||||
|
storage.saveLibraryEntry(type,path,req.query,fullBody).then(function() {
|
||||||
|
res.send(204);
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
util.log("[red] Error saving library entry '"+path+"' : "+err);
|
||||||
|
if (err.message.indexOf('forbidden') === 0) {
|
||||||
|
res.send(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.send(500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
init: function(app) {
|
||||||
|
redApp = app;
|
||||||
|
},
|
||||||
|
register: createLibrary,
|
||||||
|
|
||||||
|
getAll: function(req,res) {
|
||||||
|
storage.getAllFlows().then(function(flows) {
|
||||||
|
res.json(flows);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
get: function(req,res) {
|
||||||
|
storage.getFlow(req.params[0]).then(function(data) {
|
||||||
|
res.set('Content-Type', 'application/json');
|
||||||
|
res.send(data);
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
if (err) {
|
||||||
|
util.log("[red] Error loading flow '"+req.params[0]+"' : "+err);
|
||||||
|
if (err.message.indexOf('forbidden') === 0) {
|
||||||
|
res.send(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.send(404);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
post: function(req,res) {
|
||||||
|
var flow = JSON.stringify(req.body);
|
||||||
|
storage.saveFlow(req.params[0],flow).then(function() {
|
||||||
|
res.send(204);
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
util.log("[red] Error loading flow '"+req.params[0]+"' : "+err);
|
||||||
|
if (err.message.indexOf('forbidden') === 0) {
|
||||||
|
res.send(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.send(500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
155
red/api/nodes.js
Normal file
155
red/api/nodes.js
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/**
|
||||||
|
* 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 express = require('express');
|
||||||
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
var when = require('when');
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
var events = require("../events");
|
||||||
|
var redNodes = require("../nodes");
|
||||||
|
var comms = require("../comms");
|
||||||
|
var server = require("../server");
|
||||||
|
|
||||||
|
var settings = require("../settings");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getAll: function(req,res) {
|
||||||
|
if (req.get("accept") == "application/json") {
|
||||||
|
res.json(redNodes.getNodeList());
|
||||||
|
} else {
|
||||||
|
res.send(redNodes.getNodeConfigs());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
post: function(req,res) {
|
||||||
|
if (!settings.available()) {
|
||||||
|
res.send(400,new Error("Settings unavailable").toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var node = req.body;
|
||||||
|
var promise;
|
||||||
|
if (node.file) {
|
||||||
|
promise = redNodes.addNode(node.file).then(server.reportAddedModules);
|
||||||
|
} else if (node.module) {
|
||||||
|
var module = redNodes.getNodeModuleInfo(node.module);
|
||||||
|
if (module) {
|
||||||
|
res.send(400,"Module already loaded");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promise = server.installModule(node.module);
|
||||||
|
} else {
|
||||||
|
res.send(400,"Invalid request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
promise.then(function(info) {
|
||||||
|
res.json(info);
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
if (err.code === 404) {
|
||||||
|
res.send(404);
|
||||||
|
} else {
|
||||||
|
res.send(400,err.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
delete: function(req,res) {
|
||||||
|
if (!settings.available()) {
|
||||||
|
res.send(400,new Error("Settings unavailable").toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var id = req.params.id;
|
||||||
|
var removedNodes = [];
|
||||||
|
try {
|
||||||
|
var node = redNodes.getNodeInfo(id);
|
||||||
|
var promise = null;
|
||||||
|
if (!node) {
|
||||||
|
var module = redNodes.getNodeModuleInfo(id);
|
||||||
|
if (!module) {
|
||||||
|
res.send(404);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
promise = server.uninstallModule(id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
promise = when.resolve([redNodes.removeNode(id)]).then(server.reportRemovedModules);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(function(removedNodes) {
|
||||||
|
res.json(removedNodes);
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
res.send(400,err.toString());
|
||||||
|
});
|
||||||
|
} catch(err) {
|
||||||
|
res.send(400,err.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function(req,res) {
|
||||||
|
var id = req.params.id;
|
||||||
|
var result = null;
|
||||||
|
if (req.get("accept") == "application/json") {
|
||||||
|
result = redNodes.getNodeInfo(id);
|
||||||
|
} else {
|
||||||
|
result = redNodes.getNodeConfig(id);
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
res.send(result);
|
||||||
|
} else {
|
||||||
|
res.send(404);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
put: function(req,res) {
|
||||||
|
if (!settings.available()) {
|
||||||
|
res.send(400,new Error("Settings unavailable").toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var body = req.body;
|
||||||
|
if (!body.hasOwnProperty("enabled")) {
|
||||||
|
res.send(400,"Invalid request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var info;
|
||||||
|
var id = req.params.id;
|
||||||
|
var node = redNodes.getNodeInfo(id);
|
||||||
|
if (!node) {
|
||||||
|
res.send(404);
|
||||||
|
} else if (!node.err && node.enabled === body.enabled) {
|
||||||
|
res.json(node);
|
||||||
|
} else {
|
||||||
|
if (body.enabled) {
|
||||||
|
info = redNodes.enableNode(id);
|
||||||
|
} else {
|
||||||
|
info = redNodes.disableNode(id);
|
||||||
|
}
|
||||||
|
if (info.enabled == body.enabled && !info.err) {
|
||||||
|
comms.publish("node/"+(body.enabled?"enabled":"disabled"),info,false);
|
||||||
|
util.log("[red] "+(body.enabled?"Enabled":"Disabled")+" node types:");
|
||||||
|
for (var i=0;i<info.types.length;i++) {
|
||||||
|
util.log("[red] - "+info.types[i]);
|
||||||
|
}
|
||||||
|
} else if (body.enabled && info.err) {
|
||||||
|
util.log("[red] Failed to enable node:");
|
||||||
|
util.log("[red] - "+info.name+" : "+info.err);
|
||||||
|
}
|
||||||
|
res.json(info);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
res.send(400,err.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,37 +15,29 @@
|
|||||||
**/
|
**/
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var events = require("./events");
|
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
|
|
||||||
var icon_paths = [path.resolve(__dirname + '/../public/icons')];
|
var events = require("../events");
|
||||||
|
var settings = require("../settings");
|
||||||
|
|
||||||
var settings; // settings has to be global, otherwise variable not in scope for express
|
var icon_paths = [path.resolve(__dirname + '/../../public/icons')];
|
||||||
|
var iconCache = {};
|
||||||
|
//TODO: create a default icon
|
||||||
|
var defaultIcon = path.resolve(__dirname + '/../../public/icons/arrow-in.png');
|
||||||
|
|
||||||
events.on("node-icon-dir",function(dir) {
|
events.on("node-icon-dir",function(dir) {
|
||||||
icon_paths.push(path.resolve(dir));
|
icon_paths.push(path.resolve(dir));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
function setupUI(_settings,app) {
|
ensureSlash: function(req,res,next) {
|
||||||
|
|
||||||
settings = _settings;
|
|
||||||
|
|
||||||
// Need to ensure the url ends with a '/' so the static serving works
|
|
||||||
// with relative paths
|
|
||||||
app.get("/",function(req,res) {
|
|
||||||
if (req.originalUrl.slice(-1) != "/") {
|
if (req.originalUrl.slice(-1) != "/") {
|
||||||
res.redirect(req.originalUrl+"/");
|
res.redirect(301,req.originalUrl+"/");
|
||||||
} else {
|
} else {
|
||||||
req.next();
|
next();
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
icon: function(req,res) {
|
||||||
var iconCache = {};
|
|
||||||
//TODO: create a default icon
|
|
||||||
var defaultIcon = path.resolve(__dirname + '/../public/icons/arrow-in.png');
|
|
||||||
|
|
||||||
app.get("/icons/:icon",function(req,res) {
|
|
||||||
if (iconCache[req.params.icon]) {
|
if (iconCache[req.params.icon]) {
|
||||||
res.sendfile(iconCache[req.params.icon]); // if not found, express prints this to the console and serves 404
|
res.sendfile(iconCache[req.params.icon]); // if not found, express prints this to the console and serves 404
|
||||||
} else {
|
} else {
|
||||||
@ -59,19 +51,13 @@ function setupUI(_settings,app) {
|
|||||||
}
|
}
|
||||||
res.sendfile(defaultIcon);
|
res.sendfile(defaultIcon);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
settings: function(req,res) {
|
||||||
app.get("/settings", function(req,res) {
|
|
||||||
var safeSettings = {
|
var safeSettings = {
|
||||||
httpNodeRoot: settings.httpNodeRoot,
|
httpNodeRoot: settings.httpNodeRoot,
|
||||||
version: settings.version
|
version: settings.version
|
||||||
};
|
};
|
||||||
res.json(safeSettings);
|
res.json(safeSettings);
|
||||||
});
|
},
|
||||||
|
editor: express.static(__dirname + '/../../public')
|
||||||
app.use("/",express.static(__dirname + '/../public'));
|
};
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = setupUI;
|
|
117
red/library.js
117
red/library.js
@ -1,117 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2013 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 util = require("util");
|
|
||||||
|
|
||||||
var redApp = null;
|
|
||||||
var storage = null;
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
redApp = require("./server").app;
|
|
||||||
storage = require("./storage");
|
|
||||||
|
|
||||||
// -------- Flow Library --------
|
|
||||||
redApp.post(new RegExp("/library/flows\/(.*)"), function(req,res) {
|
|
||||||
var fullBody = '';
|
|
||||||
req.on('data', function(chunk) {
|
|
||||||
fullBody += chunk.toString();
|
|
||||||
});
|
|
||||||
req.on('end', function() {
|
|
||||||
storage.saveFlow(req.params[0],fullBody).then(function() {
|
|
||||||
res.send(204);
|
|
||||||
}).otherwise(function(err) {
|
|
||||||
util.log("[red] Error loading flow '"+req.params[0]+"' : "+err);
|
|
||||||
if (err.message.indexOf('forbidden') === 0) {
|
|
||||||
res.send(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.send(500);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
redApp.get("/library/flows",function(req,res) {
|
|
||||||
storage.getAllFlows().then(function(flows) {
|
|
||||||
res.json(flows);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
redApp.get(new RegExp("/library/flows\/(.*)"), function(req,res) {
|
|
||||||
storage.getFlow(req.params[0]).then(function(data) {
|
|
||||||
res.set('Content-Type', 'application/json');
|
|
||||||
res.send(data);
|
|
||||||
}).otherwise(function(err) {
|
|
||||||
if (err) {
|
|
||||||
util.log("[red] Error loading flow '"+req.params[0]+"' : "+err);
|
|
||||||
if (err.message.indexOf('forbidden') === 0) {
|
|
||||||
res.send(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.send(404);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ------------------------------
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLibrary(type) {
|
|
||||||
|
|
||||||
redApp.get(new RegExp("/library/"+type+"($|\/(.*))"),function(req,res) {
|
|
||||||
var path = req.params[1]||"";
|
|
||||||
storage.getLibraryEntry(type,path).then(function(result) {
|
|
||||||
if (typeof result === "string") {
|
|
||||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
|
||||||
res.write(result);
|
|
||||||
res.end();
|
|
||||||
} else {
|
|
||||||
res.json(result);
|
|
||||||
}
|
|
||||||
}).otherwise(function(err) {
|
|
||||||
if (err) {
|
|
||||||
util.log("[red] Error loading library entry '"+path+"' : "+err);
|
|
||||||
if (err.message.indexOf('forbidden') === 0) {
|
|
||||||
res.send(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.send(404);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
redApp.post(new RegExp("/library/"+type+"\/(.*)"),function(req,res) {
|
|
||||||
var path = req.params[0];
|
|
||||||
var fullBody = '';
|
|
||||||
req.on('data', function(chunk) {
|
|
||||||
fullBody += chunk.toString();
|
|
||||||
});
|
|
||||||
req.on('end', function() {
|
|
||||||
storage.saveLibraryEntry(type,path,req.query,fullBody).then(function() {
|
|
||||||
res.send(204);
|
|
||||||
}).otherwise(function(err) {
|
|
||||||
util.log("[red] Error saving library entry '"+path+"' : "+err);
|
|
||||||
if (err.message.indexOf('forbidden') === 0) {
|
|
||||||
res.send(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.send(500);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.init = init;
|
|
||||||
module.exports.register = createLibrary;
|
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var server = require("./server");
|
var server = require("./server");
|
||||||
var nodes = require("./nodes");
|
var nodes = require("./nodes");
|
||||||
var library = require("./library");
|
var library = require("./api/library");
|
||||||
var comms = require("./comms");
|
var comms = require("./comms");
|
||||||
var log = require("./log");
|
var log = require("./log");
|
||||||
var util = require("./util");
|
var util = require("./util");
|
||||||
@ -36,14 +36,13 @@ var RED = {
|
|||||||
userSettings.version = this.version();
|
userSettings.version = this.version();
|
||||||
settings.init(userSettings);
|
settings.init(userSettings);
|
||||||
server.init(httpServer,settings);
|
server.init(httpServer,settings);
|
||||||
library.init();
|
|
||||||
return server.app;
|
return server.app;
|
||||||
},
|
},
|
||||||
|
|
||||||
start: server.start,
|
start: server.start,
|
||||||
stop: server.stop,
|
stop: server.stop,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
library: library,
|
library: { register: library.register },
|
||||||
credentials: credentials,
|
credentials: credentials,
|
||||||
events: events,
|
events: events,
|
||||||
log: log,
|
log: log,
|
||||||
|
355
red/server.js
355
red/server.js
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013 IBM Corp.
|
* Copyright 2013, 2014 IBM Corp.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,9 +17,8 @@
|
|||||||
var express = require('express');
|
var express = require('express');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var when = require('when');
|
var when = require('when');
|
||||||
var exec = require('child_process').exec;
|
var child_process = require('child_process');
|
||||||
|
|
||||||
var createUI = require("./ui");
|
|
||||||
var redNodes = require("./nodes");
|
var redNodes = require("./nodes");
|
||||||
var comms = require("./comms");
|
var comms = require("./comms");
|
||||||
var storage = require("./storage");
|
var storage = require("./storage");
|
||||||
@ -29,7 +28,7 @@ var nodeApp = null;
|
|||||||
var server = null;
|
var server = null;
|
||||||
var settings = null;
|
var settings = null;
|
||||||
|
|
||||||
function createServer(_server,_settings) {
|
function init(_server,_settings) {
|
||||||
server = _server;
|
server = _server;
|
||||||
settings = _settings;
|
settings = _settings;
|
||||||
|
|
||||||
@ -37,260 +36,11 @@ function createServer(_server,_settings) {
|
|||||||
|
|
||||||
nodeApp = express();
|
nodeApp = express();
|
||||||
app = express();
|
app = express();
|
||||||
|
|
||||||
if (settings.httpAdminRoot !== false) {
|
if (settings.httpAdminRoot !== false) {
|
||||||
|
require("./api").init(app);
|
||||||
|
|
||||||
if (!settings.disableEditor) {
|
|
||||||
createUI(settings,app);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.get("/flows",function(req,res) {
|
|
||||||
res.json(redNodes.getFlows());
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/flows",
|
|
||||||
express.json(),
|
|
||||||
function(req,res) {
|
|
||||||
var flows = req.body;
|
|
||||||
redNodes.setFlows(flows).then(function() {
|
|
||||||
res.send(204);
|
|
||||||
}).otherwise(function(err) {
|
|
||||||
util.log("[red] Error saving flows : "+err);
|
|
||||||
res.send(500,err.message);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(error,req,res,next) {
|
|
||||||
res.send(400,"Invalid Flow");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get("/nodes",function(req,res) {
|
|
||||||
if (req.get("accept") == "application/json") {
|
|
||||||
res.json(redNodes.getNodeList());
|
|
||||||
} else {
|
|
||||||
res.send(redNodes.getNodeConfigs());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/nodes",
|
|
||||||
express.json(),
|
|
||||||
function(req,res) {
|
|
||||||
if (!settings.available()) {
|
|
||||||
res.send(400,new Error("Settings unavailable").toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var node = req.body;
|
|
||||||
var promise;
|
|
||||||
if (node.file) {
|
|
||||||
promise = redNodes.addNode(node.file).then(reportAddedModules);
|
|
||||||
} else if (node.module) {
|
|
||||||
var module = redNodes.getNodeModuleInfo(node.module);
|
|
||||||
if (module) {
|
|
||||||
res.send(400,"Module already loaded");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
promise = installModule(node.module);
|
|
||||||
} else {
|
|
||||||
res.send(400,"Invalid request");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
promise.then(function(info) {
|
|
||||||
res.json(info);
|
|
||||||
}).otherwise(function(err) {
|
|
||||||
if (err.code === 404) {
|
|
||||||
res.send(404);
|
|
||||||
} else {
|
|
||||||
res.send(400,err.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(err,req,res,next) {
|
|
||||||
console.log(err.toString());
|
|
||||||
res.send(400,err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
app.delete("/nodes/:id",
|
|
||||||
function(req,res) {
|
|
||||||
if (!settings.available()) {
|
|
||||||
res.send(400,new Error("Settings unavailable").toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var id = req.params.id;
|
|
||||||
var removedNodes = [];
|
|
||||||
try {
|
|
||||||
var node = redNodes.getNodeInfo(id);
|
|
||||||
var promise = null;
|
|
||||||
if (!node) {
|
|
||||||
var module = redNodes.getNodeModuleInfo(id);
|
|
||||||
if (!module) {
|
|
||||||
res.send(404);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
promise = uninstallModule(id);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
promise = when.resolve([redNodes.removeNode(id)]).then(reportRemovedModules);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then(function(removedNodes) {
|
|
||||||
res.json(removedNodes);
|
|
||||||
}).otherwise(function(err) {
|
|
||||||
console.log(err.stack);
|
|
||||||
res.send(400,err.toString());
|
|
||||||
});
|
|
||||||
} catch(err) {
|
|
||||||
res.send(400,err.toString());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(err,req,res,next) {
|
|
||||||
res.send(400,err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
app.get("/nodes/:id", function(req,res) {
|
|
||||||
var id = req.params.id;
|
|
||||||
var result = null;
|
|
||||||
if (req.get("accept") == "application/json") {
|
|
||||||
result = redNodes.getNodeInfo(id);
|
|
||||||
} else {
|
|
||||||
result = redNodes.getNodeConfig(id);
|
|
||||||
}
|
|
||||||
if (result) {
|
|
||||||
res.send(result);
|
|
||||||
} else {
|
|
||||||
res.send(404);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.put("/nodes/:id",
|
|
||||||
express.json(),
|
|
||||||
function(req,res) {
|
|
||||||
if (!settings.available()) {
|
|
||||||
res.send(400,new Error("Settings unavailable").toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var body = req.body;
|
|
||||||
if (!body.hasOwnProperty("enabled")) {
|
|
||||||
res.send(400,"Invalid request");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var info;
|
|
||||||
var id = req.params.id;
|
|
||||||
var node = redNodes.getNodeInfo(id);
|
|
||||||
if (!node) {
|
|
||||||
res.send(404);
|
|
||||||
} else if (!node.err && node.enabled === body.enabled) {
|
|
||||||
res.json(node);
|
|
||||||
} else {
|
|
||||||
if (body.enabled) {
|
|
||||||
info = redNodes.enableNode(id);
|
|
||||||
} else {
|
|
||||||
info = redNodes.disableNode(id);
|
|
||||||
}
|
|
||||||
if (info.enabled == body.enabled && !info.err) {
|
|
||||||
comms.publish("node/"+(body.enabled?"enabled":"disabled"),info,false);
|
|
||||||
util.log("[red] "+(body.enabled?"Enabled":"Disabled")+" node types:");
|
|
||||||
for (var i=0;i<info.types.length;i++) {
|
|
||||||
util.log("[red] - "+info.types[i]);
|
|
||||||
}
|
|
||||||
} else if (body.enabled && info.err) {
|
|
||||||
util.log("[red] Failed to enable node:");
|
|
||||||
util.log("[red] - "+info.name+" : "+info.err);
|
|
||||||
}
|
|
||||||
res.json(info);
|
|
||||||
}
|
|
||||||
} catch(err) {
|
|
||||||
res.send(400,err.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function reportAddedModules(info) {
|
|
||||||
comms.publish("node/added",info,false);
|
|
||||||
if (info.length > 0) {
|
|
||||||
util.log("[red] Added node types:");
|
|
||||||
for (var i=0;i<info.length;i++) {
|
|
||||||
for (var j=0;j<info[i].types.length;j++) {
|
|
||||||
util.log("[red] - "+
|
|
||||||
(info[i].module?info[i].module+":":"")+
|
|
||||||
info[i].types[j]+
|
|
||||||
(info[i].err?" : "+info[i].err:"")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reportRemovedModules(removedNodes) {
|
|
||||||
comms.publish("node/removed",removedNodes,false);
|
|
||||||
util.log("[red] Removed node types:");
|
|
||||||
for (var j=0;j<removedNodes.length;j++) {
|
|
||||||
for (var i=0;i<removedNodes[j].types.length;i++) {
|
|
||||||
util.log("[red] - "+(removedNodes[i].module?removedNodes[i].module+":":"")+removedNodes[j].types[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return removedNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function installModule(module) {
|
|
||||||
//TODO: ensure module is 'safe'
|
|
||||||
return when.promise(function(resolve,reject) {
|
|
||||||
if (/[\s;]/.test(module)) {
|
|
||||||
reject(new Error("Invalid module name"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
util.log("[red] Installing module: "+module);
|
|
||||||
var child = exec('npm install --production '+module, function(err, stdin, stdout) {
|
|
||||||
if (err) {
|
|
||||||
var lookFor404 = new RegExp(" 404 .*"+module+"$","m");
|
|
||||||
if (lookFor404.test(stdout)) {
|
|
||||||
util.log("[red] Installation of module "+module+" failed: module not found");
|
|
||||||
var e = new Error();
|
|
||||||
e.code = 404;
|
|
||||||
reject(e);
|
|
||||||
} else {
|
|
||||||
util.log("[red] Installation of module "+module+" failed:");
|
|
||||||
util.log("------------------------------------------");
|
|
||||||
console.log(err.toString());
|
|
||||||
util.log("------------------------------------------");
|
|
||||||
reject(new Error("Install failed"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
util.log("[red] Installed module: "+module);
|
|
||||||
resolve(redNodes.addModule(module).then(reportAddedModules));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function uninstallModule(module) {
|
|
||||||
var list = redNodes.removeModule(module);
|
|
||||||
return when.promise(function(resolve,reject) {
|
|
||||||
if (/[\s;]/.test(module)) {
|
|
||||||
reject(new Error("Invalid module name"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
util.log("[red] Removing module: "+module);
|
|
||||||
var child = exec('npm remove '+module, function(err, stdin, stdout) {
|
|
||||||
if (err) {
|
|
||||||
util.log("[red] Removal of module "+module+" failed:");
|
|
||||||
util.log("------------------------------------------");
|
|
||||||
console.log(err.toString());
|
|
||||||
util.log("------------------------------------------");
|
|
||||||
reject(new Error("Removal failed"));
|
|
||||||
} else {
|
|
||||||
util.log("[red] Removed module: "+module);
|
|
||||||
reportRemovedModules(list);
|
|
||||||
resolve(list);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
var defer = when.defer();
|
var defer = when.defer();
|
||||||
@ -359,15 +109,106 @@ function start() {
|
|||||||
return defer.promise;
|
return defer.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function reportAddedModules(info) {
|
||||||
|
comms.publish("node/added",info,false);
|
||||||
|
if (info.length > 0) {
|
||||||
|
util.log("[red] Added node types:");
|
||||||
|
for (var i=0;i<info.length;i++) {
|
||||||
|
for (var j=0;j<info[i].types.length;j++) {
|
||||||
|
util.log("[red] - "+
|
||||||
|
(info[i].module?info[i].module+":":"")+
|
||||||
|
info[i].types[j]+
|
||||||
|
(info[i].err?" : "+info[i].err:"")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportRemovedModules(removedNodes) {
|
||||||
|
comms.publish("node/removed",removedNodes,false);
|
||||||
|
util.log("[red] Removed node types:");
|
||||||
|
for (var j=0;j<removedNodes.length;j++) {
|
||||||
|
for (var i=0;i<removedNodes[j].types.length;i++) {
|
||||||
|
util.log("[red] - "+(removedNodes[i].module?removedNodes[i].module+":":"")+removedNodes[j].types[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function installModule(module) {
|
||||||
|
//TODO: ensure module is 'safe'
|
||||||
|
return when.promise(function(resolve,reject) {
|
||||||
|
if (/[\s;]/.test(module)) {
|
||||||
|
reject(new Error("Invalid module name"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
util.log("[red] Installing module: "+module);
|
||||||
|
var child = child_process.exec('npm install --production '+module, function(err, stdin, stdout) {
|
||||||
|
if (err) {
|
||||||
|
var lookFor404 = new RegExp(" 404 .*"+module+"$","m");
|
||||||
|
if (lookFor404.test(stdout)) {
|
||||||
|
util.log("[red] Installation of module "+module+" failed: module not found");
|
||||||
|
var e = new Error();
|
||||||
|
e.code = 404;
|
||||||
|
reject(e);
|
||||||
|
} else {
|
||||||
|
util.log("[red] Installation of module "+module+" failed:");
|
||||||
|
util.log("------------------------------------------");
|
||||||
|
console.log(err.toString());
|
||||||
|
util.log("------------------------------------------");
|
||||||
|
reject(new Error("Install failed"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
util.log("[red] Installed module: "+module);
|
||||||
|
resolve(redNodes.addModule(module).then(reportAddedModules));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstallModule(module) {
|
||||||
|
return when.promise(function(resolve,reject) {
|
||||||
|
if (/[\s;]/.test(module)) {
|
||||||
|
reject(new Error("Invalid module name"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var list = redNodes.removeModule(module);
|
||||||
|
util.log("[red] Removing module: "+module);
|
||||||
|
var child = child_process.exec('npm remove '+module, function(err, stdin, stdout) {
|
||||||
|
if (err) {
|
||||||
|
util.log("[red] Removal of module "+module+" failed:");
|
||||||
|
util.log("------------------------------------------");
|
||||||
|
console.log(err.toString());
|
||||||
|
util.log("------------------------------------------");
|
||||||
|
reject(new Error("Removal failed"));
|
||||||
|
} else {
|
||||||
|
util.log("[red] Removed module: "+module);
|
||||||
|
reportRemovedModules(list);
|
||||||
|
resolve(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function stop() {
|
function stop() {
|
||||||
redNodes.stopFlows();
|
redNodes.stopFlows();
|
||||||
comms.stop();
|
comms.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: createServer,
|
init: init,
|
||||||
start: start,
|
start: start,
|
||||||
stop: stop
|
stop: stop,
|
||||||
|
|
||||||
|
reportAddedModules: reportAddedModules,
|
||||||
|
reportRemovedModules: reportRemovedModules,
|
||||||
|
installModule: installModule,
|
||||||
|
uninstallModule: uninstallModule
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.__defineGetter__("app", function() { return app });
|
module.exports.__defineGetter__("app", function() { return app });
|
||||||
|
@ -75,9 +75,17 @@ var persistentSettings = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reset: function() {
|
reset: function() {
|
||||||
|
for (var i in userSettings) {
|
||||||
|
if (userSettings.hasOwnProperty(i)) {
|
||||||
|
delete persistentSettings[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
userSettings = null;
|
userSettings = null;
|
||||||
globalSettings = null;
|
globalSettings = null;
|
||||||
storage = null;
|
storage = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
91
test/red/api/flows_spec.js
Normal file
91
test/red/api/flows_spec.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* 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 request = require('supertest');
|
||||||
|
var express = require('express');
|
||||||
|
var sinon = require('sinon');
|
||||||
|
var when = require('when');
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
var redNodes = require("../../../red/nodes");
|
||||||
|
|
||||||
|
var flows = require("../../../red/api/flows");
|
||||||
|
|
||||||
|
describe("flows api", function() {
|
||||||
|
|
||||||
|
var app;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
app.get("/flows",flows.get);
|
||||||
|
app.post("/flows",flows.post);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns flow', function(done) {
|
||||||
|
var getFlows = sinon.stub(redNodes,'getFlows', function() {
|
||||||
|
return [1,2,3];
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.get('/flows')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
getFlows.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.body.should.be.an.Array.and.have.lengthOf(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets flows', function(done) {
|
||||||
|
var setFlows = sinon.stub(redNodes,'setFlows', function() {
|
||||||
|
return when.resolve();
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.post('/flows')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(204)
|
||||||
|
.end(function(err,res) {
|
||||||
|
setFlows.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns error when set fails', function(done) {
|
||||||
|
var setFlows = sinon.stub(redNodes,'setFlows', function() {
|
||||||
|
return when.reject(new Error("test error"));
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.post('/flows')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(500)
|
||||||
|
.end(function(err,res) {
|
||||||
|
setFlows.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.text.should.eql("test error");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
93
test/red/api/index_spec.js
Normal file
93
test/red/api/index_spec.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* 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 request = require("supertest");
|
||||||
|
var express = require("express");
|
||||||
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
|
||||||
|
var settings = require("../../../red/settings");
|
||||||
|
var api = require("../../../red/api");
|
||||||
|
|
||||||
|
|
||||||
|
describe("api index", function() {
|
||||||
|
var app;
|
||||||
|
|
||||||
|
describe("disables editor", function() {
|
||||||
|
before(function() {
|
||||||
|
settings.init({disableEditor:true});
|
||||||
|
app = express();
|
||||||
|
api.init(app);
|
||||||
|
});
|
||||||
|
after(function() {
|
||||||
|
settings.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not serve the editor', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/")
|
||||||
|
.expect(404,done)
|
||||||
|
});
|
||||||
|
it('does not serve icons', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/icons/default.png")
|
||||||
|
.expect(404,done)
|
||||||
|
});
|
||||||
|
it('does not serve settings', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/settings")
|
||||||
|
.expect(404,done)
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("enables editor", function() {
|
||||||
|
before(function() {
|
||||||
|
settings.init({disableEditor:false});
|
||||||
|
app = express();
|
||||||
|
api.init(app);
|
||||||
|
});
|
||||||
|
after(function() {
|
||||||
|
settings.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('serves the editor', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/")
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
// Index page should probably mention Node-RED somewhere
|
||||||
|
res.text.indexOf("Node-RED").should.not.eql(-1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('serves icons', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/icons/inject.png")
|
||||||
|
.expect("Content-Type", /image\/png/)
|
||||||
|
.expect(200,done)
|
||||||
|
});
|
||||||
|
it('serves settings', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/settings")
|
||||||
|
.expect(200,done)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -15,52 +15,68 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var sinon = require('sinon');
|
|
||||||
var request = require('supertest');
|
var request = require('supertest');
|
||||||
var http = require('http');
|
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
|
|
||||||
var fs = require('fs-extra');
|
|
||||||
var path = require('path');
|
|
||||||
var when = require('when');
|
var when = require('when');
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
var RED = require("../../red/red.js");
|
var RED = require("../../../red/red.js");
|
||||||
var server = require("../../red/server.js");
|
var storage = require("../../../red/storage");
|
||||||
var nodes = require("../../red/nodes");
|
var library = require("../../../red/api/library");
|
||||||
|
|
||||||
describe("library", function() {
|
describe("library api", function() {
|
||||||
var userDir = path.join(__dirname,".testUserHome");
|
|
||||||
before(function(done) {
|
function initStorage(_flows,_libraryEntries) {
|
||||||
fs.remove(userDir,function(err) {
|
var flows = _flows;
|
||||||
fs.mkdir(userDir,function() {
|
var libraryEntries = _libraryEntries;
|
||||||
sinon.stub(nodes, 'load', function() {
|
storage.init({
|
||||||
return when.promise(function(resolve,reject){
|
storageModule: {
|
||||||
resolve([]);
|
init: function() {
|
||||||
});
|
return when.resolve();
|
||||||
});
|
},
|
||||||
RED.init(http.createServer(function(req,res){app(req,res)}),
|
getAllFlows: function() {
|
||||||
{userDir: userDir});
|
return when.resolve(flows);
|
||||||
server.start().then(function () { done(); });
|
},
|
||||||
});
|
getFlow: function(fn) {
|
||||||
|
if (flows[fn]) {
|
||||||
|
return when.resolve(flows[fn]);
|
||||||
|
} else {
|
||||||
|
return when.reject();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveFlow: function(fn,data) {
|
||||||
|
flows[fn] = data;
|
||||||
|
return when.resolve();
|
||||||
|
},
|
||||||
|
getLibraryEntry: function(type,path) {
|
||||||
|
if (libraryEntries[type] && libraryEntries[type][path]) {
|
||||||
|
return when.resolve(libraryEntries[type][path]);
|
||||||
|
} else {
|
||||||
|
return when.reject();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveLibraryEntry: function(type,path,meta,body) {
|
||||||
|
libraryEntries[type][path] = body;
|
||||||
|
return when.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
after(function(done) {
|
|
||||||
fs.remove(userDir,done);
|
|
||||||
server.stop();
|
|
||||||
nodes.load.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function(done) {
|
|
||||||
fs.remove(userDir,function(err) {
|
|
||||||
fs.mkdir(userDir,done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("flows", function() {
|
describe("flows", function() {
|
||||||
|
var app;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
app.get("/library/flows",library.getAll);
|
||||||
|
app.post(new RegExp("/library/flows\/(.*)"),library.post);
|
||||||
|
app.get(new RegExp("/library/flows\/(.*)"),library.get);
|
||||||
|
});
|
||||||
it('returns empty result', function(done) {
|
it('returns empty result', function(done) {
|
||||||
request(RED.httpAdmin)
|
initStorage({});
|
||||||
|
request(app)
|
||||||
.get('/library/flows')
|
.get('/library/flows')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
@ -68,28 +84,32 @@ describe("library", function() {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
res.body.should.not.have.property('f');
|
res.body.should.not.have.property('f');
|
||||||
|
res.body.should.not.have.property('d');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns 404 for non-existent entry', function(done) {
|
it('returns 404 for non-existent entry', function(done) {
|
||||||
request(RED.httpAdmin)
|
initStorage({});
|
||||||
|
request(app)
|
||||||
.get('/library/flows/foo')
|
.get('/library/flows/foo')
|
||||||
.expect(404)
|
.expect(404)
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('can store and retrieve item', function(done) {
|
it('can store and retrieve item', function(done) {
|
||||||
|
initStorage({});
|
||||||
var flow = '[]';
|
var flow = '[]';
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.post('/library/flows/foo')
|
.post('/library/flows/foo')
|
||||||
.set('Content-Type', 'text/plain')
|
.set('Content-Type', 'application/json')
|
||||||
.send(flow)
|
.send(flow)
|
||||||
.expect(204).end(function (err, res) {
|
.expect(204).end(function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.get('/library/flows/foo')
|
.get('/library/flows/foo')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
@ -101,55 +121,57 @@ describe("library", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('lists a stored item', function(done) {
|
it('lists a stored item', function(done) {
|
||||||
request(RED.httpAdmin)
|
initStorage({f:["bar"]});
|
||||||
.post('/library/flows/bar')
|
request(app)
|
||||||
.expect(204)
|
.get('/library/flows')
|
||||||
.end(function () {
|
.expect(200)
|
||||||
request(RED.httpAdmin)
|
.end(function(err,res) {
|
||||||
.get('/library/flows')
|
if (err) {
|
||||||
.expect(200)
|
throw err;
|
||||||
.end(function(err,res) {
|
}
|
||||||
if (err) {
|
res.body.should.have.property('f');
|
||||||
throw err;
|
should.deepEqual(res.body.f,['bar']);
|
||||||
}
|
done();
|
||||||
res.body.should.have.property('f');
|
|
||||||
should.deepEqual(res.body.f,['bar']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
it('returns 403 for malicious get attempt', function(done) {
|
||||||
|
initStorage({});
|
||||||
// without the userDir override the malicious url would be
|
// without the userDir override the malicious url would be
|
||||||
// http://127.0.0.1:1880/library/flows/../../package to
|
// http://127.0.0.1:1880/library/flows/../../package to
|
||||||
// obtain package.json from the node-red root.
|
// obtain package.json from the node-red root.
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.get('/library/flows/../../../../../package')
|
.get('/library/flows/../../../../../package')
|
||||||
.expect(403)
|
.expect(403)
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
it('returns 403 for malicious post attempt', function(done) {
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
initStorage({});
|
||||||
// without the userDir override the malicious url would be
|
// without the userDir override the malicious url would be
|
||||||
// http://127.0.0.1:1880/library/flows/../../package to
|
// http://127.0.0.1:1880/library/flows/../../package to
|
||||||
// obtain package.json from the node-red root.
|
// obtain package.json from the node-red root.
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.post('/library/flows/../../../../../package')
|
.post('/library/flows/../../../../../package')
|
||||||
.expect(403)
|
.expect(403)
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("type", function() {
|
describe("type", function() {
|
||||||
|
var app;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
RED.library.register('test');
|
app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
library.init(app);
|
||||||
|
RED.library.register("test");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns empty result', function(done) {
|
it('returns empty result', function(done) {
|
||||||
request(RED.httpAdmin)
|
initStorage({},{'test':{"":[]}});
|
||||||
|
request(app)
|
||||||
.get('/library/test')
|
.get('/library/test')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
@ -160,17 +182,19 @@ describe("library", function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns 404 for non-existent entry', function(done) {
|
it('returns 404 for non-existent entry', function(done) {
|
||||||
request(RED.httpAdmin)
|
initStorage({},{});
|
||||||
|
request(app)
|
||||||
.get('/library/test/foo')
|
.get('/library/test/foo')
|
||||||
.expect(404)
|
.expect(404)
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can store and retrieve item', function(done) {
|
it('can store and retrieve item', function(done) {
|
||||||
|
initStorage({},{'test':{}});
|
||||||
var flow = '[]';
|
var flow = '[]';
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.post('/library/test/foo')
|
.post('/library/test/foo')
|
||||||
.set('Content-Type', 'text/plain')
|
.set('Content-Type', 'text/plain')
|
||||||
.send(flow)
|
.send(flow)
|
||||||
@ -178,7 +202,7 @@ describe("library", function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.get('/library/test/foo')
|
.get('/library/test/foo')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err,res) {
|
.end(function(err,res) {
|
||||||
@ -190,48 +214,46 @@ describe("library", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('lists a stored item', function(done) {
|
it('lists a stored item', function(done) {
|
||||||
request(RED.httpAdmin)
|
initStorage({},{'test':{'':['abc','def']}});
|
||||||
.post('/library/test/bar')
|
request(app)
|
||||||
.expect(204)
|
.get('/library/test')
|
||||||
.end(function () {
|
.expect(200)
|
||||||
request(RED.httpAdmin)
|
.end(function(err,res) {
|
||||||
.get('/library/test')
|
if (err) {
|
||||||
.expect(200)
|
throw err;
|
||||||
.end(function(err,res) {
|
}
|
||||||
if (err) {
|
// This response isn't strictly accurate - but it
|
||||||
throw err;
|
// verifies the api returns what storage gave it
|
||||||
}
|
should.deepEqual(res.body,['abc','def']);
|
||||||
should.deepEqual(res.body,[{ fn: 'bar'}]);
|
done();
|
||||||
done();
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
it('returns 403 for malicious access attempt', function(done) {
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.get('/library/test/../../../../../../../../../../etc/passwd')
|
.get('/library/test/../../../../../../../../../../etc/passwd')
|
||||||
.expect(403)
|
.expect(403)
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
it('returns 403 for malicious access attempt', function(done) {
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
|
.get('/library/test/..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\etc\\passwd')
|
||||||
.expect(403)
|
.expect(403)
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns 403 for malicious access attempt', function(done) {
|
it('returns 403 for malicious access attempt', function(done) {
|
||||||
request(RED.httpAdmin)
|
request(app)
|
||||||
.post('/library/test/../../../../../../../../../../etc/passwd')
|
.post('/library/test/../../../../../../../../../../etc/passwd')
|
||||||
.set('Content-Type', 'text/plain')
|
.set('Content-Type', 'text/plain')
|
||||||
.send('root:x:0:0:root:/root:/usr/bin/tclsh')
|
.send('root:x:0:0:root:/root:/usr/bin/tclsh')
|
||||||
.expect(403)
|
.expect(403)
|
||||||
.end(done);
|
.end(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
592
test/red/api/nodes_spec.js
Normal file
592
test/red/api/nodes_spec.js
Normal file
@ -0,0 +1,592 @@
|
|||||||
|
/**
|
||||||
|
* 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 request = require('supertest');
|
||||||
|
var express = require('express');
|
||||||
|
var sinon = require('sinon');
|
||||||
|
var when = require('when');
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
var redNodes = require("../../../red/nodes");
|
||||||
|
var server = require("../../../red/server");
|
||||||
|
var settings = require("../../../red/settings");
|
||||||
|
|
||||||
|
var nodes = require("../../../red/api/nodes");
|
||||||
|
|
||||||
|
describe("nodes api", function() {
|
||||||
|
|
||||||
|
var app;
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
app.get("/nodes",nodes.getAll);
|
||||||
|
app.post("/nodes",nodes.post);
|
||||||
|
app.get("/nodes/:id",nodes.get);
|
||||||
|
app.put("/nodes/:id",nodes.put);
|
||||||
|
app.delete("/nodes/:id",nodes.delete);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('get nodes', function() {
|
||||||
|
it('returns node list', function(done) {
|
||||||
|
var getNodeList = sinon.stub(redNodes,'getNodeList', function() {
|
||||||
|
return [1,2,3];
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.get('/nodes')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
getNodeList.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.body.should.be.an.Array.and.have.lengthOf(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns node configs', function(done) {
|
||||||
|
var getNodeConfigs = sinon.stub(redNodes,'getNodeConfigs', function() {
|
||||||
|
return "<script></script>";
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.get('/nodes')
|
||||||
|
.set('Accept', 'text/html')
|
||||||
|
.expect(200)
|
||||||
|
.expect("<script></script>")
|
||||||
|
.end(function(err,res) {
|
||||||
|
getNodeConfigs.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an individual node info', function(done) {
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo', function(id) {
|
||||||
|
return {"123":{id:"123"}}[id];
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.get('/nodes/123')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
getNodeInfo.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.body.should.have.property("id","123");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an individual node configs', function(done) {
|
||||||
|
var getNodeConfig = sinon.stub(redNodes,'getNodeConfig', function(id) {
|
||||||
|
return {"123":"<script></script>"}[id];
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.get('/nodes/123')
|
||||||
|
.set('Accept', 'text/html')
|
||||||
|
.expect(200)
|
||||||
|
.expect("<script></script>")
|
||||||
|
.end(function(err,res) {
|
||||||
|
getNodeConfig.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 404 for unknown node', function(done) {
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo', function(id) {
|
||||||
|
return {"123":{id:"123"}}[id];
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.get('/nodes/456')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(404)
|
||||||
|
.end(function(err,res) {
|
||||||
|
getNodeInfo.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('install', function() {
|
||||||
|
|
||||||
|
it('returns 400 if settings are unavailable', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.post('/nodes')
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 400 if request is invalid', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.post('/nodes')
|
||||||
|
.send({})
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('by module', function() {
|
||||||
|
it('installs the module and returns node info', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeModuleInfo = sinon.stub(redNodes,'getNodeModuleInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
var installModule = sinon.stub(server,'installModule', function() {
|
||||||
|
return when.resolve({id:"123"});
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.post('/nodes')
|
||||||
|
.send({module: 'foo'})
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeModuleInfo.restore();
|
||||||
|
installModule.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.body.should.have.property("id","123");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails the install if already installed', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeModuleInfo = sinon.stub(redNodes,'getNodeModuleInfo',function(id) {
|
||||||
|
return {id:"123"};
|
||||||
|
});
|
||||||
|
var installModule = sinon.stub(server,'installModule', function() {
|
||||||
|
return when.resolve({id:"123"});
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.post('/nodes')
|
||||||
|
.send({module: 'foo'})
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeModuleInfo.restore();
|
||||||
|
installModule.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails the install if module error', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeModuleInfo = sinon.stub(redNodes,'getNodeModuleInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
var installModule = sinon.stub(server,'installModule', function() {
|
||||||
|
return when.reject(new Error("test error"));
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.post('/nodes')
|
||||||
|
.send({module: 'foo'})
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeModuleInfo.restore();
|
||||||
|
installModule.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.text.should.equal("Error: test error");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('fails the install if module not found', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeModuleInfo = sinon.stub(redNodes,'getNodeModuleInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
var installModule = sinon.stub(server,'installModule', function() {
|
||||||
|
var err = new Error("test error");
|
||||||
|
err.code = 404;
|
||||||
|
return when.reject(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.post('/nodes')
|
||||||
|
.send({module: 'foo'})
|
||||||
|
.expect(404)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeModuleInfo.restore();
|
||||||
|
installModule.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('delete', function() {
|
||||||
|
it('returns 400 if settings are unavailable', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.del('/nodes/123')
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('by module', function() {
|
||||||
|
it('uninstalls the module and returns node info', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
var getNodeModuleInfo = sinon.stub(redNodes,'getNodeModuleInfo',function(id) {
|
||||||
|
return {id:"123"};
|
||||||
|
});
|
||||||
|
var uninstallModule = sinon.stub(server,'uninstallModule', function() {
|
||||||
|
return when.resolve({id:"123"});
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.del('/nodes/foo')
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
getNodeModuleInfo.restore();
|
||||||
|
uninstallModule.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.body.should.have.property("id","123");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails the uninstall if the module is not installed', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
var getNodeModuleInfo = sinon.stub(redNodes,'getNodeModuleInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.del('/nodes/foo')
|
||||||
|
.expect(404)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
getNodeModuleInfo.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails the uninstall if the module is not installed', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
var getNodeModuleInfo = sinon.stub(redNodes,'getNodeModuleInfo',function(id) {
|
||||||
|
return {id:"123"};
|
||||||
|
});
|
||||||
|
var uninstallModule = sinon.stub(server,'uninstallModule', function() {
|
||||||
|
return when.reject(new Error("test error"));
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.del('/nodes/foo')
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
getNodeModuleInfo.restore();
|
||||||
|
uninstallModule.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.text.should.equal("Error: test error");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('enable/disable', function() {
|
||||||
|
it('returns 400 if settings are unavailable', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.put('/nodes/123')
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns 400 for invalid payload', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.put('/nodes/foo')
|
||||||
|
.send({})
|
||||||
|
.expect(400)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.text.should.equal("Invalid request");
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns 404 for unknown node', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.put('/nodes/foo')
|
||||||
|
.send({enabled:false})
|
||||||
|
.expect(404)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enables disabled node', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return {id:"123",enabled: false};
|
||||||
|
});
|
||||||
|
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
|
||||||
|
return {id:"123",enabled: true,types:['a']};
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.put('/nodes/foo')
|
||||||
|
.send({enabled:true})
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
enableNode.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.body.should.have.property("id","123");
|
||||||
|
res.body.should.have.property("enabled",true);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('disables enabled node', function(done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return {id:"123",enabled: true};
|
||||||
|
});
|
||||||
|
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
|
||||||
|
return {id:"123",enabled: false,types:['a']};
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.put('/nodes/foo')
|
||||||
|
.send({enabled:false})
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
disableNode.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
res.body.should.have.property("id","123");
|
||||||
|
res.body.should.have.property("enabled",false);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('no-ops if already in the right state', function() {
|
||||||
|
function run(state,done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return {id:"123",enabled: state};
|
||||||
|
});
|
||||||
|
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
|
||||||
|
return {id:"123",enabled: true,types:['a']};
|
||||||
|
});
|
||||||
|
|
||||||
|
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
|
||||||
|
return {id:"123",enabled: false,types:['a']};
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.put('/nodes/foo')
|
||||||
|
.send({enabled:state})
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
var enableNodeCalled = enableNode.called;
|
||||||
|
var disableNodeCalled = disableNode.called;
|
||||||
|
enableNode.restore();
|
||||||
|
disableNode.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
enableNodeCalled.should.be.false;
|
||||||
|
disableNodeCalled.should.be.false;
|
||||||
|
res.body.should.have.property("id","123");
|
||||||
|
res.body.should.have.property("enabled",state);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
it('already enabled', function(done) {
|
||||||
|
run(true,done);
|
||||||
|
});
|
||||||
|
it('already disabled', function(done) {
|
||||||
|
run(false,done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('does not no-op if err on node', function() {
|
||||||
|
function run(state,done) {
|
||||||
|
var settingsAvailable = sinon.stub(settings,'available', function() {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
var getNodeInfo = sinon.stub(redNodes,'getNodeInfo',function(id) {
|
||||||
|
return {id:"123",enabled: state, err:"foo" };
|
||||||
|
});
|
||||||
|
var enableNode = sinon.stub(redNodes,'enableNode',function(id) {
|
||||||
|
return {id:"123",enabled: true,types:['a']};
|
||||||
|
});
|
||||||
|
|
||||||
|
var disableNode = sinon.stub(redNodes,'disableNode',function(id) {
|
||||||
|
return {id:"123",enabled: false,types:['a']};
|
||||||
|
});
|
||||||
|
|
||||||
|
request(app)
|
||||||
|
.put('/nodes/foo')
|
||||||
|
.send({enabled:state})
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
settingsAvailable.restore();
|
||||||
|
getNodeInfo.restore();
|
||||||
|
var enableNodeCalled = enableNode.called;
|
||||||
|
var disableNodeCalled = disableNode.called;
|
||||||
|
enableNode.restore();
|
||||||
|
disableNode.restore();
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
enableNodeCalled.should.be.equal(state);
|
||||||
|
disableNodeCalled.should.be.equal(!state);
|
||||||
|
res.body.should.have.property("id","123");
|
||||||
|
res.body.should.have.property("enabled",state);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
it('already enabled', function(done) {
|
||||||
|
run(true,done);
|
||||||
|
});
|
||||||
|
it('already disabled', function(done) {
|
||||||
|
run(false,done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
183
test/red/api/ui_spec.js
Normal file
183
test/red/api/ui_spec.js
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* 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 request = require("supertest");
|
||||||
|
var express = require("express");
|
||||||
|
var fs = require("fs");
|
||||||
|
var path = require("path");
|
||||||
|
|
||||||
|
var settings = require("../../../red/settings");
|
||||||
|
var events = require("../../../red/events");
|
||||||
|
var ui = require("../../../red/api/ui");
|
||||||
|
|
||||||
|
|
||||||
|
describe("ui api", function() {
|
||||||
|
var app;
|
||||||
|
|
||||||
|
|
||||||
|
describe("slash handler", function() {
|
||||||
|
before(function() {
|
||||||
|
app = express();
|
||||||
|
app.get("/foo",ui.ensureSlash,function(req,res) { res.send(200);});
|
||||||
|
});
|
||||||
|
it('redirects if the path does not end in a slash',function(done) {
|
||||||
|
request(app)
|
||||||
|
.get('/foo')
|
||||||
|
.expect(301,done);
|
||||||
|
});
|
||||||
|
it('does not redirect if the path ends in a slash',function(done) {
|
||||||
|
request(app)
|
||||||
|
.get('/foo/')
|
||||||
|
.expect(200,done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("icon handler", function() {
|
||||||
|
before(function() {
|
||||||
|
app = express();
|
||||||
|
app.get("/icons/:icon",ui.icon);
|
||||||
|
});
|
||||||
|
|
||||||
|
function binaryParser(res, callback) {
|
||||||
|
res.setEncoding('binary');
|
||||||
|
res.data = '';
|
||||||
|
res.on('data', function (chunk) {
|
||||||
|
res.data += chunk;
|
||||||
|
});
|
||||||
|
res.on('end', function () {
|
||||||
|
callback(null, new Buffer(res.data, 'binary'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function compareBuffers(b1,b2) {
|
||||||
|
b1.length.should.equal(b2.length);
|
||||||
|
for (var i=0;i<b1.length;i++) {
|
||||||
|
b1[i].should.equal(b2[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it('returns the default icon when getting an unknown icon', function(done) {
|
||||||
|
var defaultIcon = fs.readFileSync(path.resolve(__dirname+'/../../../public/icons/arrow-in.png'));
|
||||||
|
request(app)
|
||||||
|
.get("/icons/youwonthaveme.png")
|
||||||
|
.expect("Content-Type", /image\/png/)
|
||||||
|
.expect(200)
|
||||||
|
.parse(binaryParser)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err){
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
Buffer.isBuffer(res.body).should.be.true;
|
||||||
|
compareBuffers(res.body,defaultIcon);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
it('returns a known icon', function(done) {
|
||||||
|
var injectIcon = fs.readFileSync(path.resolve(__dirname+'/../../../public/icons/inject.png'));
|
||||||
|
request(app)
|
||||||
|
.get("/icons/inject.png")
|
||||||
|
.expect("Content-Type", /image\/png/)
|
||||||
|
.expect(200)
|
||||||
|
.parse(binaryParser)
|
||||||
|
.end(function(err, res){
|
||||||
|
if (err){
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
Buffer.isBuffer(res.body).should.be.true;
|
||||||
|
compareBuffers(res.body,injectIcon);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a registered icon' , function(done) {
|
||||||
|
var testIcon = fs.readFileSync(path.resolve(__dirname+'/../../resources/icons/test_icon.png'));
|
||||||
|
events.emit("node-icon-dir", path.resolve(__dirname+'/../../resources/icons'));
|
||||||
|
request(app)
|
||||||
|
.get("/icons/test_icon.png")
|
||||||
|
.expect("Content-Type", /image\/png/)
|
||||||
|
.expect(200)
|
||||||
|
.parse(binaryParser)
|
||||||
|
.end(function(err, res){
|
||||||
|
if (err){
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
Buffer.isBuffer(res.body).should.be.true;
|
||||||
|
compareBuffers(res.body,testIcon);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("settings handler", function() {
|
||||||
|
before(function() {
|
||||||
|
var userSettings = {
|
||||||
|
foo: 123,
|
||||||
|
httpNodeRoot: "testHttpNodeRoot",
|
||||||
|
version: "testVersion"
|
||||||
|
}
|
||||||
|
settings.init(userSettings);
|
||||||
|
app = express();
|
||||||
|
app.get("/settings",ui.settings);
|
||||||
|
//app.use("/",ui.editor);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function() {
|
||||||
|
settings.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the filtered settings', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/settings")
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
res.body.should.have.property("httpNodeRoot","testHttpNodeRoot");
|
||||||
|
res.body.should.have.property("version","testVersion");
|
||||||
|
res.body.should.not.have.property("foo",123);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("editor ui handler", function() {
|
||||||
|
before(function() {
|
||||||
|
app = express();
|
||||||
|
app.use("/",ui.editor);
|
||||||
|
});
|
||||||
|
it('serves the editor', function(done) {
|
||||||
|
request(app)
|
||||||
|
.get("/")
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
// Index page should probably mention Node-RED somewhere
|
||||||
|
res.text.indexOf("Node-RED").should.not.eql(-1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@ -14,9 +14,226 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
|
var when = require("when");
|
||||||
|
var sinon = require("sinon");
|
||||||
|
var child_process = require('child_process');
|
||||||
|
|
||||||
|
var comms = require("../../red/comms");
|
||||||
|
var redNodes = require("../../red/nodes");
|
||||||
|
var api = require("../../red/api");
|
||||||
|
var server = require("../../red/server");
|
||||||
|
|
||||||
|
|
||||||
describe("red/server", function() {
|
describe("red/server", function() {
|
||||||
it('can be required without errors', function() {
|
var commsMessages = [];
|
||||||
require("../../red/server");
|
var commsPublish;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
commsMessages = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
commsPublish = sinon.stub(comms,"publish", function(topic,msg,retained) {
|
||||||
|
commsMessages.push({topic:topic,msg:msg,retained:retained});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
after(function() {
|
||||||
|
commsPublish.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initialises components", function() {
|
||||||
|
var commsInit = sinon.stub(comms,"init",function() {});
|
||||||
|
var apiInit = sinon.stub(api,"init",function() {});
|
||||||
|
|
||||||
|
var dummyServer = {};
|
||||||
|
server.init(dummyServer,{httpAdminRoot:"/"});
|
||||||
|
|
||||||
|
commsInit.called.should.be.true;
|
||||||
|
apiInit.called.should.be.true;
|
||||||
|
|
||||||
|
should.exist(server.app);
|
||||||
|
should.exist(server.nodeApp);
|
||||||
|
|
||||||
|
server.server.should.equal(dummyServer);
|
||||||
|
|
||||||
|
commsInit.restore();
|
||||||
|
apiInit.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not initalise api when disabled", function() {
|
||||||
|
var commsInit = sinon.stub(comms,"init",function() {});
|
||||||
|
var apiInit = sinon.stub(api,"init",function() {});
|
||||||
|
|
||||||
|
var dummyServer = {};
|
||||||
|
server.init(dummyServer,{httpAdminRoot:false});
|
||||||
|
|
||||||
|
commsInit.called.should.be.true;
|
||||||
|
apiInit.called.should.be.false;
|
||||||
|
|
||||||
|
should.exist(server.app);
|
||||||
|
should.exist(server.nodeApp);
|
||||||
|
|
||||||
|
server.server.should.equal(dummyServer);
|
||||||
|
|
||||||
|
commsInit.restore();
|
||||||
|
apiInit.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stops components", function() {
|
||||||
|
var commsStop = sinon.stub(comms,"stop",function() {} );
|
||||||
|
var stopFlows = sinon.stub(redNodes,"stopFlows",function() {} );
|
||||||
|
|
||||||
|
server.stop();
|
||||||
|
|
||||||
|
commsStop.called.should.be.true;
|
||||||
|
stopFlows.called.should.be.true;
|
||||||
|
|
||||||
|
commsStop.restore();
|
||||||
|
stopFlows.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports added modules", function() {
|
||||||
|
var nodes = [
|
||||||
|
{types:["a"]},
|
||||||
|
{module:"foo",types:["b"]},
|
||||||
|
{types:["c"],err:"error"}
|
||||||
|
];
|
||||||
|
var result = server.reportAddedModules(nodes);
|
||||||
|
|
||||||
|
result.should.equal(nodes);
|
||||||
|
commsMessages.should.have.length(1);
|
||||||
|
commsMessages[0].topic.should.equal("node/added");
|
||||||
|
commsMessages[0].msg.should.eql(nodes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports removed modules", function() {
|
||||||
|
var nodes = [
|
||||||
|
{types:["a"]},
|
||||||
|
{module:"foo",types:["b"]},
|
||||||
|
{types:["c"],err:"error"}
|
||||||
|
];
|
||||||
|
var result = server.reportRemovedModules(nodes);
|
||||||
|
|
||||||
|
result.should.equal(nodes);
|
||||||
|
commsMessages.should.have.length(1);
|
||||||
|
commsMessages[0].topic.should.equal("node/removed");
|
||||||
|
commsMessages[0].msg.should.eql(nodes);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("installs module", function() {
|
||||||
|
it("rejects invalid module names", function(done) {
|
||||||
|
var promises = [];
|
||||||
|
promises.push(server.installModule("this_wont_exist "));
|
||||||
|
promises.push(server.installModule("this_wont_exist;no_it_really_wont"));
|
||||||
|
when.settle(promises).then(function(results) {
|
||||||
|
results[0].state.should.be.eql("rejected");
|
||||||
|
results[1].state.should.be.eql("rejected");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects when npm returns a 404", function(done) {
|
||||||
|
var exec = sinon.stub(child_process,"exec",function(cmd,cb) {
|
||||||
|
cb(new Error(),""," 404 this_wont_exist");
|
||||||
|
});
|
||||||
|
|
||||||
|
server.installModule("this_wont_exist").otherwise(function(err) {
|
||||||
|
err.code.should.be.eql(404);
|
||||||
|
done();
|
||||||
|
}).finally(function() {
|
||||||
|
exec.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("rejects with generic error", function(done) {
|
||||||
|
var exec = sinon.stub(child_process,"exec",function(cmd,cb) {
|
||||||
|
cb(new Error("test_error"),"","");
|
||||||
|
});
|
||||||
|
|
||||||
|
server.installModule("this_wont_exist").then(function() {
|
||||||
|
done(new Error("Unexpected success"));
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
err.message.should.be.eql("Install failed");
|
||||||
|
done();
|
||||||
|
}).finally(function() {
|
||||||
|
exec.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("succeeds when module is found", function(done) {
|
||||||
|
var nodeInfo = {module:"foo",types:["a"]};
|
||||||
|
var exec = sinon.stub(child_process,"exec",function(cmd,cb) {
|
||||||
|
cb(null,"","");
|
||||||
|
});
|
||||||
|
var addModule = sinon.stub(redNodes,"addModule",function(md) {
|
||||||
|
return when.resolve(nodeInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.installModule("this_wont_exist").then(function(info) {
|
||||||
|
info.should.eql(nodeInfo);
|
||||||
|
commsMessages.should.have.length(1);
|
||||||
|
commsMessages[0].topic.should.equal("node/added");
|
||||||
|
commsMessages[0].msg.should.eql(nodeInfo);
|
||||||
|
done();
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
done(err);
|
||||||
|
}).finally(function() {
|
||||||
|
exec.restore();
|
||||||
|
addModule.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe("uninstalls module", function() {
|
||||||
|
it("rejects invalid module names", function(done) {
|
||||||
|
var promises = [];
|
||||||
|
promises.push(server.uninstallModule("this_wont_exist "));
|
||||||
|
promises.push(server.uninstallModule("this_wont_exist;no_it_really_wont"));
|
||||||
|
when.settle(promises).then(function(results) {
|
||||||
|
results[0].state.should.be.eql("rejected");
|
||||||
|
results[1].state.should.be.eql("rejected");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects with generic error", function(done) {
|
||||||
|
var nodeInfo = [{module:"foo",types:["a"]}];
|
||||||
|
var removeModule = sinon.stub(redNodes,"removeModule",function(md) {
|
||||||
|
return when.resolve(nodeInfo);
|
||||||
|
});
|
||||||
|
var exec = sinon.stub(child_process,"exec",function(cmd,cb) {
|
||||||
|
cb(new Error("test_error"),"","");
|
||||||
|
});
|
||||||
|
|
||||||
|
server.uninstallModule("this_wont_exist").then(function() {
|
||||||
|
done(new Error("Unexpected success"));
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
err.message.should.be.eql("Removal failed");
|
||||||
|
done();
|
||||||
|
}).finally(function() {
|
||||||
|
exec.restore();
|
||||||
|
removeModule.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("succeeds when module is found", function(done) {
|
||||||
|
var nodeInfo = [{module:"foo",types:["a"]}];
|
||||||
|
var removeModule = sinon.stub(redNodes,"removeModule",function(md) {
|
||||||
|
return nodeInfo;
|
||||||
|
});
|
||||||
|
var exec = sinon.stub(child_process,"exec",function(cmd,cb) {
|
||||||
|
cb(null,"","");
|
||||||
|
});
|
||||||
|
|
||||||
|
server.uninstallModule("this_wont_exist").then(function(info) {
|
||||||
|
info.should.eql(nodeInfo);
|
||||||
|
commsMessages.should.have.length(1);
|
||||||
|
commsMessages[0].topic.should.equal("node/removed");
|
||||||
|
commsMessages[0].msg.should.eql(nodeInfo);
|
||||||
|
done();
|
||||||
|
}).otherwise(function(err) {
|
||||||
|
done(err);
|
||||||
|
}).finally(function() {
|
||||||
|
exec.restore();
|
||||||
|
removeModule.restore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -108,7 +108,27 @@ describe("red/settings", function() {
|
|||||||
}).otherwise(function(err) {
|
}).otherwise(function(err) {
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes persistent settings when reset', function() {
|
||||||
|
var userSettings = {
|
||||||
|
a: 123,
|
||||||
|
b: "test",
|
||||||
|
c: [1,2,3]
|
||||||
|
}
|
||||||
|
settings.init(userSettings);
|
||||||
|
|
||||||
|
settings.available().should.be.false;
|
||||||
|
|
||||||
|
settings.should.have.property("a",123);
|
||||||
|
settings.should.have.property("b","test");
|
||||||
|
settings.c.should.be.an.Array.with.lengthOf(3);
|
||||||
|
|
||||||
|
settings.reset();
|
||||||
|
|
||||||
|
settings.should.not.have.property("a");
|
||||||
|
settings.should.not.have.property("d");
|
||||||
|
settings.should.not.have.property("c");
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,177 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 request = require("supertest");
|
|
||||||
var express = require("express");
|
|
||||||
var redUI = require("../../red/ui");
|
|
||||||
|
|
||||||
|
|
||||||
describe("red/ui icon handler", function() {
|
|
||||||
it('returns the default icon when getting an unknown icon', function(done) {
|
|
||||||
var app = express();
|
|
||||||
redUI({},app);
|
|
||||||
request(app)
|
|
||||||
.get("/icons/youwonthaveme.png")
|
|
||||||
.expect('Content-Type', /image\/png/)
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err, res){
|
|
||||||
if (err){
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an icon from disk', function(done) {
|
|
||||||
var app = express();
|
|
||||||
redUI({},app);
|
|
||||||
request(app)
|
|
||||||
.get("/icons/arduino.png")
|
|
||||||
.expect('Content-Type', /image\/png/)
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err, res){
|
|
||||||
if (err){
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("icon cache handler", function() {
|
|
||||||
var fs = require('fs-extra');
|
|
||||||
var path = require('path');
|
|
||||||
var events = require("../../red/events");
|
|
||||||
|
|
||||||
var tempDir = path.join(__dirname,".tmp/");
|
|
||||||
var cachedFakePNG = tempDir + "cacheMe.png";
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(function(done) {
|
|
||||||
fs.remove(tempDir,function(err) {
|
|
||||||
fs.mkdirSync(tempDir);
|
|
||||||
fs.writeFileSync(cachedFakePNG, "Hello PNG\n");
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
afterEach(function(done) {
|
|
||||||
fs.exists(cachedFakePNG, function(exists) {
|
|
||||||
if(exists) {
|
|
||||||
fs.unlinkSync(cachedFakePNG);
|
|
||||||
}
|
|
||||||
fs.remove(tempDir,done);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This test case test that:
|
|
||||||
* 1) any directory can be added to the path lookup (such as /tmp) by
|
|
||||||
* calling the right event
|
|
||||||
* 2) that a file we know exists gets cached so that the lookup/verification
|
|
||||||
* of actual existence doesn't occur again when a subsequent request comes in
|
|
||||||
*
|
|
||||||
* The second point verifies that the cache works. If the cache wouldn't work
|
|
||||||
* the default PNG would be served
|
|
||||||
*/
|
|
||||||
it('returns an icon using icon cache', function(done) {
|
|
||||||
var app = express();
|
|
||||||
redUI({},app);
|
|
||||||
events.emit("node-icon-dir", tempDir);
|
|
||||||
request(app)
|
|
||||||
.get("/icons/cacheMe.png")
|
|
||||||
.expect('Content-Type', /image\/png/)
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err, res){
|
|
||||||
if (err){
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
fs.unlink(cachedFakePNG, function(err) {
|
|
||||||
if(err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
request(app)
|
|
||||||
.get("/icons/cacheMe.png")
|
|
||||||
.expect('Content-Type', /text\/html/)
|
|
||||||
.expect(404)
|
|
||||||
.end(function(err, res){
|
|
||||||
if (err){
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("red/ui settings handler", function() {
|
|
||||||
it('returns the provided settings', function(done) {
|
|
||||||
var settings = {
|
|
||||||
httpNodeRoot: "testHttpNodeRoot",
|
|
||||||
version: "testVersion",
|
|
||||||
};
|
|
||||||
var app = express();
|
|
||||||
redUI(settings,app);
|
|
||||||
request(app)
|
|
||||||
.get("/settings")
|
|
||||||
.expect('Content-Type', /application\/json/)
|
|
||||||
.expect(200, "{\n \"httpNodeRoot\": \"testHttpNodeRoot\",\n \"version\": \"testVersion\"\n}")
|
|
||||||
.end(function(err, res){
|
|
||||||
if (err){
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("red/ui root handler", function() {
|
|
||||||
it('server up the main page', function(done) {
|
|
||||||
var app = express();
|
|
||||||
redUI({},app);
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get("/")
|
|
||||||
.expect('Content-Type', /text\/html/)
|
|
||||||
.expect(200)
|
|
||||||
.end(function(err, res){
|
|
||||||
if (err){
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('redirects to path ending with /', function(done) {
|
|
||||||
var rapp = express();
|
|
||||||
redUI({},rapp);
|
|
||||||
|
|
||||||
var app = express().use('/root', rapp);
|
|
||||||
|
|
||||||
request(app)
|
|
||||||
.get("/root")
|
|
||||||
.expect('Content-Type', /text\/plain/)
|
|
||||||
.expect(302)
|
|
||||||
.end(function(err, res){
|
|
||||||
if (err){
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
BIN
test/resources/icons/test_icon.png
Normal file
BIN
test/resources/icons/test_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 163 B |
Loading…
Reference in New Issue
Block a user