@ftschopp/dynatable-core 1.4.1 → 1.4.3
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 +14 -0
- package/dist/builders/query/create-query-builder.d.ts.map +1 -1
- package/dist/builders/query/create-query-builder.js +37 -1
- package/dist/core/types.d.ts +5 -0
- package/dist/core/types.d.ts.map +1 -1
- package/package.json +11 -2
- package/eslint.config.mjs +0 -4
- package/jest.config.js +0 -11
- package/src/builders/README.md +0 -339
- package/src/builders/batch-get/README.md +0 -43
- package/src/builders/batch-get/create-batch-get-builder.test.ts +0 -193
- package/src/builders/batch-get/create-batch-get-builder.ts +0 -108
- package/src/builders/batch-get/index.ts +0 -2
- package/src/builders/batch-get/types.ts +0 -28
- package/src/builders/batch-write/README.md +0 -204
- package/src/builders/batch-write/create-batch-write-builder.test.ts +0 -173
- package/src/builders/batch-write/create-batch-write-builder.ts +0 -49
- package/src/builders/batch-write/index.ts +0 -2
- package/src/builders/batch-write/types.ts +0 -33
- package/src/builders/delete/create-delete-builder.test.ts +0 -294
- package/src/builders/delete/create-delete-builder.ts +0 -100
- package/src/builders/delete/index.ts +0 -2
- package/src/builders/delete/types.ts +0 -17
- package/src/builders/get/create-get-builder.test.ts +0 -272
- package/src/builders/get/create-get-builder.ts +0 -117
- package/src/builders/get/index.ts +0 -2
- package/src/builders/get/types.ts +0 -39
- package/src/builders/index.ts +0 -14
- package/src/builders/put/create-put-builder.test.ts +0 -213
- package/src/builders/put/create-put-builder.ts +0 -160
- package/src/builders/put/index.ts +0 -2
- package/src/builders/put/types.ts +0 -24
- package/src/builders/query/create-query-builder.test.ts +0 -708
- package/src/builders/query/create-query-builder.ts +0 -430
- package/src/builders/query/index.ts +0 -2
- package/src/builders/query/types.ts +0 -109
- package/src/builders/scan/create-scan-builder.test.ts +0 -416
- package/src/builders/scan/create-scan-builder.ts +0 -265
- package/src/builders/scan/index.ts +0 -2
- package/src/builders/scan/types.ts +0 -88
- package/src/builders/shared/conditions.ts +0 -58
- package/src/builders/shared/index.ts +0 -4
- package/src/builders/shared/operators.test.ts +0 -270
- package/src/builders/shared/operators.ts +0 -208
- package/src/builders/shared/projection.ts +0 -28
- package/src/builders/shared/types.ts +0 -101
- package/src/builders/transact-get/README.md +0 -167
- package/src/builders/transact-get/create-transact-get-builder.test.ts +0 -239
- package/src/builders/transact-get/create-transact-get-builder.ts +0 -67
- package/src/builders/transact-get/index.ts +0 -2
- package/src/builders/transact-get/types.ts +0 -31
- package/src/builders/transact-write/README.md +0 -202
- package/src/builders/transact-write/create-transact-write-builder.test.ts +0 -288
- package/src/builders/transact-write/create-transact-write-builder.ts +0 -124
- package/src/builders/transact-write/index.ts +0 -2
- package/src/builders/transact-write/types.ts +0 -71
- package/src/builders/update/create-update-builder.test.ts +0 -573
- package/src/builders/update/create-update-builder.ts +0 -375
- package/src/builders/update/index.ts +0 -2
- package/src/builders/update/types.ts +0 -63
- package/src/core/types.test.ts +0 -641
- package/src/core/types.ts +0 -366
- package/src/entity/create-entity-api.ts +0 -224
- package/src/entity/index.ts +0 -19
- package/src/entity/middleware/factories.ts +0 -15
- package/src/entity/middleware/types.ts +0 -7
- package/src/entity/middleware/with-middleware.ts +0 -37
- package/src/entity/types.ts +0 -79
- package/src/entity/validation/key-validation.ts +0 -34
- package/src/index.ts +0 -23
- package/src/table.ts +0 -116
- package/src/utils/dynamodb-logger.test.ts +0 -246
- package/src/utils/dynamodb-logger.ts +0 -175
- package/src/utils/model-utils.test.ts +0 -452
- package/src/utils/model-utils.ts +0 -188
- package/src/utils/zod-utils.test.ts +0 -363
- package/src/utils/zod-utils.ts +0 -56
- package/tests/integration/instagram-clone.integration.test.ts +0 -1019
- package/tests/integration/pagination-timestamps.integration.test.ts +0 -374
- package/tests/integration/transactions.integration.test.ts +0 -528
- package/tsconfig.json +0 -12
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
3
|
-
import {
|
|
4
|
-
BatchWriteCommand,
|
|
5
|
-
BatchWriteCommandInput,
|
|
6
|
-
BatchWriteCommandOutput,
|
|
7
|
-
} from '@aws-sdk/lib-dynamodb';
|
|
8
|
-
import { BatchWriteBuilder, WriteRequest } from './types';
|
|
9
|
-
import { DynamoDBLogger } from '../../utils/dynamodb-logger';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Creates a BatchWriteBuilder to put or delete multiple items.
|
|
13
|
-
*
|
|
14
|
-
* @param requestItems - An object where keys are table names and values are arrays of WriteRequest
|
|
15
|
-
* @param client - DynamoDB client instance
|
|
16
|
-
* @param logger - Optional logger instance
|
|
17
|
-
*/
|
|
18
|
-
export function createBatchWriteBuilder(
|
|
19
|
-
requestItems: Record<string, WriteRequest[]>,
|
|
20
|
-
client: DynamoDBClient,
|
|
21
|
-
logger?: DynamoDBLogger
|
|
22
|
-
): BatchWriteBuilder {
|
|
23
|
-
return {
|
|
24
|
-
/**
|
|
25
|
-
* Build the underlying DynamoDB input parameters.
|
|
26
|
-
*/
|
|
27
|
-
dbParams(): BatchWriteCommandInput {
|
|
28
|
-
return {
|
|
29
|
-
RequestItems: requestItems,
|
|
30
|
-
};
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Execute the BatchWriteItem command.
|
|
35
|
-
*/
|
|
36
|
-
async execute(): Promise<{
|
|
37
|
-
unprocessedItems?: Record<string, WriteRequest[]>;
|
|
38
|
-
}> {
|
|
39
|
-
const params = this.dbParams();
|
|
40
|
-
const result: BatchWriteCommandOutput = await client.send(new BatchWriteCommand(params));
|
|
41
|
-
logger?.log('BatchWriteCommand', params, result);
|
|
42
|
-
|
|
43
|
-
// Return unprocessed items if any
|
|
44
|
-
return {
|
|
45
|
-
unprocessedItems: result.UnprocessedItems as Record<string, WriteRequest[]> | undefined,
|
|
46
|
-
};
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { BatchWriteItemCommandInput } from '@aws-sdk/client-dynamodb';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A single write request that can be either a PutRequest or DeleteRequest
|
|
6
|
-
*/
|
|
7
|
-
export type WriteRequest = {
|
|
8
|
-
PutRequest?: {
|
|
9
|
-
Item: any;
|
|
10
|
-
};
|
|
11
|
-
DeleteRequest?: {
|
|
12
|
-
Key: any;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* BatchWriteBuilder allows you to put or delete multiple items from one or more tables
|
|
18
|
-
* in a single request.
|
|
19
|
-
*/
|
|
20
|
-
export type BatchWriteBuilder = {
|
|
21
|
-
/**
|
|
22
|
-
* Build the underlying DynamoDB BatchWriteItem input parameters.
|
|
23
|
-
*/
|
|
24
|
-
dbParams(): BatchWriteItemCommandInput;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Execute the BatchWriteItem command.
|
|
28
|
-
* Returns unprocessed items if any requests failed.
|
|
29
|
-
*/
|
|
30
|
-
execute(): Promise<{
|
|
31
|
-
unprocessedItems?: Record<string, WriteRequest[]>;
|
|
32
|
-
}>;
|
|
33
|
-
};
|
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
3
|
-
import { createDeleteBuilder } from './create-delete-builder';
|
|
4
|
-
|
|
5
|
-
describe('DeleteBuilder', () => {
|
|
6
|
-
const client = new DynamoDBClient({});
|
|
7
|
-
const tableName = 'TestTable';
|
|
8
|
-
|
|
9
|
-
interface TestModel {
|
|
10
|
-
pk: string;
|
|
11
|
-
sk: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
age?: number;
|
|
14
|
-
score?: number;
|
|
15
|
-
followerCount?: number;
|
|
16
|
-
status?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe('Basic delete operations', () => {
|
|
20
|
-
test('should build params with just key', () => {
|
|
21
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
22
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client).dbParams();
|
|
23
|
-
|
|
24
|
-
expect(params.TableName).toBe(tableName);
|
|
25
|
-
expect(params.Key).toEqual(key);
|
|
26
|
-
expect(params.ConditionExpression).toBeUndefined();
|
|
27
|
-
expect(params.ExpressionAttributeNames).toBeUndefined();
|
|
28
|
-
expect(params.ExpressionAttributeValues).toBeUndefined();
|
|
29
|
-
expect(params.ReturnValues).toBeUndefined();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('should build params with partial key', () => {
|
|
33
|
-
const key: Partial<TestModel> = { pk: 'USER#1' };
|
|
34
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client).dbParams();
|
|
35
|
-
|
|
36
|
-
expect(params.Key).toEqual({ pk: 'USER#1' });
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe('Condition expressions', () => {
|
|
41
|
-
test('should build params with single where condition', () => {
|
|
42
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
43
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
44
|
-
.where((attr, op) => op.eq(attr.status, 'inactive'))
|
|
45
|
-
.dbParams();
|
|
46
|
-
|
|
47
|
-
expect(params.TableName).toBe(tableName);
|
|
48
|
-
expect(params.Key).toEqual(key);
|
|
49
|
-
expect(params.ConditionExpression).toMatch(/#status = :status_\d+/);
|
|
50
|
-
expect(params.ExpressionAttributeNames).toEqual({
|
|
51
|
-
'#status': 'status',
|
|
52
|
-
});
|
|
53
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('inactive');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('should build params with AND condition', () => {
|
|
57
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
58
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
59
|
-
.where((attr, op) => op.and(op.eq(attr.status, 'inactive'), op.lt(attr.followerCount, 10)))
|
|
60
|
-
.dbParams();
|
|
61
|
-
|
|
62
|
-
expect(params.ConditionExpression).toMatch(
|
|
63
|
-
/\(#status = :status_\d+\) AND \(#followerCount < :followerCount_\d+\)/
|
|
64
|
-
);
|
|
65
|
-
expect(params.ExpressionAttributeNames).toEqual({
|
|
66
|
-
'#status': 'status',
|
|
67
|
-
'#followerCount': 'followerCount',
|
|
68
|
-
});
|
|
69
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('inactive');
|
|
70
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain(10);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('should build params with OR condition', () => {
|
|
74
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
75
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
76
|
-
.where((attr, op) => op.or(op.eq(attr.status, 'inactive'), op.eq(attr.status, 'deleted')))
|
|
77
|
-
.dbParams();
|
|
78
|
-
|
|
79
|
-
expect(params.ConditionExpression).toMatch(
|
|
80
|
-
/\(#status = :status_\d+\) OR \(#status = :status_\d+\)/
|
|
81
|
-
);
|
|
82
|
-
expect(params.ExpressionAttributeNames).toEqual({
|
|
83
|
-
'#status': 'status',
|
|
84
|
-
});
|
|
85
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('inactive');
|
|
86
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('deleted');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('should build params with multiple where conditions', () => {
|
|
90
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
91
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
92
|
-
.where((attr, op) => op.eq(attr.status, 'inactive'))
|
|
93
|
-
.where((attr, op) => op.lt(attr.followerCount, 10))
|
|
94
|
-
.dbParams();
|
|
95
|
-
|
|
96
|
-
expect(params.ConditionExpression).toMatch(
|
|
97
|
-
/\(#status = :status_\d+\) AND \(#followerCount < :followerCount_\d+\)/
|
|
98
|
-
);
|
|
99
|
-
expect(params.ExpressionAttributeNames).toEqual({
|
|
100
|
-
'#status': 'status',
|
|
101
|
-
'#followerCount': 'followerCount',
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('should build params with complex nested conditions', () => {
|
|
106
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
107
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
108
|
-
.where((attr, op) =>
|
|
109
|
-
op.and(
|
|
110
|
-
op.or(op.eq(attr.status, 'inactive'), op.eq(attr.status, 'banned')),
|
|
111
|
-
op.lt(attr.followerCount, 5)
|
|
112
|
-
)
|
|
113
|
-
)
|
|
114
|
-
.dbParams();
|
|
115
|
-
|
|
116
|
-
expect(params.ConditionExpression).toMatch(/\(/);
|
|
117
|
-
expect(params.ConditionExpression).toMatch(/AND/);
|
|
118
|
-
expect(params.ConditionExpression).toMatch(/OR/);
|
|
119
|
-
expect(params.ExpressionAttributeNames).toEqual({
|
|
120
|
-
'#status': 'status',
|
|
121
|
-
'#followerCount': 'followerCount',
|
|
122
|
-
});
|
|
123
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('inactive');
|
|
124
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('banned');
|
|
125
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain(5);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test('should support comparison operators', () => {
|
|
129
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
130
|
-
|
|
131
|
-
// Greater than
|
|
132
|
-
const gtParams = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
133
|
-
.where((attr, op) => op.gt(attr.age, 18))
|
|
134
|
-
.dbParams();
|
|
135
|
-
expect(gtParams.ConditionExpression).toMatch(/#age > :age_\d+/);
|
|
136
|
-
|
|
137
|
-
// Greater than or equal
|
|
138
|
-
const gteParams = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
139
|
-
.where((attr, op) => op.gte(attr.age, 18))
|
|
140
|
-
.dbParams();
|
|
141
|
-
expect(gteParams.ConditionExpression).toMatch(/#age >= :age_\d+/);
|
|
142
|
-
|
|
143
|
-
// Less than
|
|
144
|
-
const ltParams = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
145
|
-
.where((attr, op) => op.lt(attr.age, 65))
|
|
146
|
-
.dbParams();
|
|
147
|
-
expect(ltParams.ConditionExpression).toMatch(/#age < :age_\d+/);
|
|
148
|
-
|
|
149
|
-
// Less than or equal
|
|
150
|
-
const lteParams = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
151
|
-
.where((attr, op) => op.lte(attr.age, 65))
|
|
152
|
-
.dbParams();
|
|
153
|
-
expect(lteParams.ConditionExpression).toMatch(/#age <= :age_\d+/);
|
|
154
|
-
|
|
155
|
-
// Not equal
|
|
156
|
-
const neParams = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
157
|
-
.where((attr, op) => op.ne(attr.status, 'active'))
|
|
158
|
-
.dbParams();
|
|
159
|
-
expect(neParams.ConditionExpression).toMatch(/#status <> :status_\d+/);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test('should support BETWEEN operator', () => {
|
|
163
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
164
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
165
|
-
.where((attr, op) => op.between(attr.age, 18, 65))
|
|
166
|
-
.dbParams();
|
|
167
|
-
|
|
168
|
-
expect(params.ConditionExpression).toMatch(/#age BETWEEN :age_low_\d+ AND :age_high_\d+/);
|
|
169
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain(18);
|
|
170
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain(65);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test('should support beginsWith operator', () => {
|
|
174
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
175
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
176
|
-
.where((attr, op) => op.beginsWith(attr.name, 'John'))
|
|
177
|
-
.dbParams();
|
|
178
|
-
|
|
179
|
-
expect(params.ConditionExpression).toMatch(/begins_with\(#name, :name_\d+\)/);
|
|
180
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('John');
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe('ReturnValues', () => {
|
|
185
|
-
test('should default to NONE', () => {
|
|
186
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
187
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client).dbParams();
|
|
188
|
-
|
|
189
|
-
expect(params.ReturnValues).toBeUndefined();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test('should support ALL_OLD', () => {
|
|
193
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
194
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
195
|
-
.returning('ALL_OLD')
|
|
196
|
-
.dbParams();
|
|
197
|
-
|
|
198
|
-
expect(params.ReturnValues).toBe('ALL_OLD');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test('should support NONE explicitly', () => {
|
|
202
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
203
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
204
|
-
.returning('NONE')
|
|
205
|
-
.dbParams();
|
|
206
|
-
|
|
207
|
-
expect(params.ReturnValues).toBeUndefined();
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
describe('Immutability', () => {
|
|
212
|
-
test('should create new builder instance on each method call', () => {
|
|
213
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
214
|
-
const builder1 = createDeleteBuilder<TestModel>(tableName, key, client);
|
|
215
|
-
const builder2 = builder1.where((attr, op) => op.eq(attr.status, 'inactive'));
|
|
216
|
-
const builder3 = builder2.returning('ALL_OLD');
|
|
217
|
-
|
|
218
|
-
expect(builder1.dbParams().ConditionExpression).toBeUndefined();
|
|
219
|
-
expect(builder1.dbParams().ReturnValues).toBeUndefined();
|
|
220
|
-
|
|
221
|
-
expect(builder2.dbParams().ConditionExpression).toMatch(/#status = :status_\d+/);
|
|
222
|
-
expect(builder2.dbParams().ReturnValues).toBeUndefined();
|
|
223
|
-
|
|
224
|
-
expect(builder3.dbParams().ConditionExpression).toMatch(/#status = :status_\d+/);
|
|
225
|
-
expect(builder3.dbParams().ReturnValues).toBe('ALL_OLD');
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
describe('Complex scenarios', () => {
|
|
230
|
-
test('should handle delete with all features', () => {
|
|
231
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
232
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
233
|
-
.where((attr, op) =>
|
|
234
|
-
op.and(
|
|
235
|
-
op.eq(attr.status, 'inactive'),
|
|
236
|
-
op.or(op.lt(attr.followerCount, 5), op.gt(attr.age, 365))
|
|
237
|
-
)
|
|
238
|
-
)
|
|
239
|
-
.returning('ALL_OLD')
|
|
240
|
-
.dbParams();
|
|
241
|
-
|
|
242
|
-
expect(params.TableName).toBe(tableName);
|
|
243
|
-
expect(params.Key).toEqual(key);
|
|
244
|
-
expect(params.ConditionExpression).toBeTruthy();
|
|
245
|
-
expect(params.ConditionExpression).toMatch(/AND/);
|
|
246
|
-
expect(params.ConditionExpression).toMatch(/OR/);
|
|
247
|
-
expect(params.ReturnValues).toBe('ALL_OLD');
|
|
248
|
-
expect(params.ExpressionAttributeNames).toEqual({
|
|
249
|
-
'#status': 'status',
|
|
250
|
-
'#followerCount': 'followerCount',
|
|
251
|
-
'#age': 'age',
|
|
252
|
-
});
|
|
253
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain('inactive');
|
|
254
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain(5);
|
|
255
|
-
expect(Object.values(params.ExpressionAttributeValues || {})).toContain(365);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
test('should handle conditional delete based on multiple attributes', () => {
|
|
259
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
260
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client)
|
|
261
|
-
.where((attr, op) => op.eq(attr.status, 'inactive'))
|
|
262
|
-
.where((attr, op) => op.lt(attr.followerCount, 10))
|
|
263
|
-
.where((attr, op) => op.between(attr.age, 0, 30))
|
|
264
|
-
.returning('ALL_OLD')
|
|
265
|
-
.dbParams();
|
|
266
|
-
|
|
267
|
-
expect(params.ConditionExpression).toBeTruthy();
|
|
268
|
-
expect(params.ConditionExpression).toMatch(/AND/);
|
|
269
|
-
expect(params.ExpressionAttributeNames).toEqual({
|
|
270
|
-
'#status': 'status',
|
|
271
|
-
'#followerCount': 'followerCount',
|
|
272
|
-
'#age': 'age',
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
describe('Edge cases', () => {
|
|
278
|
-
test('should handle empty conditions gracefully', () => {
|
|
279
|
-
const key: Partial<TestModel> = { pk: 'USER#1', sk: 'USER#1' };
|
|
280
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client).dbParams();
|
|
281
|
-
|
|
282
|
-
expect(params.ConditionExpression).toBeUndefined();
|
|
283
|
-
expect(params.ExpressionAttributeNames).toBeUndefined();
|
|
284
|
-
expect(params.ExpressionAttributeValues).toBeUndefined();
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
test('should handle key with only pk', () => {
|
|
288
|
-
const key: Partial<TestModel> = { pk: 'USER#1' };
|
|
289
|
-
const params = createDeleteBuilder<TestModel>(tableName, key, client).dbParams();
|
|
290
|
-
|
|
291
|
-
expect(params.Key).toEqual({ pk: 'USER#1' });
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
3
|
-
import { DeleteCommand } from '@aws-sdk/lib-dynamodb';
|
|
4
|
-
import { buildExpression, AttrBuilder, Condition, createOpBuilder } from '../shared';
|
|
5
|
-
import { DeleteBuilder } from './types';
|
|
6
|
-
import { DynamoDBLogger } from '../../utils/dynamodb-logger';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Creates a DeleteBuilder for an item key and table.
|
|
10
|
-
*/
|
|
11
|
-
export function createDeleteBuilder<Model>(
|
|
12
|
-
tableName: string,
|
|
13
|
-
key: Partial<Model>,
|
|
14
|
-
client: DynamoDBClient,
|
|
15
|
-
prevConditions: Condition[] = [],
|
|
16
|
-
returnMode: 'NONE' | 'ALL_OLD' = 'NONE',
|
|
17
|
-
logger?: DynamoDBLogger
|
|
18
|
-
): DeleteBuilder<Model> {
|
|
19
|
-
const conditions = [...prevConditions];
|
|
20
|
-
|
|
21
|
-
const build = (): DeleteBuilder<Model> => ({
|
|
22
|
-
where(fn) {
|
|
23
|
-
const attrs = new Proxy({} as AttrBuilder<Model>, {
|
|
24
|
-
get(_, prop: string) {
|
|
25
|
-
return { name: prop };
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
const opBuilder = createOpBuilder();
|
|
29
|
-
const condition = fn(attrs, opBuilder);
|
|
30
|
-
return createDeleteBuilder(
|
|
31
|
-
tableName,
|
|
32
|
-
key,
|
|
33
|
-
client,
|
|
34
|
-
[...conditions, condition],
|
|
35
|
-
returnMode,
|
|
36
|
-
logger
|
|
37
|
-
);
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
returning(mode) {
|
|
41
|
-
return createDeleteBuilder(tableName, key, client, conditions, mode, logger);
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
dbParams() {
|
|
45
|
-
// Build ConditionExpression from condition tree
|
|
46
|
-
let conditionExpression = '';
|
|
47
|
-
let expressionAttributeNames = {};
|
|
48
|
-
let expressionAttributeValues = {};
|
|
49
|
-
|
|
50
|
-
if (conditions.length > 0) {
|
|
51
|
-
const combinedCondition =
|
|
52
|
-
conditions.length === 1 && conditions[0]
|
|
53
|
-
? conditions[0]
|
|
54
|
-
: {
|
|
55
|
-
expression: '',
|
|
56
|
-
operator: 'AND' as const,
|
|
57
|
-
children: conditions,
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const result = buildExpression(combinedCondition);
|
|
61
|
-
conditionExpression = result.expression;
|
|
62
|
-
expressionAttributeNames = result.names;
|
|
63
|
-
expressionAttributeValues = result.values;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const extra: any = returnMode !== 'NONE' ? { ReturnValues: returnMode } : {};
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
TableName: tableName,
|
|
70
|
-
Key: key,
|
|
71
|
-
...(conditionExpression && {
|
|
72
|
-
ConditionExpression: conditionExpression,
|
|
73
|
-
}),
|
|
74
|
-
...(Object.keys(expressionAttributeNames).length && {
|
|
75
|
-
ExpressionAttributeNames: expressionAttributeNames,
|
|
76
|
-
}),
|
|
77
|
-
...(Object.keys(expressionAttributeValues).length && {
|
|
78
|
-
ExpressionAttributeValues: expressionAttributeValues,
|
|
79
|
-
}),
|
|
80
|
-
...extra,
|
|
81
|
-
};
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
async execute() {
|
|
85
|
-
const params = build().dbParams();
|
|
86
|
-
const response = await client.send(new DeleteCommand(params));
|
|
87
|
-
logger?.log('DeleteCommand', params, response);
|
|
88
|
-
|
|
89
|
-
// Return the deleted item if returnMode is ALL_OLD
|
|
90
|
-
if (returnMode === 'ALL_OLD' && response.Attributes) {
|
|
91
|
-
return response.Attributes as Model;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// For NONE mode, return the key as a fallback
|
|
95
|
-
return key as Model;
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return build();
|
|
100
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { DeleteCommandInput } from '@aws-sdk/lib-dynamodb';
|
|
2
|
-
import { OperationBuilder } from '../shared';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Builder interface for DynamoDB DeleteItem operations
|
|
6
|
-
*/
|
|
7
|
-
export interface DeleteBuilder<Model> extends Omit<OperationBuilder<Model>, 'dbParams'> {
|
|
8
|
-
/**
|
|
9
|
-
* Configures what values should be returned after the delete operation
|
|
10
|
-
*/
|
|
11
|
-
returning(mode: 'NONE' | 'ALL_OLD'): DeleteBuilder<Model>;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Converts the builder state to DynamoDB DeleteItem parameters
|
|
15
|
-
*/
|
|
16
|
-
dbParams(): DeleteCommandInput;
|
|
17
|
-
}
|