mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
This reverts commit3b786ab3. On review the V3 (app, defaultProfile) collapse was too aggressive and broke parity with how the rest of the ecosystem treats the profile collection: - Loop (NightscoutKit) and Trio (NightscoutAPI.swift:411) both POST /api/v1/profile without _id on every settings edit, accumulating one doc per upload via lib/server/profile.js:create(). They have done so for years. - The Nightscout profile collection is historical/append-only by design; the NS UI profile editor lets users navigate prior snapshots, and lib/server/profile.js:last() picks the most recent for display. - Collapsing AAPS V3 edits onto a single (app, defaultProfile) row diverged from Loop/Trio/AAPS-V1 behavior and erased the upload history that NS UI exposes. The original 'AAPS edits not appearing' user complaint is sufficiently addressed by: - the V1 websocket retry dedup (commit85f7e6ac), which kills the 60s ack-window race; and - the {startDate: -1, _id: -1} secondary sort in profile.last() (also85f7e6ac), which deterministically picks the newest row when startDate ties. Both of those help every uploader (Loop, Trio, AAPS V1, AAPS V3) without changing the ecosystem-wide profile-as-history semantic. The characterization tests added inddabdc6care restored by this revert and continue to document V3's request-level (date-based) dedup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
136 lines
2.7 KiB
JavaScript
136 lines
2.7 KiB
JavaScript
'use strict';
|
|
|
|
const apiConst = require('../const.json')
|
|
, stringTools = require('./stringTools')
|
|
, uuid = require('uuid')
|
|
, uuidNamespace = [...Buffer.from("NightscoutRocks!", "ascii")] // official namespace for NS :-)
|
|
;
|
|
|
|
|
|
function sendJSON ({ res, result, status, fields }) {
|
|
|
|
const json = {
|
|
status: status || apiConst.HTTP.OK,
|
|
result: result
|
|
};
|
|
|
|
if (result) {
|
|
json.result = result
|
|
}
|
|
|
|
if (fields) {
|
|
Object.assign(json, fields);
|
|
}
|
|
|
|
res.status(json.status).json(json);
|
|
}
|
|
|
|
|
|
function sendJSONStatus (res, status, title, description, warning) {
|
|
|
|
const json = {
|
|
status: status
|
|
};
|
|
|
|
if (title) { json.message = title }
|
|
|
|
if (description) { json.description = description }
|
|
|
|
// Add optional warning message.
|
|
if (warning) { json.warning = warning; }
|
|
|
|
res.status(status)
|
|
.json(json);
|
|
|
|
return title;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate document's common fields
|
|
* @param {Object} doc
|
|
* @param {any} res
|
|
* @param {Object} options
|
|
* @returns {any} - string with error message if validation fails, true in case of success
|
|
*/
|
|
function validateCommon (doc, res, options) {
|
|
|
|
const { isPatching } = options || {};
|
|
|
|
|
|
if ((!isPatching || typeof(doc.date) !== 'undefined')
|
|
|
|
&& (typeof(doc.date) !== 'number'
|
|
|| doc.date <= apiConst.MIN_TIMESTAMP)
|
|
) {
|
|
return sendJSONStatus(res, apiConst.HTTP.BAD_REQUEST, apiConst.MSG.HTTP_400_BAD_FIELD_DATE);
|
|
}
|
|
|
|
|
|
if ((!isPatching || typeof(doc.utcOffset) !== 'undefined')
|
|
|
|
&& (typeof(doc.utcOffset) !== 'number'
|
|
|| doc.utcOffset < apiConst.MIN_UTC_OFFSET
|
|
|| doc.utcOffset > apiConst.MAX_UTC_OFFSET)
|
|
) {
|
|
return sendJSONStatus(res, apiConst.HTTP.BAD_REQUEST, apiConst.MSG.HTTP_400_BAD_FIELD_UTC);
|
|
}
|
|
|
|
|
|
if ((!isPatching || typeof(doc.app) !== 'undefined')
|
|
|
|
&& (typeof(doc.app) !== 'string'
|
|
|| stringTools.isNullOrWhitespace(doc.app))
|
|
) {
|
|
return sendJSONStatus(res, apiConst.HTTP.BAD_REQUEST, apiConst.MSG.HTTP_400_BAD_FIELD_APP);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Calculate identifier for the document
|
|
* @param {Object} doc
|
|
* @returns string
|
|
*/
|
|
function calculateIdentifier (doc) {
|
|
if (!doc)
|
|
return undefined;
|
|
|
|
let key = doc.device + '_' + doc.date;
|
|
if (doc.eventType) {
|
|
key += '_' + doc.eventType;
|
|
}
|
|
|
|
return uuid.v5(key, uuidNamespace);
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate identifier in the document
|
|
* @param {Object} doc
|
|
*/
|
|
function resolveIdentifier (doc) {
|
|
|
|
let identifier = calculateIdentifier(doc);
|
|
if (doc.identifier) {
|
|
if (doc.identifier !== identifier) {
|
|
console.warn(`APIv3: Identifier mismatch (expected: ${identifier}, received: ${doc.identifier})`);
|
|
console.log(doc);
|
|
}
|
|
}
|
|
else {
|
|
doc.identifier = identifier;
|
|
}
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
sendJSON,
|
|
sendJSONStatus,
|
|
validateCommon,
|
|
calculateIdentifier,
|
|
resolveIdentifier
|
|
};
|