2019-02-13 11:35:31 +00:00
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
|
|
|
|
import iso8601
|
2019-02-25 13:25:50 +00:00
|
|
|
from celery.exceptions import Retry
|
2019-02-13 11:35:31 +00:00
|
|
|
from flask import current_app, json
|
|
|
|
|
from sqlalchemy.orm.exc import NoResultFound
|
2017-11-17 13:41:45 +00:00
|
|
|
|
2019-02-13 11:35:31 +00:00
|
|
|
from app import notify_celery, statsd_client
|
|
|
|
|
from app.clients.email.aws_ses import get_aws_responses
|
2021-03-10 13:55:06 +00:00
|
|
|
from app.config import QueueNames
|
2019-02-13 11:35:31 +00:00
|
|
|
from app.dao import notifications_dao
|
2021-03-10 13:55:06 +00:00
|
|
|
from app.models import NOTIFICATION_PENDING, NOTIFICATION_SENDING
|
2019-02-13 11:35:31 +00:00
|
|
|
from app.notifications.notifications_ses_callback import (
|
2021-03-10 13:55:06 +00:00
|
|
|
_check_and_queue_callback_task,
|
|
|
|
|
_check_and_queue_complaint_callback_task,
|
2019-02-13 11:35:31 +00:00
|
|
|
determine_notification_bounce_type,
|
|
|
|
|
handle_complaint,
|
|
|
|
|
)
|
2017-11-17 13:41:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@notify_celery.task(bind=True, name="process-ses-result", max_retries=5, default_retry_delay=300)
|
|
|
|
|
def process_ses_results(self, response):
|
|
|
|
|
try:
|
2019-02-13 11:35:31 +00:00
|
|
|
ses_message = json.loads(response['Message'])
|
|
|
|
|
notification_type = ses_message['notificationType']
|
2020-11-20 14:10:13 +00:00
|
|
|
bounce_message = None
|
2019-02-13 11:35:31 +00:00
|
|
|
|
|
|
|
|
if notification_type == 'Bounce':
|
2020-11-20 14:10:13 +00:00
|
|
|
notification_type, bounce_message = determine_notification_bounce_type(notification_type, ses_message)
|
2019-02-13 11:35:31 +00:00
|
|
|
elif notification_type == 'Complaint':
|
|
|
|
|
_check_and_queue_complaint_callback_task(*handle_complaint(ses_message))
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
aws_response_dict = get_aws_responses(notification_type)
|
|
|
|
|
|
|
|
|
|
notification_status = aws_response_dict['notification_status']
|
|
|
|
|
reference = ses_message['mail']['messageId']
|
|
|
|
|
|
|
|
|
|
try:
|
2020-03-27 15:48:54 +00:00
|
|
|
notification = notifications_dao.dao_get_notification_or_history_by_reference(reference=reference)
|
2019-02-13 11:35:31 +00:00
|
|
|
except NoResultFound:
|
|
|
|
|
message_time = iso8601.parse_date(ses_message['mail']['timestamp']).replace(tzinfo=None)
|
2019-03-06 11:35:32 +00:00
|
|
|
if datetime.utcnow() - message_time < timedelta(minutes=5):
|
2020-06-04 15:43:03 +01:00
|
|
|
current_app.logger.info(
|
|
|
|
|
f"notification not found for reference: {reference} (update to {notification_status}). "
|
|
|
|
|
f"Callback may have arrived before notification was persisted to the DB. Adding task to retry queue"
|
|
|
|
|
)
|
2019-02-13 11:35:31 +00:00
|
|
|
self.retry(queue=QueueNames.RETRY)
|
2019-03-06 11:35:32 +00:00
|
|
|
else:
|
|
|
|
|
current_app.logger.warning(
|
2020-06-04 15:43:03 +01:00
|
|
|
f"notification not found for reference: {reference} (update to {notification_status})"
|
2019-02-13 11:35:31 +00:00
|
|
|
)
|
|
|
|
|
return
|
|
|
|
|
|
2020-11-20 14:10:13 +00:00
|
|
|
if bounce_message:
|
|
|
|
|
current_app.logger.info(f"SES bounce for notification ID {notification.id}: {bounce_message}")
|
|
|
|
|
|
2020-03-27 14:12:39 +00:00
|
|
|
if notification.status not in [NOTIFICATION_SENDING, NOTIFICATION_PENDING]:
|
|
|
|
|
notifications_dao._duplicate_update_warning(
|
|
|
|
|
notification=notification,
|
|
|
|
|
status=notification_status
|
2019-02-13 11:35:31 +00:00
|
|
|
)
|
2020-03-27 14:12:39 +00:00
|
|
|
return
|
2019-02-13 11:35:31 +00:00
|
|
|
else:
|
2020-03-27 14:12:39 +00:00
|
|
|
notifications_dao.dao_update_notifications_by_reference(
|
|
|
|
|
references=[reference],
|
|
|
|
|
update_dict={'status': notification_status}
|
|
|
|
|
)
|
2019-02-13 11:35:31 +00:00
|
|
|
|
|
|
|
|
statsd_client.incr('callback.ses.{}'.format(notification_status))
|
|
|
|
|
|
|
|
|
|
if notification.sent_at:
|
|
|
|
|
statsd_client.timing_with_dates('callback.ses.elapsed-time', datetime.utcnow(), notification.sent_at)
|
|
|
|
|
|
|
|
|
|
_check_and_queue_callback_task(notification)
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
2019-02-25 13:25:50 +00:00
|
|
|
except Retry:
|
|
|
|
|
raise
|
|
|
|
|
|
2019-02-13 11:35:31 +00:00
|
|
|
except Exception as e:
|
|
|
|
|
current_app.logger.exception('Error processing SES results: {}'.format(type(e)))
|
2017-11-23 13:49:52 +00:00
|
|
|
self.retry(queue=QueueNames.RETRY)
|