UNPKG

@gp_jcisneros/aws-utils

Version:

AWS SDK utilities for GreenPay microservices

409 lines (362 loc) 11.1 kB
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb'); const { DynamoDBDocumentClient, GetCommand, PutCommand, QueryCommand, ScanCommand, DeleteCommand, UpdateCommand, BatchGetCommand, } = require('@aws-sdk/lib-dynamodb'); const { AWSError, DatabaseError } = require('@gp_jcisneros/errors'); /** * Utilities for AWS DynamoDB operations */ class DynamoUtils { constructor(region = process.env.AWS_REGION || 'us-east-1') { const client = new DynamoDBClient({ region }); this.docClient = DynamoDBDocumentClient.from(client); } /** * Get an item from DynamoDB * @param {string} tableName - Name of the DynamoDB table * @param {Object} key - Primary key of the item * @returns {Promise<Object|null>} - The found item or null */ async getItem(tableName, key) { try { const command = new GetCommand({ TableName: tableName, Key: key, }); const response = await this.docClient.send(command); return response.Item || null; } catch (error) { // Si ya es un error personalizado, lo relanzamos if (error instanceof AWSError || error instanceof DatabaseError) { throw error; } throw new DatabaseError( `DynamoDB get failed for ${tableName}: ${error.message}`, 'GET', tableName ); } } /** * Put an item in DynamoDB * @param {string} tableName - Name of the DynamoDB table * @param {Object} item - Item to put * @returns {Promise<Object>} - Put result */ async putItem(tableName, item) { try { const command = new PutCommand({ TableName: tableName, Item: item, }); await this.docClient.send(command); return { success: true, item, }; } catch (error) { // Si ya es un error personalizado, lo relanzamos if (error instanceof AWSError || error instanceof DatabaseError) { throw error; } throw new DatabaseError( `DynamoDB put failed for ${tableName}: ${error.message}`, 'INSERT', tableName ); } } /** * Update an item in DynamoDB * @param {string} tableName - Name of the DynamoDB table * @param {Object} key - Primary key of the item * @param {Object} updateExpression - Update expression * @param {Object} expressionAttributeValues - Expression attribute values * @param {Object} expressionAttributeNames - Expression attribute names * @returns {Promise<Object>} - Update result */ async updateItem( tableName, key, updateExpression, expressionAttributeValues = {}, expressionAttributeNames = {} ) { try { const command = new UpdateCommand({ TableName: tableName, Key: key, UpdateExpression: updateExpression, ExpressionAttributeValues: expressionAttributeValues, ExpressionAttributeNames: expressionAttributeNames, ReturnValues: 'ALL_NEW', }); const response = await this.docClient.send(command); return { success: true, item: response.Attributes, }; } catch (error) { // Si ya es un error personalizado, lo relanzamos if (error instanceof AWSError || error instanceof DatabaseError) { throw error; } throw new DatabaseError( `DynamoDB update failed for ${tableName}: ${error.message}`, 'UPDATE', tableName ); } } /** * Delete an item from DynamoDB * @param {string} tableName - Name of the DynamoDB table * @param {Object} key - Primary key of the item * @returns {Promise<Object>} - Delete result */ async deleteItem(tableName, key) { try { const command = new DeleteCommand({ TableName: tableName, Key: key, }); await this.docClient.send(command); return { success: true, }; } catch (error) { // Si ya es un error personalizado, lo relanzamos if (error instanceof AWSError || error instanceof DatabaseError) { throw error; } throw new DatabaseError( `DynamoDB delete failed for ${tableName}: ${error.message}`, 'DELETE', tableName ); } } /** * Query items from DynamoDB * @param {string} tableName - Name of the DynamoDB table * @param {string} keyConditionExpression - Key condition expression * @param {Object} expressionAttributeValues - Expression attribute values * @param {Object|undefined} expressionAttributeNames - Expression attribute names (can be undefined) * @param {string} indexName - Name of the index to query * @param {number} limit - Limit the number of items to return * @param {string} filterExpression - Filter expression for additional filtering * @returns {Promise<Array>} - Query results */ async queryItems( tableName, keyConditionExpression, expressionAttributeValues = {}, expressionAttributeNames = undefined, indexName = null, limit = 1000, filterExpression = null ) { const allItems = []; let lastEvaluatedKey = undefined; do { const commandParams = { TableName: tableName, KeyConditionExpression: keyConditionExpression, ExpressionAttributeValues: expressionAttributeValues, Limit: limit, }; // Solo agregar ExpressionAttributeNames si es un objeto no vacío if ( expressionAttributeNames && typeof expressionAttributeNames === 'object' && Object.keys(expressionAttributeNames).length > 0 ) { commandParams.ExpressionAttributeNames = expressionAttributeNames; } // Solo agregar IndexName si se proporciona if (indexName) { commandParams.IndexName = indexName; } // Solo agregar FilterExpression si se proporciona if (filterExpression) { commandParams.FilterExpression = filterExpression; } if (lastEvaluatedKey) { commandParams.ExclusiveStartKey = lastEvaluatedKey; } try { const response = await this.docClient.send( new QueryCommand(commandParams) ); const items = response.Items || []; allItems.push(...items); lastEvaluatedKey = response.LastEvaluatedKey; } catch (error) { // Si ya es un error personalizado, lo relanzamos if (error instanceof AWSError || error instanceof DatabaseError) { throw error; } throw new DatabaseError( `DynamoDB query failed for ${tableName}: ${error.message}`, 'QUERY', tableName ); } } while (lastEvaluatedKey); return allItems; } /** * Scan items from DynamoDB * @param {string} tableName - Name of the DynamoDB table * @param {string} filterExpression - Filter expression * @param {Object} expressionAttributeValues - Expression attribute values * @param {Object} expressionAttributeNames - Expression attribute names * @returns {Promise<Array>} - Scan results */ async scanItems( tableName, filterExpression = null, expressionAttributeValues = {}, expressionAttributeNames = {} ) { try { const commandParams = { TableName: tableName, }; // Solo agregar FilterExpression si se proporciona if (filterExpression) { commandParams.FilterExpression = filterExpression; } // Solo agregar ExpressionAttributeValues si no está vacío if (Object.keys(expressionAttributeValues).length > 0) { commandParams.ExpressionAttributeValues = expressionAttributeValues; } // Solo agregar ExpressionAttributeNames si no está vacío if (Object.keys(expressionAttributeNames).length > 0) { commandParams.ExpressionAttributeNames = expressionAttributeNames; } const command = new ScanCommand(commandParams); const response = await this.docClient.send(command); return response.Items || []; } catch (error) { // Si ya es un error personalizado, lo relanzamos if (error instanceof AWSError || error instanceof DatabaseError) { throw error; } throw new DatabaseError( `DynamoDB scan failed for ${tableName}: ${error.message}`, 'QUERY', tableName ); } } /** * Batch get items from DynamoDB * @param {string} tableName - Name of the DynamoDB table * @param {Array} keys - Array of keys to get * @returns {Promise<Array>} - Batch get results */ async batchGetItems(tableName, keys) { try { // Filtrar keys que no sean null o undefined const validKeys = keys.filter((key) => key !== null && key !== undefined); if (validKeys.length === 0) { return []; } const command = new BatchGetCommand({ RequestItems: { [tableName]: { Keys: validKeys, }, }, }); const response = await this.docClient.send(command); return response.Responses?.[tableName] || []; } catch (error) { // Si ya es un error personalizado, lo relanzamos if (error instanceof AWSError || error instanceof DatabaseError) { throw error; } throw new DatabaseError( `DynamoDB batch get failed for ${tableName}: ${error.message}`, 'QUERY', tableName ); } } // Static methods for convenience static async getItem(tableName, key) { const utils = new DynamoUtils(); return utils.getItem(tableName, key); } static async putItem(tableName, item) { const utils = new DynamoUtils(); return utils.putItem(tableName, item); } static async updateItem( tableName, key, updateExpression, expressionAttributeValues = {}, expressionAttributeNames = {} ) { const utils = new DynamoUtils(); return utils.updateItem( tableName, key, updateExpression, expressionAttributeValues, expressionAttributeNames ); } static async deleteItem(tableName, key) { const utils = new DynamoUtils(); return utils.deleteItem(tableName, key); } static async queryItems( tableName, keyConditionExpression, expressionAttributeValues = {}, expressionAttributeNames = {}, indexName = null, limit = 1000, filterExpression = null ) { const utils = new DynamoUtils(); return utils.queryItems( tableName, keyConditionExpression, expressionAttributeValues, expressionAttributeNames, indexName, limit, filterExpression ); } static async scanItems( tableName, filterExpression = null, expressionAttributeValues = {}, expressionAttributeNames = {} ) { const utils = new DynamoUtils(); return utils.scanItems( tableName, filterExpression, expressionAttributeValues, expressionAttributeNames ); } static async batchGetItems(tableName, keys) { const utils = new DynamoUtils(); return utils.batchGetItems(tableName, keys); } } module.exports = { DynamoUtils };