Move configuration to API class

This commit is contained in:
Andrew Stewart 2014-06-04 11:37:15 -07:00
parent e08a5df88b
commit 6bc1e9d944
4 changed files with 56 additions and 176 deletions

View File

@ -8,12 +8,14 @@
"use strict"; "use strict";
var fs = require('fs'); var fs = require('fs'),
path = require('path');
var express = require('express'), var express = require('express'),
bodyParser = require('body-parser'); bodyParser = require('body-parser');
var Logger = require('./logger'); var Cylon = require('./cylon'),
Logger = require('./logger');
var API = module.exports = function API(opts) { var API = module.exports = function API(opts) {
var self = this; var self = this;
@ -22,11 +24,10 @@ var API = module.exports = function API(opts) {
opts = {}; opts = {};
} }
this.opts = opts; for (var d in this.defaults) {
this.host = opts.host || "127.0.0.1"; this[d] = opts.hasOwnProperty(d) ? opts[d] : this.defaults[d];
this.port = opts.port || "3000"; }
this.ssl = opts.ssl;
this.master = opts.master;
this.server = express(); this.server = express();
//configure ssl if requested //configure ssl if requested
@ -34,8 +35,8 @@ var API = module.exports = function API(opts) {
var https = require('https'); var https = require('https');
var options = { var options = {
key: fs.readFileSync(this.ssl.key || __dirname + "/ssl/server.key"), key: fs.readFileSync(this.ssl.key),
cert: fs.readFileSync(this.ssl.cert || __dirname + "/ssl/server.crt") cert: fs.readFileSync(this.ssl.cert)
}; };
this.server.node = https.createServer(options, this.server); this.server.node = https.createServer(options, this.server);
@ -59,6 +60,17 @@ var API = module.exports = function API(opts) {
this.server.use(express["static"](__dirname + "/../node_modules/robeaux/")); this.server.use(express["static"](__dirname + "/../node_modules/robeaux/"));
}; };
API.prototype.defaults = {
host: '127.0.0.1',
port: '3000',
auth: false,
CORS: '',
ssl: {
key: path.normalize(__dirname + "/ssl/server.key"),
cert: path.normalize(__dirname + "/ssl/server.crt")
}
};
API.prototype.listen = function() { API.prototype.listen = function() {
var self = this; var self = this;
@ -93,8 +105,6 @@ API.prototype.parseCommandParams = function(req) {
API.prototype.configureRoutes = function() { API.prototype.configureRoutes = function() {
var self = this; var self = this;
var master = this.master;
this.server.all("/*", function(req, res, next) { this.server.all("/*", function(req, res, next) {
res.set("Access-Control-Allow-Origin", self.opts.CORS || "*"); res.set("Access-Control-Allow-Origin", self.opts.CORS || "*");
res.set("Access-Control-Allow-Headers", "Content-Type"); res.set("Access-Control-Allow-Headers", "Content-Type");
@ -105,8 +115,8 @@ API.prototype.configureRoutes = function() {
this.server.get("/robots", function(req, res) { this.server.get("/robots", function(req, res) {
var data = []; var data = [];
for (var i = 0; i < master.robots.length; i++) { for (var i = 0; i < Cylon.robots.length; i++) {
var robot = master.robots[i]; var robot = Cylon.robots[i];
data.push(robot.data()); data.push(robot.data());
} }
@ -114,13 +124,13 @@ API.prototype.configureRoutes = function() {
}); });
this.server.get("/robots/:robot", function(req, res) { this.server.get("/robots/:robot", function(req, res) {
master.findRobot(req.params.robot, function(err, robot) { Cylon.findRobot(req.params.robot, function(err, robot) {
res.json(err ? err : robot.data()); res.json(err ? err : robot.data());
}); });
}); });
this.server.get("/robots/:robot/commands", function(req, res) { this.server.get("/robots/:robot/commands", function(req, res) {
master.findRobot(req.params.robot, function(err, robot) { Cylon.findRobot(req.params.robot, function(err, robot) {
res.json(err ? err : robot.data().commands); res.json(err ? err : robot.data().commands);
}); });
}); });
@ -128,7 +138,7 @@ API.prototype.configureRoutes = function() {
this.server.all("/robots/:robot/commands/:command", function(req, res) { this.server.all("/robots/:robot/commands/:command", function(req, res) {
var params = self.parseCommandParams(req); var params = self.parseCommandParams(req);
master.findRobot(req.params.robot, function(err, robot) { Cylon.findRobot(req.params.robot, function(err, robot) {
if (err) { return res.json(err); } if (err) { return res.json(err); }
var result = robot[req.params.command].apply(robot, params); var result = robot[req.params.command].apply(robot, params);
@ -137,7 +147,7 @@ API.prototype.configureRoutes = function() {
}); });
this.server.get("/robots/:robot/devices", function(req, res) { this.server.get("/robots/:robot/devices", function(req, res) {
master.findRobot(req.params.robot, function(err, robot) { Cylon.findRobot(req.params.robot, function(err, robot) {
res.json(err ? err : robot.data().devices); res.json(err ? err : robot.data().devices);
}); });
}); });
@ -146,7 +156,7 @@ API.prototype.configureRoutes = function() {
var robot = req.params.robot, var robot = req.params.robot,
device = req.params.device; device = req.params.device;
master.findRobotDevice(robot, device, function(err, device) { Cylon.findRobotDevice(robot, device, function(err, device) {
res.json(err ? err : device.data()); res.json(err ? err : device.data());
}); });
}); });
@ -156,7 +166,7 @@ API.prototype.configureRoutes = function() {
device = req.params.device, device = req.params.device,
event = req.params.event; event = req.params.event;
master.findRobotDevice(robot, device, function(err, device) { Cylon.findRobotDevice(robot, device, function(err, device) {
if (err) { res.json(err); } if (err) { res.json(err); }
res.writeHead(200, { res.writeHead(200, {
@ -181,7 +191,7 @@ API.prototype.configureRoutes = function() {
var robot = req.params.robot, var robot = req.params.robot,
device = req.params.device; device = req.params.device;
master.findRobotDevice(robot, device, function(err, device) { Cylon.findRobotDevice(robot, device, function(err, device) {
res.json(err ? err : device.data().commands); res.json(err ? err : device.data().commands);
}); });
}); });
@ -193,7 +203,7 @@ API.prototype.configureRoutes = function() {
var params = self.parseCommandParams(req); var params = self.parseCommandParams(req);
master.findRobotDevice(robot, device, function(err, device) { Cylon.findRobotDevice(robot, device, function(err, device) {
if (err) { return res.json(err); } if (err) { return res.json(err); }
var result = device[command].apply(device, params); var result = device[command].apply(device, params);
@ -202,7 +212,7 @@ API.prototype.configureRoutes = function() {
}); });
this.server.get("/robots/:robot/connections", function(req, res) { this.server.get("/robots/:robot/connections", function(req, res) {
master.findRobot(req.params.robot, function(err, robot) { Cylon.findRobot(req.params.robot, function(err, robot) {
res.json(err ? err : robot.data().connections); res.json(err ? err : robot.data().connections);
}); });
}); });
@ -211,7 +221,7 @@ API.prototype.configureRoutes = function() {
var robot = req.params.robot, var robot = req.params.robot,
connection = req.params.connection; connection = req.params.connection;
master.findRobotConnection(robot, connection, function(err, connection) { Cylon.findRobotConnection(robot, connection, function(err, connection) {
res.json(err ? err : connection.data()); res.json(err ? err : connection.data());
}); });
}); });

View File

@ -8,7 +8,8 @@
"use strict"; "use strict";
var Logger = require('./logger'), var API = require('./api'),
Logger = require('./logger'),
Robot = require('./robot'), Robot = require('./robot'),
Utils = require('./utils'); Utils = require('./utils');
@ -25,15 +26,7 @@ var Cylon = module.exports = {
api_instance: null, api_instance: null,
robots: [], robots: []
api_config: {
host: '127.0.0.1',
port: '3000',
auth: {},
CORS: null,
ssl: {}
}
}; };
// Public: Creates a new Robot // Public: Creates a new Robot
@ -55,28 +48,18 @@ Cylon.robot = function robot(opts) {
return robot; return robot;
}; };
// Public: Configures the API host and port based on passed options // Public: Creates a new API based on passed options
// //
// opts - object containing API options // opts - object containing API options
// host - host address API should serve from
// port - port API should listen for requests on
// //
// Returns the API configuration // Returns nothing
Cylon.api = function api(opts) { Cylon.api = function api(opts) {
if (opts == null) { if (opts == null) {
opts = {}; opts = {};
} }
var keys = ['host', 'port', 'auth', 'CORS', 'ssl']; this.api_instance = new API(opts);
this.api_instance.listen();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (typeof opts[key] !== "undefined") {
this.api_config[key] = opts[key];
}
}
return this.api_config;
}; };
// Public: Finds a particular robot by name // Public: Finds a particular robot by name
@ -153,8 +136,6 @@ Cylon.findRobotConnection = function findRobotConnection(robotid, connid, callba
// //
// Returns nothing // Returns nothing
Cylon.start = function start() { Cylon.start = function start() {
this.startAPI();
for (var i = 0; i < this.robots.length; i++) { for (var i = 0; i < this.robots.length; i++) {
var robot = this.robots[i]; var robot = this.robots[i];
robot.start(); robot.start();
@ -171,23 +152,6 @@ Cylon.halt = function halt() {
} }
}; };
// Creates a new instance of the Cylon API server, or returns the
// already-existing one.
//
// Returns an Cylon.ApiServer instance
Cylon.startAPI = function startAPI() {
var API = require('./api');
this.api_config.master = this;
if (this.api_instance === null) {
this.api_instance = new API(this.api_config);
this.api_instance.configureRoutes();
this.api_instance.listen();
}
return this.api_instance;
};
if (process.platform === "win32") { if (process.platform === "win32") {
var readline = require("readline"), var readline = require("readline"),
io = { input: process.stdin, output: process.stdout }; io = { input: process.stdin, output: process.stdout };

View File

@ -14,8 +14,6 @@ describe("API", function() {
beforeEach(function() { beforeEach(function() {
stub(https, 'createServer').returns({ listen: spy() }); stub(https, 'createServer').returns({ listen: spy() });
opts = { master: { name: 'master' }, ssl: {} }
api = new API(opts); api = new API(opts);
}); });
@ -35,10 +33,6 @@ describe("API", function() {
expect(api.port).to.be.eql("3000") expect(api.port).to.be.eql("3000")
}); });
it("sets @master to the passed master", function() {
expect(api.master).to.be.eql(opts.master)
});
it("sets @server to an Express server instance", function() { it("sets @server to an Express server instance", function() {
expect(api.server).to.be.a('function'); expect(api.server).to.be.a('function');

View File

@ -2,7 +2,8 @@
var Cylon = source("cylon"); var Cylon = source("cylon");
var Logger = source('logger'), var API = source('api'),
Logger = source('logger'),
Adaptor = source('adaptor'), Adaptor = source('adaptor'),
Driver = source('driver'); Driver = source('driver');
@ -25,14 +26,6 @@ describe("Cylon", function() {
expect(Cylon.api_instance).to.be.eql(null); expect(Cylon.api_instance).to.be.eql(null);
}); });
it("sets @api_config to an object containing host/port info", function() {
var config = Cylon.api_config;
expect(config).to.be.an('object');
expect(config.host).to.be.eql('127.0.0.1');
expect(config.port).to.be.eql('3000');
});
it("sets @robots to an empty array by default", function() { it("sets @robots to an empty array by default", function() {
expect(Cylon.robots).to.be.eql([]); expect(Cylon.robots).to.be.eql([]);
}); });
@ -52,94 +45,23 @@ describe("Cylon", function() {
}); });
describe("#api", function() { describe("#api", function() {
var expectedConfig;
beforeEach(function() { beforeEach(function() {
expectedConfig = { stub(API.prototype, 'listen');
host: '127.0.0.1', });
port: '3000',
auth: {},
CORS: null,
ssl: {}
};
// this is the shortest, cheapest way to dup an object in JS. afterEach(function() {
// I don't like it either. API.prototype.listen.restore();
Cylon.api_config = JSON.parse(JSON.stringify(expectedConfig)); });
})
context("without arguments", function() { it('creates a new API instance', function() {
it("returns the current API configuration", function() {
Cylon.api(); Cylon.api();
expect(Cylon.api_config).to.be.eql(expectedConfig); expect(Cylon.api_instance).to.be.an.instanceOf(API);
});
}); });
context("only specifying port", function() { it('passes arguments to the API constructor', function() {
it("changes the port, but not the host", function() { Cylon.api({ port: '1234' });
expectedConfig.port = "4000"; expect(Cylon.api_instance.port).to.be.eql('1234');
})
Cylon.api({ port: "4000" });
expect(Cylon.api_config).to.be.eql(expectedConfig);
});
});
context("only specifying host", function() {
it("changes the host, but not the port", function() {
expectedConfig.host = "0.0.0.0";
Cylon.api({ host: "0.0.0.0" });
expect(Cylon.api_config).to.be.eql(expectedConfig);
});
});
context("specifying new host and port", function() {
it("changes both the host and port", function() {
expectedConfig.host = "0.0.0.0";
expectedConfig.port = "4000";
Cylon.api({ host: "0.0.0.0", port: "4000" });
expect(Cylon.api_config).to.be.eql(expectedConfig);
});
});
context("specifiying SSL key and cert", function() {
it("changes the SSL key and cert", function() {
expectedConfig.ssl.cert = "/path/to/cert/file";
expectedConfig.ssl.key = "/path/to/key/file";
Cylon.api({
ssl: {
cert: "/path/to/cert/file",
key: "/path/to/key/file"
}
});
expect(Cylon.api_config).to.be.eql(expectedConfig);
});
});
context("specifying an auth strategy", function() {
it("changes the auth strategy", function() {
var auth = { type: 'basic', user: 'user', pass: 'pass'}
expectedConfig.auth = auth;
Cylon.api({ auth: auth })
expect(Cylon.api_config).to.be.eql(expectedConfig);
});
});
context("specifying CORS restrictions", function() {
it("changes the CORS restrictions", function() {
var CORS = "https://localhost:4000";
expectedConfig.CORS = CORS;
Cylon.api({ CORS: CORS })
expect(Cylon.api_config).to.be.eql(expectedConfig);
});
});
}); });
describe("#findRobot", function() { describe("#findRobot", function() {
@ -308,16 +230,6 @@ describe("Cylon", function() {
describe("#start", function() { describe("#start", function() {
before(function() { before(function() {
Cylon.robots = []; Cylon.robots = [];
stub(Cylon, 'startAPI').returns(true);
});
after(function() {
Cylon.startAPI.restore();
});
it("starts the API", function() {
Cylon.start();
expect(Cylon.startAPI).to.be.called;
}); });
it("calls #start() on all robots", function() { it("calls #start() on all robots", function() {