@twin.org/entity-storage-connector-cosmosdb 0.0.2-next.8 → 0.0.3-next.1
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/dist/{esm/index.mjs → es/cosmosDbEntityStorageConnector.js} +178 -102
- package/dist/es/cosmosDbEntityStorageConnector.js.map +1 -0
- package/dist/es/index.js +6 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/ICosmosDbEntityStorageConnectorConfig.js +4 -0
- package/dist/es/models/ICosmosDbEntityStorageConnectorConfig.js.map +1 -0
- package/dist/es/models/ICosmosDbEntityStorageConnectorConstructorOptions.js +2 -0
- package/dist/es/models/ICosmosDbEntityStorageConnectorConstructorOptions.js.map +1 -0
- package/dist/types/cosmosDbEntityStorageConnector.d.ts +10 -5
- package/dist/types/index.d.ts +3 -3
- package/dist/types/models/ICosmosDbEntityStorageConnectorConfig.d.ts +4 -0
- package/dist/types/models/ICosmosDbEntityStorageConnectorConstructorOptions.d.ts +5 -1
- package/docs/changelog.md +61 -0
- package/docs/reference/classes/CosmosDbEntityStorageConnector.md +24 -10
- package/docs/reference/interfaces/ICosmosDbEntityStorageConnectorConfig.md +8 -0
- package/docs/reference/interfaces/ICosmosDbEntityStorageConnectorConstructorOptions.md +8 -0
- package/locales/en.json +7 -5
- package/package.json +29 -11
- package/dist/cjs/index.cjs +0 -595
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The tests developed are functional tests and need an instance of CosmosDB up and
|
|
|
15
15
|
To run a Docker image of Azure CosmosDB:
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
docker run -d --platform=linux/amd64 -p 8081:8081 -
|
|
18
|
+
docker run -d --platform=linux/amd64 -p 8081:8081 -m 3g --name twin-entity-storage-cosmos -e AZURE_COSMOS_EMULATOR_PARTITION_COUNT=10 -e AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE=true -e AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE='127.0.0.1' mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Or
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
import { CosmosClient, CosmosDbDiagnosticLevel, PartitionKeyKind } from '@azure/cosmos';
|
|
2
|
-
import { Guards, ComponentFactory, BaseError, Is, GeneralError, Coerce, ObjectHelper } from '@twin.org/core';
|
|
3
|
-
import { EntitySchemaFactory, EntitySchemaHelper, SortDirection, ComparisonOperator, LogicalOperator } from '@twin.org/entity';
|
|
4
|
-
|
|
5
1
|
// Copyright 2024 IOTA Stiftung.
|
|
6
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { CosmosClient, CosmosDbDiagnosticLevel, PartitionKeyKind } from "@azure/cosmos";
|
|
4
|
+
import { ContextIdHelper, ContextIdStore } from "@twin.org/context";
|
|
5
|
+
import { BaseError, Coerce, ComponentFactory, GeneralError, Guards, Is, ObjectHelper } from "@twin.org/core";
|
|
6
|
+
import { ComparisonOperator, EntitySchemaFactory, EntitySchemaHelper, LogicalOperator, SortDirection } from "@twin.org/entity";
|
|
7
7
|
/**
|
|
8
8
|
* Class for performing entity storage operations using Cosmos DB.
|
|
9
9
|
*/
|
|
10
|
-
class CosmosDbEntityStorageConnector {
|
|
10
|
+
export class CosmosDbEntityStorageConnector {
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @internal
|
|
12
|
+
* Runtime name for the class.
|
|
14
13
|
*/
|
|
15
|
-
static
|
|
14
|
+
static CLASS_NAME = "CosmosDbEntityStorageConnector";
|
|
16
15
|
/**
|
|
17
|
-
*
|
|
16
|
+
* Limit the number of entities when finding.
|
|
18
17
|
* @internal
|
|
19
18
|
*/
|
|
20
|
-
static
|
|
19
|
+
static _DEFAULT_LIMIT = 40;
|
|
21
20
|
/**
|
|
22
|
-
* Partition id field
|
|
21
|
+
* Partition id field name.
|
|
23
22
|
* @internal
|
|
24
23
|
*/
|
|
25
|
-
static
|
|
24
|
+
static _PARTITION_KEY = "partitionId";
|
|
26
25
|
/**
|
|
27
26
|
* Partition id field value.
|
|
28
27
|
* @internal
|
|
29
28
|
*/
|
|
30
|
-
static
|
|
31
|
-
/**
|
|
32
|
-
* Runtime name for the class.
|
|
33
|
-
*/
|
|
34
|
-
CLASS_NAME = "CosmosDbEntityStorageConnector";
|
|
29
|
+
static _PARTITION_KEY_VALUE = "root";
|
|
35
30
|
/**
|
|
36
31
|
* The schema for the entity.
|
|
37
32
|
* @internal
|
|
38
33
|
*/
|
|
39
34
|
_entitySchema;
|
|
35
|
+
/**
|
|
36
|
+
* The keys to use from the context ids to create partitions.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
_partitionContextIds;
|
|
40
40
|
/**
|
|
41
41
|
* The primary key.
|
|
42
42
|
* @internal
|
|
@@ -62,14 +62,15 @@ class CosmosDbEntityStorageConnector {
|
|
|
62
62
|
* @param options The options for the connector.
|
|
63
63
|
*/
|
|
64
64
|
constructor(options) {
|
|
65
|
-
Guards.object(
|
|
66
|
-
Guards.stringValue(
|
|
67
|
-
Guards.object(
|
|
68
|
-
Guards.stringValue(
|
|
69
|
-
Guards.stringValue(
|
|
70
|
-
Guards.stringValue(
|
|
71
|
-
Guards.stringValue(
|
|
65
|
+
Guards.object(CosmosDbEntityStorageConnector.CLASS_NAME, "options", options);
|
|
66
|
+
Guards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, "options.entitySchema", options.entitySchema);
|
|
67
|
+
Guards.object(CosmosDbEntityStorageConnector.CLASS_NAME, "options.config", options.config);
|
|
68
|
+
Guards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, "options.config.endpoint", options.config.endpoint);
|
|
69
|
+
Guards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, "options.config.key", options.config.key);
|
|
70
|
+
Guards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, "options.config.databaseId", options.config.databaseId);
|
|
71
|
+
Guards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, "options.config.containerId", options.config.containerId);
|
|
72
72
|
this._entitySchema = EntitySchemaFactory.get(options.entitySchema);
|
|
73
|
+
this._partitionContextIds = options.partitionContextIds;
|
|
73
74
|
this._primaryKey = EntitySchemaHelper.getPrimaryKey(this._entitySchema);
|
|
74
75
|
this._config = options.config;
|
|
75
76
|
this._client = new CosmosClient({
|
|
@@ -90,36 +91,13 @@ class CosmosDbEntityStorageConnector {
|
|
|
90
91
|
const nodeLogging = ComponentFactory.getIfExists(nodeLoggingComponentType);
|
|
91
92
|
// Create the database if it does not exist
|
|
92
93
|
try {
|
|
93
|
-
await
|
|
94
|
-
|
|
95
|
-
source: this.CLASS_NAME,
|
|
96
|
-
ts: Date.now(),
|
|
97
|
-
message: "databaseCreating",
|
|
98
|
-
data: {
|
|
99
|
-
databaseId: this._config.databaseId
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
const { resource: databaseDefinition } = await this._client.databases.createIfNotExists({
|
|
103
|
-
id: this._config.databaseId
|
|
104
|
-
});
|
|
105
|
-
Guards.stringValue(this.CLASS_NAME, "databaseDefinition.id", databaseDefinition?.id);
|
|
106
|
-
await nodeLogging?.log({
|
|
107
|
-
level: "info",
|
|
108
|
-
source: this.CLASS_NAME,
|
|
109
|
-
ts: Date.now(),
|
|
110
|
-
message: "databaseExists",
|
|
111
|
-
data: {
|
|
112
|
-
databaseId: databaseDefinition.id
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
if (BaseError.isErrorCode(error, "Conflict")) {
|
|
94
|
+
const databaseExists = await this.databaseExists();
|
|
95
|
+
if (databaseExists) {
|
|
118
96
|
await nodeLogging?.log({
|
|
119
97
|
level: "info",
|
|
120
|
-
source:
|
|
98
|
+
source: CosmosDbEntityStorageConnector.CLASS_NAME,
|
|
121
99
|
ts: Date.now(),
|
|
122
|
-
message: "
|
|
100
|
+
message: "databaseExists",
|
|
123
101
|
data: {
|
|
124
102
|
databaseId: this._config.databaseId
|
|
125
103
|
}
|
|
@@ -127,33 +105,40 @@ class CosmosDbEntityStorageConnector {
|
|
|
127
105
|
}
|
|
128
106
|
else {
|
|
129
107
|
await nodeLogging?.log({
|
|
130
|
-
level: "
|
|
131
|
-
source:
|
|
108
|
+
level: "info",
|
|
109
|
+
source: CosmosDbEntityStorageConnector.CLASS_NAME,
|
|
132
110
|
ts: Date.now(),
|
|
133
|
-
message: "
|
|
134
|
-
error: BaseError.fromError(error),
|
|
111
|
+
message: "databaseCreating",
|
|
135
112
|
data: {
|
|
136
113
|
databaseId: this._config.databaseId
|
|
137
114
|
}
|
|
138
115
|
});
|
|
139
|
-
|
|
116
|
+
await this._client.databases.create({
|
|
117
|
+
id: this._config.databaseId
|
|
118
|
+
});
|
|
119
|
+
await this.waitForDatabaseExists();
|
|
140
120
|
}
|
|
141
121
|
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
await nodeLogging?.log({
|
|
124
|
+
level: "error",
|
|
125
|
+
source: CosmosDbEntityStorageConnector.CLASS_NAME,
|
|
126
|
+
ts: Date.now(),
|
|
127
|
+
message: "databaseCreateFailed",
|
|
128
|
+
error: BaseError.fromError(error),
|
|
129
|
+
data: {
|
|
130
|
+
databaseId: this._config.databaseId
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
142
135
|
// Create the container if it does not exist
|
|
143
136
|
try {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
.containers.createIfNotExists({
|
|
147
|
-
id: this._config.containerId,
|
|
148
|
-
partitionKey: {
|
|
149
|
-
kind: PartitionKeyKind.Hash,
|
|
150
|
-
paths: [CosmosDbEntityStorageConnector._PARTITION_ID_PATH]
|
|
151
|
-
}
|
|
152
|
-
}, { offerThroughput: 400 });
|
|
153
|
-
if (containerDefinition) {
|
|
137
|
+
const containerExists = await this.containerExists();
|
|
138
|
+
if (containerExists) {
|
|
154
139
|
await nodeLogging?.log({
|
|
155
140
|
level: "info",
|
|
156
|
-
source:
|
|
141
|
+
source: CosmosDbEntityStorageConnector.CLASS_NAME,
|
|
157
142
|
ts: Date.now(),
|
|
158
143
|
message: "containerExists",
|
|
159
144
|
data: {
|
|
@@ -163,21 +148,28 @@ class CosmosDbEntityStorageConnector {
|
|
|
163
148
|
}
|
|
164
149
|
else {
|
|
165
150
|
await nodeLogging?.log({
|
|
166
|
-
level: "
|
|
167
|
-
source:
|
|
151
|
+
level: "info",
|
|
152
|
+
source: CosmosDbEntityStorageConnector.CLASS_NAME,
|
|
168
153
|
ts: Date.now(),
|
|
169
|
-
message: "
|
|
154
|
+
message: "containerCreating",
|
|
170
155
|
data: {
|
|
171
156
|
containerId: this._config.containerId
|
|
172
157
|
}
|
|
173
158
|
});
|
|
174
|
-
|
|
159
|
+
await this._client.database(this._config.databaseId).containers.create({
|
|
160
|
+
id: this._config.containerId,
|
|
161
|
+
partitionKey: {
|
|
162
|
+
kind: PartitionKeyKind.Hash,
|
|
163
|
+
paths: [`/${CosmosDbEntityStorageConnector._PARTITION_KEY}`]
|
|
164
|
+
}
|
|
165
|
+
}, { offerThroughput: this._config.offerThroughput });
|
|
166
|
+
await this.waitForContainerExists();
|
|
175
167
|
}
|
|
176
168
|
}
|
|
177
169
|
catch (error) {
|
|
178
170
|
await nodeLogging?.log({
|
|
179
171
|
level: "error",
|
|
180
|
-
source:
|
|
172
|
+
source: CosmosDbEntityStorageConnector.CLASS_NAME,
|
|
181
173
|
ts: Date.now(),
|
|
182
174
|
message: "containerCreateFailed",
|
|
183
175
|
error: BaseError.fromError(error),
|
|
@@ -189,6 +181,13 @@ class CosmosDbEntityStorageConnector {
|
|
|
189
181
|
}
|
|
190
182
|
return true;
|
|
191
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Returns the class name of the component.
|
|
186
|
+
* @returns The class name of the component.
|
|
187
|
+
*/
|
|
188
|
+
className() {
|
|
189
|
+
return CosmosDbEntityStorageConnector.CLASS_NAME;
|
|
190
|
+
}
|
|
192
191
|
/**
|
|
193
192
|
* Get the schema for the entities.
|
|
194
193
|
* @returns The schema for the entities.
|
|
@@ -204,17 +203,19 @@ class CosmosDbEntityStorageConnector {
|
|
|
204
203
|
* @returns The object if it can be found or undefined.
|
|
205
204
|
*/
|
|
206
205
|
async get(id, secondaryIndex, conditions) {
|
|
207
|
-
Guards.stringValue(
|
|
206
|
+
Guards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, "id", id);
|
|
207
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
208
|
+
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
208
209
|
try {
|
|
209
210
|
// No secondary index or conditions
|
|
210
211
|
if (Is.empty(secondaryIndex) && !Is.arrayValue(conditions)) {
|
|
211
212
|
const { resource: item } = await this._container
|
|
212
|
-
.item(id, CosmosDbEntityStorageConnector.
|
|
213
|
+
.item(id, partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE)
|
|
213
214
|
.read();
|
|
214
215
|
return this.itemToEntity(item);
|
|
215
216
|
}
|
|
216
217
|
const whereQuery = [
|
|
217
|
-
`c.${CosmosDbEntityStorageConnector.
|
|
218
|
+
`c.${CosmosDbEntityStorageConnector._PARTITION_KEY} = @partitionKey`
|
|
218
219
|
];
|
|
219
220
|
// With a secondary index
|
|
220
221
|
if (Is.stringValue(secondaryIndex)) {
|
|
@@ -237,7 +238,10 @@ class CosmosDbEntityStorageConnector {
|
|
|
237
238
|
query: `SELECT * FROM c WHERE ${whereQuery.join(" AND ")}`,
|
|
238
239
|
parameters: [
|
|
239
240
|
{ name: "@id", value: id },
|
|
240
|
-
{
|
|
241
|
+
{
|
|
242
|
+
name: "@partitionKey",
|
|
243
|
+
value: partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE
|
|
244
|
+
}
|
|
241
245
|
]
|
|
242
246
|
};
|
|
243
247
|
const { resources: items } = await this._container.items.query(query).fetchAll();
|
|
@@ -247,11 +251,11 @@ class CosmosDbEntityStorageConnector {
|
|
|
247
251
|
}
|
|
248
252
|
catch (err) {
|
|
249
253
|
if (BaseError.isErrorCode(err, "NotFound")) {
|
|
250
|
-
throw new GeneralError(
|
|
251
|
-
|
|
254
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "containerDoesNotExist", {
|
|
255
|
+
containerId: this._config.containerId
|
|
252
256
|
}, err);
|
|
253
257
|
}
|
|
254
|
-
throw new GeneralError(
|
|
258
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "getFailed", {
|
|
255
259
|
id
|
|
256
260
|
}, err);
|
|
257
261
|
}
|
|
@@ -264,12 +268,14 @@ class CosmosDbEntityStorageConnector {
|
|
|
264
268
|
* @returns The id of the entity.
|
|
265
269
|
*/
|
|
266
270
|
async set(entity, conditions) {
|
|
267
|
-
Guards.object(
|
|
271
|
+
Guards.object(CosmosDbEntityStorageConnector.CLASS_NAME, "entity", entity);
|
|
272
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
273
|
+
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
268
274
|
EntitySchemaHelper.validateEntity(entity, this.getSchema());
|
|
269
275
|
const id = entity[this._primaryKey.property];
|
|
270
276
|
try {
|
|
271
277
|
if (Is.arrayValue(conditions)) {
|
|
272
|
-
const item =
|
|
278
|
+
const item = this._container.item(id, partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE);
|
|
273
279
|
const { resource: itemData } = await item.read();
|
|
274
280
|
if (Is.notEmpty(itemData) && !this.verifyConditions(conditions, itemData)) {
|
|
275
281
|
return;
|
|
@@ -277,7 +283,7 @@ class CosmosDbEntityStorageConnector {
|
|
|
277
283
|
}
|
|
278
284
|
await this._container.items.upsert({
|
|
279
285
|
id,
|
|
280
|
-
[CosmosDbEntityStorageConnector.
|
|
286
|
+
[CosmosDbEntityStorageConnector._PARTITION_KEY]: partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE,
|
|
281
287
|
...entity
|
|
282
288
|
});
|
|
283
289
|
}
|
|
@@ -285,12 +291,12 @@ class CosmosDbEntityStorageConnector {
|
|
|
285
291
|
if (BaseError.isAggregateError(err)) {
|
|
286
292
|
const errors = BaseError.fromAggregate(err);
|
|
287
293
|
if (BaseError.someErrorCode(errors, "ResourceNotFoundException")) {
|
|
288
|
-
throw new GeneralError(
|
|
294
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "containerDoesNotExist", {
|
|
289
295
|
containerId: this._config.containerId
|
|
290
296
|
}, err);
|
|
291
297
|
}
|
|
292
298
|
}
|
|
293
|
-
throw new GeneralError(
|
|
299
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "setFailed", {
|
|
294
300
|
id
|
|
295
301
|
}, err);
|
|
296
302
|
}
|
|
@@ -302,9 +308,11 @@ class CosmosDbEntityStorageConnector {
|
|
|
302
308
|
* @returns Nothing.
|
|
303
309
|
*/
|
|
304
310
|
async remove(id, conditions) {
|
|
305
|
-
Guards.stringValue(
|
|
311
|
+
Guards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, "id", id);
|
|
312
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
313
|
+
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
306
314
|
try {
|
|
307
|
-
const item =
|
|
315
|
+
const item = this._container.item(id, partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE);
|
|
308
316
|
const { resource: itemData } = await item.read();
|
|
309
317
|
if (Is.notEmpty(itemData)) {
|
|
310
318
|
if (Is.arrayValue(conditions) && !this.verifyConditions(conditions, itemData)) {
|
|
@@ -319,7 +327,7 @@ class CosmosDbEntityStorageConnector {
|
|
|
319
327
|
err.body?.code === "NotFound") {
|
|
320
328
|
return;
|
|
321
329
|
}
|
|
322
|
-
throw new GeneralError(
|
|
330
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "removeFailed", {
|
|
323
331
|
id
|
|
324
332
|
}, err);
|
|
325
333
|
}
|
|
@@ -329,19 +337,21 @@ class CosmosDbEntityStorageConnector {
|
|
|
329
337
|
* @param conditions The conditions to match for the entities.
|
|
330
338
|
* @param sortProperties The optional sort order.
|
|
331
339
|
* @param properties The optional properties to return, defaults to all.
|
|
332
|
-
* @param cursor The cursor to request the next
|
|
333
|
-
* @param
|
|
340
|
+
* @param cursor The cursor to request the next chunk of entities.
|
|
341
|
+
* @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.
|
|
334
342
|
* @returns All the entities for the storage matching the conditions,
|
|
335
343
|
* and a cursor which can be used to request more entities.
|
|
336
344
|
*/
|
|
337
|
-
async query(conditions, sortProperties, properties, cursor,
|
|
338
|
-
|
|
345
|
+
async query(conditions, sortProperties, properties, cursor, limit) {
|
|
346
|
+
let sql = "";
|
|
347
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
348
|
+
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
339
349
|
try {
|
|
340
|
-
const returnSize =
|
|
350
|
+
const returnSize = limit ?? CosmosDbEntityStorageConnector._DEFAULT_LIMIT;
|
|
341
351
|
let orderByClause = "";
|
|
342
352
|
if (Array.isArray(sortProperties)) {
|
|
343
353
|
if (sortProperties.length > 1) {
|
|
344
|
-
throw new GeneralError(
|
|
354
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "sortSingle");
|
|
345
355
|
}
|
|
346
356
|
for (const sortProperty of sortProperties) {
|
|
347
357
|
const propertySchema = this._entitySchema.properties?.find(e => e.property === sortProperty.property);
|
|
@@ -349,7 +359,7 @@ class CosmosDbEntityStorageConnector {
|
|
|
349
359
|
(!propertySchema.isPrimary &&
|
|
350
360
|
!propertySchema.isSecondary &&
|
|
351
361
|
!propertySchema.sortDirection)) {
|
|
352
|
-
throw new GeneralError(
|
|
362
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "sortNotIndexed", {
|
|
353
363
|
property: sortProperty.property
|
|
354
364
|
});
|
|
355
365
|
}
|
|
@@ -363,10 +373,14 @@ class CosmosDbEntityStorageConnector {
|
|
|
363
373
|
if (queryClause.length > 0) {
|
|
364
374
|
queryClause = ` AND ${queryClause}`;
|
|
365
375
|
}
|
|
376
|
+
sql = `SELECT ${properties ? properties.map(p => `c.${p}`).join(", ") : "*"} FROM c WHERE c.partitionId = @partitionId ${queryClause} ${orderByClause}`;
|
|
366
377
|
const querySpecs = {
|
|
367
|
-
query:
|
|
378
|
+
query: sql,
|
|
368
379
|
parameters: [
|
|
369
|
-
{
|
|
380
|
+
{
|
|
381
|
+
name: "@partitionId",
|
|
382
|
+
value: partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE
|
|
383
|
+
},
|
|
370
384
|
...Object.keys(attributeValues).map(key => ({ name: `@${key}`, value: attributeValues[key] }))
|
|
371
385
|
]
|
|
372
386
|
};
|
|
@@ -381,7 +395,7 @@ class CosmosDbEntityStorageConnector {
|
|
|
381
395
|
};
|
|
382
396
|
}
|
|
383
397
|
catch (err) {
|
|
384
|
-
throw new GeneralError(
|
|
398
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "queryFailed", { sql }, err);
|
|
385
399
|
}
|
|
386
400
|
}
|
|
387
401
|
/**
|
|
@@ -389,8 +403,10 @@ class CosmosDbEntityStorageConnector {
|
|
|
389
403
|
* @returns Nothing.
|
|
390
404
|
*/
|
|
391
405
|
async containerDelete() {
|
|
406
|
+
const contextIds = await ContextIdStore.getContextIds();
|
|
407
|
+
const partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);
|
|
392
408
|
try {
|
|
393
|
-
await this._container.deleteAllItemsForPartitionKey(CosmosDbEntityStorageConnector.
|
|
409
|
+
await this._container.deleteAllItemsForPartitionKey(partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE);
|
|
394
410
|
await this._container.delete();
|
|
395
411
|
}
|
|
396
412
|
catch {
|
|
@@ -500,7 +516,7 @@ class CosmosDbEntityStorageConnector {
|
|
|
500
516
|
else if (comparator.comparison === ComparisonOperator.In) {
|
|
501
517
|
return `c.${propName} IN ${attributeName}`;
|
|
502
518
|
}
|
|
503
|
-
throw new GeneralError(
|
|
519
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "comparisonNotSupported", {
|
|
504
520
|
comparison: comparator.comparison
|
|
505
521
|
});
|
|
506
522
|
}
|
|
@@ -563,7 +579,9 @@ class CosmosDbEntityStorageConnector {
|
|
|
563
579
|
else if (operator === LogicalOperator.Or) {
|
|
564
580
|
return "OR";
|
|
565
581
|
}
|
|
566
|
-
throw new GeneralError(
|
|
582
|
+
throw new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, "conditionalNotSupported", {
|
|
583
|
+
operator
|
|
584
|
+
});
|
|
567
585
|
}
|
|
568
586
|
/**
|
|
569
587
|
* Verify the conditions for the entity.
|
|
@@ -588,6 +606,64 @@ class CosmosDbEntityStorageConnector {
|
|
|
588
606
|
ObjectHelper.propertyDelete(item, "_ts");
|
|
589
607
|
return item;
|
|
590
608
|
}
|
|
609
|
+
/**
|
|
610
|
+
* Check if the database exists.
|
|
611
|
+
* @returns True if the database exists, false otherwise.
|
|
612
|
+
* @internal
|
|
613
|
+
*/
|
|
614
|
+
async databaseExists() {
|
|
615
|
+
try {
|
|
616
|
+
const { resources: databaseList } = await this._client.databases.readAll().fetchAll();
|
|
617
|
+
return databaseList.some((db) => db.id === this._config.databaseId);
|
|
618
|
+
}
|
|
619
|
+
catch {
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Wait for a database to exist.
|
|
625
|
+
* @returns Nothing.
|
|
626
|
+
* @internal
|
|
627
|
+
*/
|
|
628
|
+
async waitForDatabaseExists() {
|
|
629
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
630
|
+
const databaseExists = await this.databaseExists();
|
|
631
|
+
if (databaseExists) {
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
await new Promise(resolve => setTimeout(resolve, 250));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Check if the container exists.
|
|
639
|
+
* @returns True if the container exists, false otherwise.
|
|
640
|
+
* @internal
|
|
641
|
+
*/
|
|
642
|
+
async containerExists() {
|
|
643
|
+
try {
|
|
644
|
+
const { resources: containers } = await this._client
|
|
645
|
+
.database(this._config.databaseId)
|
|
646
|
+
.containers.readAll()
|
|
647
|
+
.fetchAll();
|
|
648
|
+
return containers.some((c) => c.id === this._config.containerId);
|
|
649
|
+
}
|
|
650
|
+
catch {
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Wait for a container to exist.
|
|
656
|
+
* @returns Nothing.
|
|
657
|
+
* @internal
|
|
658
|
+
*/
|
|
659
|
+
async waitForContainerExists() {
|
|
660
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
661
|
+
const containerExists = await this.containerExists();
|
|
662
|
+
if (containerExists) {
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
await new Promise(resolve => setTimeout(resolve, 250));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
591
668
|
}
|
|
592
|
-
|
|
593
|
-
export { CosmosDbEntityStorageConnector };
|
|
669
|
+
//# sourceMappingURL=cosmosDbEntityStorageConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cosmosDbEntityStorageConnector.js","sourceRoot":"","sources":["../../src/cosmosDbEntityStorageConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\ttype Container,\n\tCosmosClient,\n\tCosmosDbDiagnosticLevel,\n\ttype FeedOptions,\n\ttype ItemDefinition,\n\tPartitionKeyKind,\n\ttype Resource,\n\ttype SqlParameter,\n\ttype SqlQuerySpec\n} from \"@azure/cosmos\";\nimport { ContextIdHelper, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tObjectHelper\n} from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaFactory,\n\tEntitySchemaHelper,\n\ttype EntitySchemaPropertyType,\n\ttype IComparator,\n\ttype IEntitySchema,\n\ttype IEntitySchemaProperty,\n\tLogicalOperator,\n\tSortDirection\n} from \"@twin.org/entity\";\nimport type { IEntityStorageConnector } from \"@twin.org/entity-storage-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { ICosmosDbEntityStorageConnectorConfig } from \"./models/ICosmosDbEntityStorageConnectorConfig.js\";\nimport type { ICosmosDbEntityStorageConnectorConstructorOptions } from \"./models/ICosmosDbEntityStorageConnectorConstructorOptions.js\";\n\n/**\n * Class for performing entity storage operations using Cosmos DB.\n */\nexport class CosmosDbEntityStorageConnector<T = unknown> implements IEntityStorageConnector<T> {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<CosmosDbEntityStorageConnector>();\n\n\t/**\n\t * Limit the number of entities when finding.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LIMIT: number = 40;\n\n\t/**\n\t * Partition id field name.\n\t * @internal\n\t */\n\tprivate static readonly _PARTITION_KEY: string = \"partitionId\";\n\n\t/**\n\t * Partition id field value.\n\t * @internal\n\t */\n\tprivate static readonly _PARTITION_KEY_VALUE: string = \"root\";\n\n\t/**\n\t * The schema for the entity.\n\t * @internal\n\t */\n\tprivate readonly _entitySchema: IEntitySchema<T>;\n\n\t/**\n\t * The keys to use from the context ids to create partitions.\n\t * @internal\n\t */\n\tprivate readonly _partitionContextIds?: string[];\n\n\t/**\n\t * The primary key.\n\t * @internal\n\t */\n\tprivate readonly _primaryKey: IEntitySchemaProperty<T>;\n\n\t/**\n\t * The configuration for the connector.\n\t * @internal\n\t */\n\tprivate readonly _config: ICosmosDbEntityStorageConnectorConfig;\n\n\t/**\n\t * The Cosmos DB client.\n\t * @internal\n\t */\n\tprivate readonly _client: CosmosClient;\n\n\t/**\n\t * Container user for the data storage.\n\t * @internal\n\t */\n\tprivate readonly _container: Container;\n\n\t/**\n\t * Create a new instance of CosmosDbEntityStorageConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: ICosmosDbEntityStorageConnectorConstructorOptions) {\n\t\tGuards.object(CosmosDbEntityStorageConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.entitySchema),\n\t\t\toptions.entitySchema\n\t\t);\n\t\tGuards.object<ICosmosDbEntityStorageConnectorConfig>(\n\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config),\n\t\t\toptions.config\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.endpoint),\n\t\t\toptions.config.endpoint\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.key),\n\t\t\toptions.config.key\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.databaseId),\n\t\t\toptions.config.databaseId\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\tnameof(options.config.containerId),\n\t\t\toptions.config.containerId\n\t\t);\n\n\t\tthis._entitySchema = EntitySchemaFactory.get(options.entitySchema);\n\t\tthis._partitionContextIds = options.partitionContextIds;\n\n\t\tthis._primaryKey = EntitySchemaHelper.getPrimaryKey<T>(this._entitySchema);\n\n\t\tthis._config = options.config;\n\n\t\tthis._client = new CosmosClient({\n\t\t\tendpoint: this._config.endpoint,\n\t\t\tkey: this._config.key,\n\t\t\tdiagnosticLevel: CosmosDbDiagnosticLevel.debug\n\t\t});\n\n\t\tthis._container = this._client\n\t\t\t.database(this._config.databaseId)\n\t\t\t.container(this._config.containerId);\n\t}\n\n\t/**\n\t * Initialize the Cosmos DB environment.\n\t * @param nodeLoggingComponentType Optional type of the logging component.\n\t * @returns A promise that resolves to a boolean indicating success.\n\t */\n\tpublic async bootstrap(nodeLoggingComponentType?: string): Promise<boolean> {\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\n\t\t// Create the database if it does not exist\n\t\ttry {\n\t\t\tconst databaseExists = await this.databaseExists();\n\n\t\t\tif (databaseExists) {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: CosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"databaseExists\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tdatabaseId: this._config.databaseId\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: CosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"databaseCreating\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tdatabaseId: this._config.databaseId\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tawait this._client.databases.create({\n\t\t\t\t\tid: this._config.databaseId\n\t\t\t\t});\n\n\t\t\t\tawait this.waitForDatabaseExists();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: CosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"databaseCreateFailed\",\n\t\t\t\terror: BaseError.fromError(error),\n\t\t\t\tdata: {\n\t\t\t\t\tdatabaseId: this._config.databaseId\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\t// Create the container if it does not exist\n\t\ttry {\n\t\t\tconst containerExists = await this.containerExists();\n\n\t\t\tif (containerExists) {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: CosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"containerExists\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tcontainerId: this._config.containerId\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\tsource: CosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"containerCreating\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tcontainerId: this._config.containerId\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tawait this._client.database(this._config.databaseId).containers.create(\n\t\t\t\t\t{\n\t\t\t\t\t\tid: this._config.containerId,\n\t\t\t\t\t\tpartitionKey: {\n\t\t\t\t\t\t\tkind: PartitionKeyKind.Hash,\n\t\t\t\t\t\t\tpaths: [`/${CosmosDbEntityStorageConnector._PARTITION_KEY}`]\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{ offerThroughput: this._config.offerThroughput }\n\t\t\t\t);\n\n\t\t\t\tawait this.waitForContainerExists();\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tawait nodeLogging?.log({\n\t\t\t\tlevel: \"error\",\n\t\t\t\tsource: CosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\tts: Date.now(),\n\t\t\t\tmessage: \"containerCreateFailed\",\n\t\t\t\terror: BaseError.fromError(error),\n\t\t\t\tdata: {\n\t\t\t\t\tcontainerId: this._config.containerId\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn CosmosDbEntityStorageConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tpublic getSchema(): IEntitySchema {\n\t\treturn this._entitySchema as IEntitySchema;\n\t}\n\n\t/**\n\t * Get an entity from Cosmos DB.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tpublic async get(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined> {\n\t\tGuards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\t// No secondary index or conditions\n\t\t\tif (Is.empty(secondaryIndex) && !Is.arrayValue(conditions)) {\n\t\t\t\tconst { resource: item } = await this._container\n\t\t\t\t\t.item(id, partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE)\n\t\t\t\t\t.read<ItemDefinition>();\n\t\t\t\treturn this.itemToEntity(item);\n\t\t\t}\n\n\t\t\tconst whereQuery: string[] = [\n\t\t\t\t`c.${CosmosDbEntityStorageConnector._PARTITION_KEY} = @partitionKey`\n\t\t\t];\n\n\t\t\t// With a secondary index\n\t\t\tif (Is.stringValue(secondaryIndex)) {\n\t\t\t\tconst secIndex = secondaryIndex.toString();\n\t\t\t\twhereQuery.push(`c.${secIndex} = @id`);\n\t\t\t} else {\n\t\t\t\twhereQuery.push(`c.${this._primaryKey.property as string} = @id`);\n\t\t\t}\n\n\t\t\t// With conditions\n\t\t\tif (Is.arrayValue(conditions)) {\n\t\t\t\tfor (const c of conditions) {\n\t\t\t\t\tconst schemaProp = this._entitySchema.properties?.find(p => p.property === c.property);\n\t\t\t\t\twhereQuery.push(\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n\t\t\t\t\t\t`c.${String(c.property)} = ${this.propertyToDbValue(c.value, schemaProp?.type)}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst query: SqlQuerySpec = {\n\t\t\t\tquery: `SELECT * FROM c WHERE ${whereQuery.join(\" AND \")}`,\n\t\t\t\tparameters: [\n\t\t\t\t\t{ name: \"@id\", value: id },\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"@partitionKey\",\n\t\t\t\t\t\tvalue: partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t};\n\n\t\t\tconst { resources: items } = await this._container.items.query(query).fetchAll();\n\n\t\t\tif (items.length === 1) {\n\t\t\t\treturn this.itemToEntity(items[0]);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (BaseError.isErrorCode(err, \"NotFound\")) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\"containerDoesNotExist\",\n\t\t\t\t\t{\n\t\t\t\t\t\tcontainerId: this._config.containerId\n\t\t\t\t\t},\n\t\t\t\t\terr\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new GeneralError(\n\t\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"getFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t */\n\tpublic async set(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void> {\n\t\tGuards.object<T>(CosmosDbEntityStorageConnector.CLASS_NAME, nameof(entity), entity);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\tEntitySchemaHelper.validateEntity(entity, this.getSchema());\n\n\t\tconst id = entity[this._primaryKey.property] as string;\n\n\t\ttry {\n\t\t\tif (Is.arrayValue(conditions)) {\n\t\t\t\tconst item = this._container.item(\n\t\t\t\t\tid,\n\t\t\t\t\tpartitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE\n\t\t\t\t);\n\t\t\t\tconst { resource: itemData } = await item.read<ItemDefinition>();\n\t\t\t\tif (Is.notEmpty(itemData) && !this.verifyConditions(conditions, itemData as T)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this._container.items.upsert({\n\t\t\t\tid,\n\t\t\t\t[CosmosDbEntityStorageConnector._PARTITION_KEY]:\n\t\t\t\t\tpartitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE,\n\t\t\t\t...entity\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tif (BaseError.isAggregateError(err)) {\n\t\t\t\tconst errors = BaseError.fromAggregate(err);\n\t\t\t\tif (BaseError.someErrorCode(errors, \"ResourceNotFoundException\")) {\n\t\t\t\t\tthrow new GeneralError(\n\t\t\t\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\t\t\"containerDoesNotExist\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcontainerId: this._config.containerId\n\t\t\t\t\t\t},\n\t\t\t\t\t\terr\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new GeneralError(\n\t\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"setFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t */\n\tpublic async remove(\n\t\tid: string,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<void> {\n\t\tGuards.stringValue(CosmosDbEntityStorageConnector.CLASS_NAME, nameof(id), id);\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst item = this._container.item(\n\t\t\t\tid,\n\t\t\t\tpartitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE\n\t\t\t);\n\t\t\tconst { resource: itemData } = await item.read<ItemDefinition>();\n\t\t\tif (Is.notEmpty(itemData)) {\n\t\t\t\tif (Is.arrayValue(conditions) && !this.verifyConditions(conditions, itemData as T)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tawait item.delete();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (\n\t\t\t\tBaseError.fromError(err) &&\n\t\t\t\tIs.object<{ body?: { code?: string } }>(err) &&\n\t\t\t\terr.body?.code === \"NotFound\"\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new GeneralError(\n\t\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"removeFailed\",\n\t\t\t\t{\n\t\t\t\t\tid\n\t\t\t\t},\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Find all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tpublic async query(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: { property: keyof T; sortDirection: SortDirection }[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{ entities: Partial<T>[]; cursor?: string }> {\n\t\tlet sql = \"\";\n\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tconst returnSize = limit ?? CosmosDbEntityStorageConnector._DEFAULT_LIMIT;\n\n\t\t\tlet orderByClause: string = \"\";\n\t\t\tif (Array.isArray(sortProperties)) {\n\t\t\t\tif (sortProperties.length > 1) {\n\t\t\t\t\tthrow new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, \"sortSingle\");\n\t\t\t\t}\n\t\t\t\tfor (const sortProperty of sortProperties) {\n\t\t\t\t\tconst propertySchema = this._entitySchema.properties?.find(\n\t\t\t\t\t\te => e.property === sortProperty.property\n\t\t\t\t\t);\n\t\t\t\t\tif (\n\t\t\t\t\t\t!propertySchema ||\n\t\t\t\t\t\t(!propertySchema.isPrimary &&\n\t\t\t\t\t\t\t!propertySchema.isSecondary &&\n\t\t\t\t\t\t\t!propertySchema.sortDirection)\n\t\t\t\t\t) {\n\t\t\t\t\t\tthrow new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, \"sortNotIndexed\", {\n\t\t\t\t\t\t\tproperty: sortProperty.property\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tconst direction = sortProperty.sortDirection === SortDirection.Ascending ? \"asc\" : \"desc\";\n\t\t\t\t\torderByClause = `ORDER BY c.${String(sortProperty.property)} ${direction}`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst attributeNames: { [id: string]: string } = {};\n\t\t\tconst attributeValues: { [id: string]: unknown } = {};\n\t\t\tlet queryClause = this.buildQueryParameters(\"\", conditions, attributeNames, attributeValues);\n\n\t\t\tif (queryClause.length > 0) {\n\t\t\t\tqueryClause = ` AND ${queryClause}`;\n\t\t\t}\n\n\t\t\tsql = `SELECT ${properties ? properties.map(p => `c.${p as string}`).join(\", \") : \"*\"} FROM c WHERE c.partitionId = @partitionId ${queryClause} ${orderByClause}`;\n\t\t\tconst querySpecs: SqlQuerySpec = {\n\t\t\t\tquery: sql,\n\t\t\t\tparameters: [\n\t\t\t\t\t{\n\t\t\t\t\t\tname: \"@partitionId\",\n\t\t\t\t\t\tvalue: partitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE\n\t\t\t\t\t},\n\t\t\t\t\t...Object.keys(attributeValues).map(\n\t\t\t\t\t\tkey => ({ name: `@${key}`, value: attributeValues[key] }) as SqlParameter\n\t\t\t\t\t)\n\t\t\t\t]\n\t\t\t};\n\n\t\t\tconst feedOptions: FeedOptions = {\n\t\t\t\tmaxItemCount: returnSize,\n\t\t\t\tcontinuationToken: cursor\n\t\t\t};\n\n\t\t\tconst feedResponse = await this._container.items.query(querySpecs, feedOptions).fetchNext();\n\n\t\t\treturn {\n\t\t\t\tentities: feedResponse.resources.map(i => this.itemToEntity(i)),\n\t\t\t\tcursor: feedResponse.continuationToken\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tCosmosDbEntityStorageConnector.CLASS_NAME,\n\t\t\t\t\"queryFailed\",\n\t\t\t\t{ sql },\n\t\t\t\terr\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Delete the container.\n\t * @returns Nothing.\n\t */\n\tpublic async containerDelete(): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst partitionKey = ContextIdHelper.combinedContextKey(contextIds, this._partitionContextIds);\n\n\t\ttry {\n\t\t\tawait this._container.deleteAllItemsForPartitionKey(\n\t\t\t\tpartitionKey ?? CosmosDbEntityStorageConnector._PARTITION_KEY_VALUE\n\t\t\t);\n\t\t\tawait this._container.delete();\n\t\t} catch {\n\t\t\t// Ignore errors\n\t\t}\n\t}\n\n\t/**\n\t * Create an SQL condition clause.\n\t * @param objectPath The path for the nested object.\n\t * @param condition The conditions to create the query from.\n\t * @param attributeNames The attribute names to use in the query.\n\t * @param attributeValues The attribute values to use in the query.\n\t * @returns The condition clause.\n\t * @internal\n\t */\n\tprivate buildQueryParameters(\n\t\tobjectPath: string,\n\t\tcondition: EntityCondition<T> | undefined,\n\t\tattributeNames: { [id: string]: string },\n\t\tattributeValues: { [id: string]: unknown }\n\t): string {\n\t\t// If no conditions are defined then return empty string\n\t\tif (Is.undefined(condition)) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tif (\"conditions\" in condition) {\n\t\t\tif (condition.conditions.length === 0) {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\t// It's a group of comparisons, so check the individual items and combine with the logical operator\n\t\t\tconst joinConditions: string[] = condition.conditions.map(c =>\n\t\t\t\tthis.buildQueryParameters(objectPath, c, attributeNames, attributeValues)\n\t\t\t);\n\n\t\t\tconst logicalOperator = this.mapConditionalOperator(condition.logicalOperator);\n\t\t\tconst queryClause = joinConditions\n\t\t\t\t.filter(j => j.length > 0)\n\t\t\t\t.map(j => j)\n\t\t\t\t.join(` ${logicalOperator} `);\n\n\t\t\treturn Is.stringValue(queryClause) ? ` (${queryClause}) ` : \"\";\n\t\t}\n\n\t\tconst schemaProp = this._entitySchema.properties?.find(p => p.property === condition.property);\n\n\t\t// It's a single value so just create the property comparison for the condition\n\t\tconst comparison = this.mapComparisonOperator(\n\t\t\tobjectPath,\n\t\t\tcondition,\n\t\t\tschemaProp?.type,\n\t\t\tattributeNames,\n\t\t\tattributeValues\n\t\t);\n\n\t\treturn comparison;\n\t}\n\n\t/**\n\t * Map the framework comparison operators to those in CosmosDB.\n\t * @param objectPath The prefix to use for the condition.\n\t * @param comparator The operator to map.\n\t * @param type The type of the property.\n\t * @param attributeValues The attribute values to use in the query.\n\t * @returns The comparison expression.\n\t * @throws GeneralError if the comparison operator is not supported.\n\t * @internal\n\t */\n\tprivate mapComparisonOperator(\n\t\tobjectPath: string,\n\t\tcomparator: IComparator,\n\t\ttype: EntitySchemaPropertyType | undefined,\n\t\tattributeNames: { [id: string]: string },\n\t\tattributeValues: { [id: string]: unknown }\n\t): string {\n\t\tlet prop = objectPath;\n\t\tif (prop.length > 0) {\n\t\t\tprop += \".\";\n\t\t}\n\t\tprop += comparator.property;\n\n\t\tlet attributeName = this.populateAttributeNames(prop, attributeNames);\n\t\tlet propName = `${attributeName.replace(/\\./g, \"\").replace(/@/g, \"\")}`;\n\n\t\tif (Is.array(comparator.value)) {\n\t\t\tconst dbValues = comparator.value.map(v => this.propertyToDbValue(v, type));\n\t\t\tconst arrAttributeNames = [];\n\t\t\tfor (let i = 0; i < dbValues.length; i++) {\n\t\t\t\tconst arrAttributeName = `${propName}${i}`;\n\t\t\t\tattributeValues[arrAttributeName] = dbValues[i];\n\t\t\t\tarrAttributeNames.push(arrAttributeName);\n\t\t\t}\n\t\t\tpropName = attributeName;\n\t\t\tattributeName = `(${arrAttributeNames.map(name => `@${name}`).join(\", \")})`;\n\t\t} else {\n\t\t\tattributeValues[propName] = comparator.value;\n\t\t}\n\n\t\tconst matches = attributeName.split(\".\").length;\n\t\tif (matches && comparator.comparison === ComparisonOperator.Equals) {\n\t\t\tattributeName = attributeName\n\t\t\t\t.split(\".\")\n\t\t\t\t.map(part => `[\"${part}\"]`)\n\t\t\t\t.join(\"\");\n\t\t\treturn `c${attributeName} = @${propName}`;\n\t\t} else if (comparator.comparison === ComparisonOperator.Equals) {\n\t\t\treturn `c.${attributeName} = @${propName}`;\n\t\t} else if (comparator.comparison === ComparisonOperator.NotEquals) {\n\t\t\treturn `c.${attributeName} <> @${propName}`;\n\t\t} else if (comparator.comparison === ComparisonOperator.GreaterThan) {\n\t\t\treturn `c.${attributeName} > @${propName}`;\n\t\t} else if (comparator.comparison === ComparisonOperator.LessThan) {\n\t\t\treturn `c.${attributeName} < @${propName}`;\n\t\t} else if (comparator.comparison === ComparisonOperator.GreaterThanOrEqual) {\n\t\t\treturn `c.${attributeName} >= @${propName}`;\n\t\t} else if (comparator.comparison === ComparisonOperator.LessThanOrEqual) {\n\t\t\treturn `c.${attributeName} <= @${propName}`;\n\t\t} else if (\n\t\t\ttypeof attributeValues[propName] === \"object\" &&\n\t\t\tcomparator.comparison === ComparisonOperator.Includes\n\t\t) {\n\t\t\treturn `array_contains(c.${attributeName}, @${propName})`;\n\t\t} else if (comparator.comparison === ComparisonOperator.Includes) {\n\t\t\treturn `contains(c.${attributeName}, @${propName})`;\n\t\t} else if (comparator.comparison === ComparisonOperator.NotIncludes) {\n\t\t\treturn `notContains(c.${attributeName}, @${propName})`;\n\t\t} else if (comparator.comparison === ComparisonOperator.In) {\n\t\t\treturn `c.${propName} IN ${attributeName}`;\n\t\t}\n\n\t\tthrow new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, \"comparisonNotSupported\", {\n\t\t\tcomparison: comparator.comparison\n\t\t});\n\t}\n\n\t/**\n\t * Format a value to insert into DB.\n\t * @param value The value to format.\n\t * @param type The type for the property.\n\t * @returns The value after conversion.\n\t * @internal\n\t */\n\tprivate propertyToDbValue(value: unknown, type?: EntitySchemaPropertyType): unknown {\n\t\tif (Is.object(value)) {\n\t\t\tconst map: { [id: string]: unknown } = {};\n\t\t\tfor (const key in value) {\n\t\t\t\tmap[key] = this.propertyToDbValue(value[key]);\n\t\t\t}\n\t\t\treturn map;\n\t\t}\n\n\t\tif (type === \"string\") {\n\t\t\treturn `${Coerce.string(value)}`;\n\t\t} else if (type === \"integer\" || type === \"number\") {\n\t\t\treturn Coerce.string(value) ?? \"\";\n\t\t} else if (type === \"boolean\") {\n\t\t\treturn Coerce.boolean(value) ?? false;\n\t\t}\n\n\t\treturn Coerce.string(value) ?? \"\";\n\t}\n\n\t/**\n\t * Create a unique name for the attribute.\n\t * @param name The name to create a unique name for.\n\t * @param attributeNames The attribute names to use in the query.\n\t * @returns The unique name.\n\t * @internal\n\t */\n\tprivate populateAttributeNames(name: string, attributeNames: { [id: string]: string }): string {\n\t\tconst parts = name.split(\".\");\n\t\tconst attributeNameParts: string[] = [];\n\n\t\tfor (const part of parts) {\n\t\t\tconst hashPart = `${part}`;\n\t\t\tif (Is.empty(attributeNames[hashPart])) {\n\t\t\t\tattributeNames[hashPart] = part;\n\t\t\t}\n\t\t\tattributeNameParts.push(hashPart);\n\t\t}\n\n\t\treturn attributeNameParts.join(\".\");\n\t}\n\n\t/**\n\t * Map the framework conditional operators to those in CosmosDB.\n\t * @param operator The operator to map.\n\t * @returns The conditional operator.\n\t * @throws GeneralError if the conditional operator is not supported.\n\t * @internal\n\t */\n\tprivate mapConditionalOperator(operator?: LogicalOperator): string {\n\t\tif ((operator ?? LogicalOperator.And) === LogicalOperator.And) {\n\t\t\treturn \"AND\";\n\t\t} else if (operator === LogicalOperator.Or) {\n\t\t\treturn \"OR\";\n\t\t}\n\n\t\tthrow new GeneralError(CosmosDbEntityStorageConnector.CLASS_NAME, \"conditionalNotSupported\", {\n\t\t\toperator\n\t\t});\n\t}\n\n\t/**\n\t * Verify the conditions for the entity.\n\t * @param conditions The conditions to verify.\n\t * @internal\n\t */\n\tprivate verifyConditions(\n\t\tconditions: { property: keyof T; value: unknown }[],\n\t\tobj: { [key in keyof T]: unknown }\n\t): boolean {\n\t\treturn conditions.every(\n\t\t\tcondition => ObjectHelper.propertyGet(obj, condition.property as string) === condition.value\n\t\t);\n\t}\n\n\t/**\n\t * Convert an entity to an item.\n\t * @param item The item to convert.\n\t * @returns The entity.\n\t * @internal\n\t */\n\tprivate itemToEntity(item: (ItemDefinition & Resource) | undefined): T {\n\t\tObjectHelper.propertyDelete(item, \"partitionId\");\n\t\tObjectHelper.propertyDelete(item, \"_attachments\");\n\t\tObjectHelper.propertyDelete(item, \"_etag\");\n\t\tObjectHelper.propertyDelete(item, \"_rid\");\n\t\tObjectHelper.propertyDelete(item, \"_self\");\n\t\tObjectHelper.propertyDelete(item, \"_ts\");\n\t\treturn item as T;\n\t}\n\n\t/**\n\t * Check if the database exists.\n\t * @returns True if the database exists, false otherwise.\n\t * @internal\n\t */\n\tprivate async databaseExists(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst { resources: databaseList } = await this._client.databases.readAll().fetchAll();\n\n\t\t\treturn databaseList.some((db: Resource) => db.id === this._config.databaseId);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Wait for a database to exist.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async waitForDatabaseExists(): Promise<void> {\n\t\tfor (let attempt = 0; attempt < 20; attempt++) {\n\t\t\tconst databaseExists = await this.databaseExists();\n\t\t\tif (databaseExists) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tawait new Promise(resolve => setTimeout(resolve, 250));\n\t\t}\n\t}\n\n\t/**\n\t * Check if the container exists.\n\t * @returns True if the container exists, false otherwise.\n\t * @internal\n\t */\n\tprivate async containerExists(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst { resources: containers } = await this._client\n\t\t\t\t.database(this._config.databaseId)\n\t\t\t\t.containers.readAll()\n\t\t\t\t.fetchAll();\n\n\t\t\treturn containers.some((c: Resource) => c.id === this._config.containerId);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Wait for a container to exist.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async waitForContainerExists(): Promise<void> {\n\t\tfor (let attempt = 0; attempt < 20; attempt++) {\n\t\t\tconst containerExists = await this.containerExists();\n\t\t\tif (containerExists) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tawait new Promise(resolve => setTimeout(resolve, 250));\n\t\t}\n\t}\n}\n"]}
|
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Copyright 2024 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
export * from "./cosmosDbEntityStorageConnector.js";
|
|
4
|
+
export * from "./models/ICosmosDbEntityStorageConnectorConfig.js";
|
|
5
|
+
export * from "./models/ICosmosDbEntityStorageConnectorConstructorOptions.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,qCAAqC,CAAC;AACpD,cAAc,mDAAmD,CAAC;AAClE,cAAc,+DAA+D,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./cosmosDbEntityStorageConnector.js\";\nexport * from \"./models/ICosmosDbEntityStorageConnectorConfig.js\";\nexport * from \"./models/ICosmosDbEntityStorageConnectorConstructorOptions.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ICosmosDbEntityStorageConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/ICosmosDbEntityStorageConnectorConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the Cosmos DB Entity Storage Connector.\n */\nexport interface ICosmosDbEntityStorageConnectorConfig {\n\t/**\n\t * The endpoint for the Cosmos DB instance.\n\t */\n\tendpoint: string;\n\n\t/**\n\t * The primary key for the Cosmos DB instance.\n\t */\n\tkey: string;\n\n\t/**\n\t * The ID of the database to be used.\n\t */\n\tdatabaseId: string;\n\n\t/**\n\t * The ID of the container for the storage.\n\t */\n\tcontainerId: string;\n\n\t/**\n\t * The offer throughput for the container.\n\t */\n\tofferThroughput?: number;\n}\n"]}
|