mirror of
https://github.com/bckelley/cgm-remote-monitor.git
synced 2026-08-24 03:14:12 -05:00
Regression from 286fa07 (switch profile api to new mongo driver) caused
E11000 duplicate key errors when saving an existing profile because
insertOne rejects documents whose _id already exists.
Changes:
- Replace insertOne with replaceOne({ _id }, obj, { upsert: true })
- Add try/catch for ObjectID construction to handle invalid/missing _id
- Use hasOwnProperty check for created_at to preserve explicit values
Tests added:
- save() updates existing profile by _id (from PR #8455)
- save() generates _id when none provided
- save() generates _id when invalid _id provided
- save() preserves explicit created_at without overwriting
Cherry-picked from AndyLow91/cgm-remote-monitor@55a70139 with
additional regression tests.
Closes nightscout/cgm-remote-monitor#8455
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
128 lines
3.1 KiB
JavaScript
128 lines
3.1 KiB
JavaScript
'use strict';
|
|
|
|
var find_options = require('./query');
|
|
var consts = require('../constants');
|
|
|
|
function storage (collection, ctx) {
|
|
var ObjectID = require('mongodb-legacy').ObjectId;
|
|
|
|
function create (objOrArray, fn) {
|
|
// Normalize to array (supports both single object and array inputs)
|
|
var docs = Array.isArray(objOrArray) ? objOrArray : [objOrArray];
|
|
|
|
if (docs.length === 0) {
|
|
fn(null, []);
|
|
ctx.bus.emit('data-received');
|
|
return;
|
|
}
|
|
|
|
// Add created_at to each document
|
|
docs.forEach(function(doc) {
|
|
if (!doc.created_at) {
|
|
doc.created_at = (new Date()).toISOString();
|
|
}
|
|
});
|
|
|
|
api().insertMany(docs, function (err, result) {
|
|
if (err) {
|
|
console.log("Error saving profile data", docs, err);
|
|
fn(err);
|
|
return;
|
|
}
|
|
// Return the inserted documents with _id (NightscoutKit expects array)
|
|
fn(null, docs);
|
|
});
|
|
ctx.bus.emit('data-received');
|
|
}
|
|
|
|
function save (obj, fn) {
|
|
try {
|
|
obj._id = new ObjectID(obj._id);
|
|
} catch (err) {
|
|
obj._id = new ObjectID();
|
|
}
|
|
if (!Object.prototype.hasOwnProperty.call(obj, 'created_at')) {
|
|
obj.created_at = (new Date( )).toISOString( );
|
|
}
|
|
// Match existing profiles by _id only. The profile editor rewrites created_at on save.
|
|
api().replaceOne({ _id: obj._id }, obj, { upsert: true }, function (err) {
|
|
//id should be added for new docs
|
|
fn(err, obj);
|
|
});
|
|
ctx.bus.emit('data-received');
|
|
}
|
|
|
|
function list (fn, count) {
|
|
const limit = count !== null ? count : Number(consts.PROFILES_DEFAULT_COUNT);
|
|
return api( ).find({ }).limit(limit).sort({startDate: -1}).toArray(fn);
|
|
}
|
|
|
|
function list_query (opts, fn) {
|
|
|
|
storage.queryOpts = {
|
|
walker: {}
|
|
, dateField: 'startDate'
|
|
};
|
|
|
|
function limit () {
|
|
if (opts && opts.count) {
|
|
return this.limit(parseInt(opts.count));
|
|
}
|
|
return this;
|
|
}
|
|
|
|
return limit.call(api()
|
|
.find(query_for(opts))
|
|
.sort(opts && opts.sort && query_sort(opts) || { startDate: -1 }), opts)
|
|
.toArray(fn);
|
|
}
|
|
|
|
function query_for (opts) {
|
|
var retVal = find_options(opts, storage.queryOpts);
|
|
return retVal;
|
|
}
|
|
|
|
function query_sort (opts) {
|
|
if (opts && opts.sort) {
|
|
var sortKeys = Object.keys(opts.sort);
|
|
|
|
for (var i = 0; i < sortKeys.length; i++) {
|
|
if (opts.sort[sortKeys[i]] == '1') {
|
|
opts.sort[sortKeys[i]] = 1;
|
|
}
|
|
else {
|
|
opts.sort[sortKeys[i]] = -1;
|
|
}
|
|
}
|
|
return opts.sort;
|
|
}
|
|
}
|
|
|
|
|
|
function last (fn) {
|
|
return api().find().sort({startDate: -1}).limit(1).toArray(fn);
|
|
}
|
|
|
|
function remove (_id, fn) {
|
|
var objId = new ObjectID(_id);
|
|
api( ).deleteOne({ '_id': objId }, fn);
|
|
|
|
ctx.bus.emit('data-received');
|
|
}
|
|
|
|
function api () {
|
|
return ctx.store.collection(collection);
|
|
}
|
|
|
|
api.list = list;
|
|
api.list_query = list_query;
|
|
api.create = create;
|
|
api.save = save;
|
|
api.remove = remove;
|
|
api.last = last;
|
|
api.indexedFields = ['startDate'];
|
|
return api;
|
|
}
|
|
|
|
module.exports = storage;
|