@naturalcycles/db-lib 8.35.0 → 8.37.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/adapter/cachedb/cache.db.d.ts +9 -9
- package/dist/adapter/cachedb/cache.db.js +3 -1
- package/dist/adapter/cachedb/cache.db.model.d.ts +5 -3
- package/dist/adapter/file/file.db.d.ts +3 -3
- package/dist/adapter/file/file.db.js +1 -0
- package/dist/adapter/inmemory/inMemory.db.d.ts +3 -3
- package/dist/adapter/inmemory/inMemory.db.js +14 -11
- package/dist/base.common.db.d.ts +3 -3
- package/dist/common.db.d.ts +7 -4
- package/dist/commondao/common.dao.d.ts +24 -23
- package/dist/commondao/common.dao.js +13 -9
- package/dist/commondao/common.dao.model.d.ts +23 -23
- package/dist/db.model.d.ts +16 -18
- package/dist/index.d.ts +3 -3
- package/dist/model.util.d.ts +2 -3
- package/dist/model.util.js +1 -7
- package/dist/query/dbQuery.d.ts +6 -6
- package/dist/testing/dbTest.d.ts +2 -0
- package/dist/testing/dbTest.js +16 -1
- package/dist/validation/index.d.ts +3 -3
- package/package.json +1 -1
- package/src/adapter/cachedb/cache.db.model.ts +8 -7
- package/src/adapter/cachedb/cache.db.ts +21 -18
- package/src/adapter/file/file.db.ts +8 -5
- package/src/adapter/inmemory/inMemory.db.ts +22 -15
- package/src/base.common.db.ts +3 -3
- package/src/common.db.ts +11 -4
- package/src/commondao/common.dao.model.ts +32 -25
- package/src/commondao/common.dao.ts +58 -65
- package/src/db.model.ts +18 -19
- package/src/index.ts +4 -20
- package/src/model.util.ts +3 -9
- package/src/query/dbQuery.ts +8 -7
- package/src/testing/dbTest.ts +22 -0
- package/dist/adapter/inmemory/index.d.ts +0 -3
- package/dist/adapter/inmemory/index.js +0 -8
- package/src/adapter/inmemory/index.ts +0 -9
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
pMap,
|
|
16
16
|
pTimeout,
|
|
17
17
|
Saved,
|
|
18
|
+
Unsaved,
|
|
18
19
|
} from '@naturalcycles/js-lib'
|
|
19
20
|
import {
|
|
20
21
|
_pipeline,
|
|
@@ -59,49 +60,60 @@ const isCI = !!process.env['CI']
|
|
|
59
60
|
* TM = Transport model (optimized to be sent over the wire)
|
|
60
61
|
*/
|
|
61
62
|
export class CommonDao<
|
|
62
|
-
BM extends Partial<ObjectWithId
|
|
63
|
-
DBM extends ObjectWithId = Saved<BM>,
|
|
63
|
+
BM extends Partial<ObjectWithId<ID>>,
|
|
64
|
+
DBM extends ObjectWithId<ID> = Saved<BM>,
|
|
64
65
|
TM = BM,
|
|
66
|
+
ID extends string | number = DBM['id'],
|
|
65
67
|
> {
|
|
66
|
-
constructor(public cfg: CommonDaoCfg<BM, DBM, TM>) {
|
|
68
|
+
constructor(public cfg: CommonDaoCfg<BM, DBM, TM, ID>) {
|
|
67
69
|
this.cfg = {
|
|
68
70
|
// Default is to NOT log in AppEngine and in CI,
|
|
69
71
|
// otherwise to log Operations
|
|
70
72
|
// e.g in Dev (local machine), Test - it will log operations (useful for debugging)
|
|
71
73
|
logLevel: isGAE || isCI ? CommonDaoLogLevel.NONE : CommonDaoLogLevel.OPERATIONS,
|
|
74
|
+
idType: 'string',
|
|
75
|
+
createId: true,
|
|
72
76
|
created: true,
|
|
73
77
|
updated: true,
|
|
74
78
|
logger: console,
|
|
75
79
|
...cfg,
|
|
76
80
|
hooks: {
|
|
77
|
-
createId: () => stringId(),
|
|
78
81
|
parseNaturalId: () => ({}),
|
|
79
82
|
beforeCreate: bm => bm as BM,
|
|
80
83
|
beforeDBMValidate: dbm => dbm,
|
|
81
84
|
beforeDBMToBM: dbm => dbm as any,
|
|
82
85
|
beforeBMToDBM: bm => bm as any,
|
|
83
|
-
beforeTMToBM: tm => tm
|
|
86
|
+
beforeTMToBM: tm => tm,
|
|
84
87
|
beforeBMToTM: bm => bm as any,
|
|
85
88
|
anonymize: dbm => dbm,
|
|
86
89
|
onValidationError: err => err,
|
|
87
90
|
...cfg.hooks,
|
|
88
91
|
},
|
|
89
92
|
}
|
|
93
|
+
|
|
94
|
+
if (this.cfg.createId) {
|
|
95
|
+
_assert(
|
|
96
|
+
this.cfg.idType === 'string',
|
|
97
|
+
'db-lib: automatic generation of non-string ids is not supported',
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
this.cfg.hooks!.createId ||= () => stringId() as ID
|
|
101
|
+
} else {
|
|
102
|
+
delete this.cfg.hooks!.createId
|
|
103
|
+
}
|
|
90
104
|
}
|
|
91
105
|
|
|
92
106
|
// CREATE
|
|
93
107
|
create(part: Partial<BM> = {}, opt: CommonDaoOptions = {}): Saved<BM> {
|
|
94
108
|
let bm = this.cfg.hooks!.beforeCreate!(part) as BM
|
|
95
109
|
bm = this.validateAndConvert(bm, this.cfg.bmSchema, DBModelType.BM, opt)
|
|
96
|
-
|
|
97
|
-
// If no SCHEMA - return as is
|
|
98
110
|
return this.assignIdCreatedUpdated(bm, opt)
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
// GET
|
|
102
114
|
async getById(id: undefined, opt?: CommonDaoOptions): Promise<null>
|
|
103
|
-
async getById(id?:
|
|
104
|
-
async getById(id?:
|
|
115
|
+
async getById(id?: ID, opt?: CommonDaoOptions): Promise<Saved<BM> | null>
|
|
116
|
+
async getById(id?: ID, opt: CommonDaoOptions = {}): Promise<Saved<BM> | null> {
|
|
105
117
|
if (!id) return null
|
|
106
118
|
const op = `getById(${id})`
|
|
107
119
|
const table = opt.table || this.cfg.table
|
|
@@ -126,22 +138,14 @@ export class CommonDao<
|
|
|
126
138
|
return bm || null
|
|
127
139
|
}
|
|
128
140
|
|
|
129
|
-
async getByIdOrEmpty(
|
|
130
|
-
id: string,
|
|
131
|
-
part: Partial<BM> = {},
|
|
132
|
-
opt?: CommonDaoOptions,
|
|
133
|
-
): Promise<Saved<BM>> {
|
|
141
|
+
async getByIdOrEmpty(id: ID, part: Partial<BM> = {}, opt?: CommonDaoOptions): Promise<Saved<BM>> {
|
|
134
142
|
const bm = await this.getById(id, opt)
|
|
135
143
|
if (bm) return bm
|
|
136
144
|
|
|
137
145
|
return this.create({ ...part, id }, opt)
|
|
138
146
|
}
|
|
139
147
|
|
|
140
|
-
async getByIdAsDBMOrEmpty(
|
|
141
|
-
id: string,
|
|
142
|
-
part: Partial<BM> = {},
|
|
143
|
-
opt?: CommonDaoOptions,
|
|
144
|
-
): Promise<DBM> {
|
|
148
|
+
async getByIdAsDBMOrEmpty(id: ID, part: Partial<BM> = {}, opt?: CommonDaoOptions): Promise<DBM> {
|
|
145
149
|
const dbm = await this.getByIdAsDBM(id, opt)
|
|
146
150
|
if (dbm) return dbm
|
|
147
151
|
|
|
@@ -150,8 +154,8 @@ export class CommonDao<
|
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
async getByIdAsDBM(id: undefined, opt?: CommonDaoOptions): Promise<null>
|
|
153
|
-
async getByIdAsDBM(id?:
|
|
154
|
-
async getByIdAsDBM(id?:
|
|
157
|
+
async getByIdAsDBM(id?: ID, opt?: CommonDaoOptions): Promise<DBM | null>
|
|
158
|
+
async getByIdAsDBM(id?: ID, opt: CommonDaoOptions = {}): Promise<DBM | null> {
|
|
155
159
|
if (!id) return null
|
|
156
160
|
const op = `getByIdAsDBM(${id})`
|
|
157
161
|
const table = opt.table || this.cfg.table
|
|
@@ -165,8 +169,8 @@ export class CommonDao<
|
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
async getByIdAsTM(id: undefined, opt?: CommonDaoOptions): Promise<null>
|
|
168
|
-
async getByIdAsTM(id?:
|
|
169
|
-
async getByIdAsTM(id?:
|
|
172
|
+
async getByIdAsTM(id?: ID, opt?: CommonDaoOptions): Promise<TM | null>
|
|
173
|
+
async getByIdAsTM(id?: ID, opt: CommonDaoOptions = {}): Promise<TM | null> {
|
|
170
174
|
if (!id) return null
|
|
171
175
|
const op = `getByIdAsTM(${id})`
|
|
172
176
|
const table = opt.table || this.cfg.table
|
|
@@ -182,7 +186,7 @@ export class CommonDao<
|
|
|
182
186
|
return tm || null
|
|
183
187
|
}
|
|
184
188
|
|
|
185
|
-
async getByIds(ids:
|
|
189
|
+
async getByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<Saved<BM>[]> {
|
|
186
190
|
const op = `getByIds ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
187
191
|
const table = opt.table || this.cfg.table
|
|
188
192
|
const started = this.logStarted(op, table)
|
|
@@ -192,7 +196,7 @@ export class CommonDao<
|
|
|
192
196
|
return bms
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
async getByIdsAsDBM(ids:
|
|
199
|
+
async getByIdsAsDBM(ids: ID[], opt: CommonDaoOptions = {}): Promise<DBM[]> {
|
|
196
200
|
const op = `getByIdsAsDBM ${ids.length} id(s) (${_truncate(ids.slice(0, 10).join(', '), 50)})`
|
|
197
201
|
const table = opt.table || this.cfg.table
|
|
198
202
|
const started = this.logStarted(op, table)
|
|
@@ -201,7 +205,7 @@ export class CommonDao<
|
|
|
201
205
|
return dbms
|
|
202
206
|
}
|
|
203
207
|
|
|
204
|
-
async requireById(id:
|
|
208
|
+
async requireById(id: ID, opt: CommonDaoOptions = {}): Promise<Saved<BM>> {
|
|
205
209
|
const r = await this.getById(id, opt)
|
|
206
210
|
if (!r) {
|
|
207
211
|
this.throwRequiredError(id, opt)
|
|
@@ -209,7 +213,7 @@ export class CommonDao<
|
|
|
209
213
|
return r
|
|
210
214
|
}
|
|
211
215
|
|
|
212
|
-
async requireByIdAsDBM(id:
|
|
216
|
+
async requireByIdAsDBM(id: ID, opt: CommonDaoOptions = {}): Promise<DBM> {
|
|
213
217
|
const r = await this.getByIdAsDBM(id, opt)
|
|
214
218
|
if (!r) {
|
|
215
219
|
this.throwRequiredError(id, opt)
|
|
@@ -217,7 +221,7 @@ export class CommonDao<
|
|
|
217
221
|
return r
|
|
218
222
|
}
|
|
219
223
|
|
|
220
|
-
private throwRequiredError(id:
|
|
224
|
+
private throwRequiredError(id: ID, opt: CommonDaoOptions): never {
|
|
221
225
|
const table = opt.table || this.cfg.table
|
|
222
226
|
throw new AppError(`DB row required, but not found: ${table}.${id}`, {
|
|
223
227
|
code: DBLibError.DB_ROW_REQUIRED,
|
|
@@ -267,8 +271,8 @@ export class CommonDao<
|
|
|
267
271
|
/**
|
|
268
272
|
* Pass `table` to override table
|
|
269
273
|
*/
|
|
270
|
-
query(table?: string): RunnableDBQuery<BM, DBM, TM> {
|
|
271
|
-
return new RunnableDBQuery<BM, DBM, TM>(this, table)
|
|
274
|
+
query(table?: string): RunnableDBQuery<BM, DBM, TM, ID> {
|
|
275
|
+
return new RunnableDBQuery<BM, DBM, TM, ID>(this, table)
|
|
272
276
|
}
|
|
273
277
|
|
|
274
278
|
async runQuery(q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<Saved<BM>[]> {
|
|
@@ -514,18 +518,18 @@ export class CommonDao<
|
|
|
514
518
|
)
|
|
515
519
|
}
|
|
516
520
|
|
|
517
|
-
async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<
|
|
521
|
+
async queryIds(q: DBQuery<DBM>, opt: CommonDaoOptions = {}): Promise<ID[]> {
|
|
518
522
|
q.table = opt.table || q.table
|
|
519
523
|
const { rows } = await this.cfg.db.runQuery(q.select(['id']), opt)
|
|
520
524
|
return rows.map(r => r.id)
|
|
521
525
|
}
|
|
522
526
|
|
|
523
|
-
streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions = {}): ReadableTyped<
|
|
527
|
+
streamQueryIds(q: DBQuery<DBM>, opt: CommonDaoStreamOptions = {}): ReadableTyped<ID> {
|
|
524
528
|
q.table = opt.table || q.table
|
|
525
529
|
opt.errorMode ||= ErrorMode.SUPPRESS
|
|
526
530
|
|
|
527
531
|
return this.cfg.db.streamQuery<DBM>(q.select(['id']), opt).pipe(
|
|
528
|
-
transformMapSimple<
|
|
532
|
+
transformMapSimple<DBM, ID>(objectWithId => objectWithId.id, {
|
|
529
533
|
errorMode: ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
530
534
|
}),
|
|
531
535
|
)
|
|
@@ -533,8 +537,8 @@ export class CommonDao<
|
|
|
533
537
|
|
|
534
538
|
async streamQueryIdsForEach(
|
|
535
539
|
q: DBQuery<DBM>,
|
|
536
|
-
mapper: AsyncMapper<
|
|
537
|
-
opt: CommonDaoStreamForEachOptions<
|
|
540
|
+
mapper: AsyncMapper<ID, void>,
|
|
541
|
+
opt: CommonDaoStreamForEachOptions<ID> = {},
|
|
538
542
|
): Promise<void> {
|
|
539
543
|
q.table = opt.table || q.table
|
|
540
544
|
opt.errorMode = opt.errorMode || ErrorMode.SUPPRESS
|
|
@@ -545,9 +549,9 @@ export class CommonDao<
|
|
|
545
549
|
|
|
546
550
|
await _pipeline([
|
|
547
551
|
this.cfg.db.streamQuery<DBM>(q.select(['id']), opt),
|
|
548
|
-
transformMapSimple<
|
|
552
|
+
transformMapSimple<DBM, ID>(objectWithId => objectWithId.id),
|
|
549
553
|
transformTap(() => count++),
|
|
550
|
-
transformMap<
|
|
554
|
+
transformMap<ID, void>(mapper, {
|
|
551
555
|
...opt,
|
|
552
556
|
predicate: _passthroughPredicate,
|
|
553
557
|
}),
|
|
@@ -570,21 +574,18 @@ export class CommonDao<
|
|
|
570
574
|
*/
|
|
571
575
|
assignIdCreatedUpdated(obj: DBM, opt?: CommonDaoOptions): DBM
|
|
572
576
|
assignIdCreatedUpdated(obj: BM, opt?: CommonDaoOptions): Saved<BM>
|
|
573
|
-
assignIdCreatedUpdated(obj:
|
|
577
|
+
assignIdCreatedUpdated(obj: Unsaved<BM>, opt?: CommonDaoOptions): Saved<BM>
|
|
578
|
+
assignIdCreatedUpdated(obj: DBM | BM | Unsaved<BM>, opt: CommonDaoOptions = {}): DBM | Saved<BM> {
|
|
574
579
|
const now = Math.floor(Date.now() / 1000)
|
|
575
580
|
|
|
576
|
-
obj.id
|
|
581
|
+
obj.id ||= this.cfg.hooks!.createId?.(obj as BM)
|
|
577
582
|
|
|
578
583
|
if (this.cfg.created) {
|
|
579
|
-
|
|
580
|
-
created: (obj as any).created || (obj as any).updated || now,
|
|
581
|
-
})
|
|
584
|
+
obj['created'] ||= obj['updated'] || now
|
|
582
585
|
}
|
|
583
586
|
|
|
584
587
|
if (this.cfg.updated) {
|
|
585
|
-
|
|
586
|
-
updated: opt.preserveUpdatedCreated && (obj as any).updated ? (obj as any).updated : now,
|
|
587
|
-
})
|
|
588
|
+
obj['updated'] = opt.preserveUpdatedCreated && obj['updated'] ? obj['updated'] : now
|
|
588
589
|
}
|
|
589
590
|
|
|
590
591
|
return obj as any
|
|
@@ -594,11 +595,11 @@ export class CommonDao<
|
|
|
594
595
|
/**
|
|
595
596
|
* Mutates with id, created, updated
|
|
596
597
|
*/
|
|
597
|
-
async save(bm: BM
|
|
598
|
+
async save(bm: Unsaved<BM>, opt: CommonDaoSaveOptions<DBM> = {}): Promise<Saved<BM>> {
|
|
598
599
|
this.requireWriteAccess()
|
|
599
600
|
const idWasGenerated = !bm.id
|
|
600
601
|
this.assignIdCreatedUpdated(bm, opt) // mutates
|
|
601
|
-
const dbm = await this.bmToDBM(bm, opt)
|
|
602
|
+
const dbm = await this.bmToDBM(bm as BM, opt)
|
|
602
603
|
const table = opt.table || this.cfg.table
|
|
603
604
|
if (opt.ensureUniqueId && idWasGenerated) await this.ensureUniqueId(table, dbm)
|
|
604
605
|
if (this.cfg.immutable) await this.ensureImmutableDontExist(table, [dbm.id])
|
|
@@ -613,7 +614,7 @@ export class CommonDao<
|
|
|
613
614
|
return bm as any
|
|
614
615
|
}
|
|
615
616
|
|
|
616
|
-
private async ensureImmutableDontExist(table: string, ids:
|
|
617
|
+
private async ensureImmutableDontExist(table: string, ids: ID[]): Promise<void> {
|
|
617
618
|
await this.throwIfObjectExists(table, ids, [
|
|
618
619
|
DBLibError.OBJECT_IS_IMMUTABLE,
|
|
619
620
|
{
|
|
@@ -640,7 +641,7 @@ export class CommonDao<
|
|
|
640
641
|
|
|
641
642
|
private async throwIfObjectExists(
|
|
642
643
|
table: string,
|
|
643
|
-
ids:
|
|
644
|
+
ids: ID[],
|
|
644
645
|
errorMeta: [DBLibError, any],
|
|
645
646
|
): Promise<void> {
|
|
646
647
|
const existing = await this.cfg.db.getByIds<DBM>(table, ids)
|
|
@@ -656,11 +657,7 @@ export class CommonDao<
|
|
|
656
657
|
*
|
|
657
658
|
* Convenience method to replace 3 operations (loading+patching+saving) with one.
|
|
658
659
|
*/
|
|
659
|
-
async patch(
|
|
660
|
-
id: string,
|
|
661
|
-
patch: Partial<BM>,
|
|
662
|
-
opt: CommonDaoSaveOptions<DBM> = {},
|
|
663
|
-
): Promise<Saved<BM>> {
|
|
660
|
+
async patch(id: ID, patch: Partial<BM>, opt: CommonDaoSaveOptions<DBM> = {}): Promise<Saved<BM>> {
|
|
664
661
|
return await this.save(
|
|
665
662
|
{
|
|
666
663
|
...(await this.getByIdOrEmpty(id, patch, opt)),
|
|
@@ -670,11 +667,7 @@ export class CommonDao<
|
|
|
670
667
|
)
|
|
671
668
|
}
|
|
672
669
|
|
|
673
|
-
async patchAsDBM(
|
|
674
|
-
id: string,
|
|
675
|
-
patch: Partial<DBM>,
|
|
676
|
-
opt: CommonDaoSaveOptions<DBM> = {},
|
|
677
|
-
): Promise<DBM> {
|
|
670
|
+
async patchAsDBM(id: ID, patch: Partial<DBM>, opt: CommonDaoSaveOptions<DBM> = {}): Promise<DBM> {
|
|
678
671
|
const dbm =
|
|
679
672
|
(await this.getByIdAsDBM(id, opt)) ||
|
|
680
673
|
(this.create({ ...patch, id } as Partial<BM>, opt) as any as DBM)
|
|
@@ -711,11 +704,11 @@ export class CommonDao<
|
|
|
711
704
|
return dbm
|
|
712
705
|
}
|
|
713
706
|
|
|
714
|
-
async saveBatch(bms: BM[], opt: CommonDaoSaveOptions<DBM> = {}): Promise<Saved<BM>[]> {
|
|
707
|
+
async saveBatch(bms: Unsaved<BM>[], opt: CommonDaoSaveOptions<DBM> = {}): Promise<Saved<BM>[]> {
|
|
715
708
|
this.requireWriteAccess()
|
|
716
709
|
const table = opt.table || this.cfg.table
|
|
717
710
|
bms.forEach(bm => this.assignIdCreatedUpdated(bm, opt))
|
|
718
|
-
const dbms = await this.bmsToDBM(bms, opt)
|
|
711
|
+
const dbms = await this.bmsToDBM(bms as BM[], opt)
|
|
719
712
|
if (opt.ensureUniqueId) throw new AppError('ensureUniqueId is not supported in saveBatch')
|
|
720
713
|
if (this.cfg.immutable)
|
|
721
714
|
await this.ensureImmutableDontExist(
|
|
@@ -778,8 +771,8 @@ export class CommonDao<
|
|
|
778
771
|
* @returns number of deleted items
|
|
779
772
|
*/
|
|
780
773
|
async deleteById(id: undefined, opt?: CommonDaoOptions): Promise<0>
|
|
781
|
-
async deleteById(id?:
|
|
782
|
-
async deleteById(id?:
|
|
774
|
+
async deleteById(id?: ID, opt?: CommonDaoOptions): Promise<number>
|
|
775
|
+
async deleteById(id?: ID, opt: CommonDaoOptions = {}): Promise<number> {
|
|
783
776
|
if (!id) return 0
|
|
784
777
|
this.requireWriteAccess()
|
|
785
778
|
if (!opt.allowMutability) this.requireObjectMutability()
|
|
@@ -791,7 +784,7 @@ export class CommonDao<
|
|
|
791
784
|
return ids
|
|
792
785
|
}
|
|
793
786
|
|
|
794
|
-
async deleteByIds(ids:
|
|
787
|
+
async deleteByIds(ids: ID[], opt: CommonDaoOptions = {}): Promise<number> {
|
|
795
788
|
this.requireWriteAccess()
|
|
796
789
|
if (!opt.allowMutability) this.requireObjectMutability()
|
|
797
790
|
const op = `deleteByIds(${ids.join(', ')})`
|
|
@@ -823,7 +816,7 @@ export class CommonDao<
|
|
|
823
816
|
|
|
824
817
|
await _pipeline([
|
|
825
818
|
this.cfg.db.streamQuery<DBM>(q.select(['id']), opt),
|
|
826
|
-
transformMapSimple<
|
|
819
|
+
transformMapSimple<DBM, ID>(objectWithId => objectWithId.id, {
|
|
827
820
|
errorMode: ErrorMode.SUPPRESS,
|
|
828
821
|
}),
|
|
829
822
|
transformBuffer<string>({ batchSize }),
|
package/src/db.model.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { AnyObjectWithId, ObjectWithId } from '@naturalcycles/js-lib'
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Similar to SQL INSERT, UPDATE.
|
|
5
|
+
* Insert will fail if row already exists.
|
|
6
|
+
* Update will fail if row is missing.
|
|
7
|
+
* Upsert will auto-detect and use Insert or Update to not fail.
|
|
8
|
+
*
|
|
9
|
+
* Default is Upsert.
|
|
10
|
+
*/
|
|
11
|
+
export type CommonDBSaveMethod = 'upsert' | 'insert' | 'update'
|
|
3
12
|
|
|
4
13
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
5
14
|
export interface CommonDBOptions {}
|
|
@@ -7,17 +16,23 @@ export interface CommonDBOptions {}
|
|
|
7
16
|
/**
|
|
8
17
|
* All properties default to undefined.
|
|
9
18
|
*/
|
|
10
|
-
export interface CommonDBSaveOptions<ROW extends ObjectWithId = AnyObjectWithId>
|
|
19
|
+
export interface CommonDBSaveOptions<ROW extends Partial<ObjectWithId> = AnyObjectWithId>
|
|
11
20
|
extends CommonDBOptions {
|
|
12
21
|
excludeFromIndexes?: (keyof ROW)[]
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Default is `upsert`
|
|
25
|
+
*/
|
|
26
|
+
saveMethod?: CommonDBSaveMethod
|
|
13
27
|
}
|
|
14
28
|
|
|
15
29
|
export type CommonDBStreamOptions = CommonDBOptions
|
|
16
30
|
|
|
17
31
|
export interface CommonDBCreateOptions extends CommonDBOptions {
|
|
18
32
|
/**
|
|
19
|
-
* @default false
|
|
20
33
|
* Caution! If set to true - will actually DROP the table!
|
|
34
|
+
*
|
|
35
|
+
* @default false
|
|
21
36
|
*/
|
|
22
37
|
dropIfExists?: boolean
|
|
23
38
|
}
|
|
@@ -51,19 +66,3 @@ export enum DBModelType {
|
|
|
51
66
|
BM = 'BM',
|
|
52
67
|
TM = 'TM',
|
|
53
68
|
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Interface for a module (lib) that implements CommonDB.
|
|
57
|
-
*
|
|
58
|
-
* Example:
|
|
59
|
-
*
|
|
60
|
-
* const lib: CommonDBModule = require('mysql-lib')
|
|
61
|
-
* const db = lib.getDB()
|
|
62
|
-
*/
|
|
63
|
-
export interface CommonDBAdapter {
|
|
64
|
-
/**
|
|
65
|
-
* @param cfg was read from SECRET_DB${i} by secret('SECRET_DB${i}') method and passed there.
|
|
66
|
-
* It's a string that can contain e.g JSON.stringified configuration object (depends on the adapter).
|
|
67
|
-
*/
|
|
68
|
-
getDBAdapter(cfg?: string): CommonDB
|
|
69
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -6,27 +6,19 @@ import { DBLibError } from './cnst'
|
|
|
6
6
|
import { CommonDB } from './common.db'
|
|
7
7
|
import { CommonDao } from './commondao/common.dao'
|
|
8
8
|
import {
|
|
9
|
-
CommonDaoAnonymizeHook,
|
|
10
|
-
CommonDaoBeforeBMToDBMHook,
|
|
11
|
-
CommonDaoBeforeBMToTMHook,
|
|
12
|
-
CommonDaoBeforeCreateHook,
|
|
13
|
-
CommonDaoBeforeDBMToBMHook,
|
|
14
|
-
CommonDaoBeforeDBMValidateHook,
|
|
15
|
-
CommonDaoBeforeTMToBMHook,
|
|
16
9
|
CommonDaoCfg,
|
|
17
|
-
CommonDaoCreateIdHook,
|
|
18
10
|
CommonDaoCreateOptions,
|
|
19
11
|
CommonDaoLogLevel,
|
|
20
12
|
CommonDaoOptions,
|
|
21
|
-
CommonDaoParseNaturalIdHook,
|
|
22
13
|
CommonDaoSaveOptions,
|
|
23
14
|
CommonDaoStreamForEachOptions,
|
|
24
15
|
CommonDaoStreamOptions,
|
|
16
|
+
CommonDaoHooks,
|
|
25
17
|
} from './commondao/common.dao.model'
|
|
26
18
|
import {
|
|
27
|
-
CommonDBAdapter,
|
|
28
19
|
CommonDBCreateOptions,
|
|
29
20
|
CommonDBOptions,
|
|
21
|
+
CommonDBSaveMethod,
|
|
30
22
|
CommonDBSaveOptions,
|
|
31
23
|
CommonDBStreamOptions,
|
|
32
24
|
DBDeleteByIdsOperation,
|
|
@@ -68,28 +60,20 @@ export type {
|
|
|
68
60
|
CommonDaoSaveOptions,
|
|
69
61
|
CommonDaoStreamForEachOptions,
|
|
70
62
|
CommonDaoStreamOptions,
|
|
63
|
+
CommonDaoHooks,
|
|
71
64
|
CommonDBOptions,
|
|
72
65
|
CommonDBSaveOptions,
|
|
66
|
+
CommonDBSaveMethod,
|
|
73
67
|
CommonDBStreamOptions,
|
|
74
68
|
CommonDBCreateOptions,
|
|
75
69
|
CommonDB,
|
|
76
70
|
RunQueryResult,
|
|
77
71
|
CommonDaoCfg,
|
|
78
|
-
CommonDaoCreateIdHook,
|
|
79
|
-
CommonDaoParseNaturalIdHook,
|
|
80
|
-
CommonDaoBeforeCreateHook,
|
|
81
|
-
CommonDaoBeforeDBMValidateHook,
|
|
82
|
-
CommonDaoBeforeDBMToBMHook,
|
|
83
|
-
CommonDaoBeforeBMToDBMHook,
|
|
84
|
-
CommonDaoBeforeTMToBMHook,
|
|
85
|
-
CommonDaoBeforeBMToTMHook,
|
|
86
|
-
CommonDaoAnonymizeHook,
|
|
87
72
|
InMemoryDBCfg,
|
|
88
73
|
InMemoryKeyValueDBCfg,
|
|
89
74
|
DBPipelineBackupOptions,
|
|
90
75
|
DBPipelineRestoreOptions,
|
|
91
76
|
DBPipelineCopyOptions,
|
|
92
|
-
CommonDBAdapter,
|
|
93
77
|
DBOperation,
|
|
94
78
|
DBSaveBatchOperation,
|
|
95
79
|
DBDeleteByIdsOperation,
|
package/src/model.util.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { stringId } from '@naturalcycles/nodejs-lib'
|
|
2
|
-
import { CreatedUpdated, CreatedUpdatedId
|
|
2
|
+
import { CreatedUpdated, CreatedUpdatedId } from '@naturalcycles/js-lib'
|
|
3
3
|
|
|
4
4
|
export function createdUpdatedFields(
|
|
5
5
|
existingObject?: Partial<CreatedUpdated> | null,
|
|
@@ -12,8 +12,8 @@ export function createdUpdatedFields(
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function createdUpdatedIdFields(
|
|
15
|
-
existingObject?: Partial<CreatedUpdatedId
|
|
16
|
-
): CreatedUpdatedId {
|
|
15
|
+
existingObject?: Partial<CreatedUpdatedId<string>> | null,
|
|
16
|
+
): CreatedUpdatedId<string> {
|
|
17
17
|
const now = Math.floor(Date.now() / 1000)
|
|
18
18
|
return {
|
|
19
19
|
created: existingObject?.created || now,
|
|
@@ -22,12 +22,6 @@ export function createdUpdatedIdFields(
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function idField(existingObject?: Partial<CreatedUpdatedId> | null): ObjectWithId {
|
|
26
|
-
return {
|
|
27
|
-
id: existingObject?.id || stringId(),
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
25
|
export function deserializeJsonField<T = any>(f?: string): T {
|
|
32
26
|
return JSON.parse(f || '{}')
|
|
33
27
|
}
|
package/src/query/dbQuery.ts
CHANGED
|
@@ -216,14 +216,15 @@ export class DBQuery<ROW extends ObjectWithId = AnyObjectWithId> {
|
|
|
216
216
|
* DBQuery that has additional method to support Fluent API style.
|
|
217
217
|
*/
|
|
218
218
|
export class RunnableDBQuery<
|
|
219
|
-
BM extends Partial<ObjectWithId
|
|
220
|
-
DBM extends ObjectWithId
|
|
219
|
+
BM extends Partial<ObjectWithId<ID>>,
|
|
220
|
+
DBM extends ObjectWithId<ID>,
|
|
221
221
|
TM,
|
|
222
|
+
ID extends string | number,
|
|
222
223
|
> extends DBQuery<DBM> {
|
|
223
224
|
/**
|
|
224
225
|
* Pass `table` to override table.
|
|
225
226
|
*/
|
|
226
|
-
constructor(public dao: CommonDao<BM, DBM, TM>, table?: string) {
|
|
227
|
+
constructor(public dao: CommonDao<BM, DBM, TM, ID>, table?: string) {
|
|
227
228
|
super(table || dao.cfg.table)
|
|
228
229
|
}
|
|
229
230
|
|
|
@@ -281,17 +282,17 @@ export class RunnableDBQuery<
|
|
|
281
282
|
return this.dao.streamQueryAsDBM(this, opt)
|
|
282
283
|
}
|
|
283
284
|
|
|
284
|
-
async queryIds(opt?: CommonDaoOptions): Promise<
|
|
285
|
+
async queryIds(opt?: CommonDaoOptions): Promise<ID[]> {
|
|
285
286
|
return await this.dao.queryIds(this, opt)
|
|
286
287
|
}
|
|
287
288
|
|
|
288
|
-
streamQueryIds(opt?: CommonDaoStreamOptions): ReadableTyped<
|
|
289
|
+
streamQueryIds(opt?: CommonDaoStreamOptions): ReadableTyped<ID> {
|
|
289
290
|
return this.dao.streamQueryIds(this, opt)
|
|
290
291
|
}
|
|
291
292
|
|
|
292
293
|
async streamQueryIdsForEach(
|
|
293
|
-
mapper: AsyncMapper<
|
|
294
|
-
opt?: CommonDaoStreamForEachOptions<
|
|
294
|
+
mapper: AsyncMapper<ID, void>,
|
|
295
|
+
opt?: CommonDaoStreamForEachOptions<ID>,
|
|
295
296
|
): Promise<void> {
|
|
296
297
|
await this.dao.streamQueryIdsForEach(this, mapper, opt)
|
|
297
298
|
}
|
package/src/testing/dbTest.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface CommonDBImplementationFeatures {
|
|
|
21
21
|
dbQueryFilterIn?: boolean
|
|
22
22
|
dbQueryOrder?: boolean
|
|
23
23
|
dbQuerySelectFields?: boolean
|
|
24
|
+
insert?: boolean
|
|
25
|
+
update?: boolean
|
|
24
26
|
|
|
25
27
|
createTable?: boolean
|
|
26
28
|
tableSchemas?: boolean
|
|
@@ -80,6 +82,8 @@ export function runCommonDBTest(
|
|
|
80
82
|
// dbQueryFilterIn = true,
|
|
81
83
|
dbQueryOrder = true,
|
|
82
84
|
dbQuerySelectFields = true,
|
|
85
|
+
insert = true,
|
|
86
|
+
update = true,
|
|
83
87
|
streaming = true,
|
|
84
88
|
strongConsistency = true,
|
|
85
89
|
bufferSupport = true,
|
|
@@ -176,10 +180,28 @@ export function runCommonDBTest(
|
|
|
176
180
|
})
|
|
177
181
|
}
|
|
178
182
|
|
|
183
|
+
if (update) {
|
|
184
|
+
test('saveBatch UPDATE method should throw', async () => {
|
|
185
|
+
await expect(db.saveBatch(TEST_TABLE, items, { saveMethod: 'update' })).rejects.toThrow()
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
|
|
179
189
|
test('saveBatch test items', async () => {
|
|
180
190
|
await db.saveBatch(TEST_TABLE, items)
|
|
181
191
|
})
|
|
182
192
|
|
|
193
|
+
if (insert) {
|
|
194
|
+
test('saveBatch INSERT method should throw', async () => {
|
|
195
|
+
await expect(db.saveBatch(TEST_TABLE, items, { saveMethod: 'insert' })).rejects.toThrow()
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (update) {
|
|
200
|
+
test('saveBatch UPDATE method should pass', async () => {
|
|
201
|
+
await db.saveBatch(TEST_TABLE, items, { saveMethod: 'update' })
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
183
205
|
// GET not empty
|
|
184
206
|
test('getByIds all items', async () => {
|
|
185
207
|
const rows = await db.getByIds<TestItemDBM>(TEST_TABLE, items.map(i => i.id).concat('abcd'))
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDBAdapter = void 0;
|
|
4
|
-
const inMemory_db_1 = require("./inMemory.db");
|
|
5
|
-
function getDBAdapter() {
|
|
6
|
-
return new inMemory_db_1.InMemoryDB();
|
|
7
|
-
}
|
|
8
|
-
exports.getDBAdapter = getDBAdapter;
|