diff --git a/app/clients/__init__.py b/app/clients/__init__.py index 89a97dab9..30c359631 100644 --- a/app/clients/__init__.py +++ b/app/clients/__init__.py @@ -16,23 +16,3 @@ class Client(object): STATISTICS_REQUESTED = 'requested' STATISTICS_DELIVERED = 'delivered' STATISTICS_FAILURE = 'failure' - - -class ClientResponse: - def __init__(self): - self.__response_model__ = None - - def response_code_to_object(self, response_code): - return self.__response_model__[response_code] - - def response_code_to_message(self, response_code): - return self.response_code_to_object(response_code)['message'] - - def response_code_to_notification_status(self, response_code): - return self.response_code_to_object(response_code)['notification_status'] - - def response_code_to_notification_statistics_status(self, response_code): - return self.response_code_to_object(response_code)['notification_statistics_status'] - - def response_code_to_notification_success(self, response_code): - return self.response_code_to_object(response_code)['success'] diff --git a/app/clients/email/aws_ses.py b/app/clients/email/aws_ses.py index 7f46a8a24..ae50a42c1 100644 --- a/app/clients/email/aws_ses.py +++ b/app/clients/email/aws_ses.py @@ -1,33 +1,33 @@ import boto3 from flask import current_app from monotonic import monotonic -from app.clients import ClientResponse, STATISTICS_DELIVERED, STATISTICS_FAILURE +from app.clients import STATISTICS_DELIVERED, STATISTICS_FAILURE from app.clients.email import (EmailClientException, EmailClient) +ses_response_map = { + 'Bounce': { + "message": 'Bounced', + "success": False, + "notification_status": 'bounce', + "notification_statistics_status": STATISTICS_FAILURE + }, + 'Delivery': { + "message": 'Delivered', + "success": True, + "notification_status": 'delivered', + "notification_statistics_status": STATISTICS_DELIVERED + }, + 'Complaint': { + "message": 'Complaint', + "success": False, + "notification_status": 'complaint', + "notification_statistics_status": STATISTICS_FAILURE + } +} -class AwsSesResponses(ClientResponse): - def __init__(self): - ClientResponse.__init__(self) - self.__response_model__ = { - 'Bounce': { - "message": 'Bounced', - "success": False, - "notification_status": 'bounce', - "notification_statistics_status": STATISTICS_FAILURE - }, - 'Delivery': { - "message": 'Delivered', - "success": True, - "notification_status": 'delivered', - "notification_statistics_status": STATISTICS_DELIVERED - }, - 'Complaint': { - "message": 'Complaint', - "success": False, - "notification_status": 'complaint', - "notification_statistics_status": STATISTICS_FAILURE - } - } + +def get_aws_responses(status): + return ses_response_map[status] class AwsSesClientException(EmailClientException): diff --git a/app/notifications/rest.py b/app/notifications/rest.py index dcb7873d6..5c4b3121e 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -11,7 +11,7 @@ from flask import ( ) from utils.recipients import allowed_to_send_to, first_column_heading from utils.template import Template -from app.clients.email.aws_ses import AwsSesResponses +from app.clients.email.aws_ses import get_aws_responses from app import api_user, encryption, create_uuid, DATETIME_FORMAT, DATE_FORMAT from app.authentication.auth import require_admin from app.dao import ( @@ -36,46 +36,40 @@ notifications = Blueprint('notifications', __name__) from app.errors import register_errors register_errors(notifications) -aws_response = AwsSesResponses() @notifications.route('/notifications/email/ses', methods=['POST']) def process_ses_response(): + client_name = 'SES' try: ses_request = json.loads(request.data) - if 'Message' not in ses_request: - current_app.logger.error( - "SES callback failed: message missing" - ) + + errors = validate_callback_data(data=ses_request, fields=['Message'], client_name=client_name) + if errors: return jsonify( - result="error", message="SES callback failed: message missing" + result="error", message=errors ), 400 ses_message = json.loads(ses_request['Message']) - - if 'notificationType' not in ses_message: - current_app.logger.error( - "SES callback failed: notificationType missing" - ) + errors = validate_callback_data(data=ses_message, fields=['notificationType'], client_name=client_name) + if errors: return jsonify( - result="error", message="SES callback failed: notificationType missing" + result="error", message=errors ), 400 + notification_type = ses_message['notificationType'] try: - aws_response.response_code_to_object(ses_message['notificationType']) + aws_response_dict = get_aws_responses(notification_type) except KeyError: - current_app.logger.info( - "SES callback failed: status {} not found.".format(ses_message['notificationType']) - ) + message = "{} callback failed: status {} not found".format(client_name, notification_type) + current_app.logger.info(message) return jsonify( result="error", - message="SES callback failed: status {} not found".format(ses_message['notificationType']) + message=message ), 400 - notification_status = aws_response.response_code_to_notification_status(ses_message['notificationType']) - notification_statistics_status = aws_response.response_code_to_notification_statistics_status( - ses_message['notificationType'] - ) + notification_status = aws_response_dict['notification_status'] + notification_statistics_status = aws_response_dict['notification_statistics_status'] try: source = ses_message['mail']['source'] @@ -101,11 +95,11 @@ def process_ses_response(): message="SES callback failed: notification not found. Status {}".format(notification_status) ), 404 - if not aws_response.response_code_to_notification_success(ses_message['notificationType']): + if not aws_response_dict['success']: current_app.logger.info( "SES delivery failed: notification {} has error found. Status {}".format( reference, - aws_response.response_code_to_message(ses_message['notificationType']) + aws_response_dict['message'] ) ) @@ -123,10 +117,10 @@ def process_ses_response(): except ValueError as ex: current_app.logger.exception( - "SES callback failed: invalid json {}".format(ex) + "{} callback failed: invalid json {}".format(client_name, ex) ) return jsonify( - result="error", message="SES callback failed: invalid json" + result="error", message="{} callback failed: invalid json".format(client_name) ), 400 diff --git a/tests/app/clients/test_aws_ses.py b/tests/app/clients/test_aws_ses.py index 452dd4960..4ed02f9ac 100644 --- a/tests/app/clients/test_aws_ses.py +++ b/tests/app/clients/test_aws_ses.py @@ -1,32 +1,33 @@ import pytest -from app.clients.email import aws_ses - -aws_responses = aws_ses.AwsSesResponses() +from app.clients.email.aws_ses import get_aws_responses def test_should_return_correct_details_for_delivery(): - assert aws_responses.response_code_to_message('Delivery') == 'Delivered' - assert aws_responses.response_code_to_notification_status('Delivery') == 'delivered' - assert aws_responses.response_code_to_notification_statistics_status('Delivery') == 'delivered' - assert aws_responses.response_code_to_notification_success('Delivery') + response_dict = get_aws_responses('Delivery') + assert response_dict['message'] == 'Delivered' + assert response_dict['notification_status'] == 'delivered' + assert response_dict['notification_statistics_status'] == 'delivered' + assert response_dict['success'] def test_should_return_correct_details_for_bounced(): - assert aws_responses.response_code_to_message('Bounce') == 'Bounced' - assert aws_responses.response_code_to_notification_status('Bounce') == 'bounce' - assert aws_responses.response_code_to_notification_statistics_status('Bounce') == 'failure' - assert not aws_responses.response_code_to_notification_success('Bounce') + response_dict = get_aws_responses('Bounce') + assert response_dict['message'] == 'Bounced' + assert response_dict['notification_status'] == 'bounce' + assert response_dict['notification_statistics_status'] == 'failure' + assert not response_dict['success'] def test_should_return_correct_details_for_complaint(): - assert aws_responses.response_code_to_message('Complaint') == 'Complaint' - assert aws_responses.response_code_to_notification_status('Complaint') == 'complaint' - assert aws_responses.response_code_to_notification_statistics_status('Complaint') == 'failure' - assert not aws_responses.response_code_to_notification_success('Complaint') + response_dict = get_aws_responses('Complaint') + assert response_dict['message'] == 'Complaint' + assert response_dict['notification_status'] == 'complaint' + assert response_dict['notification_statistics_status'] == 'failure' + assert not response_dict['success'] def test_should_be_none_if_unrecognised_status_code(): with pytest.raises(KeyError) as e: - aws_responses.response_code_to_object('99') + get_aws_responses('99') assert '99' in str(e.value)