"""Lambda function module.""" import json import config from marshmallow import fields from marshmallow import Schema from marshmallow import ValidationError from owsrequest import request from src.common import common_config from src.common import lambda_exceptions class GeneralStatusMessage(Schema): """General Status Message Schema.""" filename = fields.String(required=True) function = fields.String(required=True) status = fields.String(required=True) bucket = fields.String(required=True) errors = fields.Dict(required=False) status_time = fields.Str(required=False) class SnsParseError(Exception): """Exception class for malformed SNS messages.""" def __init__(self, err, msg, asset_message, timestamp): """Construct exception with an err object and string message. Args: err (object): Either a dict of marshmallow validation error or a generic exception. msg (str): Error message. """ self.error = err self.asset_message = asset_message self.timestamp = timestamp Exception.__init__(self, msg) def sns_event_get_general_status_details(event): """Get filename, status and description from SNS message. Args: event (dict): AWS SNS event object. Returns: (dict, str): Tuple of message, timestamp. """ try: sns_body = event['Records'][0]['Sns'] message_json = sns_body['Message'] message = json.loads(message_json) timestamp = message.get('status_time') or sns_body['Timestamp'] GeneralStatusMessage().load(message) return message, timestamp except ValidationError as e: raise SnsParseError( err=e.messages, msg='SNS message schema error: {}'.format(json.dumps(e.messages)), asset_message=message, timestamp=timestamp) def post_general_asset_status(message, timestamp): """Call ows-asset-transcoder post asset status handler. Args: message (dict): Status message. timestamp (str): Time when notification was published. Returns: bool: True if success else raise Exception. """ data = { 'filename': message['filename'], 'status': message['status'], 'timestamp': timestamp } if 'errors' in message: data['errors'] = message['errors'] response = request.process( application=config.APPLICATION_NAME, environment=common_config.ENVIRONMENT, method='POST', service_name=common_config.OWS_ASSETS_SERVICE_NAME, path=common_config.POST_ASSET_STATUS_PATH, json=data ) if response.status_code == 404: raise lambda_exceptions.PostAssetNotFoundError(response) if response.status_code != 200: raise lambda_exceptions.PostAssetError(response) return response.json() def handler(event, context): """Lambda entry point. Saves asset general status. Args: event (dict): Object with asset status info. context (dict): Environment state. Returns: bool: True if success else raise Exception. """ try: message, timestamp = sns_event_get_general_status_details(event) post_general_asset_status(message, timestamp) return {'status': 'OK', 'message': 'general status message sent'} except SnsParseError as e: # try sending this error to microservice if 'filename' in e.asset_message: err_msg = { 'filename': e.asset_message['filename'], 'status': e.asset_message.get('status', 'status_error'), 'errors': {'sns_parse_error': e.error} } post_general_asset_status(err_msg, e.timestamp) raise except Exception as e: config.logger.exception(str(e)) raise