mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-19 05:58:53 -04:00
canada UK ses callbacks monster mash
This commit is contained in:
52
app/notifications/callbacks.py
Normal file
52
app/notifications/callbacks.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from app.celery.service_callback_tasks import send_delivery_status_to_service
|
||||
from app.config import QueueNames
|
||||
from app.dao.service_callback_api_dao import (
|
||||
get_service_delivery_status_callback_api_for_service,
|
||||
)
|
||||
|
||||
|
||||
def check_and_queue_callback_task(notification):
|
||||
# queue callback task only if the service_callback_api exists
|
||||
service_callback_api = get_service_delivery_status_callback_api_for_service(service_id=notification.service_id)
|
||||
if service_callback_api:
|
||||
notification_data = create_delivery_status_callback_data(notification, service_callback_api)
|
||||
|
||||
send_delivery_status_to_service.apply_async([str(notification.id), notification_data], queue=QueueNames.CALLBACKS)
|
||||
|
||||
|
||||
def create_delivery_status_callback_data(notification, service_callback_api):
|
||||
from app import encryption
|
||||
from app.utils import DATETIME_FORMAT
|
||||
|
||||
data = {
|
||||
"notification_id": str(notification.id),
|
||||
"notification_client_reference": notification.client_reference,
|
||||
"notification_to": notification.to,
|
||||
"notification_status": notification.status,
|
||||
"notification_provider_response": notification.provider_response,
|
||||
"notification_created_at": notification.created_at.strftime(DATETIME_FORMAT),
|
||||
"notification_updated_at": notification.updated_at.strftime(DATETIME_FORMAT) if notification.updated_at else None,
|
||||
"notification_sent_at": notification.sent_at.strftime(DATETIME_FORMAT) if notification.sent_at else None,
|
||||
"notification_type": notification.notification_type,
|
||||
"service_callback_api_url": service_callback_api.url,
|
||||
"service_callback_api_bearer_token": service_callback_api.bearer_token,
|
||||
}
|
||||
|
||||
return encryption.encrypt(data)
|
||||
|
||||
|
||||
def create_complaint_callback_data(complaint, notification, service_callback_api, recipient):
|
||||
from app import encryption
|
||||
from app.utils import DATETIME_FORMAT
|
||||
|
||||
data = {
|
||||
"complaint_id": str(complaint.id),
|
||||
"notification_id": str(notification.id),
|
||||
"reference": notification.client_reference,
|
||||
"to": recipient,
|
||||
"complaint_date": complaint.complaint_date.strftime(DATETIME_FORMAT),
|
||||
"service_callback_api_url": service_callback_api.url,
|
||||
"service_callback_api_bearer_token": service_callback_api.bearer_token,
|
||||
}
|
||||
|
||||
return encryption.encrypt(data)
|
||||
@@ -1,4 +1,4 @@
|
||||
from flask import current_app
|
||||
from flask import current_app, json
|
||||
|
||||
from app.celery.service_callback_tasks import (
|
||||
create_complaint_callback_data,
|
||||
@@ -8,65 +8,125 @@ from app.celery.service_callback_tasks import (
|
||||
)
|
||||
from app.config import QueueNames
|
||||
from app.dao.complaint_dao import save_complaint
|
||||
from app.dao.notifications_dao import (
|
||||
dao_get_notification_or_history_by_reference,
|
||||
)
|
||||
from app.dao.notifications_dao import dao_get_notification_history_by_reference
|
||||
from app.dao.service_callback_api_dao import (
|
||||
get_service_complaint_callback_api_for_service,
|
||||
get_service_delivery_status_callback_api_for_service,
|
||||
)
|
||||
from app.models import Complaint
|
||||
from app.notifications.callbacks import create_complaint_callback_data
|
||||
|
||||
|
||||
def determine_notification_bounce_type(notification_type, ses_message):
|
||||
def _determine_notification_bounce_type(ses_message):
|
||||
notification_type = ses_message["notificationType"]
|
||||
if notification_type in ["Delivery", "Complaint"]:
|
||||
return notification_type
|
||||
|
||||
if notification_type != "Bounce":
|
||||
raise KeyError(f"Unhandled notification type {notification_type}")
|
||||
|
||||
remove_emails_from_bounce(ses_message)
|
||||
if ses_message['bounce']['bounceType'] == 'Permanent':
|
||||
notification_type = ses_message['bounce']['bounceType'] # permanent or not
|
||||
else:
|
||||
notification_type = 'Temporary'
|
||||
return notification_type, ses_message
|
||||
current_app.logger.info("SES bounce dict: {}".format(json.dumps(ses_message).replace("{", "(").replace("}", ")")))
|
||||
if ses_message["bounce"]["bounceType"] == "Permanent":
|
||||
return "Permanent"
|
||||
return "Temporary"
|
||||
|
||||
|
||||
def _determine_provider_response(ses_message):
|
||||
if ses_message["notificationType"] != "Bounce":
|
||||
return None
|
||||
|
||||
bounce_type = ses_message["bounce"]["bounceType"]
|
||||
bounce_subtype = ses_message["bounce"]["bounceSubType"]
|
||||
|
||||
# See https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-contents.html
|
||||
if bounce_type == "Permanent" and bounce_subtype == "Suppressed":
|
||||
return "The email address is on our email provider suppression list"
|
||||
elif bounce_type == "Permanent" and bounce_subtype == "OnAccountSuppressionList":
|
||||
return "The email address is on the GC Notify suppression list"
|
||||
elif bounce_type == "Transient" and bounce_subtype == "AttachmentRejected":
|
||||
return "The email was rejected because of its attachments"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_aws_responses(ses_message):
|
||||
status = _determine_notification_bounce_type(ses_message)
|
||||
|
||||
base = {
|
||||
"Permanent": {
|
||||
"message": "Hard bounced",
|
||||
"success": False,
|
||||
"notification_status": "permanent-failure",
|
||||
},
|
||||
"Temporary": {
|
||||
"message": "Soft bounced",
|
||||
"success": False,
|
||||
"notification_status": "temporary-failure",
|
||||
},
|
||||
"Delivery": {
|
||||
"message": "Delivered",
|
||||
"success": True,
|
||||
"notification_status": "delivered",
|
||||
},
|
||||
"Complaint": {
|
||||
"message": "Complaint",
|
||||
"success": True,
|
||||
"notification_status": "delivered",
|
||||
},
|
||||
}[status]
|
||||
|
||||
base["provider_response"] = _determine_provider_response(ses_message)
|
||||
|
||||
return base
|
||||
|
||||
|
||||
def handle_complaint(ses_message):
|
||||
recipient_email = remove_emails_from_complaint(ses_message)[0]
|
||||
current_app.logger.info("Complaint from SES: \n{}".format(ses_message))
|
||||
current_app.logger.info("Complaint from SES: \n{}".format(json.dumps(ses_message).replace("{", "(").replace("}", ")")))
|
||||
try:
|
||||
reference = ses_message['mail']['messageId']
|
||||
reference = ses_message["mail"]["messageId"]
|
||||
except KeyError as e:
|
||||
current_app.logger.exception("Complaint from SES failed to get reference from message", e)
|
||||
return
|
||||
notification = dao_get_notification_or_history_by_reference(reference)
|
||||
ses_complaint = ses_message.get('complaint', None)
|
||||
notification = dao_get_notification_history_by_reference(reference)
|
||||
ses_complaint = ses_message.get("complaint", None)
|
||||
|
||||
complaint = Complaint(
|
||||
notification_id=notification.id,
|
||||
service_id=notification.service_id,
|
||||
ses_feedback_id=ses_complaint.get('feedbackId', None) if ses_complaint else None,
|
||||
complaint_type=ses_complaint.get('complaintFeedbackType', None) if ses_complaint else None,
|
||||
complaint_date=ses_complaint.get('timestamp', None) if ses_complaint else None
|
||||
ses_feedback_id=ses_complaint.get("feedbackId", None) if ses_complaint else None,
|
||||
complaint_type=ses_complaint.get("complaintFeedbackType", None) if ses_complaint else None,
|
||||
complaint_date=ses_complaint.get("timestamp", None) if ses_complaint else None,
|
||||
)
|
||||
save_complaint(complaint)
|
||||
return complaint, notification, recipient_email
|
||||
|
||||
|
||||
def remove_mail_headers(dict_to_edit):
|
||||
if dict_to_edit['mail'].get('headers'):
|
||||
dict_to_edit['mail'].pop('headers')
|
||||
if dict_to_edit['mail'].get('commonHeaders'):
|
||||
dict_to_edit['mail'].pop('commonHeaders')
|
||||
if dict_to_edit["mail"].get("headers"):
|
||||
dict_to_edit["mail"].pop("headers")
|
||||
if dict_to_edit["mail"].get("commonHeaders"):
|
||||
dict_to_edit["mail"].pop("commonHeaders")
|
||||
|
||||
|
||||
def remove_emails_from_bounce(bounce_dict):
|
||||
remove_mail_headers(bounce_dict)
|
||||
bounce_dict['mail'].pop('destination')
|
||||
bounce_dict['bounce'].pop('bouncedRecipients')
|
||||
bounce_dict["mail"].pop("destination")
|
||||
bounce_dict["bounce"].pop("bouncedRecipients")
|
||||
|
||||
|
||||
def remove_emails_from_complaint(complaint_dict):
|
||||
remove_mail_headers(complaint_dict)
|
||||
complaint_dict['complaint'].pop('complainedRecipients')
|
||||
return complaint_dict['mail'].pop('destination')
|
||||
complaint_dict["complaint"].pop("complainedRecipients")
|
||||
return complaint_dict["mail"].pop("destination")
|
||||
|
||||
def check_and_queue_callback_task(notification):
|
||||
# queue callback task only if the service_callback_api exists
|
||||
service_callback_api = get_service_delivery_status_callback_api_for_service(service_id=notification.service_id)
|
||||
if service_callback_api:
|
||||
notification_data = create_delivery_status_callback_data(notification, service_callback_api)
|
||||
send_delivery_status_to_service.apply_async([str(notification.id), notification_data], queue=QueueNames.CALLBACKS)
|
||||
|
||||
def check_and_queue_callback_task(notification):
|
||||
# queue callback task only if the service_callback_api exists
|
||||
@@ -76,10 +136,9 @@ def check_and_queue_callback_task(notification):
|
||||
send_delivery_status_to_service.apply_async([str(notification.id), notification_data],
|
||||
queue=QueueNames.CALLBACKS)
|
||||
|
||||
|
||||
def _check_and_queue_complaint_callback_task(complaint, notification, recipient):
|
||||
# queue callback task only if the service_callback_api exists
|
||||
service_callback_api = get_service_complaint_callback_api_for_service(service_id=notification.service_id)
|
||||
if service_callback_api:
|
||||
complaint_data = create_complaint_callback_data(complaint, notification, service_callback_api, recipient)
|
||||
send_complaint_to_service.apply_async([complaint_data], queue=QueueNames.CALLBACKS)
|
||||
send_complaint_to_service.apply_async([complaint_data], queue=QueueNames.CALLBACKS)
|
||||
Reference in New Issue
Block a user