@naturalcycles/firestore-lib 1.5.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/firestore.db.d.ts +21 -5
- package/dist/firestore.db.js +73 -31
- package/dist/index.d.ts +3 -4
- package/dist/index.js +4 -5
- package/package.json +2 -2
- package/src/firestore.db.ts +116 -40
- package/src/index.ts +3 -11
package/dist/firestore.db.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Firestore, Query } from '@google-cloud/firestore';
|
|
2
|
-
import { BaseCommonDB, CommonDB, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, DBQuery, DBTransaction, RunQueryResult } from '@naturalcycles/db-lib';
|
|
1
|
+
import { Firestore, Query, Transaction } from '@google-cloud/firestore';
|
|
2
|
+
import { BaseCommonDB, CommonDB, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, CommonDBSupport, DBQuery, DBTransaction, DBTransactionFn, RunQueryResult } from '@naturalcycles/db-lib';
|
|
3
3
|
import { ObjectWithId, AnyObjectWithId } from '@naturalcycles/js-lib';
|
|
4
4
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
5
5
|
export interface FirestoreDBCfg {
|
|
@@ -9,19 +9,35 @@ export interface FirestoreDBOptions extends CommonDBOptions {
|
|
|
9
9
|
}
|
|
10
10
|
export interface FirestoreDBSaveOptions<ROW extends Partial<ObjectWithId> = AnyObjectWithId> extends CommonDBSaveOptions<ROW> {
|
|
11
11
|
}
|
|
12
|
+
export declare class RollbackError extends Error {
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
12
15
|
export declare class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
13
16
|
cfg: FirestoreDBCfg;
|
|
14
17
|
constructor(cfg: FirestoreDBCfg);
|
|
15
|
-
|
|
18
|
+
support: CommonDBSupport;
|
|
19
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: FirestoreDBOptions): Promise<ROW[]>;
|
|
16
20
|
runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<RunQueryResult<ROW>>;
|
|
17
21
|
runFirestoreQuery<ROW extends ObjectWithId>(q: Query, _opt?: FirestoreDBOptions): Promise<ROW[]>;
|
|
18
22
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: FirestoreDBOptions): Promise<number>;
|
|
19
23
|
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBStreamOptions): ReadableTyped<ROW>;
|
|
20
24
|
saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: FirestoreDBSaveOptions<ROW>): Promise<void>;
|
|
21
25
|
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<number>;
|
|
22
|
-
deleteByIds
|
|
26
|
+
deleteByIds(table: string, ids: string[], opt?: FirestoreDBOptions): Promise<number>;
|
|
23
27
|
private querySnapshotToArray;
|
|
24
|
-
|
|
28
|
+
runInTransaction(fn: DBTransactionFn): Promise<void>;
|
|
25
29
|
ping(): Promise<void>;
|
|
26
30
|
getTables(): Promise<string[]>;
|
|
27
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
34
|
+
*/
|
|
35
|
+
export declare class FirestoreDBTransaction implements DBTransaction {
|
|
36
|
+
db: FirestoreDB;
|
|
37
|
+
tx: Transaction;
|
|
38
|
+
constructor(db: FirestoreDB, tx: Transaction);
|
|
39
|
+
rollback(): Promise<void>;
|
|
40
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: CommonDBOptions): Promise<ROW[]>;
|
|
41
|
+
saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
42
|
+
deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
|
|
43
|
+
}
|
package/dist/firestore.db.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FirestoreDB = void 0;
|
|
3
|
+
exports.FirestoreDBTransaction = exports.FirestoreDB = exports.RollbackError = void 0;
|
|
4
4
|
const db_lib_1 = require("@naturalcycles/db-lib");
|
|
5
5
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
6
6
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
@@ -11,23 +11,29 @@ const methodMap = {
|
|
|
11
11
|
update: 'update',
|
|
12
12
|
upsert: 'set',
|
|
13
13
|
};
|
|
14
|
+
class RollbackError extends Error {
|
|
15
|
+
constructor() {
|
|
16
|
+
super('rollback');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.RollbackError = RollbackError;
|
|
14
20
|
class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
15
21
|
constructor(cfg) {
|
|
16
22
|
super();
|
|
17
23
|
this.cfg = cfg;
|
|
24
|
+
this.support = {
|
|
25
|
+
...db_lib_1.commonDBFullSupport,
|
|
26
|
+
updateByQuery: false,
|
|
27
|
+
tableSchemas: false,
|
|
28
|
+
};
|
|
18
29
|
}
|
|
19
30
|
// GET
|
|
20
|
-
async getByIds(table, ids,
|
|
21
|
-
// Oj, doesn't look like a very optimal implementation!
|
|
22
|
-
// TODO: check if we can query by keys or smth
|
|
23
|
-
// return (await Promise.all(ids.map(id => this.getById<ROW>(table, id, opt)))).filter(
|
|
24
|
-
// Boolean,
|
|
25
|
-
// ) as ROW[]
|
|
31
|
+
async getByIds(table, ids, opt = {}) {
|
|
26
32
|
if (!ids.length)
|
|
27
33
|
return [];
|
|
28
34
|
const { firestore } = this.cfg;
|
|
29
35
|
const col = firestore.collection(table);
|
|
30
|
-
return (await firestore.getAll(...ids.map(id => col.doc((0, firestore_util_1.escapeDocId)(id)))))
|
|
36
|
+
return (await (opt.tx?.tx || firestore).getAll(...ids.map(id => col.doc((0, firestore_util_1.escapeDocId)(id)))))
|
|
31
37
|
.map(doc => {
|
|
32
38
|
const data = doc.data();
|
|
33
39
|
if (data === undefined)
|
|
@@ -79,13 +85,23 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
79
85
|
}
|
|
80
86
|
// SAVE
|
|
81
87
|
async saveBatch(table, rows, opt = {}) {
|
|
88
|
+
const { firestore } = this.cfg;
|
|
89
|
+
const col = firestore.collection(table);
|
|
82
90
|
const method = methodMap[opt.saveMethod] || 'set';
|
|
91
|
+
if (opt.tx) {
|
|
92
|
+
const { tx } = opt.tx;
|
|
93
|
+
rows.forEach(row => {
|
|
94
|
+
(0, js_lib_1._assert)(row.id, `firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`);
|
|
95
|
+
tx[method](col.doc((0, firestore_util_1.escapeDocId)(row.id)), (0, js_lib_1._filterUndefinedValues)(row));
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
83
99
|
// Firestore allows max 500 items in one batch
|
|
84
100
|
await (0, js_lib_1.pMap)((0, js_lib_1._chunk)(rows, 500), async (chunk) => {
|
|
85
|
-
const batch =
|
|
101
|
+
const batch = firestore.batch();
|
|
86
102
|
chunk.forEach(row => {
|
|
87
103
|
(0, js_lib_1._assert)(row.id, `firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`);
|
|
88
|
-
batch[method](
|
|
104
|
+
batch[method](col.doc((0, firestore_util_1.escapeDocId)(row.id)), (0, js_lib_1._filterUndefinedValues)(row));
|
|
89
105
|
});
|
|
90
106
|
await batch.commit();
|
|
91
107
|
}, { concurrency: 1 });
|
|
@@ -104,11 +120,20 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
104
120
|
await this.deleteByIds(q.table, ids, opt);
|
|
105
121
|
return ids.length;
|
|
106
122
|
}
|
|
107
|
-
async deleteByIds(table, ids,
|
|
123
|
+
async deleteByIds(table, ids, opt = {}) {
|
|
124
|
+
const { firestore } = this.cfg;
|
|
125
|
+
const col = firestore.collection(table);
|
|
126
|
+
if (opt.tx) {
|
|
127
|
+
const { tx } = opt.tx;
|
|
128
|
+
ids.forEach(id => {
|
|
129
|
+
tx.delete(col.doc((0, firestore_util_1.escapeDocId)(id)));
|
|
130
|
+
});
|
|
131
|
+
return ids.length;
|
|
132
|
+
}
|
|
108
133
|
await (0, js_lib_1.pMap)((0, js_lib_1._chunk)(ids, 500), async (chunk) => {
|
|
109
|
-
const batch =
|
|
134
|
+
const batch = firestore.batch();
|
|
110
135
|
chunk.forEach(id => {
|
|
111
|
-
batch.delete(
|
|
136
|
+
batch.delete(col.doc((0, firestore_util_1.escapeDocId)(id)));
|
|
112
137
|
});
|
|
113
138
|
await batch.commit();
|
|
114
139
|
});
|
|
@@ -124,25 +149,20 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
124
149
|
});
|
|
125
150
|
return rows;
|
|
126
151
|
}
|
|
127
|
-
async
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
tr.delete(firestore.collection(op.table).doc((0, firestore_util_1.escapeDocId)(id)));
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
throw new Error(`DBOperation not supported: ${op.type}`);
|
|
143
|
-
}
|
|
152
|
+
async runInTransaction(fn) {
|
|
153
|
+
try {
|
|
154
|
+
await this.cfg.firestore.runTransaction(async (firestoreTx) => {
|
|
155
|
+
const tx = new FirestoreDBTransaction(this, firestoreTx);
|
|
156
|
+
await fn(tx);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
if (err instanceof RollbackError) {
|
|
161
|
+
// RollbackError should be handled gracefully (not re-throw)
|
|
162
|
+
return;
|
|
144
163
|
}
|
|
145
|
-
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
146
166
|
}
|
|
147
167
|
async ping() {
|
|
148
168
|
// no-op now
|
|
@@ -152,3 +172,25 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
152
172
|
}
|
|
153
173
|
}
|
|
154
174
|
exports.FirestoreDB = FirestoreDB;
|
|
175
|
+
/**
|
|
176
|
+
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
177
|
+
*/
|
|
178
|
+
class FirestoreDBTransaction {
|
|
179
|
+
constructor(db, tx) {
|
|
180
|
+
this.db = db;
|
|
181
|
+
this.tx = tx;
|
|
182
|
+
}
|
|
183
|
+
async rollback() {
|
|
184
|
+
throw new RollbackError();
|
|
185
|
+
}
|
|
186
|
+
async getByIds(table, ids, opt) {
|
|
187
|
+
return await this.db.getByIds(table, ids, { ...opt, tx: this });
|
|
188
|
+
}
|
|
189
|
+
async saveBatch(table, rows, opt) {
|
|
190
|
+
await this.db.saveBatch(table, rows, { ...opt, tx: this });
|
|
191
|
+
}
|
|
192
|
+
async deleteByIds(table, ids, opt) {
|
|
193
|
+
return await this.db.deleteByIds(table, ids, { ...opt, tx: this });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
exports.FirestoreDBTransaction = FirestoreDBTransaction;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Firestore } from '@google-cloud/firestore';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
export { Firestore, FirestoreDB, dbQueryToFirestoreQuery };
|
|
2
|
+
export * from './firestore.db';
|
|
3
|
+
export * from './query.util';
|
|
4
|
+
export { Firestore };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Firestore = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
4
5
|
const firestore_1 = require("@google-cloud/firestore");
|
|
5
6
|
Object.defineProperty(exports, "Firestore", { enumerable: true, get: function () { return firestore_1.Firestore; } });
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const query_util_1 = require("./query.util");
|
|
9
|
-
Object.defineProperty(exports, "dbQueryToFirestoreQuery", { enumerable: true, get: function () { return query_util_1.dbQueryToFirestoreQuery; } });
|
|
7
|
+
tslib_1.__exportStar(require("./firestore.db"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./query.util"), exports);
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@google-cloud/firestore": "^7.0.0",
|
|
8
|
-
"@naturalcycles/db-lib": "^
|
|
8
|
+
"@naturalcycles/db-lib": "^9.0.0",
|
|
9
9
|
"@naturalcycles/js-lib": "^14.6.0",
|
|
10
10
|
"@naturalcycles/nodejs-lib": "^13.1.0"
|
|
11
11
|
},
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=18.12.0"
|
|
39
39
|
},
|
|
40
|
-
"version": "1.
|
|
40
|
+
"version": "1.6.1",
|
|
41
41
|
"description": "Firestore implementation of CommonDB interface",
|
|
42
42
|
"author": "Natural Cycles Team",
|
|
43
43
|
"license": "MIT"
|
package/src/firestore.db.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Firestore,
|
|
3
|
+
Query,
|
|
4
|
+
QueryDocumentSnapshot,
|
|
5
|
+
QuerySnapshot,
|
|
6
|
+
Transaction,
|
|
7
|
+
} from '@google-cloud/firestore'
|
|
2
8
|
import {
|
|
3
9
|
BaseCommonDB,
|
|
4
10
|
CommonDB,
|
|
11
|
+
commonDBFullSupport,
|
|
5
12
|
CommonDBOptions,
|
|
6
13
|
CommonDBSaveMethod,
|
|
7
14
|
CommonDBSaveOptions,
|
|
8
15
|
CommonDBStreamOptions,
|
|
16
|
+
CommonDBSupport,
|
|
9
17
|
DBQuery,
|
|
10
18
|
DBTransaction,
|
|
19
|
+
DBTransactionFn,
|
|
11
20
|
RunQueryResult,
|
|
12
21
|
} from '@naturalcycles/db-lib'
|
|
13
22
|
import {
|
|
@@ -33,34 +42,47 @@ export interface FirestoreDBOptions extends CommonDBOptions {}
|
|
|
33
42
|
export interface FirestoreDBSaveOptions<ROW extends Partial<ObjectWithId> = AnyObjectWithId>
|
|
34
43
|
extends CommonDBSaveOptions<ROW> {}
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
type SaveOp = 'create' | 'update' | 'set'
|
|
46
|
+
|
|
47
|
+
const methodMap: Record<CommonDBSaveMethod, SaveOp> = {
|
|
37
48
|
insert: 'create',
|
|
38
49
|
update: 'update',
|
|
39
50
|
upsert: 'set',
|
|
40
51
|
}
|
|
41
52
|
|
|
53
|
+
export class RollbackError extends Error {
|
|
54
|
+
constructor() {
|
|
55
|
+
super('rollback')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
42
59
|
export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
43
60
|
constructor(public cfg: FirestoreDBCfg) {
|
|
44
61
|
super()
|
|
45
62
|
}
|
|
46
63
|
|
|
64
|
+
override support: CommonDBSupport = {
|
|
65
|
+
...commonDBFullSupport,
|
|
66
|
+
updateByQuery: false,
|
|
67
|
+
tableSchemas: false,
|
|
68
|
+
}
|
|
69
|
+
|
|
47
70
|
// GET
|
|
48
71
|
override async getByIds<ROW extends ObjectWithId>(
|
|
49
72
|
table: string,
|
|
50
|
-
ids:
|
|
51
|
-
|
|
73
|
+
ids: string[],
|
|
74
|
+
opt: FirestoreDBOptions = {},
|
|
52
75
|
): Promise<ROW[]> {
|
|
53
|
-
// Oj, doesn't look like a very optimal implementation!
|
|
54
|
-
// TODO: check if we can query by keys or smth
|
|
55
|
-
// return (await Promise.all(ids.map(id => this.getById<ROW>(table, id, opt)))).filter(
|
|
56
|
-
// Boolean,
|
|
57
|
-
// ) as ROW[]
|
|
58
76
|
if (!ids.length) return []
|
|
59
77
|
|
|
60
78
|
const { firestore } = this.cfg
|
|
61
79
|
const col = firestore.collection(table)
|
|
62
80
|
|
|
63
|
-
return (
|
|
81
|
+
return (
|
|
82
|
+
await ((opt.tx as FirestoreDBTransaction)?.tx || firestore).getAll(
|
|
83
|
+
...ids.map(id => col.doc(escapeDocId(id))),
|
|
84
|
+
)
|
|
85
|
+
)
|
|
64
86
|
.map(doc => {
|
|
65
87
|
const data = doc.data()
|
|
66
88
|
if (data === undefined) return
|
|
@@ -143,21 +165,37 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
143
165
|
rows: ROW[],
|
|
144
166
|
opt: FirestoreDBSaveOptions<ROW> = {},
|
|
145
167
|
): Promise<void> {
|
|
146
|
-
const
|
|
168
|
+
const { firestore } = this.cfg
|
|
169
|
+
const col = firestore.collection(table)
|
|
170
|
+
const method: SaveOp = methodMap[opt.saveMethod!] || 'set'
|
|
171
|
+
|
|
172
|
+
if (opt.tx) {
|
|
173
|
+
const { tx } = opt.tx as FirestoreDBTransaction
|
|
174
|
+
|
|
175
|
+
rows.forEach(row => {
|
|
176
|
+
_assert(
|
|
177
|
+
row.id,
|
|
178
|
+
`firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
tx[method as 'set' | 'create'](col.doc(escapeDocId(row.id)), _filterUndefinedValues(row))
|
|
182
|
+
})
|
|
183
|
+
return
|
|
184
|
+
}
|
|
147
185
|
|
|
148
186
|
// Firestore allows max 500 items in one batch
|
|
149
187
|
await pMap(
|
|
150
188
|
_chunk(rows, 500),
|
|
151
189
|
async chunk => {
|
|
152
|
-
const batch =
|
|
190
|
+
const batch = firestore.batch()
|
|
153
191
|
|
|
154
192
|
chunk.forEach(row => {
|
|
155
193
|
_assert(
|
|
156
194
|
row.id,
|
|
157
195
|
`firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`,
|
|
158
196
|
)
|
|
159
|
-
|
|
160
|
-
|
|
197
|
+
batch[method as 'set' | 'create'](
|
|
198
|
+
col.doc(escapeDocId(row.id)),
|
|
161
199
|
_filterUndefinedValues(row),
|
|
162
200
|
)
|
|
163
201
|
})
|
|
@@ -173,7 +211,7 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
173
211
|
q: DBQuery<ROW>,
|
|
174
212
|
opt?: FirestoreDBOptions,
|
|
175
213
|
): Promise<number> {
|
|
176
|
-
let ids:
|
|
214
|
+
let ids: string[]
|
|
177
215
|
|
|
178
216
|
const idFilter = q._filters.find(f => f.name === 'id')
|
|
179
217
|
if (idFilter) {
|
|
@@ -191,16 +229,28 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
191
229
|
return ids.length
|
|
192
230
|
}
|
|
193
231
|
|
|
194
|
-
async deleteByIds
|
|
232
|
+
override async deleteByIds(
|
|
195
233
|
table: string,
|
|
196
|
-
ids:
|
|
197
|
-
|
|
234
|
+
ids: string[],
|
|
235
|
+
opt: FirestoreDBOptions = {},
|
|
198
236
|
): Promise<number> {
|
|
237
|
+
const { firestore } = this.cfg
|
|
238
|
+
const col = firestore.collection(table)
|
|
239
|
+
|
|
240
|
+
if (opt.tx) {
|
|
241
|
+
const { tx } = opt.tx as FirestoreDBTransaction
|
|
242
|
+
|
|
243
|
+
ids.forEach(id => {
|
|
244
|
+
tx.delete(col.doc(escapeDocId(id)))
|
|
245
|
+
})
|
|
246
|
+
return ids.length
|
|
247
|
+
}
|
|
248
|
+
|
|
199
249
|
await pMap(_chunk(ids, 500), async chunk => {
|
|
200
|
-
const batch =
|
|
250
|
+
const batch = firestore.batch()
|
|
201
251
|
|
|
202
252
|
chunk.forEach(id => {
|
|
203
|
-
batch.delete(
|
|
253
|
+
batch.delete(col.doc(escapeDocId(id)))
|
|
204
254
|
})
|
|
205
255
|
|
|
206
256
|
await batch.commit()
|
|
@@ -222,27 +272,19 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
222
272
|
return rows
|
|
223
273
|
}
|
|
224
274
|
|
|
225
|
-
override async
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
)
|
|
236
|
-
})
|
|
237
|
-
} else if (op.type === 'deleteByIds') {
|
|
238
|
-
op.ids.forEach(id => {
|
|
239
|
-
tr.delete(firestore.collection(op.table).doc(escapeDocId(id)))
|
|
240
|
-
})
|
|
241
|
-
} else {
|
|
242
|
-
throw new Error(`DBOperation not supported: ${(op as any).type}`)
|
|
243
|
-
}
|
|
275
|
+
override async runInTransaction(fn: DBTransactionFn): Promise<void> {
|
|
276
|
+
try {
|
|
277
|
+
await this.cfg.firestore.runTransaction(async firestoreTx => {
|
|
278
|
+
const tx = new FirestoreDBTransaction(this, firestoreTx)
|
|
279
|
+
await fn(tx)
|
|
280
|
+
})
|
|
281
|
+
} catch (err) {
|
|
282
|
+
if (err instanceof RollbackError) {
|
|
283
|
+
// RollbackError should be handled gracefully (not re-throw)
|
|
284
|
+
return
|
|
244
285
|
}
|
|
245
|
-
|
|
286
|
+
throw err
|
|
287
|
+
}
|
|
246
288
|
}
|
|
247
289
|
|
|
248
290
|
override async ping(): Promise<void> {
|
|
@@ -253,3 +295,37 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
253
295
|
return []
|
|
254
296
|
}
|
|
255
297
|
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
301
|
+
*/
|
|
302
|
+
export class FirestoreDBTransaction implements DBTransaction {
|
|
303
|
+
constructor(
|
|
304
|
+
public db: FirestoreDB,
|
|
305
|
+
public tx: Transaction,
|
|
306
|
+
) {}
|
|
307
|
+
|
|
308
|
+
async rollback(): Promise<void> {
|
|
309
|
+
throw new RollbackError()
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async getByIds<ROW extends ObjectWithId>(
|
|
313
|
+
table: string,
|
|
314
|
+
ids: string[],
|
|
315
|
+
opt?: CommonDBOptions,
|
|
316
|
+
): Promise<ROW[]> {
|
|
317
|
+
return await this.db.getByIds(table, ids, { ...opt, tx: this })
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async saveBatch<ROW extends Partial<ObjectWithId>>(
|
|
321
|
+
table: string,
|
|
322
|
+
rows: ROW[],
|
|
323
|
+
opt?: CommonDBSaveOptions<ROW>,
|
|
324
|
+
): Promise<void> {
|
|
325
|
+
await this.db.saveBatch(table, rows, { ...opt, tx: this })
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number> {
|
|
329
|
+
return await this.db.deleteByIds(table, ids, { ...opt, tx: this })
|
|
330
|
+
}
|
|
331
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { Firestore } from '@google-cloud/firestore'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
FirestoreDBOptions,
|
|
6
|
-
FirestoreDBSaveOptions,
|
|
7
|
-
} from './firestore.db'
|
|
8
|
-
import { dbQueryToFirestoreQuery } from './query.util'
|
|
9
|
-
|
|
10
|
-
export type { FirestoreDBCfg, FirestoreDBOptions, FirestoreDBSaveOptions }
|
|
11
|
-
|
|
12
|
-
export { Firestore, FirestoreDB, dbQueryToFirestoreQuery }
|
|
2
|
+
export * from './firestore.db'
|
|
3
|
+
export * from './query.util'
|
|
4
|
+
export { Firestore }
|