betterddb 0.6.0 → 0.6.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/.eslintrc.cjs +41 -0
- package/lib/betterddb.js +29 -17
- package/lib/builders/batch-get-builder.js +6 -2
- package/lib/builders/create-builder.js +14 -6
- package/lib/builders/delete-builder.js +8 -5
- package/lib/builders/get-builder.js +11 -6
- package/lib/builders/query-builder.js +38 -31
- package/lib/builders/scan-builder.js +17 -11
- package/lib/builders/update-builder.js +27 -18
- package/lib/operator.js +10 -10
- package/lib/{betterddb.d.ts → types/betterddb.d.ts} +13 -9
- package/lib/types/betterddb.d.ts.map +1 -0
- package/lib/{builders → types/builders}/batch-get-builder.d.ts +2 -1
- package/lib/types/builders/batch-get-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/create-builder.d.ts +3 -2
- package/lib/types/builders/create-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/delete-builder.d.ts +3 -2
- package/lib/types/builders/delete-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/get-builder.d.ts +3 -2
- package/lib/types/builders/get-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/query-builder.d.ts +4 -3
- package/lib/types/builders/query-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/scan-builder.d.ts +4 -3
- package/lib/types/builders/scan-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/update-builder.d.ts +3 -2
- package/lib/types/builders/update-builder.d.ts.map +1 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/operator.d.ts +3 -0
- package/lib/types/operator.d.ts.map +1 -0
- package/lib/types/{paginated-result.d.ts → types/paginated-result.d.ts} +1 -0
- package/lib/types/types/paginated-result.d.ts.map +1 -0
- package/package.json +15 -5
- package/prettier.config.js +6 -0
- package/src/betterddb.ts +46 -37
- package/src/builders/batch-get-builder.ts +11 -6
- package/src/builders/create-builder.ts +42 -27
- package/src/builders/delete-builder.ts +28 -17
- package/src/builders/get-builder.ts +26 -19
- package/src/builders/query-builder.ts +64 -44
- package/src/builders/scan-builder.ts +18 -14
- package/src/builders/update-builder.ts +53 -27
- package/src/index.ts +1 -1
- package/src/operator.ts +21 -21
- package/src/types/paginated-result.ts +1 -1
- package/test/update.test.ts +6 -0
- package/tsconfig.json +25 -11
- package/lib/index.d.ts +0 -1
- package/lib/operator.d.ts +0 -2
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { TransactWriteCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb";
|
|
2
|
+
import { BetterDDB } from "../betterddb";
|
|
3
|
+
import {
|
|
4
|
+
TransactWriteItem,
|
|
5
|
+
Update,
|
|
6
|
+
UpdateItemInput,
|
|
7
|
+
} from "@aws-sdk/client-dynamodb";
|
|
8
|
+
import { z } from "zod";
|
|
6
9
|
interface UpdateActions<T> {
|
|
7
10
|
set?: Partial<T>;
|
|
8
11
|
remove?: (keyof T)[];
|
|
@@ -12,16 +15,26 @@ interface UpdateActions<T> {
|
|
|
12
15
|
|
|
13
16
|
export class UpdateBuilder<T> {
|
|
14
17
|
private actions: UpdateActions<T> = {};
|
|
15
|
-
private condition?: {
|
|
18
|
+
private condition?: {
|
|
19
|
+
expression: string;
|
|
20
|
+
attributeValues: Record<string, any>;
|
|
21
|
+
};
|
|
16
22
|
// When using transaction mode, we store extra transaction items.
|
|
17
23
|
private extraTransactItems: TransactWriteItem[] = [];
|
|
18
24
|
|
|
19
25
|
// Reference to the parent BetterDDB instance and key.
|
|
20
|
-
constructor(
|
|
26
|
+
constructor(
|
|
27
|
+
private parent: BetterDDB<T>,
|
|
28
|
+
private key: Partial<T>,
|
|
29
|
+
) {}
|
|
21
30
|
|
|
22
31
|
// Chainable methods:
|
|
23
32
|
public set(attrs: Partial<T>): this {
|
|
24
|
-
|
|
33
|
+
const partialSchema = (
|
|
34
|
+
this.parent.getSchema() as unknown as z.ZodObject<any>
|
|
35
|
+
).partial();
|
|
36
|
+
const validated = partialSchema.parse(attrs);
|
|
37
|
+
this.actions.set = { ...this.actions.set, ...validated };
|
|
25
38
|
return this;
|
|
26
39
|
}
|
|
27
40
|
|
|
@@ -31,7 +44,11 @@ export class UpdateBuilder<T> {
|
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
public add(attrs: Partial<Record<keyof T, number | Set<any>>>): this {
|
|
34
|
-
|
|
47
|
+
const partialSchema = (
|
|
48
|
+
this.parent.getSchema() as unknown as z.ZodObject<any>
|
|
49
|
+
).partial();
|
|
50
|
+
const validated = partialSchema.parse(attrs);
|
|
51
|
+
this.actions.add = { ...this.actions.add, ...validated };
|
|
35
52
|
return this;
|
|
36
53
|
}
|
|
37
54
|
|
|
@@ -43,7 +60,10 @@ export class UpdateBuilder<T> {
|
|
|
43
60
|
/**
|
|
44
61
|
* Adds a condition expression to the update.
|
|
45
62
|
*/
|
|
46
|
-
public setCondition(
|
|
63
|
+
public setCondition(
|
|
64
|
+
expression: string,
|
|
65
|
+
attributeValues: Record<string, any>,
|
|
66
|
+
): this {
|
|
47
67
|
if (this.condition) {
|
|
48
68
|
// Merge conditions with AND.
|
|
49
69
|
this.condition.expression += ` AND ${expression}`;
|
|
@@ -89,18 +109,18 @@ export class UpdateBuilder<T> {
|
|
|
89
109
|
setParts.push(`${nameKey} = ${valueKey}`);
|
|
90
110
|
}
|
|
91
111
|
if (setParts.length > 0) {
|
|
92
|
-
clauses.push(`SET ${setParts.join(
|
|
112
|
+
clauses.push(`SET ${setParts.join(", ")}`);
|
|
93
113
|
}
|
|
94
114
|
}
|
|
95
115
|
|
|
96
116
|
// Build REMOVE clause.
|
|
97
117
|
if (this.actions.remove && this.actions.remove.length > 0) {
|
|
98
|
-
const removeParts = this.actions.remove.map(attr => {
|
|
118
|
+
const removeParts = this.actions.remove.map((attr) => {
|
|
99
119
|
const nameKey = `#remove_${String(attr)}`;
|
|
100
120
|
ExpressionAttributeNames[nameKey] = String(attr);
|
|
101
121
|
return nameKey;
|
|
102
122
|
});
|
|
103
|
-
clauses.push(`REMOVE ${removeParts.join(
|
|
123
|
+
clauses.push(`REMOVE ${removeParts.join(", ")}`);
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
// Build ADD clause.
|
|
@@ -114,7 +134,7 @@ export class UpdateBuilder<T> {
|
|
|
114
134
|
addParts.push(`${nameKey} ${valueKey}`);
|
|
115
135
|
}
|
|
116
136
|
if (addParts.length > 0) {
|
|
117
|
-
clauses.push(`ADD ${addParts.join(
|
|
137
|
+
clauses.push(`ADD ${addParts.join(", ")}`);
|
|
118
138
|
}
|
|
119
139
|
}
|
|
120
140
|
|
|
@@ -129,7 +149,7 @@ export class UpdateBuilder<T> {
|
|
|
129
149
|
deleteParts.push(`${nameKey} ${valueKey}`);
|
|
130
150
|
}
|
|
131
151
|
if (deleteParts.length > 0) {
|
|
132
|
-
clauses.push(`DELETE ${deleteParts.join(
|
|
152
|
+
clauses.push(`DELETE ${deleteParts.join(", ")}`);
|
|
133
153
|
}
|
|
134
154
|
}
|
|
135
155
|
|
|
@@ -139,9 +159,9 @@ export class UpdateBuilder<T> {
|
|
|
139
159
|
}
|
|
140
160
|
|
|
141
161
|
return {
|
|
142
|
-
updateExpression: clauses.join(
|
|
162
|
+
updateExpression: clauses.join(" "),
|
|
143
163
|
attributeNames: ExpressionAttributeNames,
|
|
144
|
-
attributeValues: ExpressionAttributeValues
|
|
164
|
+
attributeValues: ExpressionAttributeValues,
|
|
145
165
|
};
|
|
146
166
|
}
|
|
147
167
|
|
|
@@ -149,13 +169,14 @@ export class UpdateBuilder<T> {
|
|
|
149
169
|
* Returns a transaction update item that can be included in a transactWrite call.
|
|
150
170
|
*/
|
|
151
171
|
public toTransactUpdate(): TransactWriteItem {
|
|
152
|
-
const { updateExpression, attributeNames, attributeValues } =
|
|
172
|
+
const { updateExpression, attributeNames, attributeValues } =
|
|
173
|
+
this.buildExpression();
|
|
153
174
|
const updateItem: Update = {
|
|
154
175
|
TableName: this.parent.getTableName(),
|
|
155
176
|
Key: this.parent.buildKey(this.key),
|
|
156
177
|
UpdateExpression: updateExpression,
|
|
157
178
|
ExpressionAttributeNames: attributeNames,
|
|
158
|
-
ExpressionAttributeValues: attributeValues
|
|
179
|
+
ExpressionAttributeValues: attributeValues,
|
|
159
180
|
};
|
|
160
181
|
if (this.condition && this.condition.expression) {
|
|
161
182
|
updateItem.ConditionExpression = this.condition.expression;
|
|
@@ -172,32 +193,37 @@ export class UpdateBuilder<T> {
|
|
|
172
193
|
const myTransactItem = this.toTransactUpdate();
|
|
173
194
|
// Combine with extra transaction items.
|
|
174
195
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
175
|
-
await this.parent.getClient().send(
|
|
176
|
-
|
|
177
|
-
|
|
196
|
+
await this.parent.getClient().send(
|
|
197
|
+
new TransactWriteCommand({
|
|
198
|
+
TransactItems: allItems,
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
178
201
|
// After transaction, retrieve the updated item.
|
|
179
202
|
const result = await this.parent.get(this.key).execute();
|
|
180
203
|
if (result === null) {
|
|
181
|
-
throw new Error(
|
|
204
|
+
throw new Error("Item not found after transaction update");
|
|
182
205
|
}
|
|
183
206
|
return result;
|
|
184
207
|
} else {
|
|
185
208
|
// Normal update flow.
|
|
186
|
-
const { updateExpression, attributeNames, attributeValues } =
|
|
209
|
+
const { updateExpression, attributeNames, attributeValues } =
|
|
210
|
+
this.buildExpression();
|
|
187
211
|
const params: UpdateItemInput = {
|
|
188
212
|
TableName: this.parent.getTableName(),
|
|
189
213
|
Key: this.parent.buildKey(this.key),
|
|
190
214
|
UpdateExpression: updateExpression,
|
|
191
215
|
ExpressionAttributeNames: attributeNames,
|
|
192
216
|
ExpressionAttributeValues: attributeValues,
|
|
193
|
-
ReturnValues:
|
|
217
|
+
ReturnValues: "ALL_NEW",
|
|
194
218
|
};
|
|
195
219
|
if (this.condition && this.condition.expression) {
|
|
196
220
|
params.ConditionExpression = this.condition.expression;
|
|
197
221
|
}
|
|
198
|
-
const result = await this.parent
|
|
222
|
+
const result = await this.parent
|
|
223
|
+
.getClient()
|
|
224
|
+
.send(new UpdateCommand(params));
|
|
199
225
|
if (!result.Attributes) {
|
|
200
|
-
throw new Error(
|
|
226
|
+
throw new Error("No attributes returned after update");
|
|
201
227
|
}
|
|
202
228
|
return this.parent.getSchema().parse(result.Attributes) as T;
|
|
203
229
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./betterddb";
|
package/src/operator.ts
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
export type Operator =
|
|
2
|
-
|
|
|
3
|
-
|
|
|
4
|
-
|
|
|
5
|
-
|
|
|
6
|
-
|
|
|
7
|
-
|
|
|
8
|
-
|
|
|
9
|
-
|
|
|
10
|
-
|
|
|
2
|
+
| "=="
|
|
3
|
+
| "!="
|
|
4
|
+
| "<"
|
|
5
|
+
| "<="
|
|
6
|
+
| ">"
|
|
7
|
+
| ">="
|
|
8
|
+
| "begins_with"
|
|
9
|
+
| "between"
|
|
10
|
+
| "contains";
|
|
11
11
|
|
|
12
12
|
export function getOperatorExpression(
|
|
13
13
|
operator: Operator,
|
|
14
14
|
nameKey: string,
|
|
15
15
|
valueKey: string,
|
|
16
|
-
secondValueKey?: string
|
|
16
|
+
secondValueKey?: string,
|
|
17
17
|
): string {
|
|
18
18
|
switch (operator) {
|
|
19
|
-
case
|
|
19
|
+
case "==":
|
|
20
20
|
return `${nameKey} = ${valueKey}`;
|
|
21
|
-
case
|
|
21
|
+
case "!=":
|
|
22
22
|
return `${nameKey} <> ${valueKey}`;
|
|
23
|
-
case
|
|
23
|
+
case "<":
|
|
24
24
|
return `${nameKey} < ${valueKey}`;
|
|
25
|
-
case
|
|
25
|
+
case "<=":
|
|
26
26
|
return `${nameKey} <= ${valueKey}`;
|
|
27
|
-
case
|
|
27
|
+
case ">":
|
|
28
28
|
return `${nameKey} > ${valueKey}`;
|
|
29
|
-
case
|
|
29
|
+
case ">=":
|
|
30
30
|
return `${nameKey} >= ${valueKey}`;
|
|
31
|
-
case
|
|
31
|
+
case "begins_with":
|
|
32
32
|
return `begins_with(${nameKey}, ${valueKey})`;
|
|
33
|
-
case
|
|
33
|
+
case "between":
|
|
34
34
|
if (!secondValueKey) {
|
|
35
35
|
throw new Error("The 'between' operator requires two value keys");
|
|
36
36
|
}
|
|
37
37
|
return `${nameKey} BETWEEN ${valueKey} AND ${secondValueKey}`;
|
|
38
|
-
case
|
|
38
|
+
case "contains":
|
|
39
39
|
return `contains(${nameKey}, ${valueKey})`;
|
|
40
40
|
default:
|
|
41
|
-
throw new Error(
|
|
41
|
+
throw new Error("Unsupported operator");
|
|
42
42
|
}
|
|
43
|
-
}
|
|
43
|
+
}
|
package/test/update.test.ts
CHANGED
|
@@ -75,4 +75,10 @@ describe('BetterDDB - Update Operation', () => {
|
|
|
75
75
|
expect(updatedUser.name).toBe('Jane Doe');
|
|
76
76
|
expect(updatedUser.email).toBe('john@example.com');
|
|
77
77
|
});
|
|
78
|
+
|
|
79
|
+
// it('should update an existing item using UpdateBuilder with null values throws', async () => {
|
|
80
|
+
// const updates = { name: 'Jane Doe', email: null };
|
|
81
|
+
// // @ts-ignore
|
|
82
|
+
// expect(await userDdb.update({ id: 'user-123', email: 'john@example.com' }).set(updates).execute()).rejects.toThrow();
|
|
83
|
+
// });
|
|
78
84
|
});
|
package/tsconfig.json
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ES2022", "DOM"],
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"outDir": "./lib/",
|
|
8
|
+
"declarationDir": "./lib/types",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"checkJs": true,
|
|
14
|
+
"allowJs": true,
|
|
15
|
+
"declaration": true,
|
|
16
|
+
"declarationMap": true,
|
|
17
|
+
"allowSyntheticDefaultImports": true
|
|
18
|
+
},
|
|
19
|
+
/* Path Aliases */
|
|
20
|
+
"baseUrl": ".",
|
|
21
|
+
"paths": {
|
|
22
|
+
"~/*": ["./src/*"]
|
|
23
|
+
},
|
|
24
|
+
"files": ["./src/index.ts"],
|
|
25
|
+
"exclude": ["node_modules"]
|
|
26
|
+
}
|
package/lib/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './betterddb';
|
package/lib/operator.d.ts
DELETED