betterddb 0.6.7 → 0.6.9

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.
Files changed (48) hide show
  1. package/dist/betterddb.js +4 -4
  2. package/dist/builders/query-builder.js +9 -5
  3. package/dist/builders/update-builder.js +3 -3
  4. package/dist/types/betterddb.d.ts +8 -10
  5. package/dist/types/betterddb.d.ts.map +1 -1
  6. package/dist/types/builders/batch-get-builder.d.ts +1 -1
  7. package/dist/types/builders/batch-get-builder.d.ts.map +1 -1
  8. package/dist/types/builders/create-builder.d.ts +2 -2
  9. package/dist/types/builders/create-builder.d.ts.map +1 -1
  10. package/dist/types/builders/delete-builder.d.ts +4 -3
  11. package/dist/types/builders/delete-builder.d.ts.map +1 -1
  12. package/dist/types/builders/get-builder.d.ts +2 -2
  13. package/dist/types/builders/get-builder.d.ts.map +1 -1
  14. package/dist/types/builders/query-builder.d.ts +6 -5
  15. package/dist/types/builders/query-builder.d.ts.map +1 -1
  16. package/dist/types/builders/scan-builder.d.ts +6 -5
  17. package/dist/types/builders/scan-builder.d.ts.map +1 -1
  18. package/dist/types/builders/update-builder.d.ts +6 -5
  19. package/dist/types/builders/update-builder.d.ts.map +1 -1
  20. package/dist/types/types/paginated-result.d.ts +2 -1
  21. package/dist/types/types/paginated-result.d.ts.map +1 -1
  22. package/package.json +6 -3
  23. package/.eslintrc.cjs +0 -41
  24. package/.github/workflows/npm-publish.yml +0 -33
  25. package/LICENCSE +0 -21
  26. package/docker-compose.yml +0 -16
  27. package/jest.config.js +0 -16
  28. package/prettier.config.js +0 -6
  29. package/src/betterddb.ts +0 -263
  30. package/src/builders/batch-get-builder.ts +0 -56
  31. package/src/builders/create-builder.ts +0 -97
  32. package/src/builders/delete-builder.ts +0 -80
  33. package/src/builders/get-builder.ts +0 -75
  34. package/src/builders/query-builder.ts +0 -196
  35. package/src/builders/scan-builder.ts +0 -87
  36. package/src/builders/update-builder.ts +0 -231
  37. package/src/index.ts +0 -1
  38. package/src/operator.ts +0 -43
  39. package/src/types/paginated-result.ts +0 -4
  40. package/test/batch-get.test.ts +0 -97
  41. package/test/create.test.ts +0 -95
  42. package/test/delete.test.ts +0 -79
  43. package/test/get.test.ts +0 -85
  44. package/test/query.test.ts +0 -192
  45. package/test/scan.test.ts +0 -113
  46. package/test/update.test.ts +0 -84
  47. package/test/utils/table-setup.ts +0 -56
  48. package/tsconfig.json +0 -21
@@ -1,95 +0,0 @@
1
- import { z } from 'zod';
2
- import { BetterDDB } from '../src/betterddb';
3
- import { createTestTable, deleteTestTable } from './utils/table-setup';
4
- import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
5
- import { DynamoDB, GlobalSecondaryIndex } from '@aws-sdk/client-dynamodb';
6
- import { GetCommand } from '@aws-sdk/lib-dynamodb';
7
- import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
8
- const TEST_TABLE = "create-test-table";
9
- const ENDPOINT = 'http://localhost:4566';
10
- const REGION = 'us-east-1';
11
- const ENTITY_TYPE = 'USER';
12
- const PRIMARY_KEY = 'pk';
13
- const PRIMARY_KEY_TYPE = 'S';
14
- const SORT_KEY = 'sk';
15
- const SORT_KEY_TYPE = 'S';
16
- const GSI_NAME = 'EmailIndex';
17
- const GSI_PRIMARY_KEY = 'gsi1pk';
18
- const GSI_SORT_KEY = 'gsi1sk';
19
- const KEY_SCHEMA = [
20
- { AttributeName: PRIMARY_KEY, KeyType: 'HASH' },
21
- { AttributeName: SORT_KEY, KeyType: 'RANGE' }
22
- ] as KeySchemaElement[];
23
- const ATTRIBUTE_DEFINITIONS = [
24
- { AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
25
- { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE },
26
- { AttributeName: GSI_PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
27
- { AttributeName: GSI_SORT_KEY, AttributeType: SORT_KEY_TYPE },
28
- ] as AttributeDefinition[];
29
- const GSIS = [
30
- {
31
- IndexName: GSI_NAME,
32
- KeySchema: [{ AttributeName: GSI_PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: GSI_SORT_KEY, KeyType: 'RANGE' }],
33
- Projection: {
34
- ProjectionType: 'ALL',
35
- },
36
- },
37
- ] as GlobalSecondaryIndex[];
38
- const client = DynamoDBDocumentClient.from(new DynamoDB({
39
- region: REGION,
40
- endpoint: ENDPOINT,
41
- }));
42
-
43
- const UserSchema = z.object({
44
- id: z.string(),
45
- name: z.string(),
46
- email: z.string().email(),
47
- });
48
-
49
- type User = z.infer<typeof UserSchema>;
50
-
51
- const userDdb = new BetterDDB({
52
- schema: UserSchema,
53
- tableName: TEST_TABLE,
54
- entityType: ENTITY_TYPE,
55
- keys: {
56
- primary: { name: "pk", definition: { build: (raw) => `USER#${raw.id}` } },
57
- sort: { name: "sk", definition: { build: (raw) => `EMAIL#${raw.email}` } },
58
- gsis: { gsi1: { name: 'gsi1', primary: { name: "gsi1pk", definition: { build: (raw) => "NAME" } }, sort: { name: "gsi1sk", definition: { build: (raw) => `NAME#${raw.name}` } } } },
59
- },
60
- client,
61
- timestamps: true,
62
- });
63
-
64
- beforeAll(async () => {
65
- await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS, GSIS);
66
- });
67
-
68
- afterAll(async () => {
69
- await deleteTestTable(TEST_TABLE);
70
- });
71
-
72
- describe('BetterDDB - Create Operation', () => {
73
- it('should insert an item using CreateBuilder', async () => {
74
- const user = { id: 'user-123', name: 'John Doe', email: 'john@example.com' };
75
-
76
- await userDdb.create(user as any).execute();
77
-
78
- const result = await client.send(new GetCommand({ TableName: TEST_TABLE, Key: { pk: 'USER#user-123', sk: 'EMAIL#john@example.com' } }));
79
-
80
- expect(result).not.toBeNull();
81
- expect(result.Item).not.toBeNull();
82
- expect(result.Item?.pk).toBe('USER#user-123');
83
- expect(result.Item?.sk).toBe('EMAIL#john@example.com');
84
- expect(result.Item?.gsi1pk).toBe('NAME');
85
- expect(result.Item?.gsi1sk).toBe('NAME#John Doe');
86
- expect(result.Item?.id).toBe('user-123');
87
- expect(result.Item?.createdAt).not.toBeNull();
88
- expect(result.Item?.updatedAt).not.toBeNull();
89
- });
90
-
91
- it('should fails to validate and not insert an item', async () => {
92
- const user = { id: 'user-123', email: 'john@example.com' };
93
- await expect(userDdb.create(user as any).execute()).rejects.toThrow();
94
- });
95
- });
@@ -1,79 +0,0 @@
1
- // __tests__/delete.test.ts
2
- import { z } from 'zod';
3
- import { BetterDDB } from '../src/betterddb';
4
- import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
5
- import { DynamoDB } from '@aws-sdk/client-dynamodb';
6
- import { createTestTable, deleteTestTable } from './utils/table-setup';
7
- import { KeySchemaElement, AttributeDefinition, GlobalSecondaryIndex } from '@aws-sdk/client-dynamodb';
8
- const TEST_TABLE = "delete-test-table";
9
- const ENDPOINT = 'http://localhost:4566';
10
- const REGION = 'us-east-1';
11
- const ENTITY_TYPE = 'USER';
12
- const PRIMARY_KEY = 'pk';
13
- const PRIMARY_KEY_TYPE = 'S';
14
- const SORT_KEY = 'sk';
15
- const SORT_KEY_TYPE = 'S';
16
- const GSI_NAME = 'EmailIndex';
17
- const GSI_PRIMARY_KEY = 'gsi1pk';
18
- const GSI_SORT_KEY = 'gsi1sk';
19
- const KEY_SCHEMA = [
20
- { AttributeName: PRIMARY_KEY, KeyType: 'HASH' },
21
- { AttributeName: SORT_KEY, KeyType: 'RANGE' }
22
- ] as KeySchemaElement[];
23
- const ATTRIBUTE_DEFINITIONS = [
24
- { AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
25
- { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE },
26
- { AttributeName: GSI_PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
27
- { AttributeName: GSI_SORT_KEY, AttributeType: SORT_KEY_TYPE },
28
- ] as AttributeDefinition[];
29
- const GSIS = [
30
- {
31
- IndexName: GSI_NAME,
32
- KeySchema: [{ AttributeName: GSI_PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: GSI_SORT_KEY, KeyType: 'RANGE' }],
33
- Projection: {
34
- ProjectionType: 'ALL',
35
- },
36
- },
37
- ] as GlobalSecondaryIndex[];
38
- const client = DynamoDBDocumentClient.from(new DynamoDB({
39
- region: REGION,
40
- endpoint: ENDPOINT,
41
- }));
42
-
43
- const UserSchema = z.object({
44
- id: z.string(),
45
- name: z.string(),
46
- email: z.string().email(),
47
- });
48
-
49
- type User = z.infer<typeof UserSchema>;
50
-
51
- const userDdb = new BetterDDB({
52
- schema: UserSchema,
53
- tableName: TEST_TABLE,
54
- entityType: ENTITY_TYPE,
55
- keys: {
56
- primary: { name: PRIMARY_KEY, definition: { build: (raw) => raw.id! } },
57
- sort: { name: SORT_KEY, definition: { build: (raw) => raw.email! } },
58
- },
59
- client,
60
- timestamps: true,
61
- });
62
-
63
- beforeAll(async () => {
64
- await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS, GSIS);
65
- await userDdb.create({ id: 'user-123', name: 'John Doe', email: 'john@example.com' } as any).execute();
66
- });
67
-
68
- afterAll(async () => {
69
- await deleteTestTable(TEST_TABLE);
70
- });
71
-
72
- describe('BetterDDB - Delete Operation', () => {
73
- it('should delete an item using DeleteBuilder', async () => {
74
- await userDdb.delete({ id: 'user-123', email: 'john@example.com' })
75
- .execute();
76
- const deletedUser = await userDdb.get({ id: 'user-123', email: 'john@example.com' }).execute();
77
- expect(deletedUser).toBeNull();
78
- });
79
- });
package/test/get.test.ts DELETED
@@ -1,85 +0,0 @@
1
- // __tests__/get.test.ts
2
- import { z } from 'zod';
3
- import { BetterDDB } from '../src/betterddb';
4
- import { createTestTable, deleteTestTable } from './utils/table-setup';
5
- import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
6
- import { DynamoDB, GlobalSecondaryIndex } from '@aws-sdk/client-dynamodb';
7
- import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
8
- const TEST_TABLE = "get-test-table";
9
- const ENDPOINT = 'http://localhost:4566';
10
- const REGION = 'us-east-1';
11
- const ENTITY_TYPE = 'USER';
12
- const PRIMARY_KEY = 'pk';
13
- const PRIMARY_KEY_TYPE = 'S';
14
- const SORT_KEY = 'sk';
15
- const SORT_KEY_TYPE = 'S';
16
- const GSI_NAME = 'EmailIndex';
17
- const GSI_PRIMARY_KEY = 'gsi1pk';
18
- const GSI_SORT_KEY = 'gsi1sk';
19
- const KEY_SCHEMA = [
20
- { AttributeName: PRIMARY_KEY, KeyType: 'HASH' },
21
- { AttributeName: SORT_KEY, KeyType: 'RANGE' }
22
- ] as KeySchemaElement[];
23
- const ATTRIBUTE_DEFINITIONS = [
24
- { AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
25
- { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE },
26
- { AttributeName: GSI_PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
27
- { AttributeName: GSI_SORT_KEY, AttributeType: SORT_KEY_TYPE },
28
- ] as AttributeDefinition[];
29
- const GSIS = [
30
- {
31
- IndexName: GSI_NAME,
32
- KeySchema: [{ AttributeName: GSI_PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: GSI_SORT_KEY, KeyType: 'RANGE' }],
33
- Projection: {
34
- ProjectionType: 'ALL',
35
- },
36
- },
37
- ] as GlobalSecondaryIndex[];
38
-
39
- const client = DynamoDBDocumentClient.from(new DynamoDB({
40
- region: REGION,
41
- endpoint: ENDPOINT,
42
- }));
43
-
44
-
45
- const UserSchema = z.object({
46
- id: z.string(),
47
- name: z.string(),
48
- email: z.string().email(),
49
- });
50
-
51
- type User = z.infer<typeof UserSchema>;
52
-
53
- const userDdb = new BetterDDB({
54
- schema: UserSchema,
55
- tableName: TEST_TABLE,
56
- entityType: ENTITY_TYPE,
57
- keys: {
58
- primary: { name: PRIMARY_KEY, definition: { build: (raw) => raw.id! } },
59
- sort: { name: SORT_KEY, definition: { build: (raw) => raw.email! } },
60
- },
61
- client,
62
- timestamps: true,
63
- });
64
-
65
- beforeAll(async () => {
66
- await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS, GSIS);
67
- await userDdb.create({ id: 'user-123', name: 'John Doe', email: 'john@example.com' } as any).execute();
68
- });
69
-
70
- afterAll(async () => {
71
- await deleteTestTable(TEST_TABLE);
72
- });
73
-
74
- describe('BetterDDB - Get Operation', () => {
75
- it('should retrieve an item using GetBuilder', async () => {
76
- const user = await userDdb.get({ id: 'user-123', email: 'john@example.com' }).execute();
77
- expect(user).not.toBeNull();
78
- expect(user?.id).toBe('user-123');
79
- });
80
-
81
- it('should retrieve an item using GetBuilder that does not exist', async () => {
82
- const user = await userDdb.get({ id: 'user-123', email: 'jane@example.com' }).execute();
83
- expect(user).toBeNull();
84
- });
85
- });
@@ -1,192 +0,0 @@
1
- import { z } from 'zod';
2
- import { BetterDDB } from '../src/betterddb';
3
- import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
4
- import { AttributeDefinition, DynamoDB, GlobalSecondaryIndex, KeySchemaElement } from '@aws-sdk/client-dynamodb';
5
- import { createTestTable, deleteTestTable } from './utils/table-setup';
6
-
7
- const TEST_TABLE = "query-test-table";
8
- const ENDPOINT = 'http://localhost:4566';
9
- const REGION = 'us-east-1';
10
- const ENTITY_TYPE = 'USER';
11
- const PRIMARY_KEY = 'pk';
12
- const PRIMARY_KEY_TYPE = 'S';
13
- const SORT_KEY = 'sk';
14
- const SORT_KEY_TYPE = 'S';
15
- const GSI_NAME = 'EmailIndex';
16
- const GSI_PRIMARY_KEY = 'gsi1pk';
17
- const GSI_SORT_KEY = 'gsi1sk';
18
- const KEY_SCHEMA = [
19
- { AttributeName: PRIMARY_KEY, KeyType: 'HASH' },
20
- { AttributeName: SORT_KEY, KeyType: 'RANGE' }
21
- ] as KeySchemaElement[];
22
- const ATTRIBUTE_DEFINITIONS = [
23
- { AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
24
- { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE },
25
- { AttributeName: GSI_PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
26
- { AttributeName: GSI_SORT_KEY, AttributeType: SORT_KEY_TYPE },
27
- ] as AttributeDefinition[];
28
- const GSIS = [
29
- {
30
- IndexName: GSI_NAME,
31
- KeySchema: [
32
- { AttributeName: GSI_PRIMARY_KEY, KeyType: 'HASH' },
33
- { AttributeName: GSI_SORT_KEY, KeyType: 'RANGE' }
34
- ],
35
- Projection: { ProjectionType: 'ALL' },
36
- },
37
- ] as GlobalSecondaryIndex[];
38
-
39
- const client = DynamoDBDocumentClient.from(new DynamoDB({
40
- region: REGION,
41
- endpoint: ENDPOINT,
42
- }));
43
-
44
- const UserSchema = z.object({
45
- id: z.string(),
46
- name: z.string(),
47
- email: z.string().email(),
48
- });
49
-
50
- type User = z.infer<typeof UserSchema>;
51
-
52
- const userDdb = new BetterDDB({
53
- schema: UserSchema,
54
- tableName: TEST_TABLE,
55
- entityType: ENTITY_TYPE,
56
- keys: {
57
- primary: {
58
- name: PRIMARY_KEY,
59
- definition: { build: (raw) => `USER#${raw.id}` }
60
- },
61
- sort: {
62
- name: SORT_KEY,
63
- definition: { build: (raw) => `USER#${raw.email}` }
64
- },
65
- gsis: {
66
- EmailIndex: {
67
- name: 'EmailIndex',
68
- primary: {
69
- name: GSI_PRIMARY_KEY,
70
- definition: { build: (raw) => `USER#${raw.email}` }
71
- },
72
- sort: {
73
- name: GSI_SORT_KEY,
74
- definition: { build: (raw) => `USER#${raw.email}` }
75
- }
76
- }
77
- }
78
- },
79
- client,
80
- timestamps: true,
81
- });
82
-
83
- beforeAll(async () => {
84
- await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS, GSIS);
85
-
86
- const items = [
87
- { id: 'user-1', name: 'Alice', email: 'alice@example.com' },
88
- { id: 'user-2', name: 'Alice B', email: 'alice@example.com' },
89
- { id: 'user-3', name: 'Bob', email: 'bob@example.com' }
90
- ];
91
- await Promise.all(items.map(item => userDdb.create(item as any).execute()));
92
- });
93
-
94
- afterAll(async () => {
95
- await deleteTestTable(TEST_TABLE);
96
- });
97
-
98
- describe('BetterDDB - Query Operation', () => {
99
- it('should query items using QueryBuilder with filter condition', async () => {
100
- const results = await userDdb.query({ id: 'user-1' })
101
- .filter('name', 'begins_with', 'Alice')
102
- .limitResults(5)
103
- .execute();
104
- expect(results.items.length).toBeGreaterThanOrEqual(1);
105
- results.items.forEach(result => {
106
- expect(result.name).toMatch(/^Alice/);
107
- });
108
- });
109
-
110
- it('should query items using QueryBuilder with index', async () => {
111
- const results = await userDdb.query({ email: 'alice@example.com' })
112
- .usingIndex('EmailIndex')
113
- .limitResults(1)
114
- .execute();
115
- expect(results.items.length).toEqual(1);
116
- results.items.forEach(result => {
117
- expect(result.email).toEqual('alice@example.com');
118
- });
119
- });
120
-
121
- it('should query items using QueryBuilder with a sort key condition', async () => {
122
- // For a complex sort key, users must supply an object.
123
- const results = await userDdb.query({ id: 'user-1' })
124
- .where('begins_with', { email: 'alice' })
125
- .execute();
126
- expect(results.items.length).toBeGreaterThanOrEqual(1);
127
- results.items.forEach(result => {
128
- expect(result.email).toMatch(/^alice/i);
129
- });
130
- });
131
-
132
- it('should return no results if the sort key condition does not match', async () => {
133
- const results = await userDdb.query({ id: 'user-1' })
134
- .where('begins_with', { email: 'bob' })
135
- .execute();
136
- expect(results.items.length).toEqual(0);
137
- });
138
-
139
- it('should query items using QueryBuilder with index and additional filter', async () => {
140
- const results = await userDdb.query({ email: 'alice@example.com' })
141
- .usingIndex('EmailIndex')
142
- .filter('name', 'begins_with', 'Alice')
143
- .execute();
144
- expect(results.items.length).toBeGreaterThanOrEqual(1);
145
- results.items.forEach(result => {
146
- expect(result.email).toEqual('alice@example.com');
147
- expect(result.name).toMatch(/^Alice/);
148
- });
149
- });
150
-
151
-
152
- it('should query items using QueryBuilder with a sort key condition using "between"', async () => {
153
- // Here we use the "between" operator. The sort key build function produces a value like "USER#alice@example.com"
154
- // We provide lower and upper bounds as objects.
155
- const results = await userDdb.query({ id: 'user-1' })
156
- .where('between', [
157
- { email: 'alice' }, // Lower bound -> built to "USER#alice"
158
- { email: 'alice@example.com' } // Upper bound -> built to "USER#alice@example.com"
159
- ])
160
- .execute();
161
- expect(results.items.length).toBeGreaterThanOrEqual(1);
162
- results.items.forEach(result => {
163
- // The built sort key for user-1 is "USER#alice@example.com"
164
- expect(result.email).toMatch(/alice@example\.com/i);
165
- });
166
- });
167
-
168
- it('should query items using QueryBuilder with multiple filter conditions on an index', async () => {
169
- // Query the GSI for email "alice@example.com". Two items match.
170
- // Then apply two filter conditions: name begins_with "Alice" and name contains "B" should only match one.
171
- const results = await userDdb.query({ email: 'alice@example.com' })
172
- .usingIndex('EmailIndex')
173
- .filter('name', 'begins_with', 'Alice')
174
- .filter('name', 'contains', 'B')
175
- .execute();
176
- expect(results.items.length).toEqual(1);
177
- results.items.forEach(result => {
178
- expect(result.name).toMatch(/^Alice/);
179
- expect(result.name).toContain('B');
180
- });
181
- });
182
-
183
- it('should query items using QueryBuilder with where clause on an index', async () => {
184
- // Query the GSI for email "alice@example.com". Two items match.
185
- // Then apply two filter conditions: name begins_with "Alice" and name contains "B" should only match one.
186
- const results = await userDdb.query({ email: 'alice@example.com' })
187
- .usingIndex('EmailIndex')
188
- .where('begins_with', { email: 'alice' })
189
- .execute();
190
- expect(results.items.length).toEqual(2);
191
- });
192
- });
package/test/scan.test.ts DELETED
@@ -1,113 +0,0 @@
1
- import { z } from 'zod';
2
- import { BetterDDB } from '../src/betterddb';
3
- import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
4
- import { DynamoDB, GlobalSecondaryIndex } from '@aws-sdk/client-dynamodb';
5
- import { createTestTable, deleteTestTable } from './utils/table-setup';
6
- import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
7
- const TEST_TABLE = "scan-test-table";
8
- const ENDPOINT = 'http://localhost:4566';
9
- const REGION = 'us-east-1';
10
- const ENTITY_TYPE = 'USER';
11
- const PRIMARY_KEY = 'pk';
12
- const PRIMARY_KEY_TYPE = 'S';
13
- const SORT_KEY = 'sk';
14
- const SORT_KEY_TYPE = 'S';
15
- const GSI_NAME = 'EmailIndex';
16
- const GSI_PRIMARY_KEY = 'gsi1pk';
17
- const GSI_SORT_KEY = 'gsi1sk';
18
- const KEY_SCHEMA = [
19
- { AttributeName: PRIMARY_KEY, KeyType: 'HASH' },
20
- { AttributeName: SORT_KEY, KeyType: 'RANGE' }
21
- ] as KeySchemaElement[];
22
- const ATTRIBUTE_DEFINITIONS = [
23
- { AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
24
- { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE },
25
- { AttributeName: GSI_PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
26
- { AttributeName: GSI_SORT_KEY, AttributeType: SORT_KEY_TYPE },
27
- ] as AttributeDefinition[];
28
- const GSIS = [
29
- {
30
- IndexName: GSI_NAME,
31
- KeySchema: [{ AttributeName: GSI_PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: GSI_SORT_KEY, KeyType: 'RANGE' }],
32
- Projection: {
33
- ProjectionType: 'ALL',
34
- },
35
- },
36
- ] as GlobalSecondaryIndex[];
37
- const client = DynamoDBDocumentClient.from(new DynamoDB({
38
- region: REGION,
39
- endpoint: ENDPOINT,
40
- }));
41
-
42
-
43
- const UserSchema = z.object({
44
- id: z.string(),
45
- name: z.string(),
46
- email: z.string().email(),
47
- });
48
-
49
- type User = z.infer<typeof UserSchema>;
50
-
51
- const userDdb = new BetterDDB({
52
- schema: UserSchema,
53
- tableName: TEST_TABLE,
54
- entityType: ENTITY_TYPE,
55
- keys: {
56
- primary: { name: PRIMARY_KEY, definition: { build: (raw) => `USER#${raw.id}` } },
57
- sort: { name: SORT_KEY, definition: { build: (raw) => `EMAIL#${raw.email}` } },
58
- },
59
- client,
60
- timestamps: true,
61
- });
62
-
63
- beforeAll(async () => {
64
- await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS, GSIS);
65
- // Insert multiple items.
66
- const items = [
67
- { id: 'user-1', name: 'Alice', email: 'alice@example.com' },
68
- { id: 'user-2', name: 'Alice B', email: 'alice@example.com' },
69
- { id: 'user-3', name: 'Bob', email: 'bob@example.com' }
70
- ];
71
- await Promise.all(items.map(item => userDdb.create(item as any).execute()));
72
- });
73
-
74
- afterAll(async () => {
75
- await deleteTestTable(TEST_TABLE);
76
- });
77
-
78
- describe('BetterDDB - Scan Operation', () => {
79
- it('should scan items using ScanBuilder', async () => {
80
- const results = await userDdb.scan()
81
- .where('email', 'begins_with', 'a')
82
- .limitResults(10).execute();
83
- expect(results.items.length).toBeGreaterThanOrEqual(1);
84
- results.items.forEach(result => {
85
- expect(result.email).toMatch(/^alice/i);
86
- });
87
- });
88
-
89
- it('should scan items using ScanBuilder with a contains filter', async () => {
90
- // Scan for users whose name contains "Alice"
91
- const results = await userDdb.scan()
92
- .where('name', 'contains', 'Alice')
93
- .limitResults(10)
94
- .execute();
95
- expect(results.items.length).toBeGreaterThan(0);
96
- results.items.forEach(result => {
97
- expect(result.name).toContain('Alice');
98
- });
99
- });
100
-
101
- it('should scan items using ScanBuilder with a between filter on email', async () => {
102
- // Using lexicographical order on the email address:
103
- // 'alice@example.com' should be between "a" and "c".
104
- const results = await userDdb.scan()
105
- .where('email', 'between', ['a', 'c'])
106
- .execute();
107
- expect(results.items.length).toBeGreaterThan(0);
108
- results.items.forEach(result => {
109
- // A simple lexicographical check
110
- expect(result.email >= 'a' && result.email <= 'c').toBeTruthy();
111
- });
112
- });
113
- });
@@ -1,84 +0,0 @@
1
- import { z } from 'zod';
2
- import { BetterDDB } from '../src/betterddb';
3
- import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
4
- import { createTestTable, deleteTestTable } from './utils/table-setup';
5
- import { DynamoDB, GlobalSecondaryIndex } from '@aws-sdk/client-dynamodb';
6
- import { KeySchemaElement, AttributeDefinition } from '@aws-sdk/client-dynamodb';
7
- const TEST_TABLE = "update-test-table";
8
- const ENDPOINT = 'http://localhost:4566';
9
- const REGION = 'us-east-1';
10
- const ENTITY_TYPE = 'USER';
11
- const PRIMARY_KEY = 'pk';
12
- const PRIMARY_KEY_TYPE = 'S';
13
- const SORT_KEY = 'sk';
14
- const SORT_KEY_TYPE = 'S';
15
- const GSI_NAME = 'EmailIndex';
16
- const GSI_PRIMARY_KEY = 'gsi1pk';
17
- const GSI_SORT_KEY = 'gsi1sk';
18
- const KEY_SCHEMA = [
19
- { AttributeName: PRIMARY_KEY, KeyType: 'HASH' },
20
- { AttributeName: SORT_KEY, KeyType: 'RANGE' }
21
- ] as KeySchemaElement[];
22
- const ATTRIBUTE_DEFINITIONS = [
23
- { AttributeName: PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
24
- { AttributeName: SORT_KEY, AttributeType: SORT_KEY_TYPE },
25
- { AttributeName: GSI_PRIMARY_KEY, AttributeType: PRIMARY_KEY_TYPE },
26
- { AttributeName: GSI_SORT_KEY, AttributeType: SORT_KEY_TYPE },
27
- ] as AttributeDefinition[];
28
- const GSIS = [
29
- {
30
- IndexName: GSI_NAME,
31
- KeySchema: [{ AttributeName: GSI_PRIMARY_KEY, KeyType: 'HASH' }, { AttributeName: GSI_SORT_KEY, KeyType: 'RANGE' }],
32
- Projection: {
33
- ProjectionType: 'ALL',
34
- },
35
- },
36
- ] as GlobalSecondaryIndex[];
37
- const client = DynamoDBDocumentClient.from(new DynamoDB({
38
- region: REGION,
39
- endpoint: ENDPOINT,
40
- }));
41
-
42
-
43
- const UserSchema = z.object({
44
- id: z.string(),
45
- name: z.string(),
46
- email: z.string().email(),
47
- });
48
-
49
- type User = z.infer<typeof UserSchema>;
50
-
51
- const userDdb = new BetterDDB({
52
- schema: UserSchema,
53
- tableName: TEST_TABLE,
54
- entityType: ENTITY_TYPE,
55
- keys: {
56
- primary: { name: PRIMARY_KEY, definition: { build: (raw) => raw.id! } },
57
- sort: { name: SORT_KEY, definition: { build: (raw) => raw.email! } },
58
- },
59
- client,
60
- timestamps: true,
61
- });
62
-
63
- beforeAll(async () => {
64
- await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS, GSIS);
65
- await userDdb.create({ id: 'user-123', name: 'John Doe', email: 'john@example.com' } as any).execute();
66
- });
67
-
68
- afterAll(async () => {
69
- await deleteTestTable(TEST_TABLE);
70
- });
71
-
72
- describe('BetterDDB - Update Operation', () => {
73
- it('should update an existing item using UpdateBuilder', async () => {
74
- const updatedUser = await userDdb.update({ id: 'user-123', email: 'john@example.com' }).set({ name: 'Jane Doe' }).execute();
75
- expect(updatedUser.name).toBe('Jane Doe');
76
- expect(updatedUser.email).toBe('john@example.com');
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
- // });
84
- });