diff --git a/lib/api/devicestatus/index.js b/lib/api/devicestatus/index.js index 5c665b1ffc5..91702902fa3 100644 --- a/lib/api/devicestatus/index.js +++ b/lib/api/devicestatus/index.js @@ -1,17 +1,18 @@ 'use strict'; -var consts = require('../../constants'); +const consts = require('../../constants'); +const moment = require('moment'); -function configure (app, wares, ctx) { - var express = require('express'), - api = express.Router( ); +function configure (app, wares, ctx, env) { + 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( )); + api.use(wares.bodyParser.raw()); // json body types get handled as parsed json - api.use(wares.bodyParser.json( )); + api.use(wares.bodyParser.json()); // also support url-encoded content-type api.use(wares.bodyParser.urlencoded({ extended: true })); @@ -23,7 +24,21 @@ function configure (app, wares, ctx) { if (!q.count) { q.count = 10; } - ctx.devicestatus.list(q, function (err, results) { + + ctx.devicestatus.list(q, function(err, results) { + + // Support date de-normalization for older clients + if (env.settings.deNormalizeDates) { + results.forEach(function(e) { + // eslint-disable-next-line no-prototype-builtins + if (e.created_at && e.hasOwnProperty('utcOffset')) { + const d = moment(e.created_at).utcOffset(e.utcOffset); + e.created_at = d.toISOString(true); + delete e.utcOffset; + } + }); + } + return res.json(results); }); }); @@ -32,7 +47,7 @@ function configure (app, wares, ctx) { function doPost (req, res) { var obj = req.body; - ctx.devicestatus.create(obj, function (err, created) { + ctx.devicestatus.create(obj, function(err, created) { if (err) { res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); } else { @@ -48,7 +63,7 @@ function configure (app, wares, ctx) { * Delete devicestatus. The query logic works the same way as find/list. This * endpoint uses same search logic to remove records from the database. */ - function delete_records(req, res, next) { + function delete_records (req, res, next) { var query = req.query; if (!query.count) { query.count = 10 @@ -91,7 +106,7 @@ function configure (app, wares, ctx) { api.delete('/devicestatus/', ctx.authorization.isPermitted('api:devicestatus:delete'), delete_records); } - if (app.enabled('api') || true /*TODO: auth disabled for quick UI testing...*/) { + if (app.enabled('api') || true /*TODO: auth disabled for quick UI testing...*/ ) { config_authed(app, api, wares, ctx); } @@ -99,4 +114,3 @@ function configure (app, wares, ctx) { } module.exports = configure; - diff --git a/lib/api/entries/index.js b/lib/api/entries/index.js index 0280c51b34a..ed663c270fa 100644 --- a/lib/api/entries/index.js +++ b/lib/api/entries/index.js @@ -1,18 +1,19 @@ 'use strict'; -var _last = require('lodash/last'); -var _isNil = require('lodash/isNil'); -var _first = require('lodash/first'); -var _includes = require('lodash/includes'); +const _last = require('lodash/last'); +const _isNil = require('lodash/isNil'); +const _first = require('lodash/first'); +const _includes = require('lodash/includes'); +const moment = require('moment'); -var consts = require('../../constants'); -var es = require('event-stream'); -var braces = require('braces'); -var expand = braces.expand; +const consts = require('../../constants'); +const es = require('event-stream'); +const braces = require('braces'); +const expand = braces.expand; -var ID_PATTERN = /^[a-f\d]{24}$/; +const ID_PATTERN = /^[a-f\d]{24}$/; -function isId(value) { +function isId (value) { //TODO: why did we need tht length check? return value && ID_PATTERN.test(value) && value.length === 24; } @@ -31,10 +32,10 @@ function isId(value) { * @param Object ctx The global ctx with all modules, storage, and event buses * configured. */ -function configure(app, wares, ctx) { +function configure (app, wares, ctx, env) { // default storage biased towards entries. - var entries = ctx.entries; - var express = require('express') + const entries = ctx.entries; + const express = require('express') , api = express.Router(); // invoke common middleware @@ -59,17 +60,28 @@ function configure(app, wares, ctx) { * elements on the stream have a `type` field. * Generate a stream that ensures elements have a `type` field. */ - function force_typed_data(opts) { + function force_typed_data (opts) { /** * @function sync * Iterate over every element in the stream, enforcing some data type. */ - function sync(data, next) { + function sync (data, next) { // if element has no data type, but we know what the type should be if (!data.type && opts.type) { // bless absence with known type data.type = opts.type; } + + // Support date de-normalization for older clients + if (env.settings.deNormalizeDates) { + // eslint-disable-next-line no-prototype-builtins + if (data.dateString && data.hasOwnProperty('utcOffset')) { + const d = moment(data.dateString).utcOffset(data.utcOffset); + data.dateString = d.toISOString(true); + delete data.utcOffset; + } + } + // continue control flow to next element in the stream next(null, data); } @@ -79,7 +91,7 @@ function configure(app, wares, ctx) { // check for last modified from in-memory data - function ifModifiedSinceCTX(req, res, next) { + function ifModifiedSinceCTX (req, res, next) { var lastEntry = _last(ctx.ddata.sgvs); var lastEntryDate = null; @@ -116,23 +128,22 @@ function configure(app, wares, ctx) { * We expect a payload to be attached to `res.entries`. // Middleware to format any response involving entries. */ - function format_entries(req, res) { + function format_entries (req, res) { // deduce what type of records we might expect var type_params = { type: (req.query && req.query.find && req.query.find.type && req.query.find.type !== req.params.model) ? req.query.find.type : req.params.model }; - // prepare a stream of elements from some prepared payload - var output = es.readArray(res.entries || []); - // on other hand, if there's been some error, report that + + // f there's been some error, report that if (res.entries_err) { return res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', res.entries_err); } // IF-Modified-Since support - function compare(a, b) { + function compare (a, b) { var a_field = a.mills ? a.mills : a.date; var b_field = b.mills ? b.mills : b.date; @@ -148,6 +159,9 @@ function configure(app, wares, ctx) { var lastEntry = _first(res.entries); var lastEntryDate = null; + // prepare a stream of elements from some prepared payload + var output = es.readArray(res.entries || []); + if (!_isNil(lastEntry)) { if (lastEntry.mills) lastEntryDate = new Date(lastEntry.mills); if (!lastEntry.mills && lastEntry.date) lastEntryDate = new Date(lastEntry.date); @@ -166,9 +180,9 @@ function configure(app, wares, ctx) { return; } - function formatWithSeparator(data, separator) { - if (data === null || data.constructor !== Array || data.length == 0) return ""; - + function formatWithSeparator (data, separator) { + if (data === null || data.constructor !== Array || data.length == 0) return ""; + var outputdata = []; data.forEach(function(e) { var entry = { @@ -245,7 +259,7 @@ function configure(app, wares, ctx) { * into the configured storage layer, saving the results in mongodb. */ // middleware to process "uploads" of sgv data - function insert_entries(req, res, next) { + function insert_entries (req, res, next) { // list of incoming records var incoming = []; // Potentially a single json encoded body. @@ -266,7 +280,7 @@ function configure(app, wares, ctx) { * Sends stream elements into storage layer. * Configures the storage layer stream. */ - function persist(fn) { + function persist (fn) { if (req.persist_entries) { // store everything return entries.persist(fn); @@ -280,7 +294,7 @@ function configure(app, wares, ctx) { * Final callback store results on `res.entries`, after all I/O is done. * store results and move to the next middleware */ - function done(err, result) { + function done (err, result) { // assign payload res.entries = result; res.entries_err = err; @@ -298,7 +312,7 @@ function configure(app, wares, ctx) { * @param {String} model The name of the model to use if not found. * Sets `req.query.find.type` to your chosen model. */ - function prepReqModel(req, model) { + function prepReqModel (req, model) { var type = model || 'sgv'; if (!req.query.find) { req.query.find = { @@ -370,7 +384,6 @@ function configure(app, wares, ctx) { } }, format_entries); - /** * @module get#/entries * @route @@ -388,7 +401,7 @@ function configure(app, wares, ctx) { * Useful for understanding how REST api parameters translate into mongodb * queries. */ - function echo_query(req, res) { + function echo_query (req, res) { var query = req.query; // make a depth-wise copy of the original raw input var input = JSON.parse(JSON.stringify(query)); @@ -416,7 +429,7 @@ function configure(app, wares, ctx) { * This middleware executes the query, assigning the payload to results on * `res.entries`. */ - function query_models(req, res, next) { + function query_models (req, res, next) { var query = req.query; // If "?count=" is present, use that number to decided how many to return. @@ -427,7 +440,7 @@ function configure(app, wares, ctx) { // bias to entries, but allow expressing a preference var storage = req.storage || ctx.entries; // perform the query - storage.list(query, function payload(err, entries) { + storage.list(query, function payload (err, entries) { // assign payload res.entries = entries; res.entries_err = err; @@ -435,10 +448,10 @@ function configure(app, wares, ctx) { }); } - function count_records(req, res, next) { + function count_records (req, res, next) { var query = req.query; var storage = req.storage || ctx.entries; - storage.aggregate(query, function payload(err, entries) { + storage.aggregate(query, function payload (err, entries) { // assign payload res.entries = entries; res.entries_err = err; @@ -446,7 +459,7 @@ function configure(app, wares, ctx) { }); } - function format_results(req, res, next) { + function format_results (req, res, next) { res.json(res.entries); next(); } @@ -456,7 +469,7 @@ function configure(app, wares, ctx) { * Delete entries. The query logic works the same way as find/list. This * endpoint uses same search logic to remove records from the database. */ - function delete_records(req, res, next) { + function delete_records (req, res, next) { // bias towards model, but allow expressing a preference if (!req.model) { req.model = ctx.entries; @@ -516,22 +529,22 @@ function configure(app, wares, ctx) { api.get('/echo/:echo/:model?/:spec?', echo_query); /** - * Prepare regexp patterns based on `prefix`, and `regex` parameters. - * Translates `/:prefix/:regex` strings into fancy mongo queries. - * @method prep_patterns - * @params String prefix - * @params String regex - * This performs bash style brace/glob pattern expansion in order to generate flexible series of regex patterns. - * Very useful in querying across days, but constrained hours of time. - * Consider the following examples: -``` -curl -s -g 'http://localhost:1337/api/v1/times/2015-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120' | json -a dateString sgv -curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120' | json -a dateString sgv -curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120' | json -a dateString sgv - -``` - */ - function prep_patterns(req, res, next) { + * Prepare regexp patterns based on `prefix`, and `regex` parameters. + * Translates `/:prefix/:regex` strings into fancy mongo queries. + * @method prep_patterns + * @params String prefix + * @params String regex + * This performs bash style brace/glob pattern expansion in order to generate flexible series of regex patterns. + * Very useful in querying across days, but constrained hours of time. + * Consider the following examples: + ``` + curl -s -g 'http://localhost:1337/api/v1/times/2015-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120' | json -a dateString sgv + curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120' | json -a dateString sgv + curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120' | json -a dateString sgv + + ``` + */ + function prep_patterns (req, res, next) { // initialize empty pattern list. var pattern = []; // initialize a basic prefix @@ -552,7 +565,7 @@ curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}/T{13..18}:{00..15}'.js // create a single pattern with all inputs considered // expand the pattern using bash/glob style brace expansion to generate // an array of patterns. - + pattern = expand(pattern.join('')); if (pattern.length == 0) pattern = ['']; @@ -565,12 +578,12 @@ curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}/T{13..18}:{00..15}'.js * RegExp with the prefix and suffix prepended, and appended, * respectively. */ - function iter_regex(prefix, suffix) { + function iter_regex (prefix, suffix) { /** * @function make * @returns RegExp Make a RegExp with configured prefix and suffix */ - function make(pat) { + function make (pat) { // concat the prefix, pattern, and suffix. pat = (prefix ? prefix : '') + pat + (suffix ? suffix : ''); // return RegExp. @@ -621,7 +634,7 @@ curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}/T{13..18}:{00..15}'.js * Default is `dateString`, because that's the iso8601 field for sgv * entries. */ - function prep_pattern_field(req, res, next) { + function prep_pattern_field (req, res, next) { // If req.params.field from routed path parameter is available use it. if (req.params.field) { req.patternField = req.params.field; @@ -640,7 +653,7 @@ curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}/T{13..18}:{00..15}'.js * the entries storage layer, because that's where sgv records are stored * by default. */ - function prep_storage(req, res, next) { + function prep_storage (req, res, next) { if (req.params.storage && _includes(['entries', 'treatments', 'devicestatus'], req.params.storage)) { req.storage = ctx[req.params.storage]; } else { @@ -667,31 +680,31 @@ curl -s -g 'http://localhost:1337/api/v1/times/20{14..15}/T{13..18}:{00..15}'.js }); /** - * @module get#/times/:prefix/:regex - * Allows searching for modal times of day across days and months. -``` -/api/v1/times/2015-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120 -/api/v1/times/20{14..15}-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120 -/api/v1/times/20{14..15}/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120 -``` - * @routed - * @response 200 /definitions/Entries - */ + * @module get#/times/:prefix/:regex + * Allows searching for modal times of day across days and months. + ``` + /api/v1/times/2015-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120 + /api/v1/times/20{14..15}-04/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120 + /api/v1/times/20{14..15}/T{13..18}:{00..15}'.json'?find[sgv][$gte]=120 + ``` + * @routed + * @response 200 /definitions/Entries + */ api.get('/times/:prefix?/:regex?', prep_storage, prep_pattern_field, prep_patterns, prep_patterns, query_models, format_entries); api.get('/count/:storage/where', prep_storage, count_records, format_results); /** - * @module get#/slice/:storage/:field/:type/:prefix/:regex - * @routed - * @response 200 /definitions/Entries - * Allows searching for modal times of day across days and months. - * Also allows specifying field to perform regexp on, the storage layer to - * use, as well as which type of model to look for. -``` -/api/v1/slice/entries/dateString/mbg/2015.json -``` - */ + * @module get#/slice/:storage/:field/:type/:prefix/:regex + * @routed + * @response 200 /definitions/Entries + * Allows searching for modal times of day across days and months. + * Also allows specifying field to perform regexp on, the storage layer to + * use, as well as which type of model to look for. + ``` + /api/v1/slice/entries/dateString/mbg/2015.json + ``` + */ api.get('/slice/:storage/:field/:type?/:prefix?/:regex?', prep_storage, prep_pattern_field, prep_patterns, query_models, format_entries); /** diff --git a/lib/api/index.js b/lib/api/index.js index 3c9748c93e6..47a8a7bac3d 100644 --- a/lib/api/index.js +++ b/lib/api/index.js @@ -40,7 +40,7 @@ function create (env, ctx) { app.use(wares.extensions([ 'json', 'svg', 'csv', 'txt', 'png', 'html', 'tsv' ])); - var entriesRouter = require('./entries/')(app, wares, ctx); + var entriesRouter = require('./entries/')(app, wares, ctx, env); // Entries and settings app.all('/entries*', entriesRouter); app.all('/echo/*', entriesRouter); @@ -48,9 +48,9 @@ function create (env, ctx) { app.all('/slice/*', entriesRouter); app.all('/count/*', entriesRouter); - app.all('/treatments*', require('./treatments/')(app, wares, ctx)); + app.all('/treatments*', require('./treatments/')(app, wares, ctx, env)); app.all('/profile*', require('./profile/')(app, wares, ctx)); - app.all('/devicestatus*', require('./devicestatus/')(app, wares, ctx)); + app.all('/devicestatus*', require('./devicestatus/')(app, wares, ctx, env)); app.all('/notifications*', require('./notifications-api')(app, wares, ctx)); app.all('/activity*', require('./activity/')(app, wares, ctx)); diff --git a/lib/api/treatments/index.js b/lib/api/treatments/index.js index 0362829fc7b..5d527fce6ac 100644 --- a/lib/api/treatments/index.js +++ b/lib/api/treatments/index.js @@ -7,167 +7,175 @@ var _isArray = require('lodash/isArray'); var consts = require('../../constants'); var moment = require('moment'); -function configure(app, wares, ctx) { - var express = require('express') - , api = express.Router(); - - api.use(wares.compression()); - api.use(wares.bodyParser({ - limit: 1048576 * 50 - })); - // text body types get handled as raw buffer stream - api.use(wares.bodyParser.raw({ - limit: 1048576 - })); - // json body types get handled as parsed json - api.use(wares.bodyParser.json({ - limit: 1048576 - })); - // also support url-encoded content-type - api.use(wares.bodyParser.urlencoded({ - limit: 1048576 - , extended: true - })); - // invoke common middleware - api.use(wares.sendJSONStatus); - - api.use(ctx.authorization.isPermitted('api:treatments:read')); - - // List treatments available - api.get('/treatments', function(req, res) { - var ifModifiedSince = req.get('If-Modified-Since'); - ctx.treatments.list(req.query, function(err, results) { - var d1 = null; - - _forEach(results, function clean(t) { - t.carbs = Number(t.carbs); - t.insulin = Number(t.insulin); - - var d2 = null; - - if (t.hasOwnProperty('created_at')) { - d2 = new Date(t.created_at); - } else { - if (t.hasOwnProperty('timestamp')) { - d2 = new Date(t.timestamp); - } - } - - if (d2 == null) { return; } - - if (d1 == null || d2.getTime() > d1.getTime()) { - d1 = d2; - } - }); - - if (!_isNil(d1)) res.setHeader('Last-Modified', d1.toUTCString()); - - if (ifModifiedSince && d1.getTime() <= moment(ifModifiedSince).valueOf()) { - res.status(304).send({ - status: 304 - , message: 'Not modified' - , type: 'internal' - }); - return; - } else { - return res.json(results); - } - }); - }); +function configure (app, wares, ctx, env) { + var express = require('express') + , api = express.Router(); + + api.use(wares.compression()); + api.use(wares.bodyParser({ + limit: 1048576 * 50 + })); + // text body types get handled as raw buffer stream + api.use(wares.bodyParser.raw({ + limit: 1048576 + })); + // json body types get handled as parsed json + api.use(wares.bodyParser.json({ + limit: 1048576 + })); + // also support url-encoded content-type + api.use(wares.bodyParser.urlencoded({ + limit: 1048576 + , extended: true + })); + // invoke common middleware + api.use(wares.sendJSONStatus); + + api.use(ctx.authorization.isPermitted('api:treatments:read')); + + // List treatments available + api.get('/treatments', function(req, res) { + var ifModifiedSince = req.get('If-Modified-Since'); + ctx.treatments.list(req.query, function(err, results) { + var d1 = null; + + const deNormalizeDates = env.settings.deNormalizeDates; + + _forEach(results, function clean (t) { + t.carbs = Number(t.carbs); + t.insulin = Number(t.insulin); + + // eslint-disable-next-line no-prototype-builtins + if (deNormalizeDates && t.hasOwnProperty('utcOffset')) { + const d = moment(t.created_at).utcOffset(t.utcOffset); + t.created_at = d.toISOString(true); + delete t.utcOffset; + } - function config_authed(app, api, wares, ctx) { + var d2 = null; - function post_response(req, res) { - var treatments = req.body; + if (t.hasOwnProperty('created_at')) { + d2 = new Date(t.created_at); + } else { + if (t.hasOwnProperty('timestamp')) { + d2 = new Date(t.timestamp); + } + } - if (!_isArray(treatments)) { - treatments = [treatments]; - }; + if (d2 == null) { return; } - ctx.treatments.create(treatments, function(err, created) { - if (err) { - console.log('Error adding treatment', err); - res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); - } else { - console.log('Treatment created'); - res.json(created); - } - }); + if (d1 == null || d2.getTime() > d1.getTime()) { + d1 = d2; } + }); - api.post('/treatments/', wares.bodyParser({ - limit: 1048576 * 50 - }), ctx.authorization.isPermitted('api:treatments:create'), post_response); - - /** - * @function delete_records - * Delete treatments. The query logic works the same way as find/list. This - * endpoint uses same search logic to remove records from the database. - */ - function delete_records(req, res, next) { - var query = req.query; - if (!query.count) { - query.count = 10 - } + if (!_isNil(d1)) res.setHeader('Last-Modified', d1.toUTCString()); + + if (ifModifiedSince && d1.getTime() <= moment(ifModifiedSince).valueOf()) { + res.status(304).send({ + status: 304 + , message: 'Not modified' + , type: 'internal' + }); + return; + } else { + return res.json(results); + } + }); + }); + + function config_authed (app, api, wares, ctx) { - console.log('Delete records with query: ', query); + function post_response (req, res) { + var treatments = req.body; - // remove using the query - ctx.treatments.remove(query, function(err, stat) { - if (err) { - console.log('treatments delete error: ', err); - return next(err); - } - // yield some information about success of operation - res.json(stat); + if (!_isArray(treatments)) { + treatments = [treatments]; + }; - console.log('treatments records deleted'); + ctx.treatments.create(treatments, function(err, created) { + if (err) { + console.log('Error adding treatment', err); + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + } else { + console.log('Treatment created'); + res.json(created); + } + }); + } - return next(); - }); + api.post('/treatments/', wares.bodyParser({ + limit: 1048576 * 50 + }), ctx.authorization.isPermitted('api:treatments:create'), post_response); + + /** + * @function delete_records + * Delete treatments. The query logic works the same way as find/list. This + * endpoint uses same search logic to remove records from the database. + */ + function delete_records (req, res, next) { + var query = req.query; + if (!query.count) { + query.count = 10 + } + + console.log('Delete records with query: ', query); + + // remove using the query + ctx.treatments.remove(query, function(err, stat) { + if (err) { + console.log('treatments delete error: ', err); + return next(err); } + // yield some information about success of operation + res.json(stat); - api.delete('/treatments/:id', ctx.authorization.isPermitted('api:treatments:delete'), function(req, res, next) { - if (!req.query.find) { - req.query.find = { - _id: req.params.id - }; - } else { - req.query.find._id = req.params.id; - } + console.log('treatments records deleted'); - if (req.query.find._id === '*') { - // match any record id - delete req.query.find._id; - } - next(); - }, delete_records); - - // delete record that match query - api.delete('/treatments/', ctx.authorization.isPermitted('api:treatments:delete'), delete_records); - - // update record - api.put('/treatments/', ctx.authorization.isPermitted('api:treatments:update'), function(req, res) { - var data = req.body; - ctx.treatments.save(data, function(err, created) { - if (err) { - res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); - console.log('Error saving treatment'); - console.log(err); - } else { - res.json(created); - console.log('Treatment saved', data); - } - }); - }); + return next(); + }); } - if (app.enabled('api') && app.enabled('careportal')) { - config_authed(app, api, wares, ctx); - } + api.delete('/treatments/:id', ctx.authorization.isPermitted('api:treatments:delete'), function(req, res, next) { + if (!req.query.find) { + req.query.find = { + _id: req.params.id + }; + } else { + req.query.find._id = req.params.id; + } + + if (req.query.find._id === '*') { + // match any record id + delete req.query.find._id; + } + next(); + }, delete_records); + + // delete record that match query + api.delete('/treatments/', ctx.authorization.isPermitted('api:treatments:delete'), delete_records); + + // update record + api.put('/treatments/', ctx.authorization.isPermitted('api:treatments:update'), function(req, res) { + var data = req.body; + ctx.treatments.save(data, function(err, created) { + if (err) { + res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err); + console.log('Error saving treatment'); + console.log(err); + } else { + res.json(created); + console.log('Treatment saved', data); + } + }); + }); + } - return api; + if (app.enabled('api') && app.enabled('careportal')) { + config_authed(app, api, wares, ctx); + } + + return api; } module.exports = configure; - diff --git a/lib/settings.js b/lib/settings.js index cf29d169447..eb0e3011bcb 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -48,6 +48,7 @@ function init () { , secureHstsHeaderPreload: false , secureCsp: false , showClockClosebutton: false + , deNormalizeDates: false }; var valueMappers = { @@ -69,6 +70,7 @@ function init () { , secureHstsHeader: mapTruthy , secureCsp: mapTruthy , showClockClosebutton: mapTruthy + , deNormalizeDates: mapTruthy }; function mapNumberArray (value) { diff --git a/tests/api.entries.test.js b/tests/api.entries.test.js index 6f70c81593c..098b5c45663 100644 --- a/tests/api.entries.test.js +++ b/tests/api.entries.test.js @@ -19,7 +19,7 @@ describe('Entries REST api', function ( ) { self.app = require('express')( ); self.app.enable('api'); bootevent(self.env, language).boot(function booted (ctx) { - self.app.use('/', entries(self.app, self.wares, ctx)); + self.app.use('/', entries(self.app, self.wares, ctx, self.env)); self.archive = require('../lib/server/entries')(self.env, ctx); var creating = load('json'); diff --git a/tests/api.unauthorized.test.js b/tests/api.unauthorized.test.js index 8c804c4244f..5785069f77e 100644 --- a/tests/api.unauthorized.test.js +++ b/tests/api.unauthorized.test.js @@ -21,7 +21,7 @@ describe('authed REST api', function ( ) { var self = this; self.known_key = known; require('../lib/server/bootevent')(env, language).boot(function booted (ctx) { - self.app.use('/', entries(self.app, self.wares, ctx)); + self.app.use('/', entries(self.app, self.wares, ctx, env)); self.archive = require('../lib/server/entries')(env, ctx); var creating = load('json'); diff --git a/tests/timeago.test.js b/tests/timeago.test.js index e47e9873d00..7b4a718ccd0 100644 --- a/tests/timeago.test.js +++ b/tests/timeago.test.js @@ -2,7 +2,7 @@ var should = require('should'); var levels = require('../lib/levels'); var times = require('../lib/times'); -describe('timeago', function ( ) { +describe('timeago', function() { var ctx = {}; ctx.ddata = require('../lib/data/ddata')(); ctx.notifications = require('../lib/notifications')(env, ctx); @@ -12,16 +12,16 @@ describe('timeago', function ( ) { var env = require('../env')(); - function freshSBX() { + function freshSBX () { //set extendedSettings right before calling withExtendedSettings, there's some strange test interference here - env.extendedSettings = {timeago: {enableAlerts: true}}; + env.extendedSettings = { timeago: { enableAlerts: true } }; var sbx = require('../lib/sandbox')().serverInit(env, ctx).withExtendedSettings(timeago); return sbx; } - it('Not trigger an alarm when data is current', function (done) { + it('Not trigger an alarm when data is current', function(done) { ctx.notifications.initRequests(); - ctx.ddata.sgvs = [{mills: Date.now(), mgdl: 100, type: 'sgv'}]; + ctx.ddata.sgvs = [{ mills: Date.now(), mgdl: 100, type: 'sgv' }]; var sbx = freshSBX(); timeago.checkNotifications(sbx); @@ -30,9 +30,9 @@ describe('timeago', function ( ) { done(); }); - it('Not trigger an alarm with future data', function (done) { + it('Not trigger an alarm with future data', function(done) { ctx.notifications.initRequests(); - ctx.ddata.sgvs = [{mills: Date.now() + times.mins(15).msecs, mgdl: 100, type: 'sgv'}]; + ctx.ddata.sgvs = [{ mills: Date.now() + times.mins(15).msecs, mgdl: 100, type: 'sgv' }]; var sbx = freshSBX(); timeago.checkNotifications(sbx); @@ -41,21 +41,27 @@ describe('timeago', function ( ) { done(); }); - it('should trigger a warning when data older than 15m', function (done) { + it('should trigger a warning when data older than 15m', function(done) { ctx.notifications.initRequests(); - ctx.ddata.sgvs = [{mills: Date.now() - times.mins(16).msecs, mgdl: 100, type: 'sgv'}]; + ctx.ddata.sgvs = [{ mills: Date.now() - times.mins(16).msecs, mgdl: 100, type: 'sgv' }]; var sbx = freshSBX(); timeago.checkNotifications(sbx); + + var currentTime = new Date().getTime(); + + // eslint-disable-next-line no-empty + while (currentTime + 500 >= new Date().getTime()) {} + var highest = ctx.notifications.findHighestAlarm('Time Ago'); highest.level.should.equal(levels.WARN); highest.message.should.equal('Last received: 16 mins ago\nBG Now: 100 mg/dl'); done(); }); - it('should trigger an urgent alarm when data older than 30m', function (done) { + it('should trigger an urgent alarm when data older than 30m', function(done) { ctx.notifications.initRequests(); - ctx.ddata.sgvs = [{mills: Date.now() - times.mins(31).msecs, mgdl: 100, type: 'sgv'}]; + ctx.ddata.sgvs = [{ mills: Date.now() - times.mins(31).msecs, mgdl: 100, type: 'sgv' }]; var sbx = freshSBX(); timeago.checkNotifications(sbx); @@ -70,55 +76,55 @@ describe('timeago', function ( ) { should.deepEqual( timeago.calcDisplay({ mills: now + times.mins(15).msecs }, now) - , {label: 'in the future', shortLabel: 'future'} + , { label: 'in the future', shortLabel: 'future' } ); //TODO: current behavior, we can do better //just a little in the future, pretend it's ok should.deepEqual( timeago.calcDisplay({ mills: now + times.mins(4).msecs }, now) - , {value: 1, label: 'min ago', shortLabel: 'm'} + , { value: 1, label: 'min ago', shortLabel: 'm' } ); should.deepEqual( timeago.calcDisplay(null, now) - , {label: 'time ago', shortLabel: 'ago'} + , { label: 'time ago', shortLabel: 'ago' } ); should.deepEqual( timeago.calcDisplay({ mills: now }, now) - , {value: 1, label: 'min ago', shortLabel: 'm'} + , { value: 1, label: 'min ago', shortLabel: 'm' } ); should.deepEqual( timeago.calcDisplay({ mills: now - 1 }, now) - , {value: 1, label: 'min ago', shortLabel: 'm'} + , { value: 1, label: 'min ago', shortLabel: 'm' } ); should.deepEqual( timeago.calcDisplay({ mills: now - times.sec(30).msecs }, now) - , {value: 1, label: 'min ago', shortLabel: 'm'} + , { value: 1, label: 'min ago', shortLabel: 'm' } ); should.deepEqual( timeago.calcDisplay({ mills: now - times.mins(30).msecs }, now) - , {value: 30, label: 'mins ago', shortLabel: 'm'} + , { value: 30, label: 'mins ago', shortLabel: 'm' } ); should.deepEqual( timeago.calcDisplay({ mills: now - times.hours(5).msecs }, now) - , {value: 5, label: 'hours ago', shortLabel: 'h'} + , { value: 5, label: 'hours ago', shortLabel: 'h' } ); should.deepEqual( timeago.calcDisplay({ mills: now - times.days(5).msecs }, now) - , {value: 5, label: 'days ago', shortLabel: 'd'} + , { value: 5, label: 'days ago', shortLabel: 'd' } ); should.deepEqual( timeago.calcDisplay({ mills: now - times.days(10).msecs }, now) - , {label: 'long ago', shortLabel: 'ago'} + , { label: 'long ago', shortLabel: 'ago' } ); }); -}); \ No newline at end of file +});