cry-db 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mongo.js ADDED
@@ -0,0 +1,1431 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DummyExportToFixTsCompilation = void 0;
7
+ const assert_1 = __importDefault(require("assert"));
8
+ const bcrypt_1 = __importDefault(require("bcrypt"));
9
+ const mongodb_1 = require("mongodb");
10
+ const tiny_typed_emitter_1 = require("tiny-typed-emitter");
11
+ const _1 = require(".");
12
+ const db_1 = require("./db");
13
+ const types_1 = require("./types");
14
+ const saltRounds = 10;
15
+ const TRANSACTION_OPTIONS = {
16
+ defaultTransactionOptions: {
17
+ readPreference: 'primary',
18
+ readConcern: { level: 'local' },
19
+ writeConcern: { w: 'majority' }
20
+ }
21
+ };
22
+ exports.DummyExportToFixTsCompilation = true;
23
+ class Mongo extends db_1.Db {
24
+ constructor(db, url) {
25
+ super(db, url);
26
+ this.revisions = false;
27
+ this.softdelete = false;
28
+ this.syncSupport = false;
29
+ this.session = undefined;
30
+ this.emittingPublishEvents = false;
31
+ this.auditing = false;
32
+ this.auditCollectionName = "dblog";
33
+ this.auditedCollections = this.auditCollections(process.env.AUDIT_COLLECTIONS || []);
34
+ this.emitter = new tiny_typed_emitter_1.TypedEmitter();
35
+ this.user = undefined;
36
+ this.audit = undefined;
37
+ db_1.log.debug('new Mongo:', this.url, this.db);
38
+ }
39
+ static newid() {
40
+ return db_1.Db.newid();
41
+ }
42
+ static toId(id) {
43
+ return db_1.Db.toId(id);
44
+ }
45
+ static objectid(o) {
46
+ return db_1.Db.objectid(o);
47
+ }
48
+ on(evt, listener) {
49
+ db_1.log.debug("on", evt, listener);
50
+ this.emitter.on(evt, listener);
51
+ }
52
+ off(evt, listener) {
53
+ db_1.log.debug("off", evt, listener);
54
+ this.emitter.off(evt, listener);
55
+ }
56
+ once(evt, listener) {
57
+ db_1.log.debug("off", evt, listener);
58
+ this.emitter.off(evt, listener);
59
+ }
60
+ setUser(username) {
61
+ return this.user = username;
62
+ }
63
+ setAudit(obj) {
64
+ return this.audit = obj;
65
+ }
66
+ useRevisions(enabled) {
67
+ return this.revisions = !!enabled;
68
+ }
69
+ usesRevisions() {
70
+ return this.revisions;
71
+ }
72
+ useSoftDelete(enabled) {
73
+ return this.softdelete = !!enabled;
74
+ }
75
+ usesSoftDelete() {
76
+ return this.softdelete;
77
+ }
78
+ useAuditing(enabled) {
79
+ return this.auditing = enabled;
80
+ }
81
+ usesAuditing() {
82
+ return this.auditing;
83
+ }
84
+ useSync(enabled) {
85
+ return this.syncSupport = enabled;
86
+ }
87
+ usesSync() {
88
+ return this.syncSupport;
89
+ }
90
+ auditToCollection(c) {
91
+ if (c)
92
+ return this.auditCollectionName = c;
93
+ return this.auditCollectionName;
94
+ }
95
+ auditCollection(coll, enable = true) {
96
+ coll = coll.toLowerCase().trim();
97
+ if (typeof enable !== "boolean")
98
+ throw new Error("auditCollection: second argument must be a boolean");
99
+ if (enable) {
100
+ if (this.auditedCollections.includes(coll))
101
+ return;
102
+ this.auditedCollections.push(coll);
103
+ }
104
+ else {
105
+ this.auditedCollections = this.auditedCollections.filter((c) => c != coll);
106
+ }
107
+ }
108
+ auditCollections(arr) {
109
+ if (arr === undefined)
110
+ return this.auditedCollections;
111
+ if (arr && typeof arr === "string")
112
+ this.auditedCollections = arr.toString().split(',').map(a => a.trim().toLowerCase()).filter(a => !!a);
113
+ if (arr instanceof Array)
114
+ this.auditedCollections = arr.map(a => a.trim().toLowerCase()).filter(a => !!a);
115
+ return this.auditedCollections;
116
+ }
117
+ auditingCollection(coll, db = this.db) {
118
+ return this._shouldAuditCollection(db, coll);
119
+ }
120
+ emitPublishEvents(enabled) {
121
+ return this.emittingPublishEvents = enabled;
122
+ }
123
+ emitsPublishEvents() {
124
+ return this.emittingPublishEvents;
125
+ }
126
+ async distinct(collection, field) {
127
+ db_1.log.debug('distinct called', collection, field);
128
+ let ret = await this.executeTransactionally(collection, async (conn) => {
129
+ return await conn.distinct(field);
130
+ }, false, { operation: "distinct", collection, field });
131
+ db_1.log.debug('distinct returns', ret);
132
+ return ret;
133
+ }
134
+ async count(collection, query = {}, opts = {}) {
135
+ db_1.log.debug('distinct called', collection, query, opts);
136
+ let ret = await this.executeTransactionally(collection, async (conn) => {
137
+ return await conn.countDocuments(query, opts);
138
+ }, false, { operation: "count", collection, query, opts });
139
+ db_1.log.debug('count returns', ret);
140
+ return ret;
141
+ }
142
+ async find(collection, query = {}, opts = {}) {
143
+ (0, assert_1.default)(collection);
144
+ query = this.replaceIds(query);
145
+ if (this.softdelete) {
146
+ if (!query._deleted)
147
+ query._deleted = { $exists: false };
148
+ }
149
+ db_1.log.debug('find called', collection, query, opts);
150
+ let ret = await this.executeTransactionally(collection, async (conn) => {
151
+ let optsIn = {};
152
+ if (opts.readPreference)
153
+ optsIn.readPreference = opts.readPreference;
154
+ if (this.session)
155
+ optsIn.session = this.session;
156
+ let r = conn.find(query, optsIn);
157
+ if (opts.project)
158
+ r = r.project(opts.project);
159
+ if (opts.sort)
160
+ r = r.sort(opts.sort);
161
+ if (opts.skip)
162
+ r = r.skip(opts.skip);
163
+ if (opts.limit)
164
+ r = r.limit(opts.limit);
165
+ if (opts.collation)
166
+ r = r.collation(opts.collation);
167
+ let res = await r.toArray();
168
+ return this._processReturnedObject(res);
169
+ }, false, { operation: "find", collection, query, opts });
170
+ db_1.log.debug('find returns', ret);
171
+ return ret;
172
+ }
173
+ async findAll(collection, query = {}, opts = {}) {
174
+ (0, assert_1.default)(collection);
175
+ query = this.replaceIds(query);
176
+ let ret = await this.executeTransactionally(collection, async (conn) => {
177
+ let optsIn = {};
178
+ if (opts.readPreference)
179
+ optsIn.readPreference = opts.readPreference;
180
+ if (this.session)
181
+ optsIn.session = this.session;
182
+ let r = conn.find(query, optsIn);
183
+ if (opts.project)
184
+ r = r.project(opts.project);
185
+ if (opts.sort)
186
+ r = r.sort(opts.sort);
187
+ if (opts.skip)
188
+ r = r.skip(opts.skip);
189
+ if (opts.limit)
190
+ r = r.limit(opts.limit);
191
+ if (opts.collation)
192
+ r = r.collation(opts.collation);
193
+ return this._processReturnedObject(await r.toArray());
194
+ }, false, { operation: "findAll", collection, query, opts });
195
+ db_1.log.debug('findAll returns', ret);
196
+ return ret;
197
+ }
198
+ async findNewer(collection, timestamp, query = {}, opts = {}) {
199
+ if (timestamp === 1 || timestamp === "1" || timestamp === "0" || timestamp === 0)
200
+ timestamp = new Date(0);
201
+ if (!(timestamp instanceof mongodb_1.Timestamp))
202
+ timestamp = _1.Base.timestamp(timestamp);
203
+ query._ts = { $gt: timestamp };
204
+ // query._deleted = { $exists: false }; // intentionally not set, to forward deletions
205
+ // query._blocked = { $exists: false }; // intentionally not set, to forward blocks
206
+ db_1.log.debug('findNewer called', collection, timestamp, query, opts);
207
+ let ret = await this.executeTransactionally(collection, async (conn) => {
208
+ let optsIn = {};
209
+ if (opts.readPreference)
210
+ optsIn.readPreference = opts.readPreference;
211
+ if (this.session)
212
+ optsIn.session = this.session;
213
+ let r = conn
214
+ .find(query, optsIn)
215
+ .sort({ _ts: 1 });
216
+ if (opts.project)
217
+ r = r.project(opts.project);
218
+ if (opts.sort)
219
+ r = r.sort(opts.sort);
220
+ if (opts.skip)
221
+ r = r.skip(opts.skip);
222
+ if (opts.limit)
223
+ r = r.limit(opts.limit);
224
+ if (opts.collation)
225
+ r = r.collation(opts.collation);
226
+ return this._processReturnedObject(await r.toArray());
227
+ }, false, { operation: "findNewer", collection, timestamp, query, opts });
228
+ db_1.log.debug('findNewer returns', ret);
229
+ return ret;
230
+ }
231
+ async findNewerMany(spec = []) {
232
+ db_1.log.debug('findAfterMany called', spec);
233
+ let conn = await this.connect();
234
+ const getOneColl = async (coll) => {
235
+ let timestamp = coll.timestamp;
236
+ if (timestamp === 1 || timestamp === "1" || timestamp === "0" || timestamp === 0)
237
+ timestamp = new Date(0);
238
+ if (!(timestamp instanceof mongodb_1.Timestamp))
239
+ timestamp = _1.Base.timestamp(timestamp);
240
+ let query = {
241
+ ...(coll.query || {}),
242
+ _ts: { $gt: timestamp }
243
+ };
244
+ let r = conn
245
+ .db(this.db)
246
+ .collection(coll.collection)
247
+ .find(query)
248
+ .sort({ _ts: 1 });
249
+ let opts = coll.opts || {};
250
+ if (opts.project)
251
+ r = r.project(opts.project);
252
+ if (opts.sort)
253
+ r = r.sort(opts.sort);
254
+ if (opts.skip)
255
+ r = r.skip(opts.skip);
256
+ if (opts.limit)
257
+ r = r.limit(opts.limit);
258
+ if (opts.collation)
259
+ r = r.collation(opts.collation);
260
+ let data = await r.toArray();
261
+ return { collection: coll.collection, data };
262
+ };
263
+ let promises = [];
264
+ for (let coll of spec)
265
+ promises.push(getOneColl(coll));
266
+ let out = {};
267
+ (await Promise.all(promises)).forEach(r => out[r.collection] = r.data);
268
+ return out;
269
+ }
270
+ async findAfter(collection, csq, query = {}, opts = {}) {
271
+ query._csq = { $gt: csq };
272
+ db_1.log.debug('findAfter called', collection, csq, query, opts);
273
+ let ret = await this.executeTransactionally(collection, async (conn) => {
274
+ let optsIn = {};
275
+ if (opts.readPreference)
276
+ optsIn.readPreference = opts.readPreference;
277
+ if (this.session)
278
+ optsIn.session = this.session;
279
+ let r = conn
280
+ .find(query, optsIn)
281
+ .sort({ _ts: 1 });
282
+ if (opts.project)
283
+ r = r.project(opts.project);
284
+ if (opts.sort)
285
+ r = r.sort(opts.sort);
286
+ if (opts.skip)
287
+ r = r.skip(opts.skip);
288
+ if (opts.limit)
289
+ r = r.limit(opts.limit);
290
+ if (opts.collation)
291
+ r = r.collation(opts.collation);
292
+ return this._processReturnedObject(await r.toArray());
293
+ }, false, { operation: "findNewer", collection, csq, query, opts });
294
+ db_1.log.debug('findNewer returns', ret);
295
+ return ret;
296
+ }
297
+ async findAfterMany(spec = []) {
298
+ db_1.log.debug('findAfterMany called', spec);
299
+ let conn = await this.connect();
300
+ const getOneColl = async (coll) => {
301
+ let r = conn
302
+ .db(this.db)
303
+ .collection(coll.collection)
304
+ .find({
305
+ _csq: { $gt: coll.csq, ...(coll.query || {}) }
306
+ })
307
+ .sort({ _csq: 1 });
308
+ let opts = coll.opts || {};
309
+ if (opts.project)
310
+ r = r.project(opts.project);
311
+ if (opts.sort)
312
+ r = r.sort(opts.sort);
313
+ if (opts.skip)
314
+ r = r.skip(opts.skip);
315
+ if (opts.limit)
316
+ r = r.limit(opts.limit);
317
+ if (opts.collation)
318
+ r = r.collation(opts.collation);
319
+ let data = await r.toArray();
320
+ return { collection: coll.collection, data };
321
+ };
322
+ let promises = [];
323
+ for (let coll of spec)
324
+ promises.push(getOneColl(coll));
325
+ let out = {};
326
+ (await Promise.all(promises)).forEach(r => out[r.collection] = r.data);
327
+ return out;
328
+ }
329
+ async findNewerFromDate(collection, date, query = {}, opts = {}) {
330
+ let ts = new mongodb_1.Timestamp(0, new Date(date).valueOf() / 1000);
331
+ db_1.log.debug('findNewerFromDate called', collection, date, query, opts);
332
+ let ret = await Mongo.prototype.findNewer.call(this, collection, ts, query, opts); // prevent calling Repo.findNewer
333
+ db_1.log.debug('findNewerFromDate returns', ret);
334
+ return ret;
335
+ }
336
+ async findOne(collection, query, projection) {
337
+ (0, assert_1.default)(collection);
338
+ (0, assert_1.default)(query);
339
+ query = this.replaceIds(query);
340
+ if (!query._deleted)
341
+ query._deleted = { $exists: false };
342
+ // if (!query._blocked) query._blocked = { $exists: false }; // intentionally - blocked records are returned
343
+ db_1.log.debug('findOne called', collection, query, projection);
344
+ let ret = await this.executeTransactionally(collection, async (conn) => conn.findOne(query, { projection, session: this.session }), false, { operation: "findOne", collection, query, projection });
345
+ db_1.log.debug('findOne returns', ret);
346
+ return this._processReturnedObject(ret);
347
+ }
348
+ async findById(collection, id, projection) {
349
+ (0, assert_1.default)(collection);
350
+ (0, assert_1.default)(id);
351
+ if (!id)
352
+ return null;
353
+ let query = {
354
+ _id: Mongo._toId(id),
355
+ // _deleted: { $exists: false }
356
+ };
357
+ db_1.log.debug('findById called', this.db, collection, id, projection);
358
+ db_1.log.trace('findById executing with query', collection, query, projection);
359
+ let ret = await this.executeTransactionally(collection, async (conn) => conn.findOne(query, { projection, session: this.session }), false, { operation: "findById", collection, id, projection });
360
+ if (ret._deleted)
361
+ ret = null;
362
+ db_1.log.debug('findById returns', ret);
363
+ return this._processReturnedObject(await ret);
364
+ }
365
+ async updateOne(collection, query, update, options = { returnFullObject: false }) {
366
+ (0, assert_1.default)(collection);
367
+ (0, assert_1.default)(query);
368
+ (0, assert_1.default)(update);
369
+ query = this.replaceIds(query);
370
+ update = this.replaceIds(update);
371
+ let opts = {
372
+ upsert: false,
373
+ returnNewDocument: true,
374
+ returnOriginal: false,
375
+ session: this.session,
376
+ };
377
+ update = await this._processUpdateObject(update);
378
+ db_1.log.debug('updateOne called', collection, query, update);
379
+ let seqKeys = this._findSequenceKeys(update.$set);
380
+ let obj = await this.executeTransactionally(collection, async (conn, client) => {
381
+ update.$set = update.$set || {};
382
+ if (seqKeys)
383
+ await this._processSequenceField(client, collection, update.$set, seqKeys);
384
+ if (update.$set === undefined || Object.keys(update.$set).length === 0)
385
+ delete update.$set;
386
+ let res = await conn.findOneAndUpdate(query, update, opts);
387
+ if (!res.value)
388
+ return null;
389
+ let resObj = this._removeUnchanged(res.value, update, !!(options === null || options === void 0 ? void 0 : options.returnFullObject));
390
+ await this._publishAndAudit('update', this.db, collection, resObj);
391
+ return resObj;
392
+ }, !!seqKeys, { operation: "updateOne", collection, query, update, options });
393
+ db_1.log.debug('updateOne returns', obj);
394
+ return this._processReturnedObject(await obj);
395
+ }
396
+ async save(collection, update, id = undefined, options = { returnFullObject: false }) {
397
+ (0, assert_1.default)(collection);
398
+ (0, assert_1.default)(update);
399
+ update = this.replaceIds(update);
400
+ let opts = {
401
+ upsert: true,
402
+ returnNewDocument: true,
403
+ returnOriginal: false,
404
+ session: this.session,
405
+ };
406
+ let _id = Mongo.toId(id || update._id) || Mongo.newid();
407
+ update = await this._processUpdateObject(update);
408
+ db_1.log.debug('save called', collection, id, update);
409
+ let seqKeys = this._findSequenceKeys(update.$set);
410
+ let obj = await this.executeTransactionally(collection, async (conn, client) => {
411
+ update.$set = update.$set || {};
412
+ if (seqKeys)
413
+ await this._processSequenceField(client, collection, update.$set, seqKeys);
414
+ if (update.$set === undefined || Object.keys(update.$set).length === 0)
415
+ delete update.$set;
416
+ let res = await conn.findOneAndUpdate({ _id }, update, opts);
417
+ if (!res.value)
418
+ return null;
419
+ let resObj = this._removeUnchanged(res.value, update, !!(options === null || options === void 0 ? void 0 : options.returnFullObject));
420
+ await this._publishAndAudit('update', this.db, collection, resObj);
421
+ return resObj;
422
+ }, !!seqKeys, { operation: "save", collection, _id, update, options });
423
+ db_1.log.debug('save returns', obj);
424
+ return this._processReturnedObject(await obj);
425
+ }
426
+ async update(collection, query, update) {
427
+ (0, assert_1.default)(collection);
428
+ (0, assert_1.default)(query);
429
+ (0, assert_1.default)(update);
430
+ if (this.syncSupport)
431
+ db_1.log.warn("update does not increase _csq, avoit it.");
432
+ if (!Object.keys(update).length)
433
+ return { n: 0, ok: false };
434
+ query = this.replaceIds(query);
435
+ update = this.replaceIds(update);
436
+ let opts = {
437
+ upsert: false,
438
+ returnNewDocument: true,
439
+ returnOriginal: false,
440
+ multi: true,
441
+ session: this.session,
442
+ };
443
+ update = await this._processUpdateObject(update);
444
+ db_1.log.debug('update called', collection, query, update);
445
+ let seqKeys = this._findSequenceKeys(update.$set);
446
+ let obj = await this.executeTransactionally(collection, async (conn, client) => {
447
+ update.$set = update.$set || {};
448
+ if (seqKeys)
449
+ await this._processSequenceField(client, collection, update.$set, seqKeys);
450
+ if (update.$set === undefined || Object.keys(update.$set).length === 0)
451
+ delete update.$set;
452
+ db_1.log.debug('update called', collection, query, update);
453
+ let res = await conn.updateMany(query, update, opts);
454
+ let resObj = {
455
+ n: res.result.n,
456
+ ok: !!res.result.ok,
457
+ };
458
+ await this._publishAndAudit('updateMany', this.db, collection, resObj);
459
+ return resObj;
460
+ }, !!seqKeys, { operation: "update", collection, query, update });
461
+ db_1.log.debug('update returns', obj);
462
+ return await obj;
463
+ }
464
+ async upsert(collection, query, update, options = { returnFullObject: false }) {
465
+ (0, assert_1.default)(collection);
466
+ (0, assert_1.default)(query);
467
+ (0, assert_1.default)(update);
468
+ (0, assert_1.default)(typeof update === 'object', 'update must be an object');
469
+ // if (!Object.keys(update).length) return null;
470
+ query = this.replaceIds(query);
471
+ update = this.replaceIds(update);
472
+ let opts = {
473
+ ...options,
474
+ upsert: true,
475
+ returnNewDocument: true,
476
+ returnOriginal: false,
477
+ session: this.session,
478
+ };
479
+ db_1.log.debug('upsert called', collection, query, update);
480
+ update = await this._processUpdateObject(update);
481
+ let seqKeys = this._findSequenceKeys(update.$set);
482
+ db_1.log.debug('upsert processed', collection, query, update);
483
+ if (Object.keys(query).length === 0)
484
+ query._id = Mongo.newid();
485
+ let ret = await this.executeTransactionally(collection, async (conn, client) => {
486
+ var _a;
487
+ update.$set = update.$set || {};
488
+ if (seqKeys)
489
+ await this._processSequenceField(client, collection, update.$set, seqKeys);
490
+ if (update.$set === undefined || Object.keys(update.$set).length === 0)
491
+ delete update.$set;
492
+ let ret = await conn.findOneAndUpdate(query, update, opts);
493
+ if ((_a = ret === null || ret === void 0 ? void 0 : ret.value) === null || _a === void 0 ? void 0 : _a._id) {
494
+ let oper = ret.lastErrorObject.updatedExisting ? "update" : "insert";
495
+ let retObj = oper === "insert" ? ret.value : this._removeUnchanged(ret.value, update, !!(options === null || options === void 0 ? void 0 : options.returnFullObject));
496
+ await this._publishAndAudit(oper, this.db, collection, retObj);
497
+ return retObj;
498
+ }
499
+ ;
500
+ return ret;
501
+ }, !!seqKeys, { operation: "upsert", query, update, options });
502
+ db_1.log.debug('upsert returns', ret);
503
+ return this._processReturnedObject(await ret);
504
+ }
505
+ async insert(collection, insert) {
506
+ (0, assert_1.default)(collection, "collection can't be null");
507
+ (0, assert_1.default)(insert, "insert can't be null");
508
+ (0, assert_1.default)(typeof insert === "object", "insert must be an object");
509
+ db_1.log.debug('insert called', collection, insert);
510
+ insert = this.replaceIds(insert);
511
+ if (this.revisions) {
512
+ insert._rev = 1;
513
+ insert._ts = _1.Base.timestamp();
514
+ }
515
+ await this._processHashedKeys(insert);
516
+ let seqKeys = this._findSequenceKeys(insert);
517
+ let ret = await this.executeTransactionally(collection, async (conn, client) => {
518
+ if (insert)
519
+ insert = await this._processSequenceField(client, collection, insert, seqKeys);
520
+ let obj = await conn.insertOne(insert, { session: this.session });
521
+ let fullObj = { _id: obj.insertedId, ...insert };
522
+ await this._publishAndAudit('insert', this.db, collection, fullObj);
523
+ return fullObj;
524
+ }, !!seqKeys, { operation: "insert", collection, insert });
525
+ db_1.log.debug('insert returns', ret);
526
+ return this._processReturnedObject(await ret);
527
+ }
528
+ async upsertBatch(collection, batch) {
529
+ (0, assert_1.default)(collection, "collection can't be null");
530
+ (0, assert_1.default)(batch, "batch can't be null");
531
+ (0, assert_1.default)(batch instanceof Array, "batch must be an Array");
532
+ db_1.log.debug('upsertBatch called', collection, batch);
533
+ batch = this.replaceIds(batch);
534
+ for (let i = 0; i < batch.length; i++)
535
+ await this._processHashedKeys(batch[i].update);
536
+ let ret = await this.executeTransactionally(collection, async (conn, client) => {
537
+ var _a;
538
+ await this._processSequenceFieldForMany(client, collection, batch.map(b => b.update));
539
+ let publications = [];
540
+ let changes = [];
541
+ for await (let part of batch) {
542
+ let { query, update, opts } = part;
543
+ let options = {
544
+ ...(opts || {}),
545
+ upsert: true,
546
+ returnNewDocument: true,
547
+ returnOriginal: false,
548
+ session: this.session,
549
+ };
550
+ update = await this._processUpdateObject(update);
551
+ let ret = await conn.findOneAndUpdate(query, update, options);
552
+ if ((_a = ret === null || ret === void 0 ? void 0 : ret.value) === null || _a === void 0 ? void 0 : _a._id) {
553
+ let oper = ret.lastErrorObject.updatedExisting ? "update" : "insert";
554
+ let retObj = oper === "insert" ? ret.value : this._removeUnchanged(ret.value, update, !!(opts === null || opts === void 0 ? void 0 : opts.returnFullObject));
555
+ publications.push(await this._publishAndAudit(oper, this.db, collection, retObj, true));
556
+ changes.push(retObj);
557
+ }
558
+ ;
559
+ }
560
+ if (this.emittingPublishEvents || this.auditing) {
561
+ await this.emit({
562
+ channel: `db/${this.db}/${collection}`,
563
+ payload: {
564
+ operation: "batch",
565
+ db: this.db,
566
+ collection,
567
+ data: publications.map(p => p === null || p === void 0 ? void 0 : p.payload).filter(p => !!p)
568
+ }
569
+ });
570
+ }
571
+ return changes;
572
+ }, false, { operation: "upsertBatch", collection, batch });
573
+ db_1.log.debug('upsertBatch returns', ret);
574
+ return ret;
575
+ }
576
+ async insertMany(collection, insert) {
577
+ (0, assert_1.default)(collection, "collection can't be null");
578
+ (0, assert_1.default)(insert, "insert can't be null");
579
+ (0, assert_1.default)(insert instanceof Array, "insert must be an Array");
580
+ db_1.log.debug('insertMany called', collection, insert);
581
+ insert = this.replaceIds(insert);
582
+ for (let i = 0; i < insert.length; i++)
583
+ await this._processHashedKeys(insert[i]);
584
+ if (this.revisions)
585
+ insert.forEach(ins => { ins._rev = 1; ins._ts = _1.Base.timestamp(); });
586
+ let ret = await this.executeTransactionally(collection, async (conn, client) => {
587
+ await this._processSequenceFieldForMany(client, collection, insert);
588
+ let obj = await conn.insertMany(insert, { session: this.session });
589
+ if (this.emittingPublishEvents || this.auditing) {
590
+ for await (let rec of obj.ops) {
591
+ await this._publishAndAudit('insert', this.db, collection, rec);
592
+ }
593
+ }
594
+ return obj.ops;
595
+ }, false, { operation: "insertMany", collection, insert });
596
+ db_1.log.debug('insertMany returns', ret);
597
+ return ret;
598
+ }
599
+ async deleteOne(collection, query) {
600
+ (0, assert_1.default)(collection);
601
+ (0, assert_1.default)(query);
602
+ query = this.replaceIds(query);
603
+ if (!this.softdelete) {
604
+ let opts = {
605
+ returnNewDocument: true,
606
+ returnOriginal: false,
607
+ session: this.session,
608
+ };
609
+ db_1.log.debug('deleteOne called', collection, query);
610
+ let ret = await this.executeTransactionally(collection, async (conn) => {
611
+ let obj = await conn.findOneAndDelete(query, opts);
612
+ await this._publishAndAudit('delete', this.db, collection, obj.value);
613
+ return obj.value;
614
+ }, false, { operation: "deleteOne", collection, query, softdelete: this.softdelete });
615
+ db_1.log.debug('deleteOne returns', ret);
616
+ return ret;
617
+ }
618
+ else {
619
+ let opts = {
620
+ upsert: true,
621
+ returnNewDocument: true,
622
+ returnOriginal: false,
623
+ session: this.session,
624
+ };
625
+ db_1.log.debug('deleteOne called', collection, query);
626
+ let ret = await this.executeTransactionally(collection, async (conn, client) => {
627
+ let del = {
628
+ $set: { _deleted: new Date() },
629
+ $inc: { _rev: 1, },
630
+ $currentDate: { _ts: { $type: 'timestamp' } }
631
+ };
632
+ if (this.syncSupport)
633
+ del.$set._csq = await this._getNextCollectionUpdateSeqNo(collection, client);
634
+ let obj = await conn.findOneAndUpdate(query, del, opts);
635
+ await this._publishAndAudit('delete', this.db, collection, obj.value);
636
+ return obj.value;
637
+ }, false, { operation: "deleteOne", collection, query, softdelete: this.softdelete });
638
+ db_1.log.debug('deleteOne returns', ret);
639
+ return ret;
640
+ }
641
+ }
642
+ async blockOne(collection, query) {
643
+ let opts = {
644
+ upsert: false,
645
+ returnNewDocument: true,
646
+ returnOriginal: false,
647
+ session: this.session,
648
+ };
649
+ query = this.replaceIds(query);
650
+ db_1.log.debug('blockOne called', collection, query);
651
+ let ret = await this.executeTransactionally(collection, async (conn, client) => {
652
+ query._blocked = { $exists: 0 };
653
+ let update = {
654
+ $set: { _blocked: new Date(), },
655
+ $inc: { _rev: 1, },
656
+ $currentDate: { _ts: { $type: 'timestamp' } }
657
+ };
658
+ if (this.syncSupport)
659
+ update.$set._csq = await this._getNextCollectionUpdateSeqNo(collection, client);
660
+ let obj = await conn.findOneAndUpdate(query, update, opts);
661
+ if (!obj.value || !obj.value._id)
662
+ return {
663
+ ok: false
664
+ };
665
+ let retObj = {
666
+ _id: obj.value._id,
667
+ _blocked: obj.value._blocked,
668
+ };
669
+ await this._publishAndAudit('block', this.db, collection, retObj);
670
+ return retObj;
671
+ }, false, { operation: "blockOne", collection, query });
672
+ db_1.log.debug('blockOne returns', ret);
673
+ return ret;
674
+ }
675
+ async unblockOne(collection, query) {
676
+ let opts = {
677
+ upsert: false,
678
+ returnNewDocument: true,
679
+ returnOriginal: false,
680
+ session: this.session,
681
+ };
682
+ query = this.replaceIds(query);
683
+ db_1.log.debug('unblockOne called', collection, query);
684
+ let ret = await this.executeTransactionally(collection, async (conn, client) => {
685
+ query._blocked = { $exists: 1 };
686
+ let update = {
687
+ $unset: { _blocked: 1 },
688
+ $inc: { _rev: 1, },
689
+ $currentDate: { _ts: { $type: 'timestamp' } }
690
+ };
691
+ if (this.syncSupport)
692
+ update.$set = { _csq: await this._getNextCollectionUpdateSeqNo(collection, client) };
693
+ let obj = await conn.findOneAndUpdate(query, update, opts);
694
+ if (!obj.value || !obj.value._id)
695
+ return {
696
+ ok: false
697
+ };
698
+ let retObj = {
699
+ _id: obj.value._id,
700
+ };
701
+ await this._publishAndAudit('unblock', this.db, collection, retObj);
702
+ return retObj;
703
+ }, false, { operation: "unblockOne", collection, query });
704
+ db_1.log.debug('unblockOne returns', ret);
705
+ return ret;
706
+ }
707
+ async hardDeleteOne(collection, query) {
708
+ (0, assert_1.default)(collection);
709
+ (0, assert_1.default)(query);
710
+ query = this.replaceIds(query);
711
+ let opts = {
712
+ returnNewDocument: true,
713
+ returnOriginal: false,
714
+ session: this.session,
715
+ };
716
+ db_1.log.debug('hardDeleteOne called', collection, query);
717
+ let ret = await this.executeTransactionally(collection, async (conn) => {
718
+ let obj = await conn.findOneAndDelete(query, opts);
719
+ if (obj.value) {
720
+ await this._publishAndAudit('delete', this.db, collection, obj.value);
721
+ }
722
+ return obj.value;
723
+ }, false, { operation: "hardDeleteOne", collection, query });
724
+ db_1.log.debug('hardDeleteOne returns', ret);
725
+ return ret;
726
+ }
727
+ async delete(collection, query) {
728
+ (0, assert_1.default)(collection);
729
+ (0, assert_1.default)(query);
730
+ if (this.syncSupport)
731
+ db_1.log.warn("delete does not increase _csq, avoit it.");
732
+ query = this.replaceIds(query);
733
+ if (!this.softdelete) {
734
+ let opts = {
735
+ returnNewDocument: true,
736
+ returnOriginal: false,
737
+ session: this.session,
738
+ };
739
+ db_1.log.debug('delete called', collection, query);
740
+ let ret = await this.executeTransactionally(collection, async (conn) => {
741
+ let obj = await conn.deleteMany(query, opts);
742
+ let resObj = {
743
+ n: obj.result.n,
744
+ ok: !!obj.result.ok,
745
+ };
746
+ await this._publishAndAudit('deleteMany', this.db, collection, resObj);
747
+ return resObj;
748
+ }, false, { operation: "delete", collection, query, softdelete: this.softdelete });
749
+ db_1.log.debug('delete returns', ret);
750
+ return ret;
751
+ }
752
+ else {
753
+ let opts = {
754
+ upsert: false,
755
+ returnNewDocument: true,
756
+ returnOriginal: false,
757
+ multi: true,
758
+ session: this.session,
759
+ };
760
+ let date = new Date();
761
+ db_1.log.debug('delete called', collection, query);
762
+ let ret = await this.executeTransactionally(collection, async (conn) => {
763
+ let obj = await conn.updateMany(query, { $set: { _deleted: date } }, opts);
764
+ let resObj = {
765
+ n: obj.result.n,
766
+ ok: obj.result.ok,
767
+ };
768
+ await this._publishAndAudit('deleteMany', this.db, collection, resObj);
769
+ return resObj;
770
+ }, false, { operation: "delete", collection, query, softdelete: this.softdelete });
771
+ db_1.log.debug('delete returns', ret);
772
+ return ret;
773
+ }
774
+ }
775
+ async hardDelete(collection, query) {
776
+ (0, assert_1.default)(collection);
777
+ (0, assert_1.default)(query);
778
+ if (this.syncSupport)
779
+ db_1.log.warn("hardDelete does not increase _csq, avoit it.");
780
+ query = this.replaceIds(query);
781
+ let opts = {
782
+ returnNewDocument: true,
783
+ returnOriginal: false,
784
+ session: this.session,
785
+ };
786
+ db_1.log.debug('hardDelete called', collection, query);
787
+ let ret = await this.executeTransactionally(collection, async (conn) => {
788
+ let obj = await conn.deleteMany(query, opts);
789
+ let resObj = {
790
+ n: obj.result.n,
791
+ ok: obj.result.ok,
792
+ };
793
+ await this._publishAndAudit('deleteMany', this.db, collection, resObj);
794
+ return resObj;
795
+ }, false, { operation: "hardDelete", collection, query, softdelete: this.softdelete });
796
+ db_1.log.debug('hardDelete returns', ret);
797
+ return ret;
798
+ }
799
+ async testHash(collection, query, field, unhashedValue) {
800
+ let _field;
801
+ db_1.log.debug('teshHash called', collection, query, field, unhashedValue);
802
+ if (typeof field === "object") {
803
+ if (Object.keys(field).length === 1)
804
+ [_field, unhashedValue] = Object.entries(field)[0];
805
+ else
806
+ throw new Error("if field is an object, it must have exactly one key");
807
+ }
808
+ else
809
+ _field = field;
810
+ if (!/^__hashed__/.test(_field))
811
+ _field = "__hashed__" + _field;
812
+ let conn = await this.connect();
813
+ let obj = await conn.db(this.db).collection(collection).findOne(query, { projection: { [_field]: 1 }, session: this.session });
814
+ if (!obj || !obj[_field]) {
815
+ db_1.log.debug('teshHash returns false', obj);
816
+ return false;
817
+ }
818
+ let res = await bcrypt_1.default.compare(unhashedValue, obj[_field].hash);
819
+ db_1.log.debug('teshHash returns', res);
820
+ return res;
821
+ }
822
+ async aggregate(collection, pipeline, opts = {
823
+ readPreference: mongodb_1.ReadPreference.SECONDARY_PREFERRED,
824
+ }) {
825
+ (0, assert_1.default)(collection);
826
+ (0, assert_1.default)(pipeline instanceof Array);
827
+ db_1.log.debug('aggregate called', collection, pipeline);
828
+ pipeline = this.replaceIds(pipeline);
829
+ if (this.session)
830
+ opts.session = this.session;
831
+ let ret = await this.executeTransactionally(collection, async (conn) => {
832
+ let agg = await conn.aggregate(pipeline, opts);
833
+ let res = await agg.toArray();
834
+ return res;
835
+ }, false, { operation: "aggregate", collection, pipeline, opts });
836
+ db_1.log.debug('aggregare returns', ret);
837
+ return ret;
838
+ }
839
+ async isUnique(collection, field, value, id) {
840
+ (0, assert_1.default)(collection);
841
+ (0, assert_1.default)(field);
842
+ (0, assert_1.default)(value);
843
+ db_1.log.debug('isUnuqie called', collection, field, value, id);
844
+ let _id = id === null || id === void 0 ? void 0 : id.toString();
845
+ let matches = await this.executeTransactionally(collection, async (conn) => {
846
+ let agg = await conn.find({ [field]: value });
847
+ let res = await agg.toArray();
848
+ return res;
849
+ }, false, { operation: "isUnique", collection, field, value, id });
850
+ let ret = true;
851
+ for (let match of matches || []) {
852
+ if (match._id.toString() != _id) {
853
+ ret = false;
854
+ return false;
855
+ }
856
+ }
857
+ db_1.log.debug('isUnuqie returns', ret);
858
+ return ret;
859
+ }
860
+ async collectFieldValues(collection, field, inArray = false, opts) {
861
+ (0, assert_1.default)(collection);
862
+ (0, assert_1.default)(field);
863
+ db_1.log.debug('collectFieldValues called', collection, field);
864
+ let pipeline = [
865
+ { $group: { _id: '$' + field } },
866
+ { $sort: { _id: 1 } }
867
+ ];
868
+ if (inArray)
869
+ pipeline = [
870
+ { $unwind: '$' + field },
871
+ ...pipeline
872
+ ];
873
+ let res = await this.executeTransactionally(collection, async (conn) => {
874
+ let agg = await conn.aggregate(pipeline, opts);
875
+ let res = await agg.toArray();
876
+ return res;
877
+ }, false, { operation: "collectFieldValues", collection, field, inArray, pipeline, opts });
878
+ let ret = res === null || res === void 0 ? void 0 : res.map((v) => v._id);
879
+ db_1.log.debug('collectFieldValues returns', ret);
880
+ return ret;
881
+ }
882
+ async dropCollection(collection) {
883
+ (0, assert_1.default)(collection);
884
+ db_1.log.debug('dropCollection called', this.auditCollections);
885
+ let client = await this.connect();
886
+ let existing = await client.db(this.db).collections();
887
+ if (existing.map((c) => c.s.name).includes(collection)) {
888
+ await client.db(this.db).dropCollection(collection);
889
+ }
890
+ db_1.log.debug('dropCollection returns');
891
+ }
892
+ async resetCollectionSync(collection) {
893
+ (0, assert_1.default)(collection);
894
+ db_1.log.debug('resetCollectionSync called for', collection);
895
+ let client = await this.connect();
896
+ await client.db(this.db)
897
+ .collection(types_1.SEQUENCES_COLLECTION)
898
+ .findOneAndDelete({ collection });
899
+ db_1.log.debug(`resetCollectionSync for ${collection} returns`);
900
+ }
901
+ async dropCollections(collections) {
902
+ (0, assert_1.default)(collections);
903
+ (0, assert_1.default)(collections instanceof Array);
904
+ db_1.log.debug('dropCollections called', this.auditCollections);
905
+ let client = await this.connect();
906
+ let existing = await client.db(this.db).collections();
907
+ for await (let collection of collections) {
908
+ if (existing.map((c) => c.s.name).includes(collection)) {
909
+ await client.db(this.db).dropCollection(collection);
910
+ }
911
+ }
912
+ db_1.log.debug('dropCollections returns');
913
+ }
914
+ async createCollections(collections) {
915
+ (0, assert_1.default)(collections);
916
+ (0, assert_1.default)(collections instanceof Array);
917
+ db_1.log.debug('createCollections called', this.auditCollections);
918
+ let client = await this.connect();
919
+ let existing = await this.getCollections();
920
+ for await (let collection of collections) {
921
+ if (!existing.includes(collection)) {
922
+ await client.db(this.db).createCollection(collection);
923
+ }
924
+ }
925
+ db_1.log.debug('createCollections returns');
926
+ }
927
+ async createCollection(collection) {
928
+ (0, assert_1.default)(collection);
929
+ db_1.log.debug('createCollection called', collection);
930
+ let client = await this.connect();
931
+ let existing = await this.getCollections();
932
+ if (!existing.includes(collection)) {
933
+ await client.db(this.db).createCollection(collection);
934
+ }
935
+ db_1.log.debug('createCollection returns');
936
+ }
937
+ async dbLogPurge(collection, _id) {
938
+ (0, assert_1.default)(collection);
939
+ db_1.log.debug('dblogPurge called', collection, _id);
940
+ let ret = await this.executeTransactionally(collection, async () => {
941
+ let cond = { db: this.db, collection, };
942
+ if (_id !== undefined)
943
+ cond._id = Mongo._toId(_id);
944
+ let client = await this.connect();
945
+ let ret = await client
946
+ .db(this.db)
947
+ .collection(this.auditCollectionName)
948
+ .deleteMany(cond, { session: this.session });
949
+ return {
950
+ ok: true,
951
+ n: ret.result.n
952
+ };
953
+ }, false, { operation: "dbLogPurge", collection, _id });
954
+ db_1.log.debug('dblogPurge returns', ret);
955
+ return ret;
956
+ }
957
+ async dbLogGet(collection, _id) {
958
+ (0, assert_1.default)(collection);
959
+ db_1.log.debug('dblogGet called', collection, _id);
960
+ let ret = await this.executeTransactionally(collection, async () => {
961
+ let cond = { db: this.db, collection };
962
+ if (_id)
963
+ cond.entityid = Mongo._toId(_id);
964
+ let client = await this.connect();
965
+ let ret = await client
966
+ .db(this.db)
967
+ .collection(this.auditCollectionName)
968
+ .find(cond, { session: this.session })
969
+ .sort({ _id: -1 })
970
+ .toArray();
971
+ return ret;
972
+ }, false, { operation: "dbLogGet", collection, _id });
973
+ db_1.log.debug('dblogGet returns', ret);
974
+ return ret;
975
+ }
976
+ // HELPER FUNCTIONS
977
+ replaceIds(data) {
978
+ try {
979
+ if (!data)
980
+ return data;
981
+ if (typeof data === "number")
982
+ return data;
983
+ if (typeof data === "boolean")
984
+ return data;
985
+ if (typeof data === "bigint")
986
+ return data;
987
+ if (typeof data === "function")
988
+ return undefined;
989
+ if (typeof data === "symbol")
990
+ return data.toString();
991
+ if (data instanceof mongodb_1.ObjectId)
992
+ return data;
993
+ if (data instanceof mongodb_1.Timestamp)
994
+ return data;
995
+ if (data instanceof Date)
996
+ return data;
997
+ if (data instanceof RegExp)
998
+ return data;
999
+ if (data instanceof Number)
1000
+ return data;
1001
+ if (data instanceof String)
1002
+ return data;
1003
+ if (typeof data === "string" && data.match(/^[0-9a-f]{24,24}$/g))
1004
+ return new mongodb_1.ObjectId(data);
1005
+ if (typeof data === "string")
1006
+ return data;
1007
+ if (data instanceof Array) {
1008
+ return data.map(d => this.replaceIds(d));
1009
+ }
1010
+ else if (typeof data == 'object') {
1011
+ for (let key in data) {
1012
+ data[key] = this.replaceIds(data[key]);
1013
+ }
1014
+ return data;
1015
+ }
1016
+ throw new Error(`ObjectId test failed for ${data}`);
1017
+ }
1018
+ catch (err) {
1019
+ console.error('error in replaceIds', err, data);
1020
+ throw err;
1021
+ }
1022
+ }
1023
+ async connect() {
1024
+ super.connect();
1025
+ // this.session = undefined;
1026
+ return this.client;
1027
+ }
1028
+ async close() {
1029
+ if (this.session)
1030
+ try {
1031
+ await this.session.endSession();
1032
+ db_1.log.info("session ended");
1033
+ }
1034
+ catch (err) {
1035
+ db_1.log.error(`Error ending session ${err.message}`);
1036
+ }
1037
+ try {
1038
+ await super.close();
1039
+ db_1.log.info("connection closed");
1040
+ }
1041
+ catch ( /** intentionally */_a) { /** intentionally */ }
1042
+ this.session = undefined;
1043
+ }
1044
+ async inTransaction() {
1045
+ if (!await this.isOnReplicaSet())
1046
+ return false;
1047
+ if (!this.session)
1048
+ return false;
1049
+ return this.session.inTransaction();
1050
+ }
1051
+ async withTransaction(funct) {
1052
+ if (!await this.isOnReplicaSet())
1053
+ return await funct(await this.connect(), null);
1054
+ try {
1055
+ let client = await this.connect();
1056
+ let hadSession = !!this.session;
1057
+ if (!this.session) {
1058
+ this.session = client.startSession();
1059
+ db_1.log.info("session started");
1060
+ }
1061
+ let session = this.session;
1062
+ await session.withTransaction(async () => await funct(client, session));
1063
+ if (!hadSession) {
1064
+ session.endSession();
1065
+ db_1.log.info("session ended");
1066
+ this.session = undefined;
1067
+ }
1068
+ return;
1069
+ }
1070
+ catch (err) {
1071
+ throw err;
1072
+ }
1073
+ }
1074
+ async startTransaction() {
1075
+ if (!await this.isOnReplicaSet())
1076
+ return;
1077
+ let client = await this.connect();
1078
+ try {
1079
+ if (!this.session) {
1080
+ this.session = client.startSession(TRANSACTION_OPTIONS);
1081
+ db_1.log.info("session started");
1082
+ }
1083
+ if (!await this.inTransaction()) {
1084
+ await this.session.startTransaction();
1085
+ db_1.log.info("transaction started");
1086
+ }
1087
+ }
1088
+ catch (err) {
1089
+ db_1.log.error('startTransaction error', err);
1090
+ try {
1091
+ if (this.session) {
1092
+ await this.session.endSession();
1093
+ db_1.log.info("session ended");
1094
+ }
1095
+ this.session = undefined;
1096
+ }
1097
+ catch (e) {
1098
+ db_1.log.error("startTransaction - error in endSession", e.message || e);
1099
+ }
1100
+ return;
1101
+ }
1102
+ }
1103
+ async commitTransaction() {
1104
+ if (!await this.isOnReplicaSet())
1105
+ return;
1106
+ try {
1107
+ if (!await this.inTransaction())
1108
+ return;
1109
+ let session = this.session;
1110
+ await session.commitTransaction();
1111
+ db_1.log.info("transaction committed");
1112
+ session.endSession();
1113
+ this.session = undefined;
1114
+ db_1.log.info("session ended");
1115
+ }
1116
+ catch (err) {
1117
+ db_1.log.error(`commitTransaction error ${err.message || err}`);
1118
+ }
1119
+ }
1120
+ async abortTransaction() {
1121
+ if (!await this.isOnReplicaSet())
1122
+ return;
1123
+ try {
1124
+ if (!await this.inTransaction())
1125
+ return;
1126
+ let session = this.session;
1127
+ await session.abortTransaction();
1128
+ db_1.log.info("transaction aborted");
1129
+ await session.endSession();
1130
+ this.session = undefined;
1131
+ db_1.log.info("session ended");
1132
+ }
1133
+ catch (err) {
1134
+ db_1.log.error(`abortTransaction error ${err.message || err}`);
1135
+ }
1136
+ }
1137
+ async _try_once(useTransaction, f, collection) {
1138
+ try {
1139
+ let conn = await this.connect();
1140
+ if (useTransaction)
1141
+ await this.startTransaction();
1142
+ let ret = await f(await conn.db(this.db).collection(collection), conn);
1143
+ if (useTransaction)
1144
+ await this.commitTransaction();
1145
+ return ret;
1146
+ }
1147
+ catch (err) {
1148
+ try {
1149
+ if (useTransaction)
1150
+ await this.abortTransaction();
1151
+ }
1152
+ catch ( /* intentionally */_a) { /* intentionally */ }
1153
+ throw err;
1154
+ }
1155
+ }
1156
+ async executeTransactionally(collection, f, useTransaction = false, debugObject) {
1157
+ try {
1158
+ // NOTE - add this.syncSuppoer to use transactions for _csq increases
1159
+ // Adds A LOT of performance penalty - soft.js takes 20x longer!!! (5.2s vs 250ms)
1160
+ let useTransaction = /* this.syncSupport || */ this._shouldAuditCollection(this.db, collection);
1161
+ if (useTransaction && !await this.inTransaction())
1162
+ useTransaction = true;
1163
+ return await this._try_once(useTransaction, f, collection);
1164
+ }
1165
+ catch (err) {
1166
+ db_1.log.error(`Mongo command has failed for ${this.db}.${collection} - ${(this.session ? "ROLLBACK - " : "")} ${err.message || err}`);
1167
+ db_1.log.error(debugObject);
1168
+ let x = (err || "").toString();
1169
+ let isRepeatable = x.match(/Topology is closed, please connect/i)
1170
+ || x.match(/connection timed out/)
1171
+ || x.match(/not master/)
1172
+ || x.match(/Topology closed/);
1173
+ if (isRepeatable) {
1174
+ try {
1175
+ db_1.log.error("Trying to reopen connection and repeat commands");
1176
+ await this.close();
1177
+ // a single retry
1178
+ await super.connect();
1179
+ let ret = await this._try_once(useTransaction, f, collection);
1180
+ db_1.log.error("OK - Retry succeeded.");
1181
+ db_1.log.error("");
1182
+ return ret;
1183
+ }
1184
+ catch (err2) {
1185
+ /* intentional */
1186
+ if (debugObject)
1187
+ db_1.log.error(debugObject);
1188
+ db_1.log.error(`FAIL - Retry failed: ${err2.message || err2}`);
1189
+ db_1.log.error("");
1190
+ }
1191
+ }
1192
+ throw err;
1193
+ }
1194
+ }
1195
+ async _findLastSequenceForKey(connection, key) {
1196
+ let maxfld = await (connection
1197
+ .find({}, { session: this.session })
1198
+ .sort({ [key]: -1 })
1199
+ .limit(1)
1200
+ .toArray());
1201
+ if (maxfld.length === 0)
1202
+ return undefined;
1203
+ return parseInt(maxfld[0][key]) || 0;
1204
+ }
1205
+ async _getNextCollectionUpdateSeqNo(collection, conn) {
1206
+ var _a;
1207
+ let opts = {
1208
+ upsert: true,
1209
+ returnNewDocument: true,
1210
+ returnOriginal: false
1211
+ };
1212
+ let nextSeq = await (conn.db(this.db)
1213
+ .collection(types_1.SEQUENCES_COLLECTION)
1214
+ .findOneAndUpdate({ collection }, {
1215
+ $inc: { seq: 1 },
1216
+ $currentDate: { last: { $type: "date" }, ts: { $type: "timestamp" } }
1217
+ }, opts));
1218
+ conn.db(this.db).collection(collection);
1219
+ return ((_a = nextSeq === null || nextSeq === void 0 ? void 0 : nextSeq.value) === null || _a === void 0 ? void 0 : _a.seq) || 1;
1220
+ }
1221
+ _findSequenceKeys(object) {
1222
+ if (!object)
1223
+ return;
1224
+ let seqKeys = Object.keys(object).filter(key => object[key] === 'SEQ_NEXT' || object[key] === 'SEQ_LAST');
1225
+ return ((seqKeys === null || seqKeys === void 0 ? void 0 : seqKeys.length) > 0 || this.syncSupport) ? { seqKeys } : undefined;
1226
+ }
1227
+ async _processSequenceField(client, collection, insert, seqKeys) {
1228
+ (0, assert_1.default)(this.client);
1229
+ if (this.syncSupport) {
1230
+ insert._csq = (await this._getNextCollectionUpdateSeqNo(collection, client));
1231
+ }
1232
+ for await (let seqKey of (seqKeys === null || seqKeys === void 0 ? void 0 : seqKeys.seqKeys) || []) {
1233
+ let last = await this._findLastSequenceForKey(client.db(this.db).collection(collection), seqKey);
1234
+ if (last === undefined) {
1235
+ await this.createCollection(collection);
1236
+ last = 0;
1237
+ }
1238
+ let next = insert[seqKey] === 'SEQ_LAST' ? last : last + 1;
1239
+ insert[seqKey] = next;
1240
+ }
1241
+ return insert;
1242
+ }
1243
+ async _processSequenceFieldForMany(connection, collection, inserts) {
1244
+ (0, assert_1.default)(this.client);
1245
+ (0, assert_1.default)(connection);
1246
+ if (!(inserts === null || inserts === void 0 ? void 0 : inserts.length))
1247
+ return;
1248
+ let seqKeys = this._findSequenceKeys(inserts[0]);
1249
+ let seq = 0;
1250
+ if (this.syncSupport)
1251
+ seq = await this._getNextCollectionUpdateSeqNo(collection, connection);
1252
+ for await (let seqKey of (seqKeys === null || seqKeys === void 0 ? void 0 : seqKeys.seqKeys) || []) {
1253
+ let last = await this._findLastSequenceForKey(connection.db(this.db).collection(collection), seqKey);
1254
+ if (last === undefined) {
1255
+ try {
1256
+ await this.createCollection(collection);
1257
+ }
1258
+ catch ( /* intentionaly */_a) { /* intentionaly */ }
1259
+ last = 0;
1260
+ }
1261
+ for (let insert of inserts) {
1262
+ let next = insert[seqKey] === 'SEQ_LAST' ? last : last + 1;
1263
+ last = insert[seqKey] = next;
1264
+ if (this.syncSupport)
1265
+ insert._csq = (seq++);
1266
+ }
1267
+ }
1268
+ return inserts;
1269
+ }
1270
+ _getFieldsRecursively(obj, into = []) {
1271
+ for (let key in obj) {
1272
+ let startsWith$ = /^\$/.test(key);
1273
+ if (startsWith$ && typeof obj[key] === "object" && !Array.isArray(obj[key]))
1274
+ this._getFieldsRecursively(obj[key], into);
1275
+ else if (!startsWith$ && typeof obj[key] === "object" && !Array.isArray(obj[key]))
1276
+ into.push(key);
1277
+ else if (!startsWith$)
1278
+ into.push(key);
1279
+ }
1280
+ return into;
1281
+ }
1282
+ _removeUnchanged(wholerecord, diff, returnFullObject) {
1283
+ if (returnFullObject)
1284
+ return wholerecord;
1285
+ let a = {};
1286
+ wholerecord = wholerecord || {};
1287
+ this._getFieldsRecursively(diff).forEach(k => a[k] = wholerecord[k]);
1288
+ if (wholerecord._rev)
1289
+ a._rev = wholerecord._rev;
1290
+ if (wholerecord._ts)
1291
+ a._ts = wholerecord._ts;
1292
+ if (wholerecord._id)
1293
+ a._id = wholerecord._id;
1294
+ return a;
1295
+ }
1296
+ _shouldAuditCollection(db, col, audited = this.auditedCollections) {
1297
+ if (!this.auditing)
1298
+ return false;
1299
+ for (let m of audited) {
1300
+ let r = (db ? db + "." : "") + (col || "");
1301
+ if (new RegExp(m, "i").test(r))
1302
+ return true;
1303
+ }
1304
+ return false;
1305
+ }
1306
+ async _publishAndAudit(operation, db, collection, data, noEmit) {
1307
+ let toPublish = undefined;
1308
+ if (this.emittingPublishEvents && data) {
1309
+ toPublish = this._makePublication(db, collection, data, operation);
1310
+ if (!noEmit)
1311
+ this.emit(toPublish);
1312
+ }
1313
+ if (this._shouldAuditCollection(this.db, collection)) {
1314
+ if (['insert', 'update', 'delete', 'block', 'unblock'].includes(operation))
1315
+ await this._writeAuditRecord(collection, operation, data);
1316
+ }
1317
+ return toPublish;
1318
+ }
1319
+ _makePublication(db, collection, data, operation) {
1320
+ let toPublish = {
1321
+ channel: `db/${db}/${collection}${data._id ? "/" + data._id.toString() : ""}`,
1322
+ payload: {
1323
+ db,
1324
+ collection,
1325
+ operation,
1326
+ data,
1327
+ }
1328
+ };
1329
+ if (data && data._auth)
1330
+ toPublish.user = data._auth.username;
1331
+ return toPublish;
1332
+ }
1333
+ emit(what) {
1334
+ db_1.log.debug("emitting publish", what);
1335
+ this.emitter.emit('publish', what);
1336
+ }
1337
+ async _writeAuditRecord(collection, operation, data, user = this.user, audit = this.audit) {
1338
+ if (!this.auditing)
1339
+ return;
1340
+ let client = await this.connect();
1341
+ let previousAuditRecords = await (await client.db(this.db).collection(this.auditCollectionName).aggregate([
1342
+ { $match: { entityid: Mongo._toId(data._id) } },
1343
+ { $sort: { rev: -1 } },
1344
+ { $limit: 1 }
1345
+ ], { session: this.session })).toArray();
1346
+ let previousAuditRecord = previousAuditRecords.length ? previousAuditRecords[0] : { rev: 0, changes: {} };
1347
+ if (previousAuditRecords.length === 0)
1348
+ await this.createCollection(this.auditCollectionName);
1349
+ let dataNoId = { ...data };
1350
+ delete dataNoId._id;
1351
+ let auditRecord = {
1352
+ db: this.db,
1353
+ collection: collection,
1354
+ entityid: _1.Base.objectid(data._id),
1355
+ rev: previousAuditRecord.rev + 1,
1356
+ ts: _1.Base.timestamp(),
1357
+ on: new Date(),
1358
+ operation: operation,
1359
+ changes: dataNoId,
1360
+ };
1361
+ if (user)
1362
+ auditRecord.user = user;
1363
+ if (audit)
1364
+ auditRecord.audit = audit;
1365
+ db_1.log.trace('AUDITING', auditRecord);
1366
+ let ret = await client.db(this.db)
1367
+ .collection(this.auditCollectionName)
1368
+ .insertOne(auditRecord, { session: this.session });
1369
+ db_1.log.debug('AUDITED', auditRecord, ret.result.n);
1370
+ }
1371
+ async _processUpdateObject(update) {
1372
+ await this._processHashedKeys(update);
1373
+ for (let k in update) {
1374
+ let key = k;
1375
+ if (key.toString()[0] === "$")
1376
+ continue;
1377
+ if (key === '' || key === null || key === undefined) {
1378
+ delete update[key];
1379
+ continue;
1380
+ }
1381
+ if (update[key] === undefined) {
1382
+ update.$unset = update.$unset || {};
1383
+ update.$unset[key] = true;
1384
+ delete update[key];
1385
+ }
1386
+ else {
1387
+ update.$set = update.$set || {};
1388
+ update.$set[key] = update[key];
1389
+ delete update[key];
1390
+ }
1391
+ }
1392
+ if (this.revisions) {
1393
+ update.$inc = update.$inc || {};
1394
+ update.$inc._rev = 1;
1395
+ update.$currentDate = { _ts: { $type: 'timestamp' } };
1396
+ }
1397
+ if (update.$set) {
1398
+ delete update.$set._ts;
1399
+ delete update.$set._rev;
1400
+ delete update.$set._id;
1401
+ }
1402
+ return update;
1403
+ }
1404
+ async _processHashedKeys(update) {
1405
+ for await (let key of Object.keys(update)) {
1406
+ let shouldBeHashed = /^__hashed_(.+)$/.test(key);
1407
+ if (shouldBeHashed) {
1408
+ let salt = await bcrypt_1.default.genSalt(saltRounds);
1409
+ let hash = await bcrypt_1.default.hash(update[key], salt);
1410
+ update[key] = { salt, hash };
1411
+ }
1412
+ }
1413
+ }
1414
+ _processReturnedObject(ret) {
1415
+ if (ret instanceof Array) {
1416
+ for (let i = 0; i < ret.length; i++) {
1417
+ this._processReturnedObject(ret[i]);
1418
+ }
1419
+ }
1420
+ else {
1421
+ for (let key in ret) {
1422
+ if (/^__/.test(key))
1423
+ delete ret[key];
1424
+ }
1425
+ }
1426
+ return ret;
1427
+ }
1428
+ }
1429
+ exports.default = Mongo;
1430
+ module.exports = Mongo;
1431
+ //# sourceMappingURL=mongo.js.map