"""Lambda smf-fan-response function module.""" import hashlib import hmac import json import logging import uuid import sentry_sdk import config from lambdacommon.common_config import logger from sentry_sdk import capture_exception from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration from sentry_sdk.integrations.logging import LoggingIntegration from src.connectors.kafka_producer import produce_for_id INVALID_REQUEST = 'Message was ignored due to an invalid URI.' INVALID_MESSAGE = 'Message ignored because it has an invalid format.' INVALID_MESSAGE_BODY = 'Message body is not a valid json.' MISSING_EMAIL = 'Message ignored for lack of email_address in message body.' SUCCESS_MESSAGE = 'Successfully sent {email_hash} message to topic:{topic}' logging_integration = LoggingIntegration( level=logging.INFO, event_level=logging.CRITICAL ) sentry_sdk.init( config.SENTRY_DSN, integrations=[AwsLambdaIntegration(), logging_integration] ) def handler(event, context): """Lambda entry point.""" try: return main_handler(event, context) except Exception as err: logger.error(f'Uncaught exception: {err} Sending it to DLQ topic.') request_id = event.get('requestContext', {}).get('requestId', '') produce_for_id(event, request_id, config.DLQ_TOPIC) def main_handler(event, context): """Lambda main logic.""" key = str(uuid.uuid4()) try: # Verify json body request_body = json.loads(event.get('body')) except Exception as e: logger.error(e) capture_exception(e) return _compose_response(400, {'status': 'IGNORED', 'message': INVALID_MESSAGE_BODY}) email_address = request_body.get('email_address') if not email_address: logger.warning(MISSING_EMAIL) return _compose_response(400, {'status': 'IGNORED', 'message': MISSING_EMAIL}) email_hash = hash_email(email_address) logger.info(f'Processing request for email: {email_hash} with key: {key}') vendor = event.get('pathParameters', {}).get('vendor') if not vendor: logger.warning(INVALID_REQUEST) return _compose_response(400, {'status': 'IGNORED', 'message': INVALID_REQUEST}) topic = config.VENDOR_TOPICS.get(vendor) if not topic: logger.warning(f'Unknown vendor or missing configuration for "{vendor}"') return _compose_response(400, {'status': 'IGNORED', 'message': INVALID_REQUEST}) if not event.get('headers'): logger.warning(INVALID_MESSAGE) return _compose_response(400, {'status': 'IGNORED', 'message': INVALID_MESSAGE}) produce_for_id(request_body, key, topic) logger.info(SUCCESS_MESSAGE.format(email_hash=email_hash, topic=topic)) return _compose_response(200, {'status': 'SUCCESS'}) def _compose_response(status_code, body): """Compose response.""" return { 'isBase64Encoded': False, 'statusCode': status_code, 'body': json.dumps(body) } def hash_email(email: str) -> str: """Hash email value.""" return hmac.new(config.EMAIL_HASH_SECRET, email.lower().encode(), hashlib.sha256).hexdigest()