@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
|
@@ -1,254 +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
|
-
|
|
8
|
-
import { describe, it, expect, vi } from "vitest";
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
|
|
11
|
-
import type { IntegrationConfig } from "../types.js";
|
|
12
|
-
import { DynamoDBClientImpl } from "./client.js";
|
|
13
|
-
|
|
14
|
-
const TEST_CONFIG: IntegrationConfig = {
|
|
15
|
-
id: "dynamodb-test-id",
|
|
16
|
-
name: "Test DynamoDB",
|
|
17
|
-
pluginId: "dynamodb",
|
|
18
|
-
configuration: {},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const ItemsSchema = z.object({ Items: z.array(z.unknown()).optional() });
|
|
22
|
-
|
|
23
|
-
function createClient(mockResult: unknown = { Items: [] }) {
|
|
24
|
-
const executeQuery = vi.fn().mockResolvedValue(mockResult);
|
|
25
|
-
const client = new DynamoDBClientImpl(TEST_CONFIG, executeQuery);
|
|
26
|
-
return { client, executeQuery };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function parsedBody(executeQuery: ReturnType<typeof vi.fn>, callIndex = 0) {
|
|
30
|
-
const request = z
|
|
31
|
-
.object({ action: z.string(), body: z.string() })
|
|
32
|
-
.parse(executeQuery.mock.calls[callIndex]?.[0]);
|
|
33
|
-
return { action: request.action, body: JSON.parse(request.body) };
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
describe("DynamoDBClientImpl", () => {
|
|
37
|
-
describe("scan", () => {
|
|
38
|
-
it("omits ExclusiveStartKey on a positional scan", async () => {
|
|
39
|
-
const { client, executeQuery } = createClient();
|
|
40
|
-
|
|
41
|
-
await client.scan("retailers", ItemsSchema, "status = :s", {
|
|
42
|
-
":s": { S: "active" },
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
46
|
-
action: "scan",
|
|
47
|
-
body: {
|
|
48
|
-
TableName: "retailers",
|
|
49
|
-
FilterExpression: "status = :s",
|
|
50
|
-
ExpressionAttributeValues: { ":s": { S: "active" } },
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("forwards ExclusiveStartKey from the options object", async () => {
|
|
56
|
-
const { client, executeQuery } = createClient();
|
|
57
|
-
const exclusiveStartKey = { id: { S: "retailer-18" } };
|
|
58
|
-
|
|
59
|
-
await client.scan("retailers", ItemsSchema, { exclusiveStartKey });
|
|
60
|
-
|
|
61
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
62
|
-
action: "scan",
|
|
63
|
-
body: {
|
|
64
|
-
TableName: "retailers",
|
|
65
|
-
ExclusiveStartKey: exclusiveStartKey,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("treats an empty object as empty scan options", async () => {
|
|
71
|
-
const { client, executeQuery } = createClient();
|
|
72
|
-
|
|
73
|
-
await client.scan("retailers", ItemsSchema, {});
|
|
74
|
-
|
|
75
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
76
|
-
action: "scan",
|
|
77
|
-
body: { TableName: "retailers" },
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("rejects objects with unknown scan option keys", async () => {
|
|
82
|
-
const { client, executeQuery } = createClient();
|
|
83
|
-
|
|
84
|
-
await expect(
|
|
85
|
-
Reflect.apply(client.scan, client, [
|
|
86
|
-
"retailers",
|
|
87
|
-
ItemsSchema,
|
|
88
|
-
{ label: "not scan options" },
|
|
89
|
-
]),
|
|
90
|
-
).rejects.toThrow("Invalid DynamoDB scan options: label");
|
|
91
|
-
expect(executeQuery).not.toHaveBeenCalled();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("forwards pagination and parallel-scan options", async () => {
|
|
95
|
-
const { client, executeQuery } = createClient();
|
|
96
|
-
|
|
97
|
-
await client.scan(
|
|
98
|
-
"retailers",
|
|
99
|
-
ItemsSchema,
|
|
100
|
-
{
|
|
101
|
-
exclusiveStartKey: { id: { S: "page-2" } },
|
|
102
|
-
limit: 25,
|
|
103
|
-
projectionExpression: "id, name",
|
|
104
|
-
indexName: "gsi1",
|
|
105
|
-
segment: 0,
|
|
106
|
-
totalSegments: 4,
|
|
107
|
-
filterExpression: "#s = :s",
|
|
108
|
-
expressionAttributeNames: { "#s": "status" },
|
|
109
|
-
expressionAttributeValues: { ":s": { S: "active" } },
|
|
110
|
-
},
|
|
111
|
-
{ label: "page retailers" },
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
115
|
-
action: "scan",
|
|
116
|
-
body: {
|
|
117
|
-
TableName: "retailers",
|
|
118
|
-
ExclusiveStartKey: { id: { S: "page-2" } },
|
|
119
|
-
Limit: 25,
|
|
120
|
-
ProjectionExpression: "id, name",
|
|
121
|
-
IndexName: "gsi1",
|
|
122
|
-
Segment: 0,
|
|
123
|
-
TotalSegments: 4,
|
|
124
|
-
FilterExpression: "#s = :s",
|
|
125
|
-
ExpressionAttributeNames: { "#s": "status" },
|
|
126
|
-
ExpressionAttributeValues: { ":s": { S: "active" } },
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
expect(executeQuery).toHaveBeenCalledWith(expect.anything(), undefined, {
|
|
130
|
-
label: "page retailers",
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("keeps positional names and metadata working together", async () => {
|
|
135
|
-
const { client, executeQuery } = createClient();
|
|
136
|
-
|
|
137
|
-
await client.scan(
|
|
138
|
-
"retailers",
|
|
139
|
-
ItemsSchema,
|
|
140
|
-
"#s = :s",
|
|
141
|
-
{ ":s": { S: "active" } },
|
|
142
|
-
{ "#s": "status" },
|
|
143
|
-
{ label: "filtered scan" },
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
expect(parsedBody(executeQuery).body).toEqual({
|
|
147
|
-
TableName: "retailers",
|
|
148
|
-
FilterExpression: "#s = :s",
|
|
149
|
-
ExpressionAttributeValues: { ":s": { S: "active" } },
|
|
150
|
-
ExpressionAttributeNames: { "#s": "status" },
|
|
151
|
-
});
|
|
152
|
-
expect(executeQuery.mock.calls[0][2]).toEqual({ label: "filtered scan" });
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("keeps schemas that strip pagination cursors backward compatible", async () => {
|
|
156
|
-
const lastEvaluatedKey = {
|
|
157
|
-
id: { S: "retailer-18" },
|
|
158
|
-
version: { N: "2" },
|
|
159
|
-
};
|
|
160
|
-
const { client } = createClient({
|
|
161
|
-
Items: [{ id: { S: "retailer-1" } }],
|
|
162
|
-
LastEvaluatedKey: lastEvaluatedKey,
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
await expect(
|
|
166
|
-
client.scan("retailers", ItemsSchema, { limit: 25 }),
|
|
167
|
-
).resolves.toEqual({
|
|
168
|
-
Items: [{ id: { S: "retailer-1" } }],
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("preserves mixed AttributeValue pagination cursors", async () => {
|
|
173
|
-
const lastEvaluatedKey = {
|
|
174
|
-
id: { S: "retailer-18" },
|
|
175
|
-
version: { N: "2" },
|
|
176
|
-
};
|
|
177
|
-
const PageSchema = z.object({
|
|
178
|
-
Items: z.array(z.unknown()).optional(),
|
|
179
|
-
LastEvaluatedKey: z.record(z.unknown()).optional(),
|
|
180
|
-
});
|
|
181
|
-
const { client } = createClient({
|
|
182
|
-
Items: [],
|
|
183
|
-
LastEvaluatedKey: lastEvaluatedKey,
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
const page = await client.scan("retailers", PageSchema, {});
|
|
187
|
-
|
|
188
|
-
expect(page.LastEvaluatedKey).toEqual(lastEvaluatedKey);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("threads each returned pagination cursor into the next request", async () => {
|
|
192
|
-
const LastEvaluatedKeySchema = z.object({
|
|
193
|
-
id: z.object({ S: z.string() }),
|
|
194
|
-
});
|
|
195
|
-
const PageSchema = z.object({
|
|
196
|
-
Items: z.array(z.string()),
|
|
197
|
-
LastEvaluatedKey: LastEvaluatedKeySchema.optional(),
|
|
198
|
-
});
|
|
199
|
-
const lastEvaluatedKey = { id: { S: "retailer-1" } };
|
|
200
|
-
const executeQuery = vi
|
|
201
|
-
.fn()
|
|
202
|
-
.mockResolvedValueOnce({
|
|
203
|
-
Items: ["retailer-1"],
|
|
204
|
-
LastEvaluatedKey: lastEvaluatedKey,
|
|
205
|
-
})
|
|
206
|
-
.mockResolvedValueOnce({ Items: ["retailer-2"] });
|
|
207
|
-
const client = new DynamoDBClientImpl(TEST_CONFIG, executeQuery);
|
|
208
|
-
|
|
209
|
-
const items: string[] = [];
|
|
210
|
-
let exclusiveStartKey: z.infer<typeof LastEvaluatedKeySchema> | undefined;
|
|
211
|
-
do {
|
|
212
|
-
const page = await client.scan("retailers", PageSchema, {
|
|
213
|
-
exclusiveStartKey,
|
|
214
|
-
});
|
|
215
|
-
items.push(...page.Items);
|
|
216
|
-
exclusiveStartKey = page.LastEvaluatedKey;
|
|
217
|
-
} while (exclusiveStartKey);
|
|
218
|
-
|
|
219
|
-
expect(items).toEqual(["retailer-1", "retailer-2"]);
|
|
220
|
-
expect(executeQuery).toHaveBeenCalledTimes(2);
|
|
221
|
-
expect(parsedBody(executeQuery, 0).body).toEqual({
|
|
222
|
-
TableName: "retailers",
|
|
223
|
-
});
|
|
224
|
-
expect(parsedBody(executeQuery, 1).body).toEqual({
|
|
225
|
-
TableName: "retailers",
|
|
226
|
-
ExclusiveStartKey: lastEvaluatedKey,
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
describe("queryTable", () => {
|
|
232
|
-
it("treats a plain names map as ExpressionAttributeNames", async () => {
|
|
233
|
-
const { client, executeQuery } = createClient();
|
|
234
|
-
|
|
235
|
-
await client.queryTable(
|
|
236
|
-
"orders",
|
|
237
|
-
"#uid = :uid",
|
|
238
|
-
{ ":uid": { S: "user-123" } },
|
|
239
|
-
ItemsSchema,
|
|
240
|
-
{ "#uid": "userId" },
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
expect(parsedBody(executeQuery)).toEqual({
|
|
244
|
-
action: "query",
|
|
245
|
-
body: {
|
|
246
|
-
TableName: "orders",
|
|
247
|
-
KeyConditionExpression: "#uid = :uid",
|
|
248
|
-
ExpressionAttributeValues: { ":uid": { S: "user-123" } },
|
|
249
|
-
ExpressionAttributeNames: { "#uid": "userId" },
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
});
|