betterddb 0.2.0 → 0.4.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.
@@ -0,0 +1,58 @@
1
+ // __tests__/delete.test.ts
2
+ import { z } from 'zod';
3
+ import { BetterDDB } from '../src/betterddb';
4
+ import { DynamoDB } from 'aws-sdk';
5
+ import { createTestTable, deleteTestTable } from './utils/table-setup';
6
+
7
+ const TEST_TABLE = "delete-test-table";
8
+ const ENDPOINT = 'http://localhost:4566';
9
+ const REGION = 'us-east-1';
10
+ const ENTITY_NAME = 'USER';
11
+ const PRIMARY_KEY = 'id';
12
+ const PRIMARY_KEY_TYPE = 'S';
13
+ const SORT_KEY = 'email';
14
+ 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.DocumentClient({
18
+ region: REGION,
19
+ endpoint: ENDPOINT,
20
+ });
21
+
22
+ const UserSchema = z.object({
23
+ id: z.string(),
24
+ name: z.string(),
25
+ email: z.string().email(),
26
+ createdAt: z.string(),
27
+ updatedAt: z.string(),
28
+ }).passthrough();
29
+
30
+ const userDdb = new BetterDDB({
31
+ schema: UserSchema,
32
+ tableName: TEST_TABLE,
33
+ entityName: ENTITY_NAME,
34
+ keys: {
35
+ primary: { name: PRIMARY_KEY, definition: { build: (raw) => raw.id! } },
36
+ sort: { name: SORT_KEY, definition: { build: (raw) => raw.email! } },
37
+ },
38
+ client,
39
+ autoTimestamps: true,
40
+ });
41
+
42
+ beforeAll(async () => {
43
+ await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS);
44
+ await userDdb.create({ id: 'user-123', name: 'John Doe', email: 'john@example.com' } as any).execute();
45
+ });
46
+
47
+ afterAll(async () => {
48
+ await deleteTestTable(TEST_TABLE);
49
+ });
50
+
51
+ describe('BetterDDB - Delete Operation', () => {
52
+ it('should delete an item using DeleteBuilder', async () => {
53
+ await userDdb.delete({ id: 'user-123', email: 'john@example.com' })
54
+ .execute();
55
+ const deletedUser = await userDdb.get({ id: 'user-123', email: 'john@example.com' }).execute();
56
+ expect(deletedUser).toBeNull();
57
+ });
58
+ });
@@ -0,0 +1,58 @@
1
+ // __tests__/get.test.ts
2
+ import { z } from 'zod';
3
+ import { BetterDDB } from '../src/betterddb';
4
+ import { DynamoDB } from 'aws-sdk';
5
+ import { createTestTable, deleteTestTable } from './utils/table-setup';
6
+
7
+ const TEST_TABLE = "get-test-table";
8
+ const ENDPOINT = 'http://localhost:4566';
9
+ const REGION = 'us-east-1';
10
+ const ENTITY_NAME = 'USER';
11
+ const PRIMARY_KEY = 'id';
12
+ const PRIMARY_KEY_TYPE = 'S';
13
+ const SORT_KEY = 'email';
14
+ 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.DocumentClient({
18
+ region: REGION,
19
+ endpoint: ENDPOINT,
20
+ });
21
+
22
+
23
+ const UserSchema = z.object({
24
+ id: z.string(),
25
+ name: z.string(),
26
+ email: z.string().email(),
27
+ createdAt: z.string(),
28
+ updatedAt: z.string(),
29
+ }).passthrough();
30
+
31
+ const userDdb = new BetterDDB({
32
+ schema: UserSchema,
33
+ tableName: TEST_TABLE,
34
+ entityName: ENTITY_NAME,
35
+ keys: {
36
+ primary: { name: PRIMARY_KEY, definition: { build: (raw) => raw.id! } },
37
+ sort: { name: SORT_KEY, definition: { build: (raw) => raw.email! } },
38
+ },
39
+ client,
40
+ autoTimestamps: true,
41
+ });
42
+
43
+ beforeAll(async () => {
44
+ await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS);
45
+ await userDdb.create({ id: 'user-123', name: 'John Doe', email: 'john@example.com' } as any).execute();
46
+ });
47
+
48
+ afterAll(async () => {
49
+ await deleteTestTable(TEST_TABLE);
50
+ });
51
+
52
+ describe('BetterDDB - Get Operation', () => {
53
+ it('should retrieve an item using GetBuilder', async () => {
54
+ const user = await userDdb.get({ id: 'user-123', email: 'john@example.com' }).execute();
55
+ expect(user).not.toBeNull();
56
+ expect(user?.id).toBe('user-123');
57
+ });
58
+ });
@@ -0,0 +1,73 @@
1
+ import { z } from 'zod';
2
+ import { BetterDDB } from '../src/betterddb';
3
+ import { DynamoDB } from 'aws-sdk';
4
+ import { createTestTable, deleteTestTable } from './utils/table-setup';
5
+
6
+ const TEST_TABLE = "query-test-table";
7
+ const ENDPOINT = 'http://localhost:4566';
8
+ const REGION = 'us-east-1';
9
+ const ENTITY_NAME = 'USER';
10
+ const PRIMARY_KEY = 'id';
11
+ const PRIMARY_KEY_TYPE = 'S';
12
+ const SORT_KEY = 'email';
13
+ 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.DocumentClient({
17
+ region: REGION,
18
+ endpoint: ENDPOINT,
19
+ });
20
+
21
+ const UserSchema = z.object({
22
+ id: z.string(),
23
+ name: z.string(),
24
+ email: z.string().email(),
25
+ createdAt: z.string(),
26
+ updatedAt: z.string(),
27
+ }).passthrough();
28
+
29
+ const userDdb = new BetterDDB({
30
+ schema: UserSchema,
31
+ tableName: TEST_TABLE,
32
+ entityName: ENTITY_NAME,
33
+ keys: {
34
+ primary: { name: PRIMARY_KEY, definition: PRIMARY_KEY },
35
+ gsis: {
36
+ EmailIndex: {
37
+ name: 'EmailIndex',
38
+ primary: { name: 'email', definition: 'email' }
39
+ }
40
+ }
41
+ },
42
+ client,
43
+ autoTimestamps: true,
44
+ });
45
+
46
+ beforeAll(async () => {
47
+ await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS);
48
+ // Insert multiple items.
49
+ const items = [
50
+ { id: 'user-1', name: 'Alice', email: 'alice@example.com' },
51
+ { id: 'user-2', name: 'Alice B', email: 'alice@example.com' },
52
+ { id: 'user-3', name: 'Bob', email: 'bob@example.com' }
53
+ ];
54
+ for (const item of items) {
55
+ await userDdb.create(item as any).execute();
56
+ }
57
+ });
58
+
59
+ afterAll(async () => {
60
+ await deleteTestTable(TEST_TABLE);
61
+ });
62
+
63
+ describe('BetterDDB - Query Operation', () => {
64
+ it('should query items using QueryBuilder', async () => {
65
+ const results = await userDdb.query({ id: 'user-1' })
66
+ .where('name', 'begins_with', 'Alice')
67
+ .limitResults(5);
68
+ expect(results.length).toBeGreaterThanOrEqual(1);
69
+ results.forEach(result => {
70
+ expect(result.name).toMatch(/^Alice/);
71
+ });
72
+ });
73
+ });
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ import { BetterDDB } from '../src/betterddb';
3
+ import { DynamoDB } from 'aws-sdk';
4
+ import { createTestTable, deleteTestTable } from './utils/table-setup';
5
+
6
+ const TEST_TABLE = "scan-test-table";
7
+ const ENDPOINT = 'http://localhost:4566';
8
+ const REGION = 'us-east-1';
9
+ const ENTITY_NAME = 'USER';
10
+ const PRIMARY_KEY = 'id';
11
+ const PRIMARY_KEY_TYPE = 'S';
12
+ const SORT_KEY = 'email';
13
+ 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.DocumentClient({
17
+ region: REGION,
18
+ endpoint: ENDPOINT,
19
+ });
20
+
21
+ const UserSchema = z.object({
22
+ id: z.string(),
23
+ name: z.string(),
24
+ email: z.string().email(),
25
+ createdAt: z.string(),
26
+ updatedAt: z.string(),
27
+ }).passthrough();
28
+
29
+ const userDdb = new BetterDDB({
30
+ schema: UserSchema,
31
+ tableName: TEST_TABLE,
32
+ entityName: ENTITY_NAME,
33
+ keys: {
34
+ primary: { name: PRIMARY_KEY, definition: PRIMARY_KEY },
35
+ },
36
+ client,
37
+ autoTimestamps: true,
38
+ });
39
+
40
+ beforeAll(async () => {
41
+ await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS);
42
+ // Insert multiple items.
43
+ const items = [
44
+ { id: 'user-4', name: 'Charlie', email: 'charlie@example.com' },
45
+ { id: 'user-5', name: 'Dave', email: 'dave@example.com' }
46
+ ];
47
+ for (const item of items) {
48
+ await userDdb.create(item as any).execute();
49
+ }
50
+ });
51
+
52
+ afterAll(async () => {
53
+ await deleteTestTable(TEST_TABLE);
54
+ });
55
+
56
+ describe('BetterDDB - Scan Operation', () => {
57
+ it('should scan items using ScanBuilder', async () => {
58
+ const results = await userDdb.scan()
59
+ .where('email', 'begins_with', 'char')
60
+ .limitResults(10);
61
+ expect(results.length).toBeGreaterThanOrEqual(1);
62
+ results.forEach(result => {
63
+ expect(result.email).toMatch(/^char/i);
64
+ });
65
+ });
66
+ });
@@ -0,0 +1,58 @@
1
+ import { z } from 'zod';
2
+ import { BetterDDB } from '../src/betterddb';
3
+ import { DynamoDB } from 'aws-sdk';
4
+ import { createTestTable, deleteTestTable } from './utils/table-setup';
5
+
6
+ const TEST_TABLE = "update-test-table";
7
+ const ENDPOINT = 'http://localhost:4566';
8
+ const REGION = 'us-east-1';
9
+ const ENTITY_NAME = 'USER';
10
+ const PRIMARY_KEY = 'id';
11
+ const PRIMARY_KEY_TYPE = 'S';
12
+ const SORT_KEY = 'email';
13
+ 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.DocumentClient({
17
+ region: REGION,
18
+ endpoint: ENDPOINT,
19
+ });
20
+
21
+
22
+ const UserSchema = z.object({
23
+ id: z.string(),
24
+ name: z.string(),
25
+ email: z.string().email(),
26
+ createdAt: z.string(),
27
+ updatedAt: z.string(),
28
+ version: z.number().optional(),
29
+ }).passthrough();
30
+
31
+ const userDdb = new BetterDDB({
32
+ schema: UserSchema,
33
+ tableName: TEST_TABLE,
34
+ entityName: ENTITY_NAME,
35
+ keys: {
36
+ primary: { name: PRIMARY_KEY, definition: { build: (raw) => raw.id! } },
37
+ sort: { name: SORT_KEY, definition: { build: (raw) => raw.email! } },
38
+ },
39
+ client,
40
+ autoTimestamps: true,
41
+ });
42
+
43
+ beforeAll(async () => {
44
+ await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS);
45
+ await userDdb.create({ id: 'user-123', name: 'John Doe', email: 'john@example.com' } as any).execute();
46
+ });
47
+
48
+ afterAll(async () => {
49
+ await deleteTestTable(TEST_TABLE);
50
+ });
51
+
52
+ describe('BetterDDB - Update Operation', () => {
53
+ it('should update an existing item using UpdateBuilder', async () => {
54
+ const updatedUser = await userDdb.update({ id: 'user-123', email: 'john@example.com' }).set({ name: 'Jane Doe' }).execute();
55
+ expect(updatedUser.name).toBe('Jane Doe');
56
+ expect(updatedUser.email).toBe('john@example.com');
57
+ });
58
+ });
@@ -0,0 +1,55 @@
1
+ import { DynamoDB } from 'aws-sdk';
2
+
3
+ export const createTestTable = async (tableName: string, keySchema: DynamoDB.CreateTableInput['KeySchema'], attributeDefinitions: DynamoDB.CreateTableInput['AttributeDefinitions']) => {
4
+ const dynamoDB = new DynamoDB({
5
+ region: 'us-east-1',
6
+ endpoint: 'http://localhost:4566',
7
+ });
8
+
9
+ console.log('Creating DynamoDB table in LocalStack...');
10
+
11
+ try {
12
+ await dynamoDB.createTable({
13
+ TableName: tableName,
14
+ KeySchema: keySchema,
15
+ AttributeDefinitions: attributeDefinitions,
16
+ BillingMode: 'PAY_PER_REQUEST',
17
+ }).promise();
18
+ } catch (error: any) {
19
+ if (error.code === 'ResourceInUseException') {
20
+ console.log('Table already exists, skipping creation.');
21
+ } else {
22
+ throw error;
23
+ }
24
+ }
25
+
26
+ // Wait for the table to become active.
27
+ let attempts = 0;
28
+ while (attempts < 60) { // wait up to 60 seconds
29
+ const { Table } = await dynamoDB.describeTable({ TableName: tableName }).promise();
30
+ if (Table?.TableStatus === 'ACTIVE') {
31
+ console.log('DynamoDB table is ready.');
32
+ return;
33
+ }
34
+ console.log('Waiting for table to become ACTIVE...');
35
+ await new Promise((res) => setTimeout(res, 1000));
36
+ attempts++;
37
+ }
38
+ throw new Error('Table did not become active in time.');
39
+ };
40
+
41
+ export const deleteTestTable = async (tableName: string) => {
42
+ const dynamoDB = new DynamoDB({
43
+ region: 'us-east-1',
44
+ endpoint: 'http://localhost:4566',
45
+ });
46
+ try {
47
+ await dynamoDB.deleteTable({ TableName: tableName }).promise();
48
+ } catch (error: any) {
49
+ if (error.code === 'ResourceNotFoundException') {
50
+ console.log('Table not found during deletion.');
51
+ } else {
52
+ throw error;
53
+ }
54
+ }
55
+ };
@@ -1,98 +0,0 @@
1
- import { z } from 'zod';
2
- import { BetterDDB } from '../src/betterddb';
3
- import { DynamoDB } from 'aws-sdk';
4
-
5
- const TEST_TABLE = 'TestTable';
6
-
7
- // LocalStack Configuration
8
- const client = new DynamoDB.DocumentClient({
9
- region: 'us-east-1',
10
- endpoint: 'http://localhost:4566'
11
- });
12
-
13
- // Table Schema
14
- const UserSchema = z.object({
15
- id: z.string(),
16
- name: z.string(),
17
- email: z.string().email(),
18
- createdAt: z.string(),
19
- updatedAt: z.string()
20
- });
21
-
22
- const userDal = new BetterDDB({
23
- schema: UserSchema,
24
- tableName: TEST_TABLE,
25
- keys: {
26
- primary: { name: 'pk', definition: 'id' }
27
- },
28
- client,
29
- autoTimestamps: true
30
- });
31
-
32
- beforeAll(async () => {
33
- const dynamoDB = new DynamoDB({
34
- region: 'us-east-1',
35
- endpoint: 'http://localhost:4566'
36
- });
37
-
38
- console.log('Creating DynamoDB table in LocalStack...');
39
-
40
- await dynamoDB.createTable({
41
- TableName: TEST_TABLE,
42
- KeySchema: [{ AttributeName: 'pk', KeyType: 'HASH' }],
43
- AttributeDefinitions: [{ AttributeName: 'pk', AttributeType: 'S' }],
44
- BillingMode: 'PAY_PER_REQUEST'
45
- }).promise();
46
-
47
- // Wait for the table to become active
48
- while (true) {
49
- const { Table } = await dynamoDB.describeTable({ TableName: TEST_TABLE }).promise();
50
- if (Table?.TableStatus === 'ACTIVE') {
51
- console.log('DynamoDB table is ready.');
52
- break;
53
- }
54
- console.log('Waiting for table to become ACTIVE...');
55
- await new Promise(res => setTimeout(res, 1000)); // Wait 1 sec before retrying
56
- }
57
- });
58
-
59
- afterAll(async () => {
60
- // Cleanup: delete the table
61
- const dynamoDB = new DynamoDB({
62
- region: 'us-east-1',
63
- endpoint: 'http://localhost:4566'
64
- });
65
-
66
- await dynamoDB.deleteTable({ TableName: TEST_TABLE }).promise();
67
- });
68
-
69
- describe('BetterDDB - Integration Tests', () => {
70
- it('should insert an item into DynamoDB', async () => {
71
- const user = {
72
- id: 'user-123',
73
- name: 'John Doe',
74
- email: 'john@example.com'
75
- };
76
-
77
- const createdUser = await userDal.create(user as any);
78
- expect(createdUser).toHaveProperty('createdAt');
79
- expect(createdUser).toHaveProperty('updatedAt');
80
- });
81
-
82
- it('should retrieve an item by ID', async () => {
83
- const user = await userDal.get({ id: 'user-123' });
84
- expect(user).not.toBeNull();
85
- expect(user?.id).toBe('user-123');
86
- });
87
-
88
- it('should update an existing item', async () => {
89
- const updatedUser = await userDal.update({ id: 'user-123' }, { name: 'Jane Doe' });
90
- expect(updatedUser.name).toBe('Jane Doe');
91
- });
92
-
93
- it('should delete an item', async () => {
94
- await userDal.delete({ id: 'user-123' });
95
- const deletedUser = await userDal.get({ id: 'user-123' });
96
- expect(deletedUser).toBeNull();
97
- });
98
- });