@aws/nx-plugin 1.0.0-rc.13 → 1.0.0-rc.14
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/generators.json +28 -1
- package/package.json +1 -1
- package/src/connection/generator.d.ts +1 -1
- package/src/connection/generator.js +58 -42
- package/src/connection/generator.js.map +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +7 -7
- package/src/infra/app/generator.js +4 -4
- package/src/infra/app/generator.js.map +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +3 -1
- package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +1486 -0
- package/src/py/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +45 -0
- package/src/py/dynamodb/agent-connection/generator.d.ts +10 -0
- package/src/py/dynamodb/agent-connection/generator.js +39 -0
- package/src/py/dynamodb/agent-connection/generator.js.map +1 -0
- package/src/py/dynamodb/agent-connection/schema.d.ts +14 -0
- package/src/py/dynamodb/agent-connection/schema.json +22 -0
- package/src/py/dynamodb/fast-api-connection/__snapshots__/generator.spec.ts.snap +23 -0
- package/src/py/dynamodb/fast-api-connection/generator.d.ts +10 -0
- package/src/py/dynamodb/fast-api-connection/generator.js +37 -0
- package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -0
- package/src/py/dynamodb/fast-api-connection/schema.d.ts +12 -0
- package/src/py/dynamodb/fast-api-connection/schema.json +18 -0
- package/src/py/dynamodb/files/__name__/__init__.py.template +6 -0
- package/src/py/dynamodb/files/__name__/client.py.template +39 -0
- package/src/py/dynamodb/files/__name__/entities/__init__.py.template +4 -0
- package/src/py/dynamodb/files/__name__/entities/base.py.template +63 -0
- package/src/py/dynamodb/files/__name__/entities/example.py.template +64 -0
- package/src/py/dynamodb/files/config.json.template +24 -0
- package/src/py/dynamodb/generator.d.ts +10 -0
- package/src/py/dynamodb/generator.js +106 -0
- package/src/py/dynamodb/generator.js.map +1 -0
- package/src/py/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +45 -0
- package/src/py/dynamodb/mcp-server-connection/generator.d.ts +10 -0
- package/src/py/dynamodb/mcp-server-connection/generator.js +39 -0
- package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -0
- package/src/py/dynamodb/mcp-server-connection/schema.d.ts +14 -0
- package/src/py/dynamodb/mcp-server-connection/schema.json +22 -0
- package/src/py/dynamodb/schema.d.ts +15 -0
- package/src/py/dynamodb/schema.json +70 -0
- package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +303 -512
- package/src/ts/dynamodb/files/config.json.template +24 -0
- package/src/ts/dynamodb/files/src/client.ts.template +26 -10
- package/src/ts/dynamodb/files/src/index.ts.template +0 -8
- package/src/ts/dynamodb/generator.js +15 -12
- package/src/ts/dynamodb/generator.js.map +1 -1
- package/src/ts/dynamodb/schema.d.ts +1 -0
- package/src/ts/dynamodb/schema.json +20 -2
- package/src/utils/dynamodb-constructs/dynamodb-constructs.d.ts +0 -1
- package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
- package/src/utils/dynamodb-constructs/files/cdk/app/dynamodb/__nameKebabCase__.ts.template +12 -13
- package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +3 -3
- package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/create-local-table.ts.template +63 -36
- package/src/utils/files/common/scripts/src/dynamodb/pull-image.ts.template +13 -0
- package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/start-container.ts.template +28 -18
- package/src/utils/port.d.ts +6 -5
- package/src/utils/port.js +7 -5
- package/src/utils/port.js.map +1 -1
- package/src/utils/shared-constructs-constants.d.ts +1 -0
- package/src/utils/shared-constructs-constants.js +2 -1
- package/src/utils/shared-constructs-constants.js.map +1 -1
- package/src/utils/shared-dynamodb-scripts.d.ts +11 -0
- package/src/utils/shared-dynamodb-scripts.js +23 -0
- package/src/utils/shared-dynamodb-scripts.js.map +1 -0
- package/src/utils/shared-infra-scripts.d.ts +11 -0
- package/src/utils/shared-infra-scripts.js +35 -0
- package/src/utils/shared-infra-scripts.js.map +1 -0
- package/src/utils/shared-scripts.d.ts +1 -4
- package/src/utils/shared-scripts.js +2 -23
- package/src/utils/shared-scripts.js.map +1 -1
- package/src/utils/versions.d.ts +1 -0
- package/src/utils/versions.js +1 -0
- package/src/utils/versions.js.map +1 -1
- package/src/ts/dynamodb/files/dynamodb.config.json +0 -14
- package/src/ts/dynamodb/files/scripts/pull-image.ts.template +0 -15
- package/src/ts/dynamodb/files/src/constants.ts.template +0 -9
- /package/src/utils/files/common/scripts/src/{index.ts.template → infra/index.ts.template} +0 -0
- /package/src/utils/files/common/scripts/src/{infra-deploy.ts.template → infra/infra-deploy.ts.template} +0 -0
- /package/src/utils/files/common/scripts/src/{infra-destroy.ts.template → infra/infra-destroy.ts.template} +0 -0
- /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/cdk-command.ts.template +0 -0
- /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/credentials.ts.template +0 -0
- /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/run.ts.template +0 -0
- /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/stage-parser.ts.template +0 -0
|
@@ -1,296 +1,33 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`ts#dynamodb generator > should generate scripts for finch engine
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import { readFileSync } from 'fs';
|
|
13
|
-
import { join, dirname } from 'path';
|
|
14
|
-
import { fileURLToPath } from 'url';
|
|
15
|
-
|
|
16
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
-
const { globalSecondaryIndexes } = JSON.parse(
|
|
18
|
-
readFileSync(join(__dirname, '../dynamodb.config.json'), 'utf-8'),
|
|
19
|
-
) as {
|
|
20
|
-
globalSecondaryIndexes: {
|
|
21
|
-
indexName: string;
|
|
22
|
-
partitionKey: string;
|
|
23
|
-
sortKey?: string;
|
|
24
|
-
}[];
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const [tableName, port] = process.argv.slice(2);
|
|
28
|
-
const endpoint = \`http://localhost:\${port}\`;
|
|
29
|
-
|
|
30
|
-
const REQUEST_TIMEOUT_MS = 10_000;
|
|
31
|
-
const timeoutSignal = () => AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
32
|
-
|
|
33
|
-
const client = new DynamoDBClient({
|
|
34
|
-
endpoint,
|
|
35
|
-
region: 'us-east-1',
|
|
36
|
-
credentials: {
|
|
37
|
-
accessKeyId: 'accessKeyId',
|
|
38
|
-
secretAccessKey: 'secretAccessKey',
|
|
3
|
+
exports[`ts#dynamodb generator > should generate scripts for finch engine 1`] = `
|
|
4
|
+
"{
|
|
5
|
+
"runtimeConfigKey": "MyTable",
|
|
6
|
+
"serveLocal": {
|
|
7
|
+
"port": 8000,
|
|
8
|
+
"tableName": "proj-my-table",
|
|
9
|
+
"image": "public.ecr.aws/aws-dynamodb-local/aws-dynamodb-local:latest",
|
|
10
|
+
"containerName": "proj-dynamodb",
|
|
11
|
+
"containerEngine": "finch"
|
|
39
12
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const allActive =
|
|
52
|
-
Table?.TableStatus === 'ACTIVE' &&
|
|
53
|
-
(Table?.GlobalSecondaryIndexes ?? []).every(
|
|
54
|
-
(g: GlobalSecondaryIndexDescription) => g.IndexStatus === 'ACTIVE',
|
|
55
|
-
);
|
|
56
|
-
if (allActive) return;
|
|
57
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const ensureTable = async (
|
|
62
|
-
tblClient: DynamoDBClient,
|
|
63
|
-
tblName: string,
|
|
64
|
-
gsIs: { indexName: string; partitionKey: string; sortKey?: string }[],
|
|
65
|
-
): Promise<void> => {
|
|
66
|
-
const gsiAttributeDefinitions = gsIs.flatMap((gsi) => [
|
|
67
|
-
{ AttributeName: gsi.partitionKey, AttributeType: 'S' as const },
|
|
68
|
-
...(gsi.sortKey
|
|
69
|
-
? [{ AttributeName: gsi.sortKey, AttributeType: 'S' as const }]
|
|
70
|
-
: []),
|
|
71
|
-
]);
|
|
72
|
-
const attributeDefinitions = [
|
|
73
|
-
{ AttributeName: 'pk', AttributeType: 'S' as const },
|
|
74
|
-
{ AttributeName: 'sk', AttributeType: 'S' as const },
|
|
75
|
-
...gsiAttributeDefinitions.filter(
|
|
76
|
-
(a, i, arr) =>
|
|
77
|
-
arr.findIndex((b) => b.AttributeName === a.AttributeName) === i,
|
|
78
|
-
),
|
|
79
|
-
];
|
|
80
|
-
const globalSecondaryIndexes = gsIs.map((gsi) => ({
|
|
81
|
-
IndexName: gsi.indexName,
|
|
82
|
-
KeySchema: [
|
|
83
|
-
{ AttributeName: gsi.partitionKey, KeyType: 'HASH' as const },
|
|
84
|
-
...(gsi.sortKey
|
|
85
|
-
? [{ AttributeName: gsi.sortKey, KeyType: 'RANGE' as const }]
|
|
86
|
-
: []),
|
|
87
|
-
],
|
|
88
|
-
Projection: { ProjectionType: 'ALL' as const },
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
|
-
for (let attempt = 0; attempt < 30; attempt++) {
|
|
92
|
-
try {
|
|
93
|
-
// New table: create with all GSIs in one shot.
|
|
94
|
-
await tblClient.send(
|
|
95
|
-
new CreateTableCommand({
|
|
96
|
-
TableName: tblName,
|
|
97
|
-
AttributeDefinitions: attributeDefinitions,
|
|
98
|
-
KeySchema: [
|
|
99
|
-
{ AttributeName: 'pk', KeyType: 'HASH' },
|
|
100
|
-
{ AttributeName: 'sk', KeyType: 'RANGE' },
|
|
101
|
-
],
|
|
102
|
-
BillingMode: 'PAY_PER_REQUEST',
|
|
103
|
-
...(globalSecondaryIndexes.length > 0 && {
|
|
104
|
-
GlobalSecondaryIndexes: globalSecondaryIndexes,
|
|
105
|
-
}),
|
|
106
|
-
}),
|
|
107
|
-
{ abortSignal: timeoutSignal() },
|
|
108
|
-
);
|
|
109
|
-
console.log(\`Created table: \${tblName}\`);
|
|
110
|
-
for (const gsi of globalSecondaryIndexes) {
|
|
111
|
-
console.log(\`Created GSI: \${gsi.IndexName}\`);
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
} catch (e) {
|
|
115
|
-
// Table already exists: sync GSIs to match desired state.
|
|
116
|
-
if (e instanceof ResourceInUseException) {
|
|
117
|
-
const { Table } = await tblClient.send(
|
|
118
|
-
new DescribeTableCommand({ TableName: tblName }),
|
|
119
|
-
{ abortSignal: timeoutSignal() },
|
|
120
|
-
);
|
|
121
|
-
const existingGSIs = Table?.GlobalSecondaryIndexes ?? [];
|
|
122
|
-
const existingNames = new Set(
|
|
123
|
-
existingGSIs.map(
|
|
124
|
-
(g: GlobalSecondaryIndexDescription) => g.IndexName ?? '',
|
|
125
|
-
),
|
|
126
|
-
);
|
|
127
|
-
const desiredNames = new Set(gsIs.map((g) => g.indexName));
|
|
128
|
-
|
|
129
|
-
// DynamoDB Local only allows one GSI to be created or deleted at a time.
|
|
130
|
-
for (const existing of existingGSIs) {
|
|
131
|
-
if (!existing.IndexName || desiredNames.has(existing.IndexName))
|
|
132
|
-
continue;
|
|
133
|
-
await tblClient.send(
|
|
134
|
-
new UpdateTableCommand({
|
|
135
|
-
TableName: tblName,
|
|
136
|
-
GlobalSecondaryIndexUpdates: [
|
|
137
|
-
{ Delete: { IndexName: existing.IndexName } },
|
|
138
|
-
],
|
|
139
|
-
}),
|
|
140
|
-
{ abortSignal: timeoutSignal() },
|
|
141
|
-
);
|
|
142
|
-
console.log(\`Deleted GSI: \${existing.IndexName}\`);
|
|
143
|
-
await waitForTableActive(tblClient, tblName);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
for (const gsi of gsIs) {
|
|
147
|
-
if (existingNames.has(gsi.indexName)) continue;
|
|
148
|
-
await tblClient.send(
|
|
149
|
-
new UpdateTableCommand({
|
|
150
|
-
TableName: tblName,
|
|
151
|
-
AttributeDefinitions: [
|
|
152
|
-
{ AttributeName: gsi.partitionKey, AttributeType: 'S' },
|
|
153
|
-
...(gsi.sortKey
|
|
154
|
-
? [
|
|
155
|
-
{
|
|
156
|
-
AttributeName: gsi.sortKey,
|
|
157
|
-
AttributeType: 'S' as const,
|
|
158
|
-
},
|
|
159
|
-
]
|
|
160
|
-
: []),
|
|
161
|
-
],
|
|
162
|
-
GlobalSecondaryIndexUpdates: [
|
|
163
|
-
{
|
|
164
|
-
Create: {
|
|
165
|
-
IndexName: gsi.indexName,
|
|
166
|
-
KeySchema: [
|
|
167
|
-
{ AttributeName: gsi.partitionKey, KeyType: 'HASH' },
|
|
168
|
-
...(gsi.sortKey
|
|
169
|
-
? [{ AttributeName: gsi.sortKey, KeyType: 'RANGE' }]
|
|
170
|
-
: []),
|
|
171
|
-
],
|
|
172
|
-
Projection: { ProjectionType: 'ALL' },
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
}),
|
|
177
|
-
{ abortSignal: timeoutSignal() },
|
|
178
|
-
);
|
|
179
|
-
console.log(\`Created GSI: \${gsi.indexName}\`);
|
|
180
|
-
await waitForTableActive(tblClient, tblName);
|
|
181
|
-
}
|
|
182
|
-
return;
|
|
13
|
+
"tableConfig": {
|
|
14
|
+
"globalSecondaryIndexes": [
|
|
15
|
+
{
|
|
16
|
+
"indexName": "gsi1pk-gsi1sk-index",
|
|
17
|
+
"partitionKey": "gsi1pk",
|
|
18
|
+
"sortKey": "gsi1sk"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"indexName": "gsi2pk-gsi2sk-index",
|
|
22
|
+
"partitionKey": "gsi2pk",
|
|
23
|
+
"sortKey": "gsi2sk"
|
|
183
24
|
}
|
|
184
|
-
|
|
185
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
await ensureTable(client, tableName, globalSecondaryIndexes);
|
|
191
|
-
"
|
|
192
|
-
`;
|
|
193
|
-
|
|
194
|
-
exports[`ts#dynamodb generator > should generate scripts for finch engine > packages/my-table/scripts/pull-image.ts 1`] = `
|
|
195
|
-
"import { spawnSync } from 'child_process';
|
|
196
|
-
|
|
197
|
-
const ENGINE = 'finch';
|
|
198
|
-
const image = process.argv[2];
|
|
199
|
-
|
|
200
|
-
const inspect = spawnSync(ENGINE, ['image', 'inspect', image], {
|
|
201
|
-
stdio: 'ignore',
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
if (inspect.status !== 0) {
|
|
205
|
-
const pull = spawnSync(ENGINE, ['pull', image], { stdio: 'inherit' });
|
|
206
|
-
if (pull.status !== 0) {
|
|
207
|
-
process.exit(pull.status ?? 1);
|
|
25
|
+
]
|
|
208
26
|
}
|
|
209
27
|
}
|
|
210
28
|
"
|
|
211
29
|
`;
|
|
212
30
|
|
|
213
|
-
exports[`ts#dynamodb generator > should generate scripts for finch engine > packages/my-table/scripts/start-container.ts 1`] = `
|
|
214
|
-
"import { spawn, spawnSync } from 'child_process';
|
|
215
|
-
|
|
216
|
-
const ENGINE = 'finch';
|
|
217
|
-
|
|
218
|
-
const [containerName, image, hostPort] = process.argv.slice(2);
|
|
219
|
-
|
|
220
|
-
const containerExists = (): boolean =>
|
|
221
|
-
spawnSync(ENGINE, ['container', 'inspect', containerName], {
|
|
222
|
-
stdio: 'ignore',
|
|
223
|
-
}).status === 0;
|
|
224
|
-
|
|
225
|
-
const isRunning = (): boolean => {
|
|
226
|
-
const result = spawnSync(
|
|
227
|
-
ENGINE,
|
|
228
|
-
['container', 'inspect', '-f', '{{.State.Running}}', containerName],
|
|
229
|
-
{ encoding: 'utf-8' },
|
|
230
|
-
);
|
|
231
|
-
return result.status === 0 && result.stdout.trim() === 'true';
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// Multiple dynamo table packages run serve-local in parallel sharing the same
|
|
235
|
-
// container. All processes stream logs, but only the one that created the
|
|
236
|
-
// container stops it on exit — others just detach.
|
|
237
|
-
let created = false;
|
|
238
|
-
let runRetries = 0;
|
|
239
|
-
|
|
240
|
-
for (;;) {
|
|
241
|
-
if (containerExists()) {
|
|
242
|
-
if (!isRunning()) {
|
|
243
|
-
// Another process may have started it between our inspect and start — container engine start
|
|
244
|
-
// exits 0 even if the container is already running, so no special-casing needed.
|
|
245
|
-
const start = spawnSync(ENGINE, ['start', containerName], {
|
|
246
|
-
stdio: 'inherit',
|
|
247
|
-
});
|
|
248
|
-
if (start.status !== 0) process.exit(start.status ?? 1);
|
|
249
|
-
}
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
const runArgs = (
|
|
253
|
-
\`run --name \${containerName} -u root -w /home/dynamodblocal\` +
|
|
254
|
-
\` -p \${hostPort}:\${hostPort} -v \${containerName}-data:/home/dynamodblocal/data\` +
|
|
255
|
-
\` -d \${image} -jar DynamoDBLocal.jar -sharedDb -dbPath ./data -port \${hostPort} -optimizeDbBeforeStartup\`
|
|
256
|
-
).split(' ');
|
|
257
|
-
const create = spawnSync(ENGINE, runArgs, { stdio: 'pipe' });
|
|
258
|
-
if (create.status === 0) {
|
|
259
|
-
created = true;
|
|
260
|
-
break;
|
|
261
|
-
}
|
|
262
|
-
if (!containerExists() && ++runRetries > 3) {
|
|
263
|
-
process.stderr.write(create.stderr);
|
|
264
|
-
process.exit(create.status ?? 1);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const logs = spawn(ENGINE, ['logs', '-f', containerName], {
|
|
269
|
-
stdio: ['ignore', 'inherit', 'inherit'],
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
let exiting = false;
|
|
273
|
-
|
|
274
|
-
const cleanup = () => {
|
|
275
|
-
if (exiting) return;
|
|
276
|
-
exiting = true;
|
|
277
|
-
if (created) {
|
|
278
|
-
spawnSync(ENGINE, ['rm', '-f', containerName], { stdio: 'ignore' });
|
|
279
|
-
}
|
|
280
|
-
logs.kill();
|
|
281
|
-
process.exit(0);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
process.on('SIGTERM', cleanup);
|
|
285
|
-
process.on('SIGINT', cleanup);
|
|
286
|
-
process.on('SIGHUP', cleanup);
|
|
287
|
-
|
|
288
|
-
logs.on('exit', (code) => {
|
|
289
|
-
if (!exiting) process.exit(code ?? 0);
|
|
290
|
-
});
|
|
291
|
-
"
|
|
292
|
-
`;
|
|
293
|
-
|
|
294
31
|
exports[`ts#dynamodb generator > should generate terraform modules when iac is terraform 1`] = `
|
|
295
32
|
"terraform {
|
|
296
33
|
required_version = ">= 1.0"
|
|
@@ -485,8 +222,8 @@ variable "enable_key_rotation" {
|
|
|
485
222
|
}
|
|
486
223
|
|
|
487
224
|
locals {
|
|
488
|
-
dynamodb_config = jsondecode(file("\${path.module}/../../../../../../../packages/my-table/
|
|
489
|
-
global_secondary_indexes = [for gsi in local.dynamodb_config.globalSecondaryIndexes : {
|
|
225
|
+
dynamodb_config = jsondecode(file("\${path.module}/../../../../../../../packages/my-table/config.json"))
|
|
226
|
+
global_secondary_indexes = [for gsi in local.dynamodb_config.tableConfig.globalSecondaryIndexes : {
|
|
490
227
|
name = gsi.indexName
|
|
491
228
|
hash_key = gsi.partitionKey
|
|
492
229
|
range_key = try(gsi.sortKey, null)
|
|
@@ -508,7 +245,7 @@ module "add_to_runtime_config" {
|
|
|
508
245
|
source = "../../../core/runtime-config/entry"
|
|
509
246
|
|
|
510
247
|
namespace = "dynamodb"
|
|
511
|
-
key =
|
|
248
|
+
key = local.dynamodb_config.runtimeConfigKey
|
|
512
249
|
value = {
|
|
513
250
|
tableName = module.dynamodb_table.table_name
|
|
514
251
|
}
|
|
@@ -516,43 +253,43 @@ module "add_to_runtime_config" {
|
|
|
516
253
|
"
|
|
517
254
|
`;
|
|
518
255
|
|
|
519
|
-
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/
|
|
256
|
+
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/common/scripts/src/dynamodb/create-local-table.ts 1`] = `
|
|
520
257
|
"import {
|
|
258
|
+
BillingMode,
|
|
521
259
|
CreateTableCommand,
|
|
522
260
|
DescribeTableCommand,
|
|
523
261
|
DynamoDBClient,
|
|
524
262
|
GlobalSecondaryIndexDescription,
|
|
263
|
+
IndexStatus,
|
|
264
|
+
KeyType,
|
|
265
|
+
ProjectionType,
|
|
525
266
|
ResourceInUseException,
|
|
267
|
+
ScalarAttributeType,
|
|
268
|
+
TableStatus,
|
|
526
269
|
UpdateTableCommand,
|
|
527
270
|
} from '@aws-sdk/client-dynamodb';
|
|
528
271
|
import { readFileSync } from 'fs';
|
|
529
|
-
import { join, dirname } from 'path';
|
|
530
|
-
import { fileURLToPath } from 'url';
|
|
531
272
|
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
) as {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
273
|
+
const {
|
|
274
|
+
serveLocal: { port, tableName },
|
|
275
|
+
tableConfig: { globalSecondaryIndexes },
|
|
276
|
+
} = JSON.parse(readFileSync('config.json', 'utf-8')) as {
|
|
277
|
+
serveLocal: { port: number; tableName: string };
|
|
278
|
+
tableConfig: {
|
|
279
|
+
globalSecondaryIndexes: {
|
|
280
|
+
indexName: string;
|
|
281
|
+
partitionKey: string;
|
|
282
|
+
sortKey?: string;
|
|
283
|
+
}[];
|
|
284
|
+
};
|
|
541
285
|
};
|
|
542
286
|
|
|
543
|
-
const
|
|
544
|
-
const endpoint = \`http://localhost:\${port}\`;
|
|
545
|
-
|
|
546
|
-
const REQUEST_TIMEOUT_MS = 10_000;
|
|
547
|
-
const timeoutSignal = () => AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
287
|
+
const timeoutSignal = () => AbortSignal.timeout(10000);
|
|
548
288
|
|
|
549
289
|
const client = new DynamoDBClient({
|
|
550
|
-
endpoint
|
|
290
|
+
endpoint: \`http://localhost:\${port}\`,
|
|
551
291
|
region: 'us-east-1',
|
|
552
|
-
credentials: {
|
|
553
|
-
accessKeyId: 'accessKeyId',
|
|
554
|
-
secretAccessKey: 'secretAccessKey',
|
|
555
|
-
},
|
|
292
|
+
credentials: { accessKeyId: 'UNUSED', secretAccessKey: 'UNUSED' },
|
|
556
293
|
});
|
|
557
294
|
|
|
558
295
|
const waitForTableActive = async (
|
|
@@ -565,9 +302,10 @@ const waitForTableActive = async (
|
|
|
565
302
|
{ abortSignal: timeoutSignal() },
|
|
566
303
|
);
|
|
567
304
|
const allActive =
|
|
568
|
-
Table?.TableStatus ===
|
|
305
|
+
Table?.TableStatus === TableStatus.ACTIVE &&
|
|
569
306
|
(Table?.GlobalSecondaryIndexes ?? []).every(
|
|
570
|
-
(g: GlobalSecondaryIndexDescription) =>
|
|
307
|
+
(g: GlobalSecondaryIndexDescription) =>
|
|
308
|
+
g.IndexStatus === IndexStatus.ACTIVE,
|
|
571
309
|
);
|
|
572
310
|
if (allActive) return;
|
|
573
311
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -580,14 +318,14 @@ const ensureTable = async (
|
|
|
580
318
|
gsIs: { indexName: string; partitionKey: string; sortKey?: string }[],
|
|
581
319
|
): Promise<void> => {
|
|
582
320
|
const gsiAttributeDefinitions = gsIs.flatMap((gsi) => [
|
|
583
|
-
{ AttributeName: gsi.partitionKey, AttributeType:
|
|
321
|
+
{ AttributeName: gsi.partitionKey, AttributeType: ScalarAttributeType.S },
|
|
584
322
|
...(gsi.sortKey
|
|
585
|
-
? [{ AttributeName: gsi.sortKey, AttributeType:
|
|
323
|
+
? [{ AttributeName: gsi.sortKey, AttributeType: ScalarAttributeType.S }]
|
|
586
324
|
: []),
|
|
587
325
|
]);
|
|
588
326
|
const attributeDefinitions = [
|
|
589
|
-
{ AttributeName: 'pk', AttributeType:
|
|
590
|
-
{ AttributeName: 'sk', AttributeType:
|
|
327
|
+
{ AttributeName: 'pk', AttributeType: ScalarAttributeType.S },
|
|
328
|
+
{ AttributeName: 'sk', AttributeType: ScalarAttributeType.S },
|
|
591
329
|
...gsiAttributeDefinitions.filter(
|
|
592
330
|
(a, i, arr) =>
|
|
593
331
|
arr.findIndex((b) => b.AttributeName === a.AttributeName) === i,
|
|
@@ -596,12 +334,12 @@ const ensureTable = async (
|
|
|
596
334
|
const globalSecondaryIndexes = gsIs.map((gsi) => ({
|
|
597
335
|
IndexName: gsi.indexName,
|
|
598
336
|
KeySchema: [
|
|
599
|
-
{ AttributeName: gsi.partitionKey, KeyType:
|
|
337
|
+
{ AttributeName: gsi.partitionKey, KeyType: KeyType.HASH },
|
|
600
338
|
...(gsi.sortKey
|
|
601
|
-
? [{ AttributeName: gsi.sortKey, KeyType:
|
|
339
|
+
? [{ AttributeName: gsi.sortKey, KeyType: KeyType.RANGE }]
|
|
602
340
|
: []),
|
|
603
341
|
],
|
|
604
|
-
Projection: { ProjectionType:
|
|
342
|
+
Projection: { ProjectionType: ProjectionType.ALL },
|
|
605
343
|
}));
|
|
606
344
|
|
|
607
345
|
for (let attempt = 0; attempt < 30; attempt++) {
|
|
@@ -612,10 +350,10 @@ const ensureTable = async (
|
|
|
612
350
|
TableName: tblName,
|
|
613
351
|
AttributeDefinitions: attributeDefinitions,
|
|
614
352
|
KeySchema: [
|
|
615
|
-
{ AttributeName: 'pk', KeyType:
|
|
616
|
-
{ AttributeName: 'sk', KeyType:
|
|
353
|
+
{ AttributeName: 'pk', KeyType: KeyType.HASH },
|
|
354
|
+
{ AttributeName: 'sk', KeyType: KeyType.RANGE },
|
|
617
355
|
],
|
|
618
|
-
BillingMode:
|
|
356
|
+
BillingMode: BillingMode.PAY_PER_REQUEST,
|
|
619
357
|
...(globalSecondaryIndexes.length > 0 && {
|
|
620
358
|
GlobalSecondaryIndexes: globalSecondaryIndexes,
|
|
621
359
|
}),
|
|
@@ -665,12 +403,15 @@ const ensureTable = async (
|
|
|
665
403
|
new UpdateTableCommand({
|
|
666
404
|
TableName: tblName,
|
|
667
405
|
AttributeDefinitions: [
|
|
668
|
-
{
|
|
406
|
+
{
|
|
407
|
+
AttributeName: gsi.partitionKey,
|
|
408
|
+
AttributeType: ScalarAttributeType.S,
|
|
409
|
+
},
|
|
669
410
|
...(gsi.sortKey
|
|
670
411
|
? [
|
|
671
412
|
{
|
|
672
413
|
AttributeName: gsi.sortKey,
|
|
673
|
-
AttributeType:
|
|
414
|
+
AttributeType: ScalarAttributeType.S,
|
|
674
415
|
},
|
|
675
416
|
]
|
|
676
417
|
: []),
|
|
@@ -680,12 +421,20 @@ const ensureTable = async (
|
|
|
680
421
|
Create: {
|
|
681
422
|
IndexName: gsi.indexName,
|
|
682
423
|
KeySchema: [
|
|
683
|
-
{
|
|
424
|
+
{
|
|
425
|
+
AttributeName: gsi.partitionKey,
|
|
426
|
+
KeyType: KeyType.HASH,
|
|
427
|
+
},
|
|
684
428
|
...(gsi.sortKey
|
|
685
|
-
? [
|
|
429
|
+
? [
|
|
430
|
+
{
|
|
431
|
+
AttributeName: gsi.sortKey,
|
|
432
|
+
KeyType: KeyType.RANGE,
|
|
433
|
+
},
|
|
434
|
+
]
|
|
686
435
|
: []),
|
|
687
436
|
],
|
|
688
|
-
Projection: { ProjectionType:
|
|
437
|
+
Projection: { ProjectionType: ProjectionType.ALL },
|
|
689
438
|
},
|
|
690
439
|
},
|
|
691
440
|
],
|
|
@@ -707,18 +456,21 @@ await ensureTable(client, tableName, globalSecondaryIndexes);
|
|
|
707
456
|
"
|
|
708
457
|
`;
|
|
709
458
|
|
|
710
|
-
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/
|
|
459
|
+
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/common/scripts/src/dynamodb/pull-image.ts 1`] = `
|
|
711
460
|
"import { spawnSync } from 'child_process';
|
|
461
|
+
import { readFileSync } from 'fs';
|
|
712
462
|
|
|
713
|
-
const
|
|
714
|
-
|
|
463
|
+
const { containerEngine, image } = JSON.parse(
|
|
464
|
+
readFileSync('config.json', 'utf-8'),
|
|
465
|
+
).serveLocal;
|
|
715
466
|
|
|
716
|
-
const inspect = spawnSync(
|
|
467
|
+
const inspect = spawnSync(containerEngine, ['image', 'inspect', image], {
|
|
717
468
|
stdio: 'ignore',
|
|
718
469
|
});
|
|
719
|
-
|
|
720
470
|
if (inspect.status !== 0) {
|
|
721
|
-
const pull = spawnSync(
|
|
471
|
+
const pull = spawnSync(containerEngine, ['pull', image], {
|
|
472
|
+
stdio: 'inherit',
|
|
473
|
+
});
|
|
722
474
|
if (pull.status !== 0) {
|
|
723
475
|
process.exit(pull.status ?? 1);
|
|
724
476
|
}
|
|
@@ -726,21 +478,22 @@ if (inspect.status !== 0) {
|
|
|
726
478
|
"
|
|
727
479
|
`;
|
|
728
480
|
|
|
729
|
-
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/
|
|
481
|
+
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/common/scripts/src/dynamodb/start-container.ts 1`] = `
|
|
730
482
|
"import { spawn, spawnSync } from 'child_process';
|
|
483
|
+
import { readFileSync } from 'fs';
|
|
731
484
|
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
485
|
+
const { containerEngine, containerName, image, port } = JSON.parse(
|
|
486
|
+
readFileSync('config.json', 'utf-8'),
|
|
487
|
+
).serveLocal;
|
|
735
488
|
|
|
736
489
|
const containerExists = (): boolean =>
|
|
737
|
-
spawnSync(
|
|
490
|
+
spawnSync(containerEngine, ['container', 'inspect', containerName], {
|
|
738
491
|
stdio: 'ignore',
|
|
739
492
|
}).status === 0;
|
|
740
493
|
|
|
741
494
|
const isRunning = (): boolean => {
|
|
742
495
|
const result = spawnSync(
|
|
743
|
-
|
|
496
|
+
containerEngine,
|
|
744
497
|
['container', 'inspect', '-f', '{{.State.Running}}', containerName],
|
|
745
498
|
{ encoding: 'utf-8' },
|
|
746
499
|
);
|
|
@@ -758,19 +511,38 @@ for (;;) {
|
|
|
758
511
|
if (!isRunning()) {
|
|
759
512
|
// Another process may have started it between our inspect and start — container engine start
|
|
760
513
|
// exits 0 even if the container is already running, so no special-casing needed.
|
|
761
|
-
const start = spawnSync(
|
|
514
|
+
const start = spawnSync(containerEngine, ['start', containerName], {
|
|
762
515
|
stdio: 'inherit',
|
|
763
516
|
});
|
|
764
517
|
if (start.status !== 0) process.exit(start.status ?? 1);
|
|
765
518
|
}
|
|
766
519
|
break;
|
|
767
520
|
}
|
|
768
|
-
const runArgs =
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
521
|
+
const runArgs = [
|
|
522
|
+
'run',
|
|
523
|
+
...(containerEngine === 'docker' ? ['--rm'] : []),
|
|
524
|
+
'--name',
|
|
525
|
+
containerName,
|
|
526
|
+
'-u',
|
|
527
|
+
'root',
|
|
528
|
+
'-w',
|
|
529
|
+
'/home/dynamodblocal',
|
|
530
|
+
\`-p\`,
|
|
531
|
+
\`\${port}:\${port}\`,
|
|
532
|
+
'-v',
|
|
533
|
+
\`\${containerName}-data:/home/dynamodblocal/data\`,
|
|
534
|
+
'-d',
|
|
535
|
+
image,
|
|
536
|
+
'-jar',
|
|
537
|
+
'DynamoDBLocal.jar',
|
|
538
|
+
'-sharedDb',
|
|
539
|
+
'-dbPath',
|
|
540
|
+
'./data',
|
|
541
|
+
'-port',
|
|
542
|
+
\`\${port}\`,
|
|
543
|
+
'-optimizeDbBeforeStartup',
|
|
544
|
+
];
|
|
545
|
+
const create = spawnSync(containerEngine, runArgs, { stdio: 'pipe' });
|
|
774
546
|
if (create.status === 0) {
|
|
775
547
|
created = true;
|
|
776
548
|
break;
|
|
@@ -781,7 +553,7 @@ for (;;) {
|
|
|
781
553
|
}
|
|
782
554
|
}
|
|
783
555
|
|
|
784
|
-
const logs = spawn(
|
|
556
|
+
const logs = spawn(containerEngine, ['logs', '-f', containerName], {
|
|
785
557
|
stdio: ['ignore', 'inherit', 'inherit'],
|
|
786
558
|
});
|
|
787
559
|
|
|
@@ -791,7 +563,13 @@ const cleanup = () => {
|
|
|
791
563
|
if (exiting) return;
|
|
792
564
|
exiting = true;
|
|
793
565
|
if (created) {
|
|
794
|
-
|
|
566
|
+
if (containerEngine === 'docker') {
|
|
567
|
+
spawnSync(containerEngine, ['stop', containerName], { stdio: 'ignore' });
|
|
568
|
+
} else {
|
|
569
|
+
spawnSync(containerEngine, ['rm', '-f', containerName], {
|
|
570
|
+
stdio: 'ignore',
|
|
571
|
+
});
|
|
572
|
+
}
|
|
795
573
|
}
|
|
796
574
|
logs.kill();
|
|
797
575
|
process.exit(0);
|
|
@@ -810,29 +588,34 @@ logs.on('exit', (code) => {
|
|
|
810
588
|
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/my-table/src/client.ts 1`] = `
|
|
811
589
|
"import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
812
590
|
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
813
|
-
import {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
591
|
+
import { readFileSync } from 'fs';
|
|
592
|
+
import { join, dirname } from 'path';
|
|
593
|
+
import { fileURLToPath } from 'url';
|
|
594
|
+
|
|
595
|
+
// Generated: must match runtimeConfigKey in config.json
|
|
596
|
+
const runtimeConfigKey = 'MyTable';
|
|
597
|
+
|
|
598
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
599
|
+
// config.json is only present in local dev (not included in the deployed bundle); guard with SERVE_LOCAL check
|
|
600
|
+
const serveLocal = () =>
|
|
601
|
+
JSON.parse(readFileSync(join(__dirname, '../config.json'), 'utf-8'))
|
|
602
|
+
.serveLocal;
|
|
821
603
|
|
|
822
604
|
let _client: DynamoDBClient | undefined;
|
|
823
605
|
|
|
824
606
|
export const getDynamoDBClient = (): DynamoDBClient => {
|
|
825
|
-
_client
|
|
826
|
-
process.env.SERVE_LOCAL === 'true'
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
607
|
+
if (!_client) {
|
|
608
|
+
if (process.env.SERVE_LOCAL === 'true') {
|
|
609
|
+
const { port } = serveLocal();
|
|
610
|
+
_client = new DynamoDBClient({
|
|
611
|
+
endpoint: \`http://localhost:\${port}\`,
|
|
612
|
+
region: 'us-east-1',
|
|
613
|
+
credentials: { accessKeyId: 'UNUSED', secretAccessKey: 'UNUSED' },
|
|
614
|
+
});
|
|
615
|
+
} else {
|
|
616
|
+
_client = new DynamoDBClient({});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
836
619
|
return _client;
|
|
837
620
|
};
|
|
838
621
|
|
|
@@ -844,7 +627,8 @@ let _tableName: string | undefined;
|
|
|
844
627
|
|
|
845
628
|
export const resolveTableName = async (): Promise<string> => {
|
|
846
629
|
if (process.env.SERVE_LOCAL === 'true') {
|
|
847
|
-
|
|
630
|
+
const { tableName } = serveLocal();
|
|
631
|
+
return tableName;
|
|
848
632
|
}
|
|
849
633
|
if (_tableName === undefined) {
|
|
850
634
|
const appId = process.env.RUNTIME_CONFIG_APP_ID;
|
|
@@ -858,7 +642,7 @@ export const resolveTableName = async (): Promise<string> => {
|
|
|
858
642
|
environment: 'default',
|
|
859
643
|
transform: 'json',
|
|
860
644
|
});
|
|
861
|
-
const tableName = config?.[
|
|
645
|
+
const tableName = config?.[runtimeConfigKey]?.tableName;
|
|
862
646
|
if (!tableName) {
|
|
863
647
|
throw new Error('Could not resolve table name from runtime config');
|
|
864
648
|
}
|
|
@@ -869,19 +653,6 @@ export const resolveTableName = async (): Promise<string> => {
|
|
|
869
653
|
"
|
|
870
654
|
`;
|
|
871
655
|
|
|
872
|
-
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/my-table/src/constants.ts 1`] = `
|
|
873
|
-
"export const PACKAGE_NAME = 'MyTable';
|
|
874
|
-
|
|
875
|
-
// Local development connection details (used when SERVE_LOCAL=true, see serve-local Nx target)
|
|
876
|
-
export const LOCAL_PORT = 8000;
|
|
877
|
-
export const LOCAL_REGION = 'us-east-1';
|
|
878
|
-
export const LOCAL_ACCESS_KEY_ID = 'accessKeyId';
|
|
879
|
-
export const LOCAL_SECRET_ACCESS_KEY = 'secretAccessKey';
|
|
880
|
-
export const LOCAL_ENDPOINT = \`http://localhost:\${LOCAL_PORT}\`;
|
|
881
|
-
export const LOCAL_TABLE_NAME = 'proj-my-table';
|
|
882
|
-
"
|
|
883
|
-
`;
|
|
884
|
-
|
|
885
656
|
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/my-table/src/entities/example.ts 1`] = `
|
|
886
657
|
"import { Entity } from 'electrodb';
|
|
887
658
|
import { getDynamoDBClient, resolveTableName } from '../client.js';
|
|
@@ -939,15 +710,7 @@ exports[`ts#dynamodb generator > should generate the dynamodb project > packages
|
|
|
939
710
|
`;
|
|
940
711
|
|
|
941
712
|
exports[`ts#dynamodb generator > should generate the dynamodb project > packages/my-table/src/index.ts 1`] = `
|
|
942
|
-
"export {
|
|
943
|
-
PACKAGE_NAME,
|
|
944
|
-
LOCAL_ACCESS_KEY_ID,
|
|
945
|
-
LOCAL_ENDPOINT,
|
|
946
|
-
LOCAL_REGION,
|
|
947
|
-
LOCAL_SECRET_ACCESS_KEY,
|
|
948
|
-
LOCAL_TABLE_NAME,
|
|
949
|
-
} from './constants.js';
|
|
950
|
-
export { getDynamoDBClient, resolveTableName } from './client.js';
|
|
713
|
+
"export { getDynamoDBClient, resolveTableName } from './client.js';
|
|
951
714
|
export * from './entities/index.js';
|
|
952
715
|
"
|
|
953
716
|
`;
|
|
@@ -1066,7 +829,6 @@ export abstract class DynamoDBTable extends Construct {
|
|
|
1066
829
|
|
|
1067
830
|
exports[`ts#dynamodb generator > should generate the dynamodb project 2`] = `
|
|
1068
831
|
"import { Construct } from 'constructs';
|
|
1069
|
-
import { PACKAGE_NAME } from ':proj/my-table';
|
|
1070
832
|
import { AttributeType, ProjectionType } from 'aws-cdk-lib/aws-dynamodb';
|
|
1071
833
|
import { DynamoDBTable, DynamoDBTableProps } from '../../core/dynamodb.js';
|
|
1072
834
|
import { findWorkspaceRoot } from '../../core/workspace.js';
|
|
@@ -1074,32 +836,35 @@ import { readFileSync } from 'fs';
|
|
|
1074
836
|
import { join } from 'path';
|
|
1075
837
|
import { fileURLToPath } from 'url';
|
|
1076
838
|
|
|
839
|
+
const { runtimeConfigKey, tableConfig } = JSON.parse(
|
|
840
|
+
readFileSync(
|
|
841
|
+
join(
|
|
842
|
+
findWorkspaceRoot(fileURLToPath(new URL(import.meta.url))),
|
|
843
|
+
'packages/my-table/config.json',
|
|
844
|
+
),
|
|
845
|
+
'utf-8',
|
|
846
|
+
),
|
|
847
|
+
) as {
|
|
848
|
+
runtimeConfigKey: string;
|
|
849
|
+
tableConfig: {
|
|
850
|
+
globalSecondaryIndexes: {
|
|
851
|
+
indexName: string;
|
|
852
|
+
partitionKey: string;
|
|
853
|
+
sortKey?: string;
|
|
854
|
+
}[];
|
|
855
|
+
};
|
|
856
|
+
};
|
|
857
|
+
|
|
1077
858
|
export type MyTableProps = Omit<DynamoDBTableProps, 'runtimeConfigKey'>;
|
|
1078
859
|
|
|
1079
860
|
export class MyTable extends DynamoDBTable {
|
|
1080
861
|
constructor(scope: Construct, id: string, props?: MyTableProps) {
|
|
1081
862
|
super(scope, id, {
|
|
1082
863
|
...props,
|
|
1083
|
-
runtimeConfigKey
|
|
864
|
+
runtimeConfigKey,
|
|
1084
865
|
});
|
|
1085
866
|
|
|
1086
|
-
const
|
|
1087
|
-
readFileSync(
|
|
1088
|
-
join(
|
|
1089
|
-
findWorkspaceRoot(fileURLToPath(new URL(import.meta.url))),
|
|
1090
|
-
'packages/my-table/dynamodb.config.json',
|
|
1091
|
-
),
|
|
1092
|
-
'utf-8',
|
|
1093
|
-
),
|
|
1094
|
-
) as {
|
|
1095
|
-
globalSecondaryIndexes: {
|
|
1096
|
-
indexName: string;
|
|
1097
|
-
partitionKey: string;
|
|
1098
|
-
sortKey?: string;
|
|
1099
|
-
}[];
|
|
1100
|
-
};
|
|
1101
|
-
|
|
1102
|
-
for (const gsi of globalSecondaryIndexes) {
|
|
867
|
+
for (const gsi of tableConfig.globalSecondaryIndexes) {
|
|
1103
868
|
this.table.addGlobalSecondaryIndex({
|
|
1104
869
|
indexName: gsi.indexName,
|
|
1105
870
|
partitionKey: { name: gsi.partitionKey, type: AttributeType.STRING },
|
|
@@ -1124,43 +889,43 @@ exports[`ts#dynamodb generator > should generate the dynamodb project 4`] = `
|
|
|
1124
889
|
"
|
|
1125
890
|
`;
|
|
1126
891
|
|
|
1127
|
-
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/
|
|
892
|
+
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/common/scripts/src/dynamodb/create-local-table.ts 1`] = `
|
|
1128
893
|
"import {
|
|
894
|
+
BillingMode,
|
|
1129
895
|
CreateTableCommand,
|
|
1130
896
|
DescribeTableCommand,
|
|
1131
897
|
DynamoDBClient,
|
|
1132
898
|
GlobalSecondaryIndexDescription,
|
|
899
|
+
IndexStatus,
|
|
900
|
+
KeyType,
|
|
901
|
+
ProjectionType,
|
|
1133
902
|
ResourceInUseException,
|
|
903
|
+
ScalarAttributeType,
|
|
904
|
+
TableStatus,
|
|
1134
905
|
UpdateTableCommand,
|
|
1135
906
|
} from '@aws-sdk/client-dynamodb';
|
|
1136
907
|
import { readFileSync } from 'fs';
|
|
1137
|
-
import { join, dirname } from 'path';
|
|
1138
|
-
import { fileURLToPath } from 'url';
|
|
1139
908
|
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
) as {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
909
|
+
const {
|
|
910
|
+
serveLocal: { port, tableName },
|
|
911
|
+
tableConfig: { globalSecondaryIndexes },
|
|
912
|
+
} = JSON.parse(readFileSync('config.json', 'utf-8')) as {
|
|
913
|
+
serveLocal: { port: number; tableName: string };
|
|
914
|
+
tableConfig: {
|
|
915
|
+
globalSecondaryIndexes: {
|
|
916
|
+
indexName: string;
|
|
917
|
+
partitionKey: string;
|
|
918
|
+
sortKey?: string;
|
|
919
|
+
}[];
|
|
920
|
+
};
|
|
1149
921
|
};
|
|
1150
922
|
|
|
1151
|
-
const
|
|
1152
|
-
const endpoint = \`http://localhost:\${port}\`;
|
|
1153
|
-
|
|
1154
|
-
const REQUEST_TIMEOUT_MS = 10_000;
|
|
1155
|
-
const timeoutSignal = () => AbortSignal.timeout(REQUEST_TIMEOUT_MS);
|
|
923
|
+
const timeoutSignal = () => AbortSignal.timeout(10000);
|
|
1156
924
|
|
|
1157
925
|
const client = new DynamoDBClient({
|
|
1158
|
-
endpoint
|
|
926
|
+
endpoint: \`http://localhost:\${port}\`,
|
|
1159
927
|
region: 'us-east-1',
|
|
1160
|
-
credentials: {
|
|
1161
|
-
accessKeyId: 'accessKeyId',
|
|
1162
|
-
secretAccessKey: 'secretAccessKey',
|
|
1163
|
-
},
|
|
928
|
+
credentials: { accessKeyId: 'UNUSED', secretAccessKey: 'UNUSED' },
|
|
1164
929
|
});
|
|
1165
930
|
|
|
1166
931
|
const waitForTableActive = async (
|
|
@@ -1173,9 +938,10 @@ const waitForTableActive = async (
|
|
|
1173
938
|
{ abortSignal: timeoutSignal() },
|
|
1174
939
|
);
|
|
1175
940
|
const allActive =
|
|
1176
|
-
Table?.TableStatus ===
|
|
941
|
+
Table?.TableStatus === TableStatus.ACTIVE &&
|
|
1177
942
|
(Table?.GlobalSecondaryIndexes ?? []).every(
|
|
1178
|
-
(g: GlobalSecondaryIndexDescription) =>
|
|
943
|
+
(g: GlobalSecondaryIndexDescription) =>
|
|
944
|
+
g.IndexStatus === IndexStatus.ACTIVE,
|
|
1179
945
|
);
|
|
1180
946
|
if (allActive) return;
|
|
1181
947
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -1188,14 +954,14 @@ const ensureTable = async (
|
|
|
1188
954
|
gsIs: { indexName: string; partitionKey: string; sortKey?: string }[],
|
|
1189
955
|
): Promise<void> => {
|
|
1190
956
|
const gsiAttributeDefinitions = gsIs.flatMap((gsi) => [
|
|
1191
|
-
{ AttributeName: gsi.partitionKey, AttributeType:
|
|
957
|
+
{ AttributeName: gsi.partitionKey, AttributeType: ScalarAttributeType.S },
|
|
1192
958
|
...(gsi.sortKey
|
|
1193
|
-
? [{ AttributeName: gsi.sortKey, AttributeType:
|
|
959
|
+
? [{ AttributeName: gsi.sortKey, AttributeType: ScalarAttributeType.S }]
|
|
1194
960
|
: []),
|
|
1195
961
|
]);
|
|
1196
962
|
const attributeDefinitions = [
|
|
1197
|
-
{ AttributeName: 'pk', AttributeType:
|
|
1198
|
-
{ AttributeName: 'sk', AttributeType:
|
|
963
|
+
{ AttributeName: 'pk', AttributeType: ScalarAttributeType.S },
|
|
964
|
+
{ AttributeName: 'sk', AttributeType: ScalarAttributeType.S },
|
|
1199
965
|
...gsiAttributeDefinitions.filter(
|
|
1200
966
|
(a, i, arr) =>
|
|
1201
967
|
arr.findIndex((b) => b.AttributeName === a.AttributeName) === i,
|
|
@@ -1204,12 +970,12 @@ const ensureTable = async (
|
|
|
1204
970
|
const globalSecondaryIndexes = gsIs.map((gsi) => ({
|
|
1205
971
|
IndexName: gsi.indexName,
|
|
1206
972
|
KeySchema: [
|
|
1207
|
-
{ AttributeName: gsi.partitionKey, KeyType:
|
|
973
|
+
{ AttributeName: gsi.partitionKey, KeyType: KeyType.HASH },
|
|
1208
974
|
...(gsi.sortKey
|
|
1209
|
-
? [{ AttributeName: gsi.sortKey, KeyType:
|
|
975
|
+
? [{ AttributeName: gsi.sortKey, KeyType: KeyType.RANGE }]
|
|
1210
976
|
: []),
|
|
1211
977
|
],
|
|
1212
|
-
Projection: { ProjectionType:
|
|
978
|
+
Projection: { ProjectionType: ProjectionType.ALL },
|
|
1213
979
|
}));
|
|
1214
980
|
|
|
1215
981
|
for (let attempt = 0; attempt < 30; attempt++) {
|
|
@@ -1220,10 +986,10 @@ const ensureTable = async (
|
|
|
1220
986
|
TableName: tblName,
|
|
1221
987
|
AttributeDefinitions: attributeDefinitions,
|
|
1222
988
|
KeySchema: [
|
|
1223
|
-
{ AttributeName: 'pk', KeyType:
|
|
1224
|
-
{ AttributeName: 'sk', KeyType:
|
|
989
|
+
{ AttributeName: 'pk', KeyType: KeyType.HASH },
|
|
990
|
+
{ AttributeName: 'sk', KeyType: KeyType.RANGE },
|
|
1225
991
|
],
|
|
1226
|
-
BillingMode:
|
|
992
|
+
BillingMode: BillingMode.PAY_PER_REQUEST,
|
|
1227
993
|
...(globalSecondaryIndexes.length > 0 && {
|
|
1228
994
|
GlobalSecondaryIndexes: globalSecondaryIndexes,
|
|
1229
995
|
}),
|
|
@@ -1273,12 +1039,15 @@ const ensureTable = async (
|
|
|
1273
1039
|
new UpdateTableCommand({
|
|
1274
1040
|
TableName: tblName,
|
|
1275
1041
|
AttributeDefinitions: [
|
|
1276
|
-
{
|
|
1042
|
+
{
|
|
1043
|
+
AttributeName: gsi.partitionKey,
|
|
1044
|
+
AttributeType: ScalarAttributeType.S,
|
|
1045
|
+
},
|
|
1277
1046
|
...(gsi.sortKey
|
|
1278
1047
|
? [
|
|
1279
1048
|
{
|
|
1280
1049
|
AttributeName: gsi.sortKey,
|
|
1281
|
-
AttributeType:
|
|
1050
|
+
AttributeType: ScalarAttributeType.S,
|
|
1282
1051
|
},
|
|
1283
1052
|
]
|
|
1284
1053
|
: []),
|
|
@@ -1288,12 +1057,20 @@ const ensureTable = async (
|
|
|
1288
1057
|
Create: {
|
|
1289
1058
|
IndexName: gsi.indexName,
|
|
1290
1059
|
KeySchema: [
|
|
1291
|
-
{
|
|
1060
|
+
{
|
|
1061
|
+
AttributeName: gsi.partitionKey,
|
|
1062
|
+
KeyType: KeyType.HASH,
|
|
1063
|
+
},
|
|
1292
1064
|
...(gsi.sortKey
|
|
1293
|
-
? [
|
|
1065
|
+
? [
|
|
1066
|
+
{
|
|
1067
|
+
AttributeName: gsi.sortKey,
|
|
1068
|
+
KeyType: KeyType.RANGE,
|
|
1069
|
+
},
|
|
1070
|
+
]
|
|
1294
1071
|
: []),
|
|
1295
1072
|
],
|
|
1296
|
-
Projection: { ProjectionType:
|
|
1073
|
+
Projection: { ProjectionType: ProjectionType.ALL },
|
|
1297
1074
|
},
|
|
1298
1075
|
},
|
|
1299
1076
|
],
|
|
@@ -1315,18 +1092,21 @@ await ensureTable(client, tableName, globalSecondaryIndexes);
|
|
|
1315
1092
|
"
|
|
1316
1093
|
`;
|
|
1317
1094
|
|
|
1318
|
-
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/
|
|
1095
|
+
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/common/scripts/src/dynamodb/pull-image.ts 1`] = `
|
|
1319
1096
|
"import { spawnSync } from 'child_process';
|
|
1097
|
+
import { readFileSync } from 'fs';
|
|
1320
1098
|
|
|
1321
|
-
const
|
|
1322
|
-
|
|
1099
|
+
const { containerEngine, image } = JSON.parse(
|
|
1100
|
+
readFileSync('config.json', 'utf-8'),
|
|
1101
|
+
).serveLocal;
|
|
1323
1102
|
|
|
1324
|
-
const inspect = spawnSync(
|
|
1103
|
+
const inspect = spawnSync(containerEngine, ['image', 'inspect', image], {
|
|
1325
1104
|
stdio: 'ignore',
|
|
1326
1105
|
});
|
|
1327
|
-
|
|
1328
1106
|
if (inspect.status !== 0) {
|
|
1329
|
-
const pull = spawnSync(
|
|
1107
|
+
const pull = spawnSync(containerEngine, ['pull', image], {
|
|
1108
|
+
stdio: 'inherit',
|
|
1109
|
+
});
|
|
1330
1110
|
if (pull.status !== 0) {
|
|
1331
1111
|
process.exit(pull.status ?? 1);
|
|
1332
1112
|
}
|
|
@@ -1334,21 +1114,22 @@ if (inspect.status !== 0) {
|
|
|
1334
1114
|
"
|
|
1335
1115
|
`;
|
|
1336
1116
|
|
|
1337
|
-
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/
|
|
1117
|
+
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/common/scripts/src/dynamodb/start-container.ts 1`] = `
|
|
1338
1118
|
"import { spawn, spawnSync } from 'child_process';
|
|
1119
|
+
import { readFileSync } from 'fs';
|
|
1339
1120
|
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1121
|
+
const { containerEngine, containerName, image, port } = JSON.parse(
|
|
1122
|
+
readFileSync('config.json', 'utf-8'),
|
|
1123
|
+
).serveLocal;
|
|
1343
1124
|
|
|
1344
1125
|
const containerExists = (): boolean =>
|
|
1345
|
-
spawnSync(
|
|
1126
|
+
spawnSync(containerEngine, ['container', 'inspect', containerName], {
|
|
1346
1127
|
stdio: 'ignore',
|
|
1347
1128
|
}).status === 0;
|
|
1348
1129
|
|
|
1349
1130
|
const isRunning = (): boolean => {
|
|
1350
1131
|
const result = spawnSync(
|
|
1351
|
-
|
|
1132
|
+
containerEngine,
|
|
1352
1133
|
['container', 'inspect', '-f', '{{.State.Running}}', containerName],
|
|
1353
1134
|
{ encoding: 'utf-8' },
|
|
1354
1135
|
);
|
|
@@ -1366,19 +1147,38 @@ for (;;) {
|
|
|
1366
1147
|
if (!isRunning()) {
|
|
1367
1148
|
// Another process may have started it between our inspect and start — container engine start
|
|
1368
1149
|
// exits 0 even if the container is already running, so no special-casing needed.
|
|
1369
|
-
const start = spawnSync(
|
|
1150
|
+
const start = spawnSync(containerEngine, ['start', containerName], {
|
|
1370
1151
|
stdio: 'inherit',
|
|
1371
1152
|
});
|
|
1372
1153
|
if (start.status !== 0) process.exit(start.status ?? 1);
|
|
1373
1154
|
}
|
|
1374
1155
|
break;
|
|
1375
1156
|
}
|
|
1376
|
-
const runArgs =
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1157
|
+
const runArgs = [
|
|
1158
|
+
'run',
|
|
1159
|
+
...(containerEngine === 'docker' ? ['--rm'] : []),
|
|
1160
|
+
'--name',
|
|
1161
|
+
containerName,
|
|
1162
|
+
'-u',
|
|
1163
|
+
'root',
|
|
1164
|
+
'-w',
|
|
1165
|
+
'/home/dynamodblocal',
|
|
1166
|
+
\`-p\`,
|
|
1167
|
+
\`\${port}:\${port}\`,
|
|
1168
|
+
'-v',
|
|
1169
|
+
\`\${containerName}-data:/home/dynamodblocal/data\`,
|
|
1170
|
+
'-d',
|
|
1171
|
+
image,
|
|
1172
|
+
'-jar',
|
|
1173
|
+
'DynamoDBLocal.jar',
|
|
1174
|
+
'-sharedDb',
|
|
1175
|
+
'-dbPath',
|
|
1176
|
+
'./data',
|
|
1177
|
+
'-port',
|
|
1178
|
+
\`\${port}\`,
|
|
1179
|
+
'-optimizeDbBeforeStartup',
|
|
1180
|
+
];
|
|
1181
|
+
const create = spawnSync(containerEngine, runArgs, { stdio: 'pipe' });
|
|
1382
1182
|
if (create.status === 0) {
|
|
1383
1183
|
created = true;
|
|
1384
1184
|
break;
|
|
@@ -1389,7 +1189,7 @@ for (;;) {
|
|
|
1389
1189
|
}
|
|
1390
1190
|
}
|
|
1391
1191
|
|
|
1392
|
-
const logs = spawn(
|
|
1192
|
+
const logs = spawn(containerEngine, ['logs', '-f', containerName], {
|
|
1393
1193
|
stdio: ['ignore', 'inherit', 'inherit'],
|
|
1394
1194
|
});
|
|
1395
1195
|
|
|
@@ -1399,7 +1199,13 @@ const cleanup = () => {
|
|
|
1399
1199
|
if (exiting) return;
|
|
1400
1200
|
exiting = true;
|
|
1401
1201
|
if (created) {
|
|
1402
|
-
|
|
1202
|
+
if (containerEngine === 'docker') {
|
|
1203
|
+
spawnSync(containerEngine, ['stop', containerName], { stdio: 'ignore' });
|
|
1204
|
+
} else {
|
|
1205
|
+
spawnSync(containerEngine, ['rm', '-f', containerName], {
|
|
1206
|
+
stdio: 'ignore',
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1403
1209
|
}
|
|
1404
1210
|
logs.kill();
|
|
1405
1211
|
process.exit(0);
|
|
@@ -1418,29 +1224,34 @@ logs.on('exit', (code) => {
|
|
|
1418
1224
|
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/my-table/src/client.ts 1`] = `
|
|
1419
1225
|
"import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
1420
1226
|
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
1421
|
-
import {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1227
|
+
import { readFileSync } from 'fs';
|
|
1228
|
+
import { join, dirname } from 'path';
|
|
1229
|
+
import { fileURLToPath } from 'url';
|
|
1230
|
+
|
|
1231
|
+
// Generated: must match runtimeConfigKey in config.json
|
|
1232
|
+
const runtimeConfigKey = 'MyTable';
|
|
1233
|
+
|
|
1234
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1235
|
+
// config.json is only present in local dev (not included in the deployed bundle); guard with SERVE_LOCAL check
|
|
1236
|
+
const serveLocal = () =>
|
|
1237
|
+
JSON.parse(readFileSync(join(__dirname, '../config.json'), 'utf-8'))
|
|
1238
|
+
.serveLocal;
|
|
1429
1239
|
|
|
1430
1240
|
let _client: DynamoDBClient | undefined;
|
|
1431
1241
|
|
|
1432
1242
|
export const getDynamoDBClient = (): DynamoDBClient => {
|
|
1433
|
-
_client
|
|
1434
|
-
process.env.SERVE_LOCAL === 'true'
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1243
|
+
if (!_client) {
|
|
1244
|
+
if (process.env.SERVE_LOCAL === 'true') {
|
|
1245
|
+
const { port } = serveLocal();
|
|
1246
|
+
_client = new DynamoDBClient({
|
|
1247
|
+
endpoint: \`http://localhost:\${port}\`,
|
|
1248
|
+
region: 'us-east-1',
|
|
1249
|
+
credentials: { accessKeyId: 'UNUSED', secretAccessKey: 'UNUSED' },
|
|
1250
|
+
});
|
|
1251
|
+
} else {
|
|
1252
|
+
_client = new DynamoDBClient({});
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1444
1255
|
return _client;
|
|
1445
1256
|
};
|
|
1446
1257
|
|
|
@@ -1452,7 +1263,8 @@ let _tableName: string | undefined;
|
|
|
1452
1263
|
|
|
1453
1264
|
export const resolveTableName = async (): Promise<string> => {
|
|
1454
1265
|
if (process.env.SERVE_LOCAL === 'true') {
|
|
1455
|
-
|
|
1266
|
+
const { tableName } = serveLocal();
|
|
1267
|
+
return tableName;
|
|
1456
1268
|
}
|
|
1457
1269
|
if (_tableName === undefined) {
|
|
1458
1270
|
const appId = process.env.RUNTIME_CONFIG_APP_ID;
|
|
@@ -1466,7 +1278,7 @@ export const resolveTableName = async (): Promise<string> => {
|
|
|
1466
1278
|
environment: 'default',
|
|
1467
1279
|
transform: 'json',
|
|
1468
1280
|
});
|
|
1469
|
-
const tableName = config?.[
|
|
1281
|
+
const tableName = config?.[runtimeConfigKey]?.tableName;
|
|
1470
1282
|
if (!tableName) {
|
|
1471
1283
|
throw new Error('Could not resolve table name from runtime config');
|
|
1472
1284
|
}
|
|
@@ -1477,19 +1289,6 @@ export const resolveTableName = async (): Promise<string> => {
|
|
|
1477
1289
|
"
|
|
1478
1290
|
`;
|
|
1479
1291
|
|
|
1480
|
-
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/my-table/src/constants.ts 1`] = `
|
|
1481
|
-
"export const PACKAGE_NAME = 'MyTable';
|
|
1482
|
-
|
|
1483
|
-
// Local development connection details (used when SERVE_LOCAL=true, see serve-local Nx target)
|
|
1484
|
-
export const LOCAL_PORT = 8000;
|
|
1485
|
-
export const LOCAL_REGION = 'us-east-1';
|
|
1486
|
-
export const LOCAL_ACCESS_KEY_ID = 'accessKeyId';
|
|
1487
|
-
export const LOCAL_SECRET_ACCESS_KEY = 'secretAccessKey';
|
|
1488
|
-
export const LOCAL_ENDPOINT = \`http://localhost:\${LOCAL_PORT}\`;
|
|
1489
|
-
export const LOCAL_TABLE_NAME = 'proj-my-table';
|
|
1490
|
-
"
|
|
1491
|
-
`;
|
|
1492
|
-
|
|
1493
1292
|
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/my-table/src/entities/example.ts 1`] = `
|
|
1494
1293
|
"import { Entity } from 'electrodb';
|
|
1495
1294
|
import { getDynamoDBClient, resolveTableName } from '../client.js';
|
|
@@ -1547,15 +1346,7 @@ exports[`ts#dynamodb generator > should generate with infra=none then upgrade to
|
|
|
1547
1346
|
`;
|
|
1548
1347
|
|
|
1549
1348
|
exports[`ts#dynamodb generator > should generate with infra=none then upgrade to infra=dynamodb > packages/my-table/src/index.ts 1`] = `
|
|
1550
|
-
"export {
|
|
1551
|
-
PACKAGE_NAME,
|
|
1552
|
-
LOCAL_ACCESS_KEY_ID,
|
|
1553
|
-
LOCAL_ENDPOINT,
|
|
1554
|
-
LOCAL_REGION,
|
|
1555
|
-
LOCAL_SECRET_ACCESS_KEY,
|
|
1556
|
-
LOCAL_TABLE_NAME,
|
|
1557
|
-
} from './constants.js';
|
|
1558
|
-
export { getDynamoDBClient, resolveTableName } from './client.js';
|
|
1349
|
+
"export { getDynamoDBClient, resolveTableName } from './client.js';
|
|
1559
1350
|
export * from './entities/index.js';
|
|
1560
1351
|
"
|
|
1561
1352
|
`;
|