UNPKG

@nerdware/ddb-single-table

Version:

A schema-based DynamoDB modeling tool, high-level API, and type-generator built to supercharge single-table designs!⚡

244 lines (243 loc) 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DdbClientFieldParser = void 0; const util_dynamodb_1 = require("@aws-sdk/util-dynamodb"); const ts_type_safety_utils_1 = require("@nerdware/ts-type-safety-utils"); const errors_js_1 = require("../utils/errors.js"); const convertJsDates_js_1 = require("./convertJsDates.js"); /** * Utility class for preparing DynamoDB client command arguments and parsing client responses. */ class DdbClientFieldParser { /** * Default {@link MarshallingConfigs} for the DynamoDB `marshall` and `unmarshall` util fns. * > Note: the SDK defaults all of these options to `false`. */ static DEFAULT_MARSHALLING_CONFIGS = { marshallOptions: { convertEmptyValues: false, removeUndefinedValues: true, convertClassInstanceToMap: true, convertTopLevelContainer: false, allowImpreciseNumbers: false, }, unmarshallOptions: { wrapNumbers: false, convertWithoutMapWrapper: false, }, }; tableName; defaultMarshallingConfigs; constructor({ tableName, marshallingConfigs }) { this.tableName = tableName; this.defaultMarshallingConfigs = { marshallOptions: { ...DdbClientFieldParser.DEFAULT_MARSHALLING_CONFIGS.marshallOptions, ...marshallingConfigs?.marshallOptions, }, unmarshallOptions: { ...DdbClientFieldParser.DEFAULT_MARSHALLING_CONFIGS.unmarshallOptions, ...marshallingConfigs?.unmarshallOptions, }, }; } ///////////////////////////////////////////////////////////////////////////// // MARSHALLING UTIL METHODS: /** Invokes the `marshall` util function with the default marshalling configs. */ marshall = (data, marshallOpts) => { return (0, util_dynamodb_1.marshall)(data, { ...this.defaultMarshallingConfigs.marshallOptions, ...marshallOpts, }); }; /** Converts JS `Date` objects to datetime strings, and then marshalls the result. */ marshallAndConvertDates = (data, marshallOpts) => { return this.marshall((0, convertJsDates_js_1.convertJsDates)("toDB", data), marshallOpts); }; /** Invokes the `unmarshall` util function with the default unmarshalling configs. */ unmarshall = (data, unmarshallOpts) => { return (0, util_dynamodb_1.unmarshall)(data, { ...this.defaultMarshallingConfigs.unmarshallOptions, ...unmarshallOpts, }); }; /** Unmarshalls the provided `data`, and then converts datetime strings to JS `Date` objects. */ unmarshallAndConvertDates = (data, unmarshallOpts) => { return (0, convertJsDates_js_1.convertJsDates)("fromDB", this.unmarshall(data, unmarshallOpts)); }; ///////////////////////////////////////////////////////////////////////////// // COMMAND-ARG PREP METHODS: isBatchGetItemRequestItems = (requestItems) => { return !(0, ts_type_safety_utils_1.isArray)(requestItems[this.tableName]); }; prepRequestItems = (requestItems, marshallOptions) => { return { [this.tableName]: this.isBatchGetItemRequestItems(requestItems) ? { ...requestItems[this.tableName], Keys: requestItems[this.tableName].Keys.map((key) => this.marshall(key, marshallOptions)), } : requestItems[this.tableName].map((batchWriteReq) => { const { PutRequest, DeleteRequest } = batchWriteReq; if (PutRequest) return { PutRequest: { Item: this.marshallAndConvertDates(PutRequest.Item, marshallOptions), }, }; if (DeleteRequest) return { DeleteRequest: { Key: this.marshall(DeleteRequest.Key, marshallOptions), }, }; throw new errors_js_1.ItemInputError(`Invalid request item for BatchWriteItem operation. Expected a valid PutRequest or DeleteRequest. Received: ${(0, ts_type_safety_utils_1.safeJsonStringify)(batchWriteReq)}`); }), }; }; /** * This method prepares command arguments for DynamoDB client commands: * - Adds the `TableName` parameter * - Marshalls attribute values (`Key`, `Item`, `ExpressionAttributeValues`, etc.) * - Converts JS Date objects to ISO-8601 datetime strings */ prepCommandArgs = (unmarshalledCommandArgs, marshallOpts) => { // Destructure fields that require marshalling: const { Key: key, Item: item, ExpressionAttributeValues: eav, ExclusiveStartKey: exclStartKey, RequestItems: requestItems, TransactItems: transactItems, ...otherCommandArgs } = unmarshalledCommandArgs; return { // Always add `TableName` UNLESS it's a batch or transaction command: ...(!requestItems && !transactItems && { TableName: this.tableName }), ...(key && { Key: this.marshall(key, marshallOpts) }), ...(item && { Item: this.marshallAndConvertDates(item, marshallOpts) }), ...(eav && { ExpressionAttributeValues: this.marshallAndConvertDates(eav, marshallOpts) }), ...(exclStartKey && { ExclusiveStartKey: this.marshall(exclStartKey, marshallOpts) }), ...(requestItems && { RequestItems: this.prepRequestItems(requestItems, marshallOpts) }), ...(transactItems && { TransactItems: transactItems.map( // Destructure the transactWriteItem to get the relevant fields ({ ConditionCheck: { Key: ccKey, ExpressionAttributeValues: ccEAV, ...ccRest } = {}, Put: { Item: putItem, ExpressionAttributeValues: putEAV, ...putRest } = {}, Update: { Key: updateKey, ExpressionAttributeValues: updEAV, ...updateRest } = {}, Delete: { Key: deleteKey, ExpressionAttributeValues: delEAV, ...delRest } = {}, }) => ({ ...(ccKey && { ConditionCheck: { ...ccRest, TableName: this.tableName, Key: this.marshall(ccKey, marshallOpts), ...(ccEAV && { ExpressionAttributeValues: this.marshallAndConvertDates(ccEAV, marshallOpts), }), }, }), ...(putItem && { Put: { ...putRest, TableName: this.tableName, Item: this.marshallAndConvertDates(putItem, marshallOpts), ...(putEAV && { ExpressionAttributeValues: this.marshallAndConvertDates(putEAV, marshallOpts), }), }, }), ...(updateKey && { Update: { ...updateRest, TableName: this.tableName, Key: this.marshall(updateKey, marshallOpts), ...(updEAV && { ExpressionAttributeValues: this.marshallAndConvertDates(updEAV, marshallOpts), }), }, }), ...(deleteKey && { Delete: { ...delRest, TableName: this.tableName, Key: this.marshall(deleteKey, marshallOpts), ...(delEAV && { ExpressionAttributeValues: this.marshallAndConvertDates(delEAV, marshallOpts), }), }, }), })), }), ...otherCommandArgs, }; }; ///////////////////////////////////////////////////////////////////////////// // RESPONSE-PARSING METHODS: isSingleItemCollectionMetrics = (icMetrics) => { return !!icMetrics.ItemCollectionKey || !!icMetrics.SizeEstimateRangeGB; }; parseSingleItemCollectionMetrics = ({ ItemCollectionKey, SizeEstimateRangeGB }, unmarshallOptions) => ({ ...(SizeEstimateRangeGB && { SizeEstimateRangeGB, }), ...(ItemCollectionKey && { ItemCollectionKey: this.unmarshall(ItemCollectionKey, unmarshallOptions), }), }); parseItemCollectionMetrics = (icMetrics, unmarshallOptions) => { return this.isSingleItemCollectionMetrics(icMetrics) ? this.parseSingleItemCollectionMetrics(icMetrics, unmarshallOptions) : { [this.tableName]: icMetrics[this.tableName].map((icm) => this.parseSingleItemCollectionMetrics(icm, unmarshallOptions)), }; }; /** * This method parses dynamodb-client reponses: * - Unmarshalls attribute values (`Item`, `Items`, `Attributes`, `ItemCollectionKey`, etc.) * - Converts ISO-8601 datetime strings to JS Date objects */ parseClientResponse = (clientResponse, unmarshallOptions) => { // Destructure fields that require unmarshalling: const { Item: item, Items: items, Attributes: attributes, LastEvaluatedKey: lastEvalKey, ItemCollectionMetrics: icMetrics, Responses: responses, UnprocessedKeys: unprocessedKeys, UnprocessedItems: unprocessedItems, ...otherFields } = clientResponse; return { ...(item && { Item: this.unmarshallAndConvertDates(item, unmarshallOptions), }), ...(items && { Items: items.map((item) => this.unmarshallAndConvertDates(item, unmarshallOptions)), }), ...(attributes && { Attributes: this.unmarshallAndConvertDates(attributes, unmarshallOptions), }), ...(lastEvalKey && { LastEvaluatedKey: this.unmarshall(lastEvalKey, unmarshallOptions), }), ...(icMetrics && { ItemCollectionMetrics: this.parseItemCollectionMetrics(icMetrics), }), ...(responses && { Responses: { [this.tableName]: responses[this.tableName].map((item) => this.unmarshallAndConvertDates(item, unmarshallOptions)), }, }), ...((0, ts_type_safety_utils_1.isArray)(unprocessedKeys?.[this.tableName]?.Keys) && { UnprocessedKeys: { [this.tableName]: { ...unprocessedKeys[this.tableName], Keys: unprocessedKeys[this.tableName].Keys.map((key) => this.unmarshall(key, unmarshallOptions)), }, }, }), ...((0, ts_type_safety_utils_1.isArray)(unprocessedItems?.[this.tableName]) && { UnprocessedItems: { [this.tableName]: unprocessedItems[this.tableName].map(({ PutRequest, DeleteRequest }) => ({ ...(PutRequest && { PutRequest: { Item: this.unmarshallAndConvertDates(PutRequest.Item ?? {}, unmarshallOptions), }, }), ...(DeleteRequest && { DeleteRequest: { Key: this.unmarshall(DeleteRequest.Key ?? {}, unmarshallOptions), }, }), })), }, }), ...otherFields, }; }; } exports.DdbClientFieldParser = DdbClientFieldParser;