Remove the abstract ClientResponses class. Refactor aws_ses not to require the class.

All three client now use a response_map for the delivery receipt processing.
This commit is contained in:
Rebecca Law
2016-04-06 16:34:45 +01:00
parent 323b2ff537
commit 90194cbbb8
4 changed files with 61 additions and 86 deletions

View File

@@ -16,23 +16,3 @@ class Client(object):
STATISTICS_REQUESTED = 'requested' STATISTICS_REQUESTED = 'requested'
STATISTICS_DELIVERED = 'delivered' STATISTICS_DELIVERED = 'delivered'
STATISTICS_FAILURE = 'failure' 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']

View File

@@ -1,33 +1,33 @@
import boto3 import boto3
from flask import current_app from flask import current_app
from monotonic import monotonic 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) 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): def get_aws_responses(status):
ClientResponse.__init__(self) return ses_response_map[status]
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
}
}
class AwsSesClientException(EmailClientException): class AwsSesClientException(EmailClientException):

View File

@@ -11,7 +11,7 @@ from flask import (
) )
from utils.recipients import allowed_to_send_to, first_column_heading from utils.recipients import allowed_to_send_to, first_column_heading
from utils.template import Template 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 import api_user, encryption, create_uuid, DATETIME_FORMAT, DATE_FORMAT
from app.authentication.auth import require_admin from app.authentication.auth import require_admin
from app.dao import ( from app.dao import (
@@ -36,46 +36,40 @@ notifications = Blueprint('notifications', __name__)
from app.errors import register_errors from app.errors import register_errors
register_errors(notifications) register_errors(notifications)
aws_response = AwsSesResponses()
@notifications.route('/notifications/email/ses', methods=['POST']) @notifications.route('/notifications/email/ses', methods=['POST'])
def process_ses_response(): def process_ses_response():
client_name = 'SES'
try: try:
ses_request = json.loads(request.data) ses_request = json.loads(request.data)
if 'Message' not in ses_request:
current_app.logger.error( errors = validate_callback_data(data=ses_request, fields=['Message'], client_name=client_name)
"SES callback failed: message missing" if errors:
)
return jsonify( return jsonify(
result="error", message="SES callback failed: message missing" result="error", message=errors
), 400 ), 400
ses_message = json.loads(ses_request['Message']) ses_message = json.loads(ses_request['Message'])
errors = validate_callback_data(data=ses_message, fields=['notificationType'], client_name=client_name)
if 'notificationType' not in ses_message: if errors:
current_app.logger.error(
"SES callback failed: notificationType missing"
)
return jsonify( return jsonify(
result="error", message="SES callback failed: notificationType missing" result="error", message=errors
), 400 ), 400
notification_type = ses_message['notificationType']
try: try:
aws_response.response_code_to_object(ses_message['notificationType']) aws_response_dict = get_aws_responses(notification_type)
except KeyError: except KeyError:
current_app.logger.info( message = "{} callback failed: status {} not found".format(client_name, notification_type)
"SES callback failed: status {} not found.".format(ses_message['notificationType']) current_app.logger.info(message)
)
return jsonify( return jsonify(
result="error", result="error",
message="SES callback failed: status {} not found".format(ses_message['notificationType']) message=message
), 400 ), 400
notification_status = aws_response.response_code_to_notification_status(ses_message['notificationType']) notification_status = aws_response_dict['notification_status']
notification_statistics_status = aws_response.response_code_to_notification_statistics_status( notification_statistics_status = aws_response_dict['notification_statistics_status']
ses_message['notificationType']
)
try: try:
source = ses_message['mail']['source'] source = ses_message['mail']['source']
@@ -101,11 +95,11 @@ def process_ses_response():
message="SES callback failed: notification not found. Status {}".format(notification_status) message="SES callback failed: notification not found. Status {}".format(notification_status)
), 404 ), 404
if not aws_response.response_code_to_notification_success(ses_message['notificationType']): if not aws_response_dict['success']:
current_app.logger.info( current_app.logger.info(
"SES delivery failed: notification {} has error found. Status {}".format( "SES delivery failed: notification {} has error found. Status {}".format(
reference, 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: except ValueError as ex:
current_app.logger.exception( current_app.logger.exception(
"SES callback failed: invalid json {}".format(ex) "{} callback failed: invalid json {}".format(client_name, ex)
) )
return jsonify( return jsonify(
result="error", message="SES callback failed: invalid json" result="error", message="{} callback failed: invalid json".format(client_name)
), 400 ), 400

View File

@@ -1,32 +1,33 @@
import pytest import pytest
from app.clients.email import aws_ses from app.clients.email.aws_ses import get_aws_responses
aws_responses = aws_ses.AwsSesResponses()
def test_should_return_correct_details_for_delivery(): def test_should_return_correct_details_for_delivery():
assert aws_responses.response_code_to_message('Delivery') == 'Delivered' response_dict = get_aws_responses('Delivery')
assert aws_responses.response_code_to_notification_status('Delivery') == 'delivered' assert response_dict['message'] == 'Delivered'
assert aws_responses.response_code_to_notification_statistics_status('Delivery') == 'delivered' assert response_dict['notification_status'] == 'delivered'
assert aws_responses.response_code_to_notification_success('Delivery') assert response_dict['notification_statistics_status'] == 'delivered'
assert response_dict['success']
def test_should_return_correct_details_for_bounced(): def test_should_return_correct_details_for_bounced():
assert aws_responses.response_code_to_message('Bounce') == 'Bounced' response_dict = get_aws_responses('Bounce')
assert aws_responses.response_code_to_notification_status('Bounce') == 'bounce' assert response_dict['message'] == 'Bounced'
assert aws_responses.response_code_to_notification_statistics_status('Bounce') == 'failure' assert response_dict['notification_status'] == 'bounce'
assert not aws_responses.response_code_to_notification_success('Bounce') assert response_dict['notification_statistics_status'] == 'failure'
assert not response_dict['success']
def test_should_return_correct_details_for_complaint(): def test_should_return_correct_details_for_complaint():
assert aws_responses.response_code_to_message('Complaint') == 'Complaint' response_dict = get_aws_responses('Complaint')
assert aws_responses.response_code_to_notification_status('Complaint') == 'complaint' assert response_dict['message'] == 'Complaint'
assert aws_responses.response_code_to_notification_statistics_status('Complaint') == 'failure' assert response_dict['notification_status'] == 'complaint'
assert not aws_responses.response_code_to_notification_success('Complaint') assert response_dict['notification_statistics_status'] == 'failure'
assert not response_dict['success']
def test_should_be_none_if_unrecognised_status_code(): def test_should_be_none_if_unrecognised_status_code():
with pytest.raises(KeyError) as e: with pytest.raises(KeyError) as e:
aws_responses.response_code_to_object('99') get_aws_responses('99')
assert '99' in str(e.value) assert '99' in str(e.value)