@model-ts/dynamodb 4.0.0 → 4.2.0
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 +12 -0
- package/dist/cjs/__test__/client-env-guard.test.d.ts +1 -0
- package/dist/cjs/__test__/client-env-guard.test.js +28 -0
- package/dist/cjs/__test__/client-env-guard.test.js.map +1 -0
- package/dist/cjs/__test__/conformance.test.d.ts +1 -0
- package/dist/cjs/__test__/conformance.test.js +1835 -0
- package/dist/cjs/__test__/conformance.test.js.map +1 -0
- package/dist/cjs/__test__/in-memory.spec.test.d.ts +1 -0
- package/dist/cjs/__test__/in-memory.spec.test.js +185 -0
- package/dist/cjs/__test__/in-memory.spec.test.js.map +1 -0
- package/dist/cjs/__test__/rollback.test.d.ts +1 -0
- package/dist/cjs/__test__/rollback.test.js +196 -0
- package/dist/cjs/__test__/rollback.test.js.map +1 -0
- package/dist/cjs/client.d.ts +2 -2
- package/dist/cjs/client.js +14 -6
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/errors.d.ts +13 -1
- package/dist/cjs/errors.js +12 -1
- package/dist/cjs/errors.js.map +1 -1
- package/dist/cjs/in-memory/document-client.d.ts +45 -0
- package/dist/cjs/in-memory/document-client.js +795 -0
- package/dist/cjs/in-memory/document-client.js.map +1 -0
- package/dist/cjs/in-memory/expression.d.ts +42 -0
- package/dist/cjs/in-memory/expression.js +585 -0
- package/dist/cjs/in-memory/expression.js.map +1 -0
- package/dist/cjs/in-memory/index.d.ts +2 -0
- package/dist/cjs/in-memory/index.js +6 -0
- package/dist/cjs/in-memory/index.js.map +1 -0
- package/dist/cjs/in-memory/spec.d.ts +23 -0
- package/dist/cjs/in-memory/spec.js +141 -0
- package/dist/cjs/in-memory/spec.js.map +1 -0
- package/dist/cjs/in-memory/store.d.ts +73 -0
- package/dist/cjs/in-memory/store.js +267 -0
- package/dist/cjs/in-memory/store.js.map +1 -0
- package/dist/cjs/in-memory/treap.d.ts +31 -0
- package/dist/cjs/in-memory/treap.js +187 -0
- package/dist/cjs/in-memory/treap.js.map +1 -0
- package/dist/cjs/in-memory/utils.d.ts +10 -0
- package/dist/cjs/in-memory/utils.js +38 -0
- package/dist/cjs/in-memory/utils.js.map +1 -0
- package/dist/cjs/sandbox.d.ts +2 -0
- package/dist/cjs/sandbox.js +172 -1
- package/dist/cjs/sandbox.js.map +1 -1
- package/dist/esm/__test__/client-env-guard.test.d.ts +1 -0
- package/dist/esm/__test__/client-env-guard.test.js +26 -0
- package/dist/esm/__test__/client-env-guard.test.js.map +1 -0
- package/dist/esm/__test__/conformance.test.d.ts +1 -0
- package/dist/esm/__test__/conformance.test.js +1833 -0
- package/dist/esm/__test__/conformance.test.js.map +1 -0
- package/dist/esm/__test__/in-memory.spec.test.d.ts +1 -0
- package/dist/esm/__test__/in-memory.spec.test.js +183 -0
- package/dist/esm/__test__/in-memory.spec.test.js.map +1 -0
- package/dist/esm/__test__/rollback.test.d.ts +1 -0
- package/dist/esm/__test__/rollback.test.js +194 -0
- package/dist/esm/__test__/rollback.test.js.map +1 -0
- package/dist/esm/client.d.ts +2 -2
- package/dist/esm/client.js +14 -6
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.d.ts +13 -1
- package/dist/esm/errors.js +10 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/in-memory/document-client.d.ts +45 -0
- package/dist/esm/in-memory/document-client.js +791 -0
- package/dist/esm/in-memory/document-client.js.map +1 -0
- package/dist/esm/in-memory/expression.d.ts +42 -0
- package/dist/esm/in-memory/expression.js +577 -0
- package/dist/esm/in-memory/expression.js.map +1 -0
- package/dist/esm/in-memory/index.d.ts +2 -0
- package/dist/esm/in-memory/index.js +3 -0
- package/dist/esm/in-memory/index.js.map +1 -0
- package/dist/esm/in-memory/spec.d.ts +23 -0
- package/dist/esm/in-memory/spec.js +138 -0
- package/dist/esm/in-memory/spec.js.map +1 -0
- package/dist/esm/in-memory/store.d.ts +73 -0
- package/dist/esm/in-memory/store.js +258 -0
- package/dist/esm/in-memory/store.js.map +1 -0
- package/dist/esm/in-memory/treap.d.ts +31 -0
- package/dist/esm/in-memory/treap.js +183 -0
- package/dist/esm/in-memory/treap.js.map +1 -0
- package/dist/esm/in-memory/utils.d.ts +10 -0
- package/dist/esm/in-memory/utils.js +28 -0
- package/dist/esm/in-memory/utils.js.map +1 -0
- package/dist/esm/sandbox.d.ts +2 -0
- package/dist/esm/sandbox.js +172 -1
- package/dist/esm/sandbox.js.map +1 -1
- package/package.json +2 -1
- package/src/__test__/client-env-guard.test.ts +31 -0
- package/src/__test__/conformance.test.ts +2042 -0
- package/src/__test__/in-memory.spec.test.ts +230 -0
- package/src/__test__/rollback.test.ts +279 -0
- package/src/client.ts +17 -4
- package/src/errors.ts +24 -0
- package/src/in-memory/document-client.ts +1140 -0
- package/src/in-memory/expression.ts +730 -0
- package/src/in-memory/index.ts +2 -0
- package/src/in-memory/spec.ts +159 -0
- package/src/in-memory/store.ts +360 -0
- package/src/in-memory/treap.ts +239 -0
- package/src/in-memory/utils.ts +45 -0
- package/src/sandbox.ts +227 -1
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createInMemoryDocumentClient = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const expression_1 = require("./expression");
|
|
7
|
+
const store_1 = require("./store");
|
|
8
|
+
const spec_1 = require("./spec");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
class InMemoryDocumentClientImpl {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.tables = new Map();
|
|
13
|
+
}
|
|
14
|
+
get(params) {
|
|
15
|
+
return this.request("get", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
this.assertSupportedParams("get", params);
|
|
17
|
+
const tableName = this.getRequiredTableName(params);
|
|
18
|
+
const key = this.getRequiredKey(params, "Key");
|
|
19
|
+
const table = this.getTable(tableName);
|
|
20
|
+
const item = table.cloneItemByKey(key);
|
|
21
|
+
return item ? { Item: item } : {};
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
put(params) {
|
|
25
|
+
return this.request("put", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
this.assertSupportedParams("put", params);
|
|
27
|
+
const tableName = this.getRequiredTableName(params);
|
|
28
|
+
const table = this.getTable(tableName);
|
|
29
|
+
const item = params.Item;
|
|
30
|
+
if (!item || typeof item !== "object") {
|
|
31
|
+
throw new errors_1.NotSupportedError({
|
|
32
|
+
method: "put",
|
|
33
|
+
featurePath: "put.Item",
|
|
34
|
+
reason: "Item must be an object.",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
this.assertPrimaryKey(item, "put");
|
|
38
|
+
this.assertExpressionAttributeInputs(params, [params.ConditionExpression]);
|
|
39
|
+
const existing = table.cloneItemByKey({ PK: item.PK, SK: item.SK });
|
|
40
|
+
this.assertCondition("put", params, existing);
|
|
41
|
+
table.put(item);
|
|
42
|
+
return {};
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
update(params) {
|
|
46
|
+
return this.request("update", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
var _a;
|
|
48
|
+
this.assertSupportedParams("update", params);
|
|
49
|
+
const tableName = this.getRequiredTableName(params);
|
|
50
|
+
const key = this.getRequiredKey(params, "Key");
|
|
51
|
+
if (typeof params.UpdateExpression !== "string") {
|
|
52
|
+
throw new errors_1.NotSupportedError({
|
|
53
|
+
method: "update",
|
|
54
|
+
featurePath: "update.UpdateExpression",
|
|
55
|
+
reason: "UpdateExpression is required.",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const returnValues = (_a = params.ReturnValues) !== null && _a !== void 0 ? _a : "NONE";
|
|
59
|
+
if (!["NONE", "ALL_NEW"].includes(returnValues)) {
|
|
60
|
+
throw this.validationError(`Unsupported ReturnValues value: ${returnValues}`);
|
|
61
|
+
}
|
|
62
|
+
this.assertExpressionAttributeInputs(params, [
|
|
63
|
+
params.ConditionExpression,
|
|
64
|
+
params.UpdateExpression,
|
|
65
|
+
]);
|
|
66
|
+
const table = this.getTable(tableName);
|
|
67
|
+
const existing = table.cloneItemByKey(key);
|
|
68
|
+
this.assertCondition("update", params, existing);
|
|
69
|
+
const base = existing !== null && existing !== void 0 ? existing : { PK: key.PK, SK: key.SK };
|
|
70
|
+
const parsed = this.parseUpdateExpressionOrValidationError(params.UpdateExpression, {
|
|
71
|
+
method: "update",
|
|
72
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
73
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
74
|
+
item: base,
|
|
75
|
+
});
|
|
76
|
+
const next = utils_1.cloneItem(base);
|
|
77
|
+
for (const assignment of parsed.set) {
|
|
78
|
+
if ((assignment.attribute === "PK" || assignment.attribute === "SK") && assignment.value !== next[assignment.attribute]) {
|
|
79
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${assignment.attribute}. This attribute is part of the key`);
|
|
80
|
+
}
|
|
81
|
+
next[assignment.attribute] = assignment.value;
|
|
82
|
+
}
|
|
83
|
+
for (const attribute of parsed.remove) {
|
|
84
|
+
if (attribute === "PK" || attribute === "SK") {
|
|
85
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${attribute}. This attribute is part of the key`);
|
|
86
|
+
}
|
|
87
|
+
delete next[attribute];
|
|
88
|
+
}
|
|
89
|
+
this.assertPrimaryKey(next, "update");
|
|
90
|
+
table.put(next);
|
|
91
|
+
if (returnValues === "ALL_NEW") {
|
|
92
|
+
return { Attributes: utils_1.cloneItem(next) };
|
|
93
|
+
}
|
|
94
|
+
return {};
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
delete(params) {
|
|
98
|
+
return this.request("delete", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
this.assertSupportedParams("delete", params);
|
|
100
|
+
const tableName = this.getRequiredTableName(params);
|
|
101
|
+
const key = this.getRequiredKey(params, "Key");
|
|
102
|
+
const table = this.getTable(tableName);
|
|
103
|
+
const existing = table.cloneItemByKey(key);
|
|
104
|
+
this.assertExpressionAttributeInputs(params, [params.ConditionExpression]);
|
|
105
|
+
this.assertCondition("delete", params, existing);
|
|
106
|
+
table.deleteByKey(key);
|
|
107
|
+
return {};
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
query(params) {
|
|
111
|
+
return this.request("query", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
var _a;
|
|
113
|
+
this.assertSupportedParams("query", params);
|
|
114
|
+
const tableName = this.getRequiredTableName(params);
|
|
115
|
+
const table = this.getTable(tableName);
|
|
116
|
+
if (typeof params.KeyConditionExpression !== "string") {
|
|
117
|
+
throw new errors_1.NotSupportedError({
|
|
118
|
+
method: "query",
|
|
119
|
+
featurePath: "query.KeyConditionExpression",
|
|
120
|
+
reason: "KeyConditionExpression is required.",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const indexName = this.resolveIndexName(params.IndexName);
|
|
124
|
+
if (params.ConsistentRead && store_1.isGSI(indexName)) {
|
|
125
|
+
throw this.validationError("Consistent read cannot be true when querying a GSI");
|
|
126
|
+
}
|
|
127
|
+
this.assertExpressionAttributeInputs(params, [
|
|
128
|
+
params.KeyConditionExpression,
|
|
129
|
+
params.FilterExpression,
|
|
130
|
+
]);
|
|
131
|
+
const condition = this.parseKeyConditionOrValidationError(params.KeyConditionExpression, {
|
|
132
|
+
method: "query",
|
|
133
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
134
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
135
|
+
});
|
|
136
|
+
if (!store_1.matchesKeyConditionDescriptor(indexName, condition)) {
|
|
137
|
+
throw new errors_1.NotSupportedError({
|
|
138
|
+
method: "query",
|
|
139
|
+
featurePath: "query.KeyConditionExpression",
|
|
140
|
+
reason: "KeyConditionExpression attributes do not match the selected index.",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (typeof condition.hashValue !== "string") {
|
|
144
|
+
throw new errors_1.NotSupportedError({
|
|
145
|
+
method: "query",
|
|
146
|
+
featurePath: "query.KeyConditionExpression.partitionValue",
|
|
147
|
+
reason: "Partition key values must be strings.",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const scanIndexForward = params.ScanIndexForward !== false;
|
|
151
|
+
const limit = this.normalizeLimit(params.Limit, "query");
|
|
152
|
+
const exclusiveStartKey = params.ExclusiveStartKey
|
|
153
|
+
? this.resolveExclusiveStartKey(params.ExclusiveStartKey, indexName, table)
|
|
154
|
+
: undefined;
|
|
155
|
+
const iterator = table.iterateQueryCandidates({
|
|
156
|
+
indexName,
|
|
157
|
+
hashKey: condition.hashValue,
|
|
158
|
+
rangeCondition: (_a = condition.range) === null || _a === void 0 ? void 0 : _a.condition,
|
|
159
|
+
scanIndexForward,
|
|
160
|
+
exclusiveStartKey,
|
|
161
|
+
})[Symbol.iterator]();
|
|
162
|
+
const items = [];
|
|
163
|
+
let scannedCount = 0;
|
|
164
|
+
let lastEvaluatedKey;
|
|
165
|
+
let next = iterator.next();
|
|
166
|
+
while (!next.done) {
|
|
167
|
+
const candidate = next.value;
|
|
168
|
+
scannedCount += 1;
|
|
169
|
+
const include = params.FilterExpression
|
|
170
|
+
? this.evaluateConditionOrValidationError(params.FilterExpression, candidate.item, {
|
|
171
|
+
method: "query",
|
|
172
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
173
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
174
|
+
}, "FilterExpression")
|
|
175
|
+
: true;
|
|
176
|
+
if (include) {
|
|
177
|
+
items.push(candidate.item);
|
|
178
|
+
}
|
|
179
|
+
if (limit !== undefined && scannedCount >= limit) {
|
|
180
|
+
lastEvaluatedKey = this.buildLastEvaluatedKey(indexName, candidate.item);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
next = iterator.next();
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
Items: items,
|
|
187
|
+
Count: items.length,
|
|
188
|
+
ScannedCount: scannedCount,
|
|
189
|
+
LastEvaluatedKey: lastEvaluatedKey,
|
|
190
|
+
};
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
scan(params) {
|
|
194
|
+
return this.request("scan", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
this.assertSupportedParams("scan", params);
|
|
196
|
+
const tableName = this.getRequiredTableName(params);
|
|
197
|
+
const table = this.getTable(tableName);
|
|
198
|
+
const limit = this.normalizeLimit(params.Limit, "scan");
|
|
199
|
+
this.assertExpressionAttributeInputs(params, [params.FilterExpression]);
|
|
200
|
+
const exclusiveStartKey = params.ExclusiveStartKey
|
|
201
|
+
? this.getRequiredKey({ Key: params.ExclusiveStartKey }, "ExclusiveStartKey")
|
|
202
|
+
: undefined;
|
|
203
|
+
const all = table.scanItems(exclusiveStartKey);
|
|
204
|
+
const items = [];
|
|
205
|
+
let scannedCount = 0;
|
|
206
|
+
let lastEvaluatedKey;
|
|
207
|
+
for (let index = 0; index < all.length; index += 1) {
|
|
208
|
+
const candidate = all[index];
|
|
209
|
+
scannedCount += 1;
|
|
210
|
+
const include = params.FilterExpression
|
|
211
|
+
? this.evaluateConditionOrValidationError(params.FilterExpression, candidate, {
|
|
212
|
+
method: "scan",
|
|
213
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
214
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
215
|
+
}, "FilterExpression")
|
|
216
|
+
: true;
|
|
217
|
+
if (include) {
|
|
218
|
+
items.push(candidate);
|
|
219
|
+
}
|
|
220
|
+
if (limit !== undefined && scannedCount >= limit) {
|
|
221
|
+
lastEvaluatedKey = { PK: candidate.PK, SK: candidate.SK };
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
Items: items,
|
|
227
|
+
Count: items.length,
|
|
228
|
+
ScannedCount: scannedCount,
|
|
229
|
+
LastEvaluatedKey: lastEvaluatedKey,
|
|
230
|
+
};
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
batchGet(params) {
|
|
234
|
+
return this.request("batchGet", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
235
|
+
this.assertSupportedParams("batchGet", params);
|
|
236
|
+
if (!params.RequestItems || typeof params.RequestItems !== "object") {
|
|
237
|
+
throw new errors_1.NotSupportedError({
|
|
238
|
+
method: "batchGet",
|
|
239
|
+
featurePath: "batchGet.RequestItems",
|
|
240
|
+
reason: "RequestItems is required.",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
const responses = {};
|
|
244
|
+
for (const [tableName, request] of Object.entries(params.RequestItems)) {
|
|
245
|
+
if (!request || typeof request !== "object") {
|
|
246
|
+
throw new errors_1.NotSupportedError({
|
|
247
|
+
method: "batchGet",
|
|
248
|
+
featurePath: "batchGet.RequestItems",
|
|
249
|
+
reason: "Each RequestItems entry must be an object.",
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
const keys = Array.isArray(request.Keys) ? request.Keys : [];
|
|
253
|
+
if (keys.length > 100) {
|
|
254
|
+
throw this.validationError("Too many items requested for the BatchGetItem call");
|
|
255
|
+
}
|
|
256
|
+
const table = this.getTable(tableName);
|
|
257
|
+
const tableResponses = [];
|
|
258
|
+
const unique = new Set();
|
|
259
|
+
for (const key of keys) {
|
|
260
|
+
const parsedKey = this.getRequiredKey({ Key: key }, "Key");
|
|
261
|
+
const dedupKey = `${parsedKey.PK}::${parsedKey.SK}`;
|
|
262
|
+
if (unique.has(dedupKey)) {
|
|
263
|
+
throw this.validationError("Provided list of item keys contains duplicates");
|
|
264
|
+
}
|
|
265
|
+
unique.add(dedupKey);
|
|
266
|
+
const item = table.cloneItemByKey(parsedKey);
|
|
267
|
+
if (item)
|
|
268
|
+
tableResponses.push(item);
|
|
269
|
+
}
|
|
270
|
+
responses[tableName] = tableResponses;
|
|
271
|
+
}
|
|
272
|
+
return { Responses: responses };
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
batchWrite(params) {
|
|
276
|
+
return this.request("batchWrite", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
277
|
+
this.assertSupportedParams("batchWrite", params);
|
|
278
|
+
if (!params.RequestItems || typeof params.RequestItems !== "object") {
|
|
279
|
+
throw new errors_1.NotSupportedError({
|
|
280
|
+
method: "batchWrite",
|
|
281
|
+
featurePath: "batchWrite.RequestItems",
|
|
282
|
+
reason: "RequestItems is required.",
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
for (const [tableName, requests] of Object.entries(params.RequestItems)) {
|
|
286
|
+
if (!Array.isArray(requests)) {
|
|
287
|
+
throw new errors_1.NotSupportedError({
|
|
288
|
+
method: "batchWrite",
|
|
289
|
+
featurePath: "batchWrite.RequestItems",
|
|
290
|
+
reason: "Each RequestItems entry must be an array.",
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if (requests.length > 25) {
|
|
294
|
+
throw this.validationError("Too many items requested for the BatchWriteItem call");
|
|
295
|
+
}
|
|
296
|
+
const table = this.getTable(tableName);
|
|
297
|
+
for (const entry of requests) {
|
|
298
|
+
if (entry.PutRequest) {
|
|
299
|
+
const item = entry.PutRequest.Item;
|
|
300
|
+
if (!item || typeof item !== "object") {
|
|
301
|
+
throw new errors_1.NotSupportedError({
|
|
302
|
+
method: "batchWrite",
|
|
303
|
+
featurePath: "batchWrite.RequestItems.PutRequest.Item",
|
|
304
|
+
reason: "PutRequest.Item must be an object.",
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
this.assertPrimaryKey(item, "batchWrite");
|
|
308
|
+
table.put(item);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (entry.DeleteRequest) {
|
|
312
|
+
const key = this.getRequiredKey({ Key: entry.DeleteRequest.Key }, "DeleteRequest.Key");
|
|
313
|
+
table.deleteByKey(key);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
throw this.validationError("Supplied AttributeValue has more than one datatypes set, must contain exactly one of the supported datatypes");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return { UnprocessedItems: {} };
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
transactWrite(params) {
|
|
323
|
+
return this.request("transactWrite", params, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
324
|
+
var _a, _b;
|
|
325
|
+
this.assertSupportedParams("transactWrite", params);
|
|
326
|
+
const transactItems = params.TransactItems;
|
|
327
|
+
if (!Array.isArray(transactItems) || transactItems.length === 0) {
|
|
328
|
+
throw new errors_1.NotSupportedError({
|
|
329
|
+
method: "transactWrite",
|
|
330
|
+
featurePath: "transactWrite.TransactItems",
|
|
331
|
+
reason: "TransactItems must be a non-empty array.",
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
if (transactItems.length > 100) {
|
|
335
|
+
throw this.validationError("Member must have length less than or equal to 100");
|
|
336
|
+
}
|
|
337
|
+
const touched = new Set();
|
|
338
|
+
const journal = new Map();
|
|
339
|
+
const remember = (tableName, key, before) => {
|
|
340
|
+
const marker = `${tableName}::${key.PK}::${key.SK}`;
|
|
341
|
+
if (!journal.has(marker)) {
|
|
342
|
+
journal.set(marker, {
|
|
343
|
+
tableName,
|
|
344
|
+
key,
|
|
345
|
+
before: before ? utils_1.cloneItem(before) : null,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
const ensureNoDuplicate = (tableName, key) => {
|
|
350
|
+
const marker = `${tableName}::${key.PK}::${key.SK}`;
|
|
351
|
+
if (touched.has(marker)) {
|
|
352
|
+
throw this.validationError("Transaction request cannot include multiple operations on one item");
|
|
353
|
+
}
|
|
354
|
+
touched.add(marker);
|
|
355
|
+
};
|
|
356
|
+
const rollback = () => {
|
|
357
|
+
for (const entry of [...journal.values()].reverse()) {
|
|
358
|
+
const table = this.getTable(entry.tableName);
|
|
359
|
+
if (entry.before === null) {
|
|
360
|
+
table.deleteByKey(entry.key);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
table.put(entry.before);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
try {
|
|
368
|
+
for (const transactionEntry of transactItems) {
|
|
369
|
+
if (transactionEntry.Put) {
|
|
370
|
+
const put = transactionEntry.Put;
|
|
371
|
+
const tableName = this.getRequiredTableName(put);
|
|
372
|
+
const table = this.getTable(tableName);
|
|
373
|
+
const item = put.Item;
|
|
374
|
+
if (!item || typeof item !== "object") {
|
|
375
|
+
throw new errors_1.NotSupportedError({
|
|
376
|
+
method: "transactWrite",
|
|
377
|
+
featurePath: "transactWrite.TransactItems.Put.Item",
|
|
378
|
+
reason: "Put.Item must be an object.",
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
this.assertPrimaryKey(item, "transactWrite");
|
|
382
|
+
const key = { PK: item.PK, SK: item.SK };
|
|
383
|
+
ensureNoDuplicate(tableName, key);
|
|
384
|
+
const existing = table.cloneItemByKey(key);
|
|
385
|
+
this.assertExpressionAttributeInputs(put, [put.ConditionExpression]);
|
|
386
|
+
this.assertCondition("transactWrite", put, existing);
|
|
387
|
+
remember(tableName, key, existing);
|
|
388
|
+
table.put(item);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (transactionEntry.Update) {
|
|
392
|
+
const update = transactionEntry.Update;
|
|
393
|
+
const tableName = this.getRequiredTableName(update);
|
|
394
|
+
const key = this.getRequiredKey(update, "Key");
|
|
395
|
+
ensureNoDuplicate(tableName, key);
|
|
396
|
+
const table = this.getTable(tableName);
|
|
397
|
+
const existing = table.cloneItemByKey(key);
|
|
398
|
+
this.assertExpressionAttributeInputs(update, [
|
|
399
|
+
update.ConditionExpression,
|
|
400
|
+
update.UpdateExpression,
|
|
401
|
+
]);
|
|
402
|
+
this.assertCondition("transactWrite", update, existing);
|
|
403
|
+
if (typeof update.UpdateExpression !== "string") {
|
|
404
|
+
throw new errors_1.NotSupportedError({
|
|
405
|
+
method: "transactWrite",
|
|
406
|
+
featurePath: "transactWrite.TransactItems.Update.UpdateExpression",
|
|
407
|
+
reason: "UpdateExpression is required.",
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
const base = existing !== null && existing !== void 0 ? existing : { PK: key.PK, SK: key.SK };
|
|
411
|
+
const parsed = this.parseUpdateExpressionOrValidationError(update.UpdateExpression, {
|
|
412
|
+
method: "transactWrite",
|
|
413
|
+
expressionAttributeNames: update.ExpressionAttributeNames,
|
|
414
|
+
expressionAttributeValues: update.ExpressionAttributeValues,
|
|
415
|
+
item: base,
|
|
416
|
+
});
|
|
417
|
+
const next = utils_1.cloneItem(base);
|
|
418
|
+
for (const assignment of parsed.set) {
|
|
419
|
+
if ((assignment.attribute === "PK" || assignment.attribute === "SK") && assignment.value !== next[assignment.attribute]) {
|
|
420
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${assignment.attribute}. This attribute is part of the key`);
|
|
421
|
+
}
|
|
422
|
+
next[assignment.attribute] = assignment.value;
|
|
423
|
+
}
|
|
424
|
+
for (const attribute of parsed.remove) {
|
|
425
|
+
if (attribute === "PK" || attribute === "SK") {
|
|
426
|
+
throw this.validationError(`One or more parameter values were invalid: Cannot update attribute ${attribute}. This attribute is part of the key`);
|
|
427
|
+
}
|
|
428
|
+
delete next[attribute];
|
|
429
|
+
}
|
|
430
|
+
this.assertPrimaryKey(next, "transactWrite");
|
|
431
|
+
remember(tableName, key, existing);
|
|
432
|
+
table.put(next);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (transactionEntry.Delete) {
|
|
436
|
+
const del = transactionEntry.Delete;
|
|
437
|
+
const tableName = this.getRequiredTableName(del);
|
|
438
|
+
const key = this.getRequiredKey(del, "Key");
|
|
439
|
+
ensureNoDuplicate(tableName, key);
|
|
440
|
+
const table = this.getTable(tableName);
|
|
441
|
+
const existing = table.cloneItemByKey(key);
|
|
442
|
+
this.assertExpressionAttributeInputs(del, [del.ConditionExpression]);
|
|
443
|
+
this.assertCondition("transactWrite", del, existing);
|
|
444
|
+
remember(tableName, key, existing);
|
|
445
|
+
table.deleteByKey(key);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (transactionEntry.ConditionCheck) {
|
|
449
|
+
const check = transactionEntry.ConditionCheck;
|
|
450
|
+
const tableName = this.getRequiredTableName(check);
|
|
451
|
+
const key = this.getRequiredKey(check, "Key");
|
|
452
|
+
ensureNoDuplicate(tableName, key);
|
|
453
|
+
const table = this.getTable(tableName);
|
|
454
|
+
const existing = table.cloneItemByKey(key);
|
|
455
|
+
this.assertExpressionAttributeInputs(check, [check.ConditionExpression]);
|
|
456
|
+
this.assertCondition("transactWrite", check, existing);
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
throw new errors_1.NotSupportedError({
|
|
460
|
+
method: "transactWrite",
|
|
461
|
+
featurePath: "transactWrite.TransactItems",
|
|
462
|
+
reason: "Each transaction entry must include Put, Update, Delete, or ConditionCheck.",
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
rollback();
|
|
468
|
+
if (error instanceof errors_1.NotSupportedError)
|
|
469
|
+
throw error;
|
|
470
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "ValidationException" &&
|
|
471
|
+
/Cannot update attribute (PK|SK)\. This attribute is part of the key/.test(String((_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : ""))) {
|
|
472
|
+
throw this.transactionCanceledError("Transaction cancelled, please refer cancellation reasons for specific reasons [ValidationError]");
|
|
473
|
+
}
|
|
474
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "ValidationException") {
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "ConditionalCheckFailedException") {
|
|
478
|
+
throw this.transactionCanceledError("Transaction cancelled, please refer cancellation reasons for specific reasons [None, ConditionalCheckFailed]");
|
|
479
|
+
}
|
|
480
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === "TransactionCanceledException") {
|
|
481
|
+
throw error;
|
|
482
|
+
}
|
|
483
|
+
throw this.transactionCanceledError((_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : "Transaction failed.");
|
|
484
|
+
}
|
|
485
|
+
return {};
|
|
486
|
+
}));
|
|
487
|
+
}
|
|
488
|
+
__inMemorySnapshot(tableName) {
|
|
489
|
+
return this.getTable(tableName).snapshot();
|
|
490
|
+
}
|
|
491
|
+
__inMemoryResetTable(tableName) {
|
|
492
|
+
this.getTable(tableName).clear();
|
|
493
|
+
}
|
|
494
|
+
request(method, params, fn) {
|
|
495
|
+
return {
|
|
496
|
+
promise: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
497
|
+
try {
|
|
498
|
+
return yield fn();
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
throw error;
|
|
502
|
+
}
|
|
503
|
+
}),
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
getTable(tableName) {
|
|
507
|
+
const existing = this.tables.get(tableName);
|
|
508
|
+
if (existing)
|
|
509
|
+
return existing;
|
|
510
|
+
const next = new store_1.InMemoryTableState();
|
|
511
|
+
this.tables.set(tableName, next);
|
|
512
|
+
return next;
|
|
513
|
+
}
|
|
514
|
+
assertSupportedParams(method, params) {
|
|
515
|
+
var _a;
|
|
516
|
+
const spec = spec_1.IN_MEMORY_SPEC.methods[method];
|
|
517
|
+
if (!spec) {
|
|
518
|
+
throw new errors_1.NotSupportedError({
|
|
519
|
+
method,
|
|
520
|
+
featurePath: method,
|
|
521
|
+
reason: `Unsupported method: ${method}`,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
for (const [key, value] of Object.entries(params)) {
|
|
525
|
+
if (typeof value === "undefined")
|
|
526
|
+
continue;
|
|
527
|
+
if ((_a = spec.unsupportedParams) === null || _a === void 0 ? void 0 : _a.includes(key)) {
|
|
528
|
+
throw new errors_1.NotSupportedError({
|
|
529
|
+
method,
|
|
530
|
+
featurePath: `${method}.${key}`,
|
|
531
|
+
reason: `${key} is not supported in in-memory mode.`,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
if (!spec.supportedParams.includes(key)) {
|
|
535
|
+
throw new errors_1.NotSupportedError({
|
|
536
|
+
method,
|
|
537
|
+
featurePath: `${method}.${key}`,
|
|
538
|
+
reason: `Unsupported parameter: ${key}`,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
getRequiredTableName(params) {
|
|
544
|
+
if (typeof params.TableName !== "string" || !params.TableName) {
|
|
545
|
+
throw new errors_1.NotSupportedError({
|
|
546
|
+
method: "documentClient",
|
|
547
|
+
featurePath: "TableName",
|
|
548
|
+
reason: "TableName must be a non-empty string.",
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
return params.TableName;
|
|
552
|
+
}
|
|
553
|
+
getRequiredKey(params, featurePath) {
|
|
554
|
+
const key = params.Key;
|
|
555
|
+
if (!key || typeof key !== "object") {
|
|
556
|
+
throw new errors_1.NotSupportedError({
|
|
557
|
+
method: "documentClient",
|
|
558
|
+
featurePath,
|
|
559
|
+
reason: "Key must be an object.",
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
if (typeof key.PK !== "string" || typeof key.SK !== "string") {
|
|
563
|
+
throw this.validationError("One or more parameter values were invalid: Type mismatch for key");
|
|
564
|
+
}
|
|
565
|
+
return { PK: key.PK, SK: key.SK };
|
|
566
|
+
}
|
|
567
|
+
assertPrimaryKey(item, method) {
|
|
568
|
+
if (typeof item.PK !== "string" || typeof item.SK !== "string") {
|
|
569
|
+
throw this.validationError("One or more parameter values were invalid: Type mismatch for key");
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
assertCondition(method, params, item) {
|
|
573
|
+
if (!params.ConditionExpression)
|
|
574
|
+
return;
|
|
575
|
+
const ok = this.evaluateConditionOrValidationError(params.ConditionExpression, item, {
|
|
576
|
+
method,
|
|
577
|
+
expressionAttributeNames: params.ExpressionAttributeNames,
|
|
578
|
+
expressionAttributeValues: params.ExpressionAttributeValues,
|
|
579
|
+
}, "ConditionExpression");
|
|
580
|
+
if (!ok) {
|
|
581
|
+
throw this.awsError("ConditionalCheckFailedException", "The conditional request failed.");
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
resolveIndexName(indexName) {
|
|
585
|
+
if (!indexName)
|
|
586
|
+
return store_1.PRIMARY_INDEX_NAME;
|
|
587
|
+
if (indexName === "GSI1") {
|
|
588
|
+
throw new errors_1.NotSupportedError({
|
|
589
|
+
method: "query",
|
|
590
|
+
featurePath: "query.IndexName",
|
|
591
|
+
reason: "GSI1 is intentionally excluded from in-memory mode.",
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
if (!store_1.isSupportedIndexName(indexName)) {
|
|
595
|
+
throw new errors_1.NotSupportedError({
|
|
596
|
+
method: "query",
|
|
597
|
+
featurePath: "query.IndexName",
|
|
598
|
+
reason: `Unsupported index: ${indexName}`,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
return store_1.parseIndexName(indexName);
|
|
602
|
+
}
|
|
603
|
+
resolveExclusiveStartKey(startKey, indexName, table) {
|
|
604
|
+
const key = this.getRequiredKey({ Key: startKey }, "query.ExclusiveStartKey");
|
|
605
|
+
const descriptor = table.getDescriptor(indexName);
|
|
606
|
+
const rangeKey = startKey === null || startKey === void 0 ? void 0 : startKey[descriptor.rangeAttribute];
|
|
607
|
+
const hashKey = startKey === null || startKey === void 0 ? void 0 : startKey[descriptor.hashAttribute];
|
|
608
|
+
if (typeof rangeKey !== "string" || typeof hashKey !== "string") {
|
|
609
|
+
throw this.validationError("Exclusive Start Key must have same size as table's key schema");
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
itemKey: utils_1.encodeItemKey(key.PK, key.SK),
|
|
613
|
+
rangeKey,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
buildLastEvaluatedKey(indexName, item) {
|
|
617
|
+
const key = {
|
|
618
|
+
PK: item.PK,
|
|
619
|
+
SK: item.SK,
|
|
620
|
+
};
|
|
621
|
+
if (indexName !== store_1.PRIMARY_INDEX_NAME) {
|
|
622
|
+
key[`${indexName}PK`] = item[`${indexName}PK`];
|
|
623
|
+
key[`${indexName}SK`] = item[`${indexName}SK`];
|
|
624
|
+
}
|
|
625
|
+
return key;
|
|
626
|
+
}
|
|
627
|
+
normalizeLimit(value, method) {
|
|
628
|
+
if (typeof value === "undefined")
|
|
629
|
+
return undefined;
|
|
630
|
+
if (typeof value !== "number" ||
|
|
631
|
+
!Number.isFinite(value) ||
|
|
632
|
+
value < 1) {
|
|
633
|
+
throw this.validationError("Limit must be greater than or equal to 1");
|
|
634
|
+
}
|
|
635
|
+
return Math.floor(value);
|
|
636
|
+
}
|
|
637
|
+
awsError(code, message) {
|
|
638
|
+
const error = new Error(message);
|
|
639
|
+
error.code = code;
|
|
640
|
+
error.name = code;
|
|
641
|
+
return error;
|
|
642
|
+
}
|
|
643
|
+
transactionCanceledError(message) {
|
|
644
|
+
return this.awsError("TransactionCanceledException", message);
|
|
645
|
+
}
|
|
646
|
+
validationError(message) {
|
|
647
|
+
return this.awsError("ValidationException", message);
|
|
648
|
+
}
|
|
649
|
+
parseKeyConditionOrValidationError(expression, context) {
|
|
650
|
+
try {
|
|
651
|
+
return expression_1.parseKeyConditionExpression(expression, context);
|
|
652
|
+
}
|
|
653
|
+
catch (error) {
|
|
654
|
+
if (error instanceof errors_1.NotSupportedError) {
|
|
655
|
+
throw this.validationError(this.toDynamoExpressionValidationMessage("KeyConditionExpression", error));
|
|
656
|
+
}
|
|
657
|
+
throw error;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
parseUpdateExpressionOrValidationError(expression, context) {
|
|
661
|
+
try {
|
|
662
|
+
return expression_1.parseUpdateExpression(expression, context);
|
|
663
|
+
}
|
|
664
|
+
catch (error) {
|
|
665
|
+
if (error instanceof errors_1.NotSupportedError) {
|
|
666
|
+
throw this.validationError(this.toDynamoExpressionValidationMessage("UpdateExpression", error));
|
|
667
|
+
}
|
|
668
|
+
throw error;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
evaluateConditionOrValidationError(expression, item, context, expressionType) {
|
|
672
|
+
try {
|
|
673
|
+
return expression_1.evaluateConditionExpression(expression, item, context);
|
|
674
|
+
}
|
|
675
|
+
catch (error) {
|
|
676
|
+
if (error instanceof errors_1.NotSupportedError) {
|
|
677
|
+
throw this.validationError(this.toDynamoExpressionValidationMessage(expressionType, error));
|
|
678
|
+
}
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
toDynamoExpressionValidationMessage(expressionType, error) {
|
|
683
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
684
|
+
const reason = String((_a = error.reason) !== null && _a !== void 0 ? _a : "").replace(/\.$/, "");
|
|
685
|
+
const namePlaceholder = (_b = error.featurePath.match(/ExpressionAttributeNames\.(#[A-Za-z_][A-Za-z0-9_]*)$/)) === null || _b === void 0 ? void 0 : _b[1];
|
|
686
|
+
if (namePlaceholder) {
|
|
687
|
+
return `Invalid ${expressionType}: An expression attribute name used in the document path is not defined; attribute name: ${namePlaceholder}`;
|
|
688
|
+
}
|
|
689
|
+
const valuePlaceholder = (_c = error.featurePath.match(/ExpressionAttributeValues\.(:[A-Za-z_][A-Za-z0-9_]*)$/)) === null || _c === void 0 ? void 0 : _c[1];
|
|
690
|
+
if (reason === "ExpressionAttributeValues are required for value placeholders" ||
|
|
691
|
+
reason === "Missing expression attribute value placeholder") {
|
|
692
|
+
const token = valuePlaceholder !== null && valuePlaceholder !== void 0 ? valuePlaceholder : ":missing";
|
|
693
|
+
return `Invalid ${expressionType}: An expression attribute value used in expression is not defined; attribute value: ${token}`;
|
|
694
|
+
}
|
|
695
|
+
if (reason === "Malformed SET assignment" ||
|
|
696
|
+
reason === "Malformed REMOVE assignment") {
|
|
697
|
+
const token = reason.includes("REMOVE") ? "REMOVE" : "SET";
|
|
698
|
+
return `Invalid UpdateExpression: Syntax error; token: "<EOF>", near: "${token}"`;
|
|
699
|
+
}
|
|
700
|
+
const invalidFunctionFromClause = (_d = reason.match(/^Unsupported clause:\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/)) === null || _d === void 0 ? void 0 : _d[1];
|
|
701
|
+
if (invalidFunctionFromClause) {
|
|
702
|
+
return `Invalid ${expressionType}: Invalid function name; function: ${invalidFunctionFromClause}`;
|
|
703
|
+
}
|
|
704
|
+
const unsupportedValue = (_e = reason.match(/^Unsupported value token:\s*(.+)$/)) === null || _e === void 0 ? void 0 : _e[1];
|
|
705
|
+
if (unsupportedValue) {
|
|
706
|
+
const fn = (_g = (_f = unsupportedValue.match(/\band\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/i)) === null || _f === void 0 ? void 0 : _f[1]) !== null && _g !== void 0 ? _g : (_h = unsupportedValue.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*\(/)) === null || _h === void 0 ? void 0 : _h[1];
|
|
707
|
+
if (fn) {
|
|
708
|
+
return `Invalid ${expressionType}: Invalid function name; function: ${fn}`;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return reason;
|
|
712
|
+
}
|
|
713
|
+
assertExpressionAttributeInputs(params, expressions) {
|
|
714
|
+
var _a, _b;
|
|
715
|
+
const activeExpressions = expressions.filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
716
|
+
if (activeExpressions.length === 0)
|
|
717
|
+
return;
|
|
718
|
+
if (typeof params.ExpressionAttributeValues !== "undefined") {
|
|
719
|
+
const values = params.ExpressionAttributeValues;
|
|
720
|
+
if (!values || typeof values !== "object" || Array.isArray(values)) {
|
|
721
|
+
throw this.validationError("ExpressionAttributeValues must not be empty");
|
|
722
|
+
}
|
|
723
|
+
const valueKeys = Object.keys(values);
|
|
724
|
+
if (valueKeys.length === 0) {
|
|
725
|
+
throw this.validationError("ExpressionAttributeValues must not be empty");
|
|
726
|
+
}
|
|
727
|
+
const usedValueTokens = new Set();
|
|
728
|
+
for (const expression of activeExpressions) {
|
|
729
|
+
for (const token of (_a = expression.match(/:[A-Za-z_][A-Za-z0-9_]*/g)) !== null && _a !== void 0 ? _a : []) {
|
|
730
|
+
usedValueTokens.add(token);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
const unusedValues = valueKeys
|
|
734
|
+
.filter((token) => !usedValueTokens.has(token))
|
|
735
|
+
.sort();
|
|
736
|
+
if (unusedValues.length > 0) {
|
|
737
|
+
throw this.validationError(`Value provided in ExpressionAttributeValues unused in expressions: keys: {${unusedValues.join(", ")}}`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
if (typeof params.ExpressionAttributeNames !== "undefined") {
|
|
741
|
+
const names = params.ExpressionAttributeNames;
|
|
742
|
+
if (!names || typeof names !== "object" || Array.isArray(names)) {
|
|
743
|
+
throw this.validationError("ExpressionAttributeNames must not be empty");
|
|
744
|
+
}
|
|
745
|
+
const nameKeys = Object.keys(names);
|
|
746
|
+
if (nameKeys.length === 0) {
|
|
747
|
+
throw this.validationError("ExpressionAttributeNames must not be empty");
|
|
748
|
+
}
|
|
749
|
+
const usedNameTokens = new Set();
|
|
750
|
+
for (const expression of activeExpressions) {
|
|
751
|
+
for (const token of (_b = expression.match(/#[A-Za-z_][A-Za-z0-9_]*/g)) !== null && _b !== void 0 ? _b : []) {
|
|
752
|
+
usedNameTokens.add(token);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const unusedNames = nameKeys
|
|
756
|
+
.filter((token) => !usedNameTokens.has(token))
|
|
757
|
+
.sort();
|
|
758
|
+
if (unusedNames.length > 0) {
|
|
759
|
+
throw this.validationError(`Value provided in ExpressionAttributeNames unused in expressions: keys: {${unusedNames.join(", ")}}`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const createUnsupportedRequest = (method) => {
|
|
765
|
+
return (_params) => ({
|
|
766
|
+
promise: () => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
767
|
+
throw new errors_1.NotSupportedError({
|
|
768
|
+
method,
|
|
769
|
+
featurePath: method,
|
|
770
|
+
reason: `${method} is not supported in in-memory mode.`,
|
|
771
|
+
});
|
|
772
|
+
}),
|
|
773
|
+
});
|
|
774
|
+
};
|
|
775
|
+
const createInMemoryDocumentClient = () => {
|
|
776
|
+
const instance = new InMemoryDocumentClientImpl();
|
|
777
|
+
const proxied = new Proxy(instance, {
|
|
778
|
+
get(target, prop, receiver) {
|
|
779
|
+
if (typeof prop === "string") {
|
|
780
|
+
if (prop in target) {
|
|
781
|
+
const value = Reflect.get(target, prop, receiver);
|
|
782
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
783
|
+
}
|
|
784
|
+
if (spec_1.IN_MEMORY_SPEC.unsupportedMethods.includes(prop)) {
|
|
785
|
+
return createUnsupportedRequest(prop);
|
|
786
|
+
}
|
|
787
|
+
return createUnsupportedRequest(prop);
|
|
788
|
+
}
|
|
789
|
+
return Reflect.get(target, prop, receiver);
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
return proxied;
|
|
793
|
+
};
|
|
794
|
+
exports.createInMemoryDocumentClient = createInMemoryDocumentClient;
|
|
795
|
+
//# sourceMappingURL=document-client.js.map
|