@naturalcycles/db-lib 8.51.1 → 8.53.0

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.
@@ -1,4 +1,4 @@
1
- import { AnyObject, AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Saved, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
1
+ import { AnyObject, AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Promisable, Saved, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
2
2
  import { AjvSchema, ObjectSchemaTyped, ReadableTyped } from '@naturalcycles/nodejs-lib';
3
3
  import { DBDeleteByIdsOperation, DBModelType, DBOperation, DBPatch, DBSaveBatchOperation, RunQueryResult } from '../db.model';
4
4
  import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
@@ -85,9 +85,9 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
85
85
  assignIdCreatedUpdated(obj: Unsaved<BM>, opt?: CommonDaoOptions): Saved<BM>;
86
86
  tx: {
87
87
  save: (bm: Unsaved<BM>, opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation>;
88
- saveBatch: (bms: Unsaved<BM>[], opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation>;
89
- deleteByIds: (ids: ID[], opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation>;
90
- deleteById: (id: ID, opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation>;
88
+ saveBatch: (bms: Unsaved<BM>[], opt?: CommonDaoSaveOptions<DBM>) => Promise<DBSaveBatchOperation | undefined>;
89
+ deleteByIds: (ids: ID[], opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation | undefined>;
90
+ deleteById: (id: ID | null | undefined, opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation | undefined>;
91
91
  };
92
92
  /**
93
93
  * Mutates with id, created, updated
@@ -139,9 +139,6 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
139
139
  bmToTM(bm: undefined, opt?: CommonDaoOptions): TM | undefined;
140
140
  bmToTM(bm?: Saved<BM>, opt?: CommonDaoOptions): TM;
141
141
  bmsToTM(bms: Saved<BM>[], opt?: CommonDaoOptions): TM[];
142
- tmToBM(tm: undefined, opt?: CommonDaoOptions): undefined;
143
- tmToBM(tm?: TM, opt?: CommonDaoOptions): BM;
144
- tmsToBM(tms: TM[], opt?: CommonDaoOptions): BM[];
145
142
  /**
146
143
  * Returns *converted value*.
147
144
  * Validates (unless `skipValidation=true` passed).
@@ -155,7 +152,7 @@ export declare class CommonDao<BM extends Partial<ObjectWithId<ID>>, DBM extends
155
152
  * Proxy to this.cfg.db.ping
156
153
  */
157
154
  ping(): Promise<void>;
158
- runInTransaction(ops: Promise<DBOperation>[]): Promise<void>;
155
+ runInTransaction(ops: Promisable<DBOperation | undefined>[]): Promise<void>;
159
156
  protected logResult(started: number, op: string, res: any, table: string): void;
160
157
  protected logSaveResult(started: number, op: string, table: string): void;
161
158
  protected logStarted(op: string, table: string, force?: boolean): number;
@@ -34,6 +34,8 @@ class CommonDao {
34
34
  };
35
35
  },
36
36
  saveBatch: async (bms, opt = {}) => {
37
+ if (!bms.length)
38
+ return;
37
39
  const rows = (await this.saveBatch(bms, { ...opt, tx: true }));
38
40
  return {
39
41
  type: 'saveBatch',
@@ -46,6 +48,8 @@ class CommonDao {
46
48
  };
47
49
  },
48
50
  deleteByIds: async (ids, opt = {}) => {
51
+ if (!ids.length)
52
+ return;
49
53
  return {
50
54
  type: 'deleteByIds',
51
55
  table: this.cfg.table,
@@ -54,6 +58,8 @@ class CommonDao {
54
58
  };
55
59
  },
56
60
  deleteById: async (id, opt = {}) => {
61
+ if (!id)
62
+ return;
57
63
  return {
58
64
  type: 'deleteByIds',
59
65
  table: this.cfg.table,
@@ -80,7 +86,6 @@ class CommonDao {
80
86
  beforeDBMValidate: dbm => dbm,
81
87
  beforeDBMToBM: dbm => dbm,
82
88
  beforeBMToDBM: bm => bm,
83
- beforeTMToBM: tm => tm,
84
89
  beforeBMToTM: bm => bm,
85
90
  anonymize: dbm => dbm,
86
91
  onValidationError: err => err,
@@ -784,22 +789,6 @@ class CommonDao {
784
789
  // try/catch?
785
790
  return bms.map(bm => this.bmToTM(bm, opt));
786
791
  }
787
- tmToBM(tm, opt = {}) {
788
- if (!tm)
789
- return;
790
- // optimization: 1 validation is enough
791
- // Validate/convert TM
792
- // bm gets assigned to the new reference
793
- // tm = this.validateAndConvert(tm, this.cfg.tmSchema, DBModelType.TM, opt)
794
- // TM > BM
795
- const bm = this.cfg.hooks.beforeTMToBM(tm);
796
- // Validate/convert BM
797
- return this.validateAndConvert(bm, this.cfg.bmSchema, db_model_1.DBModelType.BM, opt);
798
- }
799
- tmsToBM(tms, opt = {}) {
800
- // try/catch?
801
- return tms.map(tm => this.tmToBM(tm, opt));
802
- }
803
792
  /**
804
793
  * Returns *converted value*.
805
794
  * Validates (unless `skipValidation=true` passed).
@@ -879,9 +868,9 @@ class CommonDao {
879
868
  await this.cfg.db.ping();
880
869
  }
881
870
  async runInTransaction(ops) {
882
- if (!ops.length)
871
+ const resolvedOps = (await Promise.all(ops)).filter(js_lib_1._isTruthy);
872
+ if (!resolvedOps.length)
883
873
  return;
884
- const resolvedOps = await Promise.all(ops);
885
874
  await this.cfg.db.commitTransaction(dbTransaction_1.DBTransaction.create(resolvedOps));
886
875
  }
887
876
  logResult(started, op, res, table) {
@@ -3,19 +3,53 @@ import { AjvSchema, AjvValidationError, JoiValidationError, ObjectSchemaTyped, T
3
3
  import { CommonDB } from '../common.db';
4
4
  import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions } from '../db.model';
5
5
  export interface CommonDaoHooks<BM extends Partial<ObjectWithId<ID>>, DBM extends ObjectWithId<ID>, TM, ID extends string | number> {
6
+ /**
7
+ * Allows to override the id generation function.
8
+ * By default it uses `stringId` from nodejs-lib
9
+ * (which uses lowercase alphanumberic alphabet and the size of 16).
10
+ */
6
11
  createRandomId: () => ID;
7
12
  /**
8
13
  * createNaturalId hook is called (tried) first.
9
14
  * If it doesn't exist - createRandomId is called.
10
15
  */
11
16
  createNaturalId: (obj: DBM | BM) => ID;
17
+ /**
18
+ * It's a counter-part of `createNaturalId`.
19
+ * Allows to provide a parser function to parse "natural id" into
20
+ * DBM components (e.g accountId and some other property that is part of the id).
21
+ */
12
22
  parseNaturalId: (id: ID) => Partial<DBM>;
23
+ /**
24
+ * It is called only on `dao.create` method.
25
+ * Dao.create method is called in:
26
+ *
27
+ * - getByIdOrEmpty, getByIdAsDBMOrEmpty
28
+ * - patch, patchAsDBM
29
+ */
13
30
  beforeCreate: (bm: Partial<BM>) => Partial<BM>;
31
+ /**
32
+ * Called when loading things "as DBM" and validation is not skipped.
33
+ * When loading things like BM/TM - other hooks get involved instead:
34
+ * - beforeDBMToBM
35
+ * - beforeBMToTM
36
+ *
37
+ * TODO: maybe rename those to `validateAs(model)`
38
+ * as it only validates "final state", not intermediate
39
+ */
14
40
  beforeDBMValidate: (dbm: Partial<DBM>) => Partial<DBM>;
15
41
  beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>;
16
42
  beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>;
17
- beforeTMToBM: (tm: TM) => Partial<BM>;
18
43
  beforeBMToTM: (bm: BM) => Partial<TM>;
44
+ /**
45
+ * Called in:
46
+ * - dbmToBM (applied before DBM becomes BM)
47
+ * - anyToDBM
48
+ *
49
+ * Hook only allows to apply anonymization to DBM (not to BM).
50
+ * It still applies to BM "transitively", during dbmToBM
51
+ * (e.g after loaded from the Database).
52
+ */
19
53
  anonymize: (dbm: DBM) => DBM;
20
54
  /**
21
55
  * If hook is defined - allows to prevent or modify the error thrown.
package/package.json CHANGED
@@ -41,7 +41,7 @@
41
41
  "engines": {
42
42
  "node": ">=18.12"
43
43
  },
44
- "version": "8.51.1",
44
+ "version": "8.53.0",
45
45
  "description": "Lowest Common Denominator API to supported Databases",
46
46
  "keywords": [
47
47
  "db",
@@ -23,19 +23,59 @@ export interface CommonDaoHooks<
23
23
  TM,
24
24
  ID extends string | number,
25
25
  > {
26
+ /**
27
+ * Allows to override the id generation function.
28
+ * By default it uses `stringId` from nodejs-lib
29
+ * (which uses lowercase alphanumberic alphabet and the size of 16).
30
+ */
26
31
  createRandomId: () => ID
32
+
27
33
  /**
28
34
  * createNaturalId hook is called (tried) first.
29
35
  * If it doesn't exist - createRandomId is called.
30
36
  */
31
37
  createNaturalId: (obj: DBM | BM) => ID
38
+
39
+ /**
40
+ * It's a counter-part of `createNaturalId`.
41
+ * Allows to provide a parser function to parse "natural id" into
42
+ * DBM components (e.g accountId and some other property that is part of the id).
43
+ */
32
44
  parseNaturalId: (id: ID) => Partial<DBM>
45
+
46
+ /**
47
+ * It is called only on `dao.create` method.
48
+ * Dao.create method is called in:
49
+ *
50
+ * - getByIdOrEmpty, getByIdAsDBMOrEmpty
51
+ * - patch, patchAsDBM
52
+ */
33
53
  beforeCreate: (bm: Partial<BM>) => Partial<BM>
54
+
55
+ /**
56
+ * Called when loading things "as DBM" and validation is not skipped.
57
+ * When loading things like BM/TM - other hooks get involved instead:
58
+ * - beforeDBMToBM
59
+ * - beforeBMToTM
60
+ *
61
+ * TODO: maybe rename those to `validateAs(model)`
62
+ * as it only validates "final state", not intermediate
63
+ */
34
64
  beforeDBMValidate: (dbm: Partial<DBM>) => Partial<DBM>
65
+
35
66
  beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>
36
67
  beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
37
- beforeTMToBM: (tm: TM) => Partial<BM>
38
68
  beforeBMToTM: (bm: BM) => Partial<TM>
69
+
70
+ /**
71
+ * Called in:
72
+ * - dbmToBM (applied before DBM becomes BM)
73
+ * - anyToDBM
74
+ *
75
+ * Hook only allows to apply anonymization to DBM (not to BM).
76
+ * It still applies to BM "transitively", during dbmToBM
77
+ * (e.g after loaded from the Database).
78
+ */
39
79
  anonymize: (dbm: DBM) => DBM
40
80
 
41
81
  /**
@@ -2,6 +2,7 @@ import {
2
2
  _assert,
3
3
  _filterNullishValues,
4
4
  _filterUndefinedValues,
5
+ _isTruthy,
5
6
  _passthroughPredicate,
6
7
  _since,
7
8
  _truncate,
@@ -14,6 +15,7 @@ import {
14
15
  JsonSchemaRootObject,
15
16
  ObjectWithId,
16
17
  pMap,
18
+ Promisable,
17
19
  Saved,
18
20
  Unsaved,
19
21
  ZodSchema,
@@ -93,7 +95,6 @@ export class CommonDao<
93
95
  beforeDBMValidate: dbm => dbm,
94
96
  beforeDBMToBM: dbm => dbm as any,
95
97
  beforeBMToDBM: bm => bm as any,
96
- beforeTMToBM: tm => tm as any,
97
98
  beforeBMToTM: bm => bm as any,
98
99
  anonymize: dbm => dbm,
99
100
  onValidationError: err => err,
@@ -633,7 +634,8 @@ export class CommonDao<
633
634
  saveBatch: async (
634
635
  bms: Unsaved<BM>[],
635
636
  opt: CommonDaoSaveOptions<DBM> = {},
636
- ): Promise<DBSaveBatchOperation> => {
637
+ ): Promise<DBSaveBatchOperation | undefined> => {
638
+ if (!bms.length) return
637
639
  const rows: DBM[] = (await this.saveBatch(bms, { ...opt, tx: true })) as any
638
640
 
639
641
  return {
@@ -646,7 +648,11 @@ export class CommonDao<
646
648
  },
647
649
  }
648
650
  },
649
- deleteByIds: async (ids: ID[], opt: CommonDaoOptions = {}): Promise<DBDeleteByIdsOperation> => {
651
+ deleteByIds: async (
652
+ ids: ID[],
653
+ opt: CommonDaoOptions = {},
654
+ ): Promise<DBDeleteByIdsOperation | undefined> => {
655
+ if (!ids.length) return
650
656
  return {
651
657
  type: 'deleteByIds',
652
658
  table: this.cfg.table,
@@ -654,7 +660,11 @@ export class CommonDao<
654
660
  opt,
655
661
  }
656
662
  },
657
- deleteById: async (id: ID, opt: CommonDaoOptions = {}): Promise<DBDeleteByIdsOperation> => {
663
+ deleteById: async (
664
+ id: ID | null | undefined,
665
+ opt: CommonDaoOptions = {},
666
+ ): Promise<DBDeleteByIdsOperation | undefined> => {
667
+ if (!id) return
658
668
  return {
659
669
  type: 'deleteByIds',
660
670
  table: this.cfg.table,
@@ -1050,28 +1060,6 @@ export class CommonDao<
1050
1060
  return bms.map(bm => this.bmToTM(bm, opt))
1051
1061
  }
1052
1062
 
1053
- tmToBM(tm: undefined, opt?: CommonDaoOptions): undefined
1054
- tmToBM(tm?: TM, opt?: CommonDaoOptions): BM
1055
- tmToBM(tm?: TM, opt: CommonDaoOptions = {}): BM | undefined {
1056
- if (!tm) return
1057
-
1058
- // optimization: 1 validation is enough
1059
- // Validate/convert TM
1060
- // bm gets assigned to the new reference
1061
- // tm = this.validateAndConvert(tm, this.cfg.tmSchema, DBModelType.TM, opt)
1062
-
1063
- // TM > BM
1064
- const bm = this.cfg.hooks!.beforeTMToBM!(tm) as BM
1065
-
1066
- // Validate/convert BM
1067
- return this.validateAndConvert<BM>(bm, this.cfg.bmSchema, DBModelType.BM, opt)
1068
- }
1069
-
1070
- tmsToBM(tms: TM[], opt: CommonDaoOptions = {}): BM[] {
1071
- // try/catch?
1072
- return tms.map(tm => this.tmToBM(tm, opt))
1073
- }
1074
-
1075
1063
  /**
1076
1064
  * Returns *converted value*.
1077
1065
  * Validates (unless `skipValidation=true` passed).
@@ -1164,10 +1152,9 @@ export class CommonDao<
1164
1152
  await this.cfg.db.ping()
1165
1153
  }
1166
1154
 
1167
- async runInTransaction(ops: Promise<DBOperation>[]): Promise<void> {
1168
- if (!ops.length) return
1169
-
1170
- const resolvedOps = await Promise.all(ops)
1155
+ async runInTransaction(ops: Promisable<DBOperation | undefined>[]): Promise<void> {
1156
+ const resolvedOps = (await Promise.all(ops)).filter(_isTruthy)
1157
+ if (!resolvedOps.length) return
1171
1158
 
1172
1159
  await this.cfg.db.commitTransaction(DBTransaction.create(resolvedOps))
1173
1160
  }
package/src/db.model.ts CHANGED
@@ -10,7 +10,6 @@ import { ObjectWithId } from '@naturalcycles/js-lib'
10
10
  */
11
11
  export type CommonDBSaveMethod = 'upsert' | 'insert' | 'update'
12
12
 
13
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
14
13
  export interface CommonDBOptions {}
15
14
 
16
15
  /**