mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-03 01:41:05 -05:00
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:
@@ -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']
|
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user