@naturalcycles/db-lib 9.3.1 → 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.
- package/dist/commondao/common.dao.d.ts +14 -6
- package/dist/commondao/common.dao.js +44 -21
- package/dist/commondao/common.dao.model.d.ts +8 -1
- package/dist/query/dbQuery.d.ts +2 -2
- package/package.json +1 -1
- package/src/commondao/common.dao.model.ts +9 -1
- package/src/commondao/common.dao.ts +80 -38
- package/src/query/dbQuery.ts +2 -2
|
@@ -60,11 +60,11 @@ export declare class CommonDao<BM extends PartialObjectWithId, DBM extends Parti
|
|
|
60
60
|
runQueryExtendedAsTM(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<RunQueryResult<TM>>;
|
|
61
61
|
runQueryCount(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<number>;
|
|
62
62
|
streamQueryForEach(q: DBQuery<DBM>, mapper: AsyncMapper<Saved<BM>, void>, opt?: CommonDaoStreamForEachOptions<Saved<BM>>): Promise<void>;
|
|
63
|
-
streamQueryAsDBMForEach(q: DBQuery<DBM>, mapper: AsyncMapper<DBM
|
|
63
|
+
streamQueryAsDBMForEach(q: DBQuery<DBM>, mapper: AsyncMapper<Saved<DBM>, void>, opt?: CommonDaoStreamForEachOptions<DBM>): Promise<void>;
|
|
64
64
|
/**
|
|
65
65
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
66
66
|
*/
|
|
67
|
-
streamQueryAsDBM(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<DBM
|
|
67
|
+
streamQueryAsDBM(q: DBQuery<DBM>, opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<Saved<DBM>>;
|
|
68
68
|
/**
|
|
69
69
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
70
70
|
*
|
|
@@ -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/dist/query/dbQuery.d.ts
CHANGED
|
@@ -100,9 +100,9 @@ export declare class RunnableDBQuery<BM extends PartialObjectWithId, DBM extends
|
|
|
100
100
|
runQueryCount(opt?: CommonDaoOptions): Promise<number>;
|
|
101
101
|
updateByQuery(patch: DBPatch<DBM>, opt?: CommonDaoOptions): Promise<number>;
|
|
102
102
|
streamQueryForEach(mapper: AsyncMapper<Saved<BM>, void>, opt?: CommonDaoStreamForEachOptions<Saved<BM>>): Promise<void>;
|
|
103
|
-
streamQueryAsDBMForEach(mapper: AsyncMapper<DBM
|
|
103
|
+
streamQueryAsDBMForEach(mapper: AsyncMapper<Saved<DBM>, void>, opt?: CommonDaoStreamForEachOptions<DBM>): Promise<void>;
|
|
104
104
|
streamQuery(opt?: CommonDaoStreamOptions<Saved<BM>>): ReadableTyped<Saved<BM>>;
|
|
105
|
-
streamQueryAsDBM(opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<DBM
|
|
105
|
+
streamQueryAsDBM(opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<Saved<DBM>>;
|
|
106
106
|
queryIds(opt?: CommonDaoOptions): Promise<string[]>;
|
|
107
107
|
streamQueryIds(opt?: CommonDaoStreamOptions<string>): ReadableTyped<string>;
|
|
108
108
|
streamQueryIdsForEach(mapper: AsyncMapper<string, void>, opt?: CommonDaoStreamForEachOptions<string>): Promise<void>;
|
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,
|
|
@@ -484,7 +484,7 @@ export class CommonDao<
|
|
|
484
484
|
|
|
485
485
|
async streamQueryAsDBMForEach(
|
|
486
486
|
q: DBQuery<DBM>,
|
|
487
|
-
mapper: AsyncMapper<DBM
|
|
487
|
+
mapper: AsyncMapper<Saved<DBM>, void>,
|
|
488
488
|
opt: CommonDaoStreamForEachOptions<DBM> = {},
|
|
489
489
|
): Promise<void> {
|
|
490
490
|
q.table = opt.table || q.table
|
|
@@ -515,7 +515,7 @@ export class CommonDao<
|
|
|
515
515
|
errorMode: opt.errorMode,
|
|
516
516
|
},
|
|
517
517
|
),
|
|
518
|
-
transformMap<DBM
|
|
518
|
+
transformMap<Saved<DBM>, void>(mapper, {
|
|
519
519
|
...opt,
|
|
520
520
|
predicate: _passthroughPredicate, // to be able to logProgress
|
|
521
521
|
}),
|
|
@@ -535,7 +535,10 @@ export class CommonDao<
|
|
|
535
535
|
/**
|
|
536
536
|
* Stream as Readable, to be able to .pipe() it further with support of backpressure.
|
|
537
537
|
*/
|
|
538
|
-
streamQueryAsDBM(
|
|
538
|
+
streamQueryAsDBM(
|
|
539
|
+
q: DBQuery<DBM>,
|
|
540
|
+
opt: CommonDaoStreamOptions<DBM> = {},
|
|
541
|
+
): ReadableTyped<Saved<DBM>> {
|
|
539
542
|
q.table = opt.table || q.table
|
|
540
543
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
541
544
|
opt.skipConversion = opt.skipConversion !== false // default true
|
|
@@ -686,7 +689,7 @@ export class CommonDao<
|
|
|
686
689
|
obj.updated = opt.preserveUpdatedCreated && obj.updated ? obj.updated : now
|
|
687
690
|
}
|
|
688
691
|
|
|
689
|
-
if (this.cfg.
|
|
692
|
+
if (this.cfg.generateId) {
|
|
690
693
|
obj.id ||= this.cfg.hooks!.createNaturalId?.(obj as any) || this.cfg.hooks!.createRandomId!()
|
|
691
694
|
}
|
|
692
695
|
|
|
@@ -705,7 +708,7 @@ export class CommonDao<
|
|
|
705
708
|
return bm as Saved<BM>
|
|
706
709
|
}
|
|
707
710
|
|
|
708
|
-
const idWasGenerated = !bm.id && this.cfg.
|
|
711
|
+
const idWasGenerated = !bm.id && this.cfg.generateId
|
|
709
712
|
this.assignIdCreatedUpdated(bm, opt) // mutates
|
|
710
713
|
let dbm = await this.bmToDBM(bm, opt)
|
|
711
714
|
|
|
@@ -724,7 +727,7 @@ export class CommonDao<
|
|
|
724
727
|
const { excludeFromIndexes } = this.cfg
|
|
725
728
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
726
729
|
|
|
727
|
-
await this.cfg.db.saveBatch(table, [dbm], {
|
|
730
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
|
|
728
731
|
excludeFromIndexes,
|
|
729
732
|
assignGeneratedIds,
|
|
730
733
|
...opt,
|
|
@@ -781,6 +784,13 @@ export class CommonDao<
|
|
|
781
784
|
patch: Partial<BM>,
|
|
782
785
|
opt: CommonDaoSaveBatchOptions<DBM> = {},
|
|
783
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
|
+
|
|
784
794
|
let patched: Saved<BM>
|
|
785
795
|
const loaded = await this.getById(id, opt)
|
|
786
796
|
|
|
@@ -798,6 +808,19 @@ export class CommonDao<
|
|
|
798
808
|
return await this.save(patched, opt)
|
|
799
809
|
}
|
|
800
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
|
+
|
|
801
824
|
/**
|
|
802
825
|
* Same as patchById, but takes the whole object as input.
|
|
803
826
|
* This "whole object" is mutated with the patch and returned.
|
|
@@ -809,9 +832,12 @@ export class CommonDao<
|
|
|
809
832
|
patch: Partial<BM>,
|
|
810
833
|
opt: CommonDaoSaveBatchOptions<DBM> = {},
|
|
811
834
|
): Promise<Saved<BM>> {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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
|
+
}
|
|
815
841
|
|
|
816
842
|
const loaded = await this.getById(bm.id, opt)
|
|
817
843
|
|
|
@@ -832,6 +858,19 @@ export class CommonDao<
|
|
|
832
858
|
return await this.save(bm, opt)
|
|
833
859
|
}
|
|
834
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
|
+
|
|
835
874
|
async saveAsDBM(dbm: DBM, opt: CommonDaoSaveBatchOptions<DBM> = {}): Promise<Saved<DBM>> {
|
|
836
875
|
this.requireWriteAccess()
|
|
837
876
|
const table = opt.table || this.cfg.table
|
|
@@ -840,7 +879,7 @@ export class CommonDao<
|
|
|
840
879
|
// will override/set `updated` field, unless opts.preserveUpdated is set
|
|
841
880
|
let row = dbm as Saved<DBM>
|
|
842
881
|
if (!opt.raw) {
|
|
843
|
-
const idWasGenerated = !dbm.id && this.cfg.
|
|
882
|
+
const idWasGenerated = !dbm.id && this.cfg.generateId
|
|
844
883
|
this.assignIdCreatedUpdated(dbm, opt) // mutates
|
|
845
884
|
row = this.anyToDBM(dbm, opt)
|
|
846
885
|
if (opt.ensureUniqueId && idWasGenerated) await this.ensureUniqueId(table, row)
|
|
@@ -858,7 +897,7 @@ export class CommonDao<
|
|
|
858
897
|
if (row === null) return dbm as Saved<DBM>
|
|
859
898
|
}
|
|
860
899
|
|
|
861
|
-
await this.cfg.db.saveBatch(table, [row], {
|
|
900
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [row], {
|
|
862
901
|
excludeFromIndexes,
|
|
863
902
|
assignGeneratedIds,
|
|
864
903
|
...opt,
|
|
@@ -949,7 +988,7 @@ export class CommonDao<
|
|
|
949
988
|
)
|
|
950
989
|
}
|
|
951
990
|
|
|
952
|
-
await this.cfg.db.saveBatch(table, rows, {
|
|
991
|
+
await (opt.tx || this.cfg.db).saveBatch(table, rows, {
|
|
953
992
|
excludeFromIndexes,
|
|
954
993
|
assignGeneratedIds,
|
|
955
994
|
...opt,
|
|
@@ -1160,7 +1199,7 @@ export class CommonDao<
|
|
|
1160
1199
|
const bm = await this.cfg.hooks!.beforeDBMToBM!(dbm)
|
|
1161
1200
|
|
|
1162
1201
|
// Validate/convert BM
|
|
1163
|
-
|
|
1202
|
+
|
|
1164
1203
|
return this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
|
|
1165
1204
|
}
|
|
1166
1205
|
|
|
@@ -1189,7 +1228,7 @@ export class CommonDao<
|
|
|
1189
1228
|
const dbm = { ...(await this.cfg.hooks!.beforeBMToDBM!(bm)) }
|
|
1190
1229
|
|
|
1191
1230
|
// Validate/convert DBM
|
|
1192
|
-
|
|
1231
|
+
|
|
1193
1232
|
return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
|
|
1194
1233
|
}
|
|
1195
1234
|
|
|
@@ -1248,14 +1287,14 @@ export class CommonDao<
|
|
|
1248
1287
|
*
|
|
1249
1288
|
* Does NOT mutate the object.
|
|
1250
1289
|
*/
|
|
1251
|
-
validateAndConvert<
|
|
1252
|
-
obj: Partial<
|
|
1253
|
-
schema: ObjectSchema<
|
|
1290
|
+
validateAndConvert<T>(
|
|
1291
|
+
obj: Partial<T>,
|
|
1292
|
+
schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined,
|
|
1254
1293
|
modelType: DBModelType,
|
|
1255
1294
|
opt: CommonDaoOptions = {},
|
|
1256
|
-
):
|
|
1295
|
+
): any {
|
|
1257
1296
|
// `raw` option completely bypasses any processing
|
|
1258
|
-
if (opt.raw) return obj as any
|
|
1297
|
+
if (opt.raw) return obj as any
|
|
1259
1298
|
|
|
1260
1299
|
// Kirill 2021-10-18: I realized that there's little reason to keep removing null values
|
|
1261
1300
|
// So, from now on we'll preserve them
|
|
@@ -1274,31 +1313,31 @@ export class CommonDao<
|
|
|
1274
1313
|
|
|
1275
1314
|
// Pre-validation hooks
|
|
1276
1315
|
if (modelType === DBModelType.DBM) {
|
|
1277
|
-
obj = this.cfg.hooks!.beforeDBMValidate!(obj as any) as
|
|
1316
|
+
obj = this.cfg.hooks!.beforeDBMValidate!(obj as any) as T
|
|
1278
1317
|
}
|
|
1279
1318
|
|
|
1280
1319
|
// Return as is if no schema is passed or if `skipConversion` is set
|
|
1281
1320
|
if (!schema || opt.skipConversion) {
|
|
1282
|
-
return obj
|
|
1321
|
+
return obj
|
|
1283
1322
|
}
|
|
1284
1323
|
|
|
1285
1324
|
// This will Convert and Validate
|
|
1286
1325
|
const table = opt.table || this.cfg.table
|
|
1287
1326
|
const objectName = table + (modelType || '')
|
|
1288
1327
|
|
|
1289
|
-
let error: JoiValidationError | AjvValidationError | ZodValidationError<
|
|
1328
|
+
let error: JoiValidationError | AjvValidationError | ZodValidationError<T> | undefined
|
|
1290
1329
|
let convertedValue: any
|
|
1291
1330
|
|
|
1292
1331
|
if (schema instanceof ZodSchema) {
|
|
1293
1332
|
// Zod schema
|
|
1294
|
-
const vr = zSafeValidate(obj as
|
|
1333
|
+
const vr = zSafeValidate(obj as T, schema)
|
|
1295
1334
|
error = vr.error
|
|
1296
1335
|
convertedValue = vr.data
|
|
1297
1336
|
} else if (schema instanceof AjvSchema) {
|
|
1298
1337
|
// Ajv schema
|
|
1299
1338
|
convertedValue = obj // because Ajv mutates original object
|
|
1300
1339
|
|
|
1301
|
-
error = schema.getValidationError(obj as
|
|
1340
|
+
error = schema.getValidationError(obj as T, {
|
|
1302
1341
|
objectName,
|
|
1303
1342
|
})
|
|
1304
1343
|
} else {
|
|
@@ -1334,20 +1373,24 @@ export class CommonDao<
|
|
|
1334
1373
|
await this.cfg.db.ping()
|
|
1335
1374
|
}
|
|
1336
1375
|
|
|
1337
|
-
async runInTransaction(
|
|
1338
|
-
fn: CommonDaoTransactionFn
|
|
1376
|
+
async runInTransaction<T = void>(
|
|
1377
|
+
fn: CommonDaoTransactionFn<T>,
|
|
1339
1378
|
opt?: CommonDBTransactionOptions,
|
|
1340
|
-
): Promise<
|
|
1379
|
+
): Promise<T> {
|
|
1380
|
+
let r: T
|
|
1381
|
+
|
|
1341
1382
|
await this.cfg.db.runInTransaction(async tx => {
|
|
1342
1383
|
const daoTx = new CommonDaoTransaction(tx, this.cfg.logger!)
|
|
1343
1384
|
|
|
1344
1385
|
try {
|
|
1345
|
-
await fn(daoTx)
|
|
1386
|
+
r = await fn(daoTx)
|
|
1346
1387
|
} catch (err) {
|
|
1347
1388
|
await daoTx.rollback() // graceful rollback that "never throws"
|
|
1348
1389
|
throw err
|
|
1349
1390
|
}
|
|
1350
1391
|
}, opt)
|
|
1392
|
+
|
|
1393
|
+
return r!
|
|
1351
1394
|
}
|
|
1352
1395
|
|
|
1353
1396
|
protected logResult(started: number, op: string, res: any, table: string): void {
|
|
@@ -1412,7 +1455,7 @@ export class CommonDao<
|
|
|
1412
1455
|
*
|
|
1413
1456
|
* Transaction is rolled back when the function returns rejected Promise (aka "throws").
|
|
1414
1457
|
*/
|
|
1415
|
-
export type CommonDaoTransactionFn = (tx: CommonDaoTransaction) => Promise<
|
|
1458
|
+
export type CommonDaoTransactionFn<T = void> = (tx: CommonDaoTransaction) => Promise<T>
|
|
1416
1459
|
|
|
1417
1460
|
/**
|
|
1418
1461
|
* Transaction context.
|
|
@@ -1420,7 +1463,7 @@ export type CommonDaoTransactionFn = (tx: CommonDaoTransaction) => Promise<void>
|
|
|
1420
1463
|
*/
|
|
1421
1464
|
export class CommonDaoTransaction {
|
|
1422
1465
|
constructor(
|
|
1423
|
-
|
|
1466
|
+
public tx: DBTransaction,
|
|
1424
1467
|
private logger: CommonLogger,
|
|
1425
1468
|
) {}
|
|
1426
1469
|
|
|
@@ -1441,8 +1484,7 @@ export class CommonDaoTransaction {
|
|
|
1441
1484
|
id?: string | null,
|
|
1442
1485
|
opt?: CommonDaoOptions,
|
|
1443
1486
|
): Promise<Saved<BM> | null> {
|
|
1444
|
-
|
|
1445
|
-
return (await this.getByIds(dao, [id], opt))[0] || null
|
|
1487
|
+
return await dao.getById(id, { ...opt, tx: this.tx })
|
|
1446
1488
|
}
|
|
1447
1489
|
|
|
1448
1490
|
async getByIds<BM extends PartialObjectWithId, DBM extends PartialObjectWithId>(
|
package/src/query/dbQuery.ts
CHANGED
|
@@ -297,7 +297,7 @@ export class RunnableDBQuery<
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
async streamQueryAsDBMForEach(
|
|
300
|
-
mapper: AsyncMapper<DBM
|
|
300
|
+
mapper: AsyncMapper<Saved<DBM>, void>,
|
|
301
301
|
opt?: CommonDaoStreamForEachOptions<DBM>,
|
|
302
302
|
): Promise<void> {
|
|
303
303
|
await this.dao.streamQueryAsDBMForEach(this, mapper, opt)
|
|
@@ -307,7 +307,7 @@ export class RunnableDBQuery<
|
|
|
307
307
|
return this.dao.streamQuery(this, opt)
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
-
streamQueryAsDBM(opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<DBM
|
|
310
|
+
streamQueryAsDBM(opt?: CommonDaoStreamOptions<DBM>): ReadableTyped<Saved<DBM>> {
|
|
311
311
|
return this.dao.streamQueryAsDBM(this, opt)
|
|
312
312
|
}
|
|
313
313
|
|