@naturalcycles/firestore-lib 1.2.1 → 1.3.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.
@@ -12,8 +12,7 @@ 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
- getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], opt?: FirestoreDBOptions): Promise<ROW[]>;
16
- getById<ROW extends ObjectWithId>(table: string, id: ROW['id'], _opt?: FirestoreDBOptions): Promise<ROW | null>;
15
+ getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], _opt?: FirestoreDBOptions): Promise<ROW[]>;
17
16
  runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<RunQueryResult<ROW>>;
18
17
  runFirestoreQuery<ROW extends ObjectWithId>(q: Query, _opt?: FirestoreDBOptions): Promise<ROW[]>;
19
18
  runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<number>;
@@ -22,6 +21,6 @@ export declare class FirestoreDB extends BaseCommonDB implements CommonDB {
22
21
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<number>;
23
22
  deleteByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], _opt?: FirestoreDBOptions): Promise<number>;
24
23
  private querySnapshotToArray;
25
- commitTransaction(_tx: DBTransaction, _opt?: CommonDBSaveOptions): Promise<void>;
24
+ commitTransaction(tx: DBTransaction, _opt?: CommonDBSaveOptions): Promise<void>;
26
25
  ping(): Promise<void>;
27
26
  }
@@ -17,20 +17,27 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
17
17
  this.cfg = cfg;
18
18
  }
19
19
  // GET
20
- async getByIds(table, ids, opt) {
20
+ async getByIds(table, ids, _opt) {
21
21
  // Oj, doesn't look like a very optimal implementation!
22
22
  // TODO: check if we can query by keys or smth
23
- return (await Promise.all(ids.map(id => this.getById(table, id, opt)))).filter(Boolean);
24
- }
25
- async getById(table, id, _opt) {
26
- const doc = await this.cfg.firestore.collection(table).doc((0, firestore_util_1.escapeDocId)(id)).get();
27
- const data = doc.data();
28
- if (data === undefined)
29
- return null;
30
- return {
31
- id,
32
- ...data,
33
- };
23
+ // return (await Promise.all(ids.map(id => this.getById<ROW>(table, id, opt)))).filter(
24
+ // Boolean,
25
+ // ) as ROW[]
26
+ if (!ids.length)
27
+ return [];
28
+ const { firestore } = this.cfg;
29
+ const col = firestore.collection(table);
30
+ return (await firestore.getAll(...ids.map(id => col.doc((0, firestore_util_1.escapeDocId)(id)))))
31
+ .map(doc => {
32
+ const data = doc.data();
33
+ if (data === undefined)
34
+ return;
35
+ return {
36
+ id: (0, firestore_util_1.unescapeDocId)(doc.id),
37
+ ...data,
38
+ };
39
+ })
40
+ .filter(js_lib_1._isTruthy);
34
41
  }
35
42
  // QUERY
36
43
  async runQuery(q, opt) {
@@ -98,8 +105,25 @@ class FirestoreDB extends db_lib_1.BaseCommonDB {
98
105
  });
99
106
  return rows;
100
107
  }
101
- async commitTransaction(_tx, _opt) {
102
- throw new Error('commitTransaction is not supported yet');
108
+ async commitTransaction(tx, _opt) {
109
+ const { firestore } = this.cfg;
110
+ await firestore.runTransaction(async (tr) => {
111
+ for (const op of tx.ops) {
112
+ if (op.type === 'saveBatch') {
113
+ op.rows.forEach(row => {
114
+ tr.set(firestore.collection(op.table).doc((0, firestore_util_1.escapeDocId)(row.id)), (0, js_lib_1._filterUndefinedValues)(row));
115
+ });
116
+ }
117
+ else if (op.type === 'deleteByIds') {
118
+ op.ids.forEach(id => {
119
+ tr.delete(firestore.collection(op.table).doc((0, firestore_util_1.escapeDocId)(id)));
120
+ });
121
+ }
122
+ else {
123
+ throw new Error(`DBOperation not supported: ${op.type}`);
124
+ }
125
+ }
126
+ });
103
127
  }
104
128
  async ping() {
105
129
  // no-op now
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "prepare": "husky install"
5
5
  },
6
6
  "dependencies": {
7
- "@google-cloud/firestore": "^5.0.2",
7
+ "@google-cloud/firestore": "^6.0.0",
8
8
  "@naturalcycles/db-lib": "^8.2.0",
9
9
  "@naturalcycles/js-lib": "^14.6.0",
10
10
  "@naturalcycles/nodejs-lib": "^12.4.0",
@@ -15,7 +15,7 @@
15
15
  "@naturalcycles/test-lib": "^1.0.3",
16
16
  "@types/node": "^18.0.0",
17
17
  "dotenv": "^16.0.0",
18
- "jest": "^28.1.0"
18
+ "jest": "^29.1.2"
19
19
  },
20
20
  "files": [
21
21
  "dist",
@@ -37,7 +37,7 @@
37
37
  "engines": {
38
38
  "node": ">=14.15.0"
39
39
  },
40
- "version": "1.2.1",
40
+ "version": "1.3.0",
41
41
  "description": "Firestore implementation of CommonDB interface",
42
42
  "author": "Natural Cycles Team",
43
43
  "license": "MIT"
@@ -19,6 +19,7 @@ import {
19
19
  ObjectWithId,
20
20
  AnyObjectWithId,
21
21
  _assert,
22
+ _isTruthy,
22
23
  } from '@naturalcycles/js-lib'
23
24
  import { ReadableTyped, transformMapSimple } from '@naturalcycles/nodejs-lib'
24
25
  import { escapeDocId, unescapeDocId } from './firestore.util'
@@ -47,29 +48,28 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
47
48
  override async getByIds<ROW extends ObjectWithId>(
48
49
  table: string,
49
50
  ids: ROW['id'][],
50
- opt?: FirestoreDBOptions,
51
+ _opt?: FirestoreDBOptions,
51
52
  ): Promise<ROW[]> {
52
53
  // Oj, doesn't look like a very optimal implementation!
53
54
  // TODO: check if we can query by keys or smth
54
- return (await Promise.all(ids.map(id => this.getById<ROW>(table, id, opt)))).filter(
55
- Boolean,
56
- ) as ROW[]
57
- }
58
-
59
- async getById<ROW extends ObjectWithId>(
60
- table: string,
61
- id: ROW['id'],
62
- _opt?: FirestoreDBOptions,
63
- ): Promise<ROW | null> {
64
- const doc = await this.cfg.firestore.collection(table).doc(escapeDocId(id)).get()
65
-
66
- const data = doc.data()
67
- if (data === undefined) return null
68
-
69
- return {
70
- id,
71
- ...(data as any),
72
- }
55
+ // return (await Promise.all(ids.map(id => this.getById<ROW>(table, id, opt)))).filter(
56
+ // Boolean,
57
+ // ) as ROW[]
58
+ if (!ids.length) return []
59
+
60
+ const { firestore } = this.cfg
61
+ const col = firestore.collection(table)
62
+
63
+ return (await firestore.getAll(...ids.map(id => col.doc(escapeDocId(id)))))
64
+ .map(doc => {
65
+ const data = doc.data()
66
+ if (data === undefined) return
67
+ return {
68
+ id: unescapeDocId(doc.id),
69
+ ...(data as any),
70
+ }
71
+ })
72
+ .filter(_isTruthy)
73
73
  }
74
74
 
75
75
  // QUERY
@@ -83,7 +83,7 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
83
83
 
84
84
  // Special case when projection query didn't specify 'id'
85
85
  if (q._selectedFieldNames && !q._selectedFieldNames.includes('id')) {
86
- rows = rows.map(r => _omit(r as any, ['id']))
86
+ rows = rows.map(r => _omit(r, ['id']))
87
87
  }
88
88
 
89
89
  return { rows }
@@ -201,8 +201,27 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
201
201
  return rows
202
202
  }
203
203
 
204
- override async commitTransaction(_tx: DBTransaction, _opt?: CommonDBSaveOptions): Promise<void> {
205
- throw new Error('commitTransaction is not supported yet')
204
+ override async commitTransaction(tx: DBTransaction, _opt?: CommonDBSaveOptions): Promise<void> {
205
+ const { firestore } = this.cfg
206
+
207
+ await firestore.runTransaction(async tr => {
208
+ for (const op of tx.ops) {
209
+ if (op.type === 'saveBatch') {
210
+ op.rows.forEach(row => {
211
+ tr.set(
212
+ firestore.collection(op.table).doc(escapeDocId(row.id)),
213
+ _filterUndefinedValues(row),
214
+ )
215
+ })
216
+ } else if (op.type === 'deleteByIds') {
217
+ op.ids.forEach(id => {
218
+ tr.delete(firestore.collection(op.table).doc(escapeDocId(id)))
219
+ })
220
+ } else {
221
+ throw new Error(`DBOperation not supported: ${(op as any).type}`)
222
+ }
223
+ }
224
+ })
206
225
  }
207
226
 
208
227
  override async ping(): Promise<void> {