@naturalcycles/db-lib 8.60.1 → 9.0.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 +3 -4
- package/dist/adapter/cachedb/cache.db.js +5 -4
- package/dist/adapter/cachedb/cache.db.model.d.ts +2 -2
- package/dist/adapter/file/file.db.d.ts +15 -7
- package/dist/adapter/file/file.db.js +93 -57
- package/dist/adapter/inmemory/inMemory.db.d.ts +30 -4
- package/dist/adapter/inmemory/inMemory.db.js +87 -31
- package/dist/base.common.db.d.ts +7 -10
- package/dist/base.common.db.js +11 -7
- package/dist/common.db.d.ts +55 -3
- package/dist/common.db.js +23 -0
- package/dist/commondao/common.dao.d.ts +17 -9
- package/dist/commondao/common.dao.js +82 -69
- package/dist/commondao/common.dao.model.d.ts +0 -10
- package/dist/db.model.d.ts +12 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/testing/daoTest.d.ts +2 -2
- package/dist/testing/daoTest.js +29 -39
- package/dist/testing/dbTest.d.ts +1 -39
- package/dist/testing/dbTest.js +40 -49
- package/dist/testing/index.d.ts +2 -2
- package/dist/timeseries/commonTimeSeriesDao.js +5 -6
- package/dist/transaction/dbTransaction.util.d.ts +17 -4
- package/dist/transaction/dbTransaction.util.js +46 -22
- package/dist/validation/index.js +2 -2
- package/package.json +1 -1
- package/src/adapter/cachedb/cache.db.model.ts +7 -2
- package/src/adapter/cachedb/cache.db.ts +7 -8
- package/src/adapter/file/file.db.ts +121 -69
- package/src/adapter/inmemory/inMemory.db.ts +103 -29
- package/src/base.common.db.ts +20 -11
- package/src/common.db.ts +79 -2
- package/src/commondao/common.dao.model.ts +0 -11
- package/src/commondao/common.dao.ts +103 -89
- package/src/db.model.ts +15 -2
- package/src/index.ts +0 -1
- package/src/testing/daoTest.ts +32 -52
- package/src/testing/dbTest.ts +42 -119
- package/src/testing/index.ts +2 -12
- package/src/timeseries/commonTimeSeriesDao.ts +5 -6
- package/src/transaction/dbTransaction.util.ts +61 -22
- package/src/validation/index.ts +2 -2
- package/dist/transaction/dbTransaction.d.ts +0 -27
- package/dist/transaction/dbTransaction.js +0 -64
- package/src/transaction/dbTransaction.ts +0 -67
package/dist/common.db.d.ts
CHANGED
|
@@ -1,9 +1,35 @@
|
|
|
1
1
|
import { JsonSchemaObject, JsonSchemaRootObject, ObjectWithId } from '@naturalcycles/js-lib';
|
|
2
2
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
3
|
-
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, DBPatch, RunQueryResult } from './db.model';
|
|
3
|
+
import { CommonDBCreateOptions, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, DBPatch, DBTransaction, RunQueryResult } from './db.model';
|
|
4
4
|
import { DBQuery } from './query/dbQuery';
|
|
5
|
-
|
|
5
|
+
export declare enum CommonDBType {
|
|
6
|
+
'document' = "document",
|
|
7
|
+
'relational' = "relational"
|
|
8
|
+
}
|
|
6
9
|
export interface CommonDB {
|
|
10
|
+
/**
|
|
11
|
+
* Relational databases are expected to return `null` for all missing properties.
|
|
12
|
+
*/
|
|
13
|
+
dbType: CommonDBType;
|
|
14
|
+
/**
|
|
15
|
+
* Manifest of supported features.
|
|
16
|
+
*/
|
|
17
|
+
support: CommonDBSupport;
|
|
18
|
+
supportsQueries?: boolean;
|
|
19
|
+
supportsDBQueryFilter?: boolean;
|
|
20
|
+
supportsDBQueryFilterIn?: boolean;
|
|
21
|
+
supportsDBQueryOrder?: boolean;
|
|
22
|
+
supportsDBQuerySelectFields?: boolean;
|
|
23
|
+
supportsInsertSaveMethod?: boolean;
|
|
24
|
+
supportsUpdateSaveMethod?: boolean;
|
|
25
|
+
supportsUpdateByQuery?: boolean;
|
|
26
|
+
supportsDBIncrement?: boolean;
|
|
27
|
+
supportsCreateTable?: boolean;
|
|
28
|
+
supportsTableSchemas?: boolean;
|
|
29
|
+
supportsStreaming?: boolean;
|
|
30
|
+
supportsBufferValues?: boolean;
|
|
31
|
+
supportsNullValues?: boolean;
|
|
32
|
+
supportsTransactions?: boolean;
|
|
7
33
|
/**
|
|
8
34
|
* Checks that connection/credentials/etc is ok.
|
|
9
35
|
* Also acts as a "warmup request" for a DB.
|
|
@@ -42,6 +68,11 @@ export interface CommonDB {
|
|
|
42
68
|
* rows can have missing ids only if DB supports auto-generating them (like mysql auto_increment).
|
|
43
69
|
*/
|
|
44
70
|
saveBatch: <ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>) => Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Returns number of deleted items.
|
|
73
|
+
* Not supported by all implementations (e.g Datastore will always return same number as number of ids).
|
|
74
|
+
*/
|
|
75
|
+
deleteByIds: (table: string, ids: string[], opt?: CommonDBOptions) => Promise<number>;
|
|
45
76
|
/**
|
|
46
77
|
* Returns number of deleted items.
|
|
47
78
|
* Not supported by all implementations (e.g Datastore will always return same number as number of ids).
|
|
@@ -70,5 +101,26 @@ export interface CommonDB {
|
|
|
70
101
|
* Should be implemented as a Transaction (best effort), which means that
|
|
71
102
|
* either ALL or NONE of the operations should be applied.
|
|
72
103
|
*/
|
|
73
|
-
|
|
104
|
+
createTransaction: () => Promise<DBTransaction>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Manifest of supported features.
|
|
108
|
+
*/
|
|
109
|
+
export interface CommonDBSupport {
|
|
110
|
+
queries?: boolean;
|
|
111
|
+
dbQueryFilter?: boolean;
|
|
112
|
+
dbQueryFilterIn?: boolean;
|
|
113
|
+
dbQueryOrder?: boolean;
|
|
114
|
+
dbQuerySelectFields?: boolean;
|
|
115
|
+
insertSaveMethod?: boolean;
|
|
116
|
+
updateSaveMethod?: boolean;
|
|
117
|
+
updateByQuery?: boolean;
|
|
118
|
+
dbIncrement?: boolean;
|
|
119
|
+
createTable?: boolean;
|
|
120
|
+
tableSchemas?: boolean;
|
|
121
|
+
streaming?: boolean;
|
|
122
|
+
bufferValues?: boolean;
|
|
123
|
+
nullValues?: boolean;
|
|
124
|
+
transactions?: boolean;
|
|
74
125
|
}
|
|
126
|
+
export declare const commonDBFullSupport: CommonDBSupport;
|
package/dist/common.db.js
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.commonDBFullSupport = exports.CommonDBType = void 0;
|
|
4
|
+
var CommonDBType;
|
|
5
|
+
(function (CommonDBType) {
|
|
6
|
+
CommonDBType["document"] = "document";
|
|
7
|
+
CommonDBType["relational"] = "relational";
|
|
8
|
+
})(CommonDBType || (exports.CommonDBType = CommonDBType = {}));
|
|
9
|
+
exports.commonDBFullSupport = {
|
|
10
|
+
queries: true,
|
|
11
|
+
dbQueryFilter: true,
|
|
12
|
+
dbQueryFilterIn: true,
|
|
13
|
+
dbQueryOrder: true,
|
|
14
|
+
dbQuerySelectFields: true,
|
|
15
|
+
insertSaveMethod: true,
|
|
16
|
+
updateSaveMethod: true,
|
|
17
|
+
updateByQuery: true,
|
|
18
|
+
dbIncrement: true,
|
|
19
|
+
createTable: true,
|
|
20
|
+
tableSchemas: true,
|
|
21
|
+
streaming: true,
|
|
22
|
+
bufferValues: true,
|
|
23
|
+
nullValues: true,
|
|
24
|
+
transactions: true,
|
|
25
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Transform } from 'node:stream';
|
|
3
|
-
import { AnyObject, AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId,
|
|
3
|
+
import { AnyObject, AsyncMapper, JsonSchemaObject, JsonSchemaRootObject, ObjectWithId, Saved, UnixTimestampMillisNumber, Unsaved, ZodSchema } from '@naturalcycles/js-lib';
|
|
4
4
|
import { AjvSchema, ObjectSchema, ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
5
|
-
import {
|
|
5
|
+
import { DBModelType, DBPatch, DBTransaction, RunQueryResult } from '../db.model';
|
|
6
6
|
import { DBQuery, RunnableDBQuery } from '../query/dbQuery';
|
|
7
7
|
import { CommonDaoCfg, CommonDaoCreateOptions, CommonDaoOptions, CommonDaoSaveBatchOptions, CommonDaoSaveOptions, CommonDaoStreamDeleteOptions, CommonDaoStreamForEachOptions, CommonDaoStreamOptions, CommonDaoStreamSaveOptions } from './common.dao.model';
|
|
8
8
|
/**
|
|
@@ -85,12 +85,6 @@ export declare class CommonDao<BM extends Partial<ObjectWithId>, DBM extends Obj
|
|
|
85
85
|
assignIdCreatedUpdated(obj: DBM, opt?: CommonDaoOptions): DBM;
|
|
86
86
|
assignIdCreatedUpdated(obj: BM, opt?: CommonDaoOptions): Saved<BM>;
|
|
87
87
|
assignIdCreatedUpdated(obj: Unsaved<BM>, opt?: CommonDaoOptions): Saved<BM>;
|
|
88
|
-
tx: {
|
|
89
|
-
save: (bm: Unsaved<BM>, opt?: CommonDaoSaveBatchOptions<DBM>) => Promise<DBSaveBatchOperation | undefined>;
|
|
90
|
-
saveBatch: (bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>) => Promise<DBSaveBatchOperation | undefined>;
|
|
91
|
-
deleteByIds: (ids: string[], opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation | undefined>;
|
|
92
|
-
deleteById: (id: string | null | undefined, opt?: CommonDaoOptions) => Promise<DBDeleteByIdsOperation | undefined>;
|
|
93
|
-
};
|
|
94
88
|
/**
|
|
95
89
|
* Mutates with id, created, updated
|
|
96
90
|
*/
|
|
@@ -175,9 +169,23 @@ export declare class CommonDao<BM extends Partial<ObjectWithId>, DBM extends Obj
|
|
|
175
169
|
* Proxy to this.cfg.db.ping
|
|
176
170
|
*/
|
|
177
171
|
ping(): Promise<void>;
|
|
178
|
-
|
|
172
|
+
useTransaction(fn: (tx: CommonDaoTransaction) => Promise<void>): Promise<void>;
|
|
173
|
+
createTransaction(): Promise<CommonDaoTransaction>;
|
|
179
174
|
protected logResult(started: number, op: string, res: any, table: string): void;
|
|
180
175
|
protected logSaveResult(started: number, op: string, table: string): void;
|
|
181
176
|
protected logStarted(op: string, table: string, force?: boolean): UnixTimestampMillisNumber;
|
|
182
177
|
protected logSaveStarted(op: string, items: any, table: string): UnixTimestampMillisNumber;
|
|
183
178
|
}
|
|
179
|
+
export declare class CommonDaoTransaction {
|
|
180
|
+
private tx;
|
|
181
|
+
constructor(tx: DBTransaction);
|
|
182
|
+
commit(): Promise<void>;
|
|
183
|
+
rollback(): Promise<void>;
|
|
184
|
+
getById<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, id: string, opt?: CommonDaoOptions): Promise<Saved<BM> | null>;
|
|
185
|
+
getByIds<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, ids: string[], opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
|
|
186
|
+
runQuery<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, q: DBQuery<DBM>, opt?: CommonDaoOptions): Promise<Saved<BM>[]>;
|
|
187
|
+
save<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, bm: Unsaved<BM>, opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>>;
|
|
188
|
+
saveBatch<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId>(dao: CommonDao<BM, DBM, any>, bms: Unsaved<BM>[], opt?: CommonDaoSaveBatchOptions<DBM>): Promise<Saved<BM>[]>;
|
|
189
|
+
deleteById(dao: CommonDao<any>, id: string, opt?: CommonDaoOptions): Promise<number>;
|
|
190
|
+
deleteByIds(dao: CommonDao<any>, ids: string[], opt?: CommonDaoOptions): Promise<number>;
|
|
191
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CommonDao = void 0;
|
|
3
|
+
exports.CommonDaoTransaction = exports.CommonDao = void 0;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
6
6
|
const cnst_1 = require("../cnst");
|
|
7
7
|
const db_model_1 = require("../db.model");
|
|
8
8
|
const dbQuery_1 = require("../query/dbQuery");
|
|
9
|
-
const dbTransaction_1 = require("../transaction/dbTransaction");
|
|
10
9
|
const common_dao_model_1 = require("./common.dao.model");
|
|
11
10
|
const isGAE = !!process.env['GAE_INSTANCE'];
|
|
12
11
|
const isCI = !!process.env['CI'];
|
|
@@ -20,57 +19,6 @@ const isCI = !!process.env['CI'];
|
|
|
20
19
|
class CommonDao {
|
|
21
20
|
constructor(cfg) {
|
|
22
21
|
this.cfg = cfg;
|
|
23
|
-
this.tx = {
|
|
24
|
-
save: async (bm, opt = {}) => {
|
|
25
|
-
// .save actually returns DBM (not BM) when it detects `opt.tx === true`
|
|
26
|
-
const row = (await this.save(bm, { ...opt, tx: true }));
|
|
27
|
-
if (row === null)
|
|
28
|
-
return;
|
|
29
|
-
return {
|
|
30
|
-
type: 'saveBatch',
|
|
31
|
-
table: this.cfg.table,
|
|
32
|
-
rows: [row],
|
|
33
|
-
opt: {
|
|
34
|
-
excludeFromIndexes: this.cfg.excludeFromIndexes,
|
|
35
|
-
...opt,
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
},
|
|
39
|
-
saveBatch: async (bms, opt = {}) => {
|
|
40
|
-
const rows = (await this.saveBatch(bms, { ...opt, tx: true }));
|
|
41
|
-
if (!rows.length)
|
|
42
|
-
return;
|
|
43
|
-
return {
|
|
44
|
-
type: 'saveBatch',
|
|
45
|
-
table: this.cfg.table,
|
|
46
|
-
rows,
|
|
47
|
-
opt: {
|
|
48
|
-
excludeFromIndexes: this.cfg.excludeFromIndexes,
|
|
49
|
-
...opt,
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
},
|
|
53
|
-
deleteByIds: async (ids, opt = {}) => {
|
|
54
|
-
if (!ids.length)
|
|
55
|
-
return;
|
|
56
|
-
return {
|
|
57
|
-
type: 'deleteByIds',
|
|
58
|
-
table: this.cfg.table,
|
|
59
|
-
ids,
|
|
60
|
-
opt,
|
|
61
|
-
};
|
|
62
|
-
},
|
|
63
|
-
deleteById: async (id, opt = {}) => {
|
|
64
|
-
if (!id)
|
|
65
|
-
return;
|
|
66
|
-
return {
|
|
67
|
-
type: 'deleteByIds',
|
|
68
|
-
table: this.cfg.table,
|
|
69
|
-
ids: [id],
|
|
70
|
-
opt,
|
|
71
|
-
};
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
22
|
this.cfg = {
|
|
75
23
|
// Default is to NOT log in AppEngine and in CI,
|
|
76
24
|
// otherwise to log Operations
|
|
@@ -562,13 +510,9 @@ class CommonDao {
|
|
|
562
510
|
let dbm = await this.bmToDBM(bm, opt);
|
|
563
511
|
if (this.cfg.hooks.beforeSave) {
|
|
564
512
|
dbm = (await this.cfg.hooks.beforeSave(dbm));
|
|
565
|
-
if (dbm === null
|
|
513
|
+
if (dbm === null)
|
|
566
514
|
return bm;
|
|
567
515
|
}
|
|
568
|
-
if (opt.tx) {
|
|
569
|
-
// May return `null`, in which case it'll be skipped
|
|
570
|
-
return dbm;
|
|
571
|
-
}
|
|
572
516
|
const table = opt.table || this.cfg.table;
|
|
573
517
|
if (opt.ensureUniqueId && idWasGenerated)
|
|
574
518
|
await this.ensureUniqueId(table, dbm);
|
|
@@ -706,9 +650,6 @@ class CommonDao {
|
|
|
706
650
|
if (this.cfg.hooks.beforeSave && dbms.length) {
|
|
707
651
|
dbms = (await (0, js_lib_1.pMap)(dbms, async (dbm) => await this.cfg.hooks.beforeSave(dbm))).filter(js_lib_1._isTruthy);
|
|
708
652
|
}
|
|
709
|
-
if (opt.tx) {
|
|
710
|
-
return dbms;
|
|
711
|
-
}
|
|
712
653
|
if (opt.ensureUniqueId)
|
|
713
654
|
throw new js_lib_1.AppError('ensureUniqueId is not supported in saveBatch');
|
|
714
655
|
if (this.cfg.immutable && !opt.allowMutability && !opt.saveMethod) {
|
|
@@ -792,7 +733,7 @@ class CommonDao {
|
|
|
792
733
|
let dbm = await this.bmToDBM(bm, opt);
|
|
793
734
|
if (beforeSave) {
|
|
794
735
|
dbm = (await beforeSave(dbm));
|
|
795
|
-
if (dbm === null
|
|
736
|
+
if (dbm === null)
|
|
796
737
|
return js_lib_1.SKIP;
|
|
797
738
|
}
|
|
798
739
|
return dbm;
|
|
@@ -828,7 +769,7 @@ class CommonDao {
|
|
|
828
769
|
const op = `deleteById(${id})`;
|
|
829
770
|
const table = opt.table || this.cfg.table;
|
|
830
771
|
const started = this.logStarted(op, table);
|
|
831
|
-
const count = await this.cfg.db.
|
|
772
|
+
const count = await this.cfg.db.deleteByIds(table, [id], opt);
|
|
832
773
|
this.logSaveResult(started, op, table);
|
|
833
774
|
return count;
|
|
834
775
|
}
|
|
@@ -840,7 +781,7 @@ class CommonDao {
|
|
|
840
781
|
const op = `deleteByIds(${ids.join(', ')})`;
|
|
841
782
|
const table = opt.table || this.cfg.table;
|
|
842
783
|
const started = this.logStarted(op, table);
|
|
843
|
-
const count = await this.cfg.db.
|
|
784
|
+
const count = await this.cfg.db.deleteByIds(table, ids, opt);
|
|
844
785
|
this.logSaveResult(started, op, table);
|
|
845
786
|
return count;
|
|
846
787
|
}
|
|
@@ -1050,11 +991,21 @@ class CommonDao {
|
|
|
1050
991
|
async ping() {
|
|
1051
992
|
await this.cfg.db.ping();
|
|
1052
993
|
}
|
|
1053
|
-
async
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
994
|
+
async useTransaction(fn) {
|
|
995
|
+
const tx = await this.cfg.db.createTransaction();
|
|
996
|
+
const daoTx = new CommonDaoTransaction(tx);
|
|
997
|
+
try {
|
|
998
|
+
await fn(daoTx);
|
|
999
|
+
await daoTx.commit();
|
|
1000
|
+
}
|
|
1001
|
+
catch (err) {
|
|
1002
|
+
await daoTx.rollback();
|
|
1003
|
+
throw err;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
async createTransaction() {
|
|
1007
|
+
const tx = await this.cfg.db.createTransaction();
|
|
1008
|
+
return new CommonDaoTransaction(tx);
|
|
1058
1009
|
}
|
|
1059
1010
|
logResult(started, op, res, table) {
|
|
1060
1011
|
if (!this.cfg.logLevel)
|
|
@@ -1111,3 +1062,65 @@ class CommonDao {
|
|
|
1111
1062
|
}
|
|
1112
1063
|
}
|
|
1113
1064
|
exports.CommonDao = CommonDao;
|
|
1065
|
+
class CommonDaoTransaction {
|
|
1066
|
+
constructor(tx) {
|
|
1067
|
+
this.tx = tx;
|
|
1068
|
+
}
|
|
1069
|
+
async commit() {
|
|
1070
|
+
await this.tx.commit();
|
|
1071
|
+
}
|
|
1072
|
+
async rollback() {
|
|
1073
|
+
try {
|
|
1074
|
+
await this.tx.rollback();
|
|
1075
|
+
}
|
|
1076
|
+
catch (err) {
|
|
1077
|
+
console.log(err);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
async getById(dao, id, opt) {
|
|
1081
|
+
return (await this.getByIds(dao, [id], opt))[0] || null;
|
|
1082
|
+
}
|
|
1083
|
+
async getByIds(dao, ids, opt) {
|
|
1084
|
+
try {
|
|
1085
|
+
return await dao.getByIds(ids, { ...opt, tx: this.tx });
|
|
1086
|
+
}
|
|
1087
|
+
catch (err) {
|
|
1088
|
+
await this.rollback();
|
|
1089
|
+
throw err;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
async runQuery(dao, q, opt) {
|
|
1093
|
+
try {
|
|
1094
|
+
return await dao.runQuery(q, { ...opt, tx: this.tx });
|
|
1095
|
+
}
|
|
1096
|
+
catch (err) {
|
|
1097
|
+
await this.rollback();
|
|
1098
|
+
throw err;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
async save(dao, bm, opt) {
|
|
1102
|
+
return (await this.saveBatch(dao, [bm], opt))[0];
|
|
1103
|
+
}
|
|
1104
|
+
async saveBatch(dao, bms, opt) {
|
|
1105
|
+
try {
|
|
1106
|
+
return await dao.saveBatch(bms, { ...opt, tx: this.tx });
|
|
1107
|
+
}
|
|
1108
|
+
catch (err) {
|
|
1109
|
+
await this.rollback();
|
|
1110
|
+
throw err;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
async deleteById(dao, id, opt) {
|
|
1114
|
+
return await this.deleteByIds(dao, [id], opt);
|
|
1115
|
+
}
|
|
1116
|
+
async deleteByIds(dao, ids, opt) {
|
|
1117
|
+
try {
|
|
1118
|
+
return await dao.deleteByIds(ids, { ...opt, tx: this.tx });
|
|
1119
|
+
}
|
|
1120
|
+
catch (err) {
|
|
1121
|
+
await this.rollback();
|
|
1122
|
+
throw err;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
exports.CommonDaoTransaction = CommonDaoTransaction;
|
|
@@ -222,16 +222,6 @@ export interface CommonDaoOptions extends CommonDBOptions {
|
|
|
222
222
|
* Useful e.g in AirtableDB where you can have one Dao to control multiple tables.
|
|
223
223
|
*/
|
|
224
224
|
table?: string;
|
|
225
|
-
/**
|
|
226
|
-
* If passed - operation will not be performed immediately, but instead "added" to the transaction.
|
|
227
|
-
* In the end - transaction needs to be committed (by calling `commit`).
|
|
228
|
-
* This API is inspired by Datastore API.
|
|
229
|
-
*
|
|
230
|
-
* Only applicable to save* and delete* operations
|
|
231
|
-
*
|
|
232
|
-
* @experimental
|
|
233
|
-
*/
|
|
234
|
-
tx?: boolean;
|
|
235
225
|
}
|
|
236
226
|
export interface CommonDaoSaveOptions<BM extends Partial<ObjectWithId>, DBM extends ObjectWithId> extends CommonDaoSaveBatchOptions<DBM> {
|
|
237
227
|
/**
|
package/dist/db.model.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ObjectWithId } from '@naturalcycles/js-lib';
|
|
1
|
+
import type { ObjectWithId } from '@naturalcycles/js-lib';
|
|
2
2
|
/**
|
|
3
3
|
* Similar to SQL INSERT, UPDATE.
|
|
4
4
|
* Insert will fail if row already exists.
|
|
@@ -8,7 +8,18 @@ import { ObjectWithId } from '@naturalcycles/js-lib';
|
|
|
8
8
|
* Default is Upsert.
|
|
9
9
|
*/
|
|
10
10
|
export type CommonDBSaveMethod = 'upsert' | 'insert' | 'update';
|
|
11
|
+
export interface DBTransaction {
|
|
12
|
+
commit: () => Promise<void>;
|
|
13
|
+
rollback: () => Promise<void>;
|
|
14
|
+
}
|
|
11
15
|
export interface CommonDBOptions {
|
|
16
|
+
/**
|
|
17
|
+
* If passed - the operation will be performed in the context of that DBTransaction.
|
|
18
|
+
* Note that not every type of operation supports Transaction
|
|
19
|
+
* (e.g in Datastore queries cannot be executed inside a Transaction).
|
|
20
|
+
* Also, not every CommonDB implementation supports Transactions.
|
|
21
|
+
*/
|
|
22
|
+
tx?: DBTransaction;
|
|
12
23
|
}
|
|
13
24
|
/**
|
|
14
25
|
* All properties default to undefined.
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,5 @@ export * from './pipeline/dbPipelineBackup';
|
|
|
13
13
|
export * from './pipeline/dbPipelineCopy';
|
|
14
14
|
export * from './pipeline/dbPipelineRestore';
|
|
15
15
|
export * from './query/dbQuery';
|
|
16
|
-
export * from './transaction/dbTransaction';
|
|
17
16
|
export * from './transaction/dbTransaction.util';
|
|
18
17
|
export * from './kv/commonKeyValueDaoMemoCache';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,5 @@ tslib_1.__exportStar(require("./pipeline/dbPipelineBackup"), exports);
|
|
|
16
16
|
tslib_1.__exportStar(require("./pipeline/dbPipelineCopy"), exports);
|
|
17
17
|
tslib_1.__exportStar(require("./pipeline/dbPipelineRestore"), exports);
|
|
18
18
|
tslib_1.__exportStar(require("./query/dbQuery"), exports);
|
|
19
|
-
tslib_1.__exportStar(require("./transaction/dbTransaction"), exports);
|
|
20
19
|
tslib_1.__exportStar(require("./transaction/dbTransaction.util"), exports);
|
|
21
20
|
tslib_1.__exportStar(require("./kv/commonKeyValueDaoMemoCache"), exports);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { CommonDB } from '../common.db';
|
|
2
|
-
import {
|
|
3
|
-
export declare function runCommonDaoTest(db: CommonDB,
|
|
2
|
+
import { CommonDBImplementationQuirks } from './dbTest';
|
|
3
|
+
export declare function runCommonDaoTest(db: CommonDB, quirks?: CommonDBImplementationQuirks): void;
|
package/dist/testing/daoTest.js
CHANGED
|
@@ -8,7 +8,8 @@ const __1 = require("..");
|
|
|
8
8
|
const common_dao_1 = require("../commondao/common.dao");
|
|
9
9
|
const dbTest_1 = require("./dbTest");
|
|
10
10
|
const test_model_1 = require("./test.model");
|
|
11
|
-
function runCommonDaoTest(db,
|
|
11
|
+
function runCommonDaoTest(db, quirks = {}) {
|
|
12
|
+
const { support } = db;
|
|
12
13
|
const dao = new common_dao_1.CommonDao({
|
|
13
14
|
table: test_model_1.TEST_TABLE,
|
|
14
15
|
db,
|
|
@@ -18,16 +19,6 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
18
19
|
logStarted: true,
|
|
19
20
|
logLevel: __1.CommonDaoLogLevel.DATA_FULL,
|
|
20
21
|
});
|
|
21
|
-
const { querying = true,
|
|
22
|
-
// tableSchemas = true,
|
|
23
|
-
createTable = true, dbQueryFilter = true,
|
|
24
|
-
// dbQueryFilterIn = true,
|
|
25
|
-
dbQueryOrder = true, dbQuerySelectFields = true, streaming = true, strongConsistency = true, nullValues = true, transactions = true, } = features;
|
|
26
|
-
// const {
|
|
27
|
-
// allowExtraPropertiesInResponse,
|
|
28
|
-
// allowBooleansAsUndefined,
|
|
29
|
-
// } = quirks
|
|
30
|
-
const eventualConsistencyDelay = !strongConsistency && quirks.eventualConsistencyDelay;
|
|
31
22
|
const items = (0, test_model_1.createTestItemsBM)(3);
|
|
32
23
|
const itemsClone = (0, js_lib_1._deepCopy)(items);
|
|
33
24
|
// deepFreeze(items) // mutation of id/created/updated is allowed now! (even expected)
|
|
@@ -40,12 +31,12 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
40
31
|
await dao.ping();
|
|
41
32
|
});
|
|
42
33
|
// CREATE TABLE, DROP
|
|
43
|
-
if (createTable) {
|
|
34
|
+
if (support.createTable) {
|
|
44
35
|
test('createTable, dropIfExists=true', async () => {
|
|
45
36
|
await dao.createTable(test_model_1.testItemDBMJsonSchema, { dropIfExists: true });
|
|
46
37
|
});
|
|
47
38
|
}
|
|
48
|
-
if (
|
|
39
|
+
if (support.queries) {
|
|
49
40
|
// DELETE ALL initially
|
|
50
41
|
test('deleteByIds test items', async () => {
|
|
51
42
|
const rows = await dao.query().select(['id']).runQuery();
|
|
@@ -53,8 +44,6 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
53
44
|
});
|
|
54
45
|
// QUERY empty
|
|
55
46
|
test('runQuery(all), runQueryCount should return empty', async () => {
|
|
56
|
-
if (eventualConsistencyDelay)
|
|
57
|
-
await (0, js_lib_1.pDelay)(eventualConsistencyDelay);
|
|
58
47
|
expect(await dao.query().runQuery()).toEqual([]);
|
|
59
48
|
expect(await dao.query().runQueryCount()).toBe(0);
|
|
60
49
|
});
|
|
@@ -72,7 +61,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
72
61
|
expect(await dao.getByIds(['abc', 'abcd'])).toEqual([]);
|
|
73
62
|
});
|
|
74
63
|
// SAVE
|
|
75
|
-
if (nullValues) {
|
|
64
|
+
if (support.nullValues) {
|
|
76
65
|
test('should allow to save and load null values', async () => {
|
|
77
66
|
const item3 = {
|
|
78
67
|
...(0, test_model_1.createTestItemBM)(3),
|
|
@@ -118,28 +107,26 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
118
107
|
(0, dbTest_1.expectMatch)(expectedItems, (0, js_lib_1._sortBy)(rows, r => r.id), quirks);
|
|
119
108
|
});
|
|
120
109
|
// QUERY
|
|
121
|
-
if (
|
|
110
|
+
if (support.queries) {
|
|
122
111
|
test('runQuery(all) should return all items', async () => {
|
|
123
|
-
if (eventualConsistencyDelay)
|
|
124
|
-
await (0, js_lib_1.pDelay)(eventualConsistencyDelay);
|
|
125
112
|
let rows = await dao.query().runQuery();
|
|
126
113
|
rows = (0, js_lib_1._sortBy)(rows, r => r.id);
|
|
127
114
|
(0, dbTest_1.expectMatch)(expectedItems, rows, quirks);
|
|
128
115
|
});
|
|
129
|
-
if (dbQueryFilter) {
|
|
116
|
+
if (support.dbQueryFilter) {
|
|
130
117
|
test('query even=true', async () => {
|
|
131
118
|
let rows = await dao.query().filter('even', '==', true).runQuery();
|
|
132
119
|
rows = (0, js_lib_1._sortBy)(rows, r => r.id);
|
|
133
120
|
(0, dbTest_1.expectMatch)(expectedItems.filter(i => i.even), rows, quirks);
|
|
134
121
|
});
|
|
135
122
|
}
|
|
136
|
-
if (dbQueryOrder) {
|
|
123
|
+
if (support.dbQueryOrder) {
|
|
137
124
|
test('query order by k1 desc', async () => {
|
|
138
125
|
const rows = await dao.query().order('k1', true).runQuery();
|
|
139
126
|
(0, dbTest_1.expectMatch)([...expectedItems].reverse(), rows, quirks);
|
|
140
127
|
});
|
|
141
128
|
}
|
|
142
|
-
if (dbQuerySelectFields) {
|
|
129
|
+
if (support.dbQuerySelectFields) {
|
|
143
130
|
test('projection query with only ids', async () => {
|
|
144
131
|
let rows = await dao.query().select(['id']).runQuery();
|
|
145
132
|
rows = (0, js_lib_1._sortBy)(rows, r => r.id);
|
|
@@ -151,7 +138,7 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
151
138
|
});
|
|
152
139
|
}
|
|
153
140
|
// STREAM
|
|
154
|
-
if (streaming) {
|
|
141
|
+
if (support.streaming) {
|
|
155
142
|
test('streamQueryForEach all', async () => {
|
|
156
143
|
let rows = [];
|
|
157
144
|
await dao.query().streamQueryForEach(bm => void rows.push(bm));
|
|
@@ -187,12 +174,10 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
187
174
|
});
|
|
188
175
|
}
|
|
189
176
|
// DELETE BY
|
|
190
|
-
if (
|
|
177
|
+
if (support.queries) {
|
|
191
178
|
test('deleteByQuery even=false', async () => {
|
|
192
179
|
const deleted = await dao.query().filter('even', '==', false).deleteByQuery();
|
|
193
180
|
expect(deleted).toBe(items.filter(item => !item.even).length);
|
|
194
|
-
if (eventualConsistencyDelay)
|
|
195
|
-
await (0, js_lib_1.pDelay)(eventualConsistencyDelay);
|
|
196
181
|
expect(await dao.query().runQueryCount()).toBe(1);
|
|
197
182
|
});
|
|
198
183
|
test('cleanup', async () => {
|
|
@@ -200,42 +185,47 @@ function runCommonDaoTest(db, features = {}, quirks = {}) {
|
|
|
200
185
|
await dao.query().deleteByQuery();
|
|
201
186
|
});
|
|
202
187
|
}
|
|
203
|
-
if (transactions) {
|
|
188
|
+
if (support.transactions) {
|
|
204
189
|
test('transaction happy path', async () => {
|
|
205
190
|
// cleanup
|
|
206
191
|
await dao.query().deleteByQuery();
|
|
207
192
|
// Test that id, created, updated are created
|
|
208
193
|
const now = (0, js_lib_1.localTimeNow)().unix();
|
|
209
|
-
await dao.
|
|
194
|
+
await dao.useTransaction(async (tx) => {
|
|
195
|
+
const row = (0, js_lib_1._omit)(item1, ['id', 'created', 'updated']);
|
|
196
|
+
await tx.save(dao, row);
|
|
197
|
+
});
|
|
210
198
|
const loaded = await dao.query().runQuery();
|
|
211
199
|
expect(loaded.length).toBe(1);
|
|
212
200
|
expect(loaded[0].id).toBeDefined();
|
|
213
201
|
expect(loaded[0].created).toBeGreaterThanOrEqual(now);
|
|
214
202
|
expect(loaded[0].updated).toBe(loaded[0].created);
|
|
215
|
-
await dao.
|
|
203
|
+
await dao.useTransaction(async (tx) => {
|
|
204
|
+
await tx.deleteById(dao, loaded[0].id);
|
|
205
|
+
});
|
|
216
206
|
// saveBatch [item1, 2, 3]
|
|
217
207
|
// save item3 with k1: k1_mod
|
|
218
208
|
// delete item2
|
|
219
209
|
// remaining: item1, item3_with_k1_mod
|
|
220
|
-
await dao.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
210
|
+
await dao.useTransaction(async (tx) => {
|
|
211
|
+
await tx.saveBatch(dao, items);
|
|
212
|
+
await tx.save(dao, { ...items[2], k1: 'k1_mod' });
|
|
213
|
+
await tx.deleteById(dao, items[1].id);
|
|
214
|
+
});
|
|
225
215
|
const rows = await dao.query().runQuery();
|
|
226
216
|
const expected = [items[0], { ...items[2], k1: 'k1_mod' }];
|
|
227
217
|
(0, dbTest_1.expectMatch)(expected, rows, quirks);
|
|
228
218
|
});
|
|
229
219
|
test('transaction rollback', async () => {
|
|
230
|
-
await expect(dao.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
220
|
+
await expect(dao.useTransaction(async (tx) => {
|
|
221
|
+
await tx.deleteById(dao, items[2].id);
|
|
222
|
+
await tx.save(dao, { ...items[0], k1: 5 }); // it should fail here
|
|
223
|
+
})).rejects.toThrow();
|
|
234
224
|
const rows = await dao.query().runQuery();
|
|
235
225
|
const expected = [items[0], { ...items[2], k1: 'k1_mod' }];
|
|
236
226
|
(0, dbTest_1.expectMatch)(expected, rows, quirks);
|
|
237
227
|
});
|
|
238
|
-
if (
|
|
228
|
+
if (support.queries) {
|
|
239
229
|
test('transaction cleanup', async () => {
|
|
240
230
|
await dao.query().deleteByQuery();
|
|
241
231
|
});
|
package/dist/testing/dbTest.d.ts
CHANGED
|
@@ -1,43 +1,8 @@
|
|
|
1
1
|
import { CommonDB } from '../common.db';
|
|
2
|
-
export interface CommonDBImplementationFeatures {
|
|
3
|
-
/**
|
|
4
|
-
* All querying functionality.
|
|
5
|
-
*/
|
|
6
|
-
querying?: boolean;
|
|
7
|
-
dbQueryFilter?: boolean;
|
|
8
|
-
dbQueryFilterIn?: boolean;
|
|
9
|
-
dbQueryOrder?: boolean;
|
|
10
|
-
dbQuerySelectFields?: boolean;
|
|
11
|
-
insert?: boolean;
|
|
12
|
-
update?: boolean;
|
|
13
|
-
updateByQuery?: boolean;
|
|
14
|
-
dbIncrement?: boolean;
|
|
15
|
-
createTable?: boolean;
|
|
16
|
-
tableSchemas?: boolean;
|
|
17
|
-
/**
|
|
18
|
-
* Queries should return fresh results immediately.
|
|
19
|
-
* Datastore is the one known to NOT have strong consistency for queries (not for getById though).
|
|
20
|
-
*/
|
|
21
|
-
strongConsistency?: boolean;
|
|
22
|
-
streaming?: boolean;
|
|
23
|
-
bufferSupport?: boolean;
|
|
24
|
-
nullValues?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Set false for SQL (relational) databases,
|
|
27
|
-
* they will return `null` for all missing properties.
|
|
28
|
-
*/
|
|
29
|
-
documentDB?: boolean;
|
|
30
|
-
transactions?: boolean;
|
|
31
|
-
}
|
|
32
2
|
/**
|
|
33
3
|
* All options default to `false`.
|
|
34
4
|
*/
|
|
35
5
|
export interface CommonDBImplementationQuirks {
|
|
36
|
-
/**
|
|
37
|
-
* Applicable to e.g Datastore.
|
|
38
|
-
* Time in milliseconds to wait for eventual consistency to propagate.
|
|
39
|
-
*/
|
|
40
|
-
eventualConsistencyDelay?: number;
|
|
41
6
|
/**
|
|
42
7
|
* Example: airtableId
|
|
43
8
|
*/
|
|
@@ -47,8 +12,5 @@ export interface CommonDBImplementationQuirks {
|
|
|
47
12
|
*/
|
|
48
13
|
allowBooleansAsUndefined?: boolean;
|
|
49
14
|
}
|
|
50
|
-
|
|
51
|
-
* All unclaimed features will default to 'true'
|
|
52
|
-
*/
|
|
53
|
-
export declare function runCommonDBTest(db: CommonDB, features?: CommonDBImplementationFeatures, quirks?: CommonDBImplementationQuirks): void;
|
|
15
|
+
export declare function runCommonDBTest(db: CommonDB, quirks?: CommonDBImplementationQuirks): void;
|
|
54
16
|
export declare function expectMatch(expected: any[], actual: any[], quirks: CommonDBImplementationQuirks): void;
|