UNPKG

nightscout

Version:

Nightscout acts as a web-based CGM (Continuous Glucose Monitor) to allow multiple caregivers to remotely view a patients glucose data in realtime.

155 lines (118 loc) 4.08 kB
'use strict'; const dateTools = require('../../shared/dateTools') , renderer = require('../../shared/renderer') , apiConst = require('../../const.json') , security = require('../../security') , opTools = require('../../shared/operationTools') , FieldsProjector = require('../../shared/fieldsProjector') , _ = require('lodash') ; /** * HISTORY: Retrieves incremental changes since timestamp */ async function history (opCtx, fieldsProjector) { const { req, res, col } = opCtx; let filter = parseFilter(opCtx) , limit = col.parseLimit(req, res) , projection = fieldsProjector.storageProjection() , sort = prepareSort() , skip = 0 , onlyValid = false , logicalOperator = 'or' ; if (filter !== null && limit !== null && projection !== null) { const result = await col.storage.findMany({ filter , sort , limit , skip , projection , onlyValid , logicalOperator }); if (!result) throw new Error('empty result'); if (result.length === 0) { res.status(apiConst.HTTP.OK); return renderer.render(res, result); } _.each(result, col.resolveDates); const srvModifiedValues = _.map(result, function mapSrvModified (item) { return item.srvModified; }) , maxSrvModified = _.max(srvModifiedValues); res.setHeader('Last-Modified', (new Date(maxSrvModified)).toUTCString()); res.setHeader('ETag', 'W/"' + maxSrvModified + '"'); _.each(result, fieldsProjector.applyProjection); res.status(apiConst.HTTP.OK); renderer.render(res, result); } } /** * Parse history filtering criteria from Last-Modified header */ function parseFilter (opCtx) { const { req, res } = opCtx; let lastModified = null , lastModifiedParam = req.params.lastModified , operator = null; if (lastModifiedParam) { // using param in URL as a source of timestamp const m = dateTools.parseToMoment(lastModifiedParam); if (m === null || !m.isValid()) { opTools.sendJSONStatus(res, apiConst.HTTP.BAD_REQUEST, apiConst.MSG.HTTP_400_BAD_LAST_MODIFIED); return null; } lastModified = m.toDate(); operator = 'gt'; } else { // using request HTTP header as a source of timestamp const lastModifiedHeader = req.get('Last-Modified'); if (!lastModifiedHeader) { opTools.sendJSONStatus(res, apiConst.HTTP.BAD_REQUEST, apiConst.MSG.HTTP_400_BAD_LAST_MODIFIED); return null; } try { lastModified = dateTools.floorSeconds(new Date(lastModifiedHeader)); } catch (err) { opTools.sendJSONStatus(res, apiConst.HTTP.BAD_REQUEST, apiConst.MSG.HTTP_400_BAD_LAST_MODIFIED); return null; } operator = 'gte'; } return [ { field: 'srvModified', operator: operator, value: lastModified.getTime() }, { field: 'created_at', operator: operator, value: lastModified.toISOString() }, { field: 'date', operator: operator, value: lastModified.getTime() } ]; } /** * Prepare sorting for storage query */ function prepareSort () { return { srvModified: 1, created_at: 1, date: 1 }; } function historyOperation (ctx, env, app, col) { return async function operation (req, res) { const opCtx = { app, ctx, env, col, req, res }; try { opCtx.auth = await security.authenticate(opCtx); if (col.colName === 'settings') { await security.demandPermission(opCtx, `api:${col.colName}:admin`); } else { await security.demandPermission(opCtx, `api:${col.colName}:read`); } const fieldsProjector = new FieldsProjector(req.query.fields); await history(opCtx, fieldsProjector); } catch (err) { console.error(err); if (!res.headersSent) { return opTools.sendJSONStatus(res, apiConst.HTTP.INTERNAL_ERROR, apiConst.MSG.STORAGE_ERROR); } } }; } module.exports = historyOperation;