@naturalcycles/db-lib 10.0.2 → 10.1.1
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/adapter/cachedb/cache.db.d.ts +5 -5
- package/dist/adapter/cachedb/cache.db.js +8 -12
- package/dist/adapter/cachedb/cache.db.model.d.ts +2 -2
- package/dist/adapter/cachedb/cache.db.model.js +1 -2
- package/dist/adapter/cachedb/index.d.ts +2 -2
- package/dist/adapter/cachedb/index.js +2 -5
- package/dist/adapter/file/file.db.d.ts +5 -6
- package/dist/adapter/file/file.db.js +25 -27
- package/dist/adapter/file/file.db.model.d.ts +2 -2
- package/dist/adapter/file/file.db.model.js +1 -2
- package/dist/adapter/file/inMemory.persistence.plugin.d.ts +2 -2
- package/dist/adapter/file/inMemory.persistence.plugin.js +3 -7
- package/dist/adapter/file/index.d.ts +3 -3
- package/dist/adapter/file/index.js +3 -7
- package/dist/adapter/file/localFile.persistence.plugin.d.ts +2 -2
- package/dist/adapter/file/localFile.persistence.plugin.js +11 -15
- package/dist/adapter/file/noop.persistence.plugin.d.ts +2 -2
- package/dist/adapter/file/noop.persistence.plugin.js +1 -5
- package/dist/adapter/inmemory/inMemory.db.d.ts +5 -4
- package/dist/adapter/inmemory/inMemory.db.js +43 -41
- package/dist/adapter/inmemory/inMemoryKeyValueDB.d.ts +2 -2
- package/dist/adapter/inmemory/inMemoryKeyValueDB.js +7 -11
- package/dist/adapter/inmemory/queryInMemory.d.ts +1 -1
- package/dist/adapter/inmemory/queryInMemory.js +4 -7
- package/dist/base.common.db.d.ts +5 -4
- package/dist/base.common.db.js +8 -9
- package/dist/cnst.js +2 -5
- package/dist/common.db.d.ts +8 -2
- package/dist/common.db.js +3 -6
- package/dist/commondao/common.dao.d.ts +11 -4
- package/dist/commondao/common.dao.js +120 -113
- package/dist/commondao/common.dao.model.d.ts +2 -2
- package/dist/commondao/common.dao.model.js +2 -5
- package/dist/db.model.d.ts +6 -1
- package/dist/db.model.js +4 -7
- package/dist/index.d.ts +17 -17
- package/dist/index.js +17 -20
- package/dist/kv/commonKeyValueDB.d.ts +1 -1
- package/dist/kv/commonKeyValueDB.js +1 -4
- package/dist/kv/commonKeyValueDao.d.ts +3 -3
- package/dist/kv/commonKeyValueDao.js +10 -14
- package/dist/kv/commonKeyValueDaoMemoCache.d.ts +1 -1
- package/dist/kv/commonKeyValueDaoMemoCache.js +4 -8
- package/dist/model.util.js +9 -15
- package/dist/pipeline/dbPipelineBackup.d.ts +2 -2
- package/dist/pipeline/dbPipelineBackup.js +27 -30
- package/dist/pipeline/dbPipelineCopy.d.ts +2 -2
- package/dist/pipeline/dbPipelineCopy.js +20 -23
- package/dist/pipeline/dbPipelineRestore.d.ts +2 -2
- package/dist/pipeline/dbPipelineRestore.js +27 -30
- package/dist/query/dbQuery.d.ts +3 -3
- package/dist/query/dbQuery.js +7 -12
- package/dist/testing/{dbTest.d.ts → commonDBTest.d.ts} +1 -1
- package/dist/testing/{dbTest.js → commonDBTest.js} +81 -62
- package/dist/testing/commonDaoTest.d.ts +3 -0
- package/dist/testing/{daoTest.js → commonDaoTest.js} +101 -38
- package/dist/testing/index.d.ts +7 -7
- package/dist/testing/index.js +6 -20
- package/dist/testing/keyValueDBTest.d.ts +1 -1
- package/dist/testing/keyValueDBTest.js +28 -31
- package/dist/testing/keyValueDaoTest.d.ts +1 -1
- package/dist/testing/keyValueDaoTest.js +8 -11
- package/dist/testing/test.model.js +30 -37
- package/dist/testing/timeSeriesTest.util.d.ts +1 -1
- package/dist/testing/timeSeriesTest.util.js +3 -6
- package/dist/timeseries/commonTimeSeriesDao.d.ts +1 -1
- package/dist/timeseries/commonTimeSeriesDao.js +5 -9
- package/dist/timeseries/timeSeries.model.d.ts +1 -1
- package/dist/timeseries/timeSeries.model.js +1 -2
- package/dist/transaction/dbTransaction.util.d.ts +3 -2
- package/dist/transaction/dbTransaction.util.js +2 -5
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +25 -28
- package/package.json +3 -3
- package/src/adapter/cachedb/cache.db.model.ts +2 -2
- package/src/adapter/cachedb/cache.db.ts +6 -6
- package/src/adapter/cachedb/index.ts +2 -2
- package/src/adapter/file/file.db.model.ts +2 -2
- package/src/adapter/file/file.db.ts +8 -6
- package/src/adapter/file/inMemory.persistence.plugin.ts +2 -2
- package/src/adapter/file/index.ts +3 -3
- package/src/adapter/file/localFile.persistence.plugin.ts +2 -2
- package/src/adapter/file/noop.persistence.plugin.ts +2 -2
- package/src/adapter/inmemory/inMemory.db.ts +15 -10
- package/src/adapter/inmemory/inMemoryKeyValueDB.ts +7 -3
- package/src/adapter/inmemory/queryInMemory.ts +1 -1
- package/src/base.common.db.ts +10 -5
- package/src/common.db.ts +10 -2
- package/src/commondao/common.dao.model.ts +2 -2
- package/src/commondao/common.dao.ts +20 -6
- package/src/db.model.ts +7 -1
- package/src/index.ts +17 -17
- package/src/kv/commonKeyValueDB.ts +1 -1
- package/src/kv/commonKeyValueDao.ts +3 -3
- package/src/kv/commonKeyValueDaoMemoCache.ts +1 -1
- package/src/pipeline/dbPipelineBackup.ts +2 -2
- package/src/pipeline/dbPipelineCopy.ts +3 -3
- package/src/pipeline/dbPipelineRestore.ts +2 -2
- package/src/query/dbQuery.ts +3 -3
- package/src/testing/{dbTest.ts → commonDBTest.ts} +34 -6
- package/src/testing/{daoTest.ts → commonDaoTest.ts} +89 -11
- package/src/testing/index.ts +7 -7
- package/src/testing/keyValueDBTest.ts +2 -2
- package/src/testing/keyValueDaoTest.ts +3 -3
- package/src/testing/timeSeriesTest.util.ts +1 -1
- package/src/timeseries/commonTimeSeriesDao.ts +2 -2
- package/src/timeseries/timeSeries.model.ts +1 -1
- package/src/transaction/dbTransaction.util.ts +3 -2
- package/src/validation/index.ts +8 -3
- package/dist/testing/daoTest.d.ts +0 -3
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const cnst_1 = require("../cnst");
|
|
8
|
-
const dbQuery_1 = require("../query/dbQuery");
|
|
9
|
-
const common_dao_model_1 = require("./common.dao.model");
|
|
1
|
+
import { _assert, _deepCopy, _deepJsonEquals, _filterUndefinedValues, _isTruthy, _objectAssignExact, _passthroughPredicate, _since, _truncate, _typeCast, _uniqBy, AppError, ErrorMode, localTime, pMap, SKIP, } from '@naturalcycles/js-lib';
|
|
2
|
+
import { ZodSchema, zSafeValidate } from '@naturalcycles/js-lib/dist/zod/index.js';
|
|
3
|
+
import { _pipeline, AjvSchema, getValidationResult, stringId, transformChunk, transformLogProgress, transformMap, transformNoOp, writableVoid, } from '@naturalcycles/nodejs-lib';
|
|
4
|
+
import { DBLibError } from '../cnst.js';
|
|
5
|
+
import { RunnableDBQuery } from '../query/dbQuery.js';
|
|
6
|
+
import { CommonDaoLogLevel } from './common.dao.model.js';
|
|
10
7
|
const isGAE = !!process.env['GAE_INSTANCE'];
|
|
11
8
|
const isCI = !!process.env['CI'];
|
|
12
9
|
/**
|
|
@@ -16,7 +13,7 @@ const isCI = !!process.env['CI'];
|
|
|
16
13
|
* BM = Backend model (optimized for API access)
|
|
17
14
|
* TM = Transport model (optimized to be sent over the wire)
|
|
18
15
|
*/
|
|
19
|
-
class CommonDao {
|
|
16
|
+
export class CommonDao {
|
|
20
17
|
cfg;
|
|
21
18
|
constructor(cfg) {
|
|
22
19
|
this.cfg = cfg;
|
|
@@ -24,7 +21,7 @@ class CommonDao {
|
|
|
24
21
|
// Default is to NOT log in AppEngine and in CI,
|
|
25
22
|
// otherwise to log Operations
|
|
26
23
|
// e.g in Dev (local machine), Test - it will log operations (useful for debugging)
|
|
27
|
-
logLevel: isGAE || isCI ?
|
|
24
|
+
logLevel: isGAE || isCI ? CommonDaoLogLevel.NONE : CommonDaoLogLevel.OPERATIONS,
|
|
28
25
|
generateId: true,
|
|
29
26
|
assignGeneratedIds: false,
|
|
30
27
|
useCreatedProperty: true,
|
|
@@ -42,7 +39,7 @@ class CommonDao {
|
|
|
42
39
|
},
|
|
43
40
|
};
|
|
44
41
|
if (this.cfg.generateId) {
|
|
45
|
-
this.cfg.hooks.createRandomId ||= () =>
|
|
42
|
+
this.cfg.hooks.createRandomId ||= () => stringId();
|
|
46
43
|
}
|
|
47
44
|
else {
|
|
48
45
|
delete this.cfg.hooks.createRandomId;
|
|
@@ -98,12 +95,12 @@ class CommonDao {
|
|
|
98
95
|
async getByIds(ids, opt = {}) {
|
|
99
96
|
if (!ids.length)
|
|
100
97
|
return [];
|
|
101
|
-
const op = `getByIds ${ids.length} id(s) (${
|
|
98
|
+
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`;
|
|
102
99
|
const table = opt.table || this.cfg.table;
|
|
103
100
|
const started = this.logStarted(op, table);
|
|
104
101
|
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids, opt);
|
|
105
102
|
if (this.cfg.hooks.afterLoad && dbms.length) {
|
|
106
|
-
dbms = (await
|
|
103
|
+
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
107
104
|
}
|
|
108
105
|
const bms = await this.dbmsToBM(dbms, opt);
|
|
109
106
|
this.logResult(started, op, bms, table);
|
|
@@ -112,12 +109,12 @@ class CommonDao {
|
|
|
112
109
|
async getByIdsAsDBM(ids, opt = {}) {
|
|
113
110
|
if (!ids.length)
|
|
114
111
|
return [];
|
|
115
|
-
const op = `getByIdsAsDBM ${ids.length} id(s) (${
|
|
112
|
+
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`;
|
|
116
113
|
const table = opt.table || this.cfg.table;
|
|
117
114
|
const started = this.logStarted(op, table);
|
|
118
115
|
let dbms = await (opt.tx || this.cfg.db).getByIds(table, ids, opt);
|
|
119
116
|
if (this.cfg.hooks.afterLoad && dbms.length) {
|
|
120
|
-
dbms = (await
|
|
117
|
+
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
121
118
|
}
|
|
122
119
|
this.logResult(started, op, dbms, table);
|
|
123
120
|
return dbms;
|
|
@@ -138,7 +135,7 @@ class CommonDao {
|
|
|
138
135
|
}
|
|
139
136
|
throwRequiredError(id, opt) {
|
|
140
137
|
const table = opt.table || this.cfg.table;
|
|
141
|
-
throw new
|
|
138
|
+
throw new AppError(`DB row required, but not found in ${table}`, {
|
|
142
139
|
table,
|
|
143
140
|
id,
|
|
144
141
|
});
|
|
@@ -148,7 +145,7 @@ class CommonDao {
|
|
|
148
145
|
*/
|
|
149
146
|
requireWriteAccess() {
|
|
150
147
|
if (this.cfg.readOnly) {
|
|
151
|
-
throw new
|
|
148
|
+
throw new AppError(DBLibError.DAO_IS_READ_ONLY, {
|
|
152
149
|
table: this.cfg.table,
|
|
153
150
|
});
|
|
154
151
|
}
|
|
@@ -158,7 +155,7 @@ class CommonDao {
|
|
|
158
155
|
*/
|
|
159
156
|
requireObjectMutability(opt) {
|
|
160
157
|
if (this.cfg.immutable && !opt.allowMutability) {
|
|
161
|
-
throw new
|
|
158
|
+
throw new AppError(DBLibError.OBJECT_IS_IMMUTABLE, {
|
|
162
159
|
table: this.cfg.table,
|
|
163
160
|
});
|
|
164
161
|
}
|
|
@@ -167,7 +164,7 @@ class CommonDao {
|
|
|
167
164
|
// todo: retry N times
|
|
168
165
|
const existing = await this.cfg.db.getByIds(table, [dbm.id]);
|
|
169
166
|
if (existing.length) {
|
|
170
|
-
throw new
|
|
167
|
+
throw new AppError(DBLibError.NON_UNIQUE_ID, {
|
|
171
168
|
table,
|
|
172
169
|
ids: existing.map(i => i.id),
|
|
173
170
|
});
|
|
@@ -188,14 +185,14 @@ class CommonDao {
|
|
|
188
185
|
* Pass `table` to override table
|
|
189
186
|
*/
|
|
190
187
|
query(table) {
|
|
191
|
-
return new
|
|
188
|
+
return new RunnableDBQuery(this, table);
|
|
192
189
|
}
|
|
193
190
|
async runQuery(q, opt) {
|
|
194
191
|
const { rows } = await this.runQueryExtended(q, opt);
|
|
195
192
|
return rows;
|
|
196
193
|
}
|
|
197
194
|
async runQuerySingleColumn(q, opt) {
|
|
198
|
-
|
|
195
|
+
_assert(q._selectedFieldNames?.length === 1, `runQuerySingleColumn requires exactly 1 column to be selected: ${q.pretty()}`);
|
|
199
196
|
const col = q._selectedFieldNames[0];
|
|
200
197
|
const { rows } = await this.runQueryExtended(q, opt);
|
|
201
198
|
return rows.map((r) => r[col]);
|
|
@@ -206,8 +203,8 @@ class CommonDao {
|
|
|
206
203
|
* Order is not guaranteed, as queries run in parallel.
|
|
207
204
|
*/
|
|
208
205
|
async runUnionQueries(queries, opt) {
|
|
209
|
-
const results = (await
|
|
210
|
-
return
|
|
206
|
+
const results = (await pMap(queries, async (q) => (await this.runQueryExtended(q, opt)).rows)).flat();
|
|
207
|
+
return _uniqBy(results, r => r.id);
|
|
211
208
|
}
|
|
212
209
|
async runQueryExtended(q, opt = {}) {
|
|
213
210
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
@@ -217,7 +214,7 @@ class CommonDao {
|
|
|
217
214
|
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
218
215
|
const partialQuery = !!q._selectedFieldNames;
|
|
219
216
|
if (this.cfg.hooks.afterLoad && rows.length) {
|
|
220
|
-
rows = (await
|
|
217
|
+
rows = (await pMap(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
221
218
|
}
|
|
222
219
|
const bms = partialQuery ? rows : await this.dbmsToBM(rows, opt);
|
|
223
220
|
this.logResult(started, op, bms, q.table);
|
|
@@ -237,7 +234,7 @@ class CommonDao {
|
|
|
237
234
|
const started = this.logStarted(op, q.table);
|
|
238
235
|
let { rows, ...queryResult } = await this.cfg.db.runQuery(q, opt);
|
|
239
236
|
if (this.cfg.hooks.afterLoad && rows.length) {
|
|
240
|
-
rows = (await
|
|
237
|
+
rows = (await pMap(rows, async (dbm) => await this.cfg.hooks.afterLoad(dbm))).filter(_isTruthy);
|
|
241
238
|
}
|
|
242
239
|
const partialQuery = !!q._selectedFieldNames;
|
|
243
240
|
const dbms = partialQuery ? rows : this.anyToDBMs(rows, opt);
|
|
@@ -250,8 +247,8 @@ class CommonDao {
|
|
|
250
247
|
const op = `runQueryCount(${q.pretty()})`;
|
|
251
248
|
const started = this.logStarted(op, q.table);
|
|
252
249
|
const count = await this.cfg.db.runQueryCount(q, opt);
|
|
253
|
-
if (this.cfg.logLevel >=
|
|
254
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${
|
|
250
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
251
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
255
252
|
}
|
|
256
253
|
return count;
|
|
257
254
|
}
|
|
@@ -259,78 +256,78 @@ class CommonDao {
|
|
|
259
256
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
260
257
|
q.table = opt.table || q.table;
|
|
261
258
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
262
|
-
opt.errorMode ||=
|
|
259
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
263
260
|
const partialQuery = !!q._selectedFieldNames;
|
|
264
261
|
const op = `streamQueryForEach(${q.pretty()})`;
|
|
265
262
|
const started = this.logStarted(op, q.table, true);
|
|
266
263
|
let count = 0;
|
|
267
|
-
await
|
|
264
|
+
await _pipeline([
|
|
268
265
|
this.cfg.db.streamQuery(q, opt),
|
|
269
|
-
|
|
266
|
+
transformMap(async (dbm) => {
|
|
270
267
|
count++;
|
|
271
268
|
if (partialQuery)
|
|
272
269
|
return dbm;
|
|
273
270
|
if (this.cfg.hooks.afterLoad) {
|
|
274
271
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
275
272
|
if (dbm === null)
|
|
276
|
-
return
|
|
273
|
+
return SKIP;
|
|
277
274
|
}
|
|
278
275
|
return await this.dbmToBM(dbm, opt);
|
|
279
276
|
}, {
|
|
280
277
|
errorMode: opt.errorMode,
|
|
281
278
|
}),
|
|
282
|
-
|
|
279
|
+
transformMap(mapper, {
|
|
283
280
|
...opt,
|
|
284
|
-
predicate:
|
|
281
|
+
predicate: _passthroughPredicate, // to be able to logProgress
|
|
285
282
|
}),
|
|
286
283
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
287
|
-
|
|
284
|
+
transformLogProgress({
|
|
288
285
|
metric: q.table,
|
|
289
286
|
...opt,
|
|
290
287
|
}),
|
|
291
|
-
|
|
288
|
+
writableVoid(),
|
|
292
289
|
]);
|
|
293
|
-
if (this.cfg.logLevel >=
|
|
294
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${
|
|
290
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
291
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
295
292
|
}
|
|
296
293
|
}
|
|
297
294
|
async streamQueryAsDBMForEach(q, mapper, opt = {}) {
|
|
298
295
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
299
296
|
q.table = opt.table || q.table;
|
|
300
297
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
301
|
-
opt.errorMode ||=
|
|
298
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
302
299
|
const partialQuery = !!q._selectedFieldNames;
|
|
303
300
|
const op = `streamQueryAsDBMForEach(${q.pretty()})`;
|
|
304
301
|
const started = this.logStarted(op, q.table, true);
|
|
305
302
|
let count = 0;
|
|
306
|
-
await
|
|
303
|
+
await _pipeline([
|
|
307
304
|
this.cfg.db.streamQuery(q, opt),
|
|
308
|
-
|
|
305
|
+
transformMap(async (dbm) => {
|
|
309
306
|
count++;
|
|
310
307
|
if (partialQuery)
|
|
311
308
|
return dbm;
|
|
312
309
|
if (this.cfg.hooks.afterLoad) {
|
|
313
310
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
314
311
|
if (dbm === null)
|
|
315
|
-
return
|
|
312
|
+
return SKIP;
|
|
316
313
|
}
|
|
317
314
|
return this.anyToDBM(dbm, opt);
|
|
318
315
|
}, {
|
|
319
316
|
errorMode: opt.errorMode,
|
|
320
317
|
}),
|
|
321
|
-
|
|
318
|
+
transformMap(mapper, {
|
|
322
319
|
...opt,
|
|
323
|
-
predicate:
|
|
320
|
+
predicate: _passthroughPredicate, // to be able to logProgress
|
|
324
321
|
}),
|
|
325
322
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
326
|
-
|
|
323
|
+
transformLogProgress({
|
|
327
324
|
metric: q.table,
|
|
328
325
|
...opt,
|
|
329
326
|
}),
|
|
330
|
-
|
|
327
|
+
writableVoid(),
|
|
331
328
|
]);
|
|
332
|
-
if (this.cfg.logLevel >=
|
|
333
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${
|
|
329
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
330
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} row(s) in ${_since(started)}`);
|
|
334
331
|
}
|
|
335
332
|
}
|
|
336
333
|
/**
|
|
@@ -340,18 +337,18 @@ class CommonDao {
|
|
|
340
337
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
341
338
|
q.table = opt.table || q.table;
|
|
342
339
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
343
|
-
opt.errorMode ||=
|
|
340
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
344
341
|
const partialQuery = !!q._selectedFieldNames;
|
|
345
342
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
346
343
|
if (partialQuery)
|
|
347
344
|
return stream;
|
|
348
345
|
return stream
|
|
349
346
|
.on('error', err => stream.emit('error', err))
|
|
350
|
-
.pipe(
|
|
347
|
+
.pipe(transformMap(async (dbm) => {
|
|
351
348
|
if (this.cfg.hooks.afterLoad) {
|
|
352
349
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
353
350
|
if (dbm === null)
|
|
354
|
-
return
|
|
351
|
+
return SKIP;
|
|
355
352
|
}
|
|
356
353
|
return this.anyToDBM(dbm, opt);
|
|
357
354
|
}, {
|
|
@@ -371,7 +368,7 @@ class CommonDao {
|
|
|
371
368
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
372
369
|
q.table = opt.table || q.table;
|
|
373
370
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
374
|
-
opt.errorMode ||=
|
|
371
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
375
372
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
376
373
|
const partialQuery = !!q._selectedFieldNames;
|
|
377
374
|
if (partialQuery)
|
|
@@ -392,11 +389,11 @@ class CommonDao {
|
|
|
392
389
|
// .pipe(transformMap<any, DBM>(dbm => this.anyToDBM(dbm, opt), safeOpt))
|
|
393
390
|
// .pipe(transformMap<DBM, BM>(dbm => this.dbmToBM(dbm, opt), safeOpt))
|
|
394
391
|
.on('error', err => stream.emit('error', err))
|
|
395
|
-
.pipe(
|
|
392
|
+
.pipe(transformMap(async (dbm) => {
|
|
396
393
|
if (this.cfg.hooks.afterLoad) {
|
|
397
394
|
dbm = (await this.cfg.hooks.afterLoad(dbm));
|
|
398
395
|
if (dbm === null)
|
|
399
|
-
return
|
|
396
|
+
return SKIP;
|
|
400
397
|
}
|
|
401
398
|
return await this.dbmToBM(dbm, opt);
|
|
402
399
|
}, {
|
|
@@ -406,7 +403,7 @@ class CommonDao {
|
|
|
406
403
|
// but not applying it now for perf reasons
|
|
407
404
|
// UPD: applying, to be compliant with `.toArray()`, etc.
|
|
408
405
|
.on('error', err => stream.emit('error', err))
|
|
409
|
-
.pipe(
|
|
406
|
+
.pipe(transformNoOp()));
|
|
410
407
|
}
|
|
411
408
|
async queryIds(q, opt = {}) {
|
|
412
409
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
@@ -417,7 +414,7 @@ class CommonDao {
|
|
|
417
414
|
streamQueryIds(q, opt = {}) {
|
|
418
415
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
419
416
|
q.table = opt.table || q.table;
|
|
420
|
-
opt.errorMode ||=
|
|
417
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
421
418
|
// Experimental: using `.map()`
|
|
422
419
|
const stream = this.cfg.db
|
|
423
420
|
.streamQuery(q.select(['id']), opt)
|
|
@@ -436,28 +433,28 @@ class CommonDao {
|
|
|
436
433
|
async streamQueryIdsForEach(q, mapper, opt = {}) {
|
|
437
434
|
this.validateQueryIndexes(q); // throws if query uses `excludeFromIndexes` property
|
|
438
435
|
q.table = opt.table || q.table;
|
|
439
|
-
opt.errorMode ||=
|
|
436
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
440
437
|
const op = `streamQueryIdsForEach(${q.pretty()})`;
|
|
441
438
|
const started = this.logStarted(op, q.table, true);
|
|
442
439
|
let count = 0;
|
|
443
|
-
await
|
|
440
|
+
await _pipeline([
|
|
444
441
|
this.cfg.db.streamQuery(q.select(['id']), opt).map(r => {
|
|
445
442
|
count++;
|
|
446
443
|
return r.id;
|
|
447
444
|
}),
|
|
448
|
-
|
|
445
|
+
transformMap(mapper, {
|
|
449
446
|
...opt,
|
|
450
|
-
predicate:
|
|
447
|
+
predicate: _passthroughPredicate,
|
|
451
448
|
}),
|
|
452
449
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
453
|
-
|
|
450
|
+
transformLogProgress({
|
|
454
451
|
metric: q.table,
|
|
455
452
|
...opt,
|
|
456
453
|
}),
|
|
457
|
-
|
|
454
|
+
writableVoid(),
|
|
458
455
|
]);
|
|
459
|
-
if (this.cfg.logLevel >=
|
|
460
|
-
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} id(s) in ${
|
|
456
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.OPERATIONS) {
|
|
457
|
+
this.cfg.logger?.log(`<< ${q.table}.${op}: ${count} id(s) in ${_since(started)}`);
|
|
461
458
|
}
|
|
462
459
|
}
|
|
463
460
|
/**
|
|
@@ -465,7 +462,7 @@ class CommonDao {
|
|
|
465
462
|
* "Returns", just to have a type of "Saved"
|
|
466
463
|
*/
|
|
467
464
|
assignIdCreatedUpdated(obj, opt = {}) {
|
|
468
|
-
const now =
|
|
465
|
+
const now = localTime.nowUnix();
|
|
469
466
|
if (this.cfg.useCreatedProperty) {
|
|
470
467
|
obj.created ||= obj.updated || now;
|
|
471
468
|
}
|
|
@@ -504,14 +501,14 @@ class CommonDao {
|
|
|
504
501
|
});
|
|
505
502
|
if (loaded) {
|
|
506
503
|
patched = { ...loaded, ...patch };
|
|
507
|
-
if (
|
|
504
|
+
if (_deepJsonEquals(loaded, patched)) {
|
|
508
505
|
// Skipping the save operation, as data is the same
|
|
509
506
|
return patched;
|
|
510
507
|
}
|
|
511
508
|
}
|
|
512
509
|
else {
|
|
513
510
|
const table = opt.table || this.cfg.table;
|
|
514
|
-
|
|
511
|
+
_assert(opt.createIfMissing, `DB row required, but not found in ${table}`, {
|
|
515
512
|
id,
|
|
516
513
|
table,
|
|
517
514
|
});
|
|
@@ -545,7 +542,7 @@ class CommonDao {
|
|
|
545
542
|
...bm,
|
|
546
543
|
...patch,
|
|
547
544
|
};
|
|
548
|
-
if (
|
|
545
|
+
if (_deepJsonEquals(bm, patched)) {
|
|
549
546
|
// Skipping the save operation, as data is the same
|
|
550
547
|
return bm;
|
|
551
548
|
}
|
|
@@ -563,8 +560,8 @@ class CommonDao {
|
|
|
563
560
|
...patch,
|
|
564
561
|
};
|
|
565
562
|
// Make `bm` exactly the same as `loadedWithPatch`
|
|
566
|
-
|
|
567
|
-
if (
|
|
563
|
+
_objectAssignExact(bm, loadedWithPatch);
|
|
564
|
+
if (_deepJsonEquals(loaded, loadedWithPatch)) {
|
|
568
565
|
// Skipping the save operation, as data is the same
|
|
569
566
|
return bm;
|
|
570
567
|
}
|
|
@@ -589,14 +586,14 @@ class CommonDao {
|
|
|
589
586
|
// which should be removed post-validation, but it breaks the "equality check"
|
|
590
587
|
// Post-validation the equality check should work as intended
|
|
591
588
|
const convertedBM = this.validateAndConvert(bm, this.cfg.bmSchema, 'save', opt);
|
|
592
|
-
if (
|
|
589
|
+
if (_deepJsonEquals(convertedBM, opt.skipIfEquals)) {
|
|
593
590
|
// Skipping the save operation
|
|
594
591
|
return bm;
|
|
595
592
|
}
|
|
596
593
|
}
|
|
597
594
|
const idWasGenerated = !bm.id && this.cfg.generateId;
|
|
598
595
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
599
|
-
|
|
596
|
+
_typeCast(bm);
|
|
600
597
|
let dbm = await this.bmToDBM(bm, opt); // validates BM
|
|
601
598
|
if (this.cfg.hooks.beforeSave) {
|
|
602
599
|
dbm = (await this.cfg.hooks.beforeSave(dbm));
|
|
@@ -665,14 +662,14 @@ class CommonDao {
|
|
|
665
662
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt));
|
|
666
663
|
let dbms = await this.bmsToDBM(bms, opt);
|
|
667
664
|
if (this.cfg.hooks.beforeSave && dbms.length) {
|
|
668
|
-
dbms = (await
|
|
665
|
+
dbms = (await pMap(dbms, async (dbm) => await this.cfg.hooks.beforeSave(dbm))).filter(_isTruthy);
|
|
669
666
|
}
|
|
670
667
|
if (opt.ensureUniqueId)
|
|
671
|
-
throw new
|
|
668
|
+
throw new AppError('ensureUniqueId is not supported in saveBatch');
|
|
672
669
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
673
670
|
opt = { ...opt, saveMethod: 'insert' };
|
|
674
671
|
}
|
|
675
|
-
const op = `saveBatch ${dbms.length} row(s) (${
|
|
672
|
+
const op = `saveBatch ${dbms.length} row(s) (${_truncate(dbms
|
|
676
673
|
.slice(0, 10)
|
|
677
674
|
.map(bm => bm.id)
|
|
678
675
|
.join(', '), 50)})`;
|
|
@@ -698,11 +695,11 @@ class CommonDao {
|
|
|
698
695
|
dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt)); // mutates
|
|
699
696
|
let rows = this.anyToDBMs(dbms, opt);
|
|
700
697
|
if (opt.ensureUniqueId)
|
|
701
|
-
throw new
|
|
698
|
+
throw new AppError('ensureUniqueId is not supported in saveBatch');
|
|
702
699
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
703
700
|
opt = { ...opt, saveMethod: 'insert' };
|
|
704
701
|
}
|
|
705
|
-
const op = `saveBatchAsDBM ${rows.length} row(s) (${
|
|
702
|
+
const op = `saveBatchAsDBM ${rows.length} row(s) (${_truncate(rows
|
|
706
703
|
.slice(0, 10)
|
|
707
704
|
.map(bm => bm.id)
|
|
708
705
|
.join(', '), 50)})`;
|
|
@@ -710,7 +707,7 @@ class CommonDao {
|
|
|
710
707
|
const { excludeFromIndexes } = this.cfg;
|
|
711
708
|
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds;
|
|
712
709
|
if (this.cfg.hooks.beforeSave && rows.length) {
|
|
713
|
-
rows = (await
|
|
710
|
+
rows = (await pMap(rows, async (row) => await this.cfg.hooks.beforeSave(row))).filter(_isTruthy);
|
|
714
711
|
}
|
|
715
712
|
await (opt.tx || this.cfg.db).saveBatch(table, rows, {
|
|
716
713
|
excludeFromIndexes,
|
|
@@ -733,7 +730,7 @@ class CommonDao {
|
|
|
733
730
|
this.requireWriteAccess();
|
|
734
731
|
const table = opt.table || this.cfg.table;
|
|
735
732
|
opt.skipValidation ??= true;
|
|
736
|
-
opt.errorMode ||=
|
|
733
|
+
opt.errorMode ||= ErrorMode.SUPPRESS;
|
|
737
734
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
738
735
|
opt = { ...opt, saveMethod: 'insert' };
|
|
739
736
|
}
|
|
@@ -741,20 +738,20 @@ class CommonDao {
|
|
|
741
738
|
const { beforeSave } = this.cfg.hooks;
|
|
742
739
|
const { chunkSize = 500, chunkConcurrency = 32, errorMode } = opt;
|
|
743
740
|
return [
|
|
744
|
-
|
|
741
|
+
transformMap(async (bm) => {
|
|
745
742
|
this.assignIdCreatedUpdated(bm, opt); // mutates
|
|
746
743
|
let dbm = await this.bmToDBM(bm, opt);
|
|
747
744
|
if (beforeSave) {
|
|
748
745
|
dbm = (await beforeSave(dbm));
|
|
749
746
|
if (dbm === null)
|
|
750
|
-
return
|
|
747
|
+
return SKIP;
|
|
751
748
|
}
|
|
752
749
|
return dbm;
|
|
753
750
|
}, {
|
|
754
751
|
errorMode,
|
|
755
752
|
}),
|
|
756
|
-
|
|
757
|
-
|
|
753
|
+
transformChunk({ chunkSize }),
|
|
754
|
+
transformMap(async (batch) => {
|
|
758
755
|
await this.cfg.db.saveBatch(table, batch, {
|
|
759
756
|
...opt,
|
|
760
757
|
excludeFromIndexes,
|
|
@@ -765,13 +762,13 @@ class CommonDao {
|
|
|
765
762
|
errorMode,
|
|
766
763
|
flattenArrayOutput: true,
|
|
767
764
|
}),
|
|
768
|
-
|
|
765
|
+
transformLogProgress({
|
|
769
766
|
metric: 'saved',
|
|
770
767
|
...opt,
|
|
771
768
|
}),
|
|
772
769
|
// just to satisfy and simplify typings
|
|
773
770
|
// It's easier to return Transform[], rather than (Transform | Writable)[]
|
|
774
|
-
|
|
771
|
+
writableVoid(),
|
|
775
772
|
];
|
|
776
773
|
}
|
|
777
774
|
// DELETE
|
|
@@ -810,24 +807,24 @@ class CommonDao {
|
|
|
810
807
|
let deleted = 0;
|
|
811
808
|
if (opt.chunkSize) {
|
|
812
809
|
const { chunkSize, chunkConcurrency = 32 } = opt;
|
|
813
|
-
await
|
|
810
|
+
await _pipeline([
|
|
814
811
|
this.cfg.db.streamQuery(q.select(['id']), opt).map(r => r.id),
|
|
815
|
-
|
|
816
|
-
|
|
812
|
+
transformChunk({ chunkSize }),
|
|
813
|
+
transformMap(async (ids) => {
|
|
817
814
|
await this.cfg.db.deleteByIds(q.table, ids, opt);
|
|
818
815
|
deleted += ids.length;
|
|
819
816
|
}, {
|
|
820
|
-
predicate:
|
|
817
|
+
predicate: _passthroughPredicate,
|
|
821
818
|
concurrency: chunkConcurrency,
|
|
822
819
|
}),
|
|
823
820
|
// LogProgress should be AFTER the mapper, to be able to report correct stats
|
|
824
|
-
|
|
821
|
+
transformLogProgress({
|
|
825
822
|
metric: q.table,
|
|
826
823
|
logEvery: 2, // 500 * 2 === 1000
|
|
827
824
|
chunkSize,
|
|
828
825
|
...opt,
|
|
829
826
|
}),
|
|
830
|
-
|
|
827
|
+
writableVoid(),
|
|
831
828
|
]);
|
|
832
829
|
}
|
|
833
830
|
else {
|
|
@@ -899,7 +896,7 @@ class CommonDao {
|
|
|
899
896
|
return this.validateAndConvert(bm, this.cfg.bmSchema, 'load', opt);
|
|
900
897
|
}
|
|
901
898
|
async dbmsToBM(dbms, opt = {}) {
|
|
902
|
-
return await
|
|
899
|
+
return await pMap(dbms, async (dbm) => await this.dbmToBM(dbm, opt));
|
|
903
900
|
}
|
|
904
901
|
async bmToDBM(bm, opt) {
|
|
905
902
|
if (bm === undefined)
|
|
@@ -911,7 +908,7 @@ class CommonDao {
|
|
|
911
908
|
}
|
|
912
909
|
async bmsToDBM(bms, opt = {}) {
|
|
913
910
|
// try/catch?
|
|
914
|
-
return await
|
|
911
|
+
return await pMap(bms, async (bm) => await this.bmToDBM(bm, opt));
|
|
915
912
|
}
|
|
916
913
|
anyToDBM(dbm, opt = {}) {
|
|
917
914
|
if (!dbm)
|
|
@@ -947,7 +944,7 @@ class CommonDao {
|
|
|
947
944
|
// obj = _filterNullishValues(obj as any)
|
|
948
945
|
// We still filter `undefined` values here, because `beforeDBMToBM` can return undefined values
|
|
949
946
|
// and they can be annoying with snapshot tests
|
|
950
|
-
obj =
|
|
947
|
+
obj = _filterUndefinedValues(obj);
|
|
951
948
|
// Return as is if no schema is passed or if `skipConversion` is set
|
|
952
949
|
if (!schema ||
|
|
953
950
|
opt.skipValidation ||
|
|
@@ -960,13 +957,13 @@ class CommonDao {
|
|
|
960
957
|
const objectName = table;
|
|
961
958
|
let error;
|
|
962
959
|
let convertedValue;
|
|
963
|
-
if (schema instanceof
|
|
960
|
+
if (schema instanceof ZodSchema) {
|
|
964
961
|
// Zod schema
|
|
965
|
-
const vr =
|
|
962
|
+
const vr = zSafeValidate(obj, schema);
|
|
966
963
|
error = vr.error;
|
|
967
964
|
convertedValue = vr.data;
|
|
968
965
|
}
|
|
969
|
-
else if (schema instanceof
|
|
966
|
+
else if (schema instanceof AjvSchema) {
|
|
970
967
|
// Ajv schema
|
|
971
968
|
convertedValue = obj; // because Ajv mutates original object
|
|
972
969
|
error = schema.getValidationError(obj, {
|
|
@@ -975,9 +972,9 @@ class CommonDao {
|
|
|
975
972
|
}
|
|
976
973
|
else {
|
|
977
974
|
// Joi
|
|
978
|
-
const start =
|
|
979
|
-
const vr =
|
|
980
|
-
const tookMillis =
|
|
975
|
+
const start = localTime.nowUnixMillis();
|
|
976
|
+
const vr = getValidationResult(obj, schema, objectName);
|
|
977
|
+
const tookMillis = localTime.nowUnixMillis() - start;
|
|
981
978
|
this.cfg.onValidationTime?.({
|
|
982
979
|
tookMillis,
|
|
983
980
|
table,
|
|
@@ -1007,6 +1004,10 @@ class CommonDao {
|
|
|
1007
1004
|
async ping() {
|
|
1008
1005
|
await this.cfg.db.ping();
|
|
1009
1006
|
}
|
|
1007
|
+
async createTransaction(opt) {
|
|
1008
|
+
const tx = await this.cfg.db.createTransaction(opt);
|
|
1009
|
+
return new CommonDaoTransaction(tx, this.cfg.logger);
|
|
1010
|
+
}
|
|
1010
1011
|
async runInTransaction(fn, opt) {
|
|
1011
1012
|
let r;
|
|
1012
1013
|
await this.cfg.db.runInTransaction(async (tx) => {
|
|
@@ -1029,7 +1030,7 @@ class CommonDao {
|
|
|
1029
1030
|
if (!excludeFromIndexes)
|
|
1030
1031
|
return;
|
|
1031
1032
|
for (const f of q._filters) {
|
|
1032
|
-
|
|
1033
|
+
_assert(!excludeFromIndexes.includes(f.name), `cannot query on non-indexed property: ${this.cfg.table}.${f.name}`, {
|
|
1033
1034
|
query: q.pretty(),
|
|
1034
1035
|
});
|
|
1035
1036
|
}
|
|
@@ -1041,37 +1042,37 @@ class CommonDao {
|
|
|
1041
1042
|
const args = [];
|
|
1042
1043
|
if (Array.isArray(res)) {
|
|
1043
1044
|
logRes = `${res.length} row(s)`;
|
|
1044
|
-
if (res.length && this.cfg.logLevel >=
|
|
1045
|
+
if (res.length && this.cfg.logLevel >= CommonDaoLogLevel.DATA_FULL) {
|
|
1045
1046
|
args.push('\n', ...res.slice(0, 10)); // max 10 items
|
|
1046
1047
|
}
|
|
1047
1048
|
}
|
|
1048
1049
|
else if (res) {
|
|
1049
1050
|
logRes = `1 row`;
|
|
1050
|
-
if (this.cfg.logLevel >=
|
|
1051
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.DATA_SINGLE) {
|
|
1051
1052
|
args.push('\n', res);
|
|
1052
1053
|
}
|
|
1053
1054
|
}
|
|
1054
1055
|
else {
|
|
1055
1056
|
logRes = `undefined`;
|
|
1056
1057
|
}
|
|
1057
|
-
this.cfg.logger?.log(`<< ${table}.${op}: ${logRes} in ${
|
|
1058
|
+
this.cfg.logger?.log(`<< ${table}.${op}: ${logRes} in ${_since(started)}`, ...args);
|
|
1058
1059
|
}
|
|
1059
1060
|
logSaveResult(started, op, table) {
|
|
1060
1061
|
if (!this.cfg.logLevel)
|
|
1061
1062
|
return;
|
|
1062
|
-
this.cfg.logger?.log(`<< ${table}.${op} in ${
|
|
1063
|
+
this.cfg.logger?.log(`<< ${table}.${op} in ${_since(started)}`);
|
|
1063
1064
|
}
|
|
1064
1065
|
logStarted(op, table, force = false) {
|
|
1065
1066
|
if (this.cfg.logStarted || force) {
|
|
1066
1067
|
this.cfg.logger?.log(`>> ${table}.${op}`);
|
|
1067
1068
|
}
|
|
1068
|
-
return
|
|
1069
|
+
return localTime.nowUnixMillis();
|
|
1069
1070
|
}
|
|
1070
1071
|
logSaveStarted(op, items, table) {
|
|
1071
1072
|
if (this.cfg.logStarted) {
|
|
1072
1073
|
const args = [`>> ${table}.${op}`];
|
|
1073
1074
|
if (Array.isArray(items)) {
|
|
1074
|
-
if (items.length && this.cfg.logLevel >=
|
|
1075
|
+
if (items.length && this.cfg.logLevel >= CommonDaoLogLevel.DATA_FULL) {
|
|
1075
1076
|
args.push('\n', ...items.slice(0, 10));
|
|
1076
1077
|
}
|
|
1077
1078
|
else {
|
|
@@ -1079,29 +1080,36 @@ class CommonDao {
|
|
|
1079
1080
|
}
|
|
1080
1081
|
}
|
|
1081
1082
|
else {
|
|
1082
|
-
if (this.cfg.logLevel >=
|
|
1083
|
+
if (this.cfg.logLevel >= CommonDaoLogLevel.DATA_SINGLE) {
|
|
1083
1084
|
args.push(items);
|
|
1084
1085
|
}
|
|
1085
1086
|
}
|
|
1086
1087
|
this.cfg.logger?.log(...args);
|
|
1087
1088
|
}
|
|
1088
|
-
return
|
|
1089
|
+
return localTime.nowUnixMillis();
|
|
1089
1090
|
}
|
|
1090
1091
|
}
|
|
1091
|
-
exports.CommonDao = CommonDao;
|
|
1092
1092
|
/**
|
|
1093
1093
|
* Transaction context.
|
|
1094
1094
|
* Has similar API than CommonDao, but all operations are performed in the context of the transaction.
|
|
1095
1095
|
*/
|
|
1096
|
-
class CommonDaoTransaction {
|
|
1096
|
+
export class CommonDaoTransaction {
|
|
1097
1097
|
tx;
|
|
1098
1098
|
logger;
|
|
1099
1099
|
constructor(tx, logger) {
|
|
1100
1100
|
this.tx = tx;
|
|
1101
1101
|
this.logger = logger;
|
|
1102
1102
|
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Commits the underlying DBTransaction.
|
|
1105
|
+
* May throw.
|
|
1106
|
+
*/
|
|
1107
|
+
async commit() {
|
|
1108
|
+
await this.tx.commit();
|
|
1109
|
+
}
|
|
1103
1110
|
/**
|
|
1104
1111
|
* Perform a graceful rollback without throwing/re-throwing any error.
|
|
1112
|
+
* Never throws.
|
|
1105
1113
|
*/
|
|
1106
1114
|
async rollback() {
|
|
1107
1115
|
try {
|
|
@@ -1145,7 +1153,7 @@ class CommonDaoTransaction {
|
|
|
1145
1153
|
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
1146
1154
|
*/
|
|
1147
1155
|
async patch(dao, bm, patch, opt) {
|
|
1148
|
-
const skipIfEquals =
|
|
1156
|
+
const skipIfEquals = _deepCopy(bm);
|
|
1149
1157
|
Object.assign(bm, patch);
|
|
1150
1158
|
return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx });
|
|
1151
1159
|
}
|
|
@@ -1158,4 +1166,3 @@ class CommonDaoTransaction {
|
|
|
1158
1166
|
return await dao.deleteByIds(ids, { ...opt, tx: this.tx });
|
|
1159
1167
|
}
|
|
1160
1168
|
}
|
|
1161
|
-
exports.CommonDaoTransaction = CommonDaoTransaction;
|