@eresearchqut/ddb-repository 1.2.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.3.0](https://github.com/eresearchqut/ddb-repository/compare/v1.2.0...v1.3.0) (2025-11-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add test for the tracking of the consumed capacity ([4a1b23c](https://github.com/eresearchqut/ddb-repository/commit/4a1b23cadadbbb02d78507ca9d34715219fc952c))
7
+
1
8
  # [1.2.0](https://github.com/eresearchqut/ddb-repository/compare/v1.1.0...v1.2.0) (2025-11-19)
2
9
 
3
10
 
@@ -27,7 +27,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
27
27
  function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.DynamoDbRepository = exports.mapFilterExpressions = exports.mapFilterExpression = exports.FilterOperator = void 0;
30
+ exports.DynamoDbRepository = exports.consumedCapacityMiddleware = exports.mapFilterExpressions = exports.mapFilterExpression = exports.FilterOperator = void 0;
31
31
  const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
32
32
  const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
33
33
  const lodash_1 = require("lodash");
@@ -79,6 +79,21 @@ const paginate = (array, pageSize) => {
79
79
  return acc;
80
80
  }, []);
81
81
  };
82
+ const consumedCapacityMiddleware = (consumedCapacityMiddlewareConfig) => (next, context) => (args) => __awaiter(void 0, void 0, void 0, function* () {
83
+ try {
84
+ const { input } = args;
85
+ const { ReturnConsumedCapacity } = input;
86
+ const response = yield next(args);
87
+ const { output } = response;
88
+ const consumedCapacity = (0, lodash_1.get)(output, "ConsumedCapacity");
89
+ yield consumedCapacityMiddlewareConfig.onConsumedCapacity({ ReturnConsumedCapacity, ConsumedCapacity: consumedCapacity });
90
+ return response;
91
+ }
92
+ catch (error) {
93
+ throw error;
94
+ }
95
+ });
96
+ exports.consumedCapacityMiddleware = consumedCapacityMiddleware;
82
97
  class DynamoDbRepository {
83
98
  constructor(dynamoDBClient, tableName, hashKey, rangKey, returnConsumedCapacity = client_dynamodb_1.ReturnConsumedCapacity.TOTAL) {
84
99
  this.dynamoDBClient = dynamoDBClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eresearchqut/ddb-repository",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,7 @@
23
23
  "@semantic-release/git": "^10.0.1",
24
24
  "@semantic-release/github": "^12.0.2",
25
25
  "@semantic-release/npm": "^13.1.2",
26
+ "@smithy/types": "^4.9.0",
26
27
  "@testcontainers/localstack": "^10.15.0",
27
28
  "@types/jest": "^29.5.0",
28
29
  "@types/lodash": "^4.17.20",
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  BatchGetItemCommand,
3
3
  BatchGetItemCommandInput,
4
+ ConsumedCapacity,
4
5
  DeleteItemCommand,
5
6
  DynamoDBClient,
6
7
  GetItemCommand,
@@ -11,7 +12,14 @@ import {
11
12
  UpdateItemCommand,
12
13
  } from "@aws-sdk/client-dynamodb";
13
14
  import {marshall, unmarshall} from "@aws-sdk/util-dynamodb";
14
- import {replace, uniqWith, isEqual, pickBy} from "lodash";
15
+ import {replace, uniqWith, isEqual, pickBy, get} from "lodash";
16
+ import {
17
+ HandlerExecutionContext,
18
+ InitializeHandler,
19
+ InitializeHandlerArguments,
20
+ InitializeHandlerOutput,
21
+ MetadataBearer
22
+ } from "@smithy/types";
15
23
 
16
24
  const expressionAttributeKey = (key: string) => replace(key, /-/g, "_");
17
25
 
@@ -119,6 +127,35 @@ const paginate = <T>(array: Array<T>, pageSize: number) => {
119
127
  }, [] as Array<Array<T>>);
120
128
  }
121
129
 
130
+ export interface ConsumedCapacityDetail {
131
+ ReturnConsumedCapacity: ReturnConsumedCapacity | undefined
132
+ ConsumedCapacity: ConsumedCapacity | undefined
133
+ }
134
+
135
+ export interface ConsumedCapacityMiddlewareConfig {
136
+ onConsumedCapacity: (consumedCapacity: ConsumedCapacityDetail) => Promise<unknown>;
137
+ }
138
+
139
+ export const consumedCapacityMiddleware =
140
+ (consumedCapacityMiddlewareConfig: ConsumedCapacityMiddlewareConfig) =>
141
+ <Output extends MetadataBearer = MetadataBearer>(
142
+ next: InitializeHandler<any, Output>,
143
+ context: HandlerExecutionContext
144
+ ): InitializeHandler<any, Output> =>
145
+ async (args: InitializeHandlerArguments<any>): Promise<InitializeHandlerOutput<Output>> => {
146
+ try {
147
+ const {input} = args;
148
+ const {ReturnConsumedCapacity} = input;
149
+ const response = await next(args);
150
+ const {output} = response;
151
+ const consumedCapacity = get(output, "ConsumedCapacity") as ConsumedCapacity | undefined;
152
+ await consumedCapacityMiddlewareConfig.onConsumedCapacity({ReturnConsumedCapacity, ConsumedCapacity: consumedCapacity});
153
+ return response;
154
+ } catch (error) {
155
+ throw error;
156
+ }
157
+ };
158
+
122
159
  export class DynamoDbRepository<K, T> {
123
160
 
124
161
  constructor(
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { DynamoDBClient, CreateTableCommand, DescribeTableCommand } from "@aws-sdk/client-dynamodb";
3
3
  import { LocalstackContainer, StartedLocalStackContainer } from "@testcontainers/localstack";
4
- import {DynamoDbRepository, FilterOperator} from "../src";
4
+ import {ConsumedCapacityDetail, consumedCapacityMiddleware, DynamoDbRepository, FilterOperator} from "../src";
5
5
 
6
6
  describe('DynamoDbRepository Integration Tests', () => {
7
7
  let container: StartedLocalStackContainer;
@@ -12,6 +12,11 @@ describe('DynamoDbRepository Integration Tests', () => {
12
12
  const tableName = 'test-table';
13
13
  const compositeTableName = 'test-composite-table';
14
14
  const gsiTableName = 'test-gsi-table';
15
+ const consumedCapacityRegister = new Array<ConsumedCapacityDetail>() ;
16
+
17
+ const sumConsumedCapacity = () =>
18
+ consumedCapacityRegister.reduce((total, value) =>
19
+ total + (value.ConsumedCapacity?.CapacityUnits || 0), 0);
15
20
 
16
21
  beforeAll(async () => {
17
22
  // Start LocalStack container with DynamoDB
@@ -28,7 +33,10 @@ describe('DynamoDbRepository Integration Tests', () => {
28
33
  },
29
34
  });
30
35
 
31
- // Create the test table with simple key
36
+ dynamoDBClient.middlewareStack
37
+ .add(consumedCapacityMiddleware({onConsumedCapacity: async (consumedCapacity) => consumedCapacityRegister.push(consumedCapacity)}));
38
+
39
+ // Create the test table with a simple key
32
40
  await dynamoDBClient.send(
33
41
  new CreateTableCommand({
34
42
  TableName: tableName,
@@ -42,7 +50,7 @@ describe('DynamoDbRepository Integration Tests', () => {
42
50
  })
43
51
  );
44
52
 
45
- // Create the test table with composite key (partition key + sort key)
53
+ // Create the test table with a composite key (partition key + sort key)
46
54
  await dynamoDBClient.send(
47
55
  new CreateTableCommand({
48
56
  TableName: compositeTableName,
@@ -108,6 +116,10 @@ describe('DynamoDbRepository Integration Tests', () => {
108
116
  gsiRepository = new DynamoDbRepository(dynamoDBClient, gsiTableName, "userId", "itemId");
109
117
  });
110
118
 
119
+ afterEach(async () => {
120
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
121
+ });
122
+
111
123
  afterAll(async () => {
112
124
  // Clean up
113
125
  if (dynamoDBClient) {
@@ -122,6 +134,7 @@ describe('DynamoDbRepository Integration Tests', () => {
122
134
  it('should return undefined when item does not exist', async () => {
123
135
  const result = await repository.getItem({ id: 'non-existent' });
124
136
  expect(result).toBeUndefined();
137
+ expect(sumConsumedCapacity()).toEqual(0.5);
125
138
  });
126
139
 
127
140
  it('should return the item when it exists', async () => {
@@ -132,6 +145,7 @@ describe('DynamoDbRepository Integration Tests', () => {
132
145
  const result = await repository.getItem(key);
133
146
 
134
147
  expect(result).toEqual(record);
148
+ expect(sumConsumedCapacity()).toEqual(2);
135
149
  });
136
150
  });
137
151
 
@@ -143,6 +157,7 @@ describe('DynamoDbRepository Integration Tests', () => {
143
157
  const result = await repository.putItem(key, record);
144
158
 
145
159
  expect(result).toEqual(record);
160
+ expect(sumConsumedCapacity()).toEqual(1.5);
146
161
  });
147
162
  });
148
163
 
@@ -155,6 +170,7 @@ describe('DynamoDbRepository Integration Tests', () => {
155
170
  await repository.deleteItem(key);
156
171
  const afterDelete = await repository.getItem(key);
157
172
  expect(afterDelete).toBeUndefined();
173
+ expect(sumConsumedCapacity()).toEqual(2);
158
174
  });
159
175
  });
160
176