"""Lambda function module.""" import copy import json from lambdacommon.common_config import logger import sentry_sdk from sentry_sdk import capture_exception from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration from config import DEV_ENVIRONMENT from config import ENVIRONMENT from config import IGNORE_ALL_EVENTS from config import S3_BUCKET_NAME from config import S3_DESTINATION_KEY_TEMPLATE from config import S3_PER_LABEL_DIR from config import SENTRY_DSN from src.connectors import s3 from src.connectors.ows_moneyhub import OwsMoneyhub from src.constants.constants import Error from src.constants.constants import FeatureFlag from src.constants.constants import IGNORE_EMPTY_PAYLOAD from src.constants.constants import IGNORE_MESSAGE from src.utils import functions from src.utils.custom_dataclasses import InternalAttachmentPayload from src.utils.features import is_feature_enabled if SENTRY_DSN: sentry_sdk.init( dsn=SENTRY_DSN, environment=ENVIRONMENT, integrations=[AwsLambdaIntegration(timeout_warning=True)], ) def _format_period_ids_string(period_ids: list[int]) -> str: """Format period ids to store in dynamodb. Args: period_ids (list): periods ids list Returns: str: Formatted periods string, separated by coma e.g. 217,218,219 """ return ','.join(str(period_id) for period_id in period_ids) def _get_attachment_destination_key(account_id: int, account_type: str, file_name: str) -> str: """Get attachment destination key for S3 move operation. Args: account_id (int): account id account_type (str): account type file_name (str): attachment file name Result: str: destination S3 key suitable for move operation """ account_dir = '{}{}'.format(account_type, account_id) dest_key = S3_DESTINATION_KEY_TEMPLATE.format( attachments_dir=S3_PER_LABEL_DIR, account_dir=account_dir, file_name=file_name ) return dest_key def handler(event: dict, context: object) -> None: """Lambda entry point.""" try: logger.info('event=' + str(event) + 'context=' + str(context)) if IGNORE_ALL_EVENTS: logger.warning(IGNORE_MESSAGE) return if not event: logger.warning(IGNORE_EMPTY_PAYLOAD) return # S3 event feature flag - s3 event parsing, validation logic and email sending gated if not is_feature_enabled(FeatureFlag.S3_EVENT): sns_body = event['Records'][0]['Sns'] message_json = sns_body['Message'] message = json.loads(message_json) attachment_month_data = functions.s3_event_get_attachment_details( message['s3_event'], message['attachment_attrs'] ) attachment_month_data['event_timestamp'] = sns_body['Timestamp'] else: validation_error = functions.validate_file(event) if validation_error: s3_key = event['Records'][0]['s3']['object'].get('key') error_message = f'Verification error: {s3_key} {validation_error.value}' logger.error(error_message) functions.send_error_email(event, validation_error) return # Parse file attributes from S3 key s3_key = event['Records'][0]['s3']['object']['key'] parsed_key = functions.parse_file_key(s3_key) attachment_month_data = functions.s3_event_get_attachment_details(event, parsed_key) attachment_month_data['event_timestamp'] = event['Records'][0]['eventTime'] account_id = attachment_month_data['label_id'] account_type = attachment_month_data['account_type'] source_key = attachment_month_data['file_key'] file_name = functions.get_file_name_from_key(source_key) dest_key = _get_attachment_destination_key(account_id, account_type, file_name) # Move the file to its destination move_successful = s3.move_object( bucket=S3_BUCKET_NAME, source_key=source_key, dest_key=dest_key ) if move_successful: attachment_month_data['file_key'] = dest_key else: logger.error(Error.MOVE_ERROR) period_id = attachment_month_data['period_ids'] # generate primary key for monthly label Item attachment_month_data['label_type_id_period_id'] = ( functions.get_attachment_primary_key_value(account_type, account_id, [period_id]) ) # Get ready quarter data attachment_quarter_data = copy.copy(attachment_month_data) quarter_period_ids = functions.get_quarter_periods_by_period(period_id) attachment_quarter_data['period_ids'] = _format_period_ids_string(quarter_period_ids) # Form sort key, to avoid duplicates attachment_quarter_data['file_name'] = functions.format_quarter_file_name( attachment_quarter_data['file_name'], period_id ) # Form primary key attachment_quarter_data['label_type_id_period_id'] = ( functions.get_attachment_primary_key_value( attachment_quarter_data['account_type'], attachment_quarter_data['label_id'], quarter_period_ids, ) ) if ENVIRONMENT != DEV_ENVIRONMENT: file_key = attachment_month_data['file_key'] payload = InternalAttachmentPayload( contract_id=attachment_month_data['contract_id'], file_type=attachment_month_data['file_type'].lower(), file_location=f's3://{S3_BUCKET_NAME}/{file_key}', upload_date=attachment_month_data['upload_date'], ) OwsMoneyhub.create_internal_statement_attachment(account_id, period_id, payload) except Exception as e: logger.exception(str(e)) capture_exception(e) raise e