@moicky/dynamodb
Version:
Contains a collection of convenience functions for working with AWS DynamoDB
165 lines (164 loc) • 6.14 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.transactWriteItems = void 0;
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
const lib_1 = require("../lib");
/**
* Performs a TransactWriteItems operation against DynamoDB. This allows you to perform multiple write operations in a single transaction.
* @param transactItems - Array of items to transact. Each item can be a Put, Update, Delete, or ConditionCheck operation.
* @param args - The additional arguments to override or specify for {@link TransactWriteItemsCommandInput}
* @returns A promise that resolves to a record of response items
*
* @example
* Perform a TransactWriteItems operation
* ```javascript
* const response = await transactWriteItems([
* {
* Put: {
* item: {
* PK: "User/1",
* SK: "Book/1",
* title: "The Great Gatsby",
* author: "F. Scott Fitzgerald",
* released: 1925,
* },
* },
* },
* {
* Update: {
* key: { PK: "User/1", SK: "Book/1" },
* updateData: { title: "The Great Gatsby - Updated" },
* },
* },
* {
* Delete: {
* key: { PK: "User/1", SK: "Book/1" },
* },
* },
* {
* ConditionCheck: {
* key: { PK: "User/1", SK: "Book/1" },
* ConditionExpression: "#title = :title",
* conditionData: { title: "The Great Gatsby" },
* },
* },
* ]);
*
* console.log(response);
* ```
*/
async function transactWriteItems(transactItems, args = {}) {
return new Promise(async (resolve, reject) => {
args = (0, lib_1.withDefaults)(args, "transactWriteItems");
if (transactItems.length > 100) {
throw new Error("[@moicky/dynamodb]: TransactWriteItems can only handle up to 100 items");
}
const now = Date.now();
const table = args.TableName || (0, lib_1.getDefaultTable)();
const populatedItems = transactItems.map((item) => {
if (item.ConditionCheck) {
return handleConditionCheck(item.ConditionCheck, { now, table });
}
else if (item.Put) {
return handlePutItem(item.Put, { now, table });
}
else if (item.Delete) {
return handleDeleteItem(item.Delete, { now, table });
}
else if (item.Update) {
return handleUpdateItem(item.Update, { now, table });
}
else {
throw new Error("[@moicky/dynamodb]: Invalid TransactItem: " + JSON.stringify(item));
}
});
return (0, lib_1.getClient)()
.send(new client_dynamodb_1.TransactWriteItemsCommand({
TransactItems: populatedItems,
...args,
}))
.then((res) => {
const results = {};
Object.entries(res.ItemCollectionMetrics || {}).forEach(([tableName, metrics]) => {
const unmarshalledMetrics = metrics.map((metric) => ({
Key: (0, lib_1.unmarshallWithOptions)(metric.ItemCollectionKey || {}),
SizeEstimateRangeGB: metric.SizeEstimateRangeGB,
}));
if (!results[tableName]) {
results[tableName] = [];
}
results[tableName].push(...unmarshalledMetrics);
});
resolve(results);
})
.catch(reject);
});
}
exports.transactWriteItems = transactWriteItems;
function handleExpressionAttributes(rest, data) {
return {
ExpressionAttributeValues: (0, lib_1.getAttributeValues)(data || {}, {
attributesToGet: (0, lib_1.getAttributesFromExpression)(rest.ConditionExpression || "", ":"),
}),
ExpressionAttributeNames: (0, lib_1.getAttributeNames)(data || {}, {
attributesToGet: (0, lib_1.getAttributesFromExpression)(rest.ConditionExpression || ""),
}),
};
}
function handleConditionCheck(params, args) {
const { key, conditionData, ...rest } = params;
return {
ConditionCheck: {
Key: (0, lib_1.stripKey)(key, { TableName: rest.TableName || args.table }),
...handleExpressionAttributes(rest, conditionData),
...rest,
TableName: rest.TableName || args.table,
},
};
}
function handlePutItem(params, args) {
const populatedData = structuredClone(params.item);
populatedData.createdAt ??= args.now;
const { item, conditionData, ...rest } = params;
return {
Put: {
Item: (0, lib_1.marshallWithOptions)(populatedData),
...handleExpressionAttributes(rest, conditionData),
...rest,
TableName: rest.TableName || args.table,
},
};
}
function handleDeleteItem(params, args) {
const { key, conditionData, ...rest } = params;
return {
Delete: {
Key: (0, lib_1.stripKey)(key, { TableName: rest.TableName || args.table }),
...handleExpressionAttributes(rest, conditionData),
...rest,
TableName: rest.TableName || args.table,
},
};
}
function handleUpdateItem(params, args) {
const { key, updateData, conditionData, ...rest } = params;
const populatedData = structuredClone(updateData);
populatedData.updatedAt ??= args.now;
const mergedData = { ...populatedData, ...conditionData };
const UpdateExpression = "SET " +
Object.keys(populatedData)
.map((key) => `#${key} = :${key}`)
.join(", ");
return {
Update: {
Key: (0, lib_1.stripKey)(key, { TableName: rest.TableName || args.table }),
UpdateExpression,
ExpressionAttributeValues: (0, lib_1.getAttributeValues)(mergedData),
ExpressionAttributeNames: (0, lib_1.getAttributeNames)({}, {
attributesToGet: (0, lib_1.getAttributesFromExpression)(rest.ConditionExpression || "").concat(Object.keys(populatedData)),
}),
...rest,
TableName: rest.TableName || args.table,
},
};
}