"""trigger-state-machine.""" import json from os import path import uuid import boto3 from botocore.exceptions import ClientError from bulk_metadata_ingester_common.utils.logging import get_current_logger import config from lambdacommon.aws import s3 def handler(event: dict, context: object) -> dict: """Lambda entrypoint.""" # Check event key is for an xml file correlation_id = event.get('correlation_id') or str(uuid.uuid4()) logger = get_current_logger( config.ENVIRONMENT, config.LAMBDA_NAME, logging_level=config.LOGGING_LEVEL, correlation_id=correlation_id) logger.info(f'Event: {event}') logger.info(f'Context: {context}') bucket =\ event.get('detail', {}).get('requestParameters', {}).get('bucketName') key = event.get('detail', {}).get('requestParameters', {}).get('key') logger.info(f'Lambda triggered by: {bucket} | {key}') if not key or 'generated_csv' in key \ or (not key.endswith('.csv') and not key.endswith('.xlsx')): return logger.info(f'Executing state machine for: {key}') # Get key folder name to use as part of sfn execution name execution_name = ( f'{path.basename(path.dirname(key))}' f'-{uuid.uuid4()}' ) execution_id = get_execution_id(bucket, key) sfn_arn = config.STATE_MACHINE_ARN sfn_input = { 'key': key, 'bucket': bucket, 'execution_name': execution_name, 'execution_id': execution_id, 'state_machine_name': f'{config.ENVIRONMENT}-bulk-metadata-ingester-sfn', # noqa: E501 'correlation_id': correlation_id } logger.info(f'Starting Execution: {execution_name}') logger.info(f'Execution input: {json.dumps(sfn_input)}') client = boto3.client('stepfunctions') response = client.start_execution( stateMachineArn=sfn_arn, name=execution_name, input=json.dumps(sfn_input) ) execution_arn = response.get('executionArn') logger.info(f'Execution: {execution_arn}') return execution_arn def get_execution_id(bucket: str, key: str) -> dict: """Get the execution id for this file from S3 or generate one. We store the execution id in the S3 path of the input file. This allows every execution of the same file to have the same execution id. """ key_without_file_name = key.rsplit('/', 1)[0] execution_id_key = key_without_file_name + '/execution_id.json' # Try to read execution_id from S3 try: file = s3.client.get_object(Bucket=bucket, Key=execution_id_key) # Get the JSON content from the object file_content = file.get('Body').read() # Convert the JSON into a dict execution_id = json.loads(file_content)['execution_id'] return execution_id # If an execution id is not found, generate one except ClientError as e: # The only expected error is for the file to not exist yet if e.response['Error']['Code'] != 'NoSuchKey': raise e execution_id = str(uuid.uuid4()) execution_id_json = json.dumps({'execution_id': execution_id}) # Write JSON in same dir as source key s3.resource.Object(bucket, execution_id_key).put( Body=execution_id_json) return execution_id