@naturalcycles/db-lib 9.3.2 → 9.4.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.
|
@@ -106,6 +106,10 @@ export declare class CommonDao<BM extends PartialObjectWithId, DBM extends Parti
|
|
|
106
106
|
* 3. Saves (as fast as possible since the read) with the Patch applied, but only if the data has changed.
|
|
107
107
|
*/
|
|
108
108
|
patchById(id: string, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>>;
|
|
109
|
+
/**
|
|
110
|
+
* Like patchById, but runs all operations within a Transaction.
|
|
111
|
+
*/
|
|
112
|
+
patchByIdInTransaction(id: string, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>>;
|
|
109
113
|
/**
|
|
110
114
|
* Same as patchById, but takes the whole object as input.
|
|
111
115
|
* This "whole object" is mutated with the patch and returned.
|
|
@@ -113,6 +117,10 @@ export declare class CommonDao<BM extends PartialObjectWithId, DBM extends Parti
|
|
|
113
117
|
* It still loads the row from the DB.
|
|
114
118
|
*/
|
|
115
119
|
patch(bm: Saved<BM>, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>>;
|
|
120
|
+
/**
|
|
121
|
+
* Like patch, but runs all operations within a Transaction.
|
|
122
|
+
*/
|
|
123
|
+
patchInTransaction(bm: Saved<BM>, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>>;
|
|
116
124
|
saveAsDBM(dbm: DBM, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<DBM>>;
|
|
117
125
|
saveBatch(bms: BM[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>[]>;
|
|
118
126
|
saveBatchAsDBM(dbms: DBM[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<DBM>[]>;
|
|
@@ -160,14 +168,14 @@ export declare class CommonDao<BM extends PartialObjectWithId, DBM extends Parti
|
|
|
160
168
|
*
|
|
161
169
|
* Does NOT mutate the object.
|
|
162
170
|
*/
|
|
163
|
-
validateAndConvert<
|
|
171
|
+
validateAndConvert<T>(obj: Partial<T>, schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined, modelType: DBModelType, opt?: CommonDaoOptions): any;
|
|
164
172
|
getTableSchema(): Promise<JsonSchemaRootObject<DBM>>;
|
|
165
173
|
createTable(schema: JsonSchemaObject<DBM>, opt?: CommonDaoCreateOptions): Promise<void>;
|
|
166
174
|
/**
|
|
167
175
|
* Proxy to this.cfg.db.ping
|
|
168
176
|
*/
|
|
169
177
|
ping(): Promise<void>;
|
|
170
|
-
runInTransaction(fn: CommonDaoTransactionFn
|
|
178
|
+
runInTransaction<T = void>(fn: CommonDaoTransactionFn<T>, opt?: CommonDBTransactionOptions): Promise<T>;
|
|
171
179
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
172
180
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
173
181
|
protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillisNumber;
|
|
@@ -178,13 +186,13 @@ export declare class CommonDao<BM extends PartialObjectWithId, DBM extends Parti
|
|
|
178
186
|
*
|
|
179
187
|
* Transaction is rolled back when the function returns rejected Promise (aka "throws").
|
|
180
188
|
*/
|
|
181
|
-
export type CommonDaoTransactionFn = (tx: CommonDaoTransaction) => Promise<
|
|
189
|
+
export type CommonDaoTransactionFn<T = void> = (tx: CommonDaoTransaction) => Promise<T>;
|
|
182
190
|
/**
|
|
183
191
|
* Transaction context.
|
|
184
192
|
* Has similar API than CommonDao, but all operations are performed in the context of the transaction.
|
|
185
193
|
*/
|
|
186
194
|
export declare class CommonDaoTransaction {
|
|
187
|
-
|
|
195
|
+
tx: DBTransaction;
|
|
188
196
|
private logger;
|
|
189
197
|
constructor(tx: DBTransaction, logger: CommonLogger);
|
|
190
198
|
/**
|
|
@@ -24,7 +24,7 @@ class CommonDao {
|
|
|
24
24
|
// otherwise to log Operations
|
|
25
25
|
// e.g in Dev (local machine), Test - it will log operations (useful for debugging)
|
|
26
26
|
logLevel: isGAE || isCI ? common_dao_model_1.CommonDaoLogLevel.NONE : common_dao_model_1.CommonDaoLogLevel.OPERATIONS,
|
|
27
|
-
|
|
27
|
+
generateId: true,
|
|
28
28
|
assignGeneratedIds: false,
|
|
29
29
|
useCreatedProperty: true,
|
|
30
30
|
useUpdatedProperty: true,
|
|
@@ -42,7 +42,7 @@ class CommonDao {
|
|
|
42
42
|
...cfg.hooks,
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
|
-
if (this.cfg.
|
|
45
|
+
if (this.cfg.generateId) {
|
|
46
46
|
this.cfg.hooks.createRandomId ||= () => (0, nodejs_lib_1.stringId)();
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
@@ -62,7 +62,7 @@ class CommonDao {
|
|
|
62
62
|
const op = `getById(${id})`;
|
|
63
63
|
const table = opt.table || this.cfg.table;
|
|
64
64
|
const started = this.logStarted(op, table);
|
|
65
|
-
let dbm = (await this.cfg.db.getByIds(table, [id]))[0];
|
|
65
|
+
let dbm = (await (opt.tx || this.cfg.db).getByIds(table, [id]))[0];
|
|
66
66
|
if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
|
|
67
67
|
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
68
68
|
}
|
|
@@ -89,7 +89,7 @@ class CommonDao {
|
|
|
89
89
|
const op = `getByIdAsDBM(${id})`;
|
|
90
90
|
const table = opt.table || this.cfg.table;
|
|
91
91
|
const started = this.logStarted(op, table);
|
|
92
|
-
let [dbm] = await this.cfg.db.getByIds(table, [id]);
|
|
92
|
+
let [dbm] = await (opt.tx || this.cfg.db).getByIds(table, [id]);
|
|
93
93
|
if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
|
|
94
94
|
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
95
95
|
}
|
|
@@ -105,7 +105,7 @@ class CommonDao {
|
|
|
105
105
|
const op = `getByIdAsTM(${id})`;
|
|
106
106
|
const table = opt.table || this.cfg.table;
|
|
107
107
|
const started = this.logStarted(op, table);
|
|
108
|
-
let [dbm] = await this.cfg.db.getByIds(table, [id]);
|
|
108
|
+
let [dbm] = await (opt.tx || this.cfg.db).getByIds(table, [id]);
|
|
109
109
|
if (dbm && !opt.raw && this.cfg.hooks.afterLoad) {
|
|
110
110
|
dbm = (await this.cfg.hooks.afterLoad(dbm)) || undefined;
|
|
111
111
|
}
|
|
@@ -138,7 +138,7 @@ class CommonDao {
|
|
|
138
138
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${(0, js_lib_1._truncate)(ids.slice(0, 10).join(', '), 50)})`;
|
|
139
139
|
const table = opt.table || this.cfg.table;
|
|
140
140
|
const started = this.logStarted(op, table);
|
|
141
|
-
let dbms = await this.cfg.db.getByIds(table, ids);
|
|
141
|
+
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids);
|
|
142
142
|
if (!opt.raw && this.cfg.hooks.afterLoad && dbms.length) {
|
|
143
143
|
dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(js_lib_1._isTruthy);
|
|
144
144
|
}
|
|
@@ -491,7 +491,7 @@ class CommonDao {
|
|
|
491
491
|
if (this.cfg.useUpdatedProperty) {
|
|
492
492
|
obj.updated = opt.preserveUpdatedCreated && obj.updated ? obj.updated : now;
|
|
493
493
|
}
|
|
494
|
-
if (this.cfg.
|
|
494
|
+
if (this.cfg.generateId) {
|
|
495
495
|
obj.id ||= this.cfg.hooks.createNaturalId?.(obj) || this.cfg.hooks.createRandomId();
|
|
496
496
|
}
|
|
497
497
|
return obj;
|
|
@@ -506,7 +506,7 @@ class CommonDao {
|
|
|
506
506
|
// Skipping the save operation
|
|
507
507
|
return bm;
|
|
508
508
|
}
|
|
509
|
-
const idWasGenerated = !bm.id && this.cfg.
|
|
509
|
+
const idWasGenerated = !bm.id && this.cfg.generateId;
|
|
510
510
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
511
511
|
let dbm = await this.bmToDBM(bm, opt);
|
|
512
512
|
if (this.cfg.hooks.beforeSave) {
|
|
@@ -524,7 +524,7 @@ class CommonDao {
|
|
|
524
524
|
const started = this.logSaveStarted(op, bm, table);
|
|
525
525
|
const { excludeFromIndexes } = this.cfg;
|
|
526
526
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
527
|
-
await this.cfg.db.saveBatch(table, [dbm], {
|
|
527
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
|
|
528
528
|
excludeFromIndexes,
|
|
529
529
|
assignGeneratedIds,
|
|
530
530
|
...opt,
|
|
@@ -566,6 +566,12 @@ class CommonDao {
|
|
|
566
566
|
* 3. Saves (as fast as possible since the read) with the Patch applied, but only if the data has changed.
|
|
567
567
|
*/
|
|
568
568
|
async patchById(id, patch, opt = {}) {
|
|
569
|
+
if (this.cfg.patchInTransaction && !opt.tx) {
|
|
570
|
+
// patchInTransaction means that we should run this op in Transaction
|
|
571
|
+
// But if opt.tx is passed - means that we are already in a Transaction,
|
|
572
|
+
// and should just continue as-is
|
|
573
|
+
return await this.patchByIdInTransaction(id, patch, opt);
|
|
574
|
+
}
|
|
569
575
|
let patched;
|
|
570
576
|
const loaded = await this.getById(id, opt);
|
|
571
577
|
if (loaded) {
|
|
@@ -580,6 +586,14 @@ class CommonDao {
|
|
|
580
586
|
}
|
|
581
587
|
return await this.save(patched, opt);
|
|
582
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* Like patchById, but runs all operations within a Transaction.
|
|
591
|
+
*/
|
|
592
|
+
async patchByIdInTransaction(id, patch, opt) {
|
|
593
|
+
return await this.runInTransaction(async (daoTx) => {
|
|
594
|
+
return await this.patchById(id, patch, { ...opt, tx: daoTx.tx });
|
|
595
|
+
});
|
|
596
|
+
}
|
|
583
597
|
/**
|
|
584
598
|
* Same as patchById, but takes the whole object as input.
|
|
585
599
|
* This "whole object" is mutated with the patch and returned.
|
|
@@ -587,9 +601,12 @@ class CommonDao {
|
|
|
587
601
|
* It still loads the row from the DB.
|
|
588
602
|
*/
|
|
589
603
|
async patch(bm, patch, opt = {}) {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
604
|
+
if (this.cfg.patchInTransaction && !opt.tx) {
|
|
605
|
+
// patchInTransaction means that we should run this op in Transaction
|
|
606
|
+
// But if opt.tx is passed - means that we are already in a Transaction,
|
|
607
|
+
// and should just continue as-is
|
|
608
|
+
return await this.patchInTransaction(bm, patch, opt);
|
|
609
|
+
}
|
|
593
610
|
const loaded = await this.getById(bm.id, opt);
|
|
594
611
|
if (loaded) {
|
|
595
612
|
Object.assign(loaded, patch);
|
|
@@ -605,6 +622,14 @@ class CommonDao {
|
|
|
605
622
|
}
|
|
606
623
|
return await this.save(bm, opt);
|
|
607
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Like patch, but runs all operations within a Transaction.
|
|
627
|
+
*/
|
|
628
|
+
async patchInTransaction(bm, patch, opt) {
|
|
629
|
+
return await this.runInTransaction(async (daoTx) => {
|
|
630
|
+
return await this.patch(bm, patch, { ...opt, tx: daoTx.tx });
|
|
631
|
+
});
|
|
632
|
+
}
|
|
608
633
|
async saveAsDBM(dbm, opt = {}) {
|
|
609
634
|
this.requireWriteAccess();
|
|
610
635
|
const table = opt.table || this.cfg.table;
|
|
@@ -612,7 +637,7 @@ class CommonDao {
|
|
|
612
637
|
// will override/set `updated` field, unless opts.preserveUpdated is set
|
|
613
638
|
let row = dbm;
|
|
614
639
|
if (!opt.raw) {
|
|
615
|
-
const idWasGenerated = !dbm.id && this.cfg.
|
|
640
|
+
const idWasGenerated = !dbm.id && this.cfg.generateId;
|
|
616
641
|
this.assignIdCreatedUpdated(dbm, opt); // mutates
|
|
617
642
|
row = this.anyToDBM(dbm, opt);
|
|
618
643
|
if (opt.ensureUniqueId && idWasGenerated)
|
|
@@ -630,7 +655,7 @@ class CommonDao {
|
|
|
630
655
|
if (row === null)
|
|
631
656
|
return dbm;
|
|
632
657
|
}
|
|
633
|
-
await this.cfg.db.saveBatch(table, [row], {
|
|
658
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [row], {
|
|
634
659
|
excludeFromIndexes,
|
|
635
660
|
assignGeneratedIds,
|
|
636
661
|
...opt,
|
|
@@ -699,7 +724,7 @@ class CommonDao {
|
|
|
699
724
|
if (this.cfg.hooks.beforeSave && rows.length) {
|
|
700
725
|
rows = (await (0, js_lib_1.pMap)(rows, async (row) => await this.cfg.hooks.beforeSave(row))).filter(js_lib_1._isTruthy);
|
|
701
726
|
}
|
|
702
|
-
await this.cfg.db.saveBatch(table, rows, {
|
|
727
|
+
await (opt.tx || this.cfg.db).saveBatch(table, rows, {
|
|
703
728
|
excludeFromIndexes,
|
|
704
729
|
assignGeneratedIds,
|
|
705
730
|
...opt,
|
|
@@ -858,7 +883,6 @@ class CommonDao {
|
|
|
858
883
|
// DBM > BM
|
|
859
884
|
const bm = await this.cfg.hooks.beforeDBMToBM(dbm);
|
|
860
885
|
// Validate/convert BM
|
|
861
|
-
// eslint-disable-next-line @typescript-eslint/return-await
|
|
862
886
|
return this.validateAndConvert(bm, this.cfg.bmSchema, db_model_1.DBModelType.BM, opt);
|
|
863
887
|
}
|
|
864
888
|
async dbmsToBM(dbms, opt = {}) {
|
|
@@ -876,7 +900,6 @@ class CommonDao {
|
|
|
876
900
|
// BM > DBM
|
|
877
901
|
const dbm = { ...(await this.cfg.hooks.beforeBMToDBM(bm)) };
|
|
878
902
|
// Validate/convert DBM
|
|
879
|
-
// eslint-disable-next-line @typescript-eslint/return-await
|
|
880
903
|
return this.validateAndConvert(dbm, this.cfg.dbmSchema, db_model_1.DBModelType.DBM, opt);
|
|
881
904
|
}
|
|
882
905
|
async bmsToDBM(bms, opt = {}) {
|
|
@@ -993,16 +1016,18 @@ class CommonDao {
|
|
|
993
1016
|
await this.cfg.db.ping();
|
|
994
1017
|
}
|
|
995
1018
|
async runInTransaction(fn, opt) {
|
|
1019
|
+
let r;
|
|
996
1020
|
await this.cfg.db.runInTransaction(async (tx) => {
|
|
997
1021
|
const daoTx = new CommonDaoTransaction(tx, this.cfg.logger);
|
|
998
1022
|
try {
|
|
999
|
-
await fn(daoTx);
|
|
1023
|
+
r = await fn(daoTx);
|
|
1000
1024
|
}
|
|
1001
1025
|
catch (err) {
|
|
1002
1026
|
await daoTx.rollback(); // graceful rollback that "never throws"
|
|
1003
1027
|
throw err;
|
|
1004
1028
|
}
|
|
1005
1029
|
}, opt);
|
|
1030
|
+
return r;
|
|
1006
1031
|
}
|
|
1007
1032
|
logResult(started, op, res, table) {
|
|
1008
1033
|
if (!this.cfg.logLevel)
|
|
@@ -1081,9 +1106,7 @@ class CommonDaoTransaction {
|
|
|
1081
1106
|
}
|
|
1082
1107
|
}
|
|
1083
1108
|
async getById(dao, id, opt) {
|
|
1084
|
-
|
|
1085
|
-
return null;
|
|
1086
|
-
return (await this.getByIds(dao, [id], opt))[0] || null;
|
|
1109
|
+
return await dao.getById(id, { ...opt, tx: this.tx });
|
|
1087
1110
|
}
|
|
1088
1111
|
async getByIds(dao, ids, opt) {
|
|
1089
1112
|
return await dao.getByIds(ids, { ...opt, tx: this.tx });
|
|
@@ -147,7 +147,7 @@ export interface CommonDaoCfg<BM extends PartialObjectWithId, DBM extends Partia
|
|
|
147
147
|
* Set to false to disable auto-generation of `id`.
|
|
148
148
|
* Useful e.g when your DB is generating ids by itself (e.g mysql auto_increment).
|
|
149
149
|
*/
|
|
150
|
-
|
|
150
|
+
generateId?: boolean;
|
|
151
151
|
/**
|
|
152
152
|
* See the same option in CommonDB.
|
|
153
153
|
* Defaults to false normally.
|
|
@@ -173,6 +173,13 @@ export interface CommonDaoCfg<BM extends PartialObjectWithId, DBM extends Partia
|
|
|
173
173
|
* @deprecated
|
|
174
174
|
*/
|
|
175
175
|
filterNullishValues?: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Defaults to false.
|
|
178
|
+
* If true - run patch operations (patch, patchById) in a Transaction.
|
|
179
|
+
*
|
|
180
|
+
* @experimental
|
|
181
|
+
*/
|
|
182
|
+
patchInTransaction?: boolean;
|
|
176
183
|
}
|
|
177
184
|
/**
|
|
178
185
|
* All properties default to undefined.
|
package/package.json
CHANGED
|
@@ -193,7 +193,7 @@ export interface CommonDaoCfg<
|
|
|
193
193
|
* Set to false to disable auto-generation of `id`.
|
|
194
194
|
* Useful e.g when your DB is generating ids by itself (e.g mysql auto_increment).
|
|
195
195
|
*/
|
|
196
|
-
|
|
196
|
+
generateId?: boolean
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
199
|
* See the same option in CommonDB.
|
|
@@ -223,6 +223,14 @@ export interface CommonDaoCfg<
|
|
|
223
223
|
* @deprecated
|
|
224
224
|
*/
|
|
225
225
|
filterNullishValues?: boolean
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Defaults to false.
|
|
229
|
+
* If true - run patch operations (patch, patchById) in a Transaction.
|
|
230
|
+
*
|
|
231
|
+
* @experimental
|
|
232
|
+
*/
|
|
233
|
+
patchInTransaction?: boolean
|
|
226
234
|
}
|
|
227
235
|
|
|
228
236
|
/**
|
|
@@ -87,7 +87,7 @@ export class CommonDao<
|
|
|
87
87
|
// otherwise to log Operations
|
|
88
88
|
// e.g in Dev (local machine), Test - it will log operations (useful for debugging)
|
|
89
89
|
logLevel: isGAE || isCI ? CommonDaoLogLevel.NONE : CommonDaoLogLevel.OPERATIONS,
|
|
90
|
-
|
|
90
|
+
generateId: true,
|
|
91
91
|
assignGeneratedIds: false,
|
|
92
92
|
useCreatedProperty: true,
|
|
93
93
|
useUpdatedProperty: true,
|
|
@@ -106,7 +106,7 @@ export class CommonDao<
|
|
|
106
106
|
} satisfies Partial<CommonDaoHooks<BM, DBM, TM>>,
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
if (this.cfg.
|
|
109
|
+
if (this.cfg.generateId) {
|
|
110
110
|
this.cfg.hooks!.createRandomId ||= () => stringId()
|
|
111
111
|
} else {
|
|
112
112
|
delete this.cfg.hooks!.createRandomId
|
|
@@ -130,7 +130,7 @@ export class CommonDao<
|
|
|
130
130
|
const table = opt.table || this.cfg.table
|
|
131
131
|
const started = this.logStarted(op, table)
|
|
132
132
|
|
|
133
|
-
let dbm = (await this.cfg.db.getByIds<DBM>(table, [id]))[0]
|
|
133
|
+
let dbm = (await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id]))[0]
|
|
134
134
|
if (dbm && !opt.raw && this.cfg.hooks!.afterLoad) {
|
|
135
135
|
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
136
136
|
}
|
|
@@ -170,7 +170,7 @@ export class CommonDao<
|
|
|
170
170
|
const op = `getByIdAsDBM(${id})`
|
|
171
171
|
const table = opt.table || this.cfg.table
|
|
172
172
|
const started = this.logStarted(op, table)
|
|
173
|
-
let [dbm] = await this.cfg.db.getByIds<DBM>(table, [id])
|
|
173
|
+
let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id])
|
|
174
174
|
if (dbm && !opt.raw && this.cfg.hooks!.afterLoad) {
|
|
175
175
|
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
176
176
|
}
|
|
@@ -189,7 +189,7 @@ export class CommonDao<
|
|
|
189
189
|
const op = `getByIdAsTM(${id})`
|
|
190
190
|
const table = opt.table || this.cfg.table
|
|
191
191
|
const started = this.logStarted(op, table)
|
|
192
|
-
let [dbm] = await this.cfg.db.getByIds<DBM>(table, [id])
|
|
192
|
+
let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id])
|
|
193
193
|
if (dbm && !opt.raw && this.cfg.hooks!.afterLoad) {
|
|
194
194
|
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
195
195
|
}
|
|
@@ -226,7 +226,7 @@ export class CommonDao<
|
|
|
226
226
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
227
227
|
const table = opt.table || this.cfg.table
|
|
228
228
|
const started = this.logStarted(op, table)
|
|
229
|
-
let dbms = await this.cfg.db.getByIds<DBM>(table, ids)
|
|
229
|
+
let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids)
|
|
230
230
|
if (!opt.raw && this.cfg.hooks!.afterLoad && dbms.length) {
|
|
231
231
|
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
232
232
|
_isTruthy,
|
|
@@ -689,7 +689,7 @@ export class CommonDao<
|
|
|
689
689
|
obj.updated = opt.preserveUpdatedCreated && obj.updated ? obj.updated : now
|
|
690
690
|
}
|
|
691
691
|
|
|
692
|
-
if (this.cfg.
|
|
692
|
+
if (this.cfg.generateId) {
|
|
693
693
|
obj.id ||= this.cfg.hooks!.createNaturalId?.(obj as any) || this.cfg.hooks!.createRandomId!()
|
|
694
694
|
}
|
|
695
695
|
|
|
@@ -708,7 +708,7 @@ export class CommonDao<
|
|
|
708
708
|
return bm as Saved<BM>
|
|
709
709
|
}
|
|
710
710
|
|
|
711
|
-
const idWasGenerated = !bm.id && this.cfg.
|
|
711
|
+
const idWasGenerated = !bm.id && this.cfg.generateId
|
|
712
712
|
this.assignIdCreatedUpdated(bm, opt) // mutates
|
|
713
713
|
let dbm = await this.bmToDBM(bm, opt)
|
|
714
714
|
|
|
@@ -727,7 +727,7 @@ export class CommonDao<
|
|
|
727
727
|
const { excludeFromIndexes } = this.cfg
|
|
728
728
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
729
729
|
|
|
730
|
-
await this.cfg.db.saveBatch(table, [dbm], {
|
|
730
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
|
|
731
731
|
excludeFromIndexes,
|
|
732
732
|
assignGeneratedIds,
|
|
733
733
|
...opt,
|
|
@@ -784,6 +784,13 @@ export class CommonDao<
|
|
|
784
784
|
patch: Partial<BM>,
|
|
785
785
|
opt: CommonDaoSaveBatchOptions<DBM> = {},
|
|
786
786
|
): Promise<Saved<BM>> {
|
|
787
|
+
if (this.cfg.patchInTransaction && !opt.tx) {
|
|
788
|
+
// patchInTransaction means that we should run this op in Transaction
|
|
789
|
+
// But if opt.tx is passed - means that we are already in a Transaction,
|
|
790
|
+
// and should just continue as-is
|
|
791
|
+
return await this.patchByIdInTransaction(id, patch, opt)
|
|
792
|
+
}
|
|
793
|
+
|
|
787
794
|
let patched: Saved<BM>
|
|
788
795
|
const loaded = await this.getById(id, opt)
|
|
789
796
|
|
|
@@ -801,6 +808,19 @@ export class CommonDao<
|
|
|
801
808
|
return await this.save(patched, opt)
|
|
802
809
|
}
|
|
803
810
|
|
|
811
|
+
/**
|
|
812
|
+
* Like patchById, but runs all operations within a Transaction.
|
|
813
|
+
*/
|
|
814
|
+
async patchByIdInTransaction(
|
|
815
|
+
id: string,
|
|
816
|
+
patch: Partial<BM>,
|
|
817
|
+
opt?: CommonDaoSaveBatchOptions<DBM>,
|
|
818
|
+
): Promise<Saved<BM>> {
|
|
819
|
+
return await this.runInTransaction(async daoTx => {
|
|
820
|
+
return await this.patchById(id, patch, { ...opt, tx: daoTx.tx })
|
|
821
|
+
})
|
|
822
|
+
}
|
|
823
|
+
|
|
804
824
|
/**
|
|
805
825
|
* Same as patchById, but takes the whole object as input.
|
|
806
826
|
* This "whole object" is mutated with the patch and returned.
|
|
@@ -812,9 +832,12 @@ export class CommonDao<
|
|
|
812
832
|
patch: Partial<BM>,
|
|
813
833
|
opt: CommonDaoSaveBatchOptions<DBM> = {},
|
|
814
834
|
): Promise<Saved<BM>> {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
835
|
+
if (this.cfg.patchInTransaction && !opt.tx) {
|
|
836
|
+
// patchInTransaction means that we should run this op in Transaction
|
|
837
|
+
// But if opt.tx is passed - means that we are already in a Transaction,
|
|
838
|
+
// and should just continue as-is
|
|
839
|
+
return await this.patchInTransaction(bm, patch, opt)
|
|
840
|
+
}
|
|
818
841
|
|
|
819
842
|
const loaded = await this.getById(bm.id, opt)
|
|
820
843
|
|
|
@@ -835,6 +858,19 @@ export class CommonDao<
|
|
|
835
858
|
return await this.save(bm, opt)
|
|
836
859
|
}
|
|
837
860
|
|
|
861
|
+
/**
|
|
862
|
+
* Like patch, but runs all operations within a Transaction.
|
|
863
|
+
*/
|
|
864
|
+
async patchInTransaction(
|
|
865
|
+
bm: Saved<BM>,
|
|
866
|
+
patch: Partial<BM>,
|
|
867
|
+
opt?: CommonDaoSaveBatchOptions<DBM>,
|
|
868
|
+
): Promise<Saved<BM>> {
|
|
869
|
+
return await this.runInTransaction(async daoTx => {
|
|
870
|
+
return await this.patch(bm, patch, { ...opt, tx: daoTx.tx })
|
|
871
|
+
})
|
|
872
|
+
}
|
|
873
|
+
|
|
838
874
|
async saveAsDBM(dbm: DBM, opt: CommonDaoSaveBatchOptions<DBM> = {}): Promise<Saved<DBM>> {
|
|
839
875
|
this.requireWriteAccess()
|
|
840
876
|
const table = opt.table || this.cfg.table
|
|
@@ -843,7 +879,7 @@ export class CommonDao<
|
|
|
843
879
|
// will override/set `updated` field, unless opts.preserveUpdated is set
|
|
844
880
|
let row = dbm as Saved<DBM>
|
|
845
881
|
if (!opt.raw) {
|
|
846
|
-
const idWasGenerated = !dbm.id && this.cfg.
|
|
882
|
+
const idWasGenerated = !dbm.id && this.cfg.generateId
|
|
847
883
|
this.assignIdCreatedUpdated(dbm, opt) // mutates
|
|
848
884
|
row = this.anyToDBM(dbm, opt)
|
|
849
885
|
if (opt.ensureUniqueId && idWasGenerated) await this.ensureUniqueId(table, row)
|
|
@@ -861,7 +897,7 @@ export class CommonDao<
|
|
|
861
897
|
if (row === null) return dbm as Saved<DBM>
|
|
862
898
|
}
|
|
863
899
|
|
|
864
|
-
await this.cfg.db.saveBatch(table, [row], {
|
|
900
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [row], {
|
|
865
901
|
excludeFromIndexes,
|
|
866
902
|
assignGeneratedIds,
|
|
867
903
|
...opt,
|
|
@@ -952,7 +988,7 @@ export class CommonDao<
|
|
|
952
988
|
)
|
|
953
989
|
}
|
|
954
990
|
|
|
955
|
-
await this.cfg.db.saveBatch(table, rows, {
|
|
991
|
+
await (opt.tx || this.cfg.db).saveBatch(table, rows, {
|
|
956
992
|
excludeFromIndexes,
|
|
957
993
|
assignGeneratedIds,
|
|
958
994
|
...opt,
|
|
@@ -1163,7 +1199,7 @@ export class CommonDao<
|
|
|
1163
1199
|
const bm = await this.cfg.hooks!.beforeDBMToBM!(dbm)
|
|
1164
1200
|
|
|
1165
1201
|
// Validate/convert BM
|
|
1166
|
-
|
|
1202
|
+
|
|
1167
1203
|
return this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
|
|
1168
1204
|
}
|
|
1169
1205
|
|
|
@@ -1192,7 +1228,7 @@ export class CommonDao<
|
|
|
1192
1228
|
const dbm = { ...(await this.cfg.hooks!.beforeBMToDBM!(bm)) }
|
|
1193
1229
|
|
|
1194
1230
|
// Validate/convert DBM
|
|
1195
|
-
|
|
1231
|
+
|
|
1196
1232
|
return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
|
|
1197
1233
|
}
|
|
1198
1234
|
|
|
@@ -1251,14 +1287,14 @@ export class CommonDao<
|
|
|
1251
1287
|
*
|
|
1252
1288
|
* Does NOT mutate the object.
|
|
1253
1289
|
*/
|
|
1254
|
-
validateAndConvert<
|
|
1255
|
-
obj: Partial<
|
|
1256
|
-
schema: ObjectSchema<
|
|
1290
|
+
validateAndConvert<T>(
|
|
1291
|
+
obj: Partial<T>,
|
|
1292
|
+
schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined,
|
|
1257
1293
|
modelType: DBModelType,
|
|
1258
1294
|
opt: CommonDaoOptions = {},
|
|
1259
|
-
):
|
|
1295
|
+
): any {
|
|
1260
1296
|
// `raw` option completely bypasses any processing
|
|
1261
|
-
if (opt.raw) return obj as any
|
|
1297
|
+
if (opt.raw) return obj as any
|
|
1262
1298
|
|
|
1263
1299
|
// Kirill 2021-10-18: I realized that there's little reason to keep removing null values
|
|
1264
1300
|
// So, from now on we'll preserve them
|
|
@@ -1277,31 +1313,31 @@ export class CommonDao<
|
|
|
1277
1313
|
|
|
1278
1314
|
// Pre-validation hooks
|
|
1279
1315
|
if (modelType === DBModelType.DBM) {
|
|
1280
|
-
obj = this.cfg.hooks!.beforeDBMValidate!(obj as any) as
|
|
1316
|
+
obj = this.cfg.hooks!.beforeDBMValidate!(obj as any) as T
|
|
1281
1317
|
}
|
|
1282
1318
|
|
|
1283
1319
|
// Return as is if no schema is passed or if `skipConversion` is set
|
|
1284
1320
|
if (!schema || opt.skipConversion) {
|
|
1285
|
-
return obj
|
|
1321
|
+
return obj
|
|
1286
1322
|
}
|
|
1287
1323
|
|
|
1288
1324
|
// This will Convert and Validate
|
|
1289
1325
|
const table = opt.table || this.cfg.table
|
|
1290
1326
|
const objectName = table + (modelType || '')
|
|
1291
1327
|
|
|
1292
|
-
let error: JoiValidationError | AjvValidationError | ZodValidationError<
|
|
1328
|
+
let error: JoiValidationError | AjvValidationError | ZodValidationError<T> | undefined
|
|
1293
1329
|
let convertedValue: any
|
|
1294
1330
|
|
|
1295
1331
|
if (schema instanceof ZodSchema) {
|
|
1296
1332
|
// Zod schema
|
|
1297
|
-
const vr = zSafeValidate(obj as
|
|
1333
|
+
const vr = zSafeValidate(obj as T, schema)
|
|
1298
1334
|
error = vr.error
|
|
1299
1335
|
convertedValue = vr.data
|
|
1300
1336
|
} else if (schema instanceof AjvSchema) {
|
|
1301
1337
|
// Ajv schema
|
|
1302
1338
|
convertedValue = obj // because Ajv mutates original object
|
|
1303
1339
|
|
|
1304
|
-
error = schema.getValidationError(obj as
|
|
1340
|
+
error = schema.getValidationError(obj as T, {
|
|
1305
1341
|
objectName,
|
|
1306
1342
|
})
|
|
1307
1343
|
} else {
|
|
@@ -1337,20 +1373,24 @@ export class CommonDao<
|
|
|
1337
1373
|
await this.cfg.db.ping()
|
|
1338
1374
|
}
|
|
1339
1375
|
|
|
1340
|
-
async runInTransaction(
|
|
1341
|
-
fn: CommonDaoTransactionFn
|
|
1376
|
+
async runInTransaction<T = void>(
|
|
1377
|
+
fn: CommonDaoTransactionFn<T>,
|
|
1342
1378
|
opt?: CommonDBTransactionOptions,
|
|
1343
|
-
): Promise<
|
|
1379
|
+
): Promise<T> {
|
|
1380
|
+
let r: T
|
|
1381
|
+
|
|
1344
1382
|
await this.cfg.db.runInTransaction(async tx => {
|
|
1345
1383
|
const daoTx = new CommonDaoTransaction(tx, this.cfg.logger!)
|
|
1346
1384
|
|
|
1347
1385
|
try {
|
|
1348
|
-
await fn(daoTx)
|
|
1386
|
+
r = await fn(daoTx)
|
|
1349
1387
|
} catch (err) {
|
|
1350
1388
|
await daoTx.rollback() // graceful rollback that "never throws"
|
|
1351
1389
|
throw err
|
|
1352
1390
|
}
|
|
1353
1391
|
}, opt)
|
|
1392
|
+
|
|
1393
|
+
return r!
|
|
1354
1394
|
}
|
|
1355
1395
|
|
|
1356
1396
|
protected logResult(started: number, op: string, res: any, table: string): void {
|
|
@@ -1415,7 +1455,7 @@ export class CommonDao<
|
|
|
1415
1455
|
*
|
|
1416
1456
|
* Transaction is rolled back when the function returns rejected Promise (aka "throws").
|
|
1417
1457
|
*/
|
|
1418
|
-
export type CommonDaoTransactionFn = (tx: CommonDaoTransaction) => Promise<
|
|
1458
|
+
export type CommonDaoTransactionFn<T = void> = (tx: CommonDaoTransaction) => Promise<T>
|
|
1419
1459
|
|
|
1420
1460
|
/**
|
|
1421
1461
|
* Transaction context.
|
|
@@ -1423,7 +1463,7 @@ export type CommonDaoTransactionFn = (tx: CommonDaoTransaction) => Promise<void>
|
|
|
1423
1463
|
*/
|
|
1424
1464
|
export class CommonDaoTransaction {
|
|
1425
1465
|
constructor(
|
|
1426
|
-
|
|
1466
|
+
public tx: DBTransaction,
|
|
1427
1467
|
private logger: CommonLogger,
|
|
1428
1468
|
) {}
|
|
1429
1469
|
|
|
@@ -1444,8 +1484,7 @@ export class CommonDaoTransaction {
|
|
|
1444
1484
|
id?: string | null,
|
|
1445
1485
|
opt?: CommonDaoOptions,
|
|
1446
1486
|
): Promise<Saved<BM> | null> {
|
|
1447
|
-
|
|
1448
|
-
return (await this.getByIds(dao, [id], opt))[0] || null
|
|
1487
|
+
return await dao.getById(id, { ...opt, tx: this.tx })
|
|
1449
1488
|
}
|
|
1450
1489
|
|
|
1451
1490
|
async getByIds<BM extends PartialObjectWithId, DBM extends PartialObjectWithId>(
|