Merge branch 'dev' into master

This commit is contained in:
gth001
2023-12-14 09:32:06 -08:00
committed by GitHub
28 changed files with 573 additions and 215 deletions
+1 -1
View File
@@ -88,7 +88,7 @@ function configure(app, wares, ctx) {
api.post('/activity/', ctx.authorization.isPermitted('api:activity:create'), post_response);
api.delete('/activity/:_id', ctx.authorization.isPermitted('api:activity:delete'), function(req, res) {
ctx.activity.deleteOne(req.params._id, function() {
ctx.activity.remove(req.params._id, function() {
res.json({});
});
});
+1 -1
View File
@@ -71,7 +71,7 @@ function configure (app, wares, ctx) {
});
// delete record
api.delete('/food/:_id', ctx.authorization.isPermitted('api:food:delete'), function(req, res) {
ctx.food.deleteOne(req.params._id, function ( ) {
ctx.food.remove(req.params._id, function ( ) {
res.json({ });
});
});
+1 -1
View File
@@ -92,7 +92,7 @@ function configure (app, wares, ctx) {
});
api.delete('/profile/:_id', ctx.authorization.isPermitted('api:profile:delete'), function(req, res) {
ctx.profile.deleteOne(req.params._id, function ( ) {
ctx.profile.remove(req.params._id, function ( ) {
res.json({ });
});
});
+63 -17
View File
@@ -46,9 +46,11 @@ function AlarmSocket (app, env, ctx) {
socket.on('subscribe', function onSubscribe (message, returnCallback) {
self.subscribe(socket, message, returnCallback);
});
});
// Turns all notifications on the event bus back into events to be
// broadcast to clients.
ctx.bus.on('notification', self.emitNotification);
};
@@ -65,7 +67,7 @@ function AlarmSocket (app, env, ctx) {
self.subscribe = function subscribe (socket, message, returnCallback) {
const shouldCallBack = typeof(returnCallback) === 'function';
// Native client
// Native client
if (message && message.accessToken) {
return ctx.authorization.resolveAccessToken(message.accessToken, function resolveFinishForToken (err, auth) {
if (err) {
@@ -76,13 +78,14 @@ function AlarmSocket (app, env, ctx) {
}
return err;
} else {
// Subscribe for acking alarms
socket.on('ack', function onAck (level, group, silenceTime) {
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
});
// Subscribe for acking alarms
// Client sends ack, which sends a notificaiton through our internal bus
socket.on('ack', function onAck (level, group, silenceTime) {
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
});
var okResponse = { success: true, message: 'Subscribed for alarms' }
var okResponse = { success: true, message: 'Subscribed for alarms' }
if (shouldCallBack) {
returnCallback(okResponse);
}
@@ -90,10 +93,32 @@ function AlarmSocket (app, env, ctx) {
}
});
}
// Web client (jwt access token or api_hash)
if (message && (message.jwtToken || message.secret)) {
if (!message) { message = {}; }
// Web client (jwt access token or api_hash)
/*
* On the web: a client may have saved a secret or using a jwtToken, or may have none.
* Some pages will automatically prompt for authorization, when needed.
* To make the main homepage require authorization as well, set
* AUTHENTICATION_PROMPT_ON_LOAD=true.
*
* If there is missing authorization when authorization is required,
* rejecting the attempt in order to trigger a prompt on the client.
* If there is no authorization required, or there are available
* credentials, attempt to resolve the available permissions.
* When processing ACK messages that dismiss alarms, Authorization should be
* required.
*/
var shouldTry = true;
if (env.settings.authenticationPromptOnLoad) {
if (!message.jwtToken && !message.secret) {
shouldTry = false;
}
}
if (message && shouldTry) {
return ctx.authorization.resolve({ api_secret: message.secret, token: message.jwtToken, ip: getRemoteIP(socket.request) }, function resolveFinish (err, auth) {
if (err) {
console.log(`${LOG_ERROR} Authorization failed for jwtToken:`, message.jwtToken);
@@ -102,13 +127,34 @@ function AlarmSocket (app, env, ctx) {
}
return err;
} else {
// Subscribe for acking alarms
socket.on('ack', function onAck (level, group, silenceTime) {
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
});
var perms = {
read: ctx.authorization.checkMultiple('api:*:read', auth.shiros)
, ack: ctx.authorization.checkMultiple('notifications:*:ack', auth.shiros)
};
// Subscribe for acking alarms
// TODO: does this produce double ACK after the authorizing? Only if reconnecting?
// TODO: how will perms get updated after authorizing?
socket.on('ack', function onAck (level, group, silenceTime) {
if (perms.ack) {
// This goes through the server-wide event bus.
ctx.notifications.ack(level, group, silenceTime, true);
console.info(LOG + 'ack received ' + level + ' ' + group + ' ' + silenceTime);
} else {
// TODO: send a message to client to silence locally, but not
// globally, and request authorization.
// This won't go through th event bus.
// var acked = { silenceTime, group, level };
// socket.emit('authorization_needed', acked);
}
});
/* TODO: need to know when to update the permissions.
// Can we use
socket.on('resubscribe', function update_permissions ( ) {
// perms = { ... };
});
*/
var okResponse = { success: true, message: 'Subscribed for alarms' }
var okResponse = { success: true, message: 'Subscribed for alarms', ...perms };
if (shouldCallBack) {
returnCallback(okResponse);
}
+2 -2
View File
@@ -69,7 +69,7 @@ function updateOne (col, identifier, setFields) {
if (err) {
reject(err);
} else {
resolve({ updated: result.modifiedCount });
resolve({ updated: result.result.nModified });
}
});
});
@@ -91,7 +91,7 @@ function deleteOne (col, identifier) {
if (err) {
reject(err);
} else {
resolve({ deleted: result.deletedCount });
resolve({ deleted: result.result.n });
}
});
});
+3 -3
View File
@@ -2,7 +2,7 @@
const _ = require('lodash')
, checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$")
, ObjectID = require('mongodb-legacy').ObjectId
, ObjectID = require('mongodb').ObjectID
;
@@ -112,7 +112,7 @@ function filterForOne (identifier) {
// fallback to "identifier = _id"
if (checkForHexRegExp.test(identifier)) {
filterOpts.push({ _id: new ObjectID(identifier) });
filterOpts.push({ _id: ObjectID(identifier) });
}
return { $or: filterOpts };
@@ -137,7 +137,7 @@ function identifyingFilter (identifier, doc, dedupFallbackFields) {
// fallback to "identifier = _id" (APIv1)
if (checkForHexRegExp.test(identifier)) {
filterItems.push({ identifier: { $exists: false }, _id: new ObjectID(identifier) });
filterItems.push({ identifier: { $exists: false }, _id: ObjectID(identifier) });
}
}
+6 -15
View File
@@ -3,7 +3,7 @@
var _ = require('lodash');
var crypto = require('crypto');
var shiroTrie = require('shiro-trie');
var ObjectID = require('mongodb-legacy').ObjectId;
var ObjectID = require('mongodb').ObjectID;
var find_options = require('../server/query');
@@ -27,14 +27,14 @@ function init (env, ctx) {
if (!Object.prototype.hasOwnProperty.call(obj, 'created_at')) {
obj.created_at = (new Date()).toISOString();
}
collection.insertOne(obj, function (err, doc) {
collection.insert(obj, function (err, doc) {
if (err != null && err.message) {
console.log('Data insertion error', err.message);
fn(err.message, null);
return;
}
storage.reload(function loaded() {
fn(null, obj);
fn(null, doc.ops);
});
});
}
@@ -42,7 +42,7 @@ function init (env, ctx) {
}
function list (collection) {
function doList(opts, fn) {
function doList(opts, fn) {
// these functions, find, sort, and limit, are used to
// dynamically configure the request, based on the options we've
// been given
@@ -65,8 +65,6 @@ function init (env, ctx) {
fn(err, entries);
}
console.log('Loading',opts);
// now just stitch them all together
limit.call(collection
.find(query_for(opts))
@@ -79,7 +77,7 @@ function init (env, ctx) {
function remove (collection) {
function doRemove (_id, callback) {
collection.deleteOne({ '_id': new ObjectID(_id) }, function (err) {
collection.remove({ '_id': new ObjectID(_id) }, function (err) {
storage.reload(function loaded() {
callback(err, null);
});
@@ -94,7 +92,7 @@ function init (env, ctx) {
if (!obj.created_at) {
obj.created_at = (new Date()).toISOString();
}
collection.insertOne(obj, function (err) {
collection.save(obj, function (err) {
//id should be added for new docs
storage.reload(function loaded() {
callback(err, obj);
@@ -137,14 +135,8 @@ function init (env, ctx) {
storage.reload = function reload (callback) {
console.log('Reloading auth data');
storage.listRoles({sort: {name: 1}}, function listResults (err, results) {
console.log('Roles listed');
if (err) {
console.log('Problem listing roles', err);
return callback && callback(err);
}
@@ -160,7 +152,6 @@ function init (env, ctx) {
storage.listSubjects({sort: {name: 1}}, function listResults (err, results) {
if (err) {
console.log('Problem listing subjects', err);
return callback && callback(err);
}
+30 -10
View File
@@ -68,7 +68,7 @@ client.init = function init (callback) {
}).done(function success (serverSettings) {
if (serverSettings.runtimeState !== 'loaded') {
console.log('Server is still loading data');
$('#loadingMessageText').html('Nightscout is still starting and should be available within about 15 seconds.');
$('#loadingMessageText').html('Server is starting and still loading data, retrying load in 5 seconds');
window.setTimeout(window.Nightscout.client.init, 5000);
return;
}
@@ -1146,15 +1146,13 @@ client.load = function load (serverSettings, callback) {
}
console.log('Subscribed for alarms', data);
if (client.settings.authenticationPromptOnLoad && !data.success) {
client.hashauth.requestAuthentication(function afterRequest () {
client.hashauth.updateSocketAuth();
if (callback) {
callback();
}
});
} else if (callback) {
callback();
var shouldAuthenticationPromptOnLoad = client.settings.authenticationPromptOnLoad ;
if (!data.success) {
if (!data.read || !hasRequiredPermission() || shouldAuthenticationPromptOnLoad) {
return client.hashauth.requestAuthentication(function afterRequest () {
return client.hashauth.updateSocketAuth();
});
}
}
}
);
@@ -1235,6 +1233,28 @@ client.load = function load (serverSettings, callback) {
stopAlarm(false, null, notify);
}
});
/*
*
// TODO: When an unauthorized client attempts to silence an alarm, we should
// allow silencing locally, request for authorization, and if the
// authorization succeeds even republish the ACK notification. something like...
alarmSocket.on('authorization_needed', function(details) {
if (alarmInProgress) {
console.log('clearing alarm');
stopAlarm(true, details.silenceTime, currentNotify);
}
client.hashauth.requestAuthentication(function afterRequest () {
console.log("SUCCESSFULLY AUTHORIZED, REPUBLISHED ACK?");
// easiest way to update permission set on server side is to send another message.
alarmSocket.emit('resubscribe', currentNotify, details);
if (isClient && currentNotify) {
alarmSocket.emit('ack', currentNotify.level, currentNotify.group, details.silenceTime);
}
});
});
*/
$('#testAlarms').click(function(event) {
+6
View File
@@ -185,6 +185,10 @@ function init (env, ctx) {
notifications.ack(1, group, time);
}
/*
* TODO: modify with a local clear, this will clear all connected clients,
* globally
*/
if (sendClear) {
var notify = {
clear: true
@@ -192,6 +196,8 @@ function init (env, ctx) {
, message: group + ' - ' + ctx.levels.toDisplay(level) + ' was ack\'d'
, group: group
};
// When web client sends ack, this translates the websocket message into
// an event on our internal bus.
ctx.bus.emit('notification', notify);
logEmitEvent(notify);
}
+1
View File
@@ -18,6 +18,7 @@ var init = function init () {
client.init(function loaded () {
console.log("LOADING CLIENT INIT");
if (c_profile !== null) {
return; // already loaded so don't load again
}
+2 -2
View File
@@ -4,7 +4,7 @@ var find_options = require('./query');
function storage (env, ctx) {
var ObjectID = require('mongodb-legacy').ObjectId;
var ObjectID = require('mongodb').ObjectID;
function create (obj, fn) {
obj.created_at = (new Date( )).toISOString( );
@@ -62,7 +62,7 @@ function storage (env, ctx) {
function remove (_id, fn) {
var objId = new ObjectID(_id);
return api( ).deleteOne({ '_id': objId }, fn);
return api( ).remove({ '_id': objId }, fn);
}
function api ( ) {
+14 -6
View File
@@ -1,15 +1,13 @@
'use strict';
const _ = require('lodash');
const UPDATE_THROTTLE = 15000;
const UPDATE_THROTTLE = 5000;
function boot (env, language) {
function startBoot(ctx, next) {
console.log('++++++++++++++++++++++++++++++');
console.log('Nightscout Executing startBoot');
console.log('++++++++++++++++++++++++++++++');
console.log('Executing startBoot');
ctx.bootErrors = [ ];
ctx.moment = require('moment-timezone');
@@ -40,7 +38,7 @@ function boot (env, language) {
const isLTS = process.release.lts ? true : false;
if (isLTS || (semver.satisfies(nodeVersion, '^20.0.0') || semver.satisfies(nodeVersion, '^18.0.0') || semver.satisfies(nodeVersion, '^16.0.0') || semver.satisfies(nodeVersion, '^14.0.0'))) {
if (isLTS && (semver.satisfies(nodeVersion, '^20.0.0') || semver.satisfies(nodeVersion, '^18.0.0') || semver.satisfies(nodeVersion, '^16.0.0') || semver.satisfies(nodeVersion, '^14.0.0'))) {
//Latest Node 14 LTS and Node 16 LTS are recommended and supported.
//Require at least Node 14 without known security issues
console.debug('Node LTS version ' + nodeVersion + ' is supported');
@@ -150,6 +148,16 @@ function boot (env, language) {
}
try {
if (_.startsWith(env.storageURI, 'openaps://')) {
require('../storage/openaps-storage')(env, function ready (err, store) {
if (err) {
throw err;
}
ctx.store = store;
console.log('OpenAPS Storage system ready');
next();
});
} else {
//TODO assume mongo for now, when there are more storage options add a lookup
require('../storage/mongo-storage')(env, function ready(err, store) {
// FIXME, error is always null, if there is an error, the index.js will throw an exception
@@ -162,6 +170,7 @@ function boot (env, language) {
ctx.store = store;
next();
});
}
} catch (err) {
console.info('ERROR CONNECTING TO MONGO', err);
ctx.bootErrors = ctx.bootErrors || [ ];
@@ -286,7 +295,6 @@ function boot (env, language) {
ctx.bus.on('data-received', function forceReloadData ( ) {
console.info('got data-received event, requesting reload');
ctx.bus.emit('data-loaded'); // Since we update local sandbox instantly, process data-loaded right away in case this gets debounced
updateData();
});
+6 -13
View File
@@ -24,16 +24,16 @@ function storage (collection, ctx) {
obj.utcOffset = d.utcOffset();
api().insertOne(obj, function(err, results) {
if (err) {
if (err !== null && err.message) {
console.log('Error inserting the device status object', err.message);
errorOccurred = true;
fn(err.message, null);
return;
}
if (results) {
if (!obj._id) obj._id = results.insertedId;
if (!err) {
if (!obj._id) obj._id = results.insertedIds[0]._id;
r.push(obj);
ctx.bus.emit('data-update', {
@@ -47,11 +47,6 @@ function storage (collection, ctx) {
fn(null, r);
ctx.bus.emit('data-received');
}
} else {
console.log('Error inserting the device status object', err.message);
errorOccurred = true;
fn(err.message, null);
return;
}
});
};
@@ -105,19 +100,17 @@ function storage (collection, ctx) {
function removed (err, stat) {
console.log('removed', err, stat);
ctx.bus.emit('data-update', {
type: 'devicestatus'
, op: 'remove'
, count: stat.deletedCount
, count: stat.result.n
, changes: opts.find._id
});
fn(err, stat);
}
return api().deleteMany(
return api().remove(
query_for(opts), removed);
}
+8 -8
View File
@@ -2,7 +2,7 @@
var es = require('event-stream');
var find_options = require('./query');
var ObjectId = require('mongodb-legacy').ObjectId;
var ObjectID = require('mongodb').ObjectID;
var moment = require('moment');
/**********\
@@ -46,12 +46,12 @@ function storage (env, ctx) {
}
function remove (opts, fn) {
api().deleteMany(query_for(opts), function(err, stat) {
api().remove(query_for(opts), function(err, stat) {
ctx.bus.emit('data-update', {
type: 'entries'
, op: 'remove'
, count: stat.deletedCount
, count: stat.result.n
, changes: opts.find._id
});
@@ -110,12 +110,12 @@ function storage (env, ctx) {
if (doc.dateString) doc.dateString = doc.sysTime;
var query = (doc.sysTime && doc.type) ? { sysTime: doc.sysTime, type: doc.type } : doc;
api().replaceOne(query, doc, { upsert: true }, function(err, updateResults) {
api().update(query, doc, { upsert: true }, function(err, updateResults) {
firstErr = firstErr || err;
if (updateResults) {
if (updateResults.upsertedCount == 1) {
doc._id = updateResults.upsertedId
if (!err) {
if (updateResults.result.upserted) {
doc._id = updateResults.result.upserted[0]._id
}
ctx.bus.emit('data-update', {
@@ -135,7 +135,7 @@ function storage (env, ctx) {
}
function getEntry (id, fn) {
api().findOne({ "_id": new ObjectId(id) }, function(err, entry) {
api().findOne({ _id: ObjectID(id) }, function(err, entry) {
if (err) {
fn(err);
} else {
+2 -2
View File
@@ -1,7 +1,7 @@
'use strict';
function storage (env, ctx) {
var ObjectID = require('mongodb-legacy').ObjectId;
var ObjectID = require('mongodb').ObjectID;
function create (obj, fn) {
obj.created_at = (new Date( )).toISOString( );
@@ -42,7 +42,7 @@ function storage (env, ctx) {
function remove (_id, fn) {
var objId = new ObjectID(_id);
return api( ).deleteOne({ '_id': objId }, fn);
return api( ).remove({ '_id': objId }, fn);
}
+2 -2
View File
@@ -4,7 +4,7 @@ var find_options = require('./query');
var consts = require('../constants');
function storage (collection, ctx) {
var ObjectID = require('mongodb-legacy').ObjectId;
var ObjectID = require('mongodb').ObjectID;
function create (obj, fn) {
obj.created_at = (new Date( )).toISOString( );
@@ -79,7 +79,7 @@ function storage (collection, ctx) {
function remove (_id, fn) {
var objId = new ObjectID(_id);
api( ).deleteOne({ '_id': objId }, fn);
api( ).remove({ '_id': objId }, fn);
ctx.bus.emit('data-received');
}
+1 -1
View File
@@ -1,7 +1,7 @@
'use strict';
const traverse = require('traverse');
const ObjectID = require('mongodb-legacy').ObjectId;
const ObjectID = require('mongodb').ObjectID;
const moment = require('moment');
const TWO_DAYS = 172800000;
+12 -12
View File
@@ -6,7 +6,7 @@ var moment = require('moment');
var find_options = require('./query');
function storage (env, ctx) {
var ObjectID = require('mongodb-legacy').ObjectId;
var ObjectID = require('mongodb').ObjectID;
function create (objOrArray, fn) {
@@ -46,18 +46,18 @@ function storage (env, ctx) {
, eventType: obj.eventType
};
api( ).replaceOne(query, obj, {upsert: true}, function complete (err, updateResults) {
api( ).update(query, obj, {upsert: true}, function complete (err, updateResults) {
if (err) console.error('Problem upserting treatment', err);
if (updateResults) {
if (updateResults.upsertedCount == 1) {
obj._id = updateResults.upsertedId
if (!err) {
if (updateResults.result.upserted) {
obj._id = updateResults.result.upserted[0]._id
}
}
// TODO document this feature
if (updateResults && obj.preBolus) {
if (!err && obj.preBolus) {
//create a new object to insert copying only the needed fields
var pbTreat = {
created_at: (new Date(new Date(results.created_at).getTime() + (obj.preBolus * 60000))).toISOString(),
@@ -70,11 +70,11 @@ function storage (env, ctx) {
}
query.created_at = pbTreat.created_at;
api( ).replaceOne(query, pbTreat, {upsert: true}, function pbComplete (err, updateResults) {
api( ).update(query, pbTreat, {upsert: true}, function pbComplete (err, updateResults) {
if (updateResults) {
if (updateResults.upsertedCount == 1) {
pbTreat._id = updateResults.upsertedId
if (!err) {
if (updateResults.result.upserted) {
pbTreat._id = updateResults.result.upserted[0]._id
}
}
@@ -122,14 +122,14 @@ function storage (env, ctx) {
}
function remove (opts, fn) {
return api( ).deleteMany(query_for(opts), {}, function (err, stat) {
return api( ).remove(query_for(opts), function (err, stat) {
//TODO: this is triggering a read from Mongo, we can do better
//console.log('Treatment removed', opts); // , stat);
ctx.bus.emit('data-update', {
type: 'treatments',
op: 'remove',
count: stat.deletedCount,
count: stat.result.n,
changes: opts.find._id
});
+9 -7
View File
@@ -2,7 +2,7 @@
var times = require('../times');
var calcData = require('../data/calcdelta');
var ObjectID = require('mongodb-legacy').ObjectId;
var ObjectID = require('mongodb').ObjectID;
const forwarded = require('forwarded-for');
function getRemoteIP (req) {
@@ -75,7 +75,9 @@ function init (env, ctx, server) {
'log level': 0
}).listen(server, {
//these only effect the socket.io.js file that is sent to the client, but better than nothing
'browser client minification': true
// compat with v2 client
allowEIO3: true
, 'browser client minification': true
, 'browser client etag': true
, 'browser client gzip': false
, 'perMessageDeflate': {
@@ -220,7 +222,7 @@ function init (env, ctx, server) {
id = new ObjectID();
}
ctx.store.collection(collection).updateOne({ '_id': id }
ctx.store.collection(collection).update({ '_id': id }
, { $set: data.data }
, function(err, results) {
@@ -268,7 +270,7 @@ function init (env, ctx, server) {
}
var objId = new ObjectID(data._id);
ctx.store.collection(collection).updateOne({ '_id': objId }, { $unset: data.data }
ctx.store.collection(collection).update({ '_id': objId }, { $unset: data.data }
, function(err, results) {
if (!err) {
@@ -394,7 +396,7 @@ function init (env, ctx, server) {
console.log(LOG_DEDUP + 'Found similiar', array[0]);
array[0].created_at = data.data.created_at;
var objId = new ObjectID(array[0]._id);
ctx.store.collection(collection).updateOne({ '_id': objId }, { $set: { created_at: data.data.created_at } });
ctx.store.collection(collection).update({ '_id': objId }, { $set: { created_at: data.data.created_at } });
if (callback) {
callback([array[0]]);
}
@@ -506,14 +508,14 @@ function init (env, ctx, server) {
}
var objId = new ObjectID(data._id);
ctx.store.collection(collection).deleteOne({ '_id': objId }
ctx.store.collection(collection).remove({ '_id': objId }
, function(err, stat) {
if (!err) {
ctx.bus.emit('data-update', {
type: data.collection
, op: 'remove'
, count: stat.deletedCount
, count: stat.result.n
, changes: data._id
});
+3 -4
View File
@@ -1,6 +1,6 @@
'use strict';
const MongoClient = require('mongodb-legacy').MongoClient;
const MongoClient = require('mongodb').MongoClient;
const mongo = {
client: null,
@@ -82,11 +82,10 @@ function init(env, cb, forceNewConnection) {
mongo.ensureIndexes = function ensureIndexes(collection, fields) {
fields.forEach(function (field) {
const name = collection.collectionName + "." + field;
console.info('ensuring index for: ' + name);
console.info('ensuring index for: ' + field);
collection.createIndex(field, { 'background': true }, function (err) {
if (err) {
console.error('unable to ensureIndex for: ' + name + ' - ' + err);
console.error('unable to ensureIndex for: ' + field + ' - ' + err);
}
});
});
+191
View File
@@ -0,0 +1,191 @@
'use strict';
var _ = require('lodash');
var fs = require('fs');
var crypto = require('crypto');
var MongoMock = require('mongomock');
var config = {
collections: {}
};
function init (env, callback) {
if (!env.storageURI || !_.isString(env.storageURI)) {
throw new Error('openaps config uri is missing or invalid');
}
var configPath = env.storageURI.split('openaps://').pop();
function addId (data) {
var shasum = crypto.createHash('sha1');
shasum.update(JSON.stringify(data));
data._id = shasum.digest('hex');
}
function loadData (path) {
if (!path || !_.isString(path)) {
return [ ];
}
try {
purgeCache(path);
var inputData = require(path);
if (_.isArray(inputData)) {
//console.info('>>>input is an array', path);
_.forEach(inputData, addId);
} else if (!_.isEmpty(inputData) && _.isObject(inputData)) {
//console.info('>>>input is an object', path);
inputData.created_at = new Date(fs.statSync(path).mtime).toISOString();
addId(inputData);
inputData = [ inputData ];
} else {
//console.info('>>>input is something else', path, inputData);
inputData = [ ];
}
return inputData;
} catch (err) {
console.error('unable to find input data for', path, err);
return [ ];
}
}
function reportAsCollection (name) {
var data = { };
var input = _.get(config, 'collections.' + name + '.input');
if (_.isArray(input)) {
//console.info('>>>input is an array', input);
data[name] = _.flatten(_.map(input, loadData));
} else {
data[name] = loadData(input);
}
var mock = new MongoMock(data);
var collection = mock.collection(name);
var wrapper = {
findQuery: null
, sortQuery: null
, limitCount: null
, find: function find (query) {
query = _.cloneDeepWith(query, function booleanize (value) {
//TODO: for some reason we're getting {$exists: NaN} instead of true/false
if (value && _.isObject(value) && '$exists' in value) {
return {$exists: true};
}
});
wrapper.findQuery = query;
return wrapper;
}
, limit: function limit (count) {
wrapper.limitCount = count;
return wrapper;
}
, sort: function sort (query) {
wrapper.sortQuery = query;
return wrapper;
}
, toArray: function toArray(callback) {
collection.find(wrapper.findQuery).toArray(function intercept (err, results) {
if (err) {
return callback(err, results);
}
if (wrapper.sortQuery) {
var field = _.keys(wrapper.sortQuery).pop();
//console.info('>>>sortField', field);
if (field) {
results = _.sortBy(results, field);
if (-1 === wrapper.sortQuery[field]) {
//console.info('>>>sort reverse');
results = _.reverse(results);
}
}
}
if (wrapper.limitCount !== null && _.isNumber(wrapper.limitCount)) {
//console.info('>>>limit count', wrapper.limitCount);
results = _.take(results, wrapper.limitCount);
}
//console.info('>>>toArray', name, wrapper.findQuery, wrapper.sortQuery, wrapper.limitCount, results.length);
callback(null, results);
});
return wrapper;
}
};
return wrapper;
}
try {
var customConfig = require(configPath);
config = _.merge({}, customConfig, config);
callback(null, {
collection: reportAsCollection
, ensureIndexes: _.noop
});
} catch (err) {
callback(err);
}
}
/**
* Removes a module from the cache
*
* see http://stackoverflow.com/a/14801711
*/
function purgeCache(moduleName) {
// Traverse the cache looking for the files
// loaded by the specified module name
searchCache(moduleName, function (mod) {
delete require.cache[mod.id];
});
// Remove cached paths to the module.
// Thanks to @bentael for pointing this out.
Object.keys(module.constructor._pathCache).forEach(function(cacheKey) {
if (cacheKey.indexOf(moduleName)>0) {
delete module.constructor._pathCache[cacheKey];
}
});
}
/**
* Traverses the cache to search for all the cached
* files of the specified module name
*
* see http://stackoverflow.com/a/14801711
*/
function searchCache(moduleName, callback) {
// Resolve the module identified by the specified name
var mod = require.resolve(moduleName);
// Check if the module has been resolved and found within
// the cache
if (mod && ((mod = require.cache[mod]) !== undefined)) {
// Recursively go over the results
(function traverse(mod) {
// Go over each of the module's children and
// traverse them
mod.children.forEach(function (child) {
traverse(child);
});
// Call the specified callback providing the
// found cached module
callback(mod);
}(mod));
}
}
module.exports = init;
+80 -94
View File
@@ -1,6 +1,6 @@
{
"name": "nightscout",
"version": "15.0.0",
"version": "15.0.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1223,20 +1223,6 @@
"integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==",
"dev": true
},
"@types/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog=="
},
"@types/whatwg-url": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
"integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
"requires": {
"@types/node": "*",
"@types/webidl-conversions": "*"
}
},
"@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
@@ -1737,6 +1723,53 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"bl": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
}
}
},
"bn.js": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
@@ -1887,9 +1920,9 @@
}
},
"bson": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-5.3.0.tgz",
"integrity": "sha512-ukmCZMneMlaC5ebPHXIkP8YJzNl5DC41N5MAIvKDqLggdao342t4McltoJBQfQya/nHBWAcSsYRqlXPoQkTJag=="
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
"integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="
},
"buffer": {
"version": "6.0.3",
@@ -2743,6 +2776,11 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"denque": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -4174,11 +4212,6 @@
"resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
},
"ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
"integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -4260,8 +4293,7 @@
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"isexe": {
"version": "2.0.0",
@@ -5075,11 +5107,6 @@
"tough-cookie": "^4.0.0"
},
"dependencies": {
"@types/tough-cookie": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
"integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
},
"@ungap/promise-all-settled": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
@@ -6281,55 +6308,16 @@
"integrity": "sha512-mv0RXncyzNt+gtnKOjD3YJK4I6+r9jk3aU5psa8/E8zvpN03AsQ0DkLJPBs5vPAzCfMs4T8gA2UQgaFx3QANHw=="
},
"mongodb": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.5.0.tgz",
"integrity": "sha512-XgrkUgAAdfnZKQfk5AsYL8j7O99WHd4YXPxYxnh8dZxD+ekYWFRA3JktUsBnfg+455Smf75/+asoU/YLwNGoQQ==",
"version": "3.7.3",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz",
"integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==",
"requires": {
"bson": "^5.3.0",
"mongodb-connection-string-url": "^2.6.0",
"saslprep": "^1.0.3",
"socks": "^2.7.1"
}
},
"mongodb-connection-string-url": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
"integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
"requires": {
"@types/whatwg-url": "^8.2.1",
"whatwg-url": "^11.0.0"
},
"dependencies": {
"tr46": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
"integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
"requires": {
"punycode": "^2.1.1"
}
},
"webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
},
"whatwg-url": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
"integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
"requires": {
"tr46": "^3.0.0",
"webidl-conversions": "^7.0.0"
}
}
}
},
"mongodb-legacy": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/mongodb-legacy/-/mongodb-legacy-5.0.0.tgz",
"integrity": "sha512-q2G+MRwde6114bCAF/EZLmMXSsebIKMHmzsfOJq6M/Tj4gr3wLT50+rJsJNkiR0e0kjFx3dllWjqwRR1n11Zsw==",
"requires": {
"mongodb": "^5.0.0"
"bl": "^2.2.1",
"bson": "^1.1.4",
"denque": "^1.4.1",
"optional-require": "^1.1.8",
"safe-buffer": "^5.1.2",
"saslprep": "^1.0.0"
}
},
"mongomock": {
@@ -6855,6 +6843,14 @@
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
"dev": true
},
"optional-require": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz",
"integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==",
"requires": {
"require-at": "^1.0.6"
}
},
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -7125,8 +7121,7 @@
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"dev": true
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"progress": {
"version": "2.0.3",
@@ -7467,6 +7462,11 @@
"tough-cookie": "^2.3.3"
}
},
"require-at": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz",
"integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g=="
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -8846,11 +8846,6 @@
}
}
},
"smart-buffer": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
},
"socket.io": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz",
@@ -8889,15 +8884,6 @@
"debug": "~4.3.1"
}
},
"socks": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
"integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"requires": {
"ip": "^2.0.0",
"smart-buffer": "^4.2.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "nightscout",
"version": "15.0.0",
"version": "15.0.3",
"description": "Nightscout acts as a web-based CGM (Continuous Glucose Montinor) to allow multiple caregivers to remotely view a patients glucose data in realtime.",
"license": "AGPL-3.0",
"author": "Nightscout Team",
@@ -114,7 +114,7 @@
"moment-timezone": "^0.5.31",
"moment-timezone-data-webpack-plugin": "^1.5.0",
"mongo-url-parser": "^1.0.2",
"mongodb-legacy": "^5.0.0",
"mongodb": "^3.6.0",
"mongomock": "^0.1.2",
"nightscout-connect": "^0.0.12",
"node-cache": "^4.2.1",
+2 -2
View File
@@ -25,13 +25,13 @@ describe('Clean MONGO after tests', function ( ) {
});
it('wipe treatment data', function (done) {
self.ctx.treatments().deleteMany({ }, function ( ) {
self.ctx.treatments().remove({ }, function ( ) {
done();
});
});
it('wipe entries data', function (done) {
self.ctx.entries().deleteMany({ }, function ( ) {
self.ctx.entries().remove({ }, function ( ) {
done();
});
});
+3 -3
View File
@@ -63,11 +63,11 @@ describe('Entries REST api', function ( ) {
});
afterEach(function (done) {
self.archive( ).deleteMany({ }, done);
self.archive( ).remove({ }, done);
});
after(function (done) {
self.archive( ).deleteMany({ }, done);
self.archive( ).remove({ }, done);
});
// keep this test pinned at or near the top in order to validate all
@@ -238,7 +238,7 @@ describe('Entries REST api', function ( ) {
it('/entries/:id', function (done) {
var app = self.app;
self.archive.list({count: 10}, function(err, records) {
self.archive.list({count: 1}, function(err, records) {
var currentId = records.pop()._id.toString();
request(app)
.get('/entries/'+currentId+'.json')
+5 -5
View File
@@ -35,7 +35,7 @@ describe('Treatment API', function ( ) {
it('post single treatments', function (done) {
self.ctx.treatments.remove({ find: { created_at: { '$gte': '1999-01-01T00:00:00.000Z' } } }, function ( ) {
self.ctx.treatments().remove({ }, function ( ) {
var now = (new Date()).toISOString();
request(self.app)
.post('/api/treatments/')
@@ -88,8 +88,8 @@ describe('Treatment API', function ( ) {
var current_time = Date.now();
console.log('Testing date with local format: ', _moment(current_time).format("YYYY-MM-DDTHH:mm:ss.SSSZZ"));
self.ctx.treatments.remove({ find: { created_at: { '$gte': '1999-01-01T00:00:00.000Z' } } }, function ( ) {
self.ctx.treatments().remove({ }, function ( ) {
request(self.app)
.post('/api/treatments/')
.set('api-secret', api_secret_hash || '')
@@ -122,7 +122,7 @@ describe('Treatment API', function ( ) {
it('post a treatment array', function (done) {
self.ctx.treatments.remove({ find: { created_at: { '$gte': '1999-01-01T00:00:00.000Z' } } }, function ( ) {
self.ctx.treatments().remove({ }, function ( ) {
var now = (new Date()).toISOString();
request(self.app)
.post('/api/treatments/')
@@ -149,7 +149,7 @@ describe('Treatment API', function ( ) {
});
it('post a treatment array and dedupe', function (done) {
self.ctx.treatments.remove({ find: { created_at: { '$gte': '1999-01-01T00:00:00.000Z' } } }, function ( ) {
self.ctx.treatments().remove({ }, function ( ) {
var now = (new Date()).toISOString();
request(self.app)
.post('/api/treatments/')
+2 -2
View File
@@ -39,11 +39,11 @@ describe('authed REST api', function ( ) {
});
afterEach(function (done) {
this.archive( ).deleteMany({ }, done);
this.archive( ).remove({ }, done);
});
after(function (done) {
this.archive( ).deleteMany({ }, done);
this.archive( ).remove({ }, done);
});
it('disallow unauthorized POST', function (done) {
+115
View File
@@ -0,0 +1,115 @@
'use strict';
var should = require('should');
describe('openaps storage', function () {
var env = require('../lib/server/env')();
before(function (done) {
delete env.api_secret;
env.storageURI = 'openaps://../../tests/fixtures/openaps-storage/config';
done();
});
it('The module class should be OK.', function (done) {
require('../lib/storage/openaps-storage')(env, function callback (err, storage) {
should.not.exist(err);
should.exist(storage.collection);
should.exist(storage.ensureIndexes);
done();
});
});
it('find sgv entries', function (done) {
require('../lib/storage/openaps-storage')(env, function callback (err, storage) {
should.not.exist(err);
should.exist(storage.collection);
storage.collection('entries').find({type: 'sgv'}).toArray(function callback (err, results) {
should.not.exist(err);
should.exist(results);
results.length.should.equal(4);
results[0].sgv.should.equal(102);
done();
});
});
});
it('find cal entries', function (done) {
require('../lib/storage/openaps-storage')(env, function callback (err, storage) {
should.not.exist(err);
should.exist(storage.collection);
storage.collection('entries').find({type: 'cal'}).toArray(function callback (err, results) {
should.not.exist(err);
should.exist(results);
results.length.should.equal(1);
results[0].slope.should.equal(841.6474113376482);
done();
});
});
});
it('find devicestatus entries', function (done) {
require('../lib/storage/openaps-storage')(env, function callback (err, storage) {
should.not.exist(err);
should.exist(storage.collection);
storage.collection('devicestatus').find({}).toArray(function callback (err, results) {
should.not.exist(err);
should.exist(results);
results.length.should.equal(1);
results[0].openaps.enacted.eventualBG.should.equal(82);
done();
});
});
});
it('find treatments', function (done) {
require('../lib/storage/openaps-storage')(env, function callback (err, storage) {
should.not.exist(err);
should.exist(storage.collection);
storage.collection('treatments').find({}).toArray(function callback (err, results) {
should.not.exist(err);
should.exist(results);
results.length.should.equal(2);
results[0].eventType.should.equal('Temp Basal');
done();
});
});
});
it('When no connection-string is given the storage-class should throw an error.', function (done) {
delete env.storageURI;
should.not.exist(env.storageURI);
(function () {
return require('../lib/storage/openaps-storage')(env);
}).should.throw('openaps config uri is missing or invalid');
done();
});
it('An invalid connection-string should throw an error.', function (done) {
env.storageURI = 'This is not an openaps config path';
(function () {
return require('../lib/storage/openaps-storage')(env);
}).should.throw(Error);
done();
});
});