@naturalcycles/firestore-lib 1.6.0 → 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 +10 -16
- package/dist/firestore.db.js +34 -71
- package/package.json +1 -1
- package/src/firestore.db.ts +45 -76
package/dist/firestore.db.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Firestore, Query, Transaction } from '@google-cloud/firestore';
|
|
2
|
-
import { BaseCommonDB, CommonDB, CommonDBOptions, CommonDBSaveOptions, CommonDBStreamOptions, CommonDBSupport, DBQuery, DBTransaction, RunQueryResult } from '@naturalcycles/db-lib';
|
|
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,11 +9,14 @@ 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;
|
|
16
|
-
getByIds<ROW extends ObjectWithId>(table: string, ids: string[],
|
|
19
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], opt?: FirestoreDBOptions): Promise<ROW[]>;
|
|
17
20
|
runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<RunQueryResult<ROW>>;
|
|
18
21
|
runFirestoreQuery<ROW extends ObjectWithId>(q: Query, _opt?: FirestoreDBOptions): Promise<ROW[]>;
|
|
19
22
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: FirestoreDBOptions): Promise<number>;
|
|
@@ -22,7 +25,7 @@ export declare class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
22
25
|
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<number>;
|
|
23
26
|
deleteByIds(table: string, ids: string[], opt?: FirestoreDBOptions): Promise<number>;
|
|
24
27
|
private querySnapshotToArray;
|
|
25
|
-
|
|
28
|
+
runInTransaction(fn: DBTransactionFn): Promise<void>;
|
|
26
29
|
ping(): Promise<void>;
|
|
27
30
|
getTables(): Promise<string[]>;
|
|
28
31
|
}
|
|
@@ -32,18 +35,9 @@ export declare class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
32
35
|
export declare class FirestoreDBTransaction implements DBTransaction {
|
|
33
36
|
db: FirestoreDB;
|
|
34
37
|
tx: Transaction;
|
|
35
|
-
|
|
36
|
-
private txCompletedDefer;
|
|
37
|
-
/**
|
|
38
|
-
* This defer is held during Transaction and
|
|
39
|
-
* is released when it's ready to be committed or rolled back.
|
|
40
|
-
*/
|
|
41
|
-
/**
|
|
42
|
-
* This is resolved after Transaction is committed or rolled back.
|
|
43
|
-
* On error - it rejects with that error.
|
|
44
|
-
*/
|
|
45
|
-
private constructor();
|
|
46
|
-
static create(db: FirestoreDB): Promise<FirestoreDBTransaction>;
|
|
47
|
-
commit(): Promise<void>;
|
|
38
|
+
constructor(db: FirestoreDB, tx: Transaction);
|
|
48
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>;
|
|
49
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.FirestoreDBTransaction = 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,6 +11,12 @@ 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();
|
|
@@ -22,17 +28,12 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
22
28
|
};
|
|
23
29
|
}
|
|
24
30
|
// GET
|
|
25
|
-
async getByIds(table, ids,
|
|
26
|
-
// Oj, doesn't look like a very optimal implementation!
|
|
27
|
-
// TODO: check if we can query by keys or smth
|
|
28
|
-
// return (await Promise.all(ids.map(id => this.getById<ROW>(table, id, opt)))).filter(
|
|
29
|
-
// Boolean,
|
|
30
|
-
// ) as ROW[]
|
|
31
|
+
async getByIds(table, ids, opt = {}) {
|
|
31
32
|
if (!ids.length)
|
|
32
33
|
return [];
|
|
33
34
|
const { firestore } = this.cfg;
|
|
34
35
|
const col = firestore.collection(table);
|
|
35
|
-
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)))))
|
|
36
37
|
.map(doc => {
|
|
37
38
|
const data = doc.data();
|
|
38
39
|
if (data === undefined)
|
|
@@ -148,30 +149,20 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
148
149
|
});
|
|
149
150
|
return rows;
|
|
150
151
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// tr.delete(firestore.collection(op.table).doc(escapeDocId(id)))
|
|
166
|
-
// })
|
|
167
|
-
// } else {
|
|
168
|
-
// throw new Error(`DBOperation not supported: ${(op as any).type}`)
|
|
169
|
-
// }
|
|
170
|
-
// }
|
|
171
|
-
// })
|
|
172
|
-
// }
|
|
173
|
-
async createTransaction() {
|
|
174
|
-
return await FirestoreDBTransaction.create(this);
|
|
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;
|
|
163
|
+
}
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
175
166
|
}
|
|
176
167
|
async ping() {
|
|
177
168
|
// no-op now
|
|
@@ -185,49 +176,21 @@ exports.FirestoreDB = FirestoreDB;
|
|
|
185
176
|
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
186
177
|
*/
|
|
187
178
|
class FirestoreDBTransaction {
|
|
188
|
-
|
|
189
|
-
* This defer is held during Transaction and
|
|
190
|
-
* is released when it's ready to be committed or rolled back.
|
|
191
|
-
*/
|
|
192
|
-
// private txPendingDefer = pDefer()
|
|
193
|
-
/**
|
|
194
|
-
* This is resolved after Transaction is committed or rolled back.
|
|
195
|
-
* On error - it rejects with that error.
|
|
196
|
-
*/
|
|
197
|
-
// private txCompletedDefer = pDefer()
|
|
198
|
-
constructor(db, tx, txPendingDefer, txCompletedDefer) {
|
|
179
|
+
constructor(db, tx) {
|
|
199
180
|
this.db = db;
|
|
200
181
|
this.tx = tx;
|
|
201
|
-
this.txPendingDefer = txPendingDefer;
|
|
202
|
-
this.txCompletedDefer = txCompletedDefer;
|
|
203
|
-
}
|
|
204
|
-
static async create(db) {
|
|
205
|
-
const txCreated = (0, js_lib_1.pDefer)();
|
|
206
|
-
const txPendingDefer = (0, js_lib_1.pDefer)();
|
|
207
|
-
const txCompletedDefer = (0, js_lib_1.pDefer)();
|
|
208
|
-
db.cfg.firestore
|
|
209
|
-
.runTransaction(async (tx) => {
|
|
210
|
-
txCreated.resolve(tx);
|
|
211
|
-
// Now we pause and let consumers to use the Transaction,
|
|
212
|
-
// until commit/rollback is called
|
|
213
|
-
await txPendingDefer;
|
|
214
|
-
})
|
|
215
|
-
.then(() => {
|
|
216
|
-
txCompletedDefer.resolve();
|
|
217
|
-
})
|
|
218
|
-
.catch(err => {
|
|
219
|
-
txCompletedDefer.reject(err);
|
|
220
|
-
});
|
|
221
|
-
const tx = await txCreated;
|
|
222
|
-
return new FirestoreDBTransaction(db, tx, txPendingDefer, txCompletedDefer);
|
|
223
|
-
}
|
|
224
|
-
async commit() {
|
|
225
|
-
this.txPendingDefer.resolve();
|
|
226
|
-
await this.txCompletedDefer;
|
|
227
182
|
}
|
|
228
183
|
async rollback() {
|
|
229
|
-
|
|
230
|
-
|
|
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 });
|
|
231
194
|
}
|
|
232
195
|
}
|
|
233
196
|
exports.FirestoreDBTransaction = FirestoreDBTransaction;
|
package/package.json
CHANGED
package/src/firestore.db.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
CommonDBSupport,
|
|
17
17
|
DBQuery,
|
|
18
18
|
DBTransaction,
|
|
19
|
+
DBTransactionFn,
|
|
19
20
|
RunQueryResult,
|
|
20
21
|
} from '@naturalcycles/db-lib'
|
|
21
22
|
import {
|
|
@@ -28,8 +29,6 @@ import {
|
|
|
28
29
|
AnyObjectWithId,
|
|
29
30
|
_assert,
|
|
30
31
|
_isTruthy,
|
|
31
|
-
pDefer,
|
|
32
|
-
DeferredPromise,
|
|
33
32
|
} from '@naturalcycles/js-lib'
|
|
34
33
|
import { ReadableTyped, transformMapSimple } from '@naturalcycles/nodejs-lib'
|
|
35
34
|
import { escapeDocId, unescapeDocId } from './firestore.util'
|
|
@@ -51,6 +50,12 @@ const methodMap: Record<CommonDBSaveMethod, SaveOp> = {
|
|
|
51
50
|
upsert: 'set',
|
|
52
51
|
}
|
|
53
52
|
|
|
53
|
+
export class RollbackError extends Error {
|
|
54
|
+
constructor() {
|
|
55
|
+
super('rollback')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
54
59
|
export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
55
60
|
constructor(public cfg: FirestoreDBCfg) {
|
|
56
61
|
super()
|
|
@@ -66,19 +71,18 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
66
71
|
override async getByIds<ROW extends ObjectWithId>(
|
|
67
72
|
table: string,
|
|
68
73
|
ids: string[],
|
|
69
|
-
|
|
74
|
+
opt: FirestoreDBOptions = {},
|
|
70
75
|
): Promise<ROW[]> {
|
|
71
|
-
// Oj, doesn't look like a very optimal implementation!
|
|
72
|
-
// TODO: check if we can query by keys or smth
|
|
73
|
-
// return (await Promise.all(ids.map(id => this.getById<ROW>(table, id, opt)))).filter(
|
|
74
|
-
// Boolean,
|
|
75
|
-
// ) as ROW[]
|
|
76
76
|
if (!ids.length) return []
|
|
77
77
|
|
|
78
78
|
const { firestore } = this.cfg
|
|
79
79
|
const col = firestore.collection(table)
|
|
80
80
|
|
|
81
|
-
return (
|
|
81
|
+
return (
|
|
82
|
+
await ((opt.tx as FirestoreDBTransaction)?.tx || firestore).getAll(
|
|
83
|
+
...ids.map(id => col.doc(escapeDocId(id))),
|
|
84
|
+
)
|
|
85
|
+
)
|
|
82
86
|
.map(doc => {
|
|
83
87
|
const data = doc.data()
|
|
84
88
|
if (data === undefined) return
|
|
@@ -268,31 +272,19 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
268
272
|
return rows
|
|
269
273
|
}
|
|
270
274
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
// op.ids.forEach(id => {
|
|
285
|
-
// tr.delete(firestore.collection(op.table).doc(escapeDocId(id)))
|
|
286
|
-
// })
|
|
287
|
-
// } else {
|
|
288
|
-
// throw new Error(`DBOperation not supported: ${(op as any).type}`)
|
|
289
|
-
// }
|
|
290
|
-
// }
|
|
291
|
-
// })
|
|
292
|
-
// }
|
|
293
|
-
|
|
294
|
-
override async createTransaction(): Promise<FirestoreDBTransaction> {
|
|
295
|
-
return await FirestoreDBTransaction.create(this)
|
|
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
|
|
285
|
+
}
|
|
286
|
+
throw err
|
|
287
|
+
}
|
|
296
288
|
}
|
|
297
289
|
|
|
298
290
|
override async ping(): Promise<void> {
|
|
@@ -308,55 +300,32 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
308
300
|
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
309
301
|
*/
|
|
310
302
|
export class FirestoreDBTransaction implements DBTransaction {
|
|
311
|
-
|
|
312
|
-
* This defer is held during Transaction and
|
|
313
|
-
* is released when it's ready to be committed or rolled back.
|
|
314
|
-
*/
|
|
315
|
-
// private txPendingDefer = pDefer()
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* This is resolved after Transaction is committed or rolled back.
|
|
319
|
-
* On error - it rejects with that error.
|
|
320
|
-
*/
|
|
321
|
-
// private txCompletedDefer = pDefer()
|
|
322
|
-
|
|
323
|
-
private constructor(
|
|
303
|
+
constructor(
|
|
324
304
|
public db: FirestoreDB,
|
|
325
305
|
public tx: Transaction,
|
|
326
|
-
private txPendingDefer: DeferredPromise,
|
|
327
|
-
private txCompletedDefer: DeferredPromise,
|
|
328
306
|
) {}
|
|
329
307
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const txCompletedDefer = pDefer()
|
|
334
|
-
|
|
335
|
-
db.cfg.firestore
|
|
336
|
-
.runTransaction(async tx => {
|
|
337
|
-
txCreated.resolve(tx)
|
|
338
|
-
|
|
339
|
-
// Now we pause and let consumers to use the Transaction,
|
|
340
|
-
// until commit/rollback is called
|
|
341
|
-
await txPendingDefer
|
|
342
|
-
})
|
|
343
|
-
.then(() => {
|
|
344
|
-
txCompletedDefer.resolve()
|
|
345
|
-
})
|
|
346
|
-
.catch(err => {
|
|
347
|
-
txCompletedDefer.reject(err)
|
|
348
|
-
})
|
|
308
|
+
async rollback(): Promise<void> {
|
|
309
|
+
throw new RollbackError()
|
|
310
|
+
}
|
|
349
311
|
|
|
350
|
-
|
|
351
|
-
|
|
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 })
|
|
352
318
|
}
|
|
353
319
|
|
|
354
|
-
async
|
|
355
|
-
|
|
356
|
-
|
|
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 })
|
|
357
326
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
await this.
|
|
327
|
+
|
|
328
|
+
async deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number> {
|
|
329
|
+
return await this.db.deleteByIds(table, ids, { ...opt, tx: this })
|
|
361
330
|
}
|
|
362
331
|
}
|