UNPKG

@moicky/dynamodb

Version:

Contains a collection of convenience functions for working with AWS DynamoDB

165 lines (164 loc) 6.14 kB
"use strict"; 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, }, }; }