betterddb 0.4.0 → 0.4.2
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/lib/betterddb.d.ts +8 -8
- package/lib/builders/create-builder.d.ts +3 -3
- package/lib/builders/create-builder.js +5 -4
- package/lib/builders/delete-builder.d.ts +3 -3
- package/lib/builders/delete-builder.js +4 -3
- package/lib/builders/get-builder.d.ts +3 -3
- package/lib/builders/get-builder.js +4 -3
- package/lib/builders/query-builder.js +2 -1
- package/lib/builders/scan-builder.js +2 -1
- package/lib/builders/update-builder.d.ts +3 -3
- package/lib/builders/update-builder.js +4 -3
- package/package.json +3 -2
- package/src/betterddb.ts +8 -8
- package/src/builders/create-builder.ts +16 -14
- package/src/builders/delete-builder.ts +10 -10
- package/src/builders/get-builder.ts +11 -11
- package/src/builders/query-builder.ts +4 -4
- package/src/builders/scan-builder.ts +4 -4
- package/src/builders/update-builder.ts +12 -11
- package/test/create.test.ts +33 -15
- package/test/delete.test.ts +11 -8
- package/test/get.test.ts +11 -8
- package/test/query.test.ts +10 -7
- package/test/scan.test.ts +12 -8
- package/test/update.test.ts +11 -8
- package/test/utils/table-setup.ts +7 -7
package/lib/betterddb.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DynamoDB } from 'aws-sdk';
|
|
1
|
+
import { z } from 'zod';
|
|
3
2
|
import { QueryBuilder } from './builders/query-builder';
|
|
4
3
|
import { ScanBuilder } from './builders/scan-builder';
|
|
5
4
|
import { UpdateBuilder } from './builders/update-builder';
|
|
6
5
|
import { CreateBuilder } from './builders/create-builder';
|
|
7
6
|
import { GetBuilder } from './builders/get-builder';
|
|
8
7
|
import { DeleteBuilder } from './builders/delete-builder';
|
|
8
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
9
9
|
export type PrimaryKeyValue = string | number;
|
|
10
10
|
/**
|
|
11
11
|
* A key definition can be either a simple key (a property name)
|
|
@@ -60,11 +60,11 @@ export interface KeysConfig<T> {
|
|
|
60
60
|
* Options for initializing BetterDDB.
|
|
61
61
|
*/
|
|
62
62
|
export interface BetterDDBOptions<T> {
|
|
63
|
-
schema:
|
|
63
|
+
schema: z.AnyZodObject;
|
|
64
64
|
tableName: string;
|
|
65
65
|
entityName: string;
|
|
66
66
|
keys: KeysConfig<T>;
|
|
67
|
-
client:
|
|
67
|
+
client: DynamoDBDocumentClient;
|
|
68
68
|
/**
|
|
69
69
|
* If true, automatically inject timestamp fields:
|
|
70
70
|
* - On create, sets both `createdAt` and `updatedAt`
|
|
@@ -78,17 +78,17 @@ export interface BetterDDBOptions<T> {
|
|
|
78
78
|
* BetterDDB is a definition-based DynamoDB wrapper library.
|
|
79
79
|
*/
|
|
80
80
|
export declare class BetterDDB<T> {
|
|
81
|
-
protected schema:
|
|
81
|
+
protected schema: z.AnyZodObject;
|
|
82
82
|
protected tableName: string;
|
|
83
83
|
protected entityName: string;
|
|
84
|
-
protected client:
|
|
84
|
+
protected client: DynamoDBDocumentClient;
|
|
85
85
|
protected keys: KeysConfig<T>;
|
|
86
86
|
protected autoTimestamps: boolean;
|
|
87
87
|
constructor(options: BetterDDBOptions<T>);
|
|
88
88
|
getKeys(): KeysConfig<T>;
|
|
89
89
|
getTableName(): string;
|
|
90
|
-
getClient():
|
|
91
|
-
getSchema():
|
|
90
|
+
getClient(): DynamoDBDocumentClient;
|
|
91
|
+
getSchema(): z.AnyZodObject;
|
|
92
92
|
getAutoTimestamps(): boolean;
|
|
93
93
|
protected getKeyValue(def: KeyDefinition<T>, rawKey: Partial<T>): string;
|
|
94
94
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TransactWriteItem } from '@aws-sdk/client-dynamodb';
|
|
2
2
|
import { BetterDDB } from '../betterddb';
|
|
3
3
|
export declare class CreateBuilder<T> {
|
|
4
4
|
private parent;
|
|
@@ -6,8 +6,8 @@ export declare class CreateBuilder<T> {
|
|
|
6
6
|
private extraTransactItems;
|
|
7
7
|
constructor(parent: BetterDDB<T>, item: T);
|
|
8
8
|
execute(): Promise<T>;
|
|
9
|
-
transactWrite(ops:
|
|
10
|
-
toTransactPut():
|
|
9
|
+
transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this;
|
|
10
|
+
toTransactPut(): TransactWriteItem;
|
|
11
11
|
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
12
12
|
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<T | TResult>;
|
|
13
13
|
finally(onfinally?: (() => void) | null): Promise<T>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CreateBuilder = void 0;
|
|
4
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
4
5
|
class CreateBuilder {
|
|
5
6
|
constructor(parent, item) {
|
|
6
7
|
this.parent = parent;
|
|
@@ -13,9 +14,9 @@ class CreateBuilder {
|
|
|
13
14
|
const myTransactItem = this.toTransactPut();
|
|
14
15
|
// Combine with extra transaction items.
|
|
15
16
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
16
|
-
await this.parent.getClient().
|
|
17
|
+
await this.parent.getClient().send(new lib_dynamodb_1.TransactWriteCommand({
|
|
17
18
|
TransactItems: allItems
|
|
18
|
-
})
|
|
19
|
+
}));
|
|
19
20
|
// After transaction, retrieve the updated item.
|
|
20
21
|
const result = await this.parent.get(this.item).execute();
|
|
21
22
|
if (result === null) {
|
|
@@ -38,10 +39,10 @@ class CreateBuilder {
|
|
|
38
39
|
// Compute and merge index attributes.
|
|
39
40
|
const indexAttributes = this.parent.buildIndexes(validated);
|
|
40
41
|
finalItem = { ...finalItem, ...indexAttributes };
|
|
41
|
-
await this.parent.getClient().
|
|
42
|
+
await this.parent.getClient().send(new lib_dynamodb_1.PutCommand({
|
|
42
43
|
TableName: this.parent.getTableName(),
|
|
43
44
|
Item: finalItem
|
|
44
|
-
})
|
|
45
|
+
}));
|
|
45
46
|
return validated;
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DynamoDB } from 'aws-sdk';
|
|
2
1
|
import { BetterDDB } from '../betterddb';
|
|
2
|
+
import { TransactWriteItem } from '@aws-sdk/client-dynamodb';
|
|
3
3
|
export declare class DeleteBuilder<T> {
|
|
4
4
|
private parent;
|
|
5
5
|
private key;
|
|
@@ -11,8 +11,8 @@ export declare class DeleteBuilder<T> {
|
|
|
11
11
|
*/
|
|
12
12
|
withCondition(expression: string, attributeValues: Record<string, any>): this;
|
|
13
13
|
execute(): Promise<void>;
|
|
14
|
-
transactWrite(ops:
|
|
15
|
-
toTransactDelete():
|
|
14
|
+
transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this;
|
|
15
|
+
toTransactDelete(): TransactWriteItem;
|
|
16
16
|
then<TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
17
17
|
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<void | TResult>;
|
|
18
18
|
finally(onfinally?: (() => void) | null): Promise<void>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DeleteBuilder = void 0;
|
|
4
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
4
5
|
class DeleteBuilder {
|
|
5
6
|
constructor(parent, key) {
|
|
6
7
|
this.parent = parent;
|
|
@@ -26,9 +27,9 @@ class DeleteBuilder {
|
|
|
26
27
|
const myTransactItem = this.toTransactDelete();
|
|
27
28
|
// Combine with extra transaction items.
|
|
28
29
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
29
|
-
await this.parent.getClient().
|
|
30
|
+
await this.parent.getClient().send(new lib_dynamodb_1.TransactWriteCommand({
|
|
30
31
|
TransactItems: allItems
|
|
31
|
-
})
|
|
32
|
+
}));
|
|
32
33
|
// After transaction, retrieve the updated item.
|
|
33
34
|
const result = await this.parent.get(this.key).execute();
|
|
34
35
|
if (result === null) {
|
|
@@ -44,7 +45,7 @@ class DeleteBuilder {
|
|
|
44
45
|
params.ConditionExpression = this.condition.expression;
|
|
45
46
|
params.ExpressionAttributeValues = this.condition.attributeValues;
|
|
46
47
|
}
|
|
47
|
-
await this.parent.getClient().
|
|
48
|
+
await this.parent.getClient().send(new lib_dynamodb_1.DeleteCommand(params));
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
transactWrite(ops) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DynamoDB } from 'aws-sdk';
|
|
2
1
|
import { BetterDDB } from '../betterddb';
|
|
2
|
+
import { TransactGetItem } from '@aws-sdk/client-dynamodb';
|
|
3
3
|
export declare class GetBuilder<T> {
|
|
4
4
|
private parent;
|
|
5
5
|
private key;
|
|
@@ -12,8 +12,8 @@ export declare class GetBuilder<T> {
|
|
|
12
12
|
*/
|
|
13
13
|
withProjection(attributes: (keyof T)[]): this;
|
|
14
14
|
execute(): Promise<T | null>;
|
|
15
|
-
transactGet(ops:
|
|
16
|
-
toTransactGet():
|
|
15
|
+
transactGet(ops: TransactGetItem[] | TransactGetItem): this;
|
|
16
|
+
toTransactGet(): TransactGetItem;
|
|
17
17
|
then<TResult1 = T | null, TResult2 = never>(onfulfilled?: ((value: T | null) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
18
18
|
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<T | null | TResult>;
|
|
19
19
|
finally(onfinally?: (() => void) | null): Promise<T | null>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GetBuilder = void 0;
|
|
4
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
4
5
|
class GetBuilder {
|
|
5
6
|
constructor(parent, key) {
|
|
6
7
|
this.parent = parent;
|
|
@@ -24,9 +25,9 @@ class GetBuilder {
|
|
|
24
25
|
const myTransactItem = this.toTransactGet();
|
|
25
26
|
// Combine with extra transaction items.
|
|
26
27
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
27
|
-
await this.parent.getClient().
|
|
28
|
+
await this.parent.getClient().send(new lib_dynamodb_1.TransactGetCommand({
|
|
28
29
|
TransactItems: allItems
|
|
29
|
-
})
|
|
30
|
+
}));
|
|
30
31
|
// After transaction, retrieve the updated item.
|
|
31
32
|
const result = await this.parent.get(this.key).execute();
|
|
32
33
|
return result;
|
|
@@ -40,7 +41,7 @@ class GetBuilder {
|
|
|
40
41
|
params.ProjectionExpression = this.projectionExpression;
|
|
41
42
|
params.ExpressionAttributeNames = this.expressionAttributeNames;
|
|
42
43
|
}
|
|
43
|
-
const result = await this.parent.getClient().
|
|
44
|
+
const result = await this.parent.getClient().send(new lib_dynamodb_1.GetCommand(params));
|
|
44
45
|
if (!result.Item)
|
|
45
46
|
return null;
|
|
46
47
|
return this.parent.getSchema().parse(result.Item);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.QueryBuilder = void 0;
|
|
4
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
4
5
|
class QueryBuilder {
|
|
5
6
|
constructor(parent, key) {
|
|
6
7
|
this.parent = parent;
|
|
@@ -89,7 +90,7 @@ class QueryBuilder {
|
|
|
89
90
|
if (this.filters.length > 0) {
|
|
90
91
|
params.FilterExpression = this.filters.join(' AND ');
|
|
91
92
|
}
|
|
92
|
-
const result = await this.parent.getClient().
|
|
93
|
+
const result = await this.parent.getClient().send(new lib_dynamodb_1.QueryCommand(params));
|
|
93
94
|
return this.parent.getSchema().array().parse(result.Items);
|
|
94
95
|
}
|
|
95
96
|
// Thenable implementation.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ScanBuilder = void 0;
|
|
4
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
4
5
|
class ScanBuilder {
|
|
5
6
|
constructor(parent) {
|
|
6
7
|
this.parent = parent;
|
|
@@ -59,7 +60,7 @@ class ScanBuilder {
|
|
|
59
60
|
if (this.filters.length > 0) {
|
|
60
61
|
params.FilterExpression = this.filters.join(' AND ');
|
|
61
62
|
}
|
|
62
|
-
const result = await this.parent.getClient().
|
|
63
|
+
const result = await this.parent.getClient().send(new lib_dynamodb_1.ScanCommand(params));
|
|
63
64
|
return this.parent.getSchema().array().parse(result.Items);
|
|
64
65
|
}
|
|
65
66
|
// Thenable implementation.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DynamoDB } from 'aws-sdk';
|
|
2
1
|
import { BetterDDB } from '../betterddb';
|
|
2
|
+
import { TransactWriteItem } from '@aws-sdk/client-dynamodb';
|
|
3
3
|
export declare class UpdateBuilder<T> {
|
|
4
4
|
private parent;
|
|
5
5
|
private key;
|
|
@@ -19,7 +19,7 @@ export declare class UpdateBuilder<T> {
|
|
|
19
19
|
/**
|
|
20
20
|
* Specifies additional transaction items to include when executing this update as a transaction.
|
|
21
21
|
*/
|
|
22
|
-
transactWrite(ops:
|
|
22
|
+
transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this;
|
|
23
23
|
/**
|
|
24
24
|
* Builds the update expression and associated maps.
|
|
25
25
|
*/
|
|
@@ -27,7 +27,7 @@ export declare class UpdateBuilder<T> {
|
|
|
27
27
|
/**
|
|
28
28
|
* Returns a transaction update item that can be included in a transactWrite call.
|
|
29
29
|
*/
|
|
30
|
-
toTransactUpdate():
|
|
30
|
+
toTransactUpdate(): TransactWriteItem;
|
|
31
31
|
/**
|
|
32
32
|
* Commits the update immediately by calling the parent's update method.
|
|
33
33
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UpdateBuilder = void 0;
|
|
4
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
4
5
|
class UpdateBuilder {
|
|
5
6
|
// Reference to the parent BetterDDB instance and key.
|
|
6
7
|
constructor(parent, key, expectedVersion) {
|
|
@@ -170,9 +171,9 @@ class UpdateBuilder {
|
|
|
170
171
|
const myTransactItem = this.toTransactUpdate();
|
|
171
172
|
// Combine with extra transaction items.
|
|
172
173
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
173
|
-
await this.parent.getClient().
|
|
174
|
+
await this.parent.getClient().send(new lib_dynamodb_1.TransactWriteCommand({
|
|
174
175
|
TransactItems: allItems
|
|
175
|
-
})
|
|
176
|
+
}));
|
|
176
177
|
// After transaction, retrieve the updated item.
|
|
177
178
|
const result = await this.parent.get(this.key).execute();
|
|
178
179
|
if (result === null) {
|
|
@@ -194,7 +195,7 @@ class UpdateBuilder {
|
|
|
194
195
|
if (this.condition && this.condition.expression) {
|
|
195
196
|
params.ConditionExpression = this.condition.expression;
|
|
196
197
|
}
|
|
197
|
-
const result = await this.parent.getClient().
|
|
198
|
+
const result = await this.parent.getClient().send(new lib_dynamodb_1.UpdateCommand(params));
|
|
198
199
|
if (!result.Attributes) {
|
|
199
200
|
throw new Error('No attributes returned after update');
|
|
200
201
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "betterddb",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "A definition-based DynamoDB wrapper library that provides a schema-driven and fully typesafe DAL.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"author": "R.R. Wang",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"aws-sdk": "^
|
|
31
|
+
"@aws-sdk/lib-dynamodb": "^3.744.0",
|
|
32
|
+
"@aws-sdk/client-dynamodb": "^3.744.0",
|
|
32
33
|
"zod": "^3.21.4"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
package/src/betterddb.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ZodSchema } from 'zod';
|
|
2
|
-
import { DynamoDB } from 'aws-sdk';
|
|
1
|
+
import { z, ZodSchema } from 'zod';
|
|
3
2
|
import { QueryBuilder } from './builders/query-builder';
|
|
4
3
|
import { ScanBuilder } from './builders/scan-builder';
|
|
5
4
|
import { UpdateBuilder } from './builders/update-builder';
|
|
6
5
|
import { CreateBuilder } from './builders/create-builder';
|
|
7
6
|
import { GetBuilder } from './builders/get-builder';
|
|
8
7
|
import { DeleteBuilder } from './builders/delete-builder';
|
|
8
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
9
9
|
|
|
10
10
|
export type PrimaryKeyValue = string | number;
|
|
11
11
|
|
|
@@ -69,11 +69,11 @@ export interface KeysConfig<T> {
|
|
|
69
69
|
* Options for initializing BetterDDB.
|
|
70
70
|
*/
|
|
71
71
|
export interface BetterDDBOptions<T> {
|
|
72
|
-
schema:
|
|
72
|
+
schema: z.AnyZodObject;
|
|
73
73
|
tableName: string;
|
|
74
74
|
entityName: string;
|
|
75
75
|
keys: KeysConfig<T>;
|
|
76
|
-
client:
|
|
76
|
+
client: DynamoDBDocumentClient;
|
|
77
77
|
/**
|
|
78
78
|
* If true, automatically inject timestamp fields:
|
|
79
79
|
* - On create, sets both `createdAt` and `updatedAt`
|
|
@@ -88,10 +88,10 @@ export interface BetterDDBOptions<T> {
|
|
|
88
88
|
* BetterDDB is a definition-based DynamoDB wrapper library.
|
|
89
89
|
*/
|
|
90
90
|
export class BetterDDB<T> {
|
|
91
|
-
protected schema:
|
|
91
|
+
protected schema: z.AnyZodObject;
|
|
92
92
|
protected tableName: string;
|
|
93
93
|
protected entityName: string;
|
|
94
|
-
protected client:
|
|
94
|
+
protected client: DynamoDBDocumentClient;
|
|
95
95
|
protected keys: KeysConfig<T>;
|
|
96
96
|
protected autoTimestamps: boolean;
|
|
97
97
|
|
|
@@ -112,12 +112,12 @@ export class BetterDDB<T> {
|
|
|
112
112
|
return this.tableName;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
public getClient():
|
|
115
|
+
public getClient(): DynamoDBDocumentClient {
|
|
116
116
|
return this.client;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
public getSchema():
|
|
120
|
+
public getSchema(): z.AnyZodObject {
|
|
121
121
|
return this.schema;
|
|
122
122
|
}
|
|
123
123
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { AttributeValue, Put, TransactWriteItem } from '@aws-sdk/client-dynamodb';
|
|
2
3
|
import { BetterDDB } from '../betterddb';
|
|
4
|
+
import { PutCommand, TransactWriteCommand } from '@aws-sdk/lib-dynamodb';
|
|
3
5
|
|
|
4
6
|
export class CreateBuilder<T> {
|
|
5
|
-
private extraTransactItems:
|
|
7
|
+
private extraTransactItems: TransactWriteItem[] = [];
|
|
6
8
|
|
|
7
9
|
constructor(private parent: BetterDDB<T>, private item: T) {}
|
|
8
10
|
|
|
@@ -12,9 +14,9 @@ export class CreateBuilder<T> {
|
|
|
12
14
|
const myTransactItem = this.toTransactPut();
|
|
13
15
|
// Combine with extra transaction items.
|
|
14
16
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
15
|
-
await this.parent.getClient().
|
|
17
|
+
await this.parent.getClient().send(new TransactWriteCommand({
|
|
16
18
|
TransactItems: allItems
|
|
17
|
-
})
|
|
19
|
+
}));
|
|
18
20
|
// After transaction, retrieve the updated item.
|
|
19
21
|
const result = await this.parent.get(this.item).execute();
|
|
20
22
|
if (result === null) {
|
|
@@ -32,23 +34,23 @@ export class CreateBuilder<T> {
|
|
|
32
34
|
let finalItem = { ...validated };
|
|
33
35
|
|
|
34
36
|
// Compute and merge primary key.
|
|
35
|
-
const computedKeys = this.parent.buildKey(validated);
|
|
37
|
+
const computedKeys = this.parent.buildKey(validated as Partial<T>);
|
|
36
38
|
finalItem = { ...finalItem, ...computedKeys };
|
|
37
39
|
|
|
38
40
|
// Compute and merge index attributes.
|
|
39
|
-
const indexAttributes = this.parent.buildIndexes(validated);
|
|
41
|
+
const indexAttributes = this.parent.buildIndexes(validated as Partial<T>);
|
|
40
42
|
finalItem = { ...finalItem, ...indexAttributes };
|
|
41
43
|
|
|
42
|
-
await this.parent.getClient().
|
|
44
|
+
await this.parent.getClient().send(new PutCommand({
|
|
43
45
|
TableName: this.parent.getTableName(),
|
|
44
|
-
Item: finalItem
|
|
45
|
-
})
|
|
46
|
+
Item: finalItem
|
|
47
|
+
}));
|
|
46
48
|
|
|
47
|
-
return validated;
|
|
49
|
+
return validated as T;
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
public transactWrite(ops:
|
|
53
|
+
public transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this {
|
|
52
54
|
if (Array.isArray(ops)) {
|
|
53
55
|
this.extraTransactItems.push(...ops);
|
|
54
56
|
} else {
|
|
@@ -57,10 +59,10 @@ export class CreateBuilder<T> {
|
|
|
57
59
|
return this;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
public toTransactPut():
|
|
61
|
-
const putItem:
|
|
62
|
+
public toTransactPut(): TransactWriteItem{
|
|
63
|
+
const putItem: Put = {
|
|
62
64
|
TableName: this.parent.getTableName(),
|
|
63
|
-
Item: this.item as
|
|
65
|
+
Item: this.item as Record<string, AttributeValue>,
|
|
64
66
|
};
|
|
65
67
|
return { Put: putItem };
|
|
66
68
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DynamoDB } from 'aws-sdk';
|
|
2
1
|
import { BetterDDB } from '../betterddb';
|
|
3
|
-
|
|
2
|
+
import { TransactWriteItem, DeleteItemInput } from '@aws-sdk/client-dynamodb';
|
|
3
|
+
import { TransactWriteCommand, DeleteCommand } from '@aws-sdk/lib-dynamodb';
|
|
4
4
|
export class DeleteBuilder<T> {
|
|
5
5
|
private condition?: { expression: string; attributeValues: Record<string, any> };
|
|
6
|
-
private extraTransactItems:
|
|
6
|
+
private extraTransactItems: TransactWriteItem[] = [];
|
|
7
7
|
constructor(private parent: BetterDDB<T>, private key: Partial<T>) {}
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -25,16 +25,16 @@ export class DeleteBuilder<T> {
|
|
|
25
25
|
const myTransactItem = this.toTransactDelete();
|
|
26
26
|
// Combine with extra transaction items.
|
|
27
27
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
28
|
-
await this.parent.getClient().
|
|
28
|
+
await this.parent.getClient().send(new TransactWriteCommand({
|
|
29
29
|
TransactItems: allItems
|
|
30
|
-
})
|
|
30
|
+
}));
|
|
31
31
|
// After transaction, retrieve the updated item.
|
|
32
32
|
const result = await this.parent.get(this.key).execute();
|
|
33
33
|
if (result === null) {
|
|
34
34
|
throw new Error('Item not found after transaction delete');
|
|
35
35
|
}
|
|
36
36
|
} else {
|
|
37
|
-
const params:
|
|
37
|
+
const params: DeleteItemInput = {
|
|
38
38
|
TableName: this.parent.getTableName(),
|
|
39
39
|
Key: this.parent.buildKey(this.key)
|
|
40
40
|
};
|
|
@@ -42,11 +42,11 @@ export class DeleteBuilder<T> {
|
|
|
42
42
|
params.ConditionExpression = this.condition.expression;
|
|
43
43
|
params.ExpressionAttributeValues = this.condition.attributeValues;
|
|
44
44
|
}
|
|
45
|
-
await this.parent.getClient().
|
|
45
|
+
await this.parent.getClient().send(new DeleteCommand(params));
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
public transactWrite(ops:
|
|
49
|
+
public transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this {
|
|
50
50
|
if (Array.isArray(ops)) {
|
|
51
51
|
this.extraTransactItems.push(...ops);
|
|
52
52
|
} else {
|
|
@@ -55,8 +55,8 @@ export class DeleteBuilder<T> {
|
|
|
55
55
|
return this;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
public toTransactDelete():
|
|
59
|
-
const deleteItem:
|
|
58
|
+
public toTransactDelete(): TransactWriteItem {
|
|
59
|
+
const deleteItem: DeleteItemInput = {
|
|
60
60
|
TableName: this.parent.getTableName(),
|
|
61
61
|
Key: this.parent.buildKey(this.key)
|
|
62
62
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { DynamoDB } from 'aws-sdk';
|
|
2
1
|
import { BetterDDB } from '../betterddb';
|
|
3
|
-
|
|
2
|
+
import { TransactGetCommand, GetCommand } from '@aws-sdk/lib-dynamodb';
|
|
3
|
+
import { GetItemInput, TransactGetItem } from '@aws-sdk/client-dynamodb';
|
|
4
4
|
export class GetBuilder<T> {
|
|
5
5
|
private projectionExpression?: string;
|
|
6
6
|
private expressionAttributeNames: Record<string, string> = {};
|
|
7
|
-
private extraTransactItems:
|
|
7
|
+
private extraTransactItems: TransactGetItem[] = [];
|
|
8
8
|
constructor(private parent: BetterDDB<T>, private key: Partial<T>) {}
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -24,14 +24,14 @@ export class GetBuilder<T> {
|
|
|
24
24
|
const myTransactItem = this.toTransactGet();
|
|
25
25
|
// Combine with extra transaction items.
|
|
26
26
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
27
|
-
await this.parent.getClient().
|
|
27
|
+
await this.parent.getClient().send(new TransactGetCommand({
|
|
28
28
|
TransactItems: allItems
|
|
29
|
-
})
|
|
29
|
+
}));
|
|
30
30
|
// After transaction, retrieve the updated item.
|
|
31
31
|
const result = await this.parent.get(this.key).execute();
|
|
32
32
|
return result;
|
|
33
33
|
} else {
|
|
34
|
-
const params:
|
|
34
|
+
const params: GetItemInput = {
|
|
35
35
|
TableName: this.parent.getTableName(),
|
|
36
36
|
Key: this.parent.buildKey(this.key)
|
|
37
37
|
};
|
|
@@ -39,13 +39,13 @@ export class GetBuilder<T> {
|
|
|
39
39
|
params.ProjectionExpression = this.projectionExpression;
|
|
40
40
|
params.ExpressionAttributeNames = this.expressionAttributeNames;
|
|
41
41
|
}
|
|
42
|
-
const result = await this.parent.getClient().
|
|
42
|
+
const result = await this.parent.getClient().send(new GetCommand(params));
|
|
43
43
|
if (!result.Item) return null;
|
|
44
|
-
return this.parent.getSchema().parse(result.Item);
|
|
44
|
+
return this.parent.getSchema().parse(result.Item) as T;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
public transactGet(ops:
|
|
48
|
+
public transactGet(ops: TransactGetItem[] | TransactGetItem): this {
|
|
49
49
|
if (Array.isArray(ops)) {
|
|
50
50
|
this.extraTransactItems.push(...ops);
|
|
51
51
|
} else {
|
|
@@ -54,8 +54,8 @@ export class GetBuilder<T> {
|
|
|
54
54
|
return this;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
public toTransactGet():
|
|
58
|
-
const getItem:
|
|
57
|
+
public toTransactGet(): TransactGetItem {
|
|
58
|
+
const getItem: GetItemInput = {
|
|
59
59
|
TableName: this.parent.getTableName(),
|
|
60
60
|
Key: this.parent.buildKey(this.key)
|
|
61
61
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { QueryCommand, QueryCommandInput } from '@aws-sdk/lib-dynamodb';
|
|
2
2
|
import { BetterDDB } from '../betterddb';
|
|
3
3
|
|
|
4
4
|
export class QueryBuilder<T> {
|
|
@@ -90,7 +90,7 @@ export class QueryBuilder<T> {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// If any filters were added, set them as FilterExpression.
|
|
93
|
-
const params:
|
|
93
|
+
const params: QueryCommandInput = {
|
|
94
94
|
TableName: this.parent.getTableName(),
|
|
95
95
|
KeyConditionExpression: keyConditionExpression,
|
|
96
96
|
ExpressionAttributeNames: this.expressionAttributeNames,
|
|
@@ -105,8 +105,8 @@ export class QueryBuilder<T> {
|
|
|
105
105
|
params.FilterExpression = this.filters.join(' AND ');
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const result = await this.parent.getClient().
|
|
109
|
-
return this.parent.getSchema().array().parse(result.Items);
|
|
108
|
+
const result = await this.parent.getClient().send(new QueryCommand(params));
|
|
109
|
+
return this.parent.getSchema().array().parse(result.Items) as T[];
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// Thenable implementation.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ScanCommand, ScanCommandInput } from '@aws-sdk/lib-dynamodb';
|
|
2
2
|
import { BetterDDB } from '../betterddb';
|
|
3
3
|
|
|
4
4
|
export class ScanBuilder<T> {
|
|
@@ -56,7 +56,7 @@ export class ScanBuilder<T> {
|
|
|
56
56
|
* Executes the scan and returns a Promise that resolves with an array of items.
|
|
57
57
|
*/
|
|
58
58
|
public async execute(): Promise<T[]> {
|
|
59
|
-
const params:
|
|
59
|
+
const params: ScanCommandInput = {
|
|
60
60
|
TableName: this.parent.getTableName(),
|
|
61
61
|
ExpressionAttributeNames: this.expressionAttributeNames,
|
|
62
62
|
ExpressionAttributeValues: this.expressionAttributeValues,
|
|
@@ -68,8 +68,8 @@ export class ScanBuilder<T> {
|
|
|
68
68
|
params.FilterExpression = this.filters.join(' AND ');
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const result = await this.parent.getClient().
|
|
72
|
-
return this.parent.getSchema().array().parse(result.Items);
|
|
71
|
+
const result = await this.parent.getClient().send(new ScanCommand(params));
|
|
72
|
+
return this.parent.getSchema().array().parse(result.Items) as T[];
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// Thenable implementation.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
import { DynamoDB } from 'aws-sdk';
|
|
3
|
-
import { BetterDDB } from '../betterddb';
|
|
4
2
|
|
|
3
|
+
import { TransactWriteCommand, UpdateCommand } from '@aws-sdk/lib-dynamodb';
|
|
4
|
+
import { BetterDDB } from '../betterddb';
|
|
5
|
+
import { TransactWriteItem, Update, UpdateItemInput } from '@aws-sdk/client-dynamodb';
|
|
5
6
|
interface UpdateActions<T> {
|
|
6
7
|
set?: Partial<T>;
|
|
7
8
|
remove?: (keyof T)[];
|
|
@@ -14,7 +15,7 @@ export class UpdateBuilder<T> {
|
|
|
14
15
|
private condition?: { expression: string; attributeValues: Record<string, any> };
|
|
15
16
|
private expectedVersion?: number;
|
|
16
17
|
// When using transaction mode, we store extra transaction items.
|
|
17
|
-
private extraTransactItems:
|
|
18
|
+
private extraTransactItems: TransactWriteItem[] = [];
|
|
18
19
|
|
|
19
20
|
// Reference to the parent BetterDDB instance and key.
|
|
20
21
|
constructor(private parent: BetterDDB<T>, private key: Partial<T>, expectedVersion?: number) {
|
|
@@ -59,7 +60,7 @@ export class UpdateBuilder<T> {
|
|
|
59
60
|
/**
|
|
60
61
|
* Specifies additional transaction items to include when executing this update as a transaction.
|
|
61
62
|
*/
|
|
62
|
-
public transactWrite(ops:
|
|
63
|
+
public transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this {
|
|
63
64
|
if (Array.isArray(ops)) {
|
|
64
65
|
this.extraTransactItems.push(...ops);
|
|
65
66
|
} else {
|
|
@@ -173,9 +174,9 @@ export class UpdateBuilder<T> {
|
|
|
173
174
|
/**
|
|
174
175
|
* Returns a transaction update item that can be included in a transactWrite call.
|
|
175
176
|
*/
|
|
176
|
-
public toTransactUpdate():
|
|
177
|
+
public toTransactUpdate(): TransactWriteItem {
|
|
177
178
|
const { updateExpression, attributeNames, attributeValues } = this.buildExpression();
|
|
178
|
-
const updateItem:
|
|
179
|
+
const updateItem: Update = {
|
|
179
180
|
TableName: this.parent.getTableName(),
|
|
180
181
|
Key: this.parent.buildKey(this.key),
|
|
181
182
|
UpdateExpression: updateExpression,
|
|
@@ -197,9 +198,9 @@ export class UpdateBuilder<T> {
|
|
|
197
198
|
const myTransactItem = this.toTransactUpdate();
|
|
198
199
|
// Combine with extra transaction items.
|
|
199
200
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
200
|
-
await this.parent.getClient().
|
|
201
|
+
await this.parent.getClient().send(new TransactWriteCommand({
|
|
201
202
|
TransactItems: allItems
|
|
202
|
-
})
|
|
203
|
+
}));
|
|
203
204
|
// After transaction, retrieve the updated item.
|
|
204
205
|
const result = await this.parent.get(this.key).execute();
|
|
205
206
|
if (result === null) {
|
|
@@ -209,7 +210,7 @@ export class UpdateBuilder<T> {
|
|
|
209
210
|
} else {
|
|
210
211
|
// Normal update flow.
|
|
211
212
|
const { updateExpression, attributeNames, attributeValues } = this.buildExpression();
|
|
212
|
-
const params:
|
|
213
|
+
const params: UpdateItemInput = {
|
|
213
214
|
TableName: this.parent.getTableName(),
|
|
214
215
|
Key: this.parent.buildKey(this.key),
|
|
215
216
|
UpdateExpression: updateExpression,
|
|
@@ -220,11 +221,11 @@ export class UpdateBuilder<T> {
|
|
|
220
221
|
if (this.condition && this.condition.expression) {
|
|
221
222
|
params.ConditionExpression = this.condition.expression;
|
|
222
223
|
}
|
|
223
|
-
const result = await this.parent.getClient().
|
|
224
|
+
const result = await this.parent.getClient().send(new UpdateCommand(params));
|
|
224
225
|
if (!result.Attributes) {
|
|
225
226
|
throw new Error('No attributes returned after update');
|
|
226
227
|
}
|
|
227
|
-
return this.parent.getSchema().parse(result.Attributes);
|
|
228
|
+
return this.parent.getSchema().parse(result.Attributes) as T;
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
231
|
}
|
package/test/create.test.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { BetterDDB } from '../src/betterddb';
|
|
3
|
-
import { DynamoDB } from 'aws-sdk';
|
|
4
3
|
import { createTestTable, deleteTestTable } from './utils/table-setup';
|
|
5
|
-
|
|
4
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
5
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
6
|
+
import { GetCommand } from '@aws-sdk/lib-dynamodb';
|
|
7
|
+
import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
|
|
6
8
|
const TEST_TABLE = "create-test-table";
|
|
7
9
|
const ENDPOINT = 'http://localhost:4566';
|
|
8
10
|
const REGION = 'us-east-1';
|
|
@@ -11,12 +13,12 @@ const PRIMARY_KEY = 'id';
|
|
|
11
13
|
const PRIMARY_KEY_TYPE = 'S';
|
|
12
14
|
const SORT_KEY = 'email';
|
|
13
15
|
const SORT_KEY_TYPE = 'S';
|
|
14
|
-
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }];
|
|
15
|
-
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }];
|
|
16
|
-
const client = new DynamoDB
|
|
16
|
+
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }] as KeySchemaElement[];
|
|
17
|
+
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }] as AttributeDefinition[];
|
|
18
|
+
const client = DynamoDBDocumentClient.from(new DynamoDB({
|
|
17
19
|
region: REGION,
|
|
18
20
|
endpoint: ENDPOINT,
|
|
19
|
-
});
|
|
21
|
+
}));
|
|
20
22
|
|
|
21
23
|
const UserSchema = z.object({
|
|
22
24
|
id: z.string(),
|
|
@@ -24,15 +26,18 @@ const UserSchema = z.object({
|
|
|
24
26
|
email: z.string().email(),
|
|
25
27
|
createdAt: z.string(),
|
|
26
28
|
updatedAt: z.string(),
|
|
27
|
-
})
|
|
29
|
+
});
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
type User = z.infer<typeof UserSchema>;
|
|
32
|
+
|
|
33
|
+
const userDdb = new BetterDDB<User>({
|
|
30
34
|
schema: UserSchema,
|
|
31
35
|
tableName: TEST_TABLE,
|
|
32
36
|
entityName: ENTITY_NAME,
|
|
33
37
|
keys: {
|
|
34
|
-
primary: { name:
|
|
35
|
-
sort: { name:
|
|
38
|
+
primary: { name: "pk", definition: { build: (raw) => `USER#${raw.id}` } },
|
|
39
|
+
sort: { name: "sk", definition: { build: (raw) => `EMAIL#${raw.email}` } },
|
|
40
|
+
gsis: { gsi1: { name: 'gsi1', primary: { name: "gsi1pk", definition: { build: (raw) => "NAME" } }, sort: { name: "gsi1sk", definition: { build: (raw) => `NAME#${raw.name}` } } } },
|
|
36
41
|
},
|
|
37
42
|
client,
|
|
38
43
|
autoTimestamps: true,
|
|
@@ -49,11 +54,24 @@ afterAll(async () => {
|
|
|
49
54
|
describe('BetterDDB - Create Operation', () => {
|
|
50
55
|
it('should insert an item using CreateBuilder', async () => {
|
|
51
56
|
const user = { id: 'user-123', name: 'John Doe', email: 'john@example.com' };
|
|
57
|
+
|
|
52
58
|
await userDdb.create(user as any).execute();
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
expect(
|
|
57
|
-
expect(
|
|
59
|
+
|
|
60
|
+
const result = await client.send(new GetCommand({ TableName: TEST_TABLE, Key: { id: 'user-123', email: 'john@example.com' } }));
|
|
61
|
+
|
|
62
|
+
expect(result).not.toBeNull();
|
|
63
|
+
expect(result.Item).not.toBeNull();
|
|
64
|
+
expect(result.Item?.pk).toBe('USER#user-123');
|
|
65
|
+
expect(result.Item?.sk).toBe('EMAIL#john@example.com');
|
|
66
|
+
expect(result.Item?.gsi1pk).toBe('NAME');
|
|
67
|
+
expect(result.Item?.gsi1sk).toBe('NAME#John Doe');
|
|
68
|
+
expect(result.Item?.id).toBe('user-123');
|
|
69
|
+
expect(result.Item?.createdAt).not.toBeNull();
|
|
70
|
+
expect(result.Item?.updatedAt).not.toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should fails to validate and not insert an item', async () => {
|
|
74
|
+
const user = { id: 'user-123', email: 'john@example.com' };
|
|
75
|
+
await expect(userDdb.create(user as any).execute()).rejects.toThrow();
|
|
58
76
|
});
|
|
59
77
|
});
|
package/test/delete.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// __tests__/delete.test.ts
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { BetterDDB } from '../src/betterddb';
|
|
4
|
-
import {
|
|
4
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
5
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
5
6
|
import { createTestTable, deleteTestTable } from './utils/table-setup';
|
|
6
|
-
|
|
7
|
+
import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
|
|
7
8
|
const TEST_TABLE = "delete-test-table";
|
|
8
9
|
const ENDPOINT = 'http://localhost:4566';
|
|
9
10
|
const REGION = 'us-east-1';
|
|
@@ -12,12 +13,12 @@ const PRIMARY_KEY = 'id';
|
|
|
12
13
|
const PRIMARY_KEY_TYPE = 'S';
|
|
13
14
|
const SORT_KEY = 'email';
|
|
14
15
|
const SORT_KEY_TYPE = 'S';
|
|
15
|
-
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }];
|
|
16
|
-
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }];
|
|
17
|
-
const client = new DynamoDB
|
|
16
|
+
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }] as KeySchemaElement[];
|
|
17
|
+
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }] as AttributeDefinition[];
|
|
18
|
+
const client = DynamoDBDocumentClient.from(new DynamoDB({
|
|
18
19
|
region: REGION,
|
|
19
20
|
endpoint: ENDPOINT,
|
|
20
|
-
});
|
|
21
|
+
}));
|
|
21
22
|
|
|
22
23
|
const UserSchema = z.object({
|
|
23
24
|
id: z.string(),
|
|
@@ -25,9 +26,11 @@ const UserSchema = z.object({
|
|
|
25
26
|
email: z.string().email(),
|
|
26
27
|
createdAt: z.string(),
|
|
27
28
|
updatedAt: z.string(),
|
|
28
|
-
})
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
type User = z.infer<typeof UserSchema>;
|
|
29
32
|
|
|
30
|
-
const userDdb = new BetterDDB({
|
|
33
|
+
const userDdb = new BetterDDB<User>({
|
|
31
34
|
schema: UserSchema,
|
|
32
35
|
tableName: TEST_TABLE,
|
|
33
36
|
entityName: ENTITY_NAME,
|
package/test/get.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// __tests__/get.test.ts
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { BetterDDB } from '../src/betterddb';
|
|
4
|
-
import { DynamoDB } from 'aws-sdk';
|
|
5
4
|
import { createTestTable, deleteTestTable } from './utils/table-setup';
|
|
6
|
-
|
|
5
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
6
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
7
|
+
import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
|
|
7
8
|
const TEST_TABLE = "get-test-table";
|
|
8
9
|
const ENDPOINT = 'http://localhost:4566';
|
|
9
10
|
const REGION = 'us-east-1';
|
|
@@ -12,12 +13,12 @@ const PRIMARY_KEY = 'id';
|
|
|
12
13
|
const PRIMARY_KEY_TYPE = 'S';
|
|
13
14
|
const SORT_KEY = 'email';
|
|
14
15
|
const SORT_KEY_TYPE = 'S';
|
|
15
|
-
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }];
|
|
16
|
-
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }];
|
|
17
|
-
const client = new DynamoDB
|
|
16
|
+
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }] as KeySchemaElement[];
|
|
17
|
+
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }] as AttributeDefinition[];
|
|
18
|
+
const client = DynamoDBDocumentClient.from(new DynamoDB({
|
|
18
19
|
region: REGION,
|
|
19
20
|
endpoint: ENDPOINT,
|
|
20
|
-
});
|
|
21
|
+
}));
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
const UserSchema = z.object({
|
|
@@ -26,9 +27,11 @@ const UserSchema = z.object({
|
|
|
26
27
|
email: z.string().email(),
|
|
27
28
|
createdAt: z.string(),
|
|
28
29
|
updatedAt: z.string(),
|
|
29
|
-
})
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
type User = z.infer<typeof UserSchema>;
|
|
30
33
|
|
|
31
|
-
const userDdb = new BetterDDB({
|
|
34
|
+
const userDdb = new BetterDDB<User>({
|
|
32
35
|
schema: UserSchema,
|
|
33
36
|
tableName: TEST_TABLE,
|
|
34
37
|
entityName: ENTITY_NAME,
|
package/test/query.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { BetterDDB } from '../src/betterddb';
|
|
3
|
-
import {
|
|
3
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
4
|
+
import { AttributeDefinition, DynamoDB, KeySchemaElement } from '@aws-sdk/client-dynamodb';
|
|
4
5
|
import { createTestTable, deleteTestTable } from './utils/table-setup';
|
|
5
6
|
|
|
6
7
|
const TEST_TABLE = "query-test-table";
|
|
@@ -11,12 +12,12 @@ const PRIMARY_KEY = 'id';
|
|
|
11
12
|
const PRIMARY_KEY_TYPE = 'S';
|
|
12
13
|
const SORT_KEY = 'email';
|
|
13
14
|
const SORT_KEY_TYPE = 'S';
|
|
14
|
-
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }];
|
|
15
|
-
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }];
|
|
16
|
-
const client = new DynamoDB
|
|
15
|
+
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }] as KeySchemaElement[];
|
|
16
|
+
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }] as AttributeDefinition[];
|
|
17
|
+
const client = DynamoDBDocumentClient.from(new DynamoDB({
|
|
17
18
|
region: REGION,
|
|
18
19
|
endpoint: ENDPOINT,
|
|
19
|
-
});
|
|
20
|
+
}));
|
|
20
21
|
|
|
21
22
|
const UserSchema = z.object({
|
|
22
23
|
id: z.string(),
|
|
@@ -24,9 +25,11 @@ const UserSchema = z.object({
|
|
|
24
25
|
email: z.string().email(),
|
|
25
26
|
createdAt: z.string(),
|
|
26
27
|
updatedAt: z.string(),
|
|
27
|
-
})
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
type User = z.infer<typeof UserSchema>;
|
|
28
31
|
|
|
29
|
-
const userDdb = new BetterDDB({
|
|
32
|
+
const userDdb = new BetterDDB<User>({
|
|
30
33
|
schema: UserSchema,
|
|
31
34
|
tableName: TEST_TABLE,
|
|
32
35
|
entityName: ENTITY_NAME,
|
package/test/scan.test.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { BetterDDB } from '../src/betterddb';
|
|
3
|
-
import {
|
|
3
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
4
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
4
5
|
import { createTestTable, deleteTestTable } from './utils/table-setup';
|
|
5
|
-
|
|
6
|
+
import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
|
|
6
7
|
const TEST_TABLE = "scan-test-table";
|
|
7
8
|
const ENDPOINT = 'http://localhost:4566';
|
|
8
9
|
const REGION = 'us-east-1';
|
|
@@ -11,12 +12,13 @@ const PRIMARY_KEY = 'id';
|
|
|
11
12
|
const PRIMARY_KEY_TYPE = 'S';
|
|
12
13
|
const SORT_KEY = 'email';
|
|
13
14
|
const SORT_KEY_TYPE = 'S';
|
|
14
|
-
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }];
|
|
15
|
-
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }];
|
|
16
|
-
const client = new DynamoDB
|
|
15
|
+
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }] as KeySchemaElement[];
|
|
16
|
+
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }] as AttributeDefinition[];
|
|
17
|
+
const client = DynamoDBDocumentClient.from(new DynamoDB({
|
|
17
18
|
region: REGION,
|
|
18
19
|
endpoint: ENDPOINT,
|
|
19
|
-
});
|
|
20
|
+
}));
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
const UserSchema = z.object({
|
|
22
24
|
id: z.string(),
|
|
@@ -24,9 +26,11 @@ const UserSchema = z.object({
|
|
|
24
26
|
email: z.string().email(),
|
|
25
27
|
createdAt: z.string(),
|
|
26
28
|
updatedAt: z.string(),
|
|
27
|
-
})
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
type User = z.infer<typeof UserSchema>;
|
|
28
32
|
|
|
29
|
-
const userDdb = new BetterDDB({
|
|
33
|
+
const userDdb = new BetterDDB<User>({
|
|
30
34
|
schema: UserSchema,
|
|
31
35
|
tableName: TEST_TABLE,
|
|
32
36
|
entityName: ENTITY_NAME,
|
package/test/update.test.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { BetterDDB } from '../src/betterddb';
|
|
3
|
-
import {
|
|
3
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
4
4
|
import { createTestTable, deleteTestTable } from './utils/table-setup';
|
|
5
|
-
|
|
5
|
+
import { DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
6
|
+
import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
|
|
6
7
|
const TEST_TABLE = "update-test-table";
|
|
7
8
|
const ENDPOINT = 'http://localhost:4566';
|
|
8
9
|
const REGION = 'us-east-1';
|
|
@@ -11,12 +12,12 @@ const PRIMARY_KEY = 'id';
|
|
|
11
12
|
const PRIMARY_KEY_TYPE = 'S';
|
|
12
13
|
const SORT_KEY = 'email';
|
|
13
14
|
const SORT_KEY_TYPE = 'S';
|
|
14
|
-
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }];
|
|
15
|
-
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }];
|
|
16
|
-
const client = new DynamoDB
|
|
15
|
+
const KEY_SCHEMA = [{ AttributeName: PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: SORT_KEY, KeyType: 'RANGE' }] as KeySchemaElement[];
|
|
16
|
+
const ATTRIBUTE_DEFINITIONS = [{ AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE }, { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE }] as AttributeDefinition[];
|
|
17
|
+
const client = DynamoDBDocumentClient.from(new DynamoDB({
|
|
17
18
|
region: REGION,
|
|
18
19
|
endpoint: ENDPOINT,
|
|
19
|
-
});
|
|
20
|
+
}));
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
const UserSchema = z.object({
|
|
@@ -26,9 +27,11 @@ const UserSchema = z.object({
|
|
|
26
27
|
createdAt: z.string(),
|
|
27
28
|
updatedAt: z.string(),
|
|
28
29
|
version: z.number().optional(),
|
|
29
|
-
})
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
type User = z.infer<typeof UserSchema>;
|
|
30
33
|
|
|
31
|
-
const userDdb = new BetterDDB({
|
|
34
|
+
const userDdb = new BetterDDB<User>({
|
|
32
35
|
schema: UserSchema,
|
|
33
36
|
tableName: TEST_TABLE,
|
|
34
37
|
entityName: ENTITY_NAME,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DynamoDB } from 'aws-sdk';
|
|
1
|
+
import { CreateTableCommandInput, DynamoDB } from '@aws-sdk/client-dynamodb';
|
|
2
2
|
|
|
3
|
-
export const createTestTable = async (tableName: string, keySchema:
|
|
3
|
+
export const createTestTable = async (tableName: string, keySchema: CreateTableCommandInput['KeySchema'], attributeDefinitions: CreateTableCommandInput['AttributeDefinitions']) => {
|
|
4
4
|
const dynamoDB = new DynamoDB({
|
|
5
5
|
region: 'us-east-1',
|
|
6
6
|
endpoint: 'http://localhost:4566',
|
|
@@ -14,7 +14,7 @@ export const createTestTable = async (tableName: string, keySchema: DynamoDB.Cre
|
|
|
14
14
|
KeySchema: keySchema,
|
|
15
15
|
AttributeDefinitions: attributeDefinitions,
|
|
16
16
|
BillingMode: 'PAY_PER_REQUEST',
|
|
17
|
-
})
|
|
17
|
+
});
|
|
18
18
|
} catch (error: any) {
|
|
19
19
|
if (error.code === 'ResourceInUseException') {
|
|
20
20
|
console.log('Table already exists, skipping creation.');
|
|
@@ -25,14 +25,14 @@ export const createTestTable = async (tableName: string, keySchema: DynamoDB.Cre
|
|
|
25
25
|
|
|
26
26
|
// Wait for the table to become active.
|
|
27
27
|
let attempts = 0;
|
|
28
|
-
while (attempts < 60) { // wait up to
|
|
29
|
-
const { Table } = await dynamoDB.describeTable({ TableName: tableName })
|
|
28
|
+
while (attempts < 60) { // wait up to 15 seconds
|
|
29
|
+
const { Table } = await dynamoDB.describeTable({ TableName: tableName });
|
|
30
30
|
if (Table?.TableStatus === 'ACTIVE') {
|
|
31
31
|
console.log('DynamoDB table is ready.');
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
console.log('Waiting for table to become ACTIVE...');
|
|
35
|
-
await new Promise((res) => setTimeout(res,
|
|
35
|
+
await new Promise((res) => setTimeout(res, 250));
|
|
36
36
|
attempts++;
|
|
37
37
|
}
|
|
38
38
|
throw new Error('Table did not become active in time.');
|
|
@@ -44,7 +44,7 @@ export const deleteTestTable = async (tableName: string) => {
|
|
|
44
44
|
endpoint: 'http://localhost:4566',
|
|
45
45
|
});
|
|
46
46
|
try {
|
|
47
|
-
await dynamoDB.deleteTable({ TableName: tableName })
|
|
47
|
+
await dynamoDB.deleteTable({ TableName: tableName });
|
|
48
48
|
} catch (error: any) {
|
|
49
49
|
if (error.code === 'ResourceNotFoundException') {
|
|
50
50
|
console.log('Table not found during deletion.');
|