@travetto/model-dynamodb 8.0.2 → 8.0.4
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/package.json +5 -5
- package/src/service.ts +51 -6
- package/support/service.dynamodb.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-dynamodb",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.4",
|
|
4
4
|
"description": "DynamoDB backing for the travetto model module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dynamodb",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@aws-sdk/client-dynamodb": "^3.
|
|
33
|
-
"@travetto/config": "^8.0.
|
|
34
|
-
"@travetto/model": "^8.0.
|
|
35
|
-
"@travetto/model-indexed": "^8.0.
|
|
32
|
+
"@aws-sdk/client-dynamodb": "^3.1131.0",
|
|
33
|
+
"@travetto/config": "^8.0.3",
|
|
34
|
+
"@travetto/model": "^8.0.4",
|
|
35
|
+
"@travetto/model-indexed": "^8.0.4"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@travetto/cli": "^8.0.2"
|
package/src/service.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import timers from 'node:timers/promises';
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
type AttributeValue,
|
|
3
5
|
DynamoDB,
|
|
@@ -50,6 +52,10 @@ const getKey = <T extends ModelType>(computed: ModelIndexedComputedIndex<T>): At
|
|
|
50
52
|
DynamoDBUtil.toValue(computed.getKey() || 'NULL');
|
|
51
53
|
const getSort = <T extends ModelType>(computed: ModelIndexedComputedIndex<T>): AttributeValue => DynamoDBUtil.toValue(computed.getSort());
|
|
52
54
|
|
|
55
|
+
function isNotFoundError(error: unknown): boolean {
|
|
56
|
+
return error instanceof Error && error.name === 'ResourceNotFoundException';
|
|
57
|
+
}
|
|
58
|
+
|
|
53
59
|
/**
|
|
54
60
|
* A model service backed by DynamoDB
|
|
55
61
|
*/
|
|
@@ -289,6 +295,19 @@ export class DynamoDBModelService implements ModelCrudSupport, ModelExpirySuppor
|
|
|
289
295
|
}
|
|
290
296
|
|
|
291
297
|
// Storage
|
|
298
|
+
async #waitForTableNotExists(table: string): Promise<void> {
|
|
299
|
+
for (let attempt = 0; attempt < 300; attempt += 1) {
|
|
300
|
+
const describeResponse = await ModelStorageUtil.runAndIgnoreNotFound(
|
|
301
|
+
() => this.client.describeTable({ TableName: table }),
|
|
302
|
+
isNotFoundError
|
|
303
|
+
);
|
|
304
|
+
if (!describeResponse?.Table) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
await timers.setTimeout(100);
|
|
308
|
+
}
|
|
309
|
+
throw new Error(`Timed out waiting for table ${table} to be deleted`);
|
|
310
|
+
}
|
|
292
311
|
|
|
293
312
|
/**
|
|
294
313
|
* Add a new model
|
|
@@ -299,8 +318,8 @@ export class DynamoDBModelService implements ModelCrudSupport, ModelExpirySuppor
|
|
|
299
318
|
const idx = DynamoDBUtil.computeIndexConfig(cls);
|
|
300
319
|
|
|
301
320
|
const [currentTable, currentTTL] = await Promise.all([
|
|
302
|
-
this.client.describeTable({ TableName: table })
|
|
303
|
-
this.client.describeTimeToLive({ TableName: table })
|
|
321
|
+
ModelStorageUtil.runAndIgnoreNotFound(() => this.client.describeTable({ TableName: table }), isNotFoundError),
|
|
322
|
+
ModelStorageUtil.runAndIgnoreNotFound(() => this.client.describeTimeToLive({ TableName: table }), isNotFoundError)
|
|
304
323
|
]);
|
|
305
324
|
|
|
306
325
|
if (!currentTable) {
|
|
@@ -328,7 +347,7 @@ export class DynamoDBModelService implements ModelCrudSupport, ModelExpirySuppor
|
|
|
328
347
|
}
|
|
329
348
|
|
|
330
349
|
const ttlRequired = ModelRegistryIndex.getConfig(cls).expiresAt !== undefined;
|
|
331
|
-
const ttlEnabled = currentTTL
|
|
350
|
+
const ttlEnabled = currentTTL?.TimeToLiveDescription?.TimeToLiveStatus === 'ENABLED';
|
|
332
351
|
if (ttlEnabled !== ttlRequired) {
|
|
333
352
|
await this.client.updateTimeToLive({
|
|
334
353
|
TableName: table,
|
|
@@ -343,10 +362,36 @@ export class DynamoDBModelService implements ModelCrudSupport, ModelExpirySuppor
|
|
|
343
362
|
*/
|
|
344
363
|
async deleteModel(cls: Class<ModelType>): Promise<void> {
|
|
345
364
|
const table = this.#resolveTable(cls);
|
|
346
|
-
|
|
347
|
-
if (verify) {
|
|
365
|
+
await ModelStorageUtil.runAndIgnoreNotFound(async () => {
|
|
348
366
|
await this.client.deleteTable({ TableName: table });
|
|
349
|
-
|
|
367
|
+
await this.#waitForTableNotExists(table);
|
|
368
|
+
}, isNotFoundError);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async truncateModel<T extends ModelType>(cls: Class<T>): Promise<void> {
|
|
372
|
+
const table = this.#resolveTable(cls);
|
|
373
|
+
let startKey: Record<string, AttributeValue> | undefined;
|
|
374
|
+
do {
|
|
375
|
+
const scanResult = await this.client.scan({
|
|
376
|
+
TableName: table,
|
|
377
|
+
ProjectionExpression: 'id',
|
|
378
|
+
ExclusiveStartKey: startKey,
|
|
379
|
+
Limit: 25
|
|
380
|
+
});
|
|
381
|
+
startKey = scanResult.LastEvaluatedKey;
|
|
382
|
+
const items = scanResult.Items ?? [];
|
|
383
|
+
if (items.length > 0) {
|
|
384
|
+
await this.client.batchWriteItem({
|
|
385
|
+
RequestItems: {
|
|
386
|
+
[table]: items.map(item => ({
|
|
387
|
+
DeleteRequest: {
|
|
388
|
+
Key: { id: item.id }
|
|
389
|
+
}
|
|
390
|
+
}))
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
} while (startKey);
|
|
350
395
|
}
|
|
351
396
|
|
|
352
397
|
async createStorage(): Promise<void> {
|