mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-18 21:48:49 -04:00
notify-api-521 fix sms temporary failure message
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from time import time
|
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
@@ -21,6 +20,7 @@ from app.models import (
|
|||||||
NOTIFICATION_DELIVERED,
|
NOTIFICATION_DELIVERED,
|
||||||
NOTIFICATION_FAILED,
|
NOTIFICATION_FAILED,
|
||||||
NOTIFICATION_TECHNICAL_FAILURE,
|
NOTIFICATION_TECHNICAL_FAILURE,
|
||||||
|
NOTIFICATION_TEMPORARY_FAILURE,
|
||||||
)
|
)
|
||||||
|
|
||||||
# This is the amount of time to wait after sending an sms message before we check the aws logs and look for delivery
|
# This is the amount of time to wait after sending an sms message before we check the aws logs and look for delivery
|
||||||
@@ -49,9 +49,20 @@ def check_sms_delivery_receipt(self, message_id, notification_id, sent_at):
|
|||||||
status = "success"
|
status = "success"
|
||||||
provider_response = "this is a fake successful localstack sms message"
|
provider_response = "this is a fake successful localstack sms message"
|
||||||
else:
|
else:
|
||||||
status, provider_response = aws_cloudwatch_client.check_sms(
|
try:
|
||||||
message_id, notification_id, sent_at
|
status, provider_response = aws_cloudwatch_client.check_sms(
|
||||||
)
|
message_id, notification_id, sent_at
|
||||||
|
)
|
||||||
|
except NotificationTechnicalFailureException as ntfe:
|
||||||
|
provider_response = "Unable to find carrier response -- still looking"
|
||||||
|
status = "pending"
|
||||||
|
current_app.logger.info(
|
||||||
|
"UPDATING WITH TEMPORARY FAILURE AND GOING TO RETRY"
|
||||||
|
)
|
||||||
|
update_notification_status_by_id(
|
||||||
|
notification_id, status, provider_response=provider_response
|
||||||
|
)
|
||||||
|
raise self.retry(exc=ntfe)
|
||||||
|
|
||||||
if status == "success":
|
if status == "success":
|
||||||
status = NOTIFICATION_DELIVERED
|
status = NOTIFICATION_DELIVERED
|
||||||
@@ -80,8 +91,6 @@ def check_sms_delivery_receipt(self, message_id, notification_id, sent_at):
|
|||||||
)
|
)
|
||||||
def deliver_sms(self, notification_id):
|
def deliver_sms(self, notification_id):
|
||||||
try:
|
try:
|
||||||
# Get the time we are doing the sending, to minimize the time period we need to check over for receipt
|
|
||||||
now = round(time() * 1000)
|
|
||||||
current_app.logger.info(
|
current_app.logger.info(
|
||||||
"Start sending SMS for notification id: {}".format(notification_id)
|
"Start sending SMS for notification id: {}".format(notification_id)
|
||||||
)
|
)
|
||||||
@@ -106,9 +115,15 @@ def deliver_sms(self, notification_id):
|
|||||||
seconds=DELIVERY_RECEIPT_DELAY_IN_SECONDS
|
seconds=DELIVERY_RECEIPT_DELAY_IN_SECONDS
|
||||||
)
|
)
|
||||||
check_sms_delivery_receipt.apply_async(
|
check_sms_delivery_receipt.apply_async(
|
||||||
[message_id, notification_id, now], eta=my_eta, queue=QueueNames.CHECK_SMS
|
[message_id, notification_id, notification.created_at],
|
||||||
|
eta=my_eta,
|
||||||
|
queue=QueueNames.CHECK_SMS,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
current_app.logger.info("TEMPORARY FAILURE!")
|
||||||
|
update_notification_status_by_id(
|
||||||
|
notification_id, NOTIFICATION_TEMPORARY_FAILURE
|
||||||
|
)
|
||||||
if isinstance(e, SmsClientResponseException):
|
if isinstance(e, SmsClientResponseException):
|
||||||
current_app.logger.warning(
|
current_app.logger.warning(
|
||||||
"SMS notification delivery for id: {} failed".format(notification_id),
|
"SMS notification delivery for id: {} failed".format(notification_id),
|
||||||
@@ -125,6 +140,7 @@ def deliver_sms(self, notification_id):
|
|||||||
else:
|
else:
|
||||||
self.retry(queue=QueueNames.RETRY)
|
self.retry(queue=QueueNames.RETRY)
|
||||||
except self.MaxRetriesExceededError:
|
except self.MaxRetriesExceededError:
|
||||||
|
current_app.logger.info("PERMANENT FAILURE!")
|
||||||
message = (
|
message = (
|
||||||
"RETRY FAILED: Max retries reached. The task send_sms_to_provider failed for notification {}. "
|
"RETRY FAILED: Max retries reached. The task send_sms_to_provider failed for notification {}. "
|
||||||
"Notification has been updated to technical-failure".format(
|
"Notification has been updated to technical-failure".format(
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from _datetime import timedelta
|
||||||
from boto3 import client
|
from boto3 import client
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from app.clients import AWS_CLIENT_CONFIG, Client
|
from app.clients import AWS_CLIENT_CONFIG, Client
|
||||||
from app.cloudfoundry_config import cloud_config
|
from app.cloudfoundry_config import cloud_config
|
||||||
|
from app.exceptions import NotificationTechnicalFailureException
|
||||||
|
|
||||||
|
|
||||||
class AwsCloudwatchClient(Client):
|
class AwsCloudwatchClient(Client):
|
||||||
@@ -89,6 +92,7 @@ class AwsCloudwatchClient(Client):
|
|||||||
# TODO this clumsy approach to getting the account number will be fixed as part of notify-api #258
|
# TODO this clumsy approach to getting the account number will be fixed as part of notify-api #258
|
||||||
account_number = self._extract_account_number(cloud_config.ses_domain_arn)
|
account_number = self._extract_account_number(cloud_config.ses_domain_arn)
|
||||||
|
|
||||||
|
time_now = datetime.utcnow()
|
||||||
log_group_name = f"sns/{region}/{account_number[4]}/DirectPublishToPhoneNumber"
|
log_group_name = f"sns/{region}/{account_number[4]}/DirectPublishToPhoneNumber"
|
||||||
current_app.logger.info(
|
current_app.logger.info(
|
||||||
f"Log group name: {log_group_name} message id: {message_id}"
|
f"Log group name: {log_group_name} message id: {message_id}"
|
||||||
@@ -105,7 +109,7 @@ class AwsCloudwatchClient(Client):
|
|||||||
log_group_name = (
|
log_group_name = (
|
||||||
f"sns/{region}/{account_number[4]}/DirectPublishToPhoneNumber/Failure"
|
f"sns/{region}/{account_number[4]}/DirectPublishToPhoneNumber/Failure"
|
||||||
)
|
)
|
||||||
# current_app.logger.info(f"Failure log group name: {log_group_name}")
|
current_app.logger.info(f"Failure log group name: {log_group_name}")
|
||||||
all_failed_events = self._get_log(filter_pattern, log_group_name, created_at)
|
all_failed_events = self._get_log(filter_pattern, log_group_name, created_at)
|
||||||
if all_failed_events and len(all_failed_events) > 0:
|
if all_failed_events and len(all_failed_events) > 0:
|
||||||
current_app.logger.info("SHOULD RETURN FAILED BECAUSE WE FOUND A FAILURE")
|
current_app.logger.info("SHOULD RETURN FAILED BECAUSE WE FOUND A FAILURE")
|
||||||
@@ -114,6 +118,10 @@ class AwsCloudwatchClient(Client):
|
|||||||
current_app.logger.info(f"MESSAGE {message}")
|
current_app.logger.info(f"MESSAGE {message}")
|
||||||
return "failure", message["delivery"]["providerResponse"]
|
return "failure", message["delivery"]["providerResponse"]
|
||||||
|
|
||||||
raise Exception(
|
if time_now > (created_at + timedelta(hours=3)):
|
||||||
|
# see app/models.py Notification. This message corresponds to "permanent-failure",
|
||||||
|
# but we are copy/pasting here to avoid circular imports.
|
||||||
|
return "failure", "Unable to find carrier response."
|
||||||
|
raise NotificationTechnicalFailureException(
|
||||||
f"No event found for message_id {message_id} notification_id {notification_id}"
|
f"No event found for message_id {message_id} notification_id {notification_id}"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1727,8 +1727,8 @@ class Notification(db.Model):
|
|||||||
"sms": {
|
"sms": {
|
||||||
"failed": "Failed",
|
"failed": "Failed",
|
||||||
"technical-failure": "Technical failure",
|
"technical-failure": "Technical failure",
|
||||||
"temporary-failure": "Phone not accepting messages right now",
|
"temporary-failure": "Unable to find carrier response -- still looking",
|
||||||
"permanent-failure": "Phone number doesn’t exist",
|
"permanent-failure": "Unable to find carrier response.",
|
||||||
"delivered": "Delivered",
|
"delivered": "Delivered",
|
||||||
"sending": "Sending",
|
"sending": "Sending",
|
||||||
"created": "Sending",
|
"created": "Sending",
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ def test_should_go_into_technical_error_if_exceeds_retries_on_deliver_sms_task(
|
|||||||
queue="retry-tasks", countdown=0
|
queue="retry-tasks", countdown=0
|
||||||
)
|
)
|
||||||
|
|
||||||
assert sample_notification.status == "technical-failure"
|
assert sample_notification.status == "temporary-failure"
|
||||||
assert mock_logger_exception.called
|
assert mock_logger_exception.called
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -139,8 +139,12 @@ def test_notification_for_csv_returns_correct_job_row_number(sample_job):
|
|||||||
("email", "technical-failure", "Technical failure"),
|
("email", "technical-failure", "Technical failure"),
|
||||||
("email", "temporary-failure", "Inbox not accepting messages right now"),
|
("email", "temporary-failure", "Inbox not accepting messages right now"),
|
||||||
("email", "permanent-failure", "Email address doesn’t exist"),
|
("email", "permanent-failure", "Email address doesn’t exist"),
|
||||||
("sms", "temporary-failure", "Phone not accepting messages right now"),
|
(
|
||||||
("sms", "permanent-failure", "Phone number doesn’t exist"),
|
"sms",
|
||||||
|
"temporary-failure",
|
||||||
|
"Unable to find carrier response -- still looking",
|
||||||
|
),
|
||||||
|
("sms", "permanent-failure", "Unable to find carrier response."),
|
||||||
("sms", "sent", "Sent internationally"),
|
("sms", "sent", "Sent internationally"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user