@superblocksteam/sdk-api 2.0.159-next.0 → 2.0.160
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/README.md +0 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/dynamodb/client.d.ts +2 -6
- package/dist/integrations/dynamodb/client.d.ts.map +1 -1
- package/dist/integrations/dynamodb/client.js +10 -83
- package/dist/integrations/dynamodb/client.js.map +1 -1
- package/dist/integrations/dynamodb/index.d.ts +1 -1
- package/dist/integrations/dynamodb/index.d.ts.map +1 -1
- package/dist/integrations/dynamodb/index.js.map +1 -1
- package/dist/integrations/dynamodb/types.d.ts +1 -27
- package/dist/integrations/dynamodb/types.d.ts.map +1 -1
- package/dist/integrations/index.d.ts +1 -1
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/runtime/context.d.ts +0 -6
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js +1 -2
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/executor.d.ts +0 -6
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +0 -1
- package/dist/runtime/executor.js.map +1 -1
- package/dist/types.d.ts +0 -10
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +0 -1
- package/src/integrations/dynamodb/README.md +12 -33
- package/src/integrations/dynamodb/client.ts +16 -121
- package/src/integrations/dynamodb/index.ts +1 -5
- package/src/integrations/dynamodb/types.ts +1 -33
- package/src/integrations/index.ts +0 -1
- package/src/runtime/context.ts +0 -9
- package/src/runtime/executor.ts +0 -8
- package/src/types.ts +0 -11
- package/dist/integrations/dynamodb/client.test.d.ts +0 -8
- package/dist/integrations/dynamodb/client.test.d.ts.map +0 -1
- package/dist/integrations/dynamodb/client.test.js +0 -198
- package/dist/integrations/dynamodb/client.test.js.map +0 -1
- package/src/integrations/dynamodb/client.test.ts +0 -254
|
@@ -12,7 +12,6 @@ Execute operations against Amazon DynamoDB with full type safety and runtime val
|
|
|
12
12
|
| `updateItem(table, key, expr, values, names?, metadata?)` | Update an existing item |
|
|
13
13
|
| `deleteItem(table, key, metadata?)` | Delete an item by key |
|
|
14
14
|
| `scan(table, schema, filter?, values?, names?, metadata?)` | Scan a table |
|
|
15
|
-
| `scan(table, schema, options, metadata?)` | Scan with pagination options |
|
|
16
15
|
| `queryTable(table, keyExpr, values, schema, names?, metadata?)` | Query by key condition |
|
|
17
16
|
| `batchWriteItem(requestItems, metadata?)` | Batch write multiple items |
|
|
18
17
|
| `listTables(schema, metadata?)` | List all tables |
|
|
@@ -134,41 +133,21 @@ await ctx.integrations.dynamodb.deleteItem("users", { id: { S: "user-123" } });
|
|
|
134
133
|
### Scan Table
|
|
135
134
|
|
|
136
135
|
```typescript
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
Items: z.array(ItemSchema).optional(),
|
|
143
|
-
LastEvaluatedKey: z.record(AttributeValueSchema).optional(),
|
|
144
|
-
});
|
|
136
|
+
// Full scan
|
|
137
|
+
const allUsers = await ctx.integrations.dynamodb.scan(
|
|
138
|
+
"users",
|
|
139
|
+
z.array(z.object({ id: z.string(), name: z.string() })),
|
|
140
|
+
);
|
|
145
141
|
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const page = await ctx.integrations.dynamodb.scan("users", ScanPageSchema, {
|
|
154
|
-
exclusiveStartKey,
|
|
155
|
-
filterExpression: "status = :s",
|
|
156
|
-
expressionAttributeValues: { ":s": { S: "active" } },
|
|
157
|
-
});
|
|
158
|
-
allItems.push(...(page.Items ?? []));
|
|
159
|
-
exclusiveStartKey = page.LastEvaluatedKey;
|
|
160
|
-
if (!exclusiveStartKey) {
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
if (pageNumber === MAX_PAGES - 1) {
|
|
164
|
-
throw new Error(`Scan exceeded the ${MAX_PAGES}-page safety limit`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
142
|
+
// With filter expression
|
|
143
|
+
const activeUsers = await ctx.integrations.dynamodb.scan(
|
|
144
|
+
"users",
|
|
145
|
+
z.array(z.object({ id: z.string(), name: z.string() })),
|
|
146
|
+
"status = :s",
|
|
147
|
+
{ ":s": { S: "active" } },
|
|
148
|
+
);
|
|
167
149
|
```
|
|
168
150
|
|
|
169
|
-
Choose a page cap appropriate for the API timeout and provisioned read
|
|
170
|
-
capacity. For very large tables, process and persist pages incrementally.
|
|
171
|
-
|
|
172
151
|
### Query Table by Key Condition
|
|
173
152
|
|
|
174
153
|
```typescript
|
|
@@ -12,23 +12,7 @@ import { RestApiValidationError } from "../../errors.js";
|
|
|
12
12
|
import { IntegrationError } from "../../runtime/errors.js";
|
|
13
13
|
import type { QueryExecutor, TraceMetadata } from "../registry.js";
|
|
14
14
|
import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
|
|
15
|
-
import type {
|
|
16
|
-
DynamoDBClient,
|
|
17
|
-
DynamoDBAttributeValue,
|
|
18
|
-
DynamoDBScanOptions,
|
|
19
|
-
} from "./types.js";
|
|
20
|
-
|
|
21
|
-
const DYNAMODB_SCAN_OPTION_KEYS = new Set<string>([
|
|
22
|
-
"filterExpression",
|
|
23
|
-
"expressionAttributeValues",
|
|
24
|
-
"expressionAttributeNames",
|
|
25
|
-
"exclusiveStartKey",
|
|
26
|
-
"limit",
|
|
27
|
-
"projectionExpression",
|
|
28
|
-
"indexName",
|
|
29
|
-
"segment",
|
|
30
|
-
"totalSegments",
|
|
31
|
-
]);
|
|
15
|
+
import type { DynamoDBClient, DynamoDBAttributeValue } from "./types.js";
|
|
32
16
|
|
|
33
17
|
/**
|
|
34
18
|
* Internal implementation of DynamoDBClient.
|
|
@@ -205,120 +189,33 @@ export class DynamoDBClientImpl
|
|
|
205
189
|
return this.executeWithErrorHandling(request, "deleteItem", metadata);
|
|
206
190
|
}
|
|
207
191
|
|
|
208
|
-
/** Copy optional AWS Scan fields onto the request body. */
|
|
209
|
-
private applyScanParams(
|
|
210
|
-
params: Record<string, unknown>,
|
|
211
|
-
options: DynamoDBScanOptions,
|
|
212
|
-
): void {
|
|
213
|
-
if (options.filterExpression) {
|
|
214
|
-
params.FilterExpression = options.filterExpression;
|
|
215
|
-
}
|
|
216
|
-
if (
|
|
217
|
-
options.expressionAttributeValues &&
|
|
218
|
-
Object.keys(options.expressionAttributeValues).length > 0
|
|
219
|
-
) {
|
|
220
|
-
params.ExpressionAttributeValues = options.expressionAttributeValues;
|
|
221
|
-
}
|
|
222
|
-
if (
|
|
223
|
-
options.expressionAttributeNames &&
|
|
224
|
-
Object.keys(options.expressionAttributeNames).length > 0
|
|
225
|
-
) {
|
|
226
|
-
params.ExpressionAttributeNames = options.expressionAttributeNames;
|
|
227
|
-
}
|
|
228
|
-
if (options.exclusiveStartKey) {
|
|
229
|
-
params.ExclusiveStartKey = options.exclusiveStartKey;
|
|
230
|
-
}
|
|
231
|
-
if (options.limit !== undefined) {
|
|
232
|
-
params.Limit = options.limit;
|
|
233
|
-
}
|
|
234
|
-
if (options.projectionExpression) {
|
|
235
|
-
params.ProjectionExpression = options.projectionExpression;
|
|
236
|
-
}
|
|
237
|
-
if (options.indexName) {
|
|
238
|
-
params.IndexName = options.indexName;
|
|
239
|
-
}
|
|
240
|
-
if (options.segment !== undefined) {
|
|
241
|
-
params.Segment = options.segment;
|
|
242
|
-
}
|
|
243
|
-
if (options.totalSegments !== undefined) {
|
|
244
|
-
params.TotalSegments = options.totalSegments;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private isScanOptions(value: unknown): value is DynamoDBScanOptions {
|
|
249
|
-
return (
|
|
250
|
-
typeof value === "object" &&
|
|
251
|
-
value !== null &&
|
|
252
|
-
Object.keys(value).every((key) => DYNAMODB_SCAN_OPTION_KEYS.has(key))
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private isTraceMetadata(value: unknown): value is TraceMetadata {
|
|
257
|
-
return (
|
|
258
|
-
typeof value === "object" &&
|
|
259
|
-
value !== null &&
|
|
260
|
-
Object.keys(value).every(
|
|
261
|
-
(key) => key === "label" || key === "description",
|
|
262
|
-
) &&
|
|
263
|
-
Object.values(value).every(
|
|
264
|
-
(entry) => entry === undefined || typeof entry === "string",
|
|
265
|
-
)
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
192
|
async scan<T>(
|
|
270
193
|
table: string,
|
|
271
194
|
schema: z.ZodSchema<T>,
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
| Record<string, DynamoDBAttributeValue>
|
|
275
|
-
| TraceMetadata,
|
|
195
|
+
filterExpression?: string,
|
|
196
|
+
expressionAttributeValues?: Record<string, DynamoDBAttributeValue>,
|
|
276
197
|
expressionAttributeNames?: Record<string, string>,
|
|
277
198
|
metadata?: TraceMetadata,
|
|
278
199
|
): Promise<T> {
|
|
279
200
|
const params: Record<string, unknown> = { TableName: table };
|
|
280
|
-
let resolvedMetadata = metadata;
|
|
281
|
-
|
|
282
|
-
if (
|
|
283
|
-
typeof filterExpressionOrOptions === "object" &&
|
|
284
|
-
filterExpressionOrOptions !== null
|
|
285
|
-
) {
|
|
286
|
-
if (!this.isScanOptions(filterExpressionOrOptions)) {
|
|
287
|
-
throw new Error(
|
|
288
|
-
`Invalid DynamoDB scan options: ${Object.keys(filterExpressionOrOptions).join(", ")}`,
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
this.applyScanParams(params, filterExpressionOrOptions);
|
|
292
201
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (
|
|
304
|
-
expressionAttributeValuesOrMetadata &&
|
|
305
|
-
Object.keys(expressionAttributeValuesOrMetadata).length > 0
|
|
306
|
-
) {
|
|
307
|
-
params.ExpressionAttributeValues = expressionAttributeValuesOrMetadata;
|
|
308
|
-
}
|
|
309
|
-
if (
|
|
310
|
-
expressionAttributeNames &&
|
|
311
|
-
Object.keys(expressionAttributeNames).length > 0
|
|
312
|
-
) {
|
|
313
|
-
params.ExpressionAttributeNames = expressionAttributeNames;
|
|
314
|
-
}
|
|
202
|
+
if (filterExpression) {
|
|
203
|
+
params.FilterExpression = filterExpression;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (expressionAttributeValues) {
|
|
207
|
+
params.ExpressionAttributeValues = expressionAttributeValues;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (expressionAttributeNames) {
|
|
211
|
+
params.ExpressionAttributeNames = expressionAttributeNames;
|
|
315
212
|
}
|
|
316
213
|
|
|
317
214
|
const request = this.buildRequest("scan", params);
|
|
318
215
|
const result = await this.executeWithErrorHandling(
|
|
319
216
|
request,
|
|
320
217
|
"scan",
|
|
321
|
-
|
|
218
|
+
metadata,
|
|
322
219
|
);
|
|
323
220
|
return this.validateResult(result, schema, "scan");
|
|
324
221
|
}
|
|
@@ -336,10 +233,8 @@ export class DynamoDBClientImpl
|
|
|
336
233
|
KeyConditionExpression: keyConditionExpression,
|
|
337
234
|
ExpressionAttributeValues: expressionAttributeValues,
|
|
338
235
|
};
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
Object.keys(expressionAttributeNames).length > 0
|
|
342
|
-
) {
|
|
236
|
+
|
|
237
|
+
if (expressionAttributeNames) {
|
|
343
238
|
params.ExpressionAttributeNames = expressionAttributeNames;
|
|
344
239
|
}
|
|
345
240
|
|
|
@@ -37,24 +37,6 @@ export type DynamoDBAttributeValue =
|
|
|
37
37
|
| { NS: string[] }
|
|
38
38
|
| { BS: string[] };
|
|
39
39
|
|
|
40
|
-
/**
|
|
41
|
-
* Optional Scan parameters forwarded to the AWS SDK.
|
|
42
|
-
*
|
|
43
|
-
* Use `exclusiveStartKey` with `LastEvaluatedKey` from a previous page to
|
|
44
|
-
* continue past DynamoDB's 1 MB per-Scan limit.
|
|
45
|
-
*/
|
|
46
|
-
export interface DynamoDBScanOptions {
|
|
47
|
-
filterExpression?: string;
|
|
48
|
-
expressionAttributeValues?: Record<string, DynamoDBAttributeValue>;
|
|
49
|
-
expressionAttributeNames?: Record<string, string>;
|
|
50
|
-
exclusiveStartKey?: Record<string, DynamoDBAttributeValue>;
|
|
51
|
-
limit?: number;
|
|
52
|
-
projectionExpression?: string;
|
|
53
|
-
indexName?: string;
|
|
54
|
-
segment?: number;
|
|
55
|
-
totalSegments?: number;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
40
|
/**
|
|
59
41
|
* DynamoDB client for database operations.
|
|
60
42
|
*
|
|
@@ -102,11 +84,6 @@ export interface DynamoDBScanOptions {
|
|
|
102
84
|
* 'status = :s',
|
|
103
85
|
* { ':s': { S: 'active' } }
|
|
104
86
|
* );
|
|
105
|
-
*
|
|
106
|
-
* // Paginate past the 1 MB Scan limit
|
|
107
|
-
* const page = await ctx.integrations.db.scan('users', UsersSchema, {
|
|
108
|
-
* exclusiveStartKey: lastEvaluatedKey,
|
|
109
|
-
* });
|
|
110
87
|
* ```
|
|
111
88
|
*/
|
|
112
89
|
export interface DynamoDBClient extends BaseIntegrationClient {
|
|
@@ -145,10 +122,7 @@ export interface DynamoDBClient extends BaseIntegrationClient {
|
|
|
145
122
|
): Promise<T>;
|
|
146
123
|
|
|
147
124
|
/**
|
|
148
|
-
* Scan a DynamoDB table.
|
|
149
|
-
*
|
|
150
|
-
* Prefer the options-object form when paginating (`exclusiveStartKey`) or
|
|
151
|
-
* when you need `limit`, `indexName`, or parallel scan segments.
|
|
125
|
+
* Scan a DynamoDB table with optional filter.
|
|
152
126
|
*
|
|
153
127
|
* @param table - The table name
|
|
154
128
|
* @param schema - Zod schema for validating the result
|
|
@@ -159,12 +133,6 @@ export interface DynamoDBClient extends BaseIntegrationClient {
|
|
|
159
133
|
* @param metadata - Optional trace metadata for diagnostics
|
|
160
134
|
* @returns The validated result
|
|
161
135
|
*/
|
|
162
|
-
scan<T>(
|
|
163
|
-
table: string,
|
|
164
|
-
schema: z.ZodSchema<T>,
|
|
165
|
-
options: DynamoDBScanOptions,
|
|
166
|
-
metadata?: TraceMetadata,
|
|
167
|
-
): Promise<T>;
|
|
168
136
|
scan<T>(
|
|
169
137
|
table: string,
|
|
170
138
|
schema: z.ZodSchema<T>,
|
package/src/runtime/context.ts
CHANGED
|
@@ -57,13 +57,6 @@ export interface CreateContextOptions {
|
|
|
57
57
|
/** Environment variables */
|
|
58
58
|
env: Record<string, string>;
|
|
59
59
|
|
|
60
|
-
/**
|
|
61
|
-
* Server-resolved data tag key.
|
|
62
|
-
*
|
|
63
|
-
* Optional for compatibility with execution wrappers from older agents.
|
|
64
|
-
*/
|
|
65
|
-
dataTag?: string;
|
|
66
|
-
|
|
67
60
|
/** User information from JWT */
|
|
68
61
|
user: ApiUser;
|
|
69
62
|
|
|
@@ -108,7 +101,6 @@ export function createApiContext(
|
|
|
108
101
|
executeQuery,
|
|
109
102
|
executionId,
|
|
110
103
|
env,
|
|
111
|
-
dataTag,
|
|
112
104
|
user,
|
|
113
105
|
logger,
|
|
114
106
|
} = options;
|
|
@@ -180,7 +172,6 @@ export function createApiContext(
|
|
|
180
172
|
>["integrations"],
|
|
181
173
|
log,
|
|
182
174
|
env: Object.freeze({ ...env }),
|
|
183
|
-
dataTag,
|
|
184
175
|
user,
|
|
185
176
|
};
|
|
186
177
|
}
|
package/src/runtime/executor.ts
CHANGED
|
@@ -36,13 +36,6 @@ export interface ExecuteApiRequest {
|
|
|
36
36
|
/** Environment variables available to the API */
|
|
37
37
|
env: Record<string, string>;
|
|
38
38
|
|
|
39
|
-
/**
|
|
40
|
-
* Server-resolved data tag key for this execution.
|
|
41
|
-
*
|
|
42
|
-
* Optional on the low-level request for compatibility with older runtimes.
|
|
43
|
-
*/
|
|
44
|
-
dataTag?: string;
|
|
45
|
-
|
|
46
39
|
/** User information extracted from the Superblocks JWT */
|
|
47
40
|
user: ApiUser;
|
|
48
41
|
|
|
@@ -169,7 +162,6 @@ export async function executeApi<TInput = unknown, TOutput = unknown>(
|
|
|
169
162
|
executeQuery: request.executeQuery,
|
|
170
163
|
executionId: request.executionId,
|
|
171
164
|
env: request.env,
|
|
172
|
-
dataTag: request.dataTag,
|
|
173
165
|
user: request.user,
|
|
174
166
|
});
|
|
175
167
|
|
package/src/types.ts
CHANGED
|
@@ -182,17 +182,6 @@ export interface ApiContext<
|
|
|
182
182
|
/** Access to environment variables */
|
|
183
183
|
readonly env: Readonly<Record<string, string>>;
|
|
184
184
|
|
|
185
|
-
/**
|
|
186
|
-
* Canonical key of the requested data tag validated for this execution.
|
|
187
|
-
*
|
|
188
|
-
* This identifies the integration configuration in use, not a user's
|
|
189
|
-
* environment entitlement. It is supplied by the Superblocks runtime and
|
|
190
|
-
* cannot be selected through API input. It is undefined when no data tag was
|
|
191
|
-
* resolved or on runtimes that predate data-tag context support;
|
|
192
|
-
* authorization checks must deny access in those cases.
|
|
193
|
-
*/
|
|
194
|
-
readonly dataTag?: string;
|
|
195
|
-
|
|
196
185
|
/** User information from the Superblocks JWT */
|
|
197
186
|
readonly user: ApiUser;
|
|
198
187
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for DynamoDB client request bodies.
|
|
3
|
-
*
|
|
4
|
-
* The orchestrator forwards `action` + JSON `body` to the AWS SDK, so these
|
|
5
|
-
* tests lock the wire params - especially ExclusiveStartKey pagination.
|
|
6
|
-
*/
|
|
7
|
-
export {};
|
|
8
|
-
//# sourceMappingURL=client.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../../src/integrations/dynamodb/client.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for DynamoDB client request bodies.
|
|
3
|
-
*
|
|
4
|
-
* The orchestrator forwards `action` + JSON `body` to the AWS SDK, so these
|
|
5
|
-
* tests lock the wire params - especially ExclusiveStartKey pagination.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect, vi } from "vitest";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { DynamoDBClientImpl } from "./client.js";
|
|
10
|
-
const TEST_CONFIG = {
|
|
11
|
-
id: "dynamodb-test-id",
|
|
12
|
-
name: "Test DynamoDB",
|
|
13
|
-
pluginId: "dynamodb",
|
|
14
|
-
configuration: {},
|
|
15
|
-
};
|
|
16
|
-
const ItemsSchema = z.object({ Items: z.array(z.unknown()).optional() });
|
|
17
|
-
function createClient(mockResult = { Items: [] }) {
|
|
18
|
-
const executeQuery = vi.fn().mockResolvedValue(mockResult);
|
|
19
|
-
const client = new DynamoDBClientImpl(TEST_CONFIG, executeQuery);
|
|
20
|
-
return { client, executeQuery };
|
|
21
|
-
}
|
|
22
|
-
function parsedBody(executeQuery, callIndex = 0) {
|
|
23
|
-
const request = z
|
|
24
|
-
.object({ action: z.string(), body: z.string() })
|
|
25
|
-
.parse(executeQuery.mock.calls[callIndex]?.[0]);
|
|
26
|
-
return { action: request.action, body: JSON.parse(request.body) };
|
|
27
|
-
}
|
|
28
|
-
describe("DynamoDBClientImpl", () => {
|
|
29
|
-
describe("scan", () => {
|
|
30
|
-
it("omits ExclusiveStartKey on a positional scan", async () => {
|
|
31
|
-
const { client, executeQuery } = createClient();
|
|
32
|
-
await client.scan("retailers", ItemsSchema, "status = :s", {
|
|
33
|
-
":s": { S: "active" },
|
|
34
|
-
});
|
|
35
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
36
|
-
action: "scan",
|
|
37
|
-
body: {
|
|
38
|
-
TableName: "retailers",
|
|
39
|
-
FilterExpression: "status = :s",
|
|
40
|
-
ExpressionAttributeValues: { ":s": { S: "active" } },
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
it("forwards ExclusiveStartKey from the options object", async () => {
|
|
45
|
-
const { client, executeQuery } = createClient();
|
|
46
|
-
const exclusiveStartKey = { id: { S: "retailer-18" } };
|
|
47
|
-
await client.scan("retailers", ItemsSchema, { exclusiveStartKey });
|
|
48
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
49
|
-
action: "scan",
|
|
50
|
-
body: {
|
|
51
|
-
TableName: "retailers",
|
|
52
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
it("treats an empty object as empty scan options", async () => {
|
|
57
|
-
const { client, executeQuery } = createClient();
|
|
58
|
-
await client.scan("retailers", ItemsSchema, {});
|
|
59
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
60
|
-
action: "scan",
|
|
61
|
-
body: { TableName: "retailers" },
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
it("rejects objects with unknown scan option keys", async () => {
|
|
65
|
-
const { client, executeQuery } = createClient();
|
|
66
|
-
await expect(Reflect.apply(client.scan, client, [
|
|
67
|
-
"retailers",
|
|
68
|
-
ItemsSchema,
|
|
69
|
-
{ label: "not scan options" },
|
|
70
|
-
])).rejects.toThrow("Invalid DynamoDB scan options: label");
|
|
71
|
-
expect(executeQuery).not.toHaveBeenCalled();
|
|
72
|
-
});
|
|
73
|
-
it("forwards pagination and parallel-scan options", async () => {
|
|
74
|
-
const { client, executeQuery } = createClient();
|
|
75
|
-
await client.scan("retailers", ItemsSchema, {
|
|
76
|
-
exclusiveStartKey: { id: { S: "page-2" } },
|
|
77
|
-
limit: 25,
|
|
78
|
-
projectionExpression: "id, name",
|
|
79
|
-
indexName: "gsi1",
|
|
80
|
-
segment: 0,
|
|
81
|
-
totalSegments: 4,
|
|
82
|
-
filterExpression: "#s = :s",
|
|
83
|
-
expressionAttributeNames: { "#s": "status" },
|
|
84
|
-
expressionAttributeValues: { ":s": { S: "active" } },
|
|
85
|
-
}, { label: "page retailers" });
|
|
86
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
87
|
-
action: "scan",
|
|
88
|
-
body: {
|
|
89
|
-
TableName: "retailers",
|
|
90
|
-
ExclusiveStartKey: { id: { S: "page-2" } },
|
|
91
|
-
Limit: 25,
|
|
92
|
-
ProjectionExpression: "id, name",
|
|
93
|
-
IndexName: "gsi1",
|
|
94
|
-
Segment: 0,
|
|
95
|
-
TotalSegments: 4,
|
|
96
|
-
FilterExpression: "#s = :s",
|
|
97
|
-
ExpressionAttributeNames: { "#s": "status" },
|
|
98
|
-
ExpressionAttributeValues: { ":s": { S: "active" } },
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
expect(executeQuery).toHaveBeenCalledWith(expect.anything(), undefined, {
|
|
102
|
-
label: "page retailers",
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
it("keeps positional names and metadata working together", async () => {
|
|
106
|
-
const { client, executeQuery } = createClient();
|
|
107
|
-
await client.scan("retailers", ItemsSchema, "#s = :s", { ":s": { S: "active" } }, { "#s": "status" }, { label: "filtered scan" });
|
|
108
|
-
expect(parsedBody(executeQuery).body).toEqual({
|
|
109
|
-
TableName: "retailers",
|
|
110
|
-
FilterExpression: "#s = :s",
|
|
111
|
-
ExpressionAttributeValues: { ":s": { S: "active" } },
|
|
112
|
-
ExpressionAttributeNames: { "#s": "status" },
|
|
113
|
-
});
|
|
114
|
-
expect(executeQuery.mock.calls[0][2]).toEqual({ label: "filtered scan" });
|
|
115
|
-
});
|
|
116
|
-
it("keeps schemas that strip pagination cursors backward compatible", async () => {
|
|
117
|
-
const lastEvaluatedKey = {
|
|
118
|
-
id: { S: "retailer-18" },
|
|
119
|
-
version: { N: "2" },
|
|
120
|
-
};
|
|
121
|
-
const { client } = createClient({
|
|
122
|
-
Items: [{ id: { S: "retailer-1" } }],
|
|
123
|
-
LastEvaluatedKey: lastEvaluatedKey,
|
|
124
|
-
});
|
|
125
|
-
await expect(client.scan("retailers", ItemsSchema, { limit: 25 })).resolves.toEqual({
|
|
126
|
-
Items: [{ id: { S: "retailer-1" } }],
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
it("preserves mixed AttributeValue pagination cursors", async () => {
|
|
130
|
-
const lastEvaluatedKey = {
|
|
131
|
-
id: { S: "retailer-18" },
|
|
132
|
-
version: { N: "2" },
|
|
133
|
-
};
|
|
134
|
-
const PageSchema = z.object({
|
|
135
|
-
Items: z.array(z.unknown()).optional(),
|
|
136
|
-
LastEvaluatedKey: z.record(z.unknown()).optional(),
|
|
137
|
-
});
|
|
138
|
-
const { client } = createClient({
|
|
139
|
-
Items: [],
|
|
140
|
-
LastEvaluatedKey: lastEvaluatedKey,
|
|
141
|
-
});
|
|
142
|
-
const page = await client.scan("retailers", PageSchema, {});
|
|
143
|
-
expect(page.LastEvaluatedKey).toEqual(lastEvaluatedKey);
|
|
144
|
-
});
|
|
145
|
-
it("threads each returned pagination cursor into the next request", async () => {
|
|
146
|
-
const LastEvaluatedKeySchema = z.object({
|
|
147
|
-
id: z.object({ S: z.string() }),
|
|
148
|
-
});
|
|
149
|
-
const PageSchema = z.object({
|
|
150
|
-
Items: z.array(z.string()),
|
|
151
|
-
LastEvaluatedKey: LastEvaluatedKeySchema.optional(),
|
|
152
|
-
});
|
|
153
|
-
const lastEvaluatedKey = { id: { S: "retailer-1" } };
|
|
154
|
-
const executeQuery = vi
|
|
155
|
-
.fn()
|
|
156
|
-
.mockResolvedValueOnce({
|
|
157
|
-
Items: ["retailer-1"],
|
|
158
|
-
LastEvaluatedKey: lastEvaluatedKey,
|
|
159
|
-
})
|
|
160
|
-
.mockResolvedValueOnce({ Items: ["retailer-2"] });
|
|
161
|
-
const client = new DynamoDBClientImpl(TEST_CONFIG, executeQuery);
|
|
162
|
-
const items = [];
|
|
163
|
-
let exclusiveStartKey;
|
|
164
|
-
do {
|
|
165
|
-
const page = await client.scan("retailers", PageSchema, {
|
|
166
|
-
exclusiveStartKey,
|
|
167
|
-
});
|
|
168
|
-
items.push(...page.Items);
|
|
169
|
-
exclusiveStartKey = page.LastEvaluatedKey;
|
|
170
|
-
} while (exclusiveStartKey);
|
|
171
|
-
expect(items).toEqual(["retailer-1", "retailer-2"]);
|
|
172
|
-
expect(executeQuery).toHaveBeenCalledTimes(2);
|
|
173
|
-
expect(parsedBody(executeQuery, 0).body).toEqual({
|
|
174
|
-
TableName: "retailers",
|
|
175
|
-
});
|
|
176
|
-
expect(parsedBody(executeQuery, 1).body).toEqual({
|
|
177
|
-
TableName: "retailers",
|
|
178
|
-
ExclusiveStartKey: lastEvaluatedKey,
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
describe("queryTable", () => {
|
|
183
|
-
it("treats a plain names map as ExpressionAttributeNames", async () => {
|
|
184
|
-
const { client, executeQuery } = createClient();
|
|
185
|
-
await client.queryTable("orders", "#uid = :uid", { ":uid": { S: "user-123" } }, ItemsSchema, { "#uid": "userId" });
|
|
186
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
187
|
-
action: "query",
|
|
188
|
-
body: {
|
|
189
|
-
TableName: "orders",
|
|
190
|
-
KeyConditionExpression: "#uid = :uid",
|
|
191
|
-
ExpressionAttributeValues: { ":uid": { S: "user-123" } },
|
|
192
|
-
ExpressionAttributeNames: { "#uid": "userId" },
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
//# sourceMappingURL=client.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../../src/integrations/dynamodb/client.test.ts"],"names":[],"mappings":"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"}
|