@travetto/model-firestore 8.0.0-alpha.1 → 8.0.0-alpha.10
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 +1 -1
- package/package.json +6 -5
- package/src/service.ts +89 -28
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ This module provides an [Firestore](https://firebase.google.com/docs/firestore)-
|
|
|
17
17
|
|
|
18
18
|
Supported features:
|
|
19
19
|
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/types/crud.ts#L11)
|
|
20
|
-
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/types/
|
|
20
|
+
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L23)
|
|
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/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.10",
|
|
4
4
|
"description": "Firestore backing for the travetto model module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -26,12 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@google-cloud/firestore": "^8.3.0",
|
|
29
|
-
"@travetto/config": "^8.0.0-alpha.
|
|
30
|
-
"@travetto/model": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/
|
|
29
|
+
"@travetto/config": "^8.0.0-alpha.10",
|
|
30
|
+
"@travetto/model": "^8.0.0-alpha.10",
|
|
31
|
+
"@travetto/model-indexed": "^8.0.0-alpha.10",
|
|
32
|
+
"@travetto/runtime": "^8.0.0-alpha.10"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
|
34
|
-
"@travetto/cli": "^8.0.0-alpha.
|
|
35
|
+
"@travetto/cli": "^8.0.0-alpha.15"
|
|
35
36
|
},
|
|
36
37
|
"peerDependenciesMeta": {
|
|
37
38
|
"@travetto/cli": {
|
package/src/service.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { type DocumentData, FieldValue, Firestore, type Query } from '@google-cloud/firestore';
|
|
2
2
|
|
|
3
|
-
import { JSONUtil, ShutdownManager, type Class
|
|
3
|
+
import { castTo, JSONUtil, ShutdownManager, type Class } from '@travetto/runtime';
|
|
4
4
|
import { Injectable, PostConstruct } from '@travetto/di';
|
|
5
5
|
import {
|
|
6
|
-
type ModelCrudSupport, ModelRegistryIndex, type ModelStorageSupport,
|
|
7
|
-
type ModelIndexedSupport, type ModelType, NotFoundError, type OptionalId,
|
|
8
|
-
ModelCrudUtil, ModelIndexedUtil,
|
|
6
|
+
type ModelCrudSupport, ModelRegistryIndex, type ModelStorageSupport, type ModelType, NotFoundError, type OptionalId, ModelCrudUtil,
|
|
9
7
|
} from '@travetto/model';
|
|
8
|
+
import {
|
|
9
|
+
type ModelIndexedSupport, type KeyedIndexSelection, type KeyedIndexBody, type ListPageOptions, ModelIndexedUtil,
|
|
10
|
+
type SingleItemIndex, type SortedIndexSelection, type ListPageResult, type SortedIndex, type FullKeyedIndexBody,
|
|
11
|
+
type FullKeyedIndexWithPartialBody, ModelIndexedComputedIndex
|
|
12
|
+
} from '@travetto/model-indexed';
|
|
10
13
|
|
|
11
14
|
import type { FirestoreModelConfig } from './config.ts';
|
|
12
15
|
|
|
@@ -38,8 +41,26 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
38
41
|
return this.client.collection(this.#resolveTable(cls));
|
|
39
42
|
}
|
|
40
43
|
|
|
44
|
+
#buildIndexQuery<
|
|
45
|
+
T extends ModelType,
|
|
46
|
+
K extends KeyedIndexSelection<T>,
|
|
47
|
+
S extends SortedIndexSelection<T>
|
|
48
|
+
>(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>): Query {
|
|
49
|
+
ModelCrudUtil.ensureNotSubType(cls);
|
|
50
|
+
const computed = ModelIndexedComputedIndex.get(idx, body).validate();
|
|
51
|
+
|
|
52
|
+
let query = computed.keyedParts.reduce<Query>((result, { path, value, state }) =>
|
|
53
|
+
result.where(path.join('.'), '==', (state === 'empty' ? null : value)), this.#getCollection(cls));
|
|
54
|
+
|
|
55
|
+
for (const { path, value } of idx.sortTemplate) {
|
|
56
|
+
query = query.orderBy(path.join('.'), value === 1 ? 'asc' : 'desc');
|
|
57
|
+
}
|
|
58
|
+
return query;
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
@PostConstruct()
|
|
42
62
|
async initializeClient(): Promise<void> {
|
|
63
|
+
globalThis.devProcessWarningExclusions?.push((_, category) => category === 'MetadataLookupWarning');
|
|
43
64
|
this.client = new Firestore({ ...this.config, useBigInt: true });
|
|
44
65
|
ShutdownManager.signal.addEventListener('abort', () => this.client.terminate());
|
|
45
66
|
}
|
|
@@ -119,49 +140,89 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
119
140
|
}
|
|
120
141
|
|
|
121
142
|
// Indexed
|
|
122
|
-
async #getIdByIndex<
|
|
143
|
+
async #getIdByIndex<
|
|
144
|
+
T extends ModelType,
|
|
145
|
+
K extends KeyedIndexSelection<T>,
|
|
146
|
+
S extends SortedIndexSelection<T>
|
|
147
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<string> {
|
|
123
148
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
const query = fields.reduce<Query>(
|
|
149
|
+
const computed = ModelIndexedComputedIndex.get(idx, body).validate({ sort: true });
|
|
150
|
+
const query = computed.allParts.reduce<Query>(
|
|
127
151
|
(result, { path, value }) => result.where(path.join('.'), '==', value),
|
|
128
152
|
this.#getCollection(cls)
|
|
129
153
|
);
|
|
130
154
|
|
|
131
155
|
const item = await query.get();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return item.docs[0].id;
|
|
156
|
+
if (!item || item.empty) {
|
|
157
|
+
throw new NotFoundError(`${cls.name} Index=${idx}`, computed.getKey());
|
|
135
158
|
}
|
|
136
|
-
|
|
159
|
+
return item.docs[0].id;
|
|
137
160
|
}
|
|
138
161
|
|
|
139
|
-
|
|
162
|
+
// Indexed contract
|
|
163
|
+
|
|
164
|
+
async getByIndex<
|
|
165
|
+
T extends ModelType,
|
|
166
|
+
K extends KeyedIndexSelection<T>,
|
|
167
|
+
S extends SortedIndexSelection<T>
|
|
168
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<T> {
|
|
140
169
|
return this.get(cls, await this.#getIdByIndex(cls, idx, body));
|
|
141
170
|
}
|
|
142
171
|
|
|
143
|
-
async deleteByIndex<
|
|
172
|
+
async deleteByIndex<
|
|
173
|
+
T extends ModelType,
|
|
174
|
+
K extends KeyedIndexSelection<T>,
|
|
175
|
+
S extends SortedIndexSelection<T>
|
|
176
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexBody<T, K, S>): Promise<void> {
|
|
144
177
|
return this.delete(cls, await this.#getIdByIndex(cls, idx, body));
|
|
145
178
|
}
|
|
146
179
|
|
|
147
|
-
upsertByIndex<
|
|
180
|
+
upsertByIndex<
|
|
181
|
+
T extends ModelType,
|
|
182
|
+
K extends KeyedIndexSelection<T>,
|
|
183
|
+
S extends SortedIndexSelection<T>
|
|
184
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: OptionalId<T>): Promise<T> {
|
|
148
185
|
return ModelIndexedUtil.naiveUpsert(this, cls, idx, body);
|
|
149
186
|
}
|
|
150
187
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
188
|
+
updateByIndex<
|
|
189
|
+
T extends ModelType,
|
|
190
|
+
K extends KeyedIndexSelection<T>,
|
|
191
|
+
S extends SortedIndexSelection<T>
|
|
192
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: T): Promise<T> {
|
|
193
|
+
return ModelIndexedUtil.naiveUpdate(this, cls, idx, body);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async updatePartialByIndex<
|
|
197
|
+
T extends ModelType,
|
|
198
|
+
K extends KeyedIndexSelection<T>,
|
|
199
|
+
S extends SortedIndexSelection<T>
|
|
200
|
+
>(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexWithPartialBody<T, K, S>): Promise<T> {
|
|
201
|
+
const item = await ModelCrudUtil.naivePartialUpdate(cls, () => this.getByIndex(cls, idx, castTo(body)), castTo(body));
|
|
202
|
+
return this.update(cls, item);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async listByIndex<
|
|
206
|
+
T extends ModelType,
|
|
207
|
+
K extends KeyedIndexSelection<T>,
|
|
208
|
+
S extends SortedIndexSelection<T>
|
|
209
|
+
>(
|
|
210
|
+
cls: Class<T>,
|
|
211
|
+
idx: SortedIndex<T, K, S>,
|
|
212
|
+
body: KeyedIndexBody<T, K>,
|
|
213
|
+
options?: ListPageOptions,
|
|
214
|
+
): Promise<ListPageResult<T>> {
|
|
215
|
+
const offset = options?.offset ? JSONUtil.fromBase64<number>(options.offset) : 0;
|
|
216
|
+
const limit = options?.limit ?? 100;
|
|
217
|
+
const query = this.#buildIndexQuery(cls, idx, body)
|
|
218
|
+
.limit(limit)
|
|
219
|
+
.offset(offset);
|
|
220
|
+
|
|
221
|
+
const items: T[] = [];
|
|
163
222
|
for (const item of (await query.get()).docs) {
|
|
164
|
-
|
|
223
|
+
items.push(await ModelCrudUtil.load(cls, item.data()!));
|
|
165
224
|
}
|
|
225
|
+
|
|
226
|
+
return { items, nextOffset: items.length ? JSONUtil.toBase64(offset + items.length) : undefined };
|
|
166
227
|
}
|
|
167
228
|
}
|