From 6bc1e9d9443d5fa8216254ed0cdcaca02005adca Mon Sep 17 00:00:00 2001 From: Andrew Stewart Date: Wed, 4 Jun 2014 11:37:15 -0700 Subject: [PATCH] Move configuration to API class --- lib/api.js | 56 ++++++++++-------- lib/cylon.js | 50 +++------------- test/specs/api.spec.js | 6 -- test/specs/cylon.spec.js | 120 ++++++--------------------------------- 4 files changed, 56 insertions(+), 176 deletions(-) diff --git a/lib/api.js b/lib/api.js index f7f161b..105f2dc 100644 --- a/lib/api.js +++ b/lib/api.js @@ -8,12 +8,14 @@ "use strict"; -var fs = require('fs'); +var fs = require('fs'), + path = require('path'); var express = require('express'), bodyParser = require('body-parser'); -var Logger = require('./logger'); +var Cylon = require('./cylon'), + Logger = require('./logger'); var API = module.exports = function API(opts) { var self = this; @@ -22,11 +24,10 @@ var API = module.exports = function API(opts) { opts = {}; } - this.opts = opts; - this.host = opts.host || "127.0.0.1"; - this.port = opts.port || "3000"; - this.ssl = opts.ssl; - this.master = opts.master; + for (var d in this.defaults) { + this[d] = opts.hasOwnProperty(d) ? opts[d] : this.defaults[d]; + } + this.server = express(); //configure ssl if requested @@ -34,8 +35,8 @@ var API = module.exports = function API(opts) { var https = require('https'); var options = { - key: fs.readFileSync(this.ssl.key || __dirname + "/ssl/server.key"), - cert: fs.readFileSync(this.ssl.cert || __dirname + "/ssl/server.crt") + key: fs.readFileSync(this.ssl.key), + cert: fs.readFileSync(this.ssl.cert) }; 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/")); }; +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() { var self = this; @@ -93,8 +105,6 @@ API.prototype.parseCommandParams = function(req) { API.prototype.configureRoutes = function() { var self = this; - var master = this.master; - this.server.all("/*", function(req, res, next) { res.set("Access-Control-Allow-Origin", self.opts.CORS || "*"); res.set("Access-Control-Allow-Headers", "Content-Type"); @@ -105,8 +115,8 @@ API.prototype.configureRoutes = function() { this.server.get("/robots", function(req, res) { var data = []; - for (var i = 0; i < master.robots.length; i++) { - var robot = master.robots[i]; + for (var i = 0; i < Cylon.robots.length; i++) { + var robot = Cylon.robots[i]; data.push(robot.data()); } @@ -114,13 +124,13 @@ API.prototype.configureRoutes = function() { }); 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()); }); }); 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); }); }); @@ -128,7 +138,7 @@ API.prototype.configureRoutes = function() { this.server.all("/robots/:robot/commands/:command", function(req, res) { 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); } 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) { - master.findRobot(req.params.robot, function(err, robot) { + Cylon.findRobot(req.params.robot, function(err, robot) { res.json(err ? err : robot.data().devices); }); }); @@ -146,7 +156,7 @@ API.prototype.configureRoutes = function() { var robot = req.params.robot, device = req.params.device; - master.findRobotDevice(robot, device, function(err, device) { + Cylon.findRobotDevice(robot, device, function(err, device) { res.json(err ? err : device.data()); }); }); @@ -156,7 +166,7 @@ API.prototype.configureRoutes = function() { device = req.params.device, event = req.params.event; - master.findRobotDevice(robot, device, function(err, device) { + Cylon.findRobotDevice(robot, device, function(err, device) { if (err) { res.json(err); } res.writeHead(200, { @@ -181,7 +191,7 @@ API.prototype.configureRoutes = function() { var robot = req.params.robot, 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); }); }); @@ -193,7 +203,7 @@ API.prototype.configureRoutes = function() { 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); } var result = device[command].apply(device, params); @@ -202,7 +212,7 @@ API.prototype.configureRoutes = function() { }); 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); }); }); @@ -211,7 +221,7 @@ API.prototype.configureRoutes = function() { var robot = req.params.robot, connection = req.params.connection; - master.findRobotConnection(robot, connection, function(err, connection) { + Cylon.findRobotConnection(robot, connection, function(err, connection) { res.json(err ? err : connection.data()); }); }); diff --git a/lib/cylon.js b/lib/cylon.js index 9eeae15..8aa5826 100644 --- a/lib/cylon.js +++ b/lib/cylon.js @@ -8,7 +8,8 @@ "use strict"; -var Logger = require('./logger'), +var API = require('./api'), + Logger = require('./logger'), Robot = require('./robot'), Utils = require('./utils'); @@ -25,15 +26,7 @@ var Cylon = module.exports = { api_instance: null, - robots: [], - - api_config: { - host: '127.0.0.1', - port: '3000', - auth: {}, - CORS: null, - ssl: {} - } + robots: [] }; // Public: Creates a new Robot @@ -55,28 +48,18 @@ Cylon.robot = function robot(opts) { 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 -// 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) { if (opts == null) { opts = {}; } - var keys = ['host', 'port', 'auth', 'CORS', 'ssl']; - - 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; + this.api_instance = new API(opts); + this.api_instance.listen(); }; // Public: Finds a particular robot by name @@ -153,8 +136,6 @@ Cylon.findRobotConnection = function findRobotConnection(robotid, connid, callba // // Returns nothing Cylon.start = function start() { - this.startAPI(); - for (var i = 0; i < this.robots.length; i++) { var robot = this.robots[i]; 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") { var readline = require("readline"), io = { input: process.stdin, output: process.stdout }; diff --git a/test/specs/api.spec.js b/test/specs/api.spec.js index f943aa2..c9c2af4 100644 --- a/test/specs/api.spec.js +++ b/test/specs/api.spec.js @@ -14,8 +14,6 @@ describe("API", function() { beforeEach(function() { stub(https, 'createServer').returns({ listen: spy() }); - opts = { master: { name: 'master' }, ssl: {} } - api = new API(opts); }); @@ -35,10 +33,6 @@ describe("API", function() { 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() { expect(api.server).to.be.a('function'); diff --git a/test/specs/cylon.spec.js b/test/specs/cylon.spec.js index 69ac0a4..ecc39a1 100644 --- a/test/specs/cylon.spec.js +++ b/test/specs/cylon.spec.js @@ -2,7 +2,8 @@ var Cylon = source("cylon"); -var Logger = source('logger'), +var API = source('api'), + Logger = source('logger'), Adaptor = source('adaptor'), Driver = source('driver'); @@ -25,14 +26,6 @@ describe("Cylon", function() { 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() { expect(Cylon.robots).to.be.eql([]); }); @@ -52,94 +45,23 @@ describe("Cylon", function() { }); describe("#api", function() { - var expectedConfig; - beforeEach(function() { - expectedConfig = { - host: '127.0.0.1', - port: '3000', - auth: {}, - CORS: null, - ssl: {} - }; + stub(API.prototype, 'listen'); + }); - // this is the shortest, cheapest way to dup an object in JS. - // I don't like it either. - Cylon.api_config = JSON.parse(JSON.stringify(expectedConfig)); + afterEach(function() { + API.prototype.listen.restore(); + }); + + it('creates a new API instance', function() { + Cylon.api(); + expect(Cylon.api_instance).to.be.an.instanceOf(API); + }); + + it('passes arguments to the API constructor', function() { + Cylon.api({ port: '1234' }); + expect(Cylon.api_instance.port).to.be.eql('1234'); }) - - context("without arguments", function() { - it("returns the current API configuration", function() { - Cylon.api(); - expect(Cylon.api_config).to.be.eql(expectedConfig); - }); - }); - - context("only specifying port", function() { - it("changes the port, but not the host", function() { - expectedConfig.port = "4000"; - - 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() { @@ -308,16 +230,6 @@ describe("Cylon", function() { describe("#start", function() { before(function() { 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() {