"""Lambda shared module.""" import functools import os import shutil from common_config import logger from constants import errors from constants import file_params import lambda_exceptions def say_hello(): """Generate high SLA Orchard greeting and logs it.""" logger.info('Hello, world!') def extract_triggered_key(cloudwatch_event): """Get S3 bucket and key from CloudWatch event. Args: cloudwatch_event (dict): AWS CloudWatch event data object. Returns: (str, str): Tuple of bucket and key names. """ request_params = cloudwatch_event.get( 'detail', {}).get('requestParameters', {}) bucket_name = request_params.get('bucketName', '') key = request_params.get('key', '') if not (bucket_name and key): raise lambda_exceptions.UnexpectedEventBody(['key', 'bucket']) return bucket_name, key def remove_none_entries(source): """Remove entries with None values from dictionary. Args: source (dict): Source dictionary containing entries with None values. Returns: dict: Return a new dictionary with None values filtered out. """ return {key: val for key, val in source.items() if val is not None} def clean_directory(path): """Remove all files and sub directories from provided directory. Args: path (str): path to directory """ try: if os.path.exists(path): for file_object in os.listdir(path): object_path = os.path.join(path, file_object) if os.path.isfile(object_path): os.unlink(object_path) else: shutil.rmtree(object_path) else: os.makedirs(path) except Exception as e: logger.exception(str(e)) def handle_lambda_result(lambda_name, default_error_code): """Run handler function, process, format and return its result. This decorator helps to keep responses of different Lambdas in PHF publishing State Machine in unified format: `` { 'function': 'lambda_name', 'status': 'lambda_processing_status', 's3_object': { 'key': key, 'bucket': bucket }, 'error_code': 'error_code_or_None', 'error_description': 'error_description_or_None', } `` Example: `` @handle_lambda_result def index(): # Place your lambda logic here. return status, s3_object, error_code, error_description `` Args: lambda_name (str): name of triggered lambda. default_error_code (str): error code that should be returned in case of unhandled exceptions. """ def wrap(fn): @functools.wraps(fn) def wrapper(event, context): s3_obj = event.get('s3_object', { 'key': 'Key is not identified', 'bucket': 'Bucket is not identified'}) key = s3_obj.get('key', 'Key is not identified') bucket = s3_obj.get('bucket', 'Bucket is not identified') error_code = default_error_code additional_info = None try: result = fn(event, context) status = result['status'] s3_obj = result.get('s3_object', {}) error_code = result.get('error_code', default_error_code) error_description = result.get('error_description') additional_info = result.get('additional_info') except Exception as e: error_code = getattr(e, 'error_code', error_code) status = errors.INTERNAL_ERROR_STATUS error_description = [str(e)] logger.exception(error_description) lambda_exceptions.notify_and_raise( lambda_name, status, error_code, key, bucket, {'message': error_description}) finally: status_result = { 'function': lambda_name, 'status': status, 's3_object': s3_obj, 'error_code': error_code, 'error_description': error_description } if additional_info: status_result['additional_info'] = additional_info clean_directory(file_params.DEFAULT_DOWNLOAD_DIRECTORY) return remove_none_entries(status_result) return wrapper return wrap def safe_cast(val, to_type, default=None): """Cast the value to given type. Args: val (int|str|float): value to cast. to_type (class): type class to convert the value to. default (int|str|float): value to return in result if conversion fails. Returns: value (int|str|float): Converted or default value. """ try: return to_type(val) except (ValueError, TypeError): return default