@model-ts/dynamodb 2.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/cjs/__test__/client-with-cursor-encryption.test.js +298 -183
- package/dist/cjs/__test__/client-with-cursor-encryption.test.js.map +1 -1
- package/dist/cjs/__test__/client.test.js +101 -0
- package/dist/cjs/__test__/client.test.js.map +1 -1
- package/dist/cjs/__test__/pagination.test.d.ts +1 -0
- package/dist/cjs/__test__/pagination.test.js +241 -0
- package/dist/cjs/__test__/pagination.test.js.map +1 -0
- package/dist/cjs/client.d.ts +6 -1
- package/dist/cjs/client.js +7 -2
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/dynamodb-model.d.ts +33 -8
- package/dist/cjs/gsi.d.ts +4 -0
- package/dist/cjs/gsi.js +24 -0
- package/dist/cjs/gsi.js.map +1 -0
- package/dist/cjs/operations.d.ts +4 -8
- package/dist/cjs/operations.js.map +1 -1
- package/dist/cjs/pagination.d.ts +48 -59
- package/dist/cjs/pagination.js +16 -26
- package/dist/cjs/pagination.js.map +1 -1
- package/dist/cjs/provider.d.ts +135 -17
- package/dist/cjs/provider.js +5 -12
- package/dist/cjs/provider.js.map +1 -1
- package/dist/cjs/sandbox.js +10 -43
- package/dist/cjs/sandbox.js.map +1 -1
- package/dist/esm/__test__/client-with-cursor-encryption.test.js +298 -183
- package/dist/esm/__test__/client-with-cursor-encryption.test.js.map +1 -1
- package/dist/esm/__test__/client.test.js +101 -0
- package/dist/esm/__test__/client.test.js.map +1 -1
- package/dist/esm/__test__/pagination.test.d.ts +1 -0
- package/dist/esm/__test__/pagination.test.js +238 -0
- package/dist/esm/__test__/pagination.test.js.map +1 -0
- package/dist/esm/client.d.ts +6 -1
- package/dist/esm/client.js +7 -2
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/dynamodb-model.d.ts +33 -8
- package/dist/esm/gsi.d.ts +4 -0
- package/dist/esm/gsi.js +21 -0
- package/dist/esm/gsi.js.map +1 -0
- package/dist/esm/operations.d.ts +4 -8
- package/dist/esm/operations.js.map +1 -1
- package/dist/esm/pagination.d.ts +48 -59
- package/dist/esm/pagination.js +17 -26
- package/dist/esm/pagination.js.map +1 -1
- package/dist/esm/provider.d.ts +135 -17
- package/dist/esm/provider.js +5 -12
- package/dist/esm/provider.js.map +1 -1
- package/dist/esm/sandbox.js +10 -43
- package/dist/esm/sandbox.js.map +1 -1
- package/package.json +1 -1
- package/src/__test__/client-with-cursor-encryption.test.ts +365 -183
- package/src/__test__/client.test.ts +168 -0
- package/src/__test__/pagination.test.ts +300 -0
- package/src/client.ts +20 -19
- package/src/dynamodb-model.ts +31 -9
- package/src/gsi.ts +25 -0
- package/src/operations.ts +4 -10
- package/src/pagination.ts +39 -52
- package/src/provider.ts +9 -9
- package/src/sandbox.ts +10 -43
- package/tsconfig.esm.json +14 -4
- package/tsconfig.json +14 -4
package/src/pagination.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import crypto from "crypto"
|
|
2
2
|
import { PaginationError } from "./errors"
|
|
3
|
+
import { GSI, GSI_NAMES, GSIPK, GSISK } from "./gsi"
|
|
3
4
|
|
|
4
5
|
const SIV = "Q05yyCR+0tyWl6glrZhlNw=="
|
|
5
6
|
const ENCRYPTION_ALG = "aes-256-ctr"
|
|
@@ -33,13 +34,27 @@ export interface PaginationInput {
|
|
|
33
34
|
after?: string | null
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
export interface PaginationOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Maximum number of items to return.
|
|
40
|
+
*/
|
|
41
|
+
limit?: number
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Default number of items to return if no limit is provided.
|
|
45
|
+
*/
|
|
46
|
+
default?: number
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
const DEFAULT_OPTIONS = {
|
|
38
50
|
limit: 50,
|
|
39
51
|
default: 20,
|
|
40
52
|
}
|
|
41
53
|
|
|
42
|
-
export function decodePagination(
|
|
54
|
+
export function decodePagination(
|
|
55
|
+
pagination: PaginationInput,
|
|
56
|
+
paginationOptions: PaginationOptions = DEFAULT_OPTIONS
|
|
57
|
+
): {
|
|
43
58
|
cursor?: string
|
|
44
59
|
limit: number
|
|
45
60
|
direction: PaginationDirection
|
|
@@ -65,8 +80,8 @@ export function decodePagination(pagination: PaginationInput): {
|
|
|
65
80
|
return {
|
|
66
81
|
cursor: before ?? after ?? undefined,
|
|
67
82
|
limit: Math.min(
|
|
68
|
-
first ?? last ?? DEFAULT_OPTIONS.default,
|
|
69
|
-
DEFAULT_OPTIONS.limit
|
|
83
|
+
first ?? last ?? paginationOptions.default ?? DEFAULT_OPTIONS.default,
|
|
84
|
+
paginationOptions.limit ?? DEFAULT_OPTIONS.limit
|
|
70
85
|
),
|
|
71
86
|
direction:
|
|
72
87
|
before || last
|
|
@@ -117,43 +132,21 @@ const decryptCursor = (key: Buffer, encryptedCursor: string) => {
|
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
export const encodeDDBCursor = (
|
|
120
|
-
{
|
|
121
|
-
PK,
|
|
122
|
-
SK,
|
|
123
|
-
GSI2PK,
|
|
124
|
-
GSI2SK,
|
|
125
|
-
GSI3PK,
|
|
126
|
-
GSI3SK,
|
|
127
|
-
GSI4PK,
|
|
128
|
-
GSI4SK,
|
|
129
|
-
GSI5PK,
|
|
130
|
-
GSI5SK,
|
|
131
|
-
}: {
|
|
135
|
+
item: {
|
|
132
136
|
PK: string
|
|
133
137
|
SK: string
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
GSI3PK?: string
|
|
137
|
-
GSI3SK?: string
|
|
138
|
-
GSI4PK?: string
|
|
139
|
-
GSI4SK?: string
|
|
140
|
-
GSI5PK?: string
|
|
141
|
-
GSI5SK?: string
|
|
142
|
-
},
|
|
138
|
+
} & { [key in GSIPK]?: string } &
|
|
139
|
+
{ [key in GSISK]?: string },
|
|
143
140
|
encryptionKey?: Buffer
|
|
144
141
|
) => {
|
|
145
142
|
const cursor = Buffer.from(
|
|
146
143
|
JSON.stringify({
|
|
147
|
-
PK,
|
|
148
|
-
SK,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
GSI4PK,
|
|
154
|
-
GSI4SK,
|
|
155
|
-
GSI5PK,
|
|
156
|
-
GSI5SK,
|
|
144
|
+
PK: item.PK,
|
|
145
|
+
SK: item.SK,
|
|
146
|
+
...GSI_NAMES.map((GSI) => ({
|
|
147
|
+
[`${GSI}PK`]: item[`${GSI}PK` as const],
|
|
148
|
+
[`${GSI}SK`]: item[`${GSI}SK` as const],
|
|
149
|
+
})).reduce((acc, cur) => Object.assign(acc, cur), {}),
|
|
157
150
|
})
|
|
158
151
|
).toString("base64")
|
|
159
152
|
|
|
@@ -164,7 +157,7 @@ export const encodeDDBCursor = (
|
|
|
164
157
|
|
|
165
158
|
export const decodeDDBCursor = (
|
|
166
159
|
encoded: string,
|
|
167
|
-
index?:
|
|
160
|
+
index?: GSI,
|
|
168
161
|
encryptionKey?: Buffer
|
|
169
162
|
) => {
|
|
170
163
|
try {
|
|
@@ -173,26 +166,20 @@ export const decodeDDBCursor = (
|
|
|
173
166
|
|
|
174
167
|
if (!json) throw new Error("Couldn't decrypt cursor")
|
|
175
168
|
|
|
176
|
-
const {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
GSI2PK,
|
|
180
|
-
GSI2SK,
|
|
181
|
-
GSI3PK,
|
|
182
|
-
GSI3SK,
|
|
183
|
-
GSI4PK,
|
|
184
|
-
GSI4SK,
|
|
185
|
-
GSI5PK,
|
|
186
|
-
GSI5SK,
|
|
187
|
-
} = JSON.parse(Buffer.from(json, "base64").toString())
|
|
169
|
+
const { PK, SK, ...values } = JSON.parse(
|
|
170
|
+
Buffer.from(json, "base64").toString()
|
|
171
|
+
)
|
|
188
172
|
|
|
189
173
|
if (typeof PK !== "string" || typeof SK !== "string") throw new Error()
|
|
190
174
|
|
|
191
175
|
if (!index) return { PK, SK }
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
PK,
|
|
179
|
+
SK,
|
|
180
|
+
[`${index}PK`]: values[`${index}PK`],
|
|
181
|
+
[`${index}SK`]: values[`${index}SK`],
|
|
182
|
+
}
|
|
196
183
|
} catch (error) {
|
|
197
184
|
throw new PaginationError("Couldn't decode cursor")
|
|
198
185
|
}
|
package/src/provider.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { OutputOf, TypeOf, ModelOf } from "@model-ts/core"
|
|
|
18
18
|
import { RaceConditionError } from "./errors"
|
|
19
19
|
import { absurd } from "fp-ts/lib/function"
|
|
20
20
|
import { encodeDDBCursor, PaginationInput } from "./pagination"
|
|
21
|
+
import { GSI_NAMES, GSIPK, GSISK } from "./gsi"
|
|
21
22
|
|
|
22
23
|
export interface DynamoDBInternals<M extends Decodable> {
|
|
23
24
|
__dynamoDBDecode(
|
|
@@ -512,18 +513,17 @@ export const getProvider = (client: Client) => {
|
|
|
512
513
|
},
|
|
513
514
|
instanceProps: {
|
|
514
515
|
dynamodb: client,
|
|
515
|
-
keys<T extends DynamoDBModelInstance>(
|
|
516
|
+
keys<T extends DynamoDBModelInstance>(
|
|
517
|
+
this: T
|
|
518
|
+
): { PK: string; SK: string } & { [key in GSIPK]?: string } &
|
|
519
|
+
{ [key in GSISK]?: string } {
|
|
516
520
|
return {
|
|
517
521
|
PK: this.PK,
|
|
518
522
|
SK: this.SK,
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
GSI4PK: this.GSI4PK,
|
|
524
|
-
GSI4SK: this.GSI4SK,
|
|
525
|
-
GSI5PK: this.GSI5PK,
|
|
526
|
-
GSI5SK: this.GSI5SK,
|
|
523
|
+
...GSI_NAMES.map((GSI) => ({
|
|
524
|
+
[`${GSI}PK`]: this[`${GSI}PK`],
|
|
525
|
+
[`${GSI}SK`]: this[`${GSI}SK`],
|
|
526
|
+
})).reduce((acc, cur) => Object.assign(acc, cur), {}),
|
|
527
527
|
}
|
|
528
528
|
},
|
|
529
529
|
cursor<T extends DynamoDBModelInstance>(this: T) {
|
package/src/sandbox.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { chunksOf } from "fp-ts/lib/Array"
|
|
|
3
3
|
import DynamoDB from "aws-sdk/clients/dynamodb"
|
|
4
4
|
import diff from "snapshot-diff"
|
|
5
5
|
import { Client } from "./client"
|
|
6
|
+
import { GSI_NAMES } from "./gsi"
|
|
6
7
|
|
|
7
8
|
const ddb = new DynamoDB({
|
|
8
9
|
accessKeyId: "xxx",
|
|
@@ -27,14 +28,10 @@ export const createTable = async () => {
|
|
|
27
28
|
AttributeDefinitions: [
|
|
28
29
|
{ AttributeName: "PK", AttributeType: "S" },
|
|
29
30
|
{ AttributeName: "SK", AttributeType: "S" },
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{ AttributeName: "GSI4PK", AttributeType: "S" },
|
|
35
|
-
{ AttributeName: "GSI4SK", AttributeType: "S" },
|
|
36
|
-
{ AttributeName: "GSI5PK", AttributeType: "S" },
|
|
37
|
-
{ AttributeName: "GSI5SK", AttributeType: "S" },
|
|
31
|
+
...GSI_NAMES.flatMap((GSI) => [
|
|
32
|
+
{ AttributeName: `${GSI}PK`, AttributeType: "S" },
|
|
33
|
+
{ AttributeName: `${GSI}SK`, AttributeType: "S" },
|
|
34
|
+
]),
|
|
38
35
|
],
|
|
39
36
|
KeySchema: [
|
|
40
37
|
{ AttributeName: "PK", KeyType: "HASH" },
|
|
@@ -51,46 +48,16 @@ export const createTable = async () => {
|
|
|
51
48
|
ProjectionType: "ALL",
|
|
52
49
|
},
|
|
53
50
|
},
|
|
54
|
-
{
|
|
55
|
-
IndexName:
|
|
56
|
-
KeySchema: [
|
|
57
|
-
{ AttributeName: "GSI2PK", KeyType: "HASH" },
|
|
58
|
-
{ AttributeName: "GSI2SK", KeyType: "RANGE" },
|
|
59
|
-
],
|
|
60
|
-
Projection: {
|
|
61
|
-
ProjectionType: "ALL",
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
IndexName: "GSI3",
|
|
51
|
+
...GSI_NAMES.map((GSI) => ({
|
|
52
|
+
IndexName: GSI,
|
|
66
53
|
KeySchema: [
|
|
67
|
-
{ AttributeName:
|
|
68
|
-
{ AttributeName:
|
|
54
|
+
{ AttributeName: `${GSI}PK`, KeyType: "HASH" },
|
|
55
|
+
{ AttributeName: `${GSI}SK`, KeyType: "RANGE" },
|
|
69
56
|
],
|
|
70
57
|
Projection: {
|
|
71
58
|
ProjectionType: "ALL",
|
|
72
59
|
},
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
IndexName: "GSI4",
|
|
76
|
-
KeySchema: [
|
|
77
|
-
{ AttributeName: "GSI4PK", KeyType: "HASH" },
|
|
78
|
-
{ AttributeName: "GSI4SK", KeyType: "RANGE" },
|
|
79
|
-
],
|
|
80
|
-
Projection: {
|
|
81
|
-
ProjectionType: "ALL",
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
IndexName: "GSI5",
|
|
86
|
-
KeySchema: [
|
|
87
|
-
{ AttributeName: "GSI5PK", KeyType: "HASH" },
|
|
88
|
-
{ AttributeName: "GSI5SK", KeyType: "RANGE" },
|
|
89
|
-
],
|
|
90
|
-
Projection: {
|
|
91
|
-
ProjectionType: "ALL",
|
|
92
|
-
},
|
|
93
|
-
},
|
|
60
|
+
})),
|
|
94
61
|
],
|
|
95
62
|
BillingMode: "PAY_PER_REQUEST",
|
|
96
63
|
})
|
package/tsconfig.esm.json
CHANGED
|
@@ -5,9 +5,19 @@
|
|
|
5
5
|
"module": "ES2015",
|
|
6
6
|
"outDir": "./dist/esm",
|
|
7
7
|
"paths": {
|
|
8
|
-
"@model-ts/core": [
|
|
8
|
+
"@model-ts/core": [
|
|
9
|
+
"../core"
|
|
10
|
+
]
|
|
9
11
|
}
|
|
10
12
|
},
|
|
11
|
-
"references": [
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
"references": [
|
|
14
|
+
{
|
|
15
|
+
"path": "../core/tsconfig.esm.json"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"exclude": [
|
|
19
|
+
"dist",
|
|
20
|
+
"__test__",
|
|
21
|
+
"test-utils"
|
|
22
|
+
]
|
|
23
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -4,9 +4,19 @@
|
|
|
4
4
|
"rootDir": "./src",
|
|
5
5
|
"outDir": "./dist/cjs",
|
|
6
6
|
"paths": {
|
|
7
|
-
"@model-ts/core": [
|
|
7
|
+
"@model-ts/core": [
|
|
8
|
+
"../core"
|
|
9
|
+
]
|
|
8
10
|
}
|
|
9
11
|
},
|
|
10
|
-
"references": [
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
"references": [
|
|
13
|
+
{
|
|
14
|
+
"path": "../core"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"exclude": [
|
|
18
|
+
"dist",
|
|
19
|
+
"__test__",
|
|
20
|
+
"test-utils"
|
|
21
|
+
]
|
|
22
|
+
}
|