@twin.org/entity-storage-connector-gcp-firestore 0.0.1-next.12
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/LICENSE +201 -0
- package/README.md +35 -0
- package/dist/cjs/index.cjs +392 -0
- package/dist/esm/index.mjs +390 -0
- package/dist/types/firestoreEntityStorageConnector.d.ts +88 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/models/IEntityWithIndexing.d.ts +14 -0
- package/dist/types/models/IFirestoreEntityStorageConnectorConfig.d.ts +38 -0
- package/dist/types/models/IValueType.d.ts +13 -0
- package/docs/changelog.md +5 -0
- package/docs/examples.md +1 -0
- package/docs/reference/classes/FirestoreEntityStorageConnector.md +235 -0
- package/docs/reference/index.md +9 -0
- package/docs/reference/interfaces/IFirestoreEntityStorageConnectorConfig.md +63 -0
- package/locales/en.json +23 -0
- package/package.json +42 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { Firestore } from '@google-cloud/firestore';
|
|
2
|
+
import { Guards, Is, ObjectHelper, Converter, BaseError, GeneralError } from '@twin.org/core';
|
|
3
|
+
import { EntitySchemaFactory, EntitySchemaHelper, SortDirection, ComparisonOperator } from '@twin.org/entity';
|
|
4
|
+
import { LoggingConnectorFactory } from '@twin.org/logging-models';
|
|
5
|
+
|
|
6
|
+
// Copyright 2024 IOTA Stiftung.
|
|
7
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
8
|
+
/**
|
|
9
|
+
* Class for performing entity storage operations using Firestore.
|
|
10
|
+
*/
|
|
11
|
+
class FirestoreEntityStorageConnector {
|
|
12
|
+
/**
|
|
13
|
+
* Limit the number of entities when finding.
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
static _PAGE_SIZE = 40;
|
|
17
|
+
/**
|
|
18
|
+
* Runtime name for the class.
|
|
19
|
+
*/
|
|
20
|
+
CLASS_NAME = "FirestoreEntityStorageConnector";
|
|
21
|
+
/**
|
|
22
|
+
* The schema for the entity.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
_entitySchema;
|
|
26
|
+
/**
|
|
27
|
+
* The primary key.
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
_primaryKey;
|
|
31
|
+
/**
|
|
32
|
+
* The configuration for the connector.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
_config;
|
|
36
|
+
/**
|
|
37
|
+
* The Firestore client.
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
_firestoreClient;
|
|
41
|
+
/**
|
|
42
|
+
* The Firestore collection.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
_collection;
|
|
46
|
+
/**
|
|
47
|
+
* Create a new instance of FirestoreEntityStorageConnector.
|
|
48
|
+
* @param options The options for the connector.
|
|
49
|
+
* @param options.entitySchema The schema for the entity.
|
|
50
|
+
* @param options.loggingConnectorType The type of logging connector to use, defaults to no logging.
|
|
51
|
+
* @param options.config The configuration for the connector.
|
|
52
|
+
*/
|
|
53
|
+
constructor(options) {
|
|
54
|
+
Guards.object(this.CLASS_NAME, "options", options);
|
|
55
|
+
Guards.stringValue(this.CLASS_NAME, "options.entitySchema", options.entitySchema);
|
|
56
|
+
Guards.object(this.CLASS_NAME, "options.config", options.config);
|
|
57
|
+
Guards.stringValue(this.CLASS_NAME, "options.config.projectId", options.config.projectId);
|
|
58
|
+
Guards.stringValue(this.CLASS_NAME, "options.config.collectionName", options.config.collectionName);
|
|
59
|
+
let credentials;
|
|
60
|
+
if (!Is.empty(options.config.credentials)) {
|
|
61
|
+
Guards.stringBase64(this.CLASS_NAME, "options.config.credentials", options.config.credentials);
|
|
62
|
+
credentials = ObjectHelper.fromBytes(Converter.base64ToBytes(options.config.credentials));
|
|
63
|
+
}
|
|
64
|
+
this._config = options.config;
|
|
65
|
+
this._entitySchema = EntitySchemaFactory.get(options.entitySchema);
|
|
66
|
+
this._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);
|
|
67
|
+
const firestoreOptions = {
|
|
68
|
+
projectId: this._config.projectId,
|
|
69
|
+
databaseId: this._config.databaseId,
|
|
70
|
+
collectionName: this._config.collectionName,
|
|
71
|
+
maxIdleChannels: this._config.settings?.maxIdleChannels,
|
|
72
|
+
timeout: this._config.settings?.timeout,
|
|
73
|
+
credentials
|
|
74
|
+
};
|
|
75
|
+
if (Is.stringValue(this._config.endpoint)) {
|
|
76
|
+
firestoreOptions.host = this._config.endpoint;
|
|
77
|
+
firestoreOptions.ssl = false;
|
|
78
|
+
}
|
|
79
|
+
this._firestoreClient = new Firestore(firestoreOptions);
|
|
80
|
+
this._collection = this._firestoreClient.collection(this._config.collectionName);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Bootstrap the component by creating and initializing any resources it needs.
|
|
84
|
+
* @param nodeLoggingConnectorType The node logging connector type, defaults to "node-logging".
|
|
85
|
+
* @returns True if the bootstrapping process was successful.
|
|
86
|
+
*/
|
|
87
|
+
async bootstrap(nodeLoggingConnectorType) {
|
|
88
|
+
const nodeLogging = LoggingConnectorFactory.getIfExists(nodeLoggingConnectorType ?? "node-logging");
|
|
89
|
+
try {
|
|
90
|
+
await nodeLogging?.log({
|
|
91
|
+
level: "info",
|
|
92
|
+
source: this.CLASS_NAME,
|
|
93
|
+
ts: Date.now(),
|
|
94
|
+
message: "firestoreCreating",
|
|
95
|
+
data: {
|
|
96
|
+
projectId: this._config.projectId,
|
|
97
|
+
collectionName: this._config.collectionName
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// Firestore doesn't require explicit collection creation
|
|
101
|
+
// Perform a small write operation to ensure connectivity
|
|
102
|
+
const testDoc = this._firestoreClient.collection(this._config.collectionName).doc("test");
|
|
103
|
+
await testDoc.set({ test: true });
|
|
104
|
+
await testDoc.delete();
|
|
105
|
+
await nodeLogging?.log({
|
|
106
|
+
level: "info",
|
|
107
|
+
source: this.CLASS_NAME,
|
|
108
|
+
ts: Date.now(),
|
|
109
|
+
message: "firestoreCreated",
|
|
110
|
+
data: {
|
|
111
|
+
projectId: this._config.projectId,
|
|
112
|
+
collectionName: this._config.collectionName
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
await nodeLogging?.log({
|
|
119
|
+
level: "error",
|
|
120
|
+
source: this.CLASS_NAME,
|
|
121
|
+
ts: Date.now(),
|
|
122
|
+
message: "firestoreCreationFailed",
|
|
123
|
+
error: BaseError.fromError(err),
|
|
124
|
+
data: {
|
|
125
|
+
projectId: this._config.projectId,
|
|
126
|
+
collectionName: this._config.collectionName
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the schema for the entities.
|
|
134
|
+
* @returns The schema for the entities.
|
|
135
|
+
*/
|
|
136
|
+
getSchema() {
|
|
137
|
+
return this._entitySchema;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get an entity.
|
|
141
|
+
* @param id The id of the entity to get.
|
|
142
|
+
* @param secondaryIndex The optional secondary index to use.
|
|
143
|
+
* @param conditions The optional conditions to apply to the query.
|
|
144
|
+
* @returns The object if it can be found or undefined.
|
|
145
|
+
*/
|
|
146
|
+
async get(id, secondaryIndex, conditions) {
|
|
147
|
+
Guards.stringValue(this.CLASS_NAME, "id", id);
|
|
148
|
+
try {
|
|
149
|
+
if (!Is.stringValue(secondaryIndex) && !Is.arrayValue(conditions)) {
|
|
150
|
+
const docRef = this._collection.doc(id);
|
|
151
|
+
const doc = await docRef.get();
|
|
152
|
+
if (doc.exists) {
|
|
153
|
+
return doc.data();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Use secondaryIndex and/or conditions to construct a query
|
|
157
|
+
let query = this._collection;
|
|
158
|
+
if (secondaryIndex) {
|
|
159
|
+
query = query.where(secondaryIndex, "==", id);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// If no secondaryIndex, include primary key in conditions
|
|
163
|
+
query = query.where(this._primaryKey.property, "==", id);
|
|
164
|
+
}
|
|
165
|
+
if (Is.arrayValue(conditions)) {
|
|
166
|
+
for (const condition of conditions) {
|
|
167
|
+
query = query.where(condition.property, "==", condition.value);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const querySnapshot = await query.limit(1).get();
|
|
171
|
+
if (!querySnapshot.empty) {
|
|
172
|
+
return querySnapshot.docs[0].data();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
throw new GeneralError(this.CLASS_NAME, "getEntityFailed", { id }, err);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Set an entity.
|
|
181
|
+
* @param entity The entity to set.
|
|
182
|
+
* @param conditions The optional conditions to apply to the update.
|
|
183
|
+
* @returns Nothing.
|
|
184
|
+
*/
|
|
185
|
+
async set(entity, conditions) {
|
|
186
|
+
Guards.object(this.CLASS_NAME, "entity", entity);
|
|
187
|
+
try {
|
|
188
|
+
const id = entity[this._primaryKey.property];
|
|
189
|
+
const entityCopy = { ...entity };
|
|
190
|
+
// Handle indexing field
|
|
191
|
+
if (entityCopy.valueArray && Is.array(entityCopy.valueArray)) {
|
|
192
|
+
const valueArrayFields = entityCopy.valueArray
|
|
193
|
+
.filter((item) => Is.notEmpty(item))
|
|
194
|
+
.map(item => `${item.field}:${item.value}`);
|
|
195
|
+
entityCopy.valueArrayFields = valueArrayFields;
|
|
196
|
+
}
|
|
197
|
+
const docRef = this._collection.doc(id);
|
|
198
|
+
if (!Is.arrayValue(conditions)) {
|
|
199
|
+
await docRef.set(entityCopy);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
await this._firestoreClient.runTransaction(async (transaction) => {
|
|
203
|
+
const docSnapshot = await transaction.get(docRef);
|
|
204
|
+
if (!docSnapshot.exists) {
|
|
205
|
+
transaction.set(docRef, entityCopy);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
const data = docSnapshot.data();
|
|
209
|
+
let conditionsMet = true;
|
|
210
|
+
for (const condition of conditions) {
|
|
211
|
+
if (data[condition.property] !== condition.value) {
|
|
212
|
+
conditionsMet = false;
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (conditionsMet) {
|
|
217
|
+
transaction.set(docRef, entityCopy);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
throw new GeneralError(this.CLASS_NAME, "setEntityFailed", { entity }, err);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Remove the entity.
|
|
229
|
+
* @param id The id of the entity to remove.
|
|
230
|
+
* @param conditions The optional conditions to apply to the delete.
|
|
231
|
+
* @returns Nothing.
|
|
232
|
+
*/
|
|
233
|
+
async remove(id, conditions) {
|
|
234
|
+
Guards.stringValue(this.CLASS_NAME, "id", id);
|
|
235
|
+
try {
|
|
236
|
+
const docRef = this._collection.doc(id);
|
|
237
|
+
if (!Is.arrayValue(conditions)) {
|
|
238
|
+
await docRef.delete();
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
await this._firestoreClient.runTransaction(async (transaction) => {
|
|
242
|
+
const docSnapshot = await transaction.get(docRef);
|
|
243
|
+
if (docSnapshot.exists) {
|
|
244
|
+
const data = docSnapshot.data();
|
|
245
|
+
let conditionsMet = true;
|
|
246
|
+
for (const condition of conditions) {
|
|
247
|
+
if (data[condition.property] !== condition.value) {
|
|
248
|
+
conditionsMet = false;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (conditionsMet) {
|
|
253
|
+
transaction.delete(docRef);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
throw new GeneralError(this.CLASS_NAME, "removeEntityFailed", { id }, err);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Find all the entities which match the conditions.
|
|
265
|
+
* @param conditions The conditions to match for the entities.
|
|
266
|
+
* @param sortProperties The optional sort order.
|
|
267
|
+
* @param properties The optional properties to return, defaults to all.
|
|
268
|
+
* @param cursor The cursor to request the next page of entities.
|
|
269
|
+
* @param pageSize The suggested number of entities to return in each chunk.
|
|
270
|
+
* @returns The matching entities and a cursor for the next page.
|
|
271
|
+
*/
|
|
272
|
+
async query(conditions, sortProperties, properties, cursor, pageSize) {
|
|
273
|
+
const queryDescription = [];
|
|
274
|
+
try {
|
|
275
|
+
let query = this._collection;
|
|
276
|
+
if (conditions) {
|
|
277
|
+
query = this.applyConditions(query, conditions);
|
|
278
|
+
queryDescription.push(`Conditions: ${JSON.stringify(conditions)}`);
|
|
279
|
+
}
|
|
280
|
+
if (Is.arrayValue(sortProperties)) {
|
|
281
|
+
for (const { property, sortDirection } of sortProperties) {
|
|
282
|
+
query = query.orderBy(property, sortDirection === SortDirection.Ascending ? "asc" : "desc");
|
|
283
|
+
}
|
|
284
|
+
queryDescription.push(`Sort: ${JSON.stringify(sortProperties)}`);
|
|
285
|
+
}
|
|
286
|
+
if (Is.stringValue(cursor)) {
|
|
287
|
+
const cursorDoc = await this._firestoreClient.doc(cursor).get();
|
|
288
|
+
if (cursorDoc?.exists) {
|
|
289
|
+
query = query.startAfter(cursorDoc);
|
|
290
|
+
}
|
|
291
|
+
queryDescription.push(`Cursor: ${cursor}`);
|
|
292
|
+
}
|
|
293
|
+
const limit = pageSize ?? FirestoreEntityStorageConnector._PAGE_SIZE;
|
|
294
|
+
query = query.limit(limit);
|
|
295
|
+
queryDescription.push(`Limit: ${limit}`);
|
|
296
|
+
if (properties) {
|
|
297
|
+
query = query.select(...properties);
|
|
298
|
+
queryDescription.push(`Properties: ${properties.join(", ")}`);
|
|
299
|
+
}
|
|
300
|
+
const querySnapshot = await query.get();
|
|
301
|
+
const entities = querySnapshot.docs.map((doc) => doc.data());
|
|
302
|
+
let nextCursor;
|
|
303
|
+
if (entities.length === limit) {
|
|
304
|
+
nextCursor = querySnapshot.docs[querySnapshot.docs.length - 1].ref.path;
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
entities,
|
|
308
|
+
cursor: nextCursor
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
catch (err) {
|
|
312
|
+
throw new GeneralError(this.CLASS_NAME, "queryFailed", { queryDescription: queryDescription.join("; ") }, err);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Delete all entities in the collection.
|
|
317
|
+
* @returns Nothing.
|
|
318
|
+
* @internal
|
|
319
|
+
*/
|
|
320
|
+
async collectionDelete() {
|
|
321
|
+
const collection = this._collection;
|
|
322
|
+
const batchSize = 500;
|
|
323
|
+
const query = collection.limit(batchSize);
|
|
324
|
+
try {
|
|
325
|
+
await this.deleteQueryBatch(query, batchSize);
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
throw new GeneralError(this.CLASS_NAME, "collectionDeleteFailed", { collectionName: this._config.collectionName }, error);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Apply conditions to a Firestore query.
|
|
333
|
+
* @param query The initial query.
|
|
334
|
+
* @param condition The condition to apply.
|
|
335
|
+
* @returns The updated query.
|
|
336
|
+
* @internal
|
|
337
|
+
*/
|
|
338
|
+
applyConditions(query, condition) {
|
|
339
|
+
if ("conditions" in condition) {
|
|
340
|
+
// It's a group of conditions
|
|
341
|
+
for (const c of condition.conditions) {
|
|
342
|
+
query = this.applyConditions(query, c);
|
|
343
|
+
}
|
|
344
|
+
return query;
|
|
345
|
+
}
|
|
346
|
+
// It's a single condition
|
|
347
|
+
const { property, value, comparison } = condition;
|
|
348
|
+
switch (comparison) {
|
|
349
|
+
case ComparisonOperator.Equals:
|
|
350
|
+
return query.where(property, "==", value);
|
|
351
|
+
case ComparisonOperator.NotEquals:
|
|
352
|
+
return query.where(property, "!=", value);
|
|
353
|
+
case ComparisonOperator.GreaterThan:
|
|
354
|
+
return query.where(property, ">", value);
|
|
355
|
+
case ComparisonOperator.LessThan:
|
|
356
|
+
return query.where(property, "<", value);
|
|
357
|
+
case ComparisonOperator.GreaterThanOrEqual:
|
|
358
|
+
return query.where(property, ">=", value);
|
|
359
|
+
case ComparisonOperator.LessThanOrEqual:
|
|
360
|
+
return query.where(property, "<=", value);
|
|
361
|
+
case ComparisonOperator.In:
|
|
362
|
+
return query.where(property, "in", value);
|
|
363
|
+
case ComparisonOperator.Includes:
|
|
364
|
+
return query.where(property, "array-contains", value);
|
|
365
|
+
default:
|
|
366
|
+
throw new GeneralError(this.CLASS_NAME, "unsupportedComparisonOperator", { comparison });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Delete all entities in the collection.
|
|
371
|
+
* @returns Nothing.
|
|
372
|
+
* @internal
|
|
373
|
+
*/
|
|
374
|
+
async deleteQueryBatch(query, batchSize) {
|
|
375
|
+
const snapshot = await query.get();
|
|
376
|
+
if (snapshot.size === 0) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const batch = this._firestoreClient.batch();
|
|
380
|
+
for (const doc of snapshot.docs) {
|
|
381
|
+
batch.delete(doc.ref);
|
|
382
|
+
}
|
|
383
|
+
await batch.commit();
|
|
384
|
+
if (snapshot.size === batchSize) {
|
|
385
|
+
await this.deleteQueryBatch(query, batchSize);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export { FirestoreEntityStorageConnector };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type EntityCondition, type IEntitySchema, SortDirection } from "@twin.org/entity";
|
|
2
|
+
import type { IEntityStorageConnector } from "@twin.org/entity-storage-models";
|
|
3
|
+
import type { IFirestoreEntityStorageConnectorConfig } from "./models/IFirestoreEntityStorageConnectorConfig";
|
|
4
|
+
/**
|
|
5
|
+
* Class for performing entity storage operations using Firestore.
|
|
6
|
+
*/
|
|
7
|
+
export declare class FirestoreEntityStorageConnector<T = unknown> implements IEntityStorageConnector<T> {
|
|
8
|
+
/**
|
|
9
|
+
* Runtime name for the class.
|
|
10
|
+
*/
|
|
11
|
+
readonly CLASS_NAME: string;
|
|
12
|
+
/**
|
|
13
|
+
* Create a new instance of FirestoreEntityStorageConnector.
|
|
14
|
+
* @param options The options for the connector.
|
|
15
|
+
* @param options.entitySchema The schema for the entity.
|
|
16
|
+
* @param options.loggingConnectorType The type of logging connector to use, defaults to no logging.
|
|
17
|
+
* @param options.config The configuration for the connector.
|
|
18
|
+
*/
|
|
19
|
+
constructor(options: {
|
|
20
|
+
entitySchema: string;
|
|
21
|
+
loggingConnectorType?: string;
|
|
22
|
+
config: IFirestoreEntityStorageConnectorConfig;
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Bootstrap the component by creating and initializing any resources it needs.
|
|
26
|
+
* @param nodeLoggingConnectorType The node logging connector type, defaults to "node-logging".
|
|
27
|
+
* @returns True if the bootstrapping process was successful.
|
|
28
|
+
*/
|
|
29
|
+
bootstrap(nodeLoggingConnectorType?: string): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Get the schema for the entities.
|
|
32
|
+
* @returns The schema for the entities.
|
|
33
|
+
*/
|
|
34
|
+
getSchema(): IEntitySchema;
|
|
35
|
+
/**
|
|
36
|
+
* Get an entity.
|
|
37
|
+
* @param id The id of the entity to get.
|
|
38
|
+
* @param secondaryIndex The optional secondary index to use.
|
|
39
|
+
* @param conditions The optional conditions to apply to the query.
|
|
40
|
+
* @returns The object if it can be found or undefined.
|
|
41
|
+
*/
|
|
42
|
+
get(id: string, secondaryIndex?: keyof T, conditions?: {
|
|
43
|
+
property: keyof T;
|
|
44
|
+
value: unknown;
|
|
45
|
+
}[]): Promise<T | undefined>;
|
|
46
|
+
/**
|
|
47
|
+
* Set an entity.
|
|
48
|
+
* @param entity The entity to set.
|
|
49
|
+
* @param conditions The optional conditions to apply to the update.
|
|
50
|
+
* @returns Nothing.
|
|
51
|
+
*/
|
|
52
|
+
set(entity: T, conditions?: {
|
|
53
|
+
property: keyof T;
|
|
54
|
+
value: unknown;
|
|
55
|
+
}[]): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Remove the entity.
|
|
58
|
+
* @param id The id of the entity to remove.
|
|
59
|
+
* @param conditions The optional conditions to apply to the delete.
|
|
60
|
+
* @returns Nothing.
|
|
61
|
+
*/
|
|
62
|
+
remove(id: string, conditions?: {
|
|
63
|
+
property: keyof T;
|
|
64
|
+
value: unknown;
|
|
65
|
+
}[]): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Find all the entities which match the conditions.
|
|
68
|
+
* @param conditions The conditions to match for the entities.
|
|
69
|
+
* @param sortProperties The optional sort order.
|
|
70
|
+
* @param properties The optional properties to return, defaults to all.
|
|
71
|
+
* @param cursor The cursor to request the next page of entities.
|
|
72
|
+
* @param pageSize The suggested number of entities to return in each chunk.
|
|
73
|
+
* @returns The matching entities and a cursor for the next page.
|
|
74
|
+
*/
|
|
75
|
+
query(conditions?: EntityCondition<T>, sortProperties?: {
|
|
76
|
+
property: keyof T;
|
|
77
|
+
sortDirection: SortDirection;
|
|
78
|
+
}[], properties?: (keyof T)[], cursor?: string, pageSize?: number): Promise<{
|
|
79
|
+
/**
|
|
80
|
+
* The entities, which can be partial if a limited keys list was provided.
|
|
81
|
+
*/
|
|
82
|
+
entities: Partial<T>[];
|
|
83
|
+
/**
|
|
84
|
+
* An optional cursor, when defined can be used to call find to get more entities.
|
|
85
|
+
*/
|
|
86
|
+
cursor?: string;
|
|
87
|
+
}>;
|
|
88
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IValueType } from "./IValueType";
|
|
2
|
+
/**
|
|
3
|
+
* Interface representing an entity with indexing fields.
|
|
4
|
+
*/
|
|
5
|
+
export interface IEntityWithIndexing {
|
|
6
|
+
/**
|
|
7
|
+
* The value array.
|
|
8
|
+
*/
|
|
9
|
+
valueArray?: IValueType[];
|
|
10
|
+
/**
|
|
11
|
+
* The value array fields.
|
|
12
|
+
*/
|
|
13
|
+
valueArrayFields?: string[];
|
|
14
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the Firestore Entity Storage Connector.
|
|
3
|
+
*/
|
|
4
|
+
export interface IFirestoreEntityStorageConnectorConfig {
|
|
5
|
+
/**
|
|
6
|
+
* The GCP project ID.
|
|
7
|
+
*/
|
|
8
|
+
projectId: string;
|
|
9
|
+
/**
|
|
10
|
+
* The database ID, if omitted default database will be used.
|
|
11
|
+
*/
|
|
12
|
+
databaseId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* The name of the collection for the storage.
|
|
15
|
+
*/
|
|
16
|
+
collectionName: string;
|
|
17
|
+
/**
|
|
18
|
+
* The GCP credentials, a base64 encoded version of the JWTInput data type.
|
|
19
|
+
*/
|
|
20
|
+
credentials?: string;
|
|
21
|
+
/**
|
|
22
|
+
* It's usually only used with an emulator (e.g., "localhost:8080").
|
|
23
|
+
*/
|
|
24
|
+
endpoint?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Optional settings for Firestore client initialization.
|
|
27
|
+
*/
|
|
28
|
+
settings?: {
|
|
29
|
+
/**
|
|
30
|
+
* The maximum number of idle channels to keep open.
|
|
31
|
+
*/
|
|
32
|
+
maxIdleChannels?: number;
|
|
33
|
+
/**
|
|
34
|
+
* The custom timeout for requests (in milliseconds).
|
|
35
|
+
*/
|
|
36
|
+
timeout?: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
package/docs/examples.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @twin.org/entity-storage-connector-gcp-firestore - Examples
|