Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Production release for 12/20/24 #1497

Merged
merged 19 commits into from
Dec 23, 2024
Merged
4 changes: 4 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging as real_logging
import os
import secrets
import string
Expand Down Expand Up @@ -36,6 +37,9 @@ def init_app(self, app):

# Configure Celery app with options from the main app config.
self.config_from_object(app.config["CELERY"])
self.conf.worker_hijack_root_logger = False
logger = real_logging.getLogger("celery")
logger.propagate = False

def send_task(self, name, args=None, kwargs=None, **other_kwargs):
other_kwargs["headers"] = other_kwargs.get("headers") or {}
Expand Down
7 changes: 6 additions & 1 deletion app/aws/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,12 @@ def extract_phones(job):
phone_index = 0
for item in first_row:
# Note: may contain a BOM and look like \ufeffphone number
if item.lower() in ["phone number", "\\ufeffphone number"]:
if item.lower() in [
"phone number",
"\\ufeffphone number",
"\\ufeffphone number\n",
"phone number\n",
]:
break
phone_index = phone_index + 1

Expand Down
2 changes: 2 additions & 0 deletions app/celery/provider_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ def deliver_sms(self, notification_id):
)
# Code branches off to send_to_providers.py
message_id = send_to_providers.send_sms_to_provider(notification)

# DEPRECATED
# We have to put it in UTC. For other timezones, the delay
# will be ignored and it will fire immediately (although this probably only affects developer testing)
my_eta = utc_now() + timedelta(seconds=DELIVERY_RECEIPT_DELAY_IN_SECONDS)
Expand Down
8 changes: 8 additions & 0 deletions app/celery/tasks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json

from celery.signals import task_postrun
from flask import current_app
from requests import HTTPError, RequestException, request
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
Expand Down Expand Up @@ -170,6 +171,13 @@ def __total_sending_limits_for_job_exceeded(service, job, job_id):
return True


@task_postrun.connect
def log_task_ejection(sender=None, task_id=None, **kwargs):
current_app.logger.info(
f"Task {task_id} ({sender.name if sender else 'unknown_task'}) has been completed and removed"
)


@notify_celery.task(bind=True, name="save-sms", max_retries=2, default_retry_delay=600)
def save_sms(self, service_id, notification_id, encrypted_notification, sender_id=None):
"""Persist notification to db and place notification in queue to send to sns."""
Expand Down
2 changes: 2 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ class Config(object):

CELERY = {
"worker_max_tasks_per_child": 500,
"task_ignore_result": True,
"result_persistent": False,
"broker_url": REDIS_URL,
"broker_transport_options": {
"visibility_timeout": 310,
Expand Down
10 changes: 10 additions & 0 deletions app/dao/notifications_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ def _update_notification_status(
return notification


def update_notification_message_id(notification_id, message_id):
stmt = (
update(Notification)
.where(Notification.id == notification_id)
.values(message_id=message_id)
)
db.session.execute(stmt)
db.session.commit()


@autocommit
def update_notification_status_by_id(
notification_id, status, sent_by=None, provider_response=None, carrier=None
Expand Down
6 changes: 5 additions & 1 deletion app/delivery/send_to_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
from app.aws.s3 import get_personalisation_from_s3, get_phone_number_from_s3
from app.celery.test_key_tasks import send_email_response, send_sms_response
from app.dao.email_branding_dao import dao_get_email_branding_by_id
from app.dao.notifications_dao import dao_update_notification
from app.dao.notifications_dao import (
dao_update_notification,
update_notification_message_id,
)
from app.dao.provider_details_dao import get_provider_details_by_notification_type
from app.dao.service_sms_sender_dao import dao_get_sms_senders_by_service_id
from app.enums import BrandType, KeyType, NotificationStatus, NotificationType
Expand Down Expand Up @@ -117,6 +120,7 @@ def send_sms_to_provider(notification):

message_id = provider.send_sms(**send_sms_kwargs)
current_app.logger.info(f"got message_id {message_id}")
update_notification_message_id(notification.id, message_id)
except Exception as e:
n = notification
msg = f"FAILED send to sms, job_id: {n.job_id} row_number {n.job_row_number} message_id {message_id}"
Expand Down
1 change: 1 addition & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,7 @@ class Notification(db.Model):

provider_response = db.Column(db.Text, nullable=True)
carrier = db.Column(db.Text, nullable=True)
message_id = db.Column(db.Text, nullable=True)

# queue_name = db.Column(db.Text, nullable=True)

Expand Down
2 changes: 2 additions & 0 deletions notifications_utils/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ def init_app(app):
for logger_instance, handler in product(loggers, handlers):
logger_instance.addHandler(handler)
logger_instance.setLevel(loglevel)
logger_instance.propagate = False
warning_loggers = [logging.getLogger("boto3"), logging.getLogger("s3transfer")]
for logger_instance, handler in product(warning_loggers, handlers):
logger_instance.addHandler(handler)
logger_instance.setLevel(logging.WARNING)
logger_instance.propagate = False

# Suppress specific loggers to prevent leaking sensitive info
logging.getLogger("boto3").setLevel(logging.ERROR)
Expand Down
14 changes: 14 additions & 0 deletions tests/app/aws/test_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,20 @@ def test_get_s3_file_makes_correct_call(notify_api, mocker):
2,
"5555555552",
),
(
# simulate file saved with utf8withbom
"\\ufeffPHONE NUMBER\n",
"eee",
2,
"5555555552",
),
(
# simulate file saved without utf8withbom
"\\PHONE NUMBER\n",
"eee",
2,
"5555555552",
),
],
)
def test_get_phone_number_from_s3(
Expand Down
40 changes: 40 additions & 0 deletions tests/app/delivery/test_send_to_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist(
mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3")
mock_s3.return_value = "2028675309"

mocker.patch("app.delivery.send_to_providers.update_notification_message_id")
mock_personalisation = mocker.patch(
"app.delivery.send_to_providers.get_personalisation_from_s3"
)
Expand Down Expand Up @@ -233,6 +234,7 @@ def test_send_sms_should_use_template_version_from_notification_not_latest(
mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3")
mock_s3.return_value = "2028675309"

mocker.patch("app.delivery.send_to_providers.update_notification_message_id")
mock_s3_p = mocker.patch(
"app.delivery.send_to_providers.get_personalisation_from_s3"
)
Expand Down Expand Up @@ -327,6 +329,7 @@ def test_should_send_sms_with_downgraded_content(notify_db_session, mocker):
# ī, grapes, tabs, zero width space and ellipsis are not
# ó isn't in GSM, but it is in the welsh alphabet so will still be sent

mocker.patch("app.delivery.send_to_providers.update_notification_message_id")
mocker.patch("app.delivery.send_to_providers.redis_store", return_value=None)
mocker.patch(
"app.delivery.send_to_providers.get_sender_numbers", return_value=["testing"]
Expand Down Expand Up @@ -365,6 +368,7 @@ def test_send_sms_should_use_service_sms_sender(

mocker.patch("app.delivery.send_to_providers.redis_store", return_value=None)
mocker.patch("app.aws_sns_client.send_sms")
mocker.patch("app.delivery.send_to_providers.update_notification_message_id")

sms_sender = create_service_sms_sender(
service=sample_service, sms_sender="123456", is_default=False
Expand Down Expand Up @@ -405,6 +409,8 @@ def test_send_email_to_provider_should_not_send_to_provider_when_status_is_not_c
)
mocker.patch("app.aws_ses_client.send_email")
mocker.patch("app.delivery.send_to_providers.send_email_response")

mocker.patch("app.delivery.send_to_providers.update_notification_message_id")
mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3")
mock_phone.return_value = "15555555555"

Expand Down Expand Up @@ -627,6 +633,10 @@ def test_should_update_billable_units_and_status_according_to_research_mode_and_
):

mocker.patch("app.delivery.send_to_providers.redis_store", return_value=None)
mocker.patch(
"app.delivery.send_to_providers.update_notification_message_id",
return_value=None,
)
mocker.patch(
"app.delivery.send_to_providers.get_sender_numbers", return_value=["testing"]
)
Expand All @@ -637,6 +647,11 @@ def test_should_update_billable_units_and_status_according_to_research_mode_and_
key_type=key_type,
reply_to_text="testing",
)

mocker.patch(
"app.delivery.send_to_providers.update_notification_message_id",
return_value=None,
)
mocker.patch("app.aws_sns_client.send_sms")
mocker.patch(
"app.delivery.send_to_providers.send_sms_response",
Expand All @@ -647,6 +662,8 @@ def test_should_update_billable_units_and_status_according_to_research_mode_and_
sample_template.service.research_mode = True

mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3")

mocker.patch("app.delivery.send_to_providers.update_notification_message_id")
mock_phone.return_value = "15555555555"

mock_personalisation = mocker.patch(
Expand All @@ -670,6 +687,8 @@ def test_should_set_notification_billable_units_and_reduces_provider_priority_if
assert sample_notification.sent_by is None

mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3")

mocker.patch("app.delivery.send_to_providers.update_notification_message_id")
mock_phone.return_value = "15555555555"

mock_personalisation = mocker.patch(
Expand Down Expand Up @@ -705,8 +724,14 @@ def test_should_send_sms_to_international_providers(
)

mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3")

mocker.patch("app.delivery.send_to_providers.update_notification_message_id")
mock_s3.return_value = "601117224412"

mocker.patch(
"app.delivery.send_to_providers.update_notification_message_id",
return_value=None,
)
mock_personalisation = mocker.patch(
"app.delivery.send_to_providers.get_personalisation_from_s3"
)
Expand Down Expand Up @@ -744,6 +769,11 @@ def test_should_handle_sms_sender_and_prefix_message(

mocker.patch("app.delivery.send_to_providers.redis_store", return_value=None)
mocker.patch("app.aws_sns_client.send_sms")

mocker.patch(
"app.delivery.send_to_providers.update_notification_message_id",
return_value=None,
)
service = create_service_with_defined_sms_sender(
sms_sender_value=sms_sender, prefix_sms=prefix_sms
)
Expand Down Expand Up @@ -803,6 +833,11 @@ def test_send_sms_to_provider_should_use_normalised_to(mocker, client, sample_te
mocker.patch(
"app.delivery.send_to_providers.get_sender_numbers", return_value=["testing"]
)

mocker.patch(
"app.delivery.send_to_providers.update_notification_message_id",
return_value=None,
)
send_mock = mocker.patch("app.aws_sns_client.send_sms")
notification = create_notification(
template=sample_template,
Expand Down Expand Up @@ -866,6 +901,11 @@ def test_send_sms_to_provider_should_return_template_if_found_in_redis(
mocker.patch(
"app.delivery.send_to_providers.get_sender_numbers", return_value=["testing"]
)

mocker.patch(
"app.delivery.send_to_providers.update_notification_message_id",
return_value=None,
)
from app.schemas import service_schema, template_schema

service_dict = service_schema.dump(sample_template.service)
Expand Down
Loading