""" DynamoDB Client ============= Provides dynamodb_resource attribute for connection to AWS DynamDB service. https://aws.amazon.com/documentation/dynamodb/ """ from copy import deepcopy import boto3 from boto3.dynamodb import conditions from botocore.client import Config from oto import response from masters_registry import config from masters_registry import utils from masters_registry.constant import field_const dynamodb_resource = boto3.resource( 'dynamodb', 'us-east-1', config=Config(max_pool_connections=1000)) client = dynamodb_resource.meta.client ITEM_FIELD = 'Item' def batch_update_item_fields( table, key, verb, expressions, attribute_names, attribute_values, batch_size=4000, force=False): """Util to update several fields within an item.""" update_expression = '{} {}'.format(verb, ', '.join(expressions)) if not expressions or batch_size > len(update_expression) and not force: return False update_item = getattr(table, 'update_item') kwargs = { 'Key': key, 'UpdateExpression': update_expression, 'ExpressionAttributeNames': attribute_names, } if attribute_values: kwargs['ExpressionAttributeValues'] = attribute_values update_item(**kwargs) log_updated_timestamp(table, key) return True def update_isrc_item_field(isrc, field_name, new_value): """Update field in entry in masters-active table with provided data. This function also updates `updated_timestamp` field so that Snowflake could correctly process data from DynamoDB. For details refer to MR-3000 ticket. Args: isrc (str): ISRC (Key in masters-active table) field_name (str): Name of field in isrc entry, e.g. territories new_value (object): Value to save into given field Returns: response.Response: dict with updated info """ kwargs = { 'TableName': config.DYNAMODB_MASTERS_ACTIVE, 'Key': {field_const.ISRC: isrc}, # Condition ensures that update will happen only if ISRC exists 'ConditionExpression': conditions.Key(field_const.ISRC).eq(isrc), 'UpdateExpression': 'SET {} = :new_value'.format(field_name), 'ExpressionAttributeValues': {':new_value': new_value}, } kwargs = _add_updated_timestamp_to_kwargs(kwargs) client.update_item(**kwargs) response_payload = { 'isrc': isrc, 'field_name': field_name, 'saved_value': new_value } return response.Response(message=response_payload) def _add_updated_timestamp_to_kwargs(update_kwargs): """Add expression to update `updated_timestamp` field of ISRC item. When performing update of ISRC item we want to log timestamp when given item was changed. Args: update_kwargs (dict): Kwargs for dynamo_client.update_item func Returns: dict: kwargs with added expression that updates timestamp field """ update_timestamp_expression = 'updated_timestamp = :new_timestamp' timestamp_attribute_value = { ':new_timestamp': utils.get_timestamp_for_dynamo() } new_kwargs = deepcopy(update_kwargs) new_kwargs['UpdateExpression'] = ', '.join( [new_kwargs['UpdateExpression'], update_timestamp_expression]) new_kwargs['ExpressionAttributeValues'].update(timestamp_attribute_value) return new_kwargs def log_updated_timestamp(table, isrc): """Write updated_timestamp field for given isrc. Supposed to be used when performing actions with masters_active table. Args: table (object): DynamoDB table isrc (str): ISRC to update """ kwargs = { 'Key': isrc, 'UpdateExpression': 'SET updated_timestamp = :r', 'ExpressionAttributeValues': {':r': utils.get_timestamp_for_dynamo()}, } table.update_item(**kwargs) return True def update_isrc(isrc): """Replace or create an ISRC item in DynamoDB active table. Args: isrc (dict): ISRC item to be written Returns: response.Response: response object, containing an updated item """ isrc[field_const.UPDATED_TIMESTAMP] = utils.get_timestamp_for_dynamo() kwargs = { 'TableName': config.DYNAMODB_MASTERS_ACTIVE, 'Item': isrc } client.put_item(**kwargs) return response.Response(isrc)