@sprucelabs/data-stores 28.1.2 → 28.1.3

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.
@@ -26,7 +26,7 @@ export default class MongoDatabase implements Database {
26
26
  dropCollection(name: string): Promise<void>;
27
27
  dropDatabase(): Promise<void>;
28
28
  private listIndexes;
29
- dropIndex(collection: string, fields: string[]): Promise<void>;
29
+ dropIndex(collection: string, index: UniqueIndex): Promise<void>;
30
30
  getUniqueIndexes(collection: string): Promise<string[][]>;
31
31
  getIndexes(collection: string, shouldIncludeUnique?: boolean): Promise<any[]>;
32
32
  createIndex(collection: string, fields: string[]): Promise<void>;
@@ -235,21 +235,21 @@ class MongoDatabase {
235
235
  return [];
236
236
  }
237
237
  }
238
- async dropIndex(collection, fields) {
238
+ async dropIndex(collection, index) {
239
239
  const indexes = await this.listIndexes(collection);
240
240
  let found = false;
241
- for (const index of indexes) {
242
- if ((0, isEqual_1.default)(Object.keys(index.key), fields)) {
241
+ for (const thisIndex of indexes) {
242
+ if ((0, isEqual_1.default)(Object.keys(thisIndex.key), index)) {
243
243
  await this.assertDbWhileAttempingTo('drop a index.', collection)
244
244
  .collection(collection)
245
- .dropIndex(index.name);
245
+ .dropIndex(thisIndex.name);
246
246
  found = true;
247
247
  }
248
248
  }
249
249
  if (!found) {
250
250
  throw new SpruceError_1.default({
251
251
  code: 'INDEX_NOT_FOUND',
252
- missingIndex: fields,
252
+ missingIndex: (0, normalizeIndex_1.default)(index).fields,
253
253
  collectionName: collection,
254
254
  });
255
255
  }
@@ -330,11 +330,19 @@ class MongoDatabase {
330
330
  return false;
331
331
  }
332
332
  async syncIndexes(collectionName, indexes) {
333
+ var _a;
333
334
  const currentIndexes = await this.getIndexes(collectionName);
334
335
  const extraIndexes = (0, differenceWith_1.default)(currentIndexes, indexes, isEqual_1.default).filter((i) => !(i.length === 1 && i[0] === '_id'));
335
336
  for (const index of indexes) {
336
337
  if (!this.doesIndexExist(currentIndexes, index)) {
337
- await this.createIndex(collectionName, index);
338
+ try {
339
+ await this.createIndex(collectionName, index);
340
+ }
341
+ catch (err) {
342
+ if (((_a = err.options) === null || _a === void 0 ? void 0 : _a.code) !== 'INDEX_EXISTS') {
343
+ throw err;
344
+ }
345
+ }
338
346
  }
339
347
  }
340
348
  for (const extra of extraIndexes) {
@@ -377,7 +385,12 @@ class MongoDatabase {
377
385
  async syncUniqueIndexes(collectionName, indexes) {
378
386
  var _a;
379
387
  const currentIndexes = await this.getUniqueIndexes(collectionName);
380
- const extraIndexes = (0, differenceWith_1.default)(currentIndexes, indexes, isEqual_1.default);
388
+ const toDelete = [];
389
+ for (const index of currentIndexes) {
390
+ if (!this.doesIndexExist(indexes, index)) {
391
+ toDelete.push(index);
392
+ }
393
+ }
381
394
  for (const index of indexes) {
382
395
  const { fields } = this.normalizeIndex(index);
383
396
  if (!this.doesIndexExist(currentIndexes, fields)) {
@@ -391,7 +404,7 @@ class MongoDatabase {
391
404
  }
392
405
  }
393
406
  }
394
- for (const extra of extraIndexes) {
407
+ for (const extra of toDelete) {
395
408
  await this.dropIndex(collectionName, extra);
396
409
  }
397
410
  }
@@ -26,12 +26,9 @@ const SpruceError_1 = __importDefault(require("../errors/SpruceError"));
26
26
  const AbstractMutexer_1 = __importDefault(require("../mutexers/AbstractMutexer"));
27
27
  const generateId_1 = __importDefault(require("../utilities/generateId"));
28
28
  const mongo_utility_1 = __importDefault(require("../utilities/mongo.utility"));
29
+ const mapIndexFilterToNeDbQuery_1 = __importDefault(require("./mapIndexFilterToNeDbQuery"));
29
30
  const normalizeIndex_1 = __importDefault(require("./normalizeIndex"));
30
31
  dotenv_1.default.config();
31
- const NULL_PLACEHOLDER = '_____NULL_____';
32
- const UNDEFINED_PLACEHOLDER = '_____UNDEFINED_____';
33
- const SHOULD_SIMULATE_SLOW_QUERIES = process.env.SHOULD_SIMULATE_SLOW_QUERIES === 'true';
34
- const SLOW_QUERY_MAX_RANDOM_DELAY_MS = parseInt(`${(_a = process.env.SLOW_QUERY_MAX_RANDOM_DELAY_MS) !== null && _a !== void 0 ? _a : 100}`, 10);
35
32
  class NeDbDatabase extends AbstractMutexer_1.default {
36
33
  constructor() {
37
34
  super(...arguments);
@@ -295,17 +292,20 @@ class NeDbDatabase extends AbstractMutexer_1.default {
295
292
  if (col._uniqueIndexes) {
296
293
  for (const index of col._uniqueIndexes) {
297
294
  const { fields, filter } = (0, normalizeIndex_1.default)(index);
298
- if (filter) {
299
- return;
300
- }
301
295
  const existing = query
302
296
  ? await this.findOne(collection, query)
303
297
  : null;
304
- const q = {};
298
+ let q = filter
299
+ ? (0, mapIndexFilterToNeDbQuery_1.default)(filter)
300
+ : {};
305
301
  const duplicateFields = [];
306
302
  const duplicateValues = [];
307
303
  fields.forEach((f) => {
308
- q[f] = (0, get_1.default)(values, f);
304
+ let value = (0, get_1.default)(values, f);
305
+ if (value === NULL_PLACEHOLDER && q[f]) {
306
+ value = q[f];
307
+ }
308
+ q[f] = value;
309
309
  duplicateFields.push(f);
310
310
  duplicateValues.push(q[f]);
311
311
  });
@@ -387,9 +387,9 @@ class NeDbDatabase extends AbstractMutexer_1.default {
387
387
  });
388
388
  }
389
389
  }
390
- doesIndexExist(currentIndexes, fields) {
391
- for (const index of currentIndexes !== null && currentIndexes !== void 0 ? currentIndexes : []) {
392
- if ((0, isEqual_1.default)(index, fields)) {
390
+ doesIndexExist(currentIndexes, index) {
391
+ for (const existing of currentIndexes !== null && currentIndexes !== void 0 ? currentIndexes : []) {
392
+ if ((0, isEqual_1.default)(existing, index)) {
393
393
  return true;
394
394
  }
395
395
  }
@@ -440,10 +440,14 @@ class NeDbDatabase extends AbstractMutexer_1.default {
440
440
  async syncUniqueIndexes(collectionName, indexes) {
441
441
  var _a;
442
442
  const currentIndexes = await this.getUniqueIndexes(collectionName);
443
- const extraIndexes = (0, differenceWith_1.default)(currentIndexes, indexes, isEqual_1.default);
443
+ const toDelete = [];
444
+ for (const index of currentIndexes) {
445
+ if (!this.doesIndexExist(indexes, index)) {
446
+ toDelete.push(index);
447
+ }
448
+ }
444
449
  for (const index of indexes) {
445
- const { fields } = (0, normalizeIndex_1.default)(index);
446
- if (!this.doesIndexExist(currentIndexes, fields)) {
450
+ if (!this.doesIndexExist(currentIndexes, index)) {
447
451
  try {
448
452
  await this.createUniqueIndex(collectionName, index);
449
453
  }
@@ -454,7 +458,7 @@ class NeDbDatabase extends AbstractMutexer_1.default {
454
458
  }
455
459
  }
456
460
  }
457
- for (const extra of extraIndexes) {
461
+ for (const extra of toDelete) {
458
462
  await this.dropIndex(collectionName, extra);
459
463
  }
460
464
  }
@@ -507,3 +511,7 @@ class NeDbDatabase extends AbstractMutexer_1.default {
507
511
  }
508
512
  }
509
513
  exports.default = NeDbDatabase;
514
+ const NULL_PLACEHOLDER = '_____NULL_____';
515
+ const UNDEFINED_PLACEHOLDER = '_____UNDEFINED_____';
516
+ const SHOULD_SIMULATE_SLOW_QUERIES = process.env.SHOULD_SIMULATE_SLOW_QUERIES === 'true';
517
+ const SLOW_QUERY_MAX_RANDOM_DELAY_MS = parseInt(`${(_a = process.env.SLOW_QUERY_MAX_RANDOM_DELAY_MS) !== null && _a !== void 0 ? _a : 100}`, 10);
@@ -0,0 +1 @@
1
+ export default function mapIndexFilterToNeDbQuery(filter: Record<string, any>): Record<string, any>;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const schema_1 = require("@sprucelabs/schema");
4
+ function mapIndexFilterToNeDbQuery(filter) {
5
+ const final = (0, schema_1.cloneDeep)(filter);
6
+ for (const key of Object.keys(final)) {
7
+ if (final[key].$type === 'string') {
8
+ final[key] = { $ne: null };
9
+ }
10
+ }
11
+ return final;
12
+ }
13
+ exports.default = mapIndexFilterToNeDbQuery;
@@ -26,7 +26,7 @@ export default class MongoDatabase implements Database {
26
26
  dropCollection(name: string): Promise<void>;
27
27
  dropDatabase(): Promise<void>;
28
28
  private listIndexes;
29
- dropIndex(collection: string, fields: string[]): Promise<void>;
29
+ dropIndex(collection: string, index: UniqueIndex): Promise<void>;
30
30
  getUniqueIndexes(collection: string): Promise<string[][]>;
31
31
  getIndexes(collection: string, shouldIncludeUnique?: boolean): Promise<any[]>;
32
32
  createIndex(collection: string, fields: string[]): Promise<void>;
@@ -260,22 +260,22 @@ export default class MongoDatabase {
260
260
  }
261
261
  });
262
262
  }
263
- dropIndex(collection, fields) {
263
+ dropIndex(collection, index) {
264
264
  return __awaiter(this, void 0, void 0, function* () {
265
265
  const indexes = yield this.listIndexes(collection);
266
266
  let found = false;
267
- for (const index of indexes) {
268
- if (isEqual(Object.keys(index.key), fields)) {
267
+ for (const thisIndex of indexes) {
268
+ if (isEqual(Object.keys(thisIndex.key), index)) {
269
269
  yield this.assertDbWhileAttempingTo('drop a index.', collection)
270
270
  .collection(collection)
271
- .dropIndex(index.name);
271
+ .dropIndex(thisIndex.name);
272
272
  found = true;
273
273
  }
274
274
  }
275
275
  if (!found) {
276
276
  throw new SpruceError({
277
277
  code: 'INDEX_NOT_FOUND',
278
- missingIndex: fields,
278
+ missingIndex: normalizeIndex(index).fields,
279
279
  collectionName: collection,
280
280
  });
281
281
  }
@@ -364,11 +364,19 @@ export default class MongoDatabase {
364
364
  }
365
365
  syncIndexes(collectionName, indexes) {
366
366
  return __awaiter(this, void 0, void 0, function* () {
367
+ var _a;
367
368
  const currentIndexes = yield this.getIndexes(collectionName);
368
369
  const extraIndexes = differenceWith(currentIndexes, indexes, isEqual).filter((i) => !(i.length === 1 && i[0] === '_id'));
369
370
  for (const index of indexes) {
370
371
  if (!this.doesIndexExist(currentIndexes, index)) {
371
- yield this.createIndex(collectionName, index);
372
+ try {
373
+ yield this.createIndex(collectionName, index);
374
+ }
375
+ catch (err) {
376
+ if (((_a = err.options) === null || _a === void 0 ? void 0 : _a.code) !== 'INDEX_EXISTS') {
377
+ throw err;
378
+ }
379
+ }
372
380
  }
373
381
  }
374
382
  for (const extra of extraIndexes) {
@@ -415,7 +423,12 @@ export default class MongoDatabase {
415
423
  return __awaiter(this, void 0, void 0, function* () {
416
424
  var _a;
417
425
  const currentIndexes = yield this.getUniqueIndexes(collectionName);
418
- const extraIndexes = differenceWith(currentIndexes, indexes, isEqual);
426
+ const toDelete = [];
427
+ for (const index of currentIndexes) {
428
+ if (!this.doesIndexExist(indexes, index)) {
429
+ toDelete.push(index);
430
+ }
431
+ }
419
432
  for (const index of indexes) {
420
433
  const { fields } = this.normalizeIndex(index);
421
434
  if (!this.doesIndexExist(currentIndexes, fields)) {
@@ -429,7 +442,7 @@ export default class MongoDatabase {
429
442
  }
430
443
  }
431
444
  }
432
- for (const extra of extraIndexes) {
445
+ for (const extra of toDelete) {
433
446
  yield this.dropIndex(collectionName, extra);
434
447
  }
435
448
  });
@@ -30,12 +30,9 @@ import SpruceError from '../errors/SpruceError.js';
30
30
  import AbstractMutexer from '../mutexers/AbstractMutexer.js';
31
31
  import generateId from '../utilities/generateId.js';
32
32
  import mongoUtil from '../utilities/mongo.utility.js';
33
+ import mapIndexFilterToNeDbQuery from './mapIndexFilterToNeDbQuery.js';
33
34
  import normalizeIndex from './normalizeIndex.js';
34
35
  dotenv.config();
35
- const NULL_PLACEHOLDER = '_____NULL_____';
36
- const UNDEFINED_PLACEHOLDER = '_____UNDEFINED_____';
37
- const SHOULD_SIMULATE_SLOW_QUERIES = process.env.SHOULD_SIMULATE_SLOW_QUERIES === 'true';
38
- const SLOW_QUERY_MAX_RANDOM_DELAY_MS = parseInt(`${(_a = process.env.SLOW_QUERY_MAX_RANDOM_DELAY_MS) !== null && _a !== void 0 ? _a : 100}`, 10);
39
36
  export default class NeDbDatabase extends AbstractMutexer {
40
37
  constructor() {
41
38
  super(...arguments);
@@ -330,17 +327,20 @@ export default class NeDbDatabase extends AbstractMutexer {
330
327
  if (col._uniqueIndexes) {
331
328
  for (const index of col._uniqueIndexes) {
332
329
  const { fields, filter } = normalizeIndex(index);
333
- if (filter) {
334
- return;
335
- }
336
330
  const existing = query
337
331
  ? yield this.findOne(collection, query)
338
332
  : null;
339
- const q = {};
333
+ let q = filter
334
+ ? mapIndexFilterToNeDbQuery(filter)
335
+ : {};
340
336
  const duplicateFields = [];
341
337
  const duplicateValues = [];
342
338
  fields.forEach((f) => {
343
- q[f] = get(values, f);
339
+ let value = get(values, f);
340
+ if (value === NULL_PLACEHOLDER && q[f]) {
341
+ value = q[f];
342
+ }
343
+ q[f] = value;
344
344
  duplicateFields.push(f);
345
345
  duplicateValues.push(q[f]);
346
346
  });
@@ -429,9 +429,9 @@ export default class NeDbDatabase extends AbstractMutexer {
429
429
  });
430
430
  }
431
431
  }
432
- doesIndexExist(currentIndexes, fields) {
433
- for (const index of currentIndexes !== null && currentIndexes !== void 0 ? currentIndexes : []) {
434
- if (isEqual(index, fields)) {
432
+ doesIndexExist(currentIndexes, index) {
433
+ for (const existing of currentIndexes !== null && currentIndexes !== void 0 ? currentIndexes : []) {
434
+ if (isEqual(existing, index)) {
435
435
  return true;
436
436
  }
437
437
  }
@@ -487,10 +487,14 @@ export default class NeDbDatabase extends AbstractMutexer {
487
487
  return __awaiter(this, void 0, void 0, function* () {
488
488
  var _a;
489
489
  const currentIndexes = yield this.getUniqueIndexes(collectionName);
490
- const extraIndexes = differenceWith(currentIndexes, indexes, isEqual);
490
+ const toDelete = [];
491
+ for (const index of currentIndexes) {
492
+ if (!this.doesIndexExist(indexes, index)) {
493
+ toDelete.push(index);
494
+ }
495
+ }
491
496
  for (const index of indexes) {
492
- const { fields } = normalizeIndex(index);
493
- if (!this.doesIndexExist(currentIndexes, fields)) {
497
+ if (!this.doesIndexExist(currentIndexes, index)) {
494
498
  try {
495
499
  yield this.createUniqueIndex(collectionName, index);
496
500
  }
@@ -501,7 +505,7 @@ export default class NeDbDatabase extends AbstractMutexer {
501
505
  }
502
506
  }
503
507
  }
504
- for (const extra of extraIndexes) {
508
+ for (const extra of toDelete) {
505
509
  yield this.dropIndex(collectionName, extra);
506
510
  }
507
511
  });
@@ -558,3 +562,7 @@ export default class NeDbDatabase extends AbstractMutexer {
558
562
  this.fakedQueries[this.queryToKey(query)] = cb;
559
563
  }
560
564
  }
565
+ const NULL_PLACEHOLDER = '_____NULL_____';
566
+ const UNDEFINED_PLACEHOLDER = '_____UNDEFINED_____';
567
+ const SHOULD_SIMULATE_SLOW_QUERIES = process.env.SHOULD_SIMULATE_SLOW_QUERIES === 'true';
568
+ const SLOW_QUERY_MAX_RANDOM_DELAY_MS = parseInt(`${(_a = process.env.SLOW_QUERY_MAX_RANDOM_DELAY_MS) !== null && _a !== void 0 ? _a : 100}`, 10);
@@ -0,0 +1 @@
1
+ export default function mapIndexFilterToNeDbQuery(filter: Record<string, any>): Record<string, any>;
@@ -0,0 +1,10 @@
1
+ import { cloneDeep } from '@sprucelabs/schema';
2
+ export default function mapIndexFilterToNeDbQuery(filter) {
3
+ const final = cloneDeep(filter);
4
+ for (const key of Object.keys(final)) {
5
+ if (final[key].$type === 'string') {
6
+ final[key] = { $ne: null };
7
+ }
8
+ }
9
+ return final;
10
+ }
@@ -753,6 +753,24 @@ const databaseAssertUtil = {
753
753
  indexes = (yield db.getUniqueIndexes(this.collectionName));
754
754
  assert.isLength(indexes, 1);
755
755
  assert.isEqual(indexes[0][0].toLowerCase(), 'uniquefield');
756
+ yield db.syncUniqueIndexes(this.collectionName, [
757
+ {
758
+ fields: ['username'],
759
+ filter: { isActive: true },
760
+ },
761
+ ]);
762
+ yield db.syncUniqueIndexes(this.collectionName, [
763
+ {
764
+ fields: ['otherField', 'otherField2'],
765
+ filter: { otherField: { $exists: true } },
766
+ },
767
+ {
768
+ fields: ['username'],
769
+ filter: { isActive: true },
770
+ },
771
+ ]);
772
+ indexes = (yield db.getUniqueIndexes(this.collectionName));
773
+ assert.isLength(indexes, 2, `Syncing unique indexs with filter is not removing extra indexes.`);
756
774
  yield this.shutdown(db);
757
775
  });
758
776
  },
@@ -1481,7 +1499,7 @@ const databaseAssertUtil = {
1481
1499
  username: 'test',
1482
1500
  phone: null,
1483
1501
  dateScrambled: 'test',
1484
- }));
1502
+ }), undefined, `Creating a duplicate record with should throw an error.`);
1485
1503
  yield db.createOne(this.collectionName, {
1486
1504
  phone: '555-000-0000',
1487
1505
  dateScrambled: 'test',
@@ -686,6 +686,24 @@ const databaseAssertUtil = {
686
686
  indexes = (await db.getUniqueIndexes(this.collectionName));
687
687
  test_utils_1.assert.isLength(indexes, 1);
688
688
  test_utils_1.assert.isEqual(indexes[0][0].toLowerCase(), 'uniquefield');
689
+ await db.syncUniqueIndexes(this.collectionName, [
690
+ {
691
+ fields: ['username'],
692
+ filter: { isActive: true },
693
+ },
694
+ ]);
695
+ await db.syncUniqueIndexes(this.collectionName, [
696
+ {
697
+ fields: ['otherField', 'otherField2'],
698
+ filter: { otherField: { $exists: true } },
699
+ },
700
+ {
701
+ fields: ['username'],
702
+ filter: { isActive: true },
703
+ },
704
+ ]);
705
+ indexes = (await db.getUniqueIndexes(this.collectionName));
706
+ test_utils_1.assert.isLength(indexes, 2, `Syncing unique indexs with filter is not removing extra indexes.`);
689
707
  await this.shutdown(db);
690
708
  },
691
709
  async assertSyncingUniqueIndexsSkipsExistingIndexs(connect) {
@@ -1356,7 +1374,7 @@ const databaseAssertUtil = {
1356
1374
  username: 'test',
1357
1375
  phone: null,
1358
1376
  dateScrambled: 'test',
1359
- }));
1377
+ }), undefined, `Creating a duplicate record with should throw an error.`);
1360
1378
  await db.createOne(this.collectionName, {
1361
1379
  phone: '555-000-0000',
1362
1380
  dateScrambled: 'test',
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "28.1.2",
6
+ "version": "28.1.3",
7
7
  "files": [
8
8
  "build/**/*",
9
9
  "!build/__tests__",