@naturalcycles/db-lib 9.5.0 → 9.7.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 +16 -10
- package/dist/commondao/common.dao.js +108 -116
- package/dist/commondao/common.dao.model.d.ts +0 -30
- package/dist/testing/daoTest.js +1 -2
- package/dist/testing/dbTest.js +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +1 -3
- package/dist/testing/test.model.d.ts +0 -2
- package/dist/testing/test.model.js +1 -21
- package/package.json +2 -2
- package/src/commondao/common.dao.model.ts +0 -33
- package/src/commondao/common.dao.ts +118 -138
- package/src/testing/daoTest.ts +2 -4
- package/src/testing/dbTest.ts +2 -2
- package/src/testing/index.ts +0 -4
- package/src/testing/test.model.ts +0 -22
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/db-lib",
|
|
3
3
|
"scripts": {
|
|
4
|
-
"prepare": "husky
|
|
4
|
+
"prepare": "husky"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@naturalcycles/js-lib": "^14.116.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=18.12"
|
|
42
42
|
},
|
|
43
|
-
"version": "9.
|
|
43
|
+
"version": "9.7.0",
|
|
44
44
|
"description": "Lowest Common Denominator API to supported Databases",
|
|
45
45
|
"keywords": [
|
|
46
46
|
"db",
|
|
@@ -47,17 +47,6 @@ export interface CommonDaoHooks<BM extends BaseDBEntity, DBM extends BaseDBEntit
|
|
|
47
47
|
*/
|
|
48
48
|
beforeCreate: (bm: Partial<BM>) => Partial<BM>
|
|
49
49
|
|
|
50
|
-
/**
|
|
51
|
-
* Called when loading things "as DBM" and validation is not skipped.
|
|
52
|
-
* When loading things as BM/TM - other hooks get involved instead:
|
|
53
|
-
* - beforeDBMToBM
|
|
54
|
-
* - beforeBMToTM
|
|
55
|
-
*
|
|
56
|
-
* TODO: maybe rename those to `validateAs(model)`
|
|
57
|
-
* as it only validates "final state", not intermediate
|
|
58
|
-
*/
|
|
59
|
-
beforeDBMValidate: (dbm: Partial<DBM>) => Partial<DBM>
|
|
60
|
-
|
|
61
50
|
beforeDBMToBM: (dbm: DBM) => Partial<BM> | Promise<Partial<BM>>
|
|
62
51
|
beforeBMToDBM: (bm: BM) => Partial<DBM> | Promise<Partial<DBM>>
|
|
63
52
|
|
|
@@ -136,7 +125,6 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
136
125
|
/**
|
|
137
126
|
* Joi, AjvSchema or ZodSchema is supported.
|
|
138
127
|
*/
|
|
139
|
-
dbmSchema?: ObjectSchema<DBM> | AjvSchema<DBM> | ZodSchema<DBM>
|
|
140
128
|
bmSchema?: ObjectSchema<BM> | AjvSchema<BM> | ZodSchema<BM>
|
|
141
129
|
|
|
142
130
|
excludeFromIndexes?: (keyof DBM)[]
|
|
@@ -201,17 +189,6 @@ export interface CommonDaoCfg<BM extends BaseDBEntity, DBM extends BaseDBEntity
|
|
|
201
189
|
*/
|
|
202
190
|
useUpdatedProperty?: boolean
|
|
203
191
|
|
|
204
|
-
/**
|
|
205
|
-
* Default is false.
|
|
206
|
-
* If true - will run `_filterNullishValues` inside `validateAndConvert` function
|
|
207
|
-
* (instead of `_filterUndefinedValues`).
|
|
208
|
-
* This was the old db-lib behavior.
|
|
209
|
-
* This option allows to keep backwards-compatible behavior.
|
|
210
|
-
*
|
|
211
|
-
* @deprecated
|
|
212
|
-
*/
|
|
213
|
-
filterNullishValues?: boolean
|
|
214
|
-
|
|
215
192
|
/**
|
|
216
193
|
* Defaults to false.
|
|
217
194
|
* If true - run patch operations (patch, patchById) in a Transaction.
|
|
@@ -242,16 +219,6 @@ export interface CommonDaoOptions extends CommonDBOptions {
|
|
|
242
219
|
*/
|
|
243
220
|
skipConversion?: boolean
|
|
244
221
|
|
|
245
|
-
/**
|
|
246
|
-
* If true - will SKIP ANY transformation/processing, will return DB objects as they are. Will also skip created/updated/id
|
|
247
|
-
* generation.
|
|
248
|
-
*
|
|
249
|
-
* Useful for performance/streaming/pipelines.
|
|
250
|
-
*
|
|
251
|
-
* @default false
|
|
252
|
-
*/
|
|
253
|
-
raw?: boolean
|
|
254
|
-
|
|
255
222
|
/**
|
|
256
223
|
* @default false
|
|
257
224
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Transform } from 'node:stream'
|
|
2
2
|
import {
|
|
3
3
|
_assert,
|
|
4
|
+
_deepCopy,
|
|
4
5
|
_deepJsonEquals,
|
|
5
|
-
_filterNullishValues,
|
|
6
6
|
_filterUndefinedValues,
|
|
7
7
|
_isTruthy,
|
|
8
8
|
_objectAssignExact,
|
|
@@ -43,13 +43,7 @@ import {
|
|
|
43
43
|
writableVoid,
|
|
44
44
|
} from '@naturalcycles/nodejs-lib'
|
|
45
45
|
import { DBLibError } from '../cnst'
|
|
46
|
-
import {
|
|
47
|
-
CommonDBTransactionOptions,
|
|
48
|
-
DBModelType,
|
|
49
|
-
DBPatch,
|
|
50
|
-
DBTransaction,
|
|
51
|
-
RunQueryResult,
|
|
52
|
-
} from '../db.model'
|
|
46
|
+
import { CommonDBTransactionOptions, DBPatch, DBTransaction, RunQueryResult } from '../db.model'
|
|
53
47
|
import { DBQuery, RunnableDBQuery } from '../query/dbQuery'
|
|
54
48
|
import {
|
|
55
49
|
CommonDaoCfg,
|
|
@@ -91,9 +85,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
91
85
|
hooks: {
|
|
92
86
|
parseNaturalId: () => ({}),
|
|
93
87
|
beforeCreate: bm => bm as BM,
|
|
94
|
-
beforeDBMValidate: dbm => dbm,
|
|
95
|
-
beforeDBMToBM: dbm => dbm as any,
|
|
96
|
-
beforeBMToDBM: bm => bm as any,
|
|
97
88
|
anonymize: dbm => dbm,
|
|
98
89
|
onValidationError: err => err,
|
|
99
90
|
...cfg.hooks,
|
|
@@ -112,7 +103,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
112
103
|
const bm = this.cfg.hooks!.beforeCreate!(part)
|
|
113
104
|
// First assignIdCreatedUpdated, then validate!
|
|
114
105
|
this.assignIdCreatedUpdated(bm, opt)
|
|
115
|
-
return this.validateAndConvert(bm, this.cfg.bmSchema,
|
|
106
|
+
return this.validateAndConvert(bm, this.cfg.bmSchema, opt)
|
|
116
107
|
}
|
|
117
108
|
|
|
118
109
|
// GET
|
|
@@ -125,11 +116,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
125
116
|
const started = this.logStarted(op, table)
|
|
126
117
|
|
|
127
118
|
let dbm = (await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id]))[0]
|
|
128
|
-
if (dbm &&
|
|
119
|
+
if (dbm && this.cfg.hooks!.afterLoad) {
|
|
129
120
|
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
130
121
|
}
|
|
131
122
|
|
|
132
|
-
const bm =
|
|
123
|
+
const bm = await this.dbmToBM(dbm, opt)
|
|
133
124
|
this.logResult(started, op, bm, table)
|
|
134
125
|
return bm || null
|
|
135
126
|
}
|
|
@@ -141,18 +132,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
141
132
|
return this.create({ ...part, id }, opt)
|
|
142
133
|
}
|
|
143
134
|
|
|
144
|
-
async getByIdAsDBMOrEmpty(
|
|
145
|
-
id: string,
|
|
146
|
-
part: Partial<BM> = {},
|
|
147
|
-
opt?: CommonDaoOptions,
|
|
148
|
-
): Promise<DBM> {
|
|
149
|
-
const dbm = await this.getByIdAsDBM(id, opt)
|
|
150
|
-
if (dbm) return dbm
|
|
151
|
-
|
|
152
|
-
const bm = this.create({ ...part, id }, opt)
|
|
153
|
-
return await this.bmToDBM(bm, opt)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
135
|
async getByIdAsDBM(id: undefined | null, opt?: CommonDaoOptions): Promise<null>
|
|
157
136
|
async getByIdAsDBM(id?: string | null, opt?: CommonDaoOptions): Promise<DBM | null>
|
|
158
137
|
async getByIdAsDBM(id?: string | null, opt: CommonDaoOptions = {}): Promise<DBM | null> {
|
|
@@ -161,13 +140,11 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
161
140
|
const table = opt.table || this.cfg.table
|
|
162
141
|
const started = this.logStarted(op, table)
|
|
163
142
|
let [dbm] = await (opt.tx || this.cfg.db).getByIds<DBM>(table, [id])
|
|
164
|
-
if (dbm &&
|
|
143
|
+
if (dbm && this.cfg.hooks!.afterLoad) {
|
|
165
144
|
dbm = (await this.cfg.hooks!.afterLoad(dbm)) || undefined
|
|
166
145
|
}
|
|
167
146
|
|
|
168
|
-
|
|
169
|
-
dbm = this.anyToDBM(dbm!, opt)
|
|
170
|
-
}
|
|
147
|
+
dbm = this.anyToDBM(dbm!, opt)
|
|
171
148
|
this.logResult(started, op, dbm, table)
|
|
172
149
|
return dbm || null
|
|
173
150
|
}
|
|
@@ -178,13 +155,13 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
178
155
|
const table = opt.table || this.cfg.table
|
|
179
156
|
const started = this.logStarted(op, table)
|
|
180
157
|
let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids)
|
|
181
|
-
if (
|
|
158
|
+
if (this.cfg.hooks!.afterLoad && dbms.length) {
|
|
182
159
|
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
183
160
|
_isTruthy,
|
|
184
161
|
)
|
|
185
162
|
}
|
|
186
163
|
|
|
187
|
-
const bms =
|
|
164
|
+
const bms = await this.dbmsToBM(dbms, opt)
|
|
188
165
|
this.logResult(started, op, bms, table)
|
|
189
166
|
return bms
|
|
190
167
|
}
|
|
@@ -195,7 +172,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
195
172
|
const table = opt.table || this.cfg.table
|
|
196
173
|
const started = this.logStarted(op, table)
|
|
197
174
|
let dbms = await (opt.tx || this.cfg.db).getByIds<DBM>(table, ids)
|
|
198
|
-
if (
|
|
175
|
+
if (this.cfg.hooks!.afterLoad && dbms.length) {
|
|
199
176
|
dbms = (await pMap(dbms, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
200
177
|
_isTruthy,
|
|
201
178
|
)
|
|
@@ -318,13 +295,13 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
318
295
|
const started = this.logStarted(op, q.table)
|
|
319
296
|
let { rows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
|
|
320
297
|
const partialQuery = !!q._selectedFieldNames
|
|
321
|
-
if (
|
|
298
|
+
if (this.cfg.hooks!.afterLoad && rows.length) {
|
|
322
299
|
rows = (await pMap(rows, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
323
300
|
_isTruthy,
|
|
324
301
|
)
|
|
325
302
|
}
|
|
326
303
|
|
|
327
|
-
const bms = partialQuery
|
|
304
|
+
const bms = partialQuery ? (rows as any[]) : await this.dbmsToBM(rows, opt)
|
|
328
305
|
this.logResult(started, op, bms, q.table)
|
|
329
306
|
return {
|
|
330
307
|
rows: bms,
|
|
@@ -345,14 +322,14 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
345
322
|
const op = `runQueryAsDBM(${q.pretty()})`
|
|
346
323
|
const started = this.logStarted(op, q.table)
|
|
347
324
|
let { rows, ...queryResult } = await this.cfg.db.runQuery<DBM>(q, opt)
|
|
348
|
-
if (
|
|
325
|
+
if (this.cfg.hooks!.afterLoad && rows.length) {
|
|
349
326
|
rows = (await pMap(rows, async dbm => await this.cfg.hooks!.afterLoad!(dbm))).filter(
|
|
350
327
|
_isTruthy,
|
|
351
328
|
)
|
|
352
329
|
}
|
|
353
330
|
|
|
354
331
|
const partialQuery = !!q._selectedFieldNames
|
|
355
|
-
const dbms = partialQuery
|
|
332
|
+
const dbms = partialQuery ? rows : this.anyToDBMs(rows, opt)
|
|
356
333
|
this.logResult(started, op, dbms, q.table)
|
|
357
334
|
return { rows: dbms, ...queryResult }
|
|
358
335
|
}
|
|
@@ -388,7 +365,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
388
365
|
transformMap<DBM, BM>(
|
|
389
366
|
async dbm => {
|
|
390
367
|
count++
|
|
391
|
-
if (partialQuery
|
|
368
|
+
if (partialQuery) return dbm as any
|
|
392
369
|
|
|
393
370
|
if (this.cfg.hooks!.afterLoad) {
|
|
394
371
|
dbm = (await this.cfg.hooks!.afterLoad(dbm))!
|
|
@@ -438,7 +415,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
438
415
|
transformMap<any, DBM>(
|
|
439
416
|
async dbm => {
|
|
440
417
|
count++
|
|
441
|
-
if (partialQuery
|
|
418
|
+
if (partialQuery) return dbm
|
|
442
419
|
|
|
443
420
|
if (this.cfg.hooks!.afterLoad) {
|
|
444
421
|
dbm = (await this.cfg.hooks!.afterLoad(dbm))!
|
|
@@ -480,7 +457,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
480
457
|
const partialQuery = !!q._selectedFieldNames
|
|
481
458
|
|
|
482
459
|
const stream = this.cfg.db.streamQuery<DBM>(q, opt)
|
|
483
|
-
if (partialQuery
|
|
460
|
+
if (partialQuery) return stream
|
|
484
461
|
|
|
485
462
|
return stream
|
|
486
463
|
.on('error', err => stream.emit('error', err))
|
|
@@ -518,7 +495,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
518
495
|
|
|
519
496
|
const stream = this.cfg.db.streamQuery<DBM>(q, opt)
|
|
520
497
|
const partialQuery = !!q._selectedFieldNames
|
|
521
|
-
if (partialQuery
|
|
498
|
+
if (partialQuery) return stream
|
|
522
499
|
|
|
523
500
|
return (
|
|
524
501
|
stream
|
|
@@ -627,51 +604,6 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
627
604
|
}
|
|
628
605
|
|
|
629
606
|
// SAVE
|
|
630
|
-
/**
|
|
631
|
-
* Mutates with id, created, updated
|
|
632
|
-
*/
|
|
633
|
-
async save(bm: Unsaved<BM>, opt: CommonDaoSaveOptions<BM, DBM> = {}): Promise<BM> {
|
|
634
|
-
this.requireWriteAccess()
|
|
635
|
-
|
|
636
|
-
if (opt.skipIfEquals && _deepJsonEquals(bm, opt.skipIfEquals)) {
|
|
637
|
-
// Skipping the save operation
|
|
638
|
-
return bm as BM
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
const idWasGenerated = !bm.id && this.cfg.generateId
|
|
642
|
-
this.assignIdCreatedUpdated(bm, opt) // mutates
|
|
643
|
-
_typeCast<BM>(bm)
|
|
644
|
-
let dbm = await this.bmToDBM(bm, opt)
|
|
645
|
-
|
|
646
|
-
if (this.cfg.hooks!.beforeSave) {
|
|
647
|
-
dbm = (await this.cfg.hooks!.beforeSave(dbm))!
|
|
648
|
-
if (dbm === null) return bm
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
const table = opt.table || this.cfg.table
|
|
652
|
-
if (opt.ensureUniqueId && idWasGenerated) await this.ensureUniqueId(table, dbm)
|
|
653
|
-
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
654
|
-
opt = { ...opt, saveMethod: 'insert' }
|
|
655
|
-
}
|
|
656
|
-
const op = `save(${dbm.id})`
|
|
657
|
-
const started = this.logSaveStarted(op, bm, table)
|
|
658
|
-
const { excludeFromIndexes } = this.cfg
|
|
659
|
-
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
660
|
-
|
|
661
|
-
await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
|
|
662
|
-
excludeFromIndexes,
|
|
663
|
-
assignGeneratedIds,
|
|
664
|
-
...opt,
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
if (assignGeneratedIds) {
|
|
668
|
-
bm.id = dbm.id
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
this.logSaveResult(started, op, table)
|
|
672
|
-
return bm
|
|
673
|
-
}
|
|
674
|
-
|
|
675
607
|
/**
|
|
676
608
|
* 1. Applies the patch
|
|
677
609
|
* 2. If object is the same after patching - skips saving it
|
|
@@ -794,19 +726,62 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
794
726
|
})
|
|
795
727
|
}
|
|
796
728
|
|
|
797
|
-
|
|
729
|
+
/**
|
|
730
|
+
* Mutates with id, created, updated
|
|
731
|
+
*/
|
|
732
|
+
async save(bm: Unsaved<BM>, opt: CommonDaoSaveOptions<BM, DBM> = {}): Promise<BM> {
|
|
733
|
+
this.requireWriteAccess()
|
|
734
|
+
|
|
735
|
+
if (opt.skipIfEquals && _deepJsonEquals(bm, opt.skipIfEquals)) {
|
|
736
|
+
// Skipping the save operation
|
|
737
|
+
return bm as BM
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const idWasGenerated = !bm.id && this.cfg.generateId
|
|
741
|
+
this.assignIdCreatedUpdated(bm, opt) // mutates
|
|
742
|
+
_typeCast<BM>(bm)
|
|
743
|
+
let dbm = await this.bmToDBM(bm, opt) // validates BM
|
|
744
|
+
|
|
745
|
+
if (this.cfg.hooks!.beforeSave) {
|
|
746
|
+
dbm = (await this.cfg.hooks!.beforeSave(dbm))!
|
|
747
|
+
if (dbm === null) return bm
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const table = opt.table || this.cfg.table
|
|
751
|
+
if (opt.ensureUniqueId && idWasGenerated) await this.ensureUniqueId(table, dbm)
|
|
752
|
+
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
753
|
+
opt = { ...opt, saveMethod: 'insert' }
|
|
754
|
+
}
|
|
755
|
+
const op = `save(${dbm.id})`
|
|
756
|
+
const started = this.logSaveStarted(op, bm, table)
|
|
757
|
+
const { excludeFromIndexes } = this.cfg
|
|
758
|
+
const assignGeneratedIds = opt.assignGeneratedIds || this.cfg.assignGeneratedIds
|
|
759
|
+
|
|
760
|
+
await (opt.tx || this.cfg.db).saveBatch(table, [dbm], {
|
|
761
|
+
excludeFromIndexes,
|
|
762
|
+
assignGeneratedIds,
|
|
763
|
+
...opt,
|
|
764
|
+
})
|
|
765
|
+
|
|
766
|
+
if (assignGeneratedIds) {
|
|
767
|
+
bm.id = dbm.id
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
this.logSaveResult(started, op, table)
|
|
771
|
+
return bm
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
async saveAsDBM(dbm: Unsaved<DBM>, opt: CommonDaoSaveOptions<BM, DBM> = {}): Promise<DBM> {
|
|
798
775
|
this.requireWriteAccess()
|
|
799
776
|
const table = opt.table || this.cfg.table
|
|
800
777
|
|
|
801
778
|
// assigning id in case it misses the id
|
|
802
779
|
// will override/set `updated` field, unless opts.preserveUpdated is set
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
if (opt.ensureUniqueId && idWasGenerated) await this.ensureUniqueId(table, row)
|
|
809
|
-
}
|
|
780
|
+
const idWasGenerated = !dbm.id && this.cfg.generateId
|
|
781
|
+
this.assignIdCreatedUpdated(dbm, opt) // mutates
|
|
782
|
+
let row = this.anyToDBM(dbm, opt)
|
|
783
|
+
if (opt.ensureUniqueId && idWasGenerated) await this.ensureUniqueId(table, row)
|
|
784
|
+
|
|
810
785
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
811
786
|
opt = { ...opt, saveMethod: 'insert' }
|
|
812
787
|
}
|
|
@@ -885,12 +860,10 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
885
860
|
if (!dbms.length) return []
|
|
886
861
|
this.requireWriteAccess()
|
|
887
862
|
const table = opt.table || this.cfg.table
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
if (opt.ensureUniqueId) throw new AppError('ensureUniqueId is not supported in saveBatch')
|
|
893
|
-
}
|
|
863
|
+
dbms.forEach(dbm => this.assignIdCreatedUpdated(dbm, opt)) // mutates
|
|
864
|
+
let rows = this.anyToDBMs(dbms as DBM[], opt)
|
|
865
|
+
if (opt.ensureUniqueId) throw new AppError('ensureUniqueId is not supported in saveBatch')
|
|
866
|
+
|
|
894
867
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
895
868
|
opt = { ...opt, saveMethod: 'insert' }
|
|
896
869
|
}
|
|
@@ -995,18 +968,9 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
995
968
|
/**
|
|
996
969
|
* @returns number of deleted items
|
|
997
970
|
*/
|
|
998
|
-
async deleteById(id: undefined | null, opt?: CommonDaoOptions): Promise<0>
|
|
999
|
-
async deleteById(id?: string | null, opt?: CommonDaoOptions): Promise<number>
|
|
1000
971
|
async deleteById(id?: string | null, opt: CommonDaoOptions = {}): Promise<number> {
|
|
1001
972
|
if (!id) return 0
|
|
1002
|
-
this.
|
|
1003
|
-
this.requireObjectMutability(opt)
|
|
1004
|
-
const op = `deleteById(${id})`
|
|
1005
|
-
const table = opt.table || this.cfg.table
|
|
1006
|
-
const started = this.logStarted(op, table)
|
|
1007
|
-
const count = await this.cfg.db.deleteByIds(table, [id], opt)
|
|
1008
|
-
this.logSaveResult(started, op, table)
|
|
1009
|
-
return count
|
|
973
|
+
return await this.deleteByIds([id], opt)
|
|
1010
974
|
}
|
|
1011
975
|
|
|
1012
976
|
async deleteByIds(ids: string[], opt: CommonDaoOptions = {}): Promise<number> {
|
|
@@ -1119,11 +1083,16 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1119
1083
|
}
|
|
1120
1084
|
|
|
1121
1085
|
// DBM > BM
|
|
1122
|
-
|
|
1086
|
+
let bm: Partial<BM>
|
|
1087
|
+
if (this.cfg.hooks!.beforeDBMToBM) {
|
|
1088
|
+
bm = await this.cfg.hooks!.beforeDBMToBM(dbm)
|
|
1089
|
+
} else {
|
|
1090
|
+
bm = dbm as any
|
|
1091
|
+
}
|
|
1123
1092
|
|
|
1124
1093
|
// Validate/convert BM
|
|
1125
1094
|
|
|
1126
|
-
return this.validateAndConvert(bm, this.cfg.bmSchema,
|
|
1095
|
+
return this.validateAndConvert(bm, this.cfg.bmSchema, opt)
|
|
1127
1096
|
}
|
|
1128
1097
|
|
|
1129
1098
|
async dbmsToBM(dbms: DBM[], opt: CommonDaoOptions = {}): Promise<BM[]> {
|
|
@@ -1139,20 +1108,23 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1139
1108
|
async bmToDBM(bm?: BM, opt?: CommonDaoOptions): Promise<DBM | undefined> {
|
|
1140
1109
|
if (bm === undefined) return
|
|
1141
1110
|
|
|
1142
|
-
// optimization: no need to run the BM validation, since DBM will be validated anyway
|
|
1143
|
-
// Validate/convert BM
|
|
1144
|
-
// bm gets assigned to the new reference
|
|
1145
|
-
// bm = this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
|
|
1146
|
-
|
|
1147
1111
|
// should not do it on load, but only on save!
|
|
1148
1112
|
// this.assignIdCreatedUpdated(bm, opt)
|
|
1149
1113
|
|
|
1114
|
+
// bm gets assigned to the new reference
|
|
1115
|
+
bm = this.validateAndConvert(bm, this.cfg.bmSchema, opt)
|
|
1116
|
+
|
|
1150
1117
|
// BM > DBM
|
|
1151
|
-
|
|
1118
|
+
let dbm: DBM
|
|
1119
|
+
if (this.cfg.hooks!.beforeBMToDBM) {
|
|
1120
|
+
dbm = { ...((await this.cfg.hooks!.beforeBMToDBM(bm!)) as DBM) }
|
|
1121
|
+
} else {
|
|
1122
|
+
dbm = bm as any
|
|
1123
|
+
}
|
|
1152
1124
|
|
|
1153
1125
|
// Validate/convert DBM
|
|
1154
|
-
|
|
1155
|
-
return
|
|
1126
|
+
// return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
|
|
1127
|
+
return dbm
|
|
1156
1128
|
}
|
|
1157
1129
|
|
|
1158
1130
|
async bmsToDBM(bms: BM[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
|
|
@@ -1170,12 +1142,15 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1170
1142
|
|
|
1171
1143
|
dbm = { ...dbm, ...this.cfg.hooks!.parseNaturalId!(dbm.id) }
|
|
1172
1144
|
|
|
1145
|
+
// todo: is this the right place?
|
|
1146
|
+
// todo: is anyToDBM even needed?
|
|
1173
1147
|
if (opt.anonymize) {
|
|
1174
1148
|
dbm = this.cfg.hooks!.anonymize!(dbm)
|
|
1175
1149
|
}
|
|
1176
1150
|
|
|
1177
1151
|
// Validate/convert DBM
|
|
1178
|
-
return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
|
|
1152
|
+
// return this.validateAndConvert(dbm, this.cfg.dbmSchema, DBModelType.DBM, opt)
|
|
1153
|
+
return dbm
|
|
1179
1154
|
}
|
|
1180
1155
|
|
|
1181
1156
|
anyToDBMs(entities: DBM[], opt: CommonDaoOptions = {}): DBM[] {
|
|
@@ -1191,12 +1166,8 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1191
1166
|
validateAndConvert<T>(
|
|
1192
1167
|
obj: Partial<T>,
|
|
1193
1168
|
schema: ObjectSchema<T> | AjvSchema<T> | ZodSchema<T> | undefined,
|
|
1194
|
-
modelType?: DBModelType,
|
|
1195
1169
|
opt: CommonDaoOptions = {},
|
|
1196
1170
|
): any {
|
|
1197
|
-
// `raw` option completely bypasses any processing
|
|
1198
|
-
if (opt.raw) return obj as any
|
|
1199
|
-
|
|
1200
1171
|
// Kirill 2021-10-18: I realized that there's little reason to keep removing null values
|
|
1201
1172
|
// So, from now on we'll preserve them
|
|
1202
1173
|
// "undefined" values, I believe, are/were not saved to/from DB anyway (due to e.g JSON.stringify removing them)
|
|
@@ -1206,16 +1177,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1206
1177
|
// obj = _filterNullishValues(obj as any)
|
|
1207
1178
|
// We still filter `undefined` values here, because `beforeDBMToBM` can return undefined values
|
|
1208
1179
|
// and they can be annoying with snapshot tests
|
|
1209
|
-
|
|
1210
|
-
obj = _filterNullishValues(obj)
|
|
1211
|
-
} else {
|
|
1212
|
-
obj = _filterUndefinedValues(obj)
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
// Pre-validation hooks
|
|
1216
|
-
if (modelType === DBModelType.DBM) {
|
|
1217
|
-
obj = this.cfg.hooks!.beforeDBMValidate!(obj as any) as T
|
|
1218
|
-
}
|
|
1180
|
+
obj = _filterUndefinedValues(obj)
|
|
1219
1181
|
|
|
1220
1182
|
// Return as is if no schema is passed or if `skipConversion` is set
|
|
1221
1183
|
if (!schema || opt.skipConversion) {
|
|
@@ -1224,7 +1186,7 @@ export class CommonDao<BM extends BaseDBEntity, DBM extends BaseDBEntity = BM> {
|
|
|
1224
1186
|
|
|
1225
1187
|
// This will Convert and Validate
|
|
1226
1188
|
const table = opt.table || this.cfg.table
|
|
1227
|
-
const objectName = table
|
|
1189
|
+
const objectName = table
|
|
1228
1190
|
|
|
1229
1191
|
let error: JoiValidationError | AjvValidationError | ZodValidationError<T> | undefined
|
|
1230
1192
|
let convertedValue: any
|
|
@@ -1413,9 +1375,9 @@ export class CommonDaoTransaction {
|
|
|
1413
1375
|
async save<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
|
|
1414
1376
|
dao: CommonDao<BM, DBM>,
|
|
1415
1377
|
bm: Unsaved<BM>,
|
|
1416
|
-
opt?:
|
|
1378
|
+
opt?: CommonDaoSaveOptions<BM, DBM>,
|
|
1417
1379
|
): Promise<BM> {
|
|
1418
|
-
return
|
|
1380
|
+
return await dao.save(bm, { ...opt, tx: this.tx })
|
|
1419
1381
|
}
|
|
1420
1382
|
|
|
1421
1383
|
async saveBatch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
|
|
@@ -1426,6 +1388,24 @@ export class CommonDaoTransaction {
|
|
|
1426
1388
|
return await dao.saveBatch(bms, { ...opt, tx: this.tx })
|
|
1427
1389
|
}
|
|
1428
1390
|
|
|
1391
|
+
/**
|
|
1392
|
+
* DaoTransaction.patch does not load from DB.
|
|
1393
|
+
* It assumes the bm was previously loaded in the same Transaction, hence could not be
|
|
1394
|
+
* concurrently modified. Hence it's safe to not sync with DB.
|
|
1395
|
+
*
|
|
1396
|
+
* So, this method is a rather simple convenience "Object.assign and then save".
|
|
1397
|
+
*/
|
|
1398
|
+
async patch<BM extends BaseDBEntity, DBM extends BaseDBEntity>(
|
|
1399
|
+
dao: CommonDao<BM, DBM>,
|
|
1400
|
+
bm: BM,
|
|
1401
|
+
patch: Partial<BM>,
|
|
1402
|
+
opt?: CommonDaoSaveOptions<BM, DBM>,
|
|
1403
|
+
): Promise<BM> {
|
|
1404
|
+
const skipIfEquals = _deepCopy(bm)
|
|
1405
|
+
Object.assign(bm, patch)
|
|
1406
|
+
return await dao.save(bm, { ...opt, skipIfEquals, tx: this.tx })
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1429
1409
|
async deleteById(
|
|
1430
1410
|
dao: CommonDao<any>,
|
|
1431
1411
|
id?: string | null,
|
package/src/testing/daoTest.ts
CHANGED
|
@@ -8,10 +8,9 @@ import { CommonDBImplementationQuirks, expectMatch } from './dbTest'
|
|
|
8
8
|
import {
|
|
9
9
|
createTestItemsBM,
|
|
10
10
|
testItemBMSchema,
|
|
11
|
-
testItemDBMSchema,
|
|
12
11
|
TEST_TABLE,
|
|
13
12
|
createTestItemBM,
|
|
14
|
-
|
|
13
|
+
testItemBMJsonSchema,
|
|
15
14
|
} from './test.model'
|
|
16
15
|
import { TestItemBM } from '.'
|
|
17
16
|
|
|
@@ -20,7 +19,6 @@ export function runCommonDaoTest(db: CommonDB, quirks: CommonDBImplementationQui
|
|
|
20
19
|
const dao = new CommonDao({
|
|
21
20
|
table: TEST_TABLE,
|
|
22
21
|
db,
|
|
23
|
-
dbmSchema: testItemDBMSchema,
|
|
24
22
|
bmSchema: testItemBMSchema,
|
|
25
23
|
logStarted: true,
|
|
26
24
|
logLevel: CommonDaoLogLevel.DATA_FULL,
|
|
@@ -43,7 +41,7 @@ export function runCommonDaoTest(db: CommonDB, quirks: CommonDBImplementationQui
|
|
|
43
41
|
// CREATE TABLE, DROP
|
|
44
42
|
if (support.createTable) {
|
|
45
43
|
test('createTable, dropIfExists=true', async () => {
|
|
46
|
-
await dao.createTable(
|
|
44
|
+
await dao.createTable(testItemBMJsonSchema, { dropIfExists: true })
|
|
47
45
|
})
|
|
48
46
|
}
|
|
49
47
|
|
package/src/testing/dbTest.ts
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
createTestItemDBM,
|
|
8
8
|
createTestItemsDBM,
|
|
9
9
|
TEST_TABLE,
|
|
10
|
+
testItemBMJsonSchema,
|
|
10
11
|
TestItemDBM,
|
|
11
|
-
testItemDBMJsonSchema,
|
|
12
12
|
} from './test.model'
|
|
13
13
|
import { deepFreeze } from './test.util'
|
|
14
14
|
|
|
@@ -42,7 +42,7 @@ export function runCommonDBTest(db: CommonDB, quirks: CommonDBImplementationQuir
|
|
|
42
42
|
// CREATE TABLE, DROP
|
|
43
43
|
if (support.createTable) {
|
|
44
44
|
test('createTable, dropIfExists=true', async () => {
|
|
45
|
-
await db.createTable(TEST_TABLE,
|
|
45
|
+
await db.createTable(TEST_TABLE, testItemBMJsonSchema, { dropIfExists: true })
|
|
46
46
|
})
|
|
47
47
|
}
|
|
48
48
|
|
package/src/testing/index.ts
CHANGED
|
@@ -10,8 +10,6 @@ import {
|
|
|
10
10
|
testItemBMJsonSchema,
|
|
11
11
|
testItemBMSchema,
|
|
12
12
|
TestItemDBM,
|
|
13
|
-
testItemDBMJsonSchema,
|
|
14
|
-
testItemDBMSchema,
|
|
15
13
|
TestItemTM,
|
|
16
14
|
testItemTMSchema,
|
|
17
15
|
TEST_TABLE,
|
|
@@ -25,11 +23,9 @@ export {
|
|
|
25
23
|
createTestItemBM,
|
|
26
24
|
createTestItemsDBM,
|
|
27
25
|
createTestItemsBM,
|
|
28
|
-
testItemDBMSchema,
|
|
29
26
|
testItemBMSchema,
|
|
30
27
|
testItemTMSchema,
|
|
31
28
|
testItemBMJsonSchema,
|
|
32
|
-
testItemDBMJsonSchema,
|
|
33
29
|
runCommonDBTest,
|
|
34
30
|
runCommonDaoTest,
|
|
35
31
|
runCommonKeyValueDBTest,
|
|
@@ -35,14 +35,6 @@ export const testItemBMSchema = objectSchema<TestItemBM>({
|
|
|
35
35
|
b1: binarySchema.optional(),
|
|
36
36
|
}).concat(baseDBEntitySchema as any)
|
|
37
37
|
|
|
38
|
-
export const testItemDBMSchema = objectSchema<TestItemDBM>({
|
|
39
|
-
k1: stringSchema,
|
|
40
|
-
k2: stringSchema.allow(null).optional(),
|
|
41
|
-
k3: numberSchema.optional(),
|
|
42
|
-
even: booleanSchema.optional(),
|
|
43
|
-
b1: binarySchema.optional(),
|
|
44
|
-
}).concat(baseDBEntitySchema as any)
|
|
45
|
-
|
|
46
38
|
export const testItemTMSchema = objectSchema<TestItemTM>({
|
|
47
39
|
k1: stringSchema,
|
|
48
40
|
even: booleanSchema.optional(),
|
|
@@ -63,20 +55,6 @@ export const testItemBMJsonSchema: JsonSchemaObject<TestItemBM> = jsonSchema
|
|
|
63
55
|
.baseDBEntity()
|
|
64
56
|
.build()
|
|
65
57
|
|
|
66
|
-
export const testItemDBMJsonSchema: JsonSchemaObject<TestItemDBM> = jsonSchema
|
|
67
|
-
.rootObject<TestItemDBM>({
|
|
68
|
-
// todo: figure out how to not copy-paste these 3 fields
|
|
69
|
-
id: jsonSchema.string(),
|
|
70
|
-
created: jsonSchema.unixTimestamp(),
|
|
71
|
-
updated: jsonSchema.unixTimestamp(),
|
|
72
|
-
k1: jsonSchema.string(),
|
|
73
|
-
k2: jsonSchema.string().optional(),
|
|
74
|
-
k3: jsonSchema.number().optional(),
|
|
75
|
-
even: jsonSchema.boolean().optional(),
|
|
76
|
-
b1: jsonSchema.buffer().optional(),
|
|
77
|
-
})
|
|
78
|
-
.build()
|
|
79
|
-
|
|
80
58
|
export function createTestItemDBM(num = 1): TestItemDBM {
|
|
81
59
|
return {
|
|
82
60
|
id: `id${num}`,
|