From 5db7c2cfa14ec7700d6a9eb5780acb4158388004 Mon Sep 17 00:00:00 2001 From: Ben West Date: Thu, 17 Jul 2014 14:18:36 -0700 Subject: [PATCH 01/14] introduce express.Router( ), small renaming Rename a few things here and there to help clarify the situation. This change introduces use of express.Router( ), which provides the routing functions of express without the full app. This also shows an experiment observing the interaction betwen express( ) and express.Router( ). There are a few reasons for doing this: Using express.Router( ) allows introspecting existing routes. As a REST application, our content payloads should include URLs, links to other resources available from our API. As a bonus feature, it's often nice for a meta page to describe the links, status, and version available from this particular API. By far, the easiest way to implement this is to introspect the existing routes. Separating the code makes testing easier. This lays the ground work for breaking apart the code into smaller, more testable modules. --- lib/api.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/api.js b/lib/api.js index dbfcc6314cb..b96ae5a4e97 100644 --- a/lib/api.js +++ b/lib/api.js @@ -8,11 +8,12 @@ var sgvdata = require('sgvdata'); * API - Expose Nightscout HTTP API * This api is designed to work with express. */ -function api (env, entries, settings) { +function configure (env, entries, settings) { // our globals var express = require('express'), - api = express() + app = express( ), + api = express.Router() ; // some middleware @@ -22,7 +23,7 @@ function api (env, entries, settings) { ; // set up express basics - api.set('title', 'Nightscout API v1'); + app.set('title', 'Nightscout API v1'); // invoke common middleware api.use(sendJSONStatus); @@ -243,7 +244,22 @@ function api (env, entries, settings) { }); }); - return api; + function debug ( ) { + console.log('ROUTES', this.stack); + for (var k in this) { + console.log('KEY', k); + } + console.log(this.settings); + console.log(this.locals); + console.log("FOOBAR", this.get && this.get('foobar')) + } + app.set('foobar', "MY VALUE"); + + debug.call(api); + app.use(api); + debug.call(app); + + return app; } -module.exports = api; +module.exports = configure; From 3af4dc54b018e6ee99bf6316c937d6d2db0f7337 Mon Sep 17 00:00:00 2001 From: Ben West Date: Thu, 17 Jul 2014 14:24:46 -0700 Subject: [PATCH 02/14] quick renaming for clarification This avoids re-using the same variable name in different contexts. --- lib/api.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/api.js b/lib/api.js index b96ae5a4e97..0544fdc0ff3 100644 --- a/lib/api.js +++ b/lib/api.js @@ -80,6 +80,7 @@ function configure (env, entries, settings) { /**********\ * Entries \**********/ + // // Middleware to format any response involving entries. function format_entries (req, res, next) { @@ -171,8 +172,8 @@ function configure (env, entries, settings) { }, format_entries); api.get('/entries/current', function(req, res, next) { - entries.list({count: 1}, function(err, entries) { - res.entries = entries; + entries.list({count: 1}, function(err, records) { + res.entries = records; res.entries_err = err; return next( ); }); From 4745927cf013fee5cfffc5bb4f8146b76d351d13 Mon Sep 17 00:00:00 2001 From: Ben West Date: Thu, 17 Jul 2014 20:16:37 -0700 Subject: [PATCH 03/14] prepping for another re-org Small hack in order to start separating things out a bit more. --- lib/api.js | 39 ++++++++++++++++++++++++++++----------- lib/middleware/index.js | 18 ++++++++++++++++++ 2 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 lib/middleware/index.js diff --git a/lib/api.js b/lib/api.js index 0544fdc0ff3..937de4054cf 100644 --- a/lib/api.js +++ b/lib/api.js @@ -6,25 +6,42 @@ var sgvdata = require('sgvdata'); /* * API - Expose Nightscout HTTP API - * This api is designed to work with express. + * This implementation is designed to work with express. */ -function configure (env, entries, settings) { + +function create (env, entries, settings) { + var express = require('express'), + app = express( ); + + var wares = require('./middleware/')(env); + app.disable('api'); + if (env.api_secret) { + console.log("API_SECRET", env.api_secret); + app.enable('api'); + } + // set up express basics + app.set('title', 'Nightscout API v1'); + return configure.call(app, wares, entries, settings); +} +create.configure = configure; +function configure (wares, entries, settings) { // our globals var express = require('express'), - app = express( ), - api = express.Router() + // app = express( ), + app = this, + api = express.Router( ) ; // some middleware - var verifyAuthorization = require('./middleware/verify-token')(env), - sendJSONStatus = require('./middleware/send-json-status')( ), + // var verifyAuthorization = require('./middleware/verify-token')(env), + // sendJSONStatus = require('./middleware/send-json-status')( ), + // bodyParser = require('body-parser') + var verifyAuthorization = wares.verifyAuthorization, + sendJSONStatus = wares.sendJSONStatus, bodyParser = require('body-parser') ; - // set up express basics - app.set('title', 'Nightscout API v1'); - // invoke common middleware api.use(sendJSONStatus); // text body types get handled as raw buffer stream @@ -80,7 +97,7 @@ function configure (env, entries, settings) { /**********\ * Entries \**********/ - // + // app.use('/entries', require('./api/entries/')(app, wares, entries)); // Middleware to format any response involving entries. function format_entries (req, res, next) { @@ -262,5 +279,5 @@ function configure (env, entries, settings) { return app; } -module.exports = configure; +module.exports = create; diff --git a/lib/middleware/index.js b/lib/middleware/index.js new file mode 100644 index 00000000000..86ec9f3d554 --- /dev/null +++ b/lib/middleware/index.js @@ -0,0 +1,18 @@ + +var wares = { + verifyAuthorization : require('./verify-token'), + sendJSONStatus : require('./send-json-status'), + requireSSL : require('./require-ssl') +}; + +function configure (env) { + var middle = { + verifyAuthorization: wares.verifyAuthorization(env), + sendJSONStatus: wares.sendJSONStatus( ), + requireSSL: wares.requireSSL( ) + }; + return middle; +} + +configure.wares = wares; +module.exports = configure; From 3d5e4377d5267d38c16ba41bc117bbfe175dfa10 Mon Sep 17 00:00:00 2001 From: Ben West Date: Thu, 17 Jul 2014 20:30:40 -0700 Subject: [PATCH 04/14] make some breathing room for separate entries --- .travis.yml | 7 ++++++- lib/api.js | 4 ++++ lib/api/entries/index.js | 16 ++++++++++++++++ package.json | 3 +++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 lib/api/entries/index.js diff --git a/.travis.yml b/.travis.yml index c9b7fcd947a..100966b2994 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,9 @@ language: node_js node_js: - 0.10 - - 0.11 \ No newline at end of file + - 0.11 +services: + - mongodb +before_script: + - sleep 10 + - mongo mongo_travis diff --git a/lib/api.js b/lib/api.js index 937de4054cf..07be04138e5 100644 --- a/lib/api.js +++ b/lib/api.js @@ -223,6 +223,10 @@ function configure (wares, entries, settings) { }, req.params.id); }); + /**********\ + * Settings + \**********/ + // Fetch settings api.get('/settings', function(req, res) { settings.getSettings(function(err, settings) { diff --git a/lib/api/entries/index.js b/lib/api/entries/index.js new file mode 100644 index 00000000000..5b1567d0ee5 --- /dev/null +++ b/lib/api/entries/index.js @@ -0,0 +1,16 @@ + +var consts = require('../../constants'); +var es = require('event-stream'); +var sgvdata = require('sgvdata'); + +/**********\ + * Entries +\**********/ +function configure (app, wares, entries) { + var express = require('express'), + api = express.Router( ) + ; + + return api; +} +module.exports = configure; diff --git a/package.json b/package.json index 1985fa92f5b..215fab3ea07 100644 --- a/package.json +++ b/package.json @@ -40,5 +40,8 @@ "express-extension-to-accept": "0.0.2", "event-stream": "~3.1.5", "sgvdata": "0.0.2" + }, + "devDependencies": { + "supertest": "~0.13.0" } } From c488682cdaa117f1f939143422cd54154837580b Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 01:29:41 -0700 Subject: [PATCH 05/14] move entries api to separate module Make as few adjustments as needed to get entries api into it's own module. --- lib/api.js | 151 ++++------------------------------ lib/api/entries/index.js | 129 +++++++++++++++++++++++++++++ lib/api/entries/middleware.js | 0 3 files changed, 146 insertions(+), 134 deletions(-) create mode 100644 lib/api/entries/middleware.js diff --git a/lib/api.js b/lib/api.js index 07be04138e5..b8cfdcc794c 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,8 +1,6 @@ 'use strict'; var consts = require('./constants'); -var es = require('event-stream'); -var sgvdata = require('sgvdata'); /* * API - Expose Nightscout HTTP API @@ -23,7 +21,7 @@ function create (env, entries, settings) { app.set('title', 'Nightscout API v1'); return configure.call(app, wares, entries, settings); } -create.configure = configure; + function configure (wares, entries, settings) { // our globals @@ -52,11 +50,19 @@ function configure (wares, entries, settings) { api.use(require('express-extension-to-accept')([ 'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv' ])); + var common = [ + sendJSONStatus, + bodyParser.raw(), + bodyParser.json(), + require('express-extension-to-accept')([ + 'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv' + ]), + bodyParser.urlencoded({ extended: true }) + ]; + wares.common = common; // also support url-encoded content-type - api.use(bodyParser.urlencoded({ - extended: true - })); + api.use(bodyParser.urlencoded({ extended: true })); /* * Start setting up routes @@ -97,131 +103,8 @@ function configure (wares, entries, settings) { /**********\ * Entries \**********/ - // app.use('/entries', require('./api/entries/')(app, wares, entries)); - - // Middleware to format any response involving entries. - function format_entries (req, res, next) { - var output = es.readArray(res.entries || [ ]); - if (res.entries_err) return res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); - return res.format({ - text: function ( ) { - es.pipeline(output, sgvdata.format( ), res); - }, - json: function ( ) { - es.pipeline(output, sgvdata.lint({strict: false}), es.writeArray(function (err, out) { - res.json(out); - })); - } - }); - } - - // middleware to process "uploads" of sgv data - function insert_entries (req, res, next) { - // list of incoming records - var incoming = [ ]; - // Potentially a single json encoded body. - // This can happen from either an url-encoded or json content-type. - if ('sgv' in req.body) { - // add it to the incoming list - incoming.push(req.body); - } - // potentially a list of json entries - if (req.body.length) { - // add them to the list - incoming = incoming.concat(req.body); - } - - /* - * inputs -> - * in node, pipe is the most interoperable interface - * inputs returns a readable stream representing all the potential - * records from the HTTP body. - * Most content-types are handled by express middeware. - * However, text/* types are given to us as a raw buffer, this - * function switches between these two variants to find the - * correct input stream. - * stream, so use svgdata to handle those. - * The inputs stream always emits sgv json objects. - */ - function inputs ( ) { - var input; - // handle all text types - if (req.is('text/*')) { - // re-use the svgdata parsing stream - input = es.pipeline(req, sgvdata.parse( )); - return input; - } - // use established list - return es.readArray(incoming); - } - - // return a writable persistent storage stream - function persist (fn) { - if (req.persist_entries) { - // store everything - return entries.persist(fn); - } - // support a preview mode, just lint everything - return es.pipeline(entries.map( ), es.writeArray(fn)); - } - - // store results and move to the next middleware - function done (err, result) { - res.entries = result; - res.entries_err = err; - return next( ); - } - - // pipe everything to persistent storage - // when finished, pass to the next piece of middleware - es.pipeline(inputs( ), persist(done)); - } - - api.get('/entries', function(req, res, next) { - // If "?count=" is present, use that number to decided how many to return. - var query = req.query; - entries.list(query, function(err, entries) { - res.entries = entries; - res.entries_err = err; - return next( ); - }); - return; - }, format_entries); - - api.get('/entries/current', function(req, res, next) { - entries.list({count: 1}, function(err, records) { - res.entries = records; - res.entries_err = err; - return next( ); - }); - return; - }, format_entries); - - - // Allow previewing your post content, just echos everything you - // posted back out. - api.post('/entries/preview', function (req, res, next) { - req.persist_entries = false; - next( ); - return; - }, insert_entries, format_entries); - - // Create and store new sgv entries - api.post('/entries', verifyAuthorization, function (req, res, next) { - req.persist_entries = true; - next( ); - return; - }, insert_entries, format_entries); - - // Fetch one entry by id - api.get('/entries/:id', function(req, res) { - entries.getEntry(function(err, entry) { - if (err) - res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); - else - return res.json(entry); - }, req.params.id); - }); + app.use('/entries', require('./api/entries/')(app, wares, entries)); + // api.route('/entries', require('./api/entries/')(app, wares, entries)); /**********\ * Settings @@ -268,9 +151,7 @@ function configure (wares, entries, settings) { function debug ( ) { console.log('ROUTES', this.stack); - for (var k in this) { - console.log('KEY', k); - } + // for (var k in this) { console.log('KEY', k); } console.log(this.settings); console.log(this.locals); console.log("FOOBAR", this.get && this.get('foobar')) @@ -283,5 +164,7 @@ function configure (wares, entries, settings) { return app; } + +create.configure = configure; module.exports = create; diff --git a/lib/api/entries/index.js b/lib/api/entries/index.js index 5b1567d0ee5..edeade4c224 100644 --- a/lib/api/entries/index.js +++ b/lib/api/entries/index.js @@ -11,6 +11,135 @@ function configure (app, wares, entries) { api = express.Router( ) ; + wares.common.forEach(function use (middle) { + api.use(middle); + }); + + // Middleware to format any response involving entries. + function format_entries (req, res, next) { + var output = es.readArray(res.entries || [ ]); + if (res.entries_err) return res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + return res.format({ + text: function ( ) { + es.pipeline(output, sgvdata.format( ), res); + }, + json: function ( ) { + es.pipeline(output, sgvdata.lint({strict: false}), es.writeArray(function (err, out) { + res.json(out); + })); + } + }); + } + + // middleware to process "uploads" of sgv data + function insert_entries (req, res, next) { + // list of incoming records + var incoming = [ ]; + // Potentially a single json encoded body. + // This can happen from either an url-encoded or json content-type. + if ('sgv' in req.body) { + // add it to the incoming list + incoming.push(req.body); + } + // potentially a list of json entries + if (req.body.length) { + // add them to the list + incoming = incoming.concat(req.body); + } + + /* + * inputs -> + * in node, pipe is the most interoperable interface + * inputs returns a readable stream representing all the potential + * records from the HTTP body. + * Most content-types are handled by express middeware. + * However, text/* types are given to us as a raw buffer, this + * function switches between these two variants to find the + * correct input stream. + * stream, so use svgdata to handle those. + * The inputs stream always emits sgv json objects. + */ + function inputs ( ) { + var input; + // handle all text types + if (req.is('text/*')) { + // re-use the svgdata parsing stream + input = es.pipeline(req, sgvdata.parse( )); + return input; + } + // use established list + return es.readArray(incoming); + } + + // return a writable persistent storage stream + function persist (fn) { + if (req.persist_entries) { + // store everything + return entries.persist(fn); + } + // support a preview mode, just lint everything + return es.pipeline(entries.map( ), es.writeArray(fn)); + } + + // store results and move to the next middleware + function done (err, result) { + res.entries = result; + res.entries_err = err; + return next( ); + } + + // pipe everything to persistent storage + // when finished, pass to the next piece of middleware + es.pipeline(inputs( ), persist(done)); + } + + api.get('/', function(req, res, next) { + // If "?count=" is present, use that number to decided how many to return. + var query = req.query; + entries.list(query, function(err, entries) { + res.entries = entries; + res.entries_err = err; + return next( ); + }); + return; + }, format_entries); + + api.get('/current', function(req, res, next) { + entries.list({count: 1}, function(err, records) { + res.entries = records; + res.entries_err = err; + return next( ); + }); + return; + }, format_entries); + + + // Allow previewing your post content, just echos everything you + // posted back out. + api.post('/preview', function (req, res, next) { + req.persist_entries = false; + next( ); + return; + }, insert_entries, format_entries); + + // Create and store new sgv entries + api.post('/', wares.verifyAuthorization, function (req, res, next) { + req.persist_entries = true; + next( ); + return; + }, insert_entries, format_entries); + + // Fetch one entry by id + api.get('/find/:id', function(req, res) { + entries.getEntry(function(err, entry) { + if (err) + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + else + return res.json(entry); + }, req.params.id); + }); + + return api; } module.exports = configure; diff --git a/lib/api/entries/middleware.js b/lib/api/entries/middleware.js new file mode 100644 index 00000000000..e69de29bb2d From e7b68cca338fe7addefe866f82903435cddcd748 Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 01:56:23 -0700 Subject: [PATCH 06/14] quick clean up --- lib/api.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/api.js b/lib/api.js index b8cfdcc794c..d30c0a538bc 100644 --- a/lib/api.js +++ b/lib/api.js @@ -131,9 +131,7 @@ function configure (wares, entries, settings) { api.put('/settings', verifyAuthorization, function(req, res) { // Retrieve the JSON formatted record. var json = req.body; - console.log(req.body, json); - // Send the new settings to mongodb. settings.updateSettings(json, function(err, config) { if (err) @@ -149,18 +147,7 @@ function configure (wares, entries, settings) { }); }); - function debug ( ) { - console.log('ROUTES', this.stack); - // for (var k in this) { console.log('KEY', k); } - console.log(this.settings); - console.log(this.locals); - console.log("FOOBAR", this.get && this.get('foobar')) - } - app.set('foobar', "MY VALUE"); - - debug.call(api); app.use(api); - debug.call(app); return app; } From 753496c4fc8619a2d6cdd9c273b4fb42a750527d Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 02:18:08 -0700 Subject: [PATCH 07/14] preparing to seperate settings and experiments --- lib/api.js | 10 ++++++---- lib/middleware/index.js | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/api.js b/lib/api.js index d30c0a538bc..f2648b12675 100644 --- a/lib/api.js +++ b/lib/api.js @@ -69,11 +69,12 @@ function configure (wares, entries, settings) { */ // Some experiments - api.get('/authorized/:secret/test', verifyAuthorization, function (req, res, next) { + // app.use('/authorized', require('./api/experiments/')(app, wares)); + api.get('/authorized/:secret/test', wares.verifyAuthorization, function (req, res, next) { return res.json({status: 'ok'}); }); - api.get('/authorized/test', verifyAuthorization, function (req, res, next) { + api.get('/authorized/test', wares.verifyAuthorization, function (req, res, next) { return res.json({status: 'ok'}); }); @@ -109,6 +110,7 @@ function configure (wares, entries, settings) { /**********\ * Settings \**********/ + // app.use('/settings', require('./api/settings/')(app, wares, settings)); // Fetch settings api.get('/settings', function(req, res) { @@ -121,14 +123,14 @@ function configure (wares, entries, settings) { }); // Delete settings - api.delete('/settings', verifyAuthorization, function(req, res) { + api.delete('/settings', wares.verifyAuthorization, function(req, res) { settings.remove(function ( ) { res.json({ }); }); }); // Replace settings - api.put('/settings', verifyAuthorization, function(req, res) { + api.put('/settings', wares.verifyAuthorization, function(req, res) { // Retrieve the JSON formatted record. var json = req.body; diff --git a/lib/middleware/index.js b/lib/middleware/index.js index 86ec9f3d554..d27b412e30c 100644 --- a/lib/middleware/index.js +++ b/lib/middleware/index.js @@ -2,14 +2,16 @@ var wares = { verifyAuthorization : require('./verify-token'), sendJSONStatus : require('./send-json-status'), - requireSSL : require('./require-ssl') + requireSSL : require('./require-ssl'), + bodyParser : require('body-parser') }; function configure (env) { var middle = { verifyAuthorization: wares.verifyAuthorization(env), sendJSONStatus: wares.sendJSONStatus( ), - requireSSL: wares.requireSSL( ) + requireSSL: wares.requireSSL( ), + bodyParser: wares.bodyParser }; return middle; } From 8b7c088fa6fab4200eab0b7f20d0103cf817d150 Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 02:19:16 -0700 Subject: [PATCH 08/14] remove dead code --- lib/api.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/api.js b/lib/api.js index f2648b12675..b66a2d17fb0 100644 --- a/lib/api.js +++ b/lib/api.js @@ -32,9 +32,6 @@ function configure (wares, entries, settings) { ; // some middleware - // var verifyAuthorization = require('./middleware/verify-token')(env), - // sendJSONStatus = require('./middleware/send-json-status')( ), - // bodyParser = require('body-parser') var verifyAuthorization = wares.verifyAuthorization, sendJSONStatus = wares.sendJSONStatus, bodyParser = require('body-parser') @@ -105,7 +102,6 @@ function configure (wares, entries, settings) { * Entries \**********/ app.use('/entries', require('./api/entries/')(app, wares, entries)); - // api.route('/entries', require('./api/entries/')(app, wares, entries)); /**********\ * Settings From c3eca8efd7893b2c07c5b8f126623f2f3724700a Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 02:42:50 -0700 Subject: [PATCH 09/14] move experiments and settings to own modules Also remove notion of common configured middleware. --- lib/api.js | 71 +----------------------------------- lib/api/entries/index.js | 29 ++++++++++----- lib/api/experiments/index.js | 22 +++++++++++ lib/api/settings/index.js | 69 +++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 78 deletions(-) create mode 100644 lib/api/experiments/index.js create mode 100644 lib/api/settings/index.js diff --git a/lib/api.js b/lib/api.js index b66a2d17fb0..79981945c4e 100644 --- a/lib/api.js +++ b/lib/api.js @@ -37,43 +37,13 @@ function configure (wares, entries, settings) { bodyParser = require('body-parser') ; - // invoke common middleware - api.use(sendJSONStatus); - // text body types get handled as raw buffer stream - api.use(bodyParser.raw()); - // json body types get handled as parsed json - api.use(bodyParser.json()); - // shortcut to use extension to specify output content-type - api.use(require('express-extension-to-accept')([ - 'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv' - ])); - var common = [ - sendJSONStatus, - bodyParser.raw(), - bodyParser.json(), - require('express-extension-to-accept')([ - 'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv' - ]), - bodyParser.urlencoded({ extended: true }) - ]; - wares.common = common; - - // also support url-encoded content-type - api.use(bodyParser.urlencoded({ extended: true })); /* * Start setting up routes */ // Some experiments - // app.use('/authorized', require('./api/experiments/')(app, wares)); - api.get('/authorized/:secret/test', wares.verifyAuthorization, function (req, res, next) { - return res.json({status: 'ok'}); - }); - - api.get('/authorized/test', wares.verifyAuthorization, function (req, res, next) { - return res.json({status: 'ok'}); - }); + app.use('/experiments', require('./api/experiments/')(app, wares)); // Status badge/text/json api.get('/status', function (req, res, next) { @@ -106,44 +76,7 @@ function configure (wares, entries, settings) { /**********\ * Settings \**********/ - // app.use('/settings', require('./api/settings/')(app, wares, settings)); - - // Fetch settings - api.get('/settings', function(req, res) { - settings.getSettings(function(err, settings) { - if (err) - res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); - else - return res.json(settings); - }); - }); - - // Delete settings - api.delete('/settings', wares.verifyAuthorization, function(req, res) { - settings.remove(function ( ) { - res.json({ }); - }); - }); - - // Replace settings - api.put('/settings', wares.verifyAuthorization, function(req, res) { - // Retrieve the JSON formatted record. - var json = req.body; - - // Send the new settings to mongodb. - settings.updateSettings(json, function(err, config) { - if (err) - res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); - else { - // Add a warning to the outgoing status when HTTPS is not being used. - var warning = ''; - if (req.secure === false) - warning = 'WARNING: HTTPS is required to secure your data!'; - - res.json(config); - } - }); - }); + app.use('/settings', require('./api/settings/')(app, wares, settings)); app.use(api); diff --git a/lib/api/entries/index.js b/lib/api/entries/index.js index edeade4c224..4933048357c 100644 --- a/lib/api/entries/index.js +++ b/lib/api/entries/index.js @@ -11,9 +11,18 @@ function configure (app, wares, entries) { api = express.Router( ) ; - wares.common.forEach(function use (middle) { - api.use(middle); - }); + // invoke common middleware + api.use(wares.sendJSONStatus); + // text body types get handled as raw buffer stream + api.use(wares.bodyParser.raw()); + // json body types get handled as parsed json + api.use(wares.bodyParser.json()); + // shortcut to use extension to specify output content-type + api.use(require('express-extension-to-accept')([ + 'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv' + ])); + // also support url-encoded content-type + api.use(wares.bodyParser.urlencoded({ extended: true })); // Middleware to format any response involving entries. function format_entries (req, res, next) { @@ -122,12 +131,14 @@ function configure (app, wares, entries) { return; }, insert_entries, format_entries); - // Create and store new sgv entries - api.post('/', wares.verifyAuthorization, function (req, res, next) { - req.persist_entries = true; - next( ); - return; - }, insert_entries, format_entries); + if (app.enabled('api')) { + // Create and store new sgv entries + api.post('/', wares.verifyAuthorization, function (req, res, next) { + req.persist_entries = true; + next( ); + return; + }, insert_entries, format_entries); + } // Fetch one entry by id api.get('/find/:id', function(req, res) { diff --git a/lib/api/experiments/index.js b/lib/api/experiments/index.js new file mode 100644 index 00000000000..4b1ce3130c8 --- /dev/null +++ b/lib/api/experiments/index.js @@ -0,0 +1,22 @@ + +var consts = require('../../constants'); + +function configure (app, wares) { + var express = require('express'), + api = express.Router( ) + ; + + if (app.enabled('api')) { + api.get('/:secret/test', wares.verifyAuthorization, function (req, res, next) { + return res.json({status: 'ok'}); + }); + + api.get('/test', wares.verifyAuthorization, function (req, res, next) { + return res.json({status: 'ok'}); + }); + } + + return api; +} + +module.exports = configure; diff --git a/lib/api/settings/index.js b/lib/api/settings/index.js new file mode 100644 index 00000000000..4bfd7c36fbb --- /dev/null +++ b/lib/api/settings/index.js @@ -0,0 +1,69 @@ + +var consts = require('../../constants'); + +function configure (app, wares, settings) { + var express = require('express'), + api = express.Router( ) + ; + // invoke common middleware + api.use(wares.sendJSONStatus); + // text body types get handled as raw buffer stream + api.use(wares.bodyParser.raw( )); + // json body types get handled as parsed json + api.use(wares.bodyParser.json( )); + // also support url-encoded content-type + api.use(wares.bodyParser.urlencoded({ extended: true })); + + + /**********\ + * Settings + \**********/ + // Fetch settings + api.get('/', function(req, res) { + settings.getSettings(function(err, settings) { + if (err) + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + else + return res.json(settings); + }); + }); + + function config_authed (app, api, wares, settings) { + + // Delete settings + api.delete('/', wares.verifyAuthorization, function(req, res) { + settings.remove(function ( ) { + res.json({ }); + }); + }); + + // Replace settings + api.put('/', wares.verifyAuthorization, function(req, res) { + // Retrieve the JSON formatted record. + var json = req.body; + + // Send the new settings to mongodb. + settings.updateSettings(json, function(err, config) { + if (err) + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + else { + // Add a warning to the outgoing status when HTTPS is not being used. + var warning = ''; + if (req.secure === false) + warning = 'WARNING: HTTPS is required to secure your data!'; + + res.json(config); + } + }); + }); + } + + if (app.enabled('api')) { + config_authed(app, api, wares, settings); + } + + return api; +} + +module.exports = configure; + From 93cc66dde1cd0ad5ccad47561fddc7853c5152fe Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 03:00:31 -0700 Subject: [PATCH 10/14] remove bit of dead code --- lib/api.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/api.js b/lib/api.js index 79981945c4e..3e6aa140571 100644 --- a/lib/api.js +++ b/lib/api.js @@ -31,13 +31,6 @@ function configure (wares, entries, settings) { api = express.Router( ) ; - // some middleware - var verifyAuthorization = wares.verifyAuthorization, - sendJSONStatus = wares.sendJSONStatus, - bodyParser = require('body-parser') - ; - - /* * Start setting up routes */ From 80ea7c6e9f7616777b927017c7ba5bd667a60e79 Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 03:21:31 -0700 Subject: [PATCH 11/14] move all rest components to separate modules --- lib/api.js | 24 +---------------------- lib/api/entries/index.js | 3 ++- lib/api/index.js | 41 ++++++++++++++++++++++++++++++++++++++++ lib/api/status.js | 35 ++++++++++++++++++++++++++++++++++ lib/middleware/index.js | 7 ++++++- server.js | 11 ++--------- 6 files changed, 87 insertions(+), 34 deletions(-) create mode 100644 lib/api/index.js create mode 100644 lib/api/status.js diff --git a/lib/api.js b/lib/api.js index 3e6aa140571..c39e2d218a8 100644 --- a/lib/api.js +++ b/lib/api.js @@ -38,29 +38,7 @@ function configure (wares, entries, settings) { // Some experiments app.use('/experiments', require('./api/experiments/')(app, wares)); - // Status badge/text/json - api.get('/status', function (req, res, next) { - var status = {status: 'ok'}; - var badge = 'http://img.shields.io/badge/Nightscout-OK-green'; - return res.format({ - html: function ( ) { - res.send("

STATUS OK

"); - }, - png: function ( ) { - res.redirect(302, badge + '.png'); - }, - svg: function ( ) { - res.redirect(302, badge + '.svg'); - }, - text: function ( ) { - res.send("STATUS OK"); - }, - json: function ( ) { - res.json(status); - } - }); - }); - + app.use('/status', require('./api/status')(app, wares)); /**********\ * Entries \**********/ diff --git a/lib/api/entries/index.js b/lib/api/entries/index.js index 4933048357c..cadebecda41 100644 --- a/lib/api/entries/index.js +++ b/lib/api/entries/index.js @@ -18,7 +18,8 @@ function configure (app, wares, entries) { // json body types get handled as parsed json api.use(wares.bodyParser.json()); // shortcut to use extension to specify output content-type - api.use(require('express-extension-to-accept')([ + // api.use(require('express-extension-to-accept')([ + api.use(wares.extensions([ 'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv' ])); // also support url-encoded content-type diff --git a/lib/api/index.js b/lib/api/index.js new file mode 100644 index 00000000000..b5a2ccd592b --- /dev/null +++ b/lib/api/index.js @@ -0,0 +1,41 @@ + +function create (env, entries, settings) { + var express = require('express'), + app = express( ) + ; + + var wares = require('../middleware/')(env); + + // Only allow access to the API if API_SECRET is set on the server. + app.disable('api'); + if (env.api_secret) { + console.log("API_SECRET", env.api_secret); + app.enable('api'); + } + + // set up express basics + app.set('title', 'Nightscout API v1'); + + /* + * Start setting up routes + */ + + if (app.enabled('api')) { + app.use('/experiments', require('./experiments/')(app, wares)); + } + + /**********\ + * Entries + \**********/ + app.use('/entries', require('./entries/')(app, wares, entries)); + + /**********\ + * Settings + \**********/ + app.use('/settings', require('./settings/')(app, wares, settings)); + + app.use('/status', require('./status')(app, wares)); + return app; +} + +module.exports = create; diff --git a/lib/api/status.js b/lib/api/status.js new file mode 100644 index 00000000000..06b662e4c68 --- /dev/null +++ b/lib/api/status.js @@ -0,0 +1,35 @@ + +function configure (app, wares) { + var express = require('express'), + api = express.Router( ) + ; + + api.use(wares.extensions([ + 'json', 'svg', 'csv', 'txt', 'png', 'html' + ])); + // Status badge/text/json + api.get('/', function (req, res, next) { + var status = {status: 'ok'}; + var badge = 'http://img.shields.io/badge/Nightscout-OK-green'; + return res.format({ + html: function ( ) { + res.send("

STATUS OK

"); + }, + png: function ( ) { + res.redirect(302, badge + '.png'); + }, + svg: function ( ) { + res.redirect(302, badge + '.svg'); + }, + text: function ( ) { + res.send("STATUS OK"); + }, + json: function ( ) { + res.json(status); + } + }); + }); + + return api; +} +module.exports = configure; diff --git a/lib/middleware/index.js b/lib/middleware/index.js index d27b412e30c..1c9e07544b7 100644 --- a/lib/middleware/index.js +++ b/lib/middleware/index.js @@ -6,12 +6,17 @@ var wares = { bodyParser : require('body-parser') }; +function extensions (list) { + return require('express-extension-to-accept')(list); +} + function configure (env) { var middle = { verifyAuthorization: wares.verifyAuthorization(env), sendJSONStatus: wares.sendJSONStatus( ), requireSSL: wares.requireSSL( ), - bodyParser: wares.bodyParser + bodyParser: wares.bodyParser, + extensions: extensions }; return middle; } diff --git a/server.js b/server.js index 2051a13c5ec..4948c5ecd0f 100644 --- a/server.js +++ b/server.js @@ -30,7 +30,7 @@ var express = require('express'); /////////////////////////////////////////////////// var entries = require('./lib/entries')(env.mongo_collection, store); var settings = require('./lib/settings')(env.settings_collection, store); -var api = require('./lib/api')(env, entries, settings); +var api = require('./lib/api/')(env, entries, settings); var pebble = require('./lib/pebble'); /////////////////////////////////////////////////// @@ -45,17 +45,10 @@ var appInfo = package.name + ' ' + package.version; app.set('title', appInfo); app.enable('trust proxy'); // Allows req.secure test on heroku https connections. -// Only allow access to the API if API_SECRET is set on the server. if (env.api_secret) { - var requireSSL = require('./lib/middleware/require-ssl')( ); console.log("API_SECRET", env.api_secret); - - // Display an error when you're not using SSL. - app.use('/api/v1', requireSSL); - - // Handle API requests. - app.use('/api/v1', api); } +app.use('/api/v1', api); // pebble data app.get('/pebble', pebble(entries)); From 65a1aceba21aa15b602e1029244d2925ce017064 Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 03:28:01 -0700 Subject: [PATCH 12/14] remove api.js in favor of api/ module --- lib/api.js | 59 ------------------------------------ lib/api/experiments/index.js | 1 + 2 files changed, 1 insertion(+), 59 deletions(-) delete mode 100644 lib/api.js diff --git a/lib/api.js b/lib/api.js deleted file mode 100644 index c39e2d218a8..00000000000 --- a/lib/api.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; - -var consts = require('./constants'); - -/* - * API - Expose Nightscout HTTP API - * This implementation is designed to work with express. - */ - -function create (env, entries, settings) { - var express = require('express'), - app = express( ); - - var wares = require('./middleware/')(env); - app.disable('api'); - if (env.api_secret) { - console.log("API_SECRET", env.api_secret); - app.enable('api'); - } - // set up express basics - app.set('title', 'Nightscout API v1'); - return configure.call(app, wares, entries, settings); -} - -function configure (wares, entries, settings) { - - // our globals - var express = require('express'), - // app = express( ), - app = this, - api = express.Router( ) - ; - - /* - * Start setting up routes - */ - - // Some experiments - app.use('/experiments', require('./api/experiments/')(app, wares)); - - app.use('/status', require('./api/status')(app, wares)); - /**********\ - * Entries - \**********/ - app.use('/entries', require('./api/entries/')(app, wares, entries)); - - /**********\ - * Settings - \**********/ - app.use('/settings', require('./api/settings/')(app, wares, settings)); - - app.use(api); - - return app; -} - -create.configure = configure; -module.exports = create; - diff --git a/lib/api/experiments/index.js b/lib/api/experiments/index.js index 4b1ce3130c8..5d5ea0f9b99 100644 --- a/lib/api/experiments/index.js +++ b/lib/api/experiments/index.js @@ -7,6 +7,7 @@ function configure (app, wares) { ; if (app.enabled('api')) { + api.use(wares.sendJSONStatus); api.get('/:secret/test', wares.verifyAuthorization, function (req, res, next) { return res.json({status: 'ok'}); }); From 873345da7ef776ab1dea0c4af8abc958d866e125 Mon Sep 17 00:00:00 2001 From: Ben West Date: Fri, 18 Jul 2014 11:19:15 -0700 Subject: [PATCH 13/14] let test run --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 100966b2994..6f150205042 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,4 +6,4 @@ services: - mongodb before_script: - sleep 10 - - mongo mongo_travis + - echo mongo mongo_travis From 575b11532485ca6a03cb0194a8d9d2a0c17851e0 Mon Sep 17 00:00:00 2001 From: Brian Hanifin Date: Fri, 18 Jul 2014 13:19:32 -0700 Subject: [PATCH 14/14] Update package.json --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 215fab3ea07..8d58f9b388b 100644 --- a/package.json +++ b/package.json @@ -32,14 +32,14 @@ }, "dependencies": { "body-parser": "^1.4.3", - "bower": "~1.3.2", + "bower": "^1.3.8", "errorhandler": "^1.1.1", - "express": "^4.5.1", - "mongodb": "^1.4.7", - "socket.io": "^0.9.17", - "express-extension-to-accept": "0.0.2", "event-stream": "~3.1.5", - "sgvdata": "0.0.2" + "express": "^4.6.1", + "express-extension-to-accept": "0.0.2", + "mongodb": "^1.4.7", + "sgvdata": "0.0.2", + "socket.io": "^0.9.17" }, "devDependencies": { "supertest": "~0.13.0"