@naturalcycles/firestore-lib 1.5.0 → 1.6.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/firestore.db.d.ts +27 -5
- package/dist/firestore.db.js +104 -25
- package/dist/index.d.ts +3 -4
- package/dist/index.js +4 -5
- package/package.json +3 -6
- package/src/firestore.db.ts +141 -34
- 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, 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 {
|
|
@@ -12,16 +12,38 @@ export interface FirestoreDBSaveOptions<ROW extends Partial<ObjectWithId> = AnyO
|
|
|
12
12
|
export declare class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
13
13
|
cfg: FirestoreDBCfg;
|
|
14
14
|
constructor(cfg: FirestoreDBCfg);
|
|
15
|
-
|
|
15
|
+
support: CommonDBSupport;
|
|
16
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: string[], _opt?: FirestoreDBOptions): Promise<ROW[]>;
|
|
16
17
|
runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<RunQueryResult<ROW>>;
|
|
17
18
|
runFirestoreQuery<ROW extends ObjectWithId>(q: Query, _opt?: FirestoreDBOptions): Promise<ROW[]>;
|
|
18
19
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: FirestoreDBOptions): Promise<number>;
|
|
19
20
|
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: CommonDBStreamOptions): ReadableTyped<ROW>;
|
|
20
21
|
saveBatch<ROW extends Partial<ObjectWithId>>(table: string, rows: ROW[], opt?: FirestoreDBSaveOptions<ROW>): Promise<void>;
|
|
21
22
|
deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<number>;
|
|
22
|
-
deleteByIds
|
|
23
|
+
deleteByIds(table: string, ids: string[], opt?: FirestoreDBOptions): Promise<number>;
|
|
23
24
|
private querySnapshotToArray;
|
|
24
|
-
|
|
25
|
+
createTransaction(): Promise<FirestoreDBTransaction>;
|
|
25
26
|
ping(): Promise<void>;
|
|
26
27
|
getTables(): Promise<string[]>;
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
31
|
+
*/
|
|
32
|
+
export declare class FirestoreDBTransaction implements DBTransaction {
|
|
33
|
+
db: FirestoreDB;
|
|
34
|
+
tx: Transaction;
|
|
35
|
+
private txPendingDefer;
|
|
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>;
|
|
48
|
+
rollback(): Promise<void>;
|
|
49
|
+
}
|
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 = 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");
|
|
@@ -15,6 +15,11 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
15
15
|
constructor(cfg) {
|
|
16
16
|
super();
|
|
17
17
|
this.cfg = cfg;
|
|
18
|
+
this.support = {
|
|
19
|
+
...db_lib_1.commonDBFullSupport,
|
|
20
|
+
updateByQuery: false,
|
|
21
|
+
tableSchemas: false,
|
|
22
|
+
};
|
|
18
23
|
}
|
|
19
24
|
// GET
|
|
20
25
|
async getByIds(table, ids, _opt) {
|
|
@@ -79,13 +84,23 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
79
84
|
}
|
|
80
85
|
// SAVE
|
|
81
86
|
async saveBatch(table, rows, opt = {}) {
|
|
87
|
+
const { firestore } = this.cfg;
|
|
88
|
+
const col = firestore.collection(table);
|
|
82
89
|
const method = methodMap[opt.saveMethod] || 'set';
|
|
90
|
+
if (opt.tx) {
|
|
91
|
+
const { tx } = opt.tx;
|
|
92
|
+
rows.forEach(row => {
|
|
93
|
+
(0, js_lib_1._assert)(row.id, `firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`);
|
|
94
|
+
tx[method](col.doc((0, firestore_util_1.escapeDocId)(row.id)), (0, js_lib_1._filterUndefinedValues)(row));
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
83
98
|
// Firestore allows max 500 items in one batch
|
|
84
99
|
await (0, js_lib_1.pMap)((0, js_lib_1._chunk)(rows, 500), async (chunk) => {
|
|
85
|
-
const batch =
|
|
100
|
+
const batch = firestore.batch();
|
|
86
101
|
chunk.forEach(row => {
|
|
87
102
|
(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](
|
|
103
|
+
batch[method](col.doc((0, firestore_util_1.escapeDocId)(row.id)), (0, js_lib_1._filterUndefinedValues)(row));
|
|
89
104
|
});
|
|
90
105
|
await batch.commit();
|
|
91
106
|
}, { concurrency: 1 });
|
|
@@ -104,11 +119,20 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
104
119
|
await this.deleteByIds(q.table, ids, opt);
|
|
105
120
|
return ids.length;
|
|
106
121
|
}
|
|
107
|
-
async deleteByIds(table, ids,
|
|
122
|
+
async deleteByIds(table, ids, opt = {}) {
|
|
123
|
+
const { firestore } = this.cfg;
|
|
124
|
+
const col = firestore.collection(table);
|
|
125
|
+
if (opt.tx) {
|
|
126
|
+
const { tx } = opt.tx;
|
|
127
|
+
ids.forEach(id => {
|
|
128
|
+
tx.delete(col.doc((0, firestore_util_1.escapeDocId)(id)));
|
|
129
|
+
});
|
|
130
|
+
return ids.length;
|
|
131
|
+
}
|
|
108
132
|
await (0, js_lib_1.pMap)((0, js_lib_1._chunk)(ids, 500), async (chunk) => {
|
|
109
|
-
const batch =
|
|
133
|
+
const batch = firestore.batch();
|
|
110
134
|
chunk.forEach(id => {
|
|
111
|
-
batch.delete(
|
|
135
|
+
batch.delete(col.doc((0, firestore_util_1.escapeDocId)(id)));
|
|
112
136
|
});
|
|
113
137
|
await batch.commit();
|
|
114
138
|
});
|
|
@@ -124,25 +148,30 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
124
148
|
});
|
|
125
149
|
return rows;
|
|
126
150
|
}
|
|
127
|
-
async commitTransaction(tx, _opt) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
151
|
+
// override async commitTransaction(tx: DBTransaction, _opt?: CommonDBSaveOptions): Promise<void> {
|
|
152
|
+
// const { firestore } = this.cfg
|
|
153
|
+
//
|
|
154
|
+
// await firestore.runTransaction(async tr => {
|
|
155
|
+
// for (const op of tx.ops) {
|
|
156
|
+
// if (op.type === 'saveBatch') {
|
|
157
|
+
// op.rows.forEach(row => {
|
|
158
|
+
// tr.set(
|
|
159
|
+
// firestore.collection(op.table).doc(escapeDocId(row.id)),
|
|
160
|
+
// _filterUndefinedValues(row),
|
|
161
|
+
// )
|
|
162
|
+
// })
|
|
163
|
+
// } else if (op.type === 'deleteByIds') {
|
|
164
|
+
// op.ids.forEach(id => {
|
|
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);
|
|
146
175
|
}
|
|
147
176
|
async ping() {
|
|
148
177
|
// no-op now
|
|
@@ -152,3 +181,53 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
|
|
|
152
181
|
}
|
|
153
182
|
}
|
|
154
183
|
exports.FirestoreDB = FirestoreDB;
|
|
184
|
+
/**
|
|
185
|
+
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
186
|
+
*/
|
|
187
|
+
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) {
|
|
199
|
+
this.db = db;
|
|
200
|
+
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
|
+
}
|
|
228
|
+
async rollback() {
|
|
229
|
+
this.txPendingDefer.reject(new Error('rollback'));
|
|
230
|
+
await this.txCompletedDefer;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
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
|
},
|
|
@@ -14,12 +14,9 @@
|
|
|
14
14
|
"@naturalcycles/test-lib": "^1.0.3",
|
|
15
15
|
"@types/node": "^20.1.2",
|
|
16
16
|
"dotenv": "^16.0.0",
|
|
17
|
-
"firebase-admin": "^
|
|
17
|
+
"firebase-admin": "^12.0.0",
|
|
18
18
|
"jest": "^29.1.2"
|
|
19
19
|
},
|
|
20
|
-
"resolutions": {
|
|
21
|
-
"@google-cloud/firestore": "^7.0.0"
|
|
22
|
-
},
|
|
23
20
|
"files": [
|
|
24
21
|
"dist",
|
|
25
22
|
"src",
|
|
@@ -40,7 +37,7 @@
|
|
|
40
37
|
"engines": {
|
|
41
38
|
"node": ">=18.12.0"
|
|
42
39
|
},
|
|
43
|
-
"version": "1.
|
|
40
|
+
"version": "1.6.0",
|
|
44
41
|
"description": "Firestore implementation of CommonDB interface",
|
|
45
42
|
"author": "Natural Cycles Team",
|
|
46
43
|
"license": "MIT"
|
package/src/firestore.db.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
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,
|
|
11
19
|
RunQueryResult,
|
|
@@ -20,6 +28,8 @@ import {
|
|
|
20
28
|
AnyObjectWithId,
|
|
21
29
|
_assert,
|
|
22
30
|
_isTruthy,
|
|
31
|
+
pDefer,
|
|
32
|
+
DeferredPromise,
|
|
23
33
|
} from '@naturalcycles/js-lib'
|
|
24
34
|
import { ReadableTyped, transformMapSimple } from '@naturalcycles/nodejs-lib'
|
|
25
35
|
import { escapeDocId, unescapeDocId } from './firestore.util'
|
|
@@ -33,7 +43,9 @@ export interface FirestoreDBOptions extends CommonDBOptions {}
|
|
|
33
43
|
export interface FirestoreDBSaveOptions<ROW extends Partial<ObjectWithId> = AnyObjectWithId>
|
|
34
44
|
extends CommonDBSaveOptions<ROW> {}
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
type SaveOp = 'create' | 'update' | 'set'
|
|
47
|
+
|
|
48
|
+
const methodMap: Record<CommonDBSaveMethod, SaveOp> = {
|
|
37
49
|
insert: 'create',
|
|
38
50
|
update: 'update',
|
|
39
51
|
upsert: 'set',
|
|
@@ -44,10 +56,16 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
44
56
|
super()
|
|
45
57
|
}
|
|
46
58
|
|
|
59
|
+
override support: CommonDBSupport = {
|
|
60
|
+
...commonDBFullSupport,
|
|
61
|
+
updateByQuery: false,
|
|
62
|
+
tableSchemas: false,
|
|
63
|
+
}
|
|
64
|
+
|
|
47
65
|
// GET
|
|
48
66
|
override async getByIds<ROW extends ObjectWithId>(
|
|
49
67
|
table: string,
|
|
50
|
-
ids:
|
|
68
|
+
ids: string[],
|
|
51
69
|
_opt?: FirestoreDBOptions,
|
|
52
70
|
): Promise<ROW[]> {
|
|
53
71
|
// Oj, doesn't look like a very optimal implementation!
|
|
@@ -143,21 +161,37 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
143
161
|
rows: ROW[],
|
|
144
162
|
opt: FirestoreDBSaveOptions<ROW> = {},
|
|
145
163
|
): Promise<void> {
|
|
146
|
-
const
|
|
164
|
+
const { firestore } = this.cfg
|
|
165
|
+
const col = firestore.collection(table)
|
|
166
|
+
const method: SaveOp = methodMap[opt.saveMethod!] || 'set'
|
|
167
|
+
|
|
168
|
+
if (opt.tx) {
|
|
169
|
+
const { tx } = opt.tx as FirestoreDBTransaction
|
|
170
|
+
|
|
171
|
+
rows.forEach(row => {
|
|
172
|
+
_assert(
|
|
173
|
+
row.id,
|
|
174
|
+
`firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
tx[method as 'set' | 'create'](col.doc(escapeDocId(row.id)), _filterUndefinedValues(row))
|
|
178
|
+
})
|
|
179
|
+
return
|
|
180
|
+
}
|
|
147
181
|
|
|
148
182
|
// Firestore allows max 500 items in one batch
|
|
149
183
|
await pMap(
|
|
150
184
|
_chunk(rows, 500),
|
|
151
185
|
async chunk => {
|
|
152
|
-
const batch =
|
|
186
|
+
const batch = firestore.batch()
|
|
153
187
|
|
|
154
188
|
chunk.forEach(row => {
|
|
155
189
|
_assert(
|
|
156
190
|
row.id,
|
|
157
191
|
`firestore-db doesn't support id auto-generation, but empty id was provided in saveBatch`,
|
|
158
192
|
)
|
|
159
|
-
|
|
160
|
-
|
|
193
|
+
batch[method as 'set' | 'create'](
|
|
194
|
+
col.doc(escapeDocId(row.id)),
|
|
161
195
|
_filterUndefinedValues(row),
|
|
162
196
|
)
|
|
163
197
|
})
|
|
@@ -173,7 +207,7 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
173
207
|
q: DBQuery<ROW>,
|
|
174
208
|
opt?: FirestoreDBOptions,
|
|
175
209
|
): Promise<number> {
|
|
176
|
-
let ids:
|
|
210
|
+
let ids: string[]
|
|
177
211
|
|
|
178
212
|
const idFilter = q._filters.find(f => f.name === 'id')
|
|
179
213
|
if (idFilter) {
|
|
@@ -191,16 +225,28 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
191
225
|
return ids.length
|
|
192
226
|
}
|
|
193
227
|
|
|
194
|
-
async deleteByIds
|
|
228
|
+
override async deleteByIds(
|
|
195
229
|
table: string,
|
|
196
|
-
ids:
|
|
197
|
-
|
|
230
|
+
ids: string[],
|
|
231
|
+
opt: FirestoreDBOptions = {},
|
|
198
232
|
): Promise<number> {
|
|
233
|
+
const { firestore } = this.cfg
|
|
234
|
+
const col = firestore.collection(table)
|
|
235
|
+
|
|
236
|
+
if (opt.tx) {
|
|
237
|
+
const { tx } = opt.tx as FirestoreDBTransaction
|
|
238
|
+
|
|
239
|
+
ids.forEach(id => {
|
|
240
|
+
tx.delete(col.doc(escapeDocId(id)))
|
|
241
|
+
})
|
|
242
|
+
return ids.length
|
|
243
|
+
}
|
|
244
|
+
|
|
199
245
|
await pMap(_chunk(ids, 500), async chunk => {
|
|
200
|
-
const batch =
|
|
246
|
+
const batch = firestore.batch()
|
|
201
247
|
|
|
202
248
|
chunk.forEach(id => {
|
|
203
|
-
batch.delete(
|
|
249
|
+
batch.delete(col.doc(escapeDocId(id)))
|
|
204
250
|
})
|
|
205
251
|
|
|
206
252
|
await batch.commit()
|
|
@@ -222,27 +268,31 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
222
268
|
return rows
|
|
223
269
|
}
|
|
224
270
|
|
|
225
|
-
override async commitTransaction(tx: DBTransaction, _opt?: CommonDBSaveOptions): Promise<void> {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
271
|
+
// override async commitTransaction(tx: DBTransaction, _opt?: CommonDBSaveOptions): Promise<void> {
|
|
272
|
+
// const { firestore } = this.cfg
|
|
273
|
+
//
|
|
274
|
+
// await firestore.runTransaction(async tr => {
|
|
275
|
+
// for (const op of tx.ops) {
|
|
276
|
+
// if (op.type === 'saveBatch') {
|
|
277
|
+
// op.rows.forEach(row => {
|
|
278
|
+
// tr.set(
|
|
279
|
+
// firestore.collection(op.table).doc(escapeDocId(row.id)),
|
|
280
|
+
// _filterUndefinedValues(row),
|
|
281
|
+
// )
|
|
282
|
+
// })
|
|
283
|
+
// } else if (op.type === 'deleteByIds') {
|
|
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)
|
|
246
296
|
}
|
|
247
297
|
|
|
248
298
|
override async ping(): Promise<void> {
|
|
@@ -253,3 +303,60 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
253
303
|
return []
|
|
254
304
|
}
|
|
255
305
|
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* https://firebase.google.com/docs/firestore/manage-data/transactions
|
|
309
|
+
*/
|
|
310
|
+
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(
|
|
324
|
+
public db: FirestoreDB,
|
|
325
|
+
public tx: Transaction,
|
|
326
|
+
private txPendingDefer: DeferredPromise,
|
|
327
|
+
private txCompletedDefer: DeferredPromise,
|
|
328
|
+
) {}
|
|
329
|
+
|
|
330
|
+
static async create(db: FirestoreDB): Promise<FirestoreDBTransaction> {
|
|
331
|
+
const txCreated = pDefer<Transaction>()
|
|
332
|
+
const txPendingDefer = pDefer()
|
|
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
|
+
})
|
|
349
|
+
|
|
350
|
+
const tx = await txCreated
|
|
351
|
+
return new FirestoreDBTransaction(db, tx, txPendingDefer, txCompletedDefer)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async commit(): Promise<void> {
|
|
355
|
+
this.txPendingDefer.resolve()
|
|
356
|
+
await this.txCompletedDefer
|
|
357
|
+
}
|
|
358
|
+
async rollback(): Promise<void> {
|
|
359
|
+
this.txPendingDefer.reject(new Error('rollback'))
|
|
360
|
+
await this.txCompletedDefer
|
|
361
|
+
}
|
|
362
|
+
}
|
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 }
|