@naturalcycles/db-lib 9.7.2 → 9.9.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 +4 -12
- package/dist/commondao/common.dao.js +30 -40
- package/dist/commondao/common.dao.model.d.ts +19 -15
- package/dist/testing/timeSeriesTest.util.js +1 -1
- package/package.json +2 -2
- package/src/commondao/common.dao.model.ts +23 -17
- package/src/commondao/common.dao.ts +35 -47
|
@@ -4,7 +4,7 @@ import { AsyncMapper, BaseDBEntity, CommonLogger, JsonSchemaObject, JsonSchemaRo
|
|
|
4
4
|
import { AjvSchema, ObjectSchema, ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
5
5
|
import { CommonDBTransactionOptions, DBPatch, DBTransaction, RunQueryResult } from '../db.model';
|
|
6
6
|
import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
|
|
7
|
-
import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model';
|
|
7
|
+
import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoPatchOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model';
|
|
8
8
|
/**
|
|
9
9
|
* Lowest common denominator API between supported Databases.
|
|
10
10
|
*
|
|
@@ -78,15 +78,6 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
78
78
|
* "Returns", just to have a type of "Saved"
|
|
79
79
|
*/
|
|
80
80
|
assignIdCreatedUpdated<T extends BaseDBEntity>(obj: Partial<T>, opt?: CommonDaoOptions): T;
|
|
81
|
-
/**
|
|
82
|
-
* 1. Applies the patch
|
|
83
|
-
* 2. If object is the same after patching - skips saving it
|
|
84
|
-
* 3. Otherwise - saves the patched object and returns it
|
|
85
|
-
*
|
|
86
|
-
* Similar to `save` with skipIfEquals.
|
|
87
|
-
* Similar to `patch`, but doesn't load the object from the Database.
|
|
88
|
-
*/
|
|
89
|
-
savePatch(bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM>;
|
|
90
81
|
/**
|
|
91
82
|
* Convenience method to replace 3 operations (loading+patching+saving) with one:
|
|
92
83
|
*
|
|
@@ -107,7 +98,7 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
107
98
|
* Otherwise, similar behavior as patchById.
|
|
108
99
|
* It still loads the row from the DB.
|
|
109
100
|
*/
|
|
110
|
-
patch(bm: BM, patch: Partial<BM>, opt?:
|
|
101
|
+
patch(bm: BM, patch: Partial<BM>, opt?: CommonDaoPatchOptions<DBM>): Promise<BM>;
|
|
111
102
|
/**
|
|
112
103
|
* Like patch, but runs all operations within a Transaction.
|
|
113
104
|
*/
|
|
@@ -158,7 +149,8 @@ export declare class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
158
149
|
* Does NOT mutate the object.
|
|
159
150
|
* Validates (unless `skipValidation=true` passed).
|
|
160
151
|
*/
|
|
161
|
-
validateAndConvert<T>(obj: Partial<T>, schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined,
|
|
152
|
+
validateAndConvert<T>(obj: Partial<T>, schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined, op?: 'load' | 'save', // this is to skip validation if validateOnLoad/Save is false
|
|
153
|
+
opt?: CommonDaoOptions): any;
|
|
162
154
|
getTableSchema(): Promise<JsonSchemaRootObject<DBM>>;
|
|
163
155
|
createTable(schema: JsonSchemaObject<DBM>, opt?: CommonDaoCreateOptions): Promise<void>;
|
|
164
156
|
/**
|
|
@@ -27,6 +27,8 @@ class CommonDao {
|
|
|
27
27
|
assignGeneratedIds: false,
|
|
28
28
|
useCreatedProperty: true,
|
|
29
29
|
useUpdatedProperty: true,
|
|
30
|
+
validateOnLoad: true,
|
|
31
|
+
validateOnSave: true,
|
|
30
32
|
logger: console,
|
|
31
33
|
...cfg,
|
|
32
34
|
hooks: {
|
|
@@ -49,7 +51,7 @@ class CommonDao {
|
|
|
49
51
|
const bm = this.cfg.hooks.beforeCreate(part);
|
|
50
52
|
// First assignIdCreatedUpdated, then validate!
|
|
51
53
|
this.assignIdCreatedUpdated(bm, opt);
|
|
52
|
-
return this.validateAndConvert(bm, this.cfg.bmSchema, opt);
|
|
54
|
+
return this.validateAndConvert(bm, this.cfg.bmSchema, undefined, opt);
|
|
53
55
|
}
|
|
54
56
|
async getById(id, opt = {}) {
|
|
55
57
|
if (!id)
|
|
@@ -245,7 +247,6 @@ class CommonDao {
|
|
|
245
247
|
async streamQueryForEach(q, mapper, opt = {}) {
|
|
246
248
|
q.table = opt.table || q.table;
|
|
247
249
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
248
|
-
opt.skipConversion = opt.skipConversion !== false; // default true
|
|
249
250
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
250
251
|
const partialQuery = !!q._selectedFieldNames;
|
|
251
252
|
const op = `streamQueryForEach(${q.pretty()})`;
|
|
@@ -284,7 +285,6 @@ class CommonDao {
|
|
|
284
285
|
async streamQueryAsDBMForEach(q, mapper, opt = {}) {
|
|
285
286
|
q.table = opt.table || q.table;
|
|
286
287
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
287
|
-
opt.skipConversion = opt.skipConversion !== false; // default true
|
|
288
288
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
289
289
|
const partialQuery = !!q._selectedFieldNames;
|
|
290
290
|
const op = `streamQueryAsDBMForEach(${q.pretty()})`;
|
|
@@ -326,7 +326,6 @@ class CommonDao {
|
|
|
326
326
|
streamQueryAsDBM(q, opt = {}) {
|
|
327
327
|
q.table = opt.table || q.table;
|
|
328
328
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
329
|
-
opt.skipConversion = opt.skipConversion !== false; // default true
|
|
330
329
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
331
330
|
const partialQuery = !!q._selectedFieldNames;
|
|
332
331
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
@@ -357,7 +356,6 @@ class CommonDao {
|
|
|
357
356
|
streamQuery(q, opt = {}) {
|
|
358
357
|
q.table = opt.table || q.table;
|
|
359
358
|
opt.skipValidation = opt.skipValidation !== false; // default true
|
|
360
|
-
opt.skipConversion = opt.skipConversion !== false; // default true
|
|
361
359
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
362
360
|
const stream = this.cfg.db.streamQuery(q, opt);
|
|
363
361
|
const partialQuery = !!q._selectedFieldNames;
|
|
@@ -444,27 +442,6 @@ class CommonDao {
|
|
|
444
442
|
return obj;
|
|
445
443
|
}
|
|
446
444
|
// SAVE
|
|
447
|
-
/**
|
|
448
|
-
* 1. Applies the patch
|
|
449
|
-
* 2. If object is the same after patching - skips saving it
|
|
450
|
-
* 3. Otherwise - saves the patched object and returns it
|
|
451
|
-
*
|
|
452
|
-
* Similar to `save` with skipIfEquals.
|
|
453
|
-
* Similar to `patch`, but doesn't load the object from the Database.
|
|
454
|
-
*/
|
|
455
|
-
async savePatch(bm, patch, opt) {
|
|
456
|
-
const patched = {
|
|
457
|
-
...bm,
|
|
458
|
-
...patch,
|
|
459
|
-
};
|
|
460
|
-
if ((0, js_lib_1._deepJsonEquals)(bm, patched)) {
|
|
461
|
-
// Skipping the save operation, as data is the same
|
|
462
|
-
return bm;
|
|
463
|
-
}
|
|
464
|
-
// Actually apply the patch by mutating the original object (by design)
|
|
465
|
-
Object.assign(bm, patch);
|
|
466
|
-
return await this.save(bm, opt);
|
|
467
|
-
}
|
|
468
445
|
/**
|
|
469
446
|
* Convenience method to replace 3 operations (loading+patching+saving) with one:
|
|
470
447
|
*
|
|
@@ -516,18 +493,28 @@ class CommonDao {
|
|
|
516
493
|
// and should just continue as-is
|
|
517
494
|
return await this.patchInTransaction(bm, patch, opt);
|
|
518
495
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
Object.assign(
|
|
522
|
-
if ((0, js_lib_1._deepJsonEquals)(
|
|
496
|
+
if (opt.skipDBRead) {
|
|
497
|
+
const bmBefore = (0, js_lib_1._deepCopy)(bm);
|
|
498
|
+
Object.assign(bm, patch);
|
|
499
|
+
if ((0, js_lib_1._deepJsonEquals)(bm, bmBefore)) {
|
|
523
500
|
// Skipping the save operation, as data is the same
|
|
524
501
|
return bm;
|
|
525
502
|
}
|
|
526
|
-
// Make `bm` exactly the same as `loaded`
|
|
527
|
-
(0, js_lib_1._objectAssignExact)(bm, loaded);
|
|
528
503
|
}
|
|
529
504
|
else {
|
|
530
|
-
|
|
505
|
+
const loaded = await this.getById(bm.id, opt);
|
|
506
|
+
if (loaded) {
|
|
507
|
+
Object.assign(loaded, patch);
|
|
508
|
+
if ((0, js_lib_1._deepJsonEquals)(loaded, bm)) {
|
|
509
|
+
// Skipping the save operation, as data is the same
|
|
510
|
+
return bm;
|
|
511
|
+
}
|
|
512
|
+
// Make `bm` exactly the same as `loaded`
|
|
513
|
+
(0, js_lib_1._objectAssignExact)(bm, loaded);
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
Object.assign(bm, patch);
|
|
517
|
+
}
|
|
531
518
|
}
|
|
532
519
|
return await this.save(bm, opt);
|
|
533
520
|
}
|
|
@@ -548,7 +535,7 @@ class CommonDao {
|
|
|
548
535
|
// We compare with convertedBM, to account for cases when some extra property is assigned to bm,
|
|
549
536
|
// which should be removed post-validation, but it breaks the "equality check"
|
|
550
537
|
// Post-validation the equality check should work as intended
|
|
551
|
-
const convertedBM = this.validateAndConvert(bm, this.cfg.bmSchema, opt);
|
|
538
|
+
const convertedBM = this.validateAndConvert(bm, this.cfg.bmSchema, 'save', opt);
|
|
552
539
|
if ((0, js_lib_1._deepJsonEquals)(convertedBM, opt.skipIfEquals)) {
|
|
553
540
|
// Skipping the save operation
|
|
554
541
|
return bm;
|
|
@@ -693,7 +680,6 @@ class CommonDao {
|
|
|
693
680
|
this.requireWriteAccess();
|
|
694
681
|
const table = opt.table || this.cfg.table;
|
|
695
682
|
opt.skipValidation ??= true;
|
|
696
|
-
opt.skipConversion ??= true;
|
|
697
683
|
opt.errorMode ||= js_lib_1.ErrorMode.SUPPRESS;
|
|
698
684
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
699
685
|
opt = { ...opt, saveMethod: 'insert' };
|
|
@@ -828,7 +814,7 @@ class CommonDao {
|
|
|
828
814
|
// DBM > BM
|
|
829
815
|
const bm = ((await this.cfg.hooks.beforeDBMToBM?.(dbm)) || dbm);
|
|
830
816
|
// Validate/convert BM
|
|
831
|
-
return this.validateAndConvert(bm, this.cfg.bmSchema, opt);
|
|
817
|
+
return this.validateAndConvert(bm, this.cfg.bmSchema, 'load', opt);
|
|
832
818
|
}
|
|
833
819
|
async dbmsToBM(dbms, opt = {}) {
|
|
834
820
|
return await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.dbmToBM(dbm, opt));
|
|
@@ -837,7 +823,7 @@ class CommonDao {
|
|
|
837
823
|
if (bm === undefined)
|
|
838
824
|
return;
|
|
839
825
|
// bm gets assigned to the new reference
|
|
840
|
-
bm = this.validateAndConvert(bm, this.cfg.bmSchema, opt);
|
|
826
|
+
bm = this.validateAndConvert(bm, this.cfg.bmSchema, 'save', opt);
|
|
841
827
|
// BM > DBM
|
|
842
828
|
return ((await this.cfg.hooks.beforeBMToDBM?.(bm)) || bm);
|
|
843
829
|
}
|
|
@@ -868,7 +854,8 @@ class CommonDao {
|
|
|
868
854
|
* Does NOT mutate the object.
|
|
869
855
|
* Validates (unless `skipValidation=true` passed).
|
|
870
856
|
*/
|
|
871
|
-
validateAndConvert(obj, schema,
|
|
857
|
+
validateAndConvert(obj, schema, op, // this is to skip validation if validateOnLoad/Save is false
|
|
858
|
+
opt = {}) {
|
|
872
859
|
// Kirill 2021-10-18: I realized that there's little reason to keep removing null values
|
|
873
860
|
// So, from now on we'll preserve them
|
|
874
861
|
// "undefined" values, I believe, are/were not saved to/from DB anyway (due to e.g JSON.stringify removing them)
|
|
@@ -880,7 +867,10 @@ class CommonDao {
|
|
|
880
867
|
// and they can be annoying with snapshot tests
|
|
881
868
|
obj = (0, js_lib_1._filterUndefinedValues)(obj);
|
|
882
869
|
// Return as is if no schema is passed or if `skipConversion` is set
|
|
883
|
-
if (!schema ||
|
|
870
|
+
if (!schema ||
|
|
871
|
+
opt.skipValidation ||
|
|
872
|
+
(op === 'load' && !this.cfg.validateOnLoad) ||
|
|
873
|
+
(op === 'save' && !this.cfg.validateOnSave)) {
|
|
884
874
|
return obj;
|
|
885
875
|
}
|
|
886
876
|
// This will Convert and Validate
|
|
@@ -908,7 +898,7 @@ class CommonDao {
|
|
|
908
898
|
convertedValue = vr.value;
|
|
909
899
|
}
|
|
910
900
|
// If we care about validation and there's an error
|
|
911
|
-
if (error
|
|
901
|
+
if (error) {
|
|
912
902
|
const processedError = this.cfg.hooks.onValidationError(error);
|
|
913
903
|
if (processedError)
|
|
914
904
|
throw processedError;
|
|
@@ -101,6 +101,16 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
101
101
|
*/
|
|
102
102
|
bmSchema?: ObjectSchema<BM> | AjvSchema<BM> | ZodSchema<BM>;
|
|
103
103
|
excludeFromIndexes?: (keyof DBM)[];
|
|
104
|
+
/**
|
|
105
|
+
* Defaults to true.
|
|
106
|
+
* If set to false - load (read) operations will skip validation (and conversion).
|
|
107
|
+
*/
|
|
108
|
+
validateOnLoad?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Defaults to true.
|
|
111
|
+
* If set to false - save (write) operations will skip validation (and conversion).
|
|
112
|
+
*/
|
|
113
|
+
validateOnSave?: boolean;
|
|
104
114
|
/**
|
|
105
115
|
* Defaults to false.
|
|
106
116
|
* Setting it to true will set saveMethod to `insert` for save/saveBatch, which will
|
|
@@ -163,20 +173,12 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
163
173
|
*/
|
|
164
174
|
export interface CommonDaoOptions extends CommonDBOptions {
|
|
165
175
|
/**
|
|
166
|
-
*
|
|
167
|
-
* (according to Joi schema).
|
|
168
|
-
*
|
|
169
|
-
* Set skipConversion=true (or raw=true) to bypass conversion step as well (e.g for performance reasons).
|
|
176
|
+
* Defaults to false.
|
|
170
177
|
*
|
|
171
|
-
*
|
|
178
|
+
* If set to true - will disable validation (and conversion).
|
|
179
|
+
* One possible use case of doing this is - performance (as validation/conversion takes time, especially with Joi).
|
|
172
180
|
*/
|
|
173
181
|
skipValidation?: boolean;
|
|
174
|
-
/**
|
|
175
|
-
* If true - will SKIP the joi validation AND conversion steps alltogether. To improve performance of DAO.
|
|
176
|
-
*
|
|
177
|
-
* @default false
|
|
178
|
-
*/
|
|
179
|
-
skipConversion?: boolean;
|
|
180
182
|
/**
|
|
181
183
|
* @default false
|
|
182
184
|
*/
|
|
@@ -209,6 +211,12 @@ export interface CommonDaoSaveOptions<BM extends BaseDBEntity, DBM extends BaseD
|
|
|
209
211
|
*/
|
|
210
212
|
skipIfEquals?: BM;
|
|
211
213
|
}
|
|
214
|
+
export interface CommonDaoPatchOptions<DBM extends BaseDBEntity> extends CommonDaoSaveBatchOptions<DBM> {
|
|
215
|
+
/**
|
|
216
|
+
* If true - patch will skip loading from DB, and will just optimistically patch passed object.
|
|
217
|
+
*/
|
|
218
|
+
skipDBRead?: boolean;
|
|
219
|
+
}
|
|
212
220
|
/**
|
|
213
221
|
* All properties default to undefined.
|
|
214
222
|
*/
|
|
@@ -235,10 +243,6 @@ export interface CommonDaoStreamOptions<IN> extends CommonDaoOptions, TransformL
|
|
|
235
243
|
* @default true (for streams)
|
|
236
244
|
*/
|
|
237
245
|
skipValidation?: boolean;
|
|
238
|
-
/**
|
|
239
|
-
* @default true (for streams)
|
|
240
|
-
*/
|
|
241
|
-
skipConversion?: boolean;
|
|
242
246
|
/**
|
|
243
247
|
* @default ErrorMode.SUPPRESS for returning ReadableStream, because .pipe() has no concept of "error propagation"
|
|
244
248
|
* @default ErrorMode.SUPPRESS for .forEach() streams as well, but overridable
|
|
@@ -4,6 +4,6 @@ exports.createTestTimeSeries = void 0;
|
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
function createTestTimeSeries(count = 10) {
|
|
6
6
|
const ts = Date.now();
|
|
7
|
-
return (0, js_lib_1._range)(1, count + 1).map(i => [ts - i *
|
|
7
|
+
return (0, js_lib_1._range)(1, count + 1).map(i => [ts - i * 60_000, (0, js_lib_1._randomInt)(10, 20)]);
|
|
8
8
|
}
|
|
9
9
|
exports.createTestTimeSeries = createTestTimeSeries;
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"@naturalcycles/nodejs-lib": "^13.1.1"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"@naturalcycles/bench-lib": "^
|
|
11
|
+
"@naturalcycles/bench-lib": "^2.0.0",
|
|
12
12
|
"@naturalcycles/dev-lib": "^13.0.0",
|
|
13
13
|
"@types/node": "^20.2.1",
|
|
14
14
|
"jest": "^29.0.0"
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=18.12"
|
|
42
42
|
},
|
|
43
|
-
"version": "9.
|
|
43
|
+
"version": "9.9.0",
|
|
44
44
|
"description": "Lowest Common Denominator API to supported Databases",
|
|
45
45
|
"keywords": [
|
|
46
46
|
"db",
|
|
@@ -129,6 +129,18 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
129
129
|
|
|
130
130
|
excludeFromIndexes?: (keyof DBM)[]
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Defaults to true.
|
|
134
|
+
* If set to false - load (read) operations will skip validation (and conversion).
|
|
135
|
+
*/
|
|
136
|
+
validateOnLoad?: boolean
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Defaults to true.
|
|
140
|
+
* If set to false - save (write) operations will skip validation (and conversion).
|
|
141
|
+
*/
|
|
142
|
+
validateOnSave?: boolean
|
|
143
|
+
|
|
132
144
|
/**
|
|
133
145
|
* Defaults to false.
|
|
134
146
|
* Setting it to true will set saveMethod to `insert` for save/saveBatch, which will
|
|
@@ -203,22 +215,13 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
203
215
|
*/
|
|
204
216
|
export interface CommonDaoOptions extends CommonDBOptions {
|
|
205
217
|
/**
|
|
206
|
-
*
|
|
207
|
-
* (according to Joi schema).
|
|
208
|
-
*
|
|
209
|
-
* Set skipConversion=true (or raw=true) to bypass conversion step as well (e.g for performance reasons).
|
|
218
|
+
* Defaults to false.
|
|
210
219
|
*
|
|
211
|
-
*
|
|
220
|
+
* If set to true - will disable validation (and conversion).
|
|
221
|
+
* One possible use case of doing this is - performance (as validation/conversion takes time, especially with Joi).
|
|
212
222
|
*/
|
|
213
223
|
skipValidation?: boolean
|
|
214
224
|
|
|
215
|
-
/**
|
|
216
|
-
* If true - will SKIP the joi validation AND conversion steps alltogether. To improve performance of DAO.
|
|
217
|
-
*
|
|
218
|
-
* @default false
|
|
219
|
-
*/
|
|
220
|
-
skipConversion?: boolean
|
|
221
|
-
|
|
222
225
|
/**
|
|
223
226
|
* @default false
|
|
224
227
|
*/
|
|
@@ -257,6 +260,14 @@ export interface CommonDaoSaveOptions<BM extends BaseDBEntity, DBM extends BaseD
|
|
|
257
260
|
skipIfEquals?: BM
|
|
258
261
|
}
|
|
259
262
|
|
|
263
|
+
export interface CommonDaoPatchOptions<DBM extends BaseDBEntity>
|
|
264
|
+
extends CommonDaoSaveBatchOptions<DBM> {
|
|
265
|
+
/**
|
|
266
|
+
* If true - patch will skip loading from DB, and will just optimistically patch passed object.
|
|
267
|
+
*/
|
|
268
|
+
skipDBRead?: boolean
|
|
269
|
+
}
|
|
270
|
+
|
|
260
271
|
/**
|
|
261
272
|
* All properties default to undefined.
|
|
262
273
|
*/
|
|
@@ -294,11 +305,6 @@ export interface CommonDaoStreamOptions<IN>
|
|
|
294
305
|
*/
|
|
295
306
|
skipValidation?: boolean
|
|
296
307
|
|
|
297
|
-
/**
|
|
298
|
-
* @default true (for streams)
|
|
299
|
-
*/
|
|
300
|
-
skipConversion?: boolean
|
|
301
|
-
|
|
302
308
|
/**
|
|
303
309
|
* @default ErrorMode.SUPPRESS for returning ReadableStream, because .pipe() has no concept of "error propagation"
|
|
304
310
|
* @default ErrorMode.SUPPRESS for .forEach() streams as well, but overridable
|
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
CommonDaoHooks,
|
|
52
52
|
CommonDaoLogLevel,
|
|
53
53
|
CommonDaoOptions,
|
|
54
|
+
CommonDaoPatchOptions,
|
|
54
55
|
CommonDaoSaveBatchOptions,
|
|
55
56
|
CommonDaoSaveOptions,
|
|
56
57
|
CommonDaoStreamDeleteOptions,
|
|
@@ -80,6 +81,8 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
80
81
|
assignGeneratedIds: false,
|
|
81
82
|
useCreatedProperty: true,
|
|
82
83
|
useUpdatedProperty: true,
|
|
84
|
+
validateOnLoad: true,
|
|
85
|
+
validateOnSave: true,
|
|
83
86
|
logger: console,
|
|
84
87
|
...cfg,
|
|
85
88
|
hooks: {
|
|
@@ -103,7 +106,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
103
106
|
const bm = this.cfg.hooks!.beforeCreate!(part)
|
|
104
107
|
// First assignIdCreatedUpdated, then validate!
|
|
105
108
|
this.assignIdCreatedUpdated(bm, opt)
|
|
106
|
-
return this.validateAndConvert(bm, this.cfg.bmSchema, opt)
|
|
109
|
+
return this.validateAndConvert(bm, this.cfg.bmSchema, undefined, opt)
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
// GET
|
|
@@ -352,7 +355,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
352
355
|
): Promise<void> {
|
|
353
356
|
q.table = opt.table || q.table
|
|
354
357
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
355
|
-
opt.skipConversion = opt.skipConversion !== false // default true
|
|
356
358
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
357
359
|
|
|
358
360
|
const partialQuery = !!q._selectedFieldNames
|
|
@@ -402,7 +404,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
402
404
|
): Promise<void> {
|
|
403
405
|
q.table = opt.table || q.table
|
|
404
406
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
405
|
-
opt.skipConversion = opt.skipConversion !== false // default true
|
|
406
407
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
407
408
|
|
|
408
409
|
const partialQuery = !!q._selectedFieldNames
|
|
@@ -451,7 +452,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
451
452
|
streamQueryAsDBM(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<DBM> = {}): ReadableTyped<DBM> {
|
|
452
453
|
q.table = opt.table || q.table
|
|
453
454
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
454
|
-
opt.skipConversion = opt.skipConversion !== false // default true
|
|
455
455
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
456
456
|
|
|
457
457
|
const partialQuery = !!q._selectedFieldNames
|
|
@@ -490,7 +490,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
490
490
|
streamQuery(q: DBQuery<DBM>, opt: CommonDaoStreamOptions<BM> = {}): ReadableTyped<BM> {
|
|
491
491
|
q.table = opt.table || q.table
|
|
492
492
|
opt.skipValidation = opt.skipValidation !== false // default true
|
|
493
|
-
opt.skipConversion = opt.skipConversion !== false // default true
|
|
494
493
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
495
494
|
|
|
496
495
|
const stream = this.cfg.db.streamQuery<DBM>(q, opt)
|
|
@@ -604,31 +603,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
604
603
|
}
|
|
605
604
|
|
|
606
605
|
// SAVE
|
|
607
|
-
/**
|
|
608
|
-
* 1. Applies the patch
|
|
609
|
-
* 2. If object is the same after patching - skips saving it
|
|
610
|
-
* 3. Otherwise - saves the patched object and returns it
|
|
611
|
-
*
|
|
612
|
-
* Similar to `save` with skipIfEquals.
|
|
613
|
-
* Similar to `patch`, but doesn't load the object from the Database.
|
|
614
|
-
*/
|
|
615
|
-
async savePatch(bm: BM, patch: Partial<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<BM> {
|
|
616
|
-
const patched: BM = {
|
|
617
|
-
...bm,
|
|
618
|
-
...patch,
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (_deepJsonEquals(bm, patched)) {
|
|
622
|
-
// Skipping the save operation, as data is the same
|
|
623
|
-
return bm
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// Actually apply the patch by mutating the original object (by design)
|
|
627
|
-
Object.assign(bm, patch)
|
|
628
|
-
|
|
629
|
-
return await this.save(bm, opt)
|
|
630
|
-
}
|
|
631
|
-
|
|
632
606
|
/**
|
|
633
607
|
* Convenience method to replace 3 operations (loading+patching+saving) with one:
|
|
634
608
|
*
|
|
@@ -686,7 +660,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
686
660
|
* Otherwise, similar behavior as patchById.
|
|
687
661
|
* It still loads the row from the DB.
|
|
688
662
|
*/
|
|
689
|
-
async patch(bm: BM, patch: Partial<BM>, opt:
|
|
663
|
+
async patch(bm: BM, patch: Partial<BM>, opt: CommonDaoPatchOptions<DBM> = {}): Promise<BM> {
|
|
690
664
|
if (this.cfg.patchInTransaction && !opt.tx) {
|
|
691
665
|
// patchInTransaction means that we should run this op in Transaction
|
|
692
666
|
// But if opt.tx is passed - means that we are already in a Transaction,
|
|
@@ -694,20 +668,29 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
694
668
|
return await this.patchInTransaction(bm, patch, opt)
|
|
695
669
|
}
|
|
696
670
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if (_deepJsonEquals(loaded, bm)) {
|
|
671
|
+
if (opt.skipDBRead) {
|
|
672
|
+
const bmBefore = _deepCopy(bm)
|
|
673
|
+
Object.assign(bm, patch)
|
|
674
|
+
if (_deepJsonEquals(bm, bmBefore)) {
|
|
703
675
|
// Skipping the save operation, as data is the same
|
|
704
676
|
return bm
|
|
705
677
|
}
|
|
706
|
-
|
|
707
|
-
// Make `bm` exactly the same as `loaded`
|
|
708
|
-
_objectAssignExact(bm, loaded)
|
|
709
678
|
} else {
|
|
710
|
-
|
|
679
|
+
const loaded = await this.getById(bm.id, opt)
|
|
680
|
+
|
|
681
|
+
if (loaded) {
|
|
682
|
+
Object.assign(loaded, patch)
|
|
683
|
+
|
|
684
|
+
if (_deepJsonEquals(loaded, bm)) {
|
|
685
|
+
// Skipping the save operation, as data is the same
|
|
686
|
+
return bm
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Make `bm` exactly the same as `loaded`
|
|
690
|
+
_objectAssignExact(bm, loaded)
|
|
691
|
+
} else {
|
|
692
|
+
Object.assign(bm, patch)
|
|
693
|
+
}
|
|
711
694
|
}
|
|
712
695
|
|
|
713
696
|
return await this.save(bm, opt)
|
|
@@ -736,7 +719,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
736
719
|
// We compare with convertedBM, to account for cases when some extra property is assigned to bm,
|
|
737
720
|
// which should be removed post-validation, but it breaks the "equality check"
|
|
738
721
|
// Post-validation the equality check should work as intended
|
|
739
|
-
const convertedBM = this.validateAndConvert(bm as Partial<BM>, this.cfg.bmSchema, opt)
|
|
722
|
+
const convertedBM = this.validateAndConvert(bm as Partial<BM>, this.cfg.bmSchema, 'save', opt)
|
|
740
723
|
if (_deepJsonEquals(convertedBM, opt.skipIfEquals)) {
|
|
741
724
|
// Skipping the save operation
|
|
742
725
|
return bm as BM
|
|
@@ -915,7 +898,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
915
898
|
|
|
916
899
|
const table = opt.table || this.cfg.table
|
|
917
900
|
opt.skipValidation ??= true
|
|
918
|
-
opt.skipConversion ??= true
|
|
919
901
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
920
902
|
|
|
921
903
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
@@ -1092,7 +1074,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1092
1074
|
const bm = ((await this.cfg.hooks!.beforeDBMToBM?.(dbm)) || dbm) as Partial<BM>
|
|
1093
1075
|
|
|
1094
1076
|
// Validate/convert BM
|
|
1095
|
-
return this.validateAndConvert(bm, this.cfg.bmSchema, opt)
|
|
1077
|
+
return this.validateAndConvert(bm, this.cfg.bmSchema, 'load', opt)
|
|
1096
1078
|
}
|
|
1097
1079
|
|
|
1098
1080
|
async dbmsToBM(dbms: DBM[], opt: CommonDaoOptions = {}): Promise<BM[]> {
|
|
@@ -1109,7 +1091,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1109
1091
|
if (bm === undefined) return
|
|
1110
1092
|
|
|
1111
1093
|
// bm gets assigned to the new reference
|
|
1112
|
-
bm = this.validateAndConvert(bm, this.cfg.bmSchema, opt)
|
|
1094
|
+
bm = this.validateAndConvert(bm, this.cfg.bmSchema, 'save', opt)
|
|
1113
1095
|
|
|
1114
1096
|
// BM > DBM
|
|
1115
1097
|
return ((await this.cfg.hooks!.beforeBMToDBM?.(bm!)) || bm) as DBM
|
|
@@ -1153,6 +1135,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1153
1135
|
validateAndConvert<T>(
|
|
1154
1136
|
obj: Partial<T>,
|
|
1155
1137
|
schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined,
|
|
1138
|
+
op?: 'load' | 'save', // this is to skip validation if validateOnLoad/Save is false
|
|
1156
1139
|
opt: CommonDaoOptions = {},
|
|
1157
1140
|
): any {
|
|
1158
1141
|
// Kirill 2021-10-18: I realized that there's little reason to keep removing null values
|
|
@@ -1167,7 +1150,12 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1167
1150
|
obj = _filterUndefinedValues(obj)
|
|
1168
1151
|
|
|
1169
1152
|
// Return as is if no schema is passed or if `skipConversion` is set
|
|
1170
|
-
if (
|
|
1153
|
+
if (
|
|
1154
|
+
!schema ||
|
|
1155
|
+
opt.skipValidation ||
|
|
1156
|
+
(op === 'load' && !this.cfg.validateOnLoad) ||
|
|
1157
|
+
(op === 'save' && !this.cfg.validateOnSave)
|
|
1158
|
+
) {
|
|
1171
1159
|
return obj
|
|
1172
1160
|
}
|
|
1173
1161
|
|
|
@@ -1198,7 +1186,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1198
1186
|
}
|
|
1199
1187
|
|
|
1200
1188
|
// If we care about validation and there's an error
|
|
1201
|
-
if (error
|
|
1189
|
+
if (error) {
|
|
1202
1190
|
const processedError = this.cfg.hooks!.onValidationError!(error)
|
|
1203
1191
|
|
|
1204
1192
|
if (processedError) throw processedError
|