merge from main

This commit is contained in:
Kenneth Kehl
2024-08-01 12:43:26 -07:00
24 changed files with 275 additions and 533 deletions

View File

@@ -51,7 +51,13 @@ def cleanup_unfinished_jobs():
for job in jobs:
# The query already checks that the processing_finished time is null, so here we are saying
# if it started more than 4 hours ago, that's too long
acceptable_finish_time = job.processing_started + timedelta(minutes=5)
try:
acceptable_finish_time = job.processing_started + timedelta(minutes=5)
except TypeError:
current_app.logger.error(
f"Job ID {job.id} processing_started is {job.processing_started}."
)
raise
if now > acceptable_finish_time:
remove_csv_object(job.original_file_name)
dao_archive_job(job)

View File

@@ -23,7 +23,7 @@ from app.utils import utc_now
# This is the amount of time to wait after sending an sms message before we check the aws logs and look for delivery
# receipts
DELIVERY_RECEIPT_DELAY_IN_SECONDS = 120
DELIVERY_RECEIPT_DELAY_IN_SECONDS = 30
@notify_celery.task(

View File

@@ -36,12 +36,14 @@ from app.dao.organization_dao import (
dao_get_organization_by_email_address,
dao_get_organization_by_id,
)
from app.dao.service_sms_sender_dao import dao_get_sms_senders_by_service_id
from app.dao.services_dao import (
dao_fetch_all_services_by_user,
dao_fetch_all_services_created_by_user,
dao_fetch_service_by_id,
dao_update_service,
delete_service_and_all_associated_db_objects,
get_services_by_partial_name,
)
from app.dao.templates_dao import dao_get_template_by_id
from app.dao.users_dao import (
@@ -600,6 +602,24 @@ def download_csv_file_by_name(csv_filename):
print(s3.get_s3_file(bucket_name, csv_filename, access_key, secret, region))
@notify_command(name="dump-sms-senders")
@click.argument("service_name")
def dump_sms_senders(service_name):
# poetry run flask command dump-sms-senders MyServiceName
# cf run-task notify-api-production --command "flask command dump-sms-senders MyServiceName"
services = get_services_by_partial_name(service_name)
if len(services) > 1:
raise ValueError(
f"Please use a unique and complete service name instead of {service_name}"
)
senders = dao_get_sms_senders_by_service_id(services[0].id)
for sender in senders:
# Not PII, okay to put in logs
click.echo(sender.serialize())
@notify_command(name="populate-annual-billing-with-the-previous-years-allowance")
@click.option(
"-y",

View File

@@ -10,6 +10,7 @@ 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.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
from app.exceptions import NotificationTechnicalFailureException
from app.serialised_models import SerialisedService, SerialisedTemplate
@@ -101,6 +102,13 @@ def send_sms_to_provider(notification):
raise Exception(
f"The recipient for (Service ID: {si}; Job ID: {ji}; Job Row Number {jrn} was not found."
)
sender_numbers = get_sender_numbers(notification)
if notification.reply_to_text not in sender_numbers:
raise ValueError(
f"{notification.reply_to_text} not in {sender_numbers} #notify-admin-1701"
)
send_sms_kwargs = {
"to": recipient,
"content": str(template),
@@ -130,6 +138,14 @@ def send_sms_to_provider(notification):
return message_id
def get_sender_numbers(notification):
possible_senders = dao_get_sms_senders_by_service_id(notification.service_id)
sender_numbers = []
for possible_sender in possible_senders:
sender_numbers.append(possible_sender.sms_sender)
return sender_numbers
def send_email_to_provider(notification):
# Someone needs an email, possibly new registration
recipient = redis_store.get(f"email-address-{notification.id}")

View File

@@ -10,11 +10,6 @@ from app.exceptions import ArchiveValidationError
from notifications_utils.recipients import InvalidEmailError
class VirusScanError(Exception):
def __init__(self, message):
super().__init__(message)
class InvalidRequest(Exception):
code = None
fields = []

View File

@@ -1,5 +1,3 @@
from datetime import timedelta
from flask import Blueprint, jsonify, request
from app.celery.process_ses_receipts_tasks import process_ses_results
@@ -8,7 +6,6 @@ from app.errors import InvalidRequest
from app.notifications.sns_handlers import sns_notification_handler
ses_callback_blueprint = Blueprint("notifications_ses_callback", __name__)
DEFAULT_MAX_AGE = timedelta(days=10000)
# 400 counts as a permanent failure so SNS will not retry.

View File

@@ -1,5 +1,4 @@
import enum
from datetime import timedelta
from json import decoder
import requests
@@ -8,8 +7,6 @@ from flask import current_app, json
from app.errors import InvalidRequest
from app.notifications.sns_cert_validator import validate_sns_cert
DEFAULT_MAX_AGE = timedelta(days=10000)
class SNSMessageType(enum.Enum):
SubscriptionConfirmation = "SubscriptionConfirmation"

View File

@@ -201,6 +201,8 @@ def get_service_by_id(service_id):
fetched = dao_fetch_service_by_id(service_id)
data = service_schema.dump(fetched)
current_app.logger.info(f'>> SERVICE: {data["id"]}; {data}')
return jsonify(data=data)

View File

@@ -30,7 +30,6 @@ from app.dao.users_dao import (
reset_failed_login_count,
save_model_user,
save_user_attribute,
update_user_password,
use_user_code,
)
from app.enums import CodeType, KeyType, NotificationType, TemplateType
@@ -45,7 +44,6 @@ from app.schemas import (
create_user_schema,
email_data_request_schema,
partial_email_data_request_schema,
user_update_password_schema_load_json,
user_update_schema_load_json,
)
from app.user.users_schema import (
@@ -628,57 +626,6 @@ def get_all_users():
return jsonify(data=result), 200
@user_blueprint.route("/reset-password", methods=["POST"])
def send_user_reset_password():
request_json = request.get_json()
email = email_data_request_schema.load(request_json)
user_to_send_to = get_user_by_email(email["email"])
template = dao_get_template_by_id(current_app.config["PASSWORD_RESET_TEMPLATE_ID"])
service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"])
personalisation = {
"user_name": user_to_send_to.name,
"url": _create_reset_password_url(
user_to_send_to.email_address,
base_url=request_json.get("admin_base_url"),
next_redirect=request_json.get("next"),
),
}
saved_notification = persist_notification(
template_id=template.id,
template_version=template.version,
recipient=email["email"],
service=service,
personalisation=None,
notification_type=template.template_type,
api_key_id=None,
key_type=KeyType.NORMAL,
reply_to_text=service.get_default_reply_to_email_address(),
)
saved_notification.personalisation = personalisation
redis_store.set(
f"email-personalisation-{saved_notification.id}",
json.dumps(personalisation),
ex=60 * 60,
)
send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY)
return jsonify({}), 204
@user_blueprint.route("/<uuid:user_id>/update-password", methods=["POST"])
def update_password(user_id):
user = get_user_by_id(user_id=user_id)
req_json = request.get_json()
password = req_json.get("_password")
user_update_password_schema_load_json.load(req_json)
update_user_password(user, password)
return jsonify(data=user.serialize()), 200
@user_blueprint.route("/report-all-users", methods=["GET"])
def report_all_users():
users = dao_report_users()
@@ -692,17 +639,6 @@ def get_organizations_and_services_for_user(user_id):
return jsonify(data)
def _create_reset_password_url(email, next_redirect, base_url=None):
data = json.dumps({"email": email, "created_at": str(utc_now())})
static_url_part = "/new-password/"
full_url = url_with_token(
data, static_url_part, current_app.config, base_url=base_url
)
if next_redirect:
full_url += "?{}".format(urlencode({"next": next_redirect}))
return full_url
def _create_verification_url(user, base_url):
data = json.dumps({"user_id": str(user.id), "email": user.email_address})
url = "/verify-email/"

View File

@@ -60,11 +60,6 @@ def get_midnight_in_utc(date):
return datetime.combine(date, datetime.min.time())
def get_midnight_for_day_before(date):
day_before = date - timedelta(1)
return get_midnight_in_utc(day_before)
def get_month_from_utc_column(column):
"""
Where queries need to count notifications by month it needs to be
@@ -112,20 +107,6 @@ def get_dt_string_or_none(val):
return val.strftime(DATETIME_FORMAT) if val else None
def get_uuid_string_or_none(val):
return str(val) if val else None
def format_sequential_number(sequential_number):
return format(sequential_number, "x").zfill(8)
def get_reference_from_personalisation(personalisation):
if personalisation:
return personalisation.get("reference")
return None
# Function used for debugging.
# Do print(hilite(message)) while debugging, then remove your print statements
def hilite(message):