@travetto/model-firestore 8.0.0-alpha.27 → 8.0.0-alpha.29
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/README.md +3 -3
- package/__index__.ts +1 -1
- package/package.json +6 -6
- package/src/config.ts +3 -3
- package/src/service.ts +81 -68
- package/support/cli.firestore_indexes.ts +18 -14
- package/support/service.firestore.ts +1 -1
package/README.md
CHANGED
|
@@ -13,11 +13,11 @@ npm install @travetto/model-firestore
|
|
|
13
13
|
yarn add @travetto/model-firestore
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
This module provides an [Firestore](https://firebase.google.com/docs/firestore)-based implementation of the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.").
|
|
16
|
+
This module provides an [Firestore](https://firebase.google.com/docs/firestore)-based implementation of the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations."). This source allows the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module to read, write and query against [Firestore](https://firebase.google.com/docs/firestore).
|
|
17
17
|
|
|
18
18
|
Supported features:
|
|
19
|
-
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#
|
|
20
|
-
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#
|
|
19
|
+
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L10)
|
|
20
|
+
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L21)
|
|
21
21
|
|
|
22
22
|
Out of the box, by installing the module, everything should be wired up by default.If you need to customize any aspect of the source or config, you can override and register it with the [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.") module.
|
|
23
23
|
|
package/__index__.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-firestore",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.29",
|
|
4
4
|
"description": "Firestore backing for the travetto model module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@google-cloud/firestore": "^8.6.0",
|
|
29
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
30
|
-
"@travetto/model": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/model-indexed": "^8.0.0-alpha.
|
|
32
|
-
"@travetto/runtime": "^8.0.0-alpha.
|
|
29
|
+
"@travetto/config": "^8.0.0-alpha.22",
|
|
30
|
+
"@travetto/model": "^8.0.0-alpha.23",
|
|
31
|
+
"@travetto/model-indexed": "^8.0.0-alpha.25",
|
|
32
|
+
"@travetto/runtime": "^8.0.0-alpha.20"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
35
|
+
"@travetto/cli": "^8.0.0-alpha.28"
|
|
36
36
|
},
|
|
37
37
|
"peerDependenciesMeta": {
|
|
38
38
|
"@travetto/cli": {
|
package/src/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { JSONUtil, Runtime, RuntimeResources } from '@travetto/runtime';
|
|
2
1
|
import { Config } from '@travetto/config';
|
|
3
|
-
import { Schema, SchemaValidator } from '@travetto/schema';
|
|
4
2
|
import { PostConstruct } from '@travetto/di';
|
|
3
|
+
import { JSONUtil, Runtime, RuntimeResources } from '@travetto/runtime';
|
|
4
|
+
import { Schema, SchemaValidator } from '@travetto/schema';
|
|
5
5
|
|
|
6
6
|
@Schema()
|
|
7
7
|
class FirestoreModelConfigCredentials {
|
|
@@ -34,4 +34,4 @@ export class FirestoreModelConfig {
|
|
|
34
34
|
await SchemaValidator.validate(FirestoreModelConfigCredentials, this.credentials);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
}
|
|
37
|
+
}
|
package/src/service.ts
CHANGED
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
import { type DocumentData, FieldValue, Firestore, type Query } from '@google-cloud/firestore';
|
|
2
2
|
|
|
3
|
-
import { castTo, JSONUtil, ShutdownManager, type Class, RuntimeError } from '@travetto/runtime';
|
|
4
3
|
import { Injectable, PostConstruct } from '@travetto/di';
|
|
5
4
|
import {
|
|
6
|
-
type ModelCrudSupport,
|
|
5
|
+
type ModelCrudSupport,
|
|
6
|
+
ModelCrudUtil,
|
|
7
7
|
type ModelListOptions,
|
|
8
|
+
ModelRegistryIndex,
|
|
9
|
+
type ModelStorageSupport,
|
|
10
|
+
type ModelType,
|
|
11
|
+
NotFoundError,
|
|
12
|
+
type OptionalId
|
|
8
13
|
} from '@travetto/model';
|
|
9
14
|
import {
|
|
10
|
-
type
|
|
11
|
-
type
|
|
12
|
-
type
|
|
13
|
-
type
|
|
15
|
+
type FullKeyedIndexBody,
|
|
16
|
+
type FullKeyedIndexWithPartialBody,
|
|
17
|
+
type KeyedIndexBody,
|
|
18
|
+
type KeyedIndexSelection,
|
|
19
|
+
ModelIndexedComputedIndex,
|
|
20
|
+
type ModelIndexedSearchOptions,
|
|
21
|
+
type ModelIndexedSupport,
|
|
22
|
+
ModelIndexedUtil,
|
|
23
|
+
type ModelPageOptions,
|
|
24
|
+
type ModelPageResult,
|
|
25
|
+
type SingleItemIndex,
|
|
26
|
+
type SortedIndex,
|
|
27
|
+
type SortedIndexSelection,
|
|
28
|
+
type SortedIndexSelectionType,
|
|
29
|
+
warnIfIndexedUniqueIndex,
|
|
30
|
+
warnIfNonIndexedIndex
|
|
14
31
|
} from '@travetto/model-indexed';
|
|
32
|
+
import { type Class, castTo, JSONUtil, RuntimeError, ShutdownManager } from '@travetto/runtime';
|
|
15
33
|
|
|
16
34
|
import type { FirestoreModelConfig } from './config.ts';
|
|
17
35
|
|
|
@@ -24,7 +42,6 @@ const setMissingValues = <T>(input: T, missingValue: unknown): T =>
|
|
|
24
42
|
*/
|
|
25
43
|
@Injectable()
|
|
26
44
|
export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupport, ModelIndexedSupport {
|
|
27
|
-
|
|
28
45
|
static resolveTable(cls: Class, namespace?: string): string {
|
|
29
46
|
let table = ModelRegistryIndex.getStoreName(cls);
|
|
30
47
|
if (namespace) {
|
|
@@ -37,7 +54,9 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
37
54
|
client: Firestore;
|
|
38
55
|
config: FirestoreModelConfig;
|
|
39
56
|
|
|
40
|
-
constructor(config: FirestoreModelConfig) {
|
|
57
|
+
constructor(config: FirestoreModelConfig) {
|
|
58
|
+
this.config = config;
|
|
59
|
+
}
|
|
41
60
|
|
|
42
61
|
#resolveTable(cls: Class): string {
|
|
43
62
|
return FirestoreModelService.resolveTable(cls, this.config.namespace);
|
|
@@ -47,11 +66,11 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
47
66
|
return this.client.collection(this.#resolveTable(cls));
|
|
48
67
|
}
|
|
49
68
|
|
|
50
|
-
async #getIdByIndex<
|
|
51
|
-
T
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
async #getIdByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
70
|
+
cls: Class<T>,
|
|
71
|
+
idx: SingleItemIndex<T, K, S>,
|
|
72
|
+
body: FullKeyedIndexBody<T, K, S>
|
|
73
|
+
): Promise<string> {
|
|
55
74
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
56
75
|
const computed = ModelIndexedComputedIndex.get(idx, body).validate({ sort: true });
|
|
57
76
|
const query = [...computed.allParts, ...(computed.idPart ? [computed.idPart] : [])].reduce<Query>(
|
|
@@ -73,8 +92,10 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
73
92
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
74
93
|
const computed = ModelIndexedComputedIndex.get(idx, body).validate();
|
|
75
94
|
|
|
76
|
-
let query = computed.keyedParts.reduce<Query>(
|
|
77
|
-
result.where(path.join('.'), '==',
|
|
95
|
+
let query = computed.keyedParts.reduce<Query>(
|
|
96
|
+
(result, { path, value, state }) => result.where(path.join('.'), '==', state === 'empty' ? null : value),
|
|
97
|
+
this.#getCollection(cls)
|
|
98
|
+
);
|
|
78
99
|
|
|
79
100
|
for (const { path, value } of idx.sortTemplate) {
|
|
80
101
|
query = query.orderBy(path.join('.'), value === 1 ? 'asc' : 'desc');
|
|
@@ -82,18 +103,18 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
82
103
|
return query;
|
|
83
104
|
}
|
|
84
105
|
|
|
85
|
-
async
|
|
106
|
+
async *#scanCollection<T extends ModelType>(
|
|
86
107
|
cls: Class<T>,
|
|
87
108
|
queryBuilder: () => Query,
|
|
88
109
|
options?: ModelListOptions & ModelPageOptions<number>
|
|
89
|
-
): AsyncIterable<{ items: T[]
|
|
110
|
+
): AsyncIterable<{ items: T[]; nextOffset?: number }> {
|
|
90
111
|
const limit = options?.limit ?? Number.MAX_SAFE_INTEGER;
|
|
91
112
|
const batchSize = Math.min(options?.batchSizeHint ?? 100, limit);
|
|
92
113
|
|
|
93
114
|
let offset = options?.offset ?? 0;
|
|
94
115
|
let produced = 0;
|
|
95
116
|
|
|
96
|
-
while (!
|
|
117
|
+
while (!options?.abort?.aborted && produced < limit) {
|
|
97
118
|
const query = queryBuilder().limit(batchSize).offset(offset);
|
|
98
119
|
|
|
99
120
|
const { docs, size } = await query.get();
|
|
@@ -101,12 +122,11 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
101
122
|
break;
|
|
102
123
|
}
|
|
103
124
|
|
|
104
|
-
const remaining =
|
|
125
|
+
const remaining = produced + size > limit ? docs.slice(0, limit - produced) : docs;
|
|
105
126
|
|
|
106
127
|
offset += size;
|
|
107
128
|
|
|
108
|
-
const items = await ModelCrudUtil.filterOutNotFound(
|
|
109
|
-
remaining.map(item => ModelCrudUtil.load(cls, item.data()!)));
|
|
129
|
+
const items = await ModelCrudUtil.filterOutNotFound(remaining.map(item => ModelCrudUtil.load(cls, item.data()!)));
|
|
110
130
|
produced += items.length;
|
|
111
131
|
|
|
112
132
|
yield { items, nextOffset: offset };
|
|
@@ -126,7 +146,7 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
126
146
|
warnIfNonIndexedIndex(this, cls, ModelRegistryIndex.getIndices(cls));
|
|
127
147
|
}
|
|
128
148
|
}
|
|
129
|
-
async deleteStorage(): Promise<void> {
|
|
149
|
+
async deleteStorage(): Promise<void> {}
|
|
130
150
|
|
|
131
151
|
async deleteModel<T extends ModelType>(cls: Class<T>): Promise<void> {
|
|
132
152
|
await this.client.recursiveDelete(this.#getCollection(cls));
|
|
@@ -136,7 +156,7 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
136
156
|
async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
|
|
137
157
|
const result = await this.#getCollection(cls).doc(id).get();
|
|
138
158
|
|
|
139
|
-
if (result
|
|
159
|
+
if (result?.exists) {
|
|
140
160
|
return await ModelCrudUtil.load(cls, result.data()!);
|
|
141
161
|
}
|
|
142
162
|
throw new NotFoundError(cls, id);
|
|
@@ -167,7 +187,9 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
167
187
|
const id = item.id;
|
|
168
188
|
const full = await ModelCrudUtil.naivePartialUpdate(cls, () => this.get(cls, id), item, view);
|
|
169
189
|
const cleaned = setMissingValues(full, FieldValue.delete());
|
|
170
|
-
await this.#getCollection(cls)
|
|
190
|
+
await this.#getCollection(cls)
|
|
191
|
+
.doc(id)
|
|
192
|
+
.set(cleaned, { mergeFields: Object.keys(full) });
|
|
171
193
|
return this.get(cls, id);
|
|
172
194
|
}
|
|
173
195
|
|
|
@@ -183,7 +205,7 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
183
205
|
}
|
|
184
206
|
}
|
|
185
207
|
|
|
186
|
-
async *
|
|
208
|
+
async *list<T extends ModelType>(cls: Class<T>, options?: ModelListOptions): AsyncIterable<T[]> {
|
|
187
209
|
for await (const { items } of this.#scanCollection(cls, () => this.#getCollection(cls), options)) {
|
|
188
210
|
yield items;
|
|
189
211
|
}
|
|
@@ -191,56 +213,52 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
191
213
|
|
|
192
214
|
// Indexed contract
|
|
193
215
|
|
|
194
|
-
async getByIndex<
|
|
195
|
-
T
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
216
|
+
async getByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
217
|
+
cls: Class<T>,
|
|
218
|
+
idx: SingleItemIndex<T, K, S>,
|
|
219
|
+
body: FullKeyedIndexBody<T, K, S>
|
|
220
|
+
): Promise<T> {
|
|
199
221
|
return this.get(cls, await this.#getIdByIndex(cls, idx, body));
|
|
200
222
|
}
|
|
201
223
|
|
|
202
|
-
async deleteByIndex<
|
|
203
|
-
T
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
224
|
+
async deleteByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
225
|
+
cls: Class<T>,
|
|
226
|
+
idx: SingleItemIndex<T, K, S>,
|
|
227
|
+
body: FullKeyedIndexBody<T, K, S>
|
|
228
|
+
): Promise<void> {
|
|
207
229
|
return this.delete(cls, await this.#getIdByIndex(cls, idx, body));
|
|
208
230
|
}
|
|
209
231
|
|
|
210
|
-
upsertByIndex<
|
|
211
|
-
T
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
232
|
+
upsertByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
233
|
+
cls: Class<T>,
|
|
234
|
+
idx: SingleItemIndex<T, K, S>,
|
|
235
|
+
body: OptionalId<T>
|
|
236
|
+
): Promise<T> {
|
|
215
237
|
return ModelIndexedUtil.naiveUpsert(this, cls, idx, body);
|
|
216
238
|
}
|
|
217
239
|
|
|
218
|
-
updateByIndex<
|
|
219
|
-
T
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
240
|
+
updateByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
241
|
+
cls: Class<T>,
|
|
242
|
+
idx: SingleItemIndex<T, K, S>,
|
|
243
|
+
body: T
|
|
244
|
+
): Promise<T> {
|
|
223
245
|
return ModelIndexedUtil.naiveUpdate(this, cls, idx, body);
|
|
224
246
|
}
|
|
225
247
|
|
|
226
|
-
async updatePartialByIndex<
|
|
227
|
-
T
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
248
|
+
async updatePartialByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
249
|
+
cls: Class<T>,
|
|
250
|
+
idx: SingleItemIndex<T, K, S>,
|
|
251
|
+
body: FullKeyedIndexWithPartialBody<T, K, S>
|
|
252
|
+
): Promise<T> {
|
|
231
253
|
const item = await ModelCrudUtil.naivePartialUpdate(cls, () => this.getByIndex(cls, idx, castTo(body)), castTo(body));
|
|
232
254
|
return this.update(cls, item);
|
|
233
255
|
}
|
|
234
256
|
|
|
235
|
-
async pageByIndex<
|
|
236
|
-
T extends ModelType,
|
|
237
|
-
K extends KeyedIndexSelection<T>,
|
|
238
|
-
S extends SortedIndexSelection<T>
|
|
239
|
-
>(
|
|
257
|
+
async pageByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
240
258
|
cls: Class<T>,
|
|
241
259
|
idx: SortedIndex<T, K, S>,
|
|
242
260
|
body: KeyedIndexBody<T, K>,
|
|
243
|
-
options?: ModelPageOptions
|
|
261
|
+
options?: ModelPageOptions
|
|
244
262
|
): Promise<ModelPageResult<T>> {
|
|
245
263
|
const items: T[] = [];
|
|
246
264
|
let nextOffset: number | undefined;
|
|
@@ -256,11 +274,7 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
256
274
|
return { items, nextOffset: nextOffset ? JSONUtil.toBase64(nextOffset) : undefined };
|
|
257
275
|
}
|
|
258
276
|
|
|
259
|
-
async *
|
|
260
|
-
T extends ModelType,
|
|
261
|
-
K extends KeyedIndexSelection<T>,
|
|
262
|
-
S extends SortedIndexSelection<T>
|
|
263
|
-
>(
|
|
277
|
+
async *listByIndex<T extends ModelType, K extends KeyedIndexSelection<T>, S extends SortedIndexSelection<T>>(
|
|
264
278
|
cls: Class<T>,
|
|
265
279
|
idx: SortedIndex<T, K, S>,
|
|
266
280
|
body: KeyedIndexBody<T, K>,
|
|
@@ -281,15 +295,14 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
281
295
|
|
|
282
296
|
const field = idx.sortTemplate[0].path.join('.');
|
|
283
297
|
|
|
284
|
-
for await (const { items } of this.#scanCollection(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
) {
|
|
298
|
+
for await (const { items } of this.#scanCollection(
|
|
299
|
+
cls,
|
|
300
|
+
() => this.#buildIndexQuery(cls, idx, body).where(field, '>=', prefix).where(field, '<', `${prefix}\uf8ff`),
|
|
301
|
+
{ limit: 10, ...options }
|
|
302
|
+
)) {
|
|
290
303
|
results.push(...items);
|
|
291
304
|
}
|
|
292
305
|
|
|
293
306
|
return results;
|
|
294
307
|
}
|
|
295
|
-
}
|
|
308
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import cp from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
|
|
4
|
-
import { type CliCommandShape,
|
|
5
|
-
import { JSONUtil, Env, ExecUtil, Runtime } from '@travetto/runtime';
|
|
6
|
-
import { Registry } from '@travetto/registry';
|
|
4
|
+
import { CliCommand, type CliCommandShape, CliModuleFlag } from '@travetto/cli';
|
|
7
5
|
import { DependencyRegistryIndex } from '@travetto/di';
|
|
6
|
+
import { ManifestFileUtil } from '@travetto/manifest';
|
|
8
7
|
import { ModelRegistryIndex } from '@travetto/model';
|
|
9
8
|
import { isModelIndexedIndex } from '@travetto/model-indexed';
|
|
10
|
-
import {
|
|
9
|
+
import { Registry } from '@travetto/registry';
|
|
10
|
+
import { Env, ExecUtil, JSONUtil, Runtime } from '@travetto/runtime';
|
|
11
11
|
|
|
12
12
|
import { FirestoreModelConfig } from '../src/config.ts';
|
|
13
13
|
import { FirestoreModelService } from '../src/service.ts';
|
|
@@ -36,7 +36,6 @@ type FirestoreIndexSet = {
|
|
|
36
36
|
*/
|
|
37
37
|
@CliCommand()
|
|
38
38
|
export class FirestoreIndexesCommand implements CliCommandShape {
|
|
39
|
-
|
|
40
39
|
indexFile = 'firestore.indexes.json';
|
|
41
40
|
firebaseFile = 'firebase.json';
|
|
42
41
|
|
|
@@ -61,14 +60,16 @@ export class FirestoreIndexesCommand implements CliCommandShape {
|
|
|
61
60
|
const indices = ModelRegistryIndex.getIndices(cls)
|
|
62
61
|
.filter(isModelIndexedIndex)
|
|
63
62
|
// We need at least 2 fields. All 1 field indices are already handled
|
|
64
|
-
.filter(
|
|
63
|
+
.filter(idx => (idx.keyTemplate?.length ?? 0) + (idx.sortTemplate?.length ?? 0) >= 2);
|
|
65
64
|
|
|
66
65
|
for (const idx of indices) {
|
|
67
66
|
indexesList.push({
|
|
68
67
|
collectionGroup: FirestoreModelService.resolveTable(cls, config.namespace),
|
|
69
68
|
queryScope: 'COLLECTION',
|
|
70
|
-
fields: [...idx.keyTemplate, ...idx.sortTemplate]
|
|
71
|
-
|
|
69
|
+
fields: [...idx.keyTemplate, ...idx.sortTemplate].map(part => ({
|
|
70
|
+
fieldPath: part.path.join('.'),
|
|
71
|
+
order: part.value === -1 ? 'DESCENDING' : 'ASCENDING'
|
|
72
|
+
}))
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
75
|
}
|
|
@@ -83,17 +84,19 @@ export class FirestoreIndexesCommand implements CliCommandShape {
|
|
|
83
84
|
await ManifestFileUtil.bufferedFileWrite(this.indexFile, text);
|
|
84
85
|
|
|
85
86
|
const firebaseLocation = Runtime.workspaceRelative(this.firebaseFile);
|
|
86
|
-
if (!await fs.stat(firebaseLocation, { throwIfNoEntry: false })) {
|
|
87
|
+
if (!(await fs.stat(firebaseLocation, { throwIfNoEntry: false }))) {
|
|
87
88
|
await ManifestFileUtil.bufferedFileWrite(firebaseLocation, '{}');
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
const firebaseContext = JSONUtil.fromBinaryArray<{ firestore?: { database?: string
|
|
91
|
+
const firebaseContext = JSONUtil.fromBinaryArray<{ firestore?: { database?: string; indexes?: string }[] }>(
|
|
92
|
+
await fs.readFile(firebaseLocation)
|
|
93
|
+
);
|
|
91
94
|
const existing = (firebaseContext.firestore ??= []);
|
|
92
95
|
const found = existing.find(x => x.database === config.databaseId);
|
|
93
96
|
|
|
94
97
|
let changed = true;
|
|
95
98
|
if (!found) {
|
|
96
|
-
existing.push(
|
|
99
|
+
existing.push({ indexes: this.indexFile, database: config.databaseId });
|
|
97
100
|
} else if (found.indexes !== this.indexFile) {
|
|
98
101
|
found.indexes = this.indexFile;
|
|
99
102
|
} else {
|
|
@@ -106,11 +109,12 @@ export class FirestoreIndexesCommand implements CliCommandShape {
|
|
|
106
109
|
|
|
107
110
|
if (this.deploy) {
|
|
108
111
|
const child = cp.spawn(
|
|
109
|
-
'firebase',
|
|
112
|
+
'firebase',
|
|
113
|
+
['deploy', '--only', 'firestore:indexes'],
|
|
110
114
|
// Complete take over
|
|
111
|
-
{ stdio: 'inherit' }
|
|
115
|
+
{ stdio: 'inherit' }
|
|
112
116
|
);
|
|
113
|
-
await ExecUtil.getResult(child)
|
|
117
|
+
await ExecUtil.getResult(child);
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
}
|