@travetto/model-firestore 2.1.5 → 2.2.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/README.md +2 -2
- package/package.json +3 -3
- package/src/config.ts +1 -1
- package/src/service.ts +34 -31
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm install @travetto/model-firestore
|
|
|
10
10
|
|
|
11
11
|
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).
|
|
12
12
|
|
|
13
|
-
Supported
|
|
13
|
+
Supported features:
|
|
14
14
|
|
|
15
15
|
* [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts#L11)
|
|
16
16
|
* [Indexed](https://github.com/travetto/travetto/tree/main/module/model/src/service/indexed.ts#L12)
|
|
@@ -57,7 +57,7 @@ export class FirestoreModelConfig {
|
|
|
57
57
|
private_key: string;
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
async postConstruct() {
|
|
60
|
+
async postConstruct(): Promise<void> {
|
|
61
61
|
if (this.emulator) {
|
|
62
62
|
process.env.FIRESTORE_EMULATOR_HOST = this.emulator;
|
|
63
63
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-firestore",
|
|
3
3
|
"displayName": "Firestore Model Support",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"description": "Firestore backing for the travetto model module.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"typescript",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"directory": "module/model-firestore"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/config": "^2.
|
|
30
|
-
"@travetto/model": "^2.
|
|
29
|
+
"@travetto/config": "^2.2.0",
|
|
30
|
+
"@travetto/model": "^2.2.0",
|
|
31
31
|
"@google-cloud/firestore": "^5.0.2"
|
|
32
32
|
},
|
|
33
33
|
"private": false,
|
package/src/config.ts
CHANGED
package/src/service.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FieldValue, Firestore,
|
|
1
|
+
import { DocumentData, FieldValue, Firestore, PartialWithFieldValue, Query, UpdateData } from '@google-cloud/firestore';
|
|
2
2
|
|
|
3
3
|
import { ShutdownManager, Util, Class } from '@travetto/base';
|
|
4
4
|
import { DeepPartial } from '@travetto/schema';
|
|
@@ -13,9 +13,9 @@ import { ModelIndexedUtil } from '@travetto/model/src/internal/service/indexed';
|
|
|
13
13
|
|
|
14
14
|
import { FirestoreModelConfig } from './config';
|
|
15
15
|
|
|
16
|
-
const clone = <T>(inp: T) => JSON.parse(JSON.stringify(inp));
|
|
16
|
+
const clone = <T>(inp: T): T => JSON.parse(JSON.stringify(inp));
|
|
17
17
|
|
|
18
|
-
const toSimpleObj = <T>(inp: T, missingValue: unknown = null) =>
|
|
18
|
+
const toSimpleObj = <T>(inp: T, missingValue: unknown = null): PartialWithFieldValue<DocumentData> =>
|
|
19
19
|
JSON.parse(JSON.stringify(inp, (_, v) => v ?? null), (_, v) => v ?? missingValue);
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -28,7 +28,7 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
28
28
|
|
|
29
29
|
constructor(public readonly config: FirestoreModelConfig) { }
|
|
30
30
|
|
|
31
|
-
#resolveTable(cls: Class) {
|
|
31
|
+
#resolveTable(cls: Class): string {
|
|
32
32
|
let table = ModelRegistry.getStore(cls);
|
|
33
33
|
if (this.config.namespace) {
|
|
34
34
|
table = `${this.config.namespace}_${table}`;
|
|
@@ -36,20 +36,20 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
36
36
|
return table;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
#getCollection(cls: Class) {
|
|
39
|
+
#getCollection(cls: Class): FirebaseFirestore.CollectionReference<DocumentData> {
|
|
40
40
|
return this.client.collection(this.#resolveTable(cls));
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
async postConstruct() {
|
|
43
|
+
async postConstruct(): Promise<void> {
|
|
44
44
|
this.client = new Firestore(this.config);
|
|
45
45
|
ShutdownManager.onShutdown(this.constructor.ᚕid, () => this.client.terminate());
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// Storage
|
|
49
|
-
async createStorage() { }
|
|
50
|
-
async deleteStorage() { }
|
|
49
|
+
async createStorage(): Promise<void> { }
|
|
50
|
+
async deleteStorage(): Promise<void> { }
|
|
51
51
|
|
|
52
|
-
async deleteModel(cls: Class) {
|
|
52
|
+
async deleteModel(cls: Class): Promise<void> {
|
|
53
53
|
for await (const el of this.list(cls)) {
|
|
54
54
|
await this.delete(cls, el.id);
|
|
55
55
|
}
|
|
@@ -60,7 +60,7 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
60
60
|
return Util.uuid();
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
async get<T extends ModelType>(cls: Class<T>, id: string) {
|
|
63
|
+
async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
|
|
64
64
|
const res = await this.#getCollection(cls).doc(id).get();
|
|
65
65
|
|
|
66
66
|
if (res && res.exists) {
|
|
@@ -69,67 +69,70 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
69
69
|
throw new NotFoundError(cls, id);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>) {
|
|
72
|
+
async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> {
|
|
73
73
|
const prepped = await ModelCrudUtil.preStore(cls, item, this);
|
|
74
74
|
await this.#getCollection(cls).doc(prepped.id).create(clone(prepped));
|
|
75
75
|
return prepped;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
async update<T extends ModelType>(cls: Class<T>, item: T) {
|
|
78
|
+
async update<T extends ModelType>(cls: Class<T>, item: T): Promise<T> {
|
|
79
79
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
const prepped = await ModelCrudUtil.preStore(cls, item, this);
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
82
|
+
await this.#getCollection(cls).doc(item.id).update(clone(prepped) as unknown as UpdateData<DocumentData>);
|
|
82
83
|
return item;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>) {
|
|
86
|
+
async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> {
|
|
86
87
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
87
88
|
const prepped = await ModelCrudUtil.preStore(cls, item, this);
|
|
88
89
|
await this.#getCollection(cls).doc(prepped.id).set(clone(prepped));
|
|
89
90
|
return prepped;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string) {
|
|
93
|
+
async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T> {
|
|
93
94
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
94
95
|
const id = item.id;
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
95
97
|
item = await ModelCrudUtil.naivePartialUpdate(cls, item, view, async () => ({} as unknown as T));
|
|
96
98
|
const cleaned = toSimpleObj(item, FieldValue.delete());
|
|
97
99
|
await this.#getCollection(cls).doc(id).set(cleaned, { merge: true });
|
|
98
100
|
return this.get(cls, id);
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
async delete<T extends ModelType>(cls: Class<T>, id: string) {
|
|
103
|
+
async delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void> {
|
|
102
104
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
103
105
|
try {
|
|
104
|
-
await this.#getCollection(cls).doc(id).delete({ exists: true }
|
|
106
|
+
await this.#getCollection(cls).doc(id).delete({ exists: true });
|
|
105
107
|
} catch (err) {
|
|
106
|
-
if (
|
|
108
|
+
if (err && err instanceof Error && err.message.includes('NOT_FOUND')) {
|
|
107
109
|
throw new NotFoundError(cls, id);
|
|
108
110
|
}
|
|
109
111
|
throw err;
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
114
|
|
|
113
|
-
async * list<T extends ModelType>(cls: Class<T>) {
|
|
115
|
+
async * list<T extends ModelType>(cls: Class<T>): AsyncIterable<T> {
|
|
114
116
|
const batch = await this.#getCollection(cls).select().get();
|
|
115
117
|
for (const el of batch.docs) {
|
|
116
118
|
try {
|
|
117
119
|
yield await this.get(cls, el.id);
|
|
118
|
-
} catch (
|
|
119
|
-
if (!(
|
|
120
|
-
throw
|
|
120
|
+
} catch (err) {
|
|
121
|
+
if (!(err instanceof NotFoundError)) {
|
|
122
|
+
throw err;
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
// Indexed
|
|
127
|
-
async #getIdByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) {
|
|
129
|
+
async #getIdByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<string> {
|
|
128
130
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
129
131
|
|
|
130
132
|
const { fields } = ModelIndexedUtil.computeIndexParts(cls, idx, body);
|
|
131
|
-
const query = fields.reduce(
|
|
132
|
-
|
|
133
|
+
const query = fields.reduce<Query>(
|
|
134
|
+
(q, { path, value }) => q.where(path.join('.'), '==', value),
|
|
135
|
+
this.#getCollection(cls)
|
|
133
136
|
);
|
|
134
137
|
|
|
135
138
|
const item = await query.get();
|
|
@@ -140,11 +143,11 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
140
143
|
throw new NotFoundError(`${cls.name} Index=${idx}`, ModelIndexedUtil.computeIndexKey(cls, idx, body, { sep: '; ' })?.key);
|
|
141
144
|
}
|
|
142
145
|
|
|
143
|
-
async getByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) {
|
|
146
|
+
async getByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<T> {
|
|
144
147
|
return this.get(cls, await this.#getIdByIndex(cls, idx, body));
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) {
|
|
150
|
+
async deleteByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): Promise<void> {
|
|
148
151
|
return this.delete(cls, await this.#getIdByIndex(cls, idx, body));
|
|
149
152
|
}
|
|
150
153
|
|
|
@@ -152,12 +155,12 @@ export class FirestoreModelService implements ModelCrudSupport, ModelStorageSupp
|
|
|
152
155
|
return ModelIndexedUtil.naiveUpsert(this, cls, idx, body);
|
|
153
156
|
}
|
|
154
157
|
|
|
155
|
-
async * listByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>) {
|
|
158
|
+
async * listByIndex<T extends ModelType>(cls: Class<T>, idx: string, body: DeepPartial<T>): AsyncIterable<T> {
|
|
156
159
|
ModelCrudUtil.ensureNotSubType(cls);
|
|
157
160
|
|
|
158
161
|
const { fields, sorted } = ModelIndexedUtil.computeIndexParts(cls, idx, body, { emptySortValue: null });
|
|
159
|
-
let query = fields.reduce((q, { path, value }) =>
|
|
160
|
-
q.where(path.join('.'), '==', value), this.#getCollection(cls)
|
|
162
|
+
let query = fields.reduce<Query>((q, { path, value }) =>
|
|
163
|
+
q.where(path.join('.'), '==', value), this.#getCollection(cls));
|
|
161
164
|
|
|
162
165
|
if (sorted) {
|
|
163
166
|
query = query.orderBy(sorted.path.join('.'), sorted.dir === 1 ? 'asc' : 'desc');
|