From c04df94b2294a034c562cb8e172269760519a1b6 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 29 Nov 2023 16:08:02 -0500 Subject: [PATCH 001/259] Adjusting some DAO stuff to send expired invites now. Signed-off-by: Cliff Hill --- app/dao/invited_user_dao.py | 13 +++---------- tests/app/dao/test_invited_user_dao.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/dao/invited_user_dao.py b/app/dao/invited_user_dao.py index 20ec5288c..c5eb75918 100644 --- a/app/dao/invited_user_dao.py +++ b/app/dao/invited_user_dao.py @@ -13,26 +13,19 @@ def get_invited_user_by_service_and_id(service_id, invited_user_id): return InvitedUser.query.filter( InvitedUser.service_id == service_id, InvitedUser.id == invited_user_id, - InvitedUser.status != INVITE_EXPIRED, ).one() def get_invited_user_by_id(invited_user_id): - return InvitedUser.query.filter( - InvitedUser.id == invited_user_id, InvitedUser.status != INVITE_EXPIRED - ).one() + return InvitedUser.query.filter(InvitedUser.id == invited_user_id).one() def get_expired_invited_users_for_service(service_id): - return InvitedUser.query.filter( - InvitedUser.service_id == service_id, InvitedUser.status == INVITE_EXPIRED - ).all() + return InvitedUser.query.filter(InvitedUser.service_id == service_id).all() def get_invited_users_for_service(service_id): - return InvitedUser.query.filter( - InvitedUser.service_id == service_id, InvitedUser.status != INVITE_EXPIRED - ).all() + return InvitedUser.query.filter(InvitedUser.service_id == service_id).all() def expire_invitations_created_more_than_two_days_ago(): diff --git a/tests/app/dao/test_invited_user_dao.py b/tests/app/dao/test_invited_user_dao.py index b0a7d75de..4242484c7 100644 --- a/tests/app/dao/test_invited_user_dao.py +++ b/tests/app/dao/test_invited_user_dao.py @@ -155,7 +155,18 @@ def test_should_not_delete_invitations_less_than_two_days_old( assert ( len(InvitedUser.query.filter(InvitedUser.status != INVITE_EXPIRED).all()) == 1 ) - assert InvitedUser.query.first().email_address == "valid@2.com" + assert ( + InvitedUser.query.filter(InvitedUser.status != INVITE_EXPIRED) + .first() + .email_address + == "valid@2.com" + ) + assert ( + InvitedUser.query.filter(InvitedUser.status == INVITE_EXPIRED) + .first() + .email_address + == "expired@1.com" + ) def make_invitation(user, service, age=None, email_address="test@test.com"): From 9bcf8113201c2fd6046dcf4c28686820d6bf2f2d Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Mon, 4 Dec 2023 16:22:32 -0500 Subject: [PATCH 002/259] Cleaned up string formatting for commands.py. Signed-off-by: Cliff Hill --- app/commands.py | 48 ++++++++++++++---------------------------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/app/commands.py b/app/commands.py index 64b933e92..a6734ad29 100644 --- a/app/commands.py +++ b/app/commands.py @@ -118,7 +118,7 @@ def purge_functional_test_data(user_email_prefix): return users = User.query.filter( - User.email_address.like("{}%".format(user_email_prefix)) + User.email_address.like(f"{user_email_prefix}%") ).all() for usr in users: # Make sure the full email includes a uuid in it @@ -126,11 +126,7 @@ def purge_functional_test_data(user_email_prefix): try: uuid.UUID(usr.email_address.split("@")[0].split("+")[1]) except ValueError: - print( - "Skipping {} as the user email doesn't contain a UUID.".format( - usr.email_address - ) - ) + print(f"Skipping {usr.email_address} as the user email doesn't contain a UUID.") else: services = dao_fetch_all_services_by_user(usr.id) if services: @@ -164,7 +160,7 @@ def purge_functional_test_data(user_email_prefix): def insert_inbound_numbers_from_file(file_name): # TODO maintainability what is the purpose of this command? Who would use it and why? - print("Inserting inbound numbers from {}".format(file_name)) + print(f"Inserting inbound numbers from {file_name}") with open(file_name) as file: sql = "insert into inbound_numbers values('{}', '{}', 'sns', null, True, now(), null);" @@ -199,9 +195,7 @@ def rebuild_ft_billing_for_day(service_id, day): def rebuild_ft_data(process_day, service): deleted_rows = delete_billing_data_for_service_for_day(process_day, service) current_app.logger.info( - "deleted {} existing billing rows for {} on {}".format( - deleted_rows, service, process_day - ) + f"deleted {deleted_rows} existing billing rows for {service} on {process_day}" ) transit_data = fetch_billing_data_for_day( process_day=process_day, service_id=service @@ -211,9 +205,7 @@ def rebuild_ft_billing_for_day(service_id, day): # upsert existing rows update_fact_billing(data, process_day) current_app.logger.info( - "added/updated {} billing rows for {} on {}".format( - len(transit_data), service, process_day - ) + f"added/updated {len(transit_data)} billing rows for {service} on {process_day}" ) if service_id: @@ -276,7 +268,7 @@ def bulk_invite_user_to_service(file_name, service_id, user_id, auth_type, permi } current_app.logger.info(f"DATA = {data}") with current_app.test_request_context( - path="/service/{}/invite/".format(service_id), + path=f"/service/{service_id}/invite/", method="POST", data=json.dumps(data), headers={"Content-Type": "application/json"}, @@ -286,16 +278,12 @@ def bulk_invite_user_to_service(file_name, service_id, user_id, auth_type, permi current_app.logger.info(f"RESPONSE {response[1]}") if response[1] != 201: print( - "*** ERROR occurred for email address: {}".format( - email_address.strip() - ) + f"*** ERROR occurred for email address: {email_address.strip()}" ) print(response[0].get_data(as_text=True)) except Exception as e: print( - "*** ERROR occurred for email address: {}. \n{}".format( - email_address.strip(), e - ) + f"*** ERROR occurred for email address: {email_address.strip()}. \n{e}" ) file.close() @@ -318,7 +306,7 @@ def bulk_invite_user_to_service(file_name, service_id, user_id, auth_type, permi ) def update_jobs_archived_flag(start_date, end_date): current_app.logger.info( - "Archiving jobs created between {} to {}".format(start_date, end_date) + f"Archiving jobs created between {start_date} to {end_date}" ) process_date = start_date @@ -337,15 +325,13 @@ def update_jobs_archived_flag(start_date, end_date): ) db.session.commit() current_app.logger.info( - "jobs: --- Completed took {}ms. Archived {} jobs for {}".format( - datetime.now() - start_time, result.rowcount, process_date - ) + f"jobs: --- Completed took {datetime.now() - start_time}ms. Archived {result.rowcount} jobs for {process_date}" ) process_date += timedelta(days=1) total_updated += result.rowcount - current_app.logger.info("Total archived jobs = {}".format(total_updated)) + current_app.logger.info(f"Total archived jobs = {total_updated}") @notify_command(name="populate-organizations-from-file") @@ -551,9 +537,7 @@ def fix_billable_units(): show_prefix=notification.service.prefix_sms, ) print( - "Updating notification: {} with {} billable_units".format( - notification.id, template.fragment_count - ) + f"Updating notification: {notification.id} with {template.fragment_count} billable_units" ) Notification.query.filter(Notification.id == notification.id).update( @@ -586,9 +570,7 @@ def process_row_from_job(job_id, job_row_number): if row.index == job_row_number: notification_id = process_row(row, template, job, job.service) current_app.logger.info( - "Process row {} for job {} created notification_id: {}".format( - job_row_number, job_id, notification_id - ) + f"Process row {job_row_number} for job {job_id} created notification_id: {notification_id}" ) @@ -623,9 +605,7 @@ def populate_annual_billing_with_the_previous_years_allowance(year): latest_annual_billing, {"service_id": row.id} ) free_allowance = [x[0] for x in free_allowance_rows] - print( - "create free limit of {} for service: {}".format(free_allowance[0], row.id) - ) + print(f"create free limit of {free_allowance[0]} for service: {row.id}") dao_create_or_update_annual_billing_for_year( service_id=row.id, free_sms_fragment_limit=free_allowance[0], From 991d9aa6212604dab362afcfa1a10b224cdcd3d0 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Mon, 4 Dec 2023 16:59:05 -0500 Subject: [PATCH 003/259] Made new /resend route in api. Signed-off-by: Cliff Hill --- app/dao/invited_user_dao.py | 7 +++-- app/service_invite/rest.py | 56 +++++++++++++++++++++++++++++-------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/app/dao/invited_user_dao.py b/app/dao/invited_user_dao.py index c5eb75918..c045489e8 100644 --- a/app/dao/invited_user_dao.py +++ b/app/dao/invited_user_dao.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from app import db -from app.models import INVITE_EXPIRED, InvitedUser +from app.models import INVITE_EXPIRED, INVITE_PENDING, InvitedUser def save_invited_user(invited_user): @@ -31,7 +31,10 @@ def get_invited_users_for_service(service_id): def expire_invitations_created_more_than_two_days_ago(): expired = ( db.session.query(InvitedUser) - .filter(InvitedUser.created_at <= datetime.utcnow() - timedelta(days=2)) + .filter( + InvitedUser.created_at <= datetime.utcnow() - timedelta(days=2), + InvitedUser.status.in_(INVITE_PENDING), + ) .update({InvitedUser.status: INVITE_EXPIRED}) ) db.session.commit() diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index b5f9c95a7..e54fba3cd 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -1,3 +1,7 @@ +from re import I + + +from datetime import datetime from flask import Blueprint, current_app, jsonify, request from itsdangerous import BadData, SignatureExpired from notifications_utils.url_safe_token import check_token, generate_token @@ -12,7 +16,7 @@ from app.dao.invited_user_dao import ( ) from app.dao.templates_dao import dao_get_template_by_id from app.errors import InvalidRequest, register_errors -from app.models import EMAIL_TYPE, KEY_TYPE_NORMAL, Service +from app.models import EMAIL_TYPE, INVITE_PENDING, KEY_TYPE_NORMAL, Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -23,13 +27,7 @@ service_invite = Blueprint("service_invite", __name__) register_errors(service_invite) - -@service_invite.route("/service//invite", methods=["POST"]) -def create_invited_user(service_id): - request_json = request.get_json() - invited_user = invited_user_schema.load(request_json) - save_invited_user(invited_user) - +def _create_service_invite(invited_user, invite_link_host): template_id = current_app.config["INVITATION_EMAIL_TEMPLATE_ID"] template = dao_get_template_by_id(template_id) @@ -43,10 +41,7 @@ def create_invited_user(service_id): personalisation={ "user_name": invited_user.from_user.name, "service_name": invited_user.service.name, - "url": invited_user_url( - invited_user.id, - request_json.get("invite_link_host"), - ), + "url": invited_user_url(invited_user.id, invite_link_host), }, notification_type=EMAIL_TYPE, api_key_id=None, @@ -56,6 +51,15 @@ def create_invited_user(service_id): send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) + +@service_invite.route("/service//invite", methods=["POST"]) +def create_invited_user(service_id): + request_json = request.get_json() + invited_user = invited_user_schema.load(request_json) + save_invited_user(invited_user) + + _create_service_invite(invited_user, request_json.get("invite_link_host")) + return jsonify(data=invited_user_schema.dump(invited_user)), 201 @@ -92,6 +96,34 @@ def update_invited_user(service_id, invited_user_id): return jsonify(data=invited_user_schema.dump(fetched)), 200 +@service_invite.route( + "/service//invite//resend", methods=["POST"] +) +def resend_service_invite(service_id, invited_user_id): + """Resend an expired invite. + + This resets the invited user's created date and status to make it a new invite, and + sends the new invite out to the user. + + Note: + This ignores the POST data entirely. + """ + fetched = get_invited_user_by_service_and_id( + service_id=service_id, invited_user_id=invited_user_id + ) + + fetched.created_at = datetime.utcnow() + fetched.status = INVITE_PENDING + + current_data = {k: v for k, v in invited_user_schema.dump(fetched).items()} + update_dict = invited_user_schema.load(current_data) + save_invited_user(update_dict) + + _create_service_invite(fetched, current_app.config["ADMIN_BASE_URL"]) + + return jsonify(data=invited_user_schema.dump(fetched)), 200 + + def invited_user_url(invited_user_id, invite_link_host=None): token = generate_token( str(invited_user_id), From 5277953761bccfcb62fcf59590d2e5f7bbfc5621 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 7 Dec 2023 16:26:25 -0500 Subject: [PATCH 004/259] Working on api test. Signed-off-by: Cliff Hill --- tests/app/conftest.py | 21 ++++++++++++ .../test_service_invite_rest.py | 33 ++++++++++++------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 13e0c0b09..249dcdcf8 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -556,6 +556,27 @@ def sample_invited_user(notify_db_session): return invited_user +@pytest.fixture(scope="function") +def sample_expired_user(notify_db_session): + service = create_service(check_if_service_exists=True) + to_email_address = "expired_user@digital.fake.gov" + + from_user = service.users[0] + + data = { + "service": service, + "email_address": to_email_address, + "from_user": from_user, + "permissions": "send_messages,manage_service,manage_api_keys", + "folder_permissions": ["folder_1_id", "folder_2_id"], + "created_at": datetime.utcnow() - timedelta(days=3), + "status": "expired", + } + expired_user = InvitedUser(**data) + save_invited_user(expired_user) + return expired_user + + @pytest.fixture(scope="function") def sample_invited_org_user(sample_user, sample_organization): return create_invited_org_user(sample_organization, sample_user) diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index 595303bc8..80d32a3e4 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -141,7 +141,7 @@ def test_create_invited_user_invalid_email(client, sample_service, mocker, fake_ def test_get_all_invited_users_by_service(client, notify_db_session, sample_service): invites = [] for i in range(0, 5): - email = "invited_user_{}@service.gov.uk".format(i) + email = f"invited_user_{i}@service.gov.uk" invited_user = create_invited_user(sample_service, to_email_address=email) invites.append(invited_user) @@ -202,11 +202,24 @@ def test_get_invited_user_by_service_when_user_does_not_belong_to_the_service( assert json_resp["result"] == "error" +def test_resend_expired_invite(client, sample_expired_user): + url = f"/service/{sample_expired_user.service_id}/invite/{sample_expired_user.id}" + # TODO: Don't actually send email, need to mock it out. + auth_header = create_admin_authorization_header() + response = client.post( + url, + data=json.dumps(data), + headers=[("Content-Type", "application/json"), auth_header], + ) + + assert response.status_code == 200 + json_resp = json.loads(response.get_data(as_text=True))["data"] + assert json_resp["status"] == "cancelled" + + def test_update_invited_user_set_status_to_cancelled(client, sample_invited_user): data = {"status": "cancelled"} - url = "/service/{0}/invite/{1}".format( - sample_invited_user.service_id, sample_invited_user.id - ) + url = f"/service/{sample_invited_user.service_id}/invite/{sample_invited_user.id}" auth_header = create_admin_authorization_header() response = client.post( url, @@ -223,7 +236,7 @@ def test_update_invited_user_for_wrong_service_returns_404( client, sample_invited_user, fake_uuid ): data = {"status": "cancelled"} - url = "/service/{0}/invite/{1}".format(fake_uuid, sample_invited_user.id) + url = f"/service/{fake_uuid}/invite/{sample_invited_user.id}" auth_header = create_admin_authorization_header() response = client.post( url, @@ -237,9 +250,7 @@ def test_update_invited_user_for_wrong_service_returns_404( def test_update_invited_user_for_invalid_data_returns_400(client, sample_invited_user): data = {"status": "garbage"} - url = "/service/{0}/invite/{1}".format( - sample_invited_user.service_id, sample_invited_user.id - ) + url = f"/service/{sample_invited_user.service_id}/invite/{sample_invited_user.id}" auth_header = create_admin_authorization_header() response = client.post( url, @@ -291,7 +302,7 @@ def test_validate_invitation_token_for_expired_token_returns_400(client): current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], ) - url = "/invite/service/{}".format(token) + url = f"/invite/service/{token}" auth_header = create_admin_authorization_header() response = client.get( url, headers=[("Content-Type", "application/json"), auth_header] @@ -312,7 +323,7 @@ def test_validate_invitation_token_returns_400_when_invited_user_does_not_exist( current_app.config["SECRET_KEY"], current_app.config["DANGEROUS_SALT"], ) - url = "/invite/service/{}".format(token) + url = f"/invite/service/{token}" auth_header = create_admin_authorization_header() response = client.get( url, headers=[("Content-Type", "application/json"), auth_header] @@ -331,7 +342,7 @@ def test_validate_invitation_token_returns_400_when_token_is_malformed(client): current_app.config["DANGEROUS_SALT"], )[:-2] - url = "/invite/service/{}".format(token) + url = f"/invite/service/{token}" auth_header = create_admin_authorization_header() response = client.get( url, headers=[("Content-Type", "application/json"), auth_header] From 1fdad3099ba91089f1b13e7d8d94538bf9ef8eee Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Dec 2023 13:15:40 -0500 Subject: [PATCH 005/259] Working on tests. Signed-off-by: Cliff Hill --- app/notifications/process_notifications.py | 18 +++++------------- .../service_invite/test_service_invite_rest.py | 8 ++++++-- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 08b8963a5..99272f6b0 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -87,9 +87,7 @@ def persist_notification( if not notification_id: notification_id = uuid.uuid4() - current_app.logger.info( - "Persisting notification with id {}".format(notification_id) - ) + current_app.logger.info(f"Persisting notification with id {notification_id}") notification = Notification( id=notification_id, @@ -124,9 +122,7 @@ def persist_notification( notification.phone_prefix = recipient_info.country_prefix notification.rate_multiplier = recipient_info.billable_units elif notification_type == EMAIL_TYPE: - current_app.logger.info( - "Persisting notification with type: {}".format(EMAIL_TYPE) - ) + current_app.logger.info(f"Persisting notification with type: {EMAIL_TYPE}") notification.normalised_to = format_email_address(notification.to) # if simulated create a Notification model to return but do not persist the Notification to the dB @@ -141,9 +137,7 @@ def persist_notification( ) current_app.logger.info( - "{} {} created at {}".format( - notification_type, notification_id, notification_created_at - ) + f"{notification_type} {notification_id} created at {notification_created_at}" ) return notification @@ -170,15 +164,13 @@ def send_notification_to_queue_detached( raise current_app.logger.debug( - "{} {} sent to the {} queue for delivery".format( - notification_type, notification_id, queue - ) + f"{notification_type} {notification_id} sent to the {queue} queue for delivery" ) def send_notification_to_queue(notification, queue=None): send_notification_to_queue_detached( - notification.key_type, notification.notification_type, notification.id, queue + notification.key_type, notification.notification_type, notification.id, queue, ) diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index 80d32a3e4..f13aa73ac 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -1,3 +1,4 @@ +from functools import partial import json import uuid @@ -202,9 +203,12 @@ def test_get_invited_user_by_service_when_user_does_not_belong_to_the_service( assert json_resp["result"] == "error" -def test_resend_expired_invite(client, sample_expired_user): +def test_resend_expired_invite(client, sample_expired_user, mocker): url = f"/service/{sample_expired_user.service_id}/invite/{sample_expired_user.id}" - # TODO: Don't actually send email, need to mock it out. + mock_send = mocker.patch("app.service_invite.rest.send_notification_to_queue") + mock_persist = mocker.patch("app.service_invite.rest.persist_notification") + from app.notifications.process_notifications import persist_notification + mock_persist.side_effect = partial(persist_notification, simulated=True) auth_header = create_admin_authorization_header() response = client.post( url, From 171326a6b0733ceb34b93fc5c629979bc9c6a599 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Dec 2023 21:41:56 -0500 Subject: [PATCH 006/259] Maybe tests are working now. Signed-off-by: Cliff Hill --- tests/app/service_invite/test_service_invite_rest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index f13aa73ac..fb07e8d78 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -218,7 +218,8 @@ def test_resend_expired_invite(client, sample_expired_user, mocker): assert response.status_code == 200 json_resp = json.loads(response.get_data(as_text=True))["data"] - assert json_resp["status"] == "cancelled" + assert json_resp["status"] == "pending" + assert mock_send.called def test_update_invited_user_set_status_to_cancelled(client, sample_invited_user): From 1157f5639de32427b8f0662b6680491aa88789d6 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Dec 2023 21:43:52 -0500 Subject: [PATCH 007/259] black, isort, flake8 Signed-off-by: Cliff Hill --- app/commands.py | 8 ++++---- app/notifications/process_notifications.py | 5 ++++- app/service_invite/rest.py | 4 ++-- gunicorn_config.py | 6 +++--- migrations/env.py | 4 +++- migrations/versions/0001_restart_migrations.py | 2 +- migrations/versions/0002_add_content_char_count.py | 4 ++-- migrations/versions/0003_add_service_history.py | 2 +- migrations/versions/0004_notification_stats_date.py | 2 +- migrations/versions/0005_add_provider_stats.py | 2 +- migrations/versions/0006_api_keys_history.py | 2 +- migrations/versions/0007_template_history.py | 2 +- migrations/versions/0008_archive_template.py | 2 +- migrations/versions/0009_created_by_for_jobs.py | 2 +- migrations/versions/0010_events_table.py | 2 +- migrations/versions/0011_ad_provider_details.py | 2 +- migrations/versions/0012_complete_provider_details.py | 2 +- migrations/versions/0014_add_template_version.py | 2 +- migrations/versions/0015_fix_template_data.py | 2 +- migrations/versions/0016_reply_to_email.py | 2 +- migrations/versions/0017_add_failure_types.py | 4 ---- migrations/versions/0018_remove_subject_uniqueness.py | 2 +- migrations/versions/0019_add_job_row_number.py | 2 +- migrations/versions/0020_template_history_fix.py | 2 +- migrations/versions/0021_add_delivered_failed_counts.py | 2 +- migrations/versions/0022_add_pending_status.py | 2 +- migrations/versions/0023_add_research_mode.py | 2 +- migrations/versions/0024_add_research_mode_defaults.py | 2 +- migrations/versions/0025_notify_service_data.py | 3 ++- migrations/versions/0026_rename_notify_service.py | 2 +- migrations/versions/0028_fix_reg_template_history.py | 2 +- migrations/versions/0029_fix_email_from.py | 2 +- migrations/versions/0030_service_id_not_null.py | 2 +- migrations/versions/0031_store_personalisation.py | 2 +- migrations/versions/0032_notification_created_status.py | 2 +- migrations/versions/0033_api_key_type.py | 2 +- migrations/versions/0034_pwd_changed_at_not_null.py | 2 +- migrations/versions/0035_notification_type_.py | 2 +- migrations/versions/0036_notif_key_type_not_null.py | 2 +- migrations/versions/0037_service_sms_sender.py | 2 +- migrations/versions/0039_fix_notifications.py | 2 +- migrations/versions/0042_notification_history.py | 2 +- migrations/versions/0043_notification_indexes.py | 2 +- migrations/versions/0044_jobs_to_notification_hist.py | 8 ++++---- migrations/versions/0045_billable_units.py | 4 ++-- migrations/versions/0046_organisations_and_branding.py | 2 +- migrations/versions/0048_job_scheduled_time.py | 2 +- migrations/versions/0050_index_for_stats.py | 2 +- migrations/versions/0052_drop_jobs_status.py | 2 +- migrations/versions/0053_cancelled_job_status.py | 2 +- migrations/versions/0054_perform_drop_status_column.py | 2 +- migrations/versions/0055_service_whitelist.py | 2 +- migrations/versions/0056_minor_updates.py | 2 +- migrations/versions/0058_add_letters_flag.py | 2 +- migrations/versions/0060_add_letter_template_type.py | 2 +- migrations/versions/0061_add_client_reference.py | 2 +- migrations/versions/0062_provider_details_history.py | 2 +- migrations/versions/0063_templates_process_type.py | 2 +- migrations/versions/0064_update_template_process_type.py | 2 +- migrations/versions/0065_users_current_session_id.py | 2 +- migrations/versions/0067_service_contact_block.py | 2 +- migrations/versions/0068_add_created_by_to_provider.py | 2 +- migrations/versions/0069_add_dvla_job_status.py | 2 +- migrations/versions/0070_fix_notify_user_email.py | 2 +- migrations/versions/0071_add_job_error_state.py | 2 +- migrations/versions/0072_add_dvla_orgs.py | 2 +- migrations/versions/0073_add_international_sms_flag.py | 2 +- migrations/versions/0075_create_rates_table.py | 2 +- migrations/versions/0076_add_intl_flag_to_provider.py | 2 +- migrations/versions/0077_add_intl_notification.py | 2 +- migrations/versions/0078_add_sent_notification_status.py | 2 +- migrations/versions/0081_noti_status_as_enum.py | 2 +- migrations/versions/0082_add_golive_template.py | 5 ++--- migrations/versions/0083_add_perm_types_and_svc_perm.py | 2 +- migrations/versions/0084_add_job_stats.py | 2 +- migrations/versions/0085_update_incoming_to_inbound.py | 2 +- migrations/versions/0086_add_norm_to_notification.py | 2 +- migrations/versions/0087_scheduled_notifications.py | 2 +- migrations/versions/0090_inbound_sms.py | 2 +- migrations/versions/0092_add_inbound_provider.py | 2 +- migrations/versions/0094_job_stats_update.py | 2 +- migrations/versions/0095_migrate_existing_svc_perms.py | 2 +- migrations/versions/0096_update_job_stats.py | 2 +- migrations/versions/0097_notnull_inbound_provider.py | 2 +- migrations/versions/0098_service_inbound_api.py | 2 +- migrations/versions/0099_tfl_dar.py | 2 +- migrations/versions/0100_notification_created_by.py | 2 +- migrations/versions/0102_template_redacted.py | 2 +- migrations/versions/0103_add_historical_redact.py | 4 ++-- migrations/versions/0105_opg_letter_org.py | 4 ++-- migrations/versions/0107_drop_template_stats.py | 2 +- migrations/versions/0108_change_logo_not_nullable.py | 2 +- migrations/versions/0109_rem_old_noti_status.py | 2 +- migrations/versions/0110_monthly_billing.py | 2 +- migrations/versions/0111_drop_old_service_flags.py | 2 +- migrations/versions/0112_add_start_end_dates.py | 3 ++- migrations/versions/0113_job_created_by_nullable.py | 2 +- migrations/versions/0114_drop_monthly_billing_cols.py | 2 +- migrations/versions/0115_add_inbound_numbers.py | 2 +- migrations/versions/0117_international_sms_notify.py | 2 +- migrations/versions/0118_service_sms_senders.py | 2 +- migrations/versions/0119_add_email_reply_to.py | 2 +- migrations/versions/0120_add_org_banner_branding.py | 2 +- migrations/versions/0121_nullable_logos.py | 3 +-- migrations/versions/0122_add_service_letter_contact.py | 2 +- migrations/versions/0123_add_noti_to_email_reply.py | 2 +- migrations/versions/0124_add_free_sms_fragment_limit.py | 3 +-- migrations/versions/0125_add_organisation_type.py | 3 +-- migrations/versions/0126_add_annual_billing.py | 2 +- migrations/versions/0127_remove_unique_constraint.py | 2 +- migrations/versions/0128_noti_to_sms_sender.py | 2 +- migrations/versions/0129_add_email_auth_permission_.py | 1 - migrations/versions/0130_service_email_reply_to_row.py | 1 - migrations/versions/0131_user_auth_types.py | 3 +-- migrations/versions/0132_add_sms_prefix_setting.py | 2 +- migrations/versions/0133_set_services_sms_prefix.py | 2 +- migrations/versions/0135_stats_template_usage.py | 2 +- migrations/versions/0136_user_mobile_nullable.py | 4 ++-- migrations/versions/0138_sms_sender_nullable.py | 2 +- migrations/versions/0139_migrate_sms_allowance_data.py | 5 ++--- migrations/versions/0140_sms_prefix_non_nullable.py | 2 +- migrations/versions/0141_remove_unused.py | 2 +- migrations/versions/0143_remove_reply_to.py | 3 +-- migrations/versions/0144_template_service_letter.py | 2 +- migrations/versions/0145_add_notification_reply_to.py | 3 +-- migrations/versions/0146_add_service_callback_api.py | 2 +- migrations/versions/0147_drop_mapping_tables.py | 2 +- migrations/versions/0149_add_crown_to_services.py | 3 +-- migrations/versions/0152_kill_service_free_fragments.py | 2 +- migrations/versions/0156_set_temp_letter_contact.py | 1 - migrations/versions/0157_add_rate_limit_to_service.py | 3 +-- migrations/versions/0158_remove_rate_limit_default.py | 3 +-- migrations/versions/0159_add_historical_redact.py | 4 ++-- migrations/versions/0161_email_branding.py | 2 +- migrations/versions/0162_remove_org.py | 2 +- migrations/versions/0163_add_new_org_model.py | 2 +- migrations/versions/0164_add_organisation_to_service.py | 2 +- migrations/versions/0166_add_org_user_stuff.py | 2 +- migrations/versions/0168_hidden_templates.py | 3 +-- migrations/versions/0169_hidden_templates_nullable.py | 1 - migrations/versions/0170_hidden_non_nullable.py | 1 - migrations/versions/0172_deprioritise_examples.py | 4 ++-- migrations/versions/0173_create_daily_sorted_letter.py | 2 +- migrations/versions/0174_add_billing_facts.py | 2 +- migrations/versions/0175_drop_job_statistics_table.py | 2 +- migrations/versions/0176_alter_billing_columns.py | 2 +- migrations/versions/0177_add_virus_scan_statuses.py | 1 - migrations/versions/0178_add_filename.py | 3 +-- migrations/versions/0179_billing_primary_const.py | 2 +- migrations/versions/0181_billing_primary_key.py | 2 +- migrations/versions/0183_alter_primary_key.py | 2 +- migrations/versions/0184_alter_primary_key_1.py | 2 +- migrations/versions/0185_add_is_active_to_reply_tos.py | 3 +-- migrations/versions/0186_rename_is_active_columns.py | 3 +-- migrations/versions/0188_add_ft_notification_status.py | 2 +- migrations/versions/0189_ft_billing_data_type.py | 2 +- migrations/versions/0192_drop_provider_statistics.py | 2 +- migrations/versions/0193_add_ft_billing_timestamps.py | 3 +-- migrations/versions/0194_ft_billing_created_at.py | 2 +- migrations/versions/0195_ft_notification_timestamps.py | 3 +-- migrations/versions/0196_complaints_table_.py | 2 +- migrations/versions/0197_service_contact_link.py | 3 +-- migrations/versions/0204_service_data_retention.py | 2 +- migrations/versions/0205_service_callback_type.py | 3 +-- migrations/versions/0206_assign_callback_type.py | 3 +-- migrations/versions/0207_set_callback_history_type.py | 3 +-- migrations/versions/0210_remove_monthly_billing.py | 2 +- migrations/versions/0211_email_branding_update_.py | 3 +-- migrations/versions/0212_remove_caseworking.py | 1 - migrations/versions/0213_brand_colour_domain_.py | 2 +- migrations/versions/0215_email_brand_type.py | 2 +- migrations/versions/0216_remove_colours.py | 2 +- migrations/versions/0221_nullable_service_branding.py | 1 - migrations/versions/0222_drop_service_branding.py | 3 +-- migrations/versions/0223_add_domain_constraint.py | 1 - migrations/versions/0224_returned_letter_status.py | 1 - migrations/versions/0226_service_postage.py | 3 +-- migrations/versions/0227_postage_constraints.py | 2 +- migrations/versions/0228_notification_postage.py | 3 +-- migrations/versions/0230_noti_postage_constraint_1.py | 1 - migrations/versions/0231_noti_postage_constraint_2.py | 1 - migrations/versions/0232_noti_postage_constraint_3.py | 1 - migrations/versions/0234_ft_billing_postage.py | 3 +-- migrations/versions/0235_add_postage_to_pk.py | 3 +-- migrations/versions/0237_add_filename_to_dvla_org.py | 3 +-- migrations/versions/0238_add_validation_failed.py | 3 +-- migrations/versions/0239_add_edit_folder_permission.py | 3 +-- migrations/versions/0240_dvla_org_non_nullable.py | 3 +-- migrations/versions/0242_template_folders.py | 2 +- migrations/versions/0245_archived_flag_jobs.py | 3 +-- migrations/versions/0248_enable_choose_postage.py | 3 +-- migrations/versions/0250_drop_stats_template_table.py | 2 +- migrations/versions/0252_letter_branding_table.py | 2 +- migrations/versions/0253_set_template_postage_.py | 3 +-- migrations/versions/0254_folders_for_all.py | 1 - migrations/versions/0256_set_postage_tmplt_hstr.py | 3 +-- migrations/versions/0258_service_postage_nullable.py | 3 +-- migrations/versions/0259_remove_service_postage.py | 3 +-- migrations/versions/0260_remove_dvla_organisation.py | 3 +-- migrations/versions/0261_service_volumes.py | 4 ++-- migrations/versions/0263_remove_edit_folders_2.py | 3 +-- migrations/versions/0264_add_folder_permissions_perm.py | 1 - migrations/versions/0266_user_folder_perms_table.py | 2 +- migrations/versions/0277_consent_to_research_null.py | 2 +- migrations/versions/0278_add_more_stuff_to_orgs.py | 2 +- migrations/versions/0280_invited_user_folder_perms.py | 2 +- migrations/versions/0281_non_null_folder_permissions.py | 2 +- migrations/versions/0282_add_count_as_live.py | 2 +- migrations/versions/0283_platform_admin_not_live.py | 3 +-- migrations/versions/0284_0283_retry.py | 2 +- migrations/versions/0285_default_org_branding.py | 2 +- migrations/versions/0286_add_unique_email_name.py | 2 +- migrations/versions/0287_drop_branding_domains.py | 2 +- migrations/versions/0288_add_go_live_user.py | 2 +- migrations/versions/0290_org_go_live_notes.py | 3 +-- migrations/versions/0291_remove_unused_index.py | 3 +-- migrations/versions/0292_give_users_folder_perms.py | 1 - migrations/versions/0293_drop_complaint_fk.py | 1 - migrations/versions/0295_api_key_constraint.py | 2 +- migrations/versions/0296_agreement_signed_by_person.py | 2 +- migrations/versions/0297_template_redacted_fix.py | 1 - migrations/versions/0299_org_types_table.py | 3 +-- migrations/versions/0300_migrate_org_types.py | 3 +-- migrations/versions/0301_upload_letters_permission.py | 3 +-- migrations/versions/0302_add_org_id_to_services.py | 2 +- migrations/versions/0303_populate_services_org_id.py | 2 +- migrations/versions/0304_remove_org_to_service.py | 2 +- migrations/versions/0307_delete_dm_datetime.py | 2 +- migrations/versions/0308_delete_loadtesting_provider.py | 1 + migrations/versions/0310_returned_letters_table_.py | 2 +- migrations/versions/0311_add_inbound_sms_history.py | 2 +- migrations/versions/0313_email_access_validated_at.py | 3 +-- migrations/versions/0314_populate_email_access.py | 1 - migrations/versions/0315_document_download_count.py | 3 +-- migrations/versions/0316_int_letters_permission.py | 3 +-- migrations/versions/0317_uploads_for_all.py | 1 - migrations/versions/0318_service_contact_list.py | 2 +- migrations/versions/0319_contact_list_archived.py | 2 +- migrations/versions/0321_drop_postage_constraints.py | 1 - migrations/versions/0322_broadcast_service_perm.py | 1 - migrations/versions/0323_broadcast_message.py | 4 ++-- migrations/versions/0326_broadcast_event.py | 2 +- migrations/versions/0327_idx_notification_history.py | 1 + migrations/versions/0329_purge_broadcast_data.py | 1 - migrations/versions/0331_add_broadcast_org.py | 5 +++-- migrations/versions/0332_broadcast_provider_msg.py | 2 +- migrations/versions/0333_service_broadcast_provider.py | 2 +- migrations/versions/0334_broadcast_message_number.py | 2 +- migrations/versions/0335_broadcast_msg_content.py | 2 +- migrations/versions/0336_broadcast_msg_content_2.py | 2 +- migrations/versions/0337_broadcast_msg_api.py | 2 +- migrations/versions/0338_add_notes_to_service.py | 3 +-- migrations/versions/0339_service_billing_details.py | 3 +-- migrations/versions/0340_stub_training_broadcasts.py | 3 +-- migrations/versions/0342_service_broadcast_settings.py | 2 +- migrations/versions/0343_org_billing_details.py | 3 +-- migrations/versions/0344_stubbed_not_nullable.py | 3 +-- migrations/versions/0345_move_broadcast_provider.py | 2 +- ...grate_broadcast_settings_migrate_broadcast_settings.py | 2 +- migrations/versions/0349_add_ft_processing_time.py | 2 +- migrations/versions/0352_broadcast_provider_types.py | 2 +- migrations/versions/0353_broadcast_provider_not_null.py | 2 +- migrations/versions/0355_add_webauthn_table.py | 2 +- migrations/versions/0359_more_permissions.py | 2 +- migrations/versions/0362_broadcast_msg_event.py | 2 +- migrations/versions/0363_cancelled_by_api_key.py | 2 +- migrations/versions/0364_drop_old_column.py | 2 +- migrations/versions/0373_add_notifications_view.py | 2 +- migrations/versions/0374_fix_reg_template_history.py | 2 +- migrations/versions/0376_add_provider_response.py | 2 +- migrations/versions/0378_add_org_names.py | 2 +- migrations/versions/0379_remove_broadcasts.py | 4 ++-- migrations/versions/0380_bst_to_local.py | 2 +- migrations/versions/0381_encrypted_column_types.py | 3 +-- migrations/versions/0383_update_default_templates.py | 5 +++-- migrations/versions/0384_remove_letter_branding_.py | 2 +- migrations/versions/0385_remove postage_.py | 2 +- migrations/versions/0387_remove_letter_perms_.py | 2 +- migrations/versions/0388_no_serv_letter_contact.py | 2 +- migrations/versions/0389_no_more_letters.py | 2 +- migrations/versions/0390_drop_dvla_provider.py | 2 +- migrations/versions/0391_update_sms_numbers.py | 2 +- migrations/versions/0392_drop_letter_permissions_.py | 1 - migrations/versions/0393_remove_crown.py | 2 +- migrations/versions/0394_remove_contact_list_.py | 2 +- migrations/versions/0395_remove_intl_letters_perm.py | 2 +- migrations/versions/0396_rename_organisation.py | 3 +-- migrations/versions/0397_rename_organisation_2.py | 2 +- migrations/versions/0398_agreements_table.py | 2 +- migrations/versions/0399_remove_research_mode.py | 2 +- migrations/versions/0400_add_total_message_limit.py | 3 +-- migrations/versions/0401_add_e2e_test_user.py | 2 +- migrations/versions/0402_total_message_limit_default.py | 3 +-- migrations/versions/0403_add_carrier.py | 3 +-- migrations/versions/0404_expire_invites.py | 3 ++- migrations/versions/0405_add_preferred_timezone.py | 3 +-- migrations/versions/0406_adjust_agreement_model.py | 2 +- migrations/versions/0407_fix_preferred_timezone.py | 5 +++-- migrations/versions/0408_fix_timezone_again.py | 5 +++-- run_celery.py | 3 +-- scripts/check_if_new_migration.py | 5 +++-- tests/app/service_invite/test_service_invite_rest.py | 3 ++- 302 files changed, 317 insertions(+), 389 deletions(-) diff --git a/app/commands.py b/app/commands.py index a6734ad29..1883a8a57 100644 --- a/app/commands.py +++ b/app/commands.py @@ -117,16 +117,16 @@ def purge_functional_test_data(user_email_prefix): current_app.logger.error("Can only be run in development") return - users = User.query.filter( - User.email_address.like(f"{user_email_prefix}%") - ).all() + users = User.query.filter(User.email_address.like(f"{user_email_prefix}%")).all() for usr in users: # Make sure the full email includes a uuid in it # Just in case someone decides to use a similar email address. try: uuid.UUID(usr.email_address.split("@")[0].split("+")[1]) except ValueError: - print(f"Skipping {usr.email_address} as the user email doesn't contain a UUID.") + print( + f"Skipping {usr.email_address} as the user email doesn't contain a UUID." + ) else: services = dao_fetch_all_services_by_user(usr.id) if services: diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 99272f6b0..0b46c8b7c 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -170,7 +170,10 @@ def send_notification_to_queue_detached( def send_notification_to_queue(notification, queue=None): send_notification_to_queue_detached( - notification.key_type, notification.notification_type, notification.id, queue, + notification.key_type, + notification.notification_type, + notification.id, + queue, ) diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index e54fba3cd..dbc706ce1 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -1,7 +1,6 @@ +from datetime import datetime from re import I - -from datetime import datetime from flask import Blueprint, current_app, jsonify, request from itsdangerous import BadData, SignatureExpired from notifications_utils.url_safe_token import check_token, generate_token @@ -27,6 +26,7 @@ service_invite = Blueprint("service_invite", __name__) register_errors(service_invite) + def _create_service_invite(invited_user, invite_link_host): template_id = current_app.config["INVITATION_EMAIL_TEMPLATE_ID"] diff --git a/gunicorn_config.py b/gunicorn_config.py index da71c5fe2..e71cbe944 100644 --- a/gunicorn_config.py +++ b/gunicorn_config.py @@ -1,10 +1,10 @@ import os +import socket import sys import traceback -import gunicorn -import eventlet -import socket +import eventlet +import gunicorn workers = 4 worker_class = "eventlet" diff --git a/migrations/env.py b/migrations/env.py index 8ba18833f..6f3fec0f9 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -1,7 +1,9 @@ from __future__ import with_statement + +from logging.config import fileConfig + from alembic import context from sqlalchemy import engine_from_config, pool -from logging.config import fileConfig # this is the Alembic Config object, which provides # access to the values within the .ini file in use. diff --git a/migrations/versions/0001_restart_migrations.py b/migrations/versions/0001_restart_migrations.py index d7cf75d64..266e82a0f 100644 --- a/migrations/versions/0001_restart_migrations.py +++ b/migrations/versions/0001_restart_migrations.py @@ -10,8 +10,8 @@ Create Date: 2016-04-07 17:22:12.147542 revision = "0001_restart_migrations" down_revision = None -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0002_add_content_char_count.py b/migrations/versions/0002_add_content_char_count.py index 4dae72308..0854dd82a 100644 --- a/migrations/versions/0002_add_content_char_count.py +++ b/migrations/versions/0002_add_content_char_count.py @@ -10,9 +10,9 @@ Create Date: 2016-04-15 12:12:46.383782 revision = "0002_add_content_char_count" down_revision = "0001_restart_migrations" -from alembic import op import sqlalchemy as sa -from sqlalchemy.sql import table, column +from alembic import op +from sqlalchemy.sql import column, table def upgrade(): diff --git a/migrations/versions/0003_add_service_history.py b/migrations/versions/0003_add_service_history.py index 981517fdc..4d48c517e 100644 --- a/migrations/versions/0003_add_service_history.py +++ b/migrations/versions/0003_add_service_history.py @@ -10,8 +10,8 @@ Create Date: 2016-04-19 13:01:54.519821 revision = "0003_add_service_history" down_revision = "0002_add_content_char_count" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0004_notification_stats_date.py b/migrations/versions/0004_notification_stats_date.py index 9fc841b68..8c1368b11 100644 --- a/migrations/versions/0004_notification_stats_date.py +++ b/migrations/versions/0004_notification_stats_date.py @@ -10,8 +10,8 @@ Create Date: 2016-04-20 13:59:01.132535 revision = "0004_notification_stats_date" down_revision = "0003_add_service_history" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0005_add_provider_stats.py b/migrations/versions/0005_add_provider_stats.py index 9d328ec09..e99360777 100644 --- a/migrations/versions/0005_add_provider_stats.py +++ b/migrations/versions/0005_add_provider_stats.py @@ -10,8 +10,8 @@ Create Date: 2016-04-20 15:13:42.229197 revision = "0005_add_provider_stats" down_revision = "0004_notification_stats_date" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0006_api_keys_history.py b/migrations/versions/0006_api_keys_history.py index dadf3d73f..57647ae3e 100644 --- a/migrations/versions/0006_api_keys_history.py +++ b/migrations/versions/0006_api_keys_history.py @@ -10,8 +10,8 @@ Create Date: 2016-04-20 17:21:38.541766 revision = "0006_api_keys_history" down_revision = "0005_add_provider_stats" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0007_template_history.py b/migrations/versions/0007_template_history.py index e85a5e608..c52c74102 100644 --- a/migrations/versions/0007_template_history.py +++ b/migrations/versions/0007_template_history.py @@ -10,8 +10,8 @@ Create Date: 2016-04-22 09:51:55.615891 revision = "0007_template_history" down_revision = "0006_api_keys_history" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0008_archive_template.py b/migrations/versions/0008_archive_template.py index 836999bc7..c19a47d65 100644 --- a/migrations/versions/0008_archive_template.py +++ b/migrations/versions/0008_archive_template.py @@ -10,8 +10,8 @@ Create Date: 2016-04-25 14:16:49.787229 revision = "0008_archive_template" down_revision = "0007_template_history" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0009_created_by_for_jobs.py b/migrations/versions/0009_created_by_for_jobs.py index 5aebfb099..622b22b63 100644 --- a/migrations/versions/0009_created_by_for_jobs.py +++ b/migrations/versions/0009_created_by_for_jobs.py @@ -10,8 +10,8 @@ Create Date: 2016-04-26 14:54:56.852642 revision = "0009_created_by_for_jobs" down_revision = "0008_archive_template" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0010_events_table.py b/migrations/versions/0010_events_table.py index 1ec629ea6..fdb3a34c5 100644 --- a/migrations/versions/0010_events_table.py +++ b/migrations/versions/0010_events_table.py @@ -10,8 +10,8 @@ Create Date: 2016-04-26 13:08:42.892813 revision = "0010_events_table" down_revision = "0009_created_by_for_jobs" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0011_ad_provider_details.py b/migrations/versions/0011_ad_provider_details.py index 9645e20e6..dfda65ce2 100644 --- a/migrations/versions/0011_ad_provider_details.py +++ b/migrations/versions/0011_ad_provider_details.py @@ -14,8 +14,8 @@ down_revision = "0010_events_table" import uuid -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0012_complete_provider_details.py b/migrations/versions/0012_complete_provider_details.py index 41d617995..e9b4514ad 100644 --- a/migrations/versions/0012_complete_provider_details.py +++ b/migrations/versions/0012_complete_provider_details.py @@ -10,8 +10,8 @@ Create Date: 2016-05-05 09:18:26.926275 revision = "0012_complete_provider_details" down_revision = "0011_ad_provider_details" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql from sqlalchemy.dialects.postgresql import ENUM diff --git a/migrations/versions/0014_add_template_version.py b/migrations/versions/0014_add_template_version.py index 4e4d0f814..8074187b8 100644 --- a/migrations/versions/0014_add_template_version.py +++ b/migrations/versions/0014_add_template_version.py @@ -10,8 +10,8 @@ Create Date: 2016-05-11 16:00:51.478012 revision = "0014_add_template_version" down_revision = "0012_complete_provider_details" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0015_fix_template_data.py b/migrations/versions/0015_fix_template_data.py index 3052e1caa..3bf8cd332 100644 --- a/migrations/versions/0015_fix_template_data.py +++ b/migrations/versions/0015_fix_template_data.py @@ -10,8 +10,8 @@ Create Date: 2016-05-16 13:55:27.179748 revision = "0015_fix_template_data" down_revision = "0014_add_template_version" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0016_reply_to_email.py b/migrations/versions/0016_reply_to_email.py index 990aa36bf..cada6e580 100644 --- a/migrations/versions/0016_reply_to_email.py +++ b/migrations/versions/0016_reply_to_email.py @@ -10,8 +10,8 @@ Create Date: 2016-05-17 09:59:49.032865 revision = "0016_reply_to_email" down_revision = "0015_fix_template_data" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0017_add_failure_types.py b/migrations/versions/0017_add_failure_types.py index 2e5356a31..959ad4fbf 100644 --- a/migrations/versions/0017_add_failure_types.py +++ b/migrations/versions/0017_add_failure_types.py @@ -10,12 +10,8 @@ Create Date: 2016-05-17 11:23:36.881219 revision = "0017_add_failure_types" down_revision = "0016_reply_to_email" -from alembic import op import sqlalchemy as sa - - from alembic import op -import sqlalchemy as sa from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0018_remove_subject_uniqueness.py b/migrations/versions/0018_remove_subject_uniqueness.py index 95fcf5f1b..30e4697c5 100644 --- a/migrations/versions/0018_remove_subject_uniqueness.py +++ b/migrations/versions/0018_remove_subject_uniqueness.py @@ -10,8 +10,8 @@ Create Date: 2016-05-18 09:39:22.512042 revision = "0018_remove_subject_uniqueness" down_revision = "0017_add_failure_types" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0019_add_job_row_number.py b/migrations/versions/0019_add_job_row_number.py index 1860ae623..605f31067 100644 --- a/migrations/versions/0019_add_job_row_number.py +++ b/migrations/versions/0019_add_job_row_number.py @@ -10,8 +10,8 @@ Create Date: 2016-05-18 15:04:24.513071 revision = "0019_add_job_row_number" down_revision = "0018_remove_subject_uniqueness" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0020_template_history_fix.py b/migrations/versions/0020_template_history_fix.py index 06df7cecb..d1ba73d6b 100644 --- a/migrations/versions/0020_template_history_fix.py +++ b/migrations/versions/0020_template_history_fix.py @@ -10,8 +10,8 @@ Create Date: 2016-05-20 15:15:03.850862 revision = "0020_template_history_fix" down_revision = "0019_add_job_row_number" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0021_add_delivered_failed_counts.py b/migrations/versions/0021_add_delivered_failed_counts.py index 620e4e33a..62b69f13b 100644 --- a/migrations/versions/0021_add_delivered_failed_counts.py +++ b/migrations/versions/0021_add_delivered_failed_counts.py @@ -12,8 +12,8 @@ from sqlalchemy import text revision = "0021_add_delivered_failed_counts" down_revision = "0020_template_history_fix" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0022_add_pending_status.py b/migrations/versions/0022_add_pending_status.py index 9a03adba9..944958326 100644 --- a/migrations/versions/0022_add_pending_status.py +++ b/migrations/versions/0022_add_pending_status.py @@ -10,8 +10,8 @@ Create Date: 2016-05-25 15:47:32.568097 revision = "0022_add_pending_status" down_revision = "0021_add_delivered_failed_counts" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0023_add_research_mode.py b/migrations/versions/0023_add_research_mode.py index a7a085b74..ff2b0497f 100644 --- a/migrations/versions/0023_add_research_mode.py +++ b/migrations/versions/0023_add_research_mode.py @@ -10,8 +10,8 @@ Create Date: 2016-05-31 11:11:45.979594 revision = "0023_add_research_mode" down_revision = "0022_add_pending_status" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0024_add_research_mode_defaults.py b/migrations/versions/0024_add_research_mode_defaults.py index ac45f2abb..26cc733b4 100644 --- a/migrations/versions/0024_add_research_mode_defaults.py +++ b/migrations/versions/0024_add_research_mode_defaults.py @@ -10,8 +10,8 @@ Create Date: 2016-05-31 11:11:45.979594 revision = "0024_add_research_mode_defaults" down_revision = "0023_add_research_mode" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0025_notify_service_data.py b/migrations/versions/0025_notify_service_data.py index c8eceb70c..a5cf99c24 100644 --- a/migrations/versions/0025_notify_service_data.py +++ b/migrations/versions/0025_notify_service_data.py @@ -6,6 +6,8 @@ Create Date: 2016-06-01 14:17:01.963181 """ +import uuid + # revision identifiers, used by Alembic. from datetime import datetime @@ -13,7 +15,6 @@ from alembic import op from sqlalchemy import text from app.hashing import hashpw -import uuid revision = "0025_notify_service_data" down_revision = "0024_add_research_mode_defaults" diff --git a/migrations/versions/0026_rename_notify_service.py b/migrations/versions/0026_rename_notify_service.py index 5d1c080ee..9cf2ed427 100644 --- a/migrations/versions/0026_rename_notify_service.py +++ b/migrations/versions/0026_rename_notify_service.py @@ -10,8 +10,8 @@ Create Date: 2016-06-07 09:51:07.343334 revision = "0026_rename_notify_service" down_revision = "0025_notify_service_data" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0028_fix_reg_template_history.py b/migrations/versions/0028_fix_reg_template_history.py index 602e3c113..8bf11fe59 100644 --- a/migrations/versions/0028_fix_reg_template_history.py +++ b/migrations/versions/0028_fix_reg_template_history.py @@ -14,8 +14,8 @@ from sqlalchemy import text revision = "0028_fix_reg_template_history" down_revision = "0026_rename_notify_service" -from alembic import op import sqlalchemy as sa +from alembic import op service_id = "d6aa2c68-a2d9-4437-ab19-3ae8eb202553" user_id = "6af522d0-2915-4e52-83a3-3690455a5fe6" diff --git a/migrations/versions/0029_fix_email_from.py b/migrations/versions/0029_fix_email_from.py index 65a36c233..d4735de50 100644 --- a/migrations/versions/0029_fix_email_from.py +++ b/migrations/versions/0029_fix_email_from.py @@ -12,8 +12,8 @@ from sqlalchemy import text revision = "0029_fix_email_from" down_revision = "0028_fix_reg_template_history" -from alembic import op import sqlalchemy as sa +from alembic import op service_id = "d6aa2c68-a2d9-4437-ab19-3ae8eb202553" diff --git a/migrations/versions/0030_service_id_not_null.py b/migrations/versions/0030_service_id_not_null.py index 367d17cd0..788b36281 100644 --- a/migrations/versions/0030_service_id_not_null.py +++ b/migrations/versions/0030_service_id_not_null.py @@ -13,8 +13,8 @@ from sqlalchemy.dialects import postgresql revision = "0030_service_id_not_null" down_revision = "0029_fix_email_from" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0031_store_personalisation.py b/migrations/versions/0031_store_personalisation.py index c83b8225a..e46b992ce 100644 --- a/migrations/versions/0031_store_personalisation.py +++ b/migrations/versions/0031_store_personalisation.py @@ -10,8 +10,8 @@ Create Date: 2016-06-20 10:39:50.892847 revision = "0031_store_personalisation" down_revision = "0030_service_id_not_null" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0032_notification_created_status.py b/migrations/versions/0032_notification_created_status.py index f82179c4c..26c608426 100644 --- a/migrations/versions/0032_notification_created_status.py +++ b/migrations/versions/0032_notification_created_status.py @@ -10,8 +10,8 @@ Create Date: 2016-06-21 11:29:28.963615 revision = "0032_notification_created_status" down_revision = "0031_store_personalisation" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0033_api_key_type.py b/migrations/versions/0033_api_key_type.py index 5a6a16fa8..e52d04af0 100644 --- a/migrations/versions/0033_api_key_type.py +++ b/migrations/versions/0033_api_key_type.py @@ -10,8 +10,8 @@ Create Date: 2016-06-24 12:02:10.915817 revision = "0033_api_key_type" down_revision = "0032_notification_created_status" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0034_pwd_changed_at_not_null.py b/migrations/versions/0034_pwd_changed_at_not_null.py index 70f4caf31..c3a01d9bb 100644 --- a/migrations/versions/0034_pwd_changed_at_not_null.py +++ b/migrations/versions/0034_pwd_changed_at_not_null.py @@ -10,8 +10,8 @@ Create Date: 2016-06-28 10:37:25.389020 revision = "0034_pwd_changed_at_not_null" down_revision = "0033_api_key_type" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0035_notification_type_.py b/migrations/versions/0035_notification_type_.py index fd129f698..dcffe419c 100644 --- a/migrations/versions/0035_notification_type_.py +++ b/migrations/versions/0035_notification_type_.py @@ -10,8 +10,8 @@ Create Date: 2016-06-29 10:48:55.955317 revision = "0035_notification_type" down_revision = "0034_pwd_changed_at_not_null" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0036_notif_key_type_not_null.py b/migrations/versions/0036_notif_key_type_not_null.py index 75c688f7c..9c5ced60e 100644 --- a/migrations/versions/0036_notif_key_type_not_null.py +++ b/migrations/versions/0036_notif_key_type_not_null.py @@ -10,8 +10,8 @@ Create Date: 2016-07-01 16:01:16.892638 revision = "0036_notif_key_type_not_null" down_revision = "0035_notification_type" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0037_service_sms_sender.py b/migrations/versions/0037_service_sms_sender.py index 3971326c3..d50adcb81 100644 --- a/migrations/versions/0037_service_sms_sender.py +++ b/migrations/versions/0037_service_sms_sender.py @@ -10,8 +10,8 @@ Create Date: 2016-06-30 14:55:33.811696 revision = "0037_service_sms_sender" down_revision = "0036_notif_key_type_not_null" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0039_fix_notifications.py b/migrations/versions/0039_fix_notifications.py index 295666f1f..9f6bfa2f6 100644 --- a/migrations/versions/0039_fix_notifications.py +++ b/migrations/versions/0039_fix_notifications.py @@ -12,8 +12,8 @@ from sqlalchemy import text revision = "0039_fix_notifications" down_revision = "0038_test_api_key_type" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0042_notification_history.py b/migrations/versions/0042_notification_history.py index 09cc3d8a9..346964477 100644 --- a/migrations/versions/0042_notification_history.py +++ b/migrations/versions/0042_notification_history.py @@ -10,8 +10,8 @@ Create Date: 2016-07-07 13:15:35.503107 revision = "0042_notification_history" down_revision = "0039_fix_notifications" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0043_notification_indexes.py b/migrations/versions/0043_notification_indexes.py index aa96a10c2..431574126 100644 --- a/migrations/versions/0043_notification_indexes.py +++ b/migrations/versions/0043_notification_indexes.py @@ -10,8 +10,8 @@ Create Date: 2016-08-01 10:37:41.198070 revision = "0043_notification_indexes" down_revision = "0042_notification_history" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0044_jobs_to_notification_hist.py b/migrations/versions/0044_jobs_to_notification_hist.py index 86d246849..e813833b4 100644 --- a/migrations/versions/0044_jobs_to_notification_hist.py +++ b/migrations/versions/0044_jobs_to_notification_hist.py @@ -10,13 +10,13 @@ Create Date: 2016-07-15 13:28:41.441009 revision = "0044_jobs_to_notification_hist" down_revision = "0043_notification_indexes" -from alembic import op +import datetime +import uuid +from alembic import op from sqlalchemy.orm.session import Session -import uuid -import datetime -from app.models import Job, Template, NotificationHistory +from app.models import Job, NotificationHistory, Template def upgrade(): diff --git a/migrations/versions/0045_billable_units.py b/migrations/versions/0045_billable_units.py index 1cede8009..41e7bc899 100644 --- a/migrations/versions/0045_billable_units.py +++ b/migrations/versions/0045_billable_units.py @@ -7,13 +7,13 @@ Create Date: 2016-08-02 16:36:42.455838 """ # revision identifiers, used by Alembic. -from sqlalchemy import text, bindparam +from sqlalchemy import bindparam, text revision = "0045_billable_units" down_revision = "0044_jobs_to_notification_hist" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.orm.session import Session from app.models import Service diff --git a/migrations/versions/0046_organisations_and_branding.py b/migrations/versions/0046_organisations_and_branding.py index 3a1092a97..c162d3256 100644 --- a/migrations/versions/0046_organisations_and_branding.py +++ b/migrations/versions/0046_organisations_and_branding.py @@ -10,8 +10,8 @@ Create Date: 2016-08-04 12:00:43.682610 revision = "0046_organisations_and_branding" down_revision = "0045_billable_units" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0048_job_scheduled_time.py b/migrations/versions/0048_job_scheduled_time.py index a8b1d349e..5900176b3 100644 --- a/migrations/versions/0048_job_scheduled_time.py +++ b/migrations/versions/0048_job_scheduled_time.py @@ -10,8 +10,8 @@ Create Date: 2016-08-24 13:21:51.744526 revision = "0048_job_scheduled_time" down_revision = "0046_organisations_and_branding" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0050_index_for_stats.py b/migrations/versions/0050_index_for_stats.py index 69a5e87c3..70514dc9e 100644 --- a/migrations/versions/0050_index_for_stats.py +++ b/migrations/versions/0050_index_for_stats.py @@ -10,8 +10,8 @@ Create Date: 2016-08-24 13:21:51.744526 revision = "0050_index_for_stats" down_revision = "0048_job_scheduled_time" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0052_drop_jobs_status.py b/migrations/versions/0052_drop_jobs_status.py index 9f7285e19..fdee67e93 100644 --- a/migrations/versions/0052_drop_jobs_status.py +++ b/migrations/versions/0052_drop_jobs_status.py @@ -10,8 +10,8 @@ Create Date: 2016-08-25 15:56:31.779399 revision = "0052_drop_jobs_status" down_revision = "0051_set_job_status" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0053_cancelled_job_status.py b/migrations/versions/0053_cancelled_job_status.py index 0485a6c22..05174565b 100644 --- a/migrations/versions/0053_cancelled_job_status.py +++ b/migrations/versions/0053_cancelled_job_status.py @@ -10,8 +10,8 @@ Create Date: 2016-09-01 14:34:06.839381 revision = "0053_cancelled_job_status" down_revision = "0052_drop_jobs_status" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0054_perform_drop_status_column.py b/migrations/versions/0054_perform_drop_status_column.py index 2af29292c..f4c3704e6 100644 --- a/migrations/versions/0054_perform_drop_status_column.py +++ b/migrations/versions/0054_perform_drop_status_column.py @@ -10,8 +10,8 @@ Create Date: 2016-08-25 15:56:31.779399 revision = "0054_perform_drop_status_column" down_revision = "0053_cancelled_job_status" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0055_service_whitelist.py b/migrations/versions/0055_service_whitelist.py index 977e48cdd..574acddde 100644 --- a/migrations/versions/0055_service_whitelist.py +++ b/migrations/versions/0055_service_whitelist.py @@ -10,8 +10,8 @@ Create Date: 2016-09-20 12:12:30.838095 revision = "0055_service_whitelist" down_revision = "0054_perform_drop_status_column" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0056_minor_updates.py b/migrations/versions/0056_minor_updates.py index 0184cd706..a66d12e2d 100644 --- a/migrations/versions/0056_minor_updates.py +++ b/migrations/versions/0056_minor_updates.py @@ -10,8 +10,8 @@ Create Date: 2016-10-04 09:43:42.321138 revision = "0056_minor_updates" down_revision = "0055_service_whitelist" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0058_add_letters_flag.py b/migrations/versions/0058_add_letters_flag.py index aa2492aef..b6afdfda7 100644 --- a/migrations/versions/0058_add_letters_flag.py +++ b/migrations/versions/0058_add_letters_flag.py @@ -10,8 +10,8 @@ Create Date: 2016-10-25 17:37:27.660723 revision = "0058_add_letters_flag" down_revision = "0056_minor_updates" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0060_add_letter_template_type.py b/migrations/versions/0060_add_letter_template_type.py index 256d97170..5ca554265 100644 --- a/migrations/versions/0060_add_letter_template_type.py +++ b/migrations/versions/0060_add_letter_template_type.py @@ -6,8 +6,8 @@ Create Date: 2016-11-07 16:13:18.961527 """ -from alembic import op import sqlalchemy as sa +from alembic import op # revision identifiers, used by Alembic. revision = "0060_add_letter_template_type" diff --git a/migrations/versions/0061_add_client_reference.py b/migrations/versions/0061_add_client_reference.py index 847f165a7..e594bb7fb 100644 --- a/migrations/versions/0061_add_client_reference.py +++ b/migrations/versions/0061_add_client_reference.py @@ -10,8 +10,8 @@ Create Date: 2016-11-17 13:19:25.820617 revision = "0061_add_client_reference" down_revision = "0060_add_letter_template_type" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0062_provider_details_history.py b/migrations/versions/0062_provider_details_history.py index f6d31e87c..4ac6f77cf 100644 --- a/migrations/versions/0062_provider_details_history.py +++ b/migrations/versions/0062_provider_details_history.py @@ -13,8 +13,8 @@ Create Date: 2016-12-14 13:00:24.226990 revision = "0062_provider_details_history" down_revision = "0061_add_client_reference" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0063_templates_process_type.py b/migrations/versions/0063_templates_process_type.py index ad6692b92..679e6b0c5 100644 --- a/migrations/versions/0063_templates_process_type.py +++ b/migrations/versions/0063_templates_process_type.py @@ -10,8 +10,8 @@ Create Date: 2017-01-10 15:39:30.909308 revision = "0063_templates_process_type" down_revision = "0062_provider_details_history" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0064_update_template_process_type.py b/migrations/versions/0064_update_template_process_type.py index 64c64ef52..58689d532 100644 --- a/migrations/versions/0064_update_template_process_type.py +++ b/migrations/versions/0064_update_template_process_type.py @@ -10,8 +10,8 @@ Create Date: 2017-01-16 11:08:00.520678 revision = "0064_update_template_process" down_revision = "0063_templates_process_type" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0065_users_current_session_id.py b/migrations/versions/0065_users_current_session_id.py index e94f0cb38..9a85aabc0 100644 --- a/migrations/versions/0065_users_current_session_id.py +++ b/migrations/versions/0065_users_current_session_id.py @@ -10,8 +10,8 @@ Create Date: 2017-02-17 11:48:40.669235 revision = "0065_users_current_session_id" down_revision = "0064_update_template_process" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0067_service_contact_block.py b/migrations/versions/0067_service_contact_block.py index 7023dd995..51c147043 100644 --- a/migrations/versions/0067_service_contact_block.py +++ b/migrations/versions/0067_service_contact_block.py @@ -10,8 +10,8 @@ Create Date: 2017-02-28 11:23:40.299110 revision = "0067_service_contact_block" down_revision = "0065_users_current_session_id" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0068_add_created_by_to_provider.py b/migrations/versions/0068_add_created_by_to_provider.py index c0af8467f..396f6a1dd 100644 --- a/migrations/versions/0068_add_created_by_to_provider.py +++ b/migrations/versions/0068_add_created_by_to_provider.py @@ -10,8 +10,8 @@ Create Date: 2017-03-06 17:19:28.492005 revision = "0068_add_created_by_to_provider" down_revision = "0067_service_contact_block" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0069_add_dvla_job_status.py b/migrations/versions/0069_add_dvla_job_status.py index 7feba7e9c..f86ba72e9 100644 --- a/migrations/versions/0069_add_dvla_job_status.py +++ b/migrations/versions/0069_add_dvla_job_status.py @@ -10,8 +10,8 @@ Create Date: 2017-03-10 16:15:22.153948 revision = "0069_add_dvla_job_status" down_revision = "0068_add_created_by_to_provider" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0070_fix_notify_user_email.py b/migrations/versions/0070_fix_notify_user_email.py index 4f55893fd..f60925f3d 100644 --- a/migrations/versions/0070_fix_notify_user_email.py +++ b/migrations/versions/0070_fix_notify_user_email.py @@ -10,8 +10,8 @@ Create Date: 2017-03-10 16:15:22.153948 revision = "0070_fix_notify_user_email" down_revision = "0069_add_dvla_job_status" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0071_add_job_error_state.py b/migrations/versions/0071_add_job_error_state.py index 19d923d01..866c04a70 100644 --- a/migrations/versions/0071_add_job_error_state.py +++ b/migrations/versions/0071_add_job_error_state.py @@ -10,8 +10,8 @@ Create Date: 2017-03-10 16:15:22.153948 revision = "0071_add_job_error_state" down_revision = "0070_fix_notify_user_email" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0072_add_dvla_orgs.py b/migrations/versions/0072_add_dvla_orgs.py index 8c829d2ed..482e47260 100644 --- a/migrations/versions/0072_add_dvla_orgs.py +++ b/migrations/versions/0072_add_dvla_orgs.py @@ -10,8 +10,8 @@ Create Date: 2017-04-19 15:25:45.155886 revision = "0072_add_dvla_orgs" down_revision = "0071_add_job_error_state" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0073_add_international_sms_flag.py b/migrations/versions/0073_add_international_sms_flag.py index 371f4e573..404df3349 100644 --- a/migrations/versions/0073_add_international_sms_flag.py +++ b/migrations/versions/0073_add_international_sms_flag.py @@ -10,8 +10,8 @@ Create Date: 2017-10-25 17:37:27.660723 revision = "0073_add_international_sms_flag" down_revision = "0072_add_dvla_orgs" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0075_create_rates_table.py b/migrations/versions/0075_create_rates_table.py index 4ef36d9b2..f3b27a6e6 100644 --- a/migrations/versions/0075_create_rates_table.py +++ b/migrations/versions/0075_create_rates_table.py @@ -14,8 +14,8 @@ from sqlalchemy import text revision = "0075_create_rates_table" down_revision = "0073_add_international_sms_flag" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0076_add_intl_flag_to_provider.py b/migrations/versions/0076_add_intl_flag_to_provider.py index 254bc9298..4853db420 100644 --- a/migrations/versions/0076_add_intl_flag_to_provider.py +++ b/migrations/versions/0076_add_intl_flag_to_provider.py @@ -10,8 +10,8 @@ Create Date: 2017-04-25 09:44:13.194164 revision = "0076_add_intl_flag_to_provider" down_revision = "0075_create_rates_table" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0077_add_intl_notification.py b/migrations/versions/0077_add_intl_notification.py index 05c29e96a..ce751414f 100644 --- a/migrations/versions/0077_add_intl_notification.py +++ b/migrations/versions/0077_add_intl_notification.py @@ -10,8 +10,8 @@ Create Date: 2017-04-25 11:34:43.229494 revision = "0077_add_intl_notification" down_revision = "0076_add_intl_flag_to_provider" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0078_add_sent_notification_status.py b/migrations/versions/0078_add_sent_notification_status.py index 4bbdd6e76..3dc088b3b 100644 --- a/migrations/versions/0078_add_sent_notification_status.py +++ b/migrations/versions/0078_add_sent_notification_status.py @@ -10,8 +10,8 @@ Create Date: 2017-04-24 16:55:20.731069 revision = "0078_sent_notification_status" down_revision = "0077_add_intl_notification" -from alembic import op import sqlalchemy as sa +from alembic import op enum_name = "notify_status_type" tmp_name = "tmp_" + enum_name diff --git a/migrations/versions/0081_noti_status_as_enum.py b/migrations/versions/0081_noti_status_as_enum.py index 42678efe3..968b3a0e2 100644 --- a/migrations/versions/0081_noti_status_as_enum.py +++ b/migrations/versions/0081_noti_status_as_enum.py @@ -10,8 +10,8 @@ Create Date: 2017-05-02 14:50:04.070874 revision = "0081_noti_status_as_enum" down_revision = "0078_sent_notification_status" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0082_add_golive_template.py b/migrations/versions/0082_add_golive_template.py index b8de36f81..55cdc3e04 100644 --- a/migrations/versions/0082_add_golive_template.py +++ b/migrations/versions/0082_add_golive_template.py @@ -9,10 +9,9 @@ Create Date: 2017-05-10 16:06:04.070874 # revision identifiers, used by Alembic. from datetime import datetime -from flask import current_app - -from alembic import op import sqlalchemy as sa +from alembic import op +from flask import current_app from sqlalchemy import text revision = "0082_add_go_live_template" diff --git a/migrations/versions/0083_add_perm_types_and_svc_perm.py b/migrations/versions/0083_add_perm_types_and_svc_perm.py index af79a08bd..d75d59cd8 100644 --- a/migrations/versions/0083_add_perm_types_and_svc_perm.py +++ b/migrations/versions/0083_add_perm_types_and_svc_perm.py @@ -10,8 +10,8 @@ Create Date: 2017-05-12 11:29:32.664811 revision = "0083_add_perm_types_and_svc_perm" down_revision = "0082_add_go_live_template" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0084_add_job_stats.py b/migrations/versions/0084_add_job_stats.py index 8749bc23f..a7aaf5d4a 100644 --- a/migrations/versions/0084_add_job_stats.py +++ b/migrations/versions/0084_add_job_stats.py @@ -10,8 +10,8 @@ Create Date: 2017-05-12 13:16:14.147368 revision = "0084_add_job_stats" down_revision = "0083_add_perm_types_and_svc_perm" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0085_update_incoming_to_inbound.py b/migrations/versions/0085_update_incoming_to_inbound.py index 936b8255e..a86675e8e 100644 --- a/migrations/versions/0085_update_incoming_to_inbound.py +++ b/migrations/versions/0085_update_incoming_to_inbound.py @@ -10,8 +10,8 @@ Create Date: 2017-05-22 10:23:43.939050 revision = "0085_update_incoming_to_inbound" down_revision = "0084_add_job_stats" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0086_add_norm_to_notification.py b/migrations/versions/0086_add_norm_to_notification.py index c68f33f8c..9617688c7 100644 --- a/migrations/versions/0086_add_norm_to_notification.py +++ b/migrations/versions/0086_add_norm_to_notification.py @@ -6,8 +6,8 @@ Create Date: 2017-05-23 10:37:00.404087 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0086_add_norm_to_notification" down_revision = "0085_update_incoming_to_inbound" diff --git a/migrations/versions/0087_scheduled_notifications.py b/migrations/versions/0087_scheduled_notifications.py index 7cc13857c..39f045928 100644 --- a/migrations/versions/0087_scheduled_notifications.py +++ b/migrations/versions/0087_scheduled_notifications.py @@ -5,8 +5,8 @@ Revises: 0086_add_norm_to_notification Create Date: 2017-05-15 12:50:20.041950 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0087_scheduled_notifications" diff --git a/migrations/versions/0090_inbound_sms.py b/migrations/versions/0090_inbound_sms.py index f610d1286..b97a770f5 100644 --- a/migrations/versions/0090_inbound_sms.py +++ b/migrations/versions/0090_inbound_sms.py @@ -10,8 +10,8 @@ Create Date: 2017-05-22 11:28:53.471004 revision = "0090_inbound_sms" down_revision = "0089_govuk_sms_sender" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0092_add_inbound_provider.py b/migrations/versions/0092_add_inbound_provider.py index 9945e4a0c..ccb1531c2 100644 --- a/migrations/versions/0092_add_inbound_provider.py +++ b/migrations/versions/0092_add_inbound_provider.py @@ -10,8 +10,8 @@ Create Date: 2017-06-02 16:07:35.445423 revision = "0092_add_inbound_provider" down_revision = "0090_inbound_sms" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0094_job_stats_update.py b/migrations/versions/0094_job_stats_update.py index 11e205f8d..8a8acd4bc 100644 --- a/migrations/versions/0094_job_stats_update.py +++ b/migrations/versions/0094_job_stats_update.py @@ -5,8 +5,8 @@ Revises: 0092_add_inbound_provider Create Date: 2017-06-06 14:37:30.051647 """ -from alembic import op import sqlalchemy as sa +from alembic import op # revision identifiers, used by Alembic. revision = "0094_job_stats_update" diff --git a/migrations/versions/0095_migrate_existing_svc_perms.py b/migrations/versions/0095_migrate_existing_svc_perms.py index ef6eb5881..5994e8c25 100644 --- a/migrations/versions/0095_migrate_existing_svc_perms.py +++ b/migrations/versions/0095_migrate_existing_svc_perms.py @@ -12,8 +12,8 @@ from sqlalchemy import text revision = "0095_migrate_existing_svc_perms" down_revision = "0094_job_stats_update" -from alembic import op import sqlalchemy as sa +from alembic import op migration_date = "2017-05-26 17:30:00.000000" diff --git a/migrations/versions/0096_update_job_stats.py b/migrations/versions/0096_update_job_stats.py index 46394a272..d3becc8b8 100644 --- a/migrations/versions/0096_update_job_stats.py +++ b/migrations/versions/0096_update_job_stats.py @@ -10,8 +10,8 @@ Create Date: 2017-06-08 15:46:49.637642 revision = "0096_update_job_stats" down_revision = "0095_migrate_existing_svc_perms" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0097_notnull_inbound_provider.py b/migrations/versions/0097_notnull_inbound_provider.py index 6f6e730e6..435fec232 100644 --- a/migrations/versions/0097_notnull_inbound_provider.py +++ b/migrations/versions/0097_notnull_inbound_provider.py @@ -10,8 +10,8 @@ Create Date: 2017-06-02 16:50:11.698423 revision = "0097_notnull_inbound_provider" down_revision = "0096_update_job_stats" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0098_service_inbound_api.py b/migrations/versions/0098_service_inbound_api.py index 24cf212fc..201cb405b 100644 --- a/migrations/versions/0098_service_inbound_api.py +++ b/migrations/versions/0098_service_inbound_api.py @@ -5,8 +5,8 @@ Revises: 0097_notnull_inbound_provider Create Date: 2017-06-13 15:02:33.609656 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0098_service_inbound_api" diff --git a/migrations/versions/0099_tfl_dar.py b/migrations/versions/0099_tfl_dar.py index 07408b1a8..6343bd7a8 100644 --- a/migrations/versions/0099_tfl_dar.py +++ b/migrations/versions/0099_tfl_dar.py @@ -12,8 +12,8 @@ from sqlalchemy import text revision = "0099_tfl_dar" down_revision = "0098_service_inbound_api" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql TFL_DAR_ID = "1d70f564-919b-4c68-8bdf-b8520d92516e" diff --git a/migrations/versions/0100_notification_created_by.py b/migrations/versions/0100_notification_created_by.py index 473e7063f..4a3c364ee 100644 --- a/migrations/versions/0100_notification_created_by.py +++ b/migrations/versions/0100_notification_created_by.py @@ -10,8 +10,8 @@ Create Date: 2017-06-13 10:53:25.032202 revision = "0100_notification_created_by" down_revision = "0099_tfl_dar" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0102_template_redacted.py b/migrations/versions/0102_template_redacted.py index dea3c8478..85320cc82 100644 --- a/migrations/versions/0102_template_redacted.py +++ b/migrations/versions/0102_template_redacted.py @@ -10,8 +10,8 @@ Create Date: 2017-06-27 15:37:28.878359 revision = "db6d9d9f06bc" down_revision = "0101_een_logo" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0103_add_historical_redact.py b/migrations/versions/0103_add_historical_redact.py index 7d72330da..47e91ca2d 100644 --- a/migrations/versions/0103_add_historical_redact.py +++ b/migrations/versions/0103_add_historical_redact.py @@ -12,10 +12,10 @@ from sqlalchemy import text revision = "0103_add_historical_redact" down_revision = "db6d9d9f06bc" -from alembic import op import sqlalchemy as sa -from sqlalchemy.dialects import postgresql +from alembic import op from flask import current_app +from sqlalchemy.dialects import postgresql def upgrade(): diff --git a/migrations/versions/0105_opg_letter_org.py b/migrations/versions/0105_opg_letter_org.py index e7abc9043..1b3a9f376 100644 --- a/migrations/versions/0105_opg_letter_org.py +++ b/migrations/versions/0105_opg_letter_org.py @@ -10,10 +10,10 @@ Create Date: 2017-06-29 12:44:16.815039 revision = "0105_opg_letter_org" down_revision = "0103_add_historical_redact" -from alembic import op import sqlalchemy as sa -from sqlalchemy.dialects import postgresql +from alembic import op from flask import current_app +from sqlalchemy.dialects import postgresql def upgrade(): diff --git a/migrations/versions/0107_drop_template_stats.py b/migrations/versions/0107_drop_template_stats.py index 8ed7b5d8d..8aeb6d866 100644 --- a/migrations/versions/0107_drop_template_stats.py +++ b/migrations/versions/0107_drop_template_stats.py @@ -10,8 +10,8 @@ Create Date: 2017-07-10 14:25:58.494636 revision = "0107_drop_template_stats" down_revision = "0106_null_noti_status" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0108_change_logo_not_nullable.py b/migrations/versions/0108_change_logo_not_nullable.py index f65a4737e..ad4e3a56e 100644 --- a/migrations/versions/0108_change_logo_not_nullable.py +++ b/migrations/versions/0108_change_logo_not_nullable.py @@ -10,8 +10,8 @@ Create Date: 2017-07-06 10:14:35.188404 revision = "0108_change_logo_not_nullable" down_revision = "0107_drop_template_stats" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0109_rem_old_noti_status.py b/migrations/versions/0109_rem_old_noti_status.py index 43f148548..ef29a4ec8 100644 --- a/migrations/versions/0109_rem_old_noti_status.py +++ b/migrations/versions/0109_rem_old_noti_status.py @@ -5,8 +5,8 @@ Revises: 0108_change_logo_not_nullable Create Date: 2017-07-10 14:25:15.712055 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0109_rem_old_noti_status" diff --git a/migrations/versions/0110_monthly_billing.py b/migrations/versions/0110_monthly_billing.py index 3a71cb0d0..e2f9380b1 100644 --- a/migrations/versions/0110_monthly_billing.py +++ b/migrations/versions/0110_monthly_billing.py @@ -10,8 +10,8 @@ Create Date: 2017-07-13 14:35:03.183659 revision = "0110_monthly_billing" down_revision = "0109_rem_old_noti_status" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0111_drop_old_service_flags.py b/migrations/versions/0111_drop_old_service_flags.py index 02ecc3af4..172b8ffb6 100644 --- a/migrations/versions/0111_drop_old_service_flags.py +++ b/migrations/versions/0111_drop_old_service_flags.py @@ -10,8 +10,8 @@ Create Date: 2017-07-12 13:35:45.636618 revision = "0111_drop_old_service_flags" down_revision = "0110_monthly_billing" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0112_add_start_end_dates.py b/migrations/versions/0112_add_start_end_dates.py index efeb3eeae..a26860b4a 100644 --- a/migrations/versions/0112_add_start_end_dates.py +++ b/migrations/versions/0112_add_start_end_dates.py @@ -6,8 +6,9 @@ Create Date: 2017-07-12 13:35:45.636618 """ from datetime import datetime -from alembic import op + import sqlalchemy as sa +from alembic import op from sqlalchemy import text from app.dao.date_util import get_month_start_and_end_date_in_utc diff --git a/migrations/versions/0113_job_created_by_nullable.py b/migrations/versions/0113_job_created_by_nullable.py index be039dd20..8b5c75150 100644 --- a/migrations/versions/0113_job_created_by_nullable.py +++ b/migrations/versions/0113_job_created_by_nullable.py @@ -10,8 +10,8 @@ Create Date: 2017-07-27 11:12:34.938086 revision = "0113_job_created_by_nullable" down_revision = "0112_add_start_end_dates" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0114_drop_monthly_billing_cols.py b/migrations/versions/0114_drop_monthly_billing_cols.py index e5ff11924..cdc88d90c 100644 --- a/migrations/versions/0114_drop_monthly_billing_cols.py +++ b/migrations/versions/0114_drop_monthly_billing_cols.py @@ -5,8 +5,8 @@ Revises: 0113_job_created_by_nullable Create Date: 2017-07-27 13:36:37.304344 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0014_drop_monthly_billing_cols" diff --git a/migrations/versions/0115_add_inbound_numbers.py b/migrations/versions/0115_add_inbound_numbers.py index 2088cd702..1bc726d28 100644 --- a/migrations/versions/0115_add_inbound_numbers.py +++ b/migrations/versions/0115_add_inbound_numbers.py @@ -10,8 +10,8 @@ Create Date: 2017-08-10 17:30:01.507694 revision = "0115_add_inbound_numbers" down_revision = "0014_drop_monthly_billing_cols" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0117_international_sms_notify.py b/migrations/versions/0117_international_sms_notify.py index 3e28618ff..ebdbbddef 100644 --- a/migrations/versions/0117_international_sms_notify.py +++ b/migrations/versions/0117_international_sms_notify.py @@ -12,9 +12,9 @@ from sqlalchemy import text revision = "0117_international_sms_notify" down_revision = "0115_add_inbound_numbers" -from alembic import op from datetime import datetime +from alembic import op NOTIFY_SERVICE_ID = "d6aa2c68-a2d9-4437-ab19-3ae8eb202553" diff --git a/migrations/versions/0118_service_sms_senders.py b/migrations/versions/0118_service_sms_senders.py index 51d58a65b..6dbca2ef6 100644 --- a/migrations/versions/0118_service_sms_senders.py +++ b/migrations/versions/0118_service_sms_senders.py @@ -10,8 +10,8 @@ Create Date: 2017-09-05 17:29:38.921045 revision = "0118_service_sms_senders" down_revision = "0117_international_sms_notify" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0119_add_email_reply_to.py b/migrations/versions/0119_add_email_reply_to.py index fbf6ec2fc..d2c035875 100644 --- a/migrations/versions/0119_add_email_reply_to.py +++ b/migrations/versions/0119_add_email_reply_to.py @@ -5,8 +5,8 @@ Revises: 0118_service_sms_senders Create Date: 2017-09-07 15:29:49.087143 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0119_add_email_reply_to" diff --git a/migrations/versions/0120_add_org_banner_branding.py b/migrations/versions/0120_add_org_banner_branding.py index 189e9ecfc..b7db6ae96 100644 --- a/migrations/versions/0120_add_org_banner_branding.py +++ b/migrations/versions/0120_add_org_banner_branding.py @@ -5,8 +5,8 @@ Revises: 0119_add_email_reply_to Create Date: 2017-09-18 14:18:49.087143 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0120_add_org_banner_branding" diff --git a/migrations/versions/0121_nullable_logos.py b/migrations/versions/0121_nullable_logos.py index d6c983e1e..930c9fe79 100644 --- a/migrations/versions/0121_nullable_logos.py +++ b/migrations/versions/0121_nullable_logos.py @@ -5,9 +5,8 @@ Revises: 0120_add_org_banner_branding Create Date: 2017-09-20 11:00:20.415523 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0121_nullable_logos" down_revision = "0120_add_org_banner_branding" diff --git a/migrations/versions/0122_add_service_letter_contact.py b/migrations/versions/0122_add_service_letter_contact.py index 91bd507ce..f743fe0f5 100644 --- a/migrations/versions/0122_add_service_letter_contact.py +++ b/migrations/versions/0122_add_service_letter_contact.py @@ -5,8 +5,8 @@ Revises: 0121_nullable_logos Create Date: 2017-09-21 12:16:02.975120 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0122_add_service_letter_contact" diff --git a/migrations/versions/0123_add_noti_to_email_reply.py b/migrations/versions/0123_add_noti_to_email_reply.py index 8949cf8c2..c8728d6d9 100644 --- a/migrations/versions/0123_add_noti_to_email_reply.py +++ b/migrations/versions/0123_add_noti_to_email_reply.py @@ -5,8 +5,8 @@ Revises: 0122_add_service_letter_contact Create Date: 2017-09-27 09:42:39.412731 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0123_add_noti_to_email_reply" diff --git a/migrations/versions/0124_add_free_sms_fragment_limit.py b/migrations/versions/0124_add_free_sms_fragment_limit.py index 178635bed..67e8f18a6 100644 --- a/migrations/versions/0124_add_free_sms_fragment_limit.py +++ b/migrations/versions/0124_add_free_sms_fragment_limit.py @@ -5,9 +5,8 @@ Revises: 0123_add_noti_to_email_reply Create Date: 2017-10-10 11:30:16.225980 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0124_add_free_sms_fragment_limit" down_revision = "0123_add_noti_to_email_reply" diff --git a/migrations/versions/0125_add_organisation_type.py b/migrations/versions/0125_add_organisation_type.py index 53bb519fb..de9451a77 100644 --- a/migrations/versions/0125_add_organisation_type.py +++ b/migrations/versions/0125_add_organisation_type.py @@ -5,9 +5,8 @@ Revises: 0124_add_free_sms_fragment_limit Create Date: 2017-10-05 14:03:00.248005 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0125_add_organisation_type" down_revision = "0124_add_free_sms_fragment_limit" diff --git a/migrations/versions/0126_add_annual_billing.py b/migrations/versions/0126_add_annual_billing.py index b8324a852..a734981c0 100644 --- a/migrations/versions/0126_add_annual_billing.py +++ b/migrations/versions/0126_add_annual_billing.py @@ -5,8 +5,8 @@ Revises: 0125_add_organisation_type Create Date: 2017-10-19 11:38:32.849573 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0126_add_annual_billing" diff --git a/migrations/versions/0127_remove_unique_constraint.py b/migrations/versions/0127_remove_unique_constraint.py index 52e429c82..7da79cfae 100644 --- a/migrations/versions/0127_remove_unique_constraint.py +++ b/migrations/versions/0127_remove_unique_constraint.py @@ -5,8 +5,8 @@ Revises: 0126_add_annual_billing Create Date: 2017-10-17 16:47:37.826333 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0127_remove_unique_constraint" down_revision = "0126_add_annual_billing" diff --git a/migrations/versions/0128_noti_to_sms_sender.py b/migrations/versions/0128_noti_to_sms_sender.py index 5ce81c725..f191cde49 100644 --- a/migrations/versions/0128_noti_to_sms_sender.py +++ b/migrations/versions/0128_noti_to_sms_sender.py @@ -5,8 +5,8 @@ Revises: 0127_remove_unique_constraint Create Date: 2017-10-26 15:17:00.752706 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0128_noti_to_sms_sender" diff --git a/migrations/versions/0129_add_email_auth_permission_.py b/migrations/versions/0129_add_email_auth_permission_.py index 1f3387e18..927121d26 100644 --- a/migrations/versions/0129_add_email_auth_permission_.py +++ b/migrations/versions/0129_add_email_auth_permission_.py @@ -7,7 +7,6 @@ Create Date: 2017-10-26 14:33:41.336861 """ from alembic import op - revision = "0129_add_email_auth_permission" down_revision = "0128_noti_to_sms_sender" diff --git a/migrations/versions/0130_service_email_reply_to_row.py b/migrations/versions/0130_service_email_reply_to_row.py index afa0fbc9f..f1e3720d1 100644 --- a/migrations/versions/0130_service_email_reply_to_row.py +++ b/migrations/versions/0130_service_email_reply_to_row.py @@ -14,7 +14,6 @@ down_revision = "0129_add_email_auth_permission" from alembic import op - NOTIFY_SERVICE_ID = "d6aa2c68-a2d9-4437-ab19-3ae8eb202553" EMAIL_REPLY_TO_ID = "b3a58d57-2337-662a-4cba-40792a9322f2" diff --git a/migrations/versions/0131_user_auth_types.py b/migrations/versions/0131_user_auth_types.py index 861fa5569..d35eddaae 100644 --- a/migrations/versions/0131_user_auth_types.py +++ b/migrations/versions/0131_user_auth_types.py @@ -5,9 +5,8 @@ Revises: 0130_service_email_reply_to_row Create Date: 2017-10-27 16:19:51.458863 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0131_user_auth_types" down_revision = "0130_service_email_reply_to_row" diff --git a/migrations/versions/0132_add_sms_prefix_setting.py b/migrations/versions/0132_add_sms_prefix_setting.py index 09455db31..fb404ae9e 100644 --- a/migrations/versions/0132_add_sms_prefix_setting.py +++ b/migrations/versions/0132_add_sms_prefix_setting.py @@ -5,8 +5,8 @@ Revises: 0131_user_auth_types Create Date: 2017-11-03 11:07:40.537006 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0132_add_sms_prefix_setting" diff --git a/migrations/versions/0133_set_services_sms_prefix.py b/migrations/versions/0133_set_services_sms_prefix.py index a4aa64c69..752edf297 100644 --- a/migrations/versions/0133_set_services_sms_prefix.py +++ b/migrations/versions/0133_set_services_sms_prefix.py @@ -11,8 +11,8 @@ Revises: 0132_add_sms_prefix_setting Create Date: 2017-11-03 15:55:35.657488 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0133_set_services_sms_prefix" diff --git a/migrations/versions/0135_stats_template_usage.py b/migrations/versions/0135_stats_template_usage.py index 7145993ce..e628c9f6f 100644 --- a/migrations/versions/0135_stats_template_usage.py +++ b/migrations/versions/0135_stats_template_usage.py @@ -5,8 +5,8 @@ Revises: 0134_add_email_2fa_template Create Date: 2017-11-07 14:35:04.798561 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0135_stats_template_usage" diff --git a/migrations/versions/0136_user_mobile_nullable.py b/migrations/versions/0136_user_mobile_nullable.py index 77e4d2a99..94fc0c16f 100644 --- a/migrations/versions/0136_user_mobile_nullable.py +++ b/migrations/versions/0136_user_mobile_nullable.py @@ -5,10 +5,10 @@ Revises: 0135_stats_template_usage Create Date: 2017-11-08 11:49:05.773974 """ -from alembic import op import sqlalchemy as sa -from sqlalchemy.sql import column +from alembic import op from sqlalchemy.dialects import postgresql +from sqlalchemy.sql import column revision = "0136_user_mobile_nullable" down_revision = "0135_stats_template_usage" diff --git a/migrations/versions/0138_sms_sender_nullable.py b/migrations/versions/0138_sms_sender_nullable.py index aae95c0ac..76bb50e32 100644 --- a/migrations/versions/0138_sms_sender_nullable.py +++ b/migrations/versions/0138_sms_sender_nullable.py @@ -5,8 +5,8 @@ Revises: 0137_notification_template_hist Create Date: 2017-11-06 15:44:59.471977 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0138_sms_sender_nullable" diff --git a/migrations/versions/0139_migrate_sms_allowance_data.py b/migrations/versions/0139_migrate_sms_allowance_data.py index 926dddb81..f7f45605e 100644 --- a/migrations/versions/0139_migrate_sms_allowance_data.py +++ b/migrations/versions/0139_migrate_sms_allowance_data.py @@ -5,15 +5,14 @@ Revises: 0138_sms_sender_nullable.py Create Date: 2017-11-10 21:42:59.715203 """ -from datetime import datetime -from alembic import op import uuid +from datetime import datetime +from alembic import op from sqlalchemy import text from app.dao.date_util import get_current_calendar_year_start_year - revision = "0139_migrate_sms_allowance_data" down_revision = "0138_sms_sender_nullable" diff --git a/migrations/versions/0140_sms_prefix_non_nullable.py b/migrations/versions/0140_sms_prefix_non_nullable.py index 98d1cc3cb..8f66d3429 100644 --- a/migrations/versions/0140_sms_prefix_non_nullable.py +++ b/migrations/versions/0140_sms_prefix_non_nullable.py @@ -5,9 +5,9 @@ Revises: 0139_migrate_sms_allowance_data Create Date: 2017-11-07 13:04:04.077142 """ +import sqlalchemy as sa from alembic import op from flask import current_app -import sqlalchemy as sa from sqlalchemy import text from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0141_remove_unused.py b/migrations/versions/0141_remove_unused.py index 75086cf7a..a811e4c4d 100644 --- a/migrations/versions/0141_remove_unused.py +++ b/migrations/versions/0141_remove_unused.py @@ -5,8 +5,8 @@ Revises: 0140_sms_prefix_non_nullable Create Date: 2017-11-20 11:35:24.402021 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0141_remove_unused" diff --git a/migrations/versions/0143_remove_reply_to.py b/migrations/versions/0143_remove_reply_to.py index 0f087b095..ad8544601 100644 --- a/migrations/versions/0143_remove_reply_to.py +++ b/migrations/versions/0143_remove_reply_to.py @@ -5,9 +5,8 @@ Revises: 0142_validate_constraint Create Date: 2017-11-21 10:42:25.045444 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0143_remove_reply_to" down_revision = "0142_validate_constraint" diff --git a/migrations/versions/0144_template_service_letter.py b/migrations/versions/0144_template_service_letter.py index 7c158aa31..7fc5f546a 100644 --- a/migrations/versions/0144_template_service_letter.py +++ b/migrations/versions/0144_template_service_letter.py @@ -5,8 +5,8 @@ Revises: 0143_remove_reply_to Create Date: 2017-11-17 15:42:16.401229 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0144_template_service_letter" diff --git a/migrations/versions/0145_add_notification_reply_to.py b/migrations/versions/0145_add_notification_reply_to.py index 25cff51c1..ade8d5959 100644 --- a/migrations/versions/0145_add_notification_reply_to.py +++ b/migrations/versions/0145_add_notification_reply_to.py @@ -5,9 +5,8 @@ Revises: 0144_template_service_letter Create Date: 2017-11-22 14:23:48.806781 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0145_add_notification_reply_to" down_revision = "0144_template_service_letter" diff --git a/migrations/versions/0146_add_service_callback_api.py b/migrations/versions/0146_add_service_callback_api.py index c7dc4489c..8ad061353 100644 --- a/migrations/versions/0146_add_service_callback_api.py +++ b/migrations/versions/0146_add_service_callback_api.py @@ -5,8 +5,8 @@ Revises: 0145_add_notification_reply_to Create Date: 2017-11-28 15:13:48.730554 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0146_add_service_callback_api" diff --git a/migrations/versions/0147_drop_mapping_tables.py b/migrations/versions/0147_drop_mapping_tables.py index 9b018ddce..a77fbbe73 100644 --- a/migrations/versions/0147_drop_mapping_tables.py +++ b/migrations/versions/0147_drop_mapping_tables.py @@ -5,8 +5,8 @@ Revises: 0146_add_service_callback_api Create Date: 2017-11-30 15:48:44.588438 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0147_drop_mapping_tables" diff --git a/migrations/versions/0149_add_crown_to_services.py b/migrations/versions/0149_add_crown_to_services.py index f2779ee9e..106960472 100644 --- a/migrations/versions/0149_add_crown_to_services.py +++ b/migrations/versions/0149_add_crown_to_services.py @@ -5,9 +5,8 @@ Revises: 0148_add_letters_as_pdf_svc_perm Create Date: 2017-12-04 12:13:35.268712 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0149_add_crown_to_services" down_revision = "0148_add_letters_as_pdf_svc_perm" diff --git a/migrations/versions/0152_kill_service_free_fragments.py b/migrations/versions/0152_kill_service_free_fragments.py index 0cda884cf..518b3ab42 100644 --- a/migrations/versions/0152_kill_service_free_fragments.py +++ b/migrations/versions/0152_kill_service_free_fragments.py @@ -5,8 +5,8 @@ Revises: 0149_add_crown_to_services Create Date: 2017-12-01 16:49:51.178455 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0152_kill_service_free_fragments" down_revision = "0149_add_crown_to_services" diff --git a/migrations/versions/0156_set_temp_letter_contact.py b/migrations/versions/0156_set_temp_letter_contact.py index a0ae3fd65..3044ce6be 100644 --- a/migrations/versions/0156_set_temp_letter_contact.py +++ b/migrations/versions/0156_set_temp_letter_contact.py @@ -7,7 +7,6 @@ Create Date: 2018-01-05 17:04:20.596271 """ from alembic import op - revision = "0156_set_temp_letter_contact" down_revision = "0152_kill_service_free_fragments" diff --git a/migrations/versions/0157_add_rate_limit_to_service.py b/migrations/versions/0157_add_rate_limit_to_service.py index 692a704f7..6a3aa7196 100644 --- a/migrations/versions/0157_add_rate_limit_to_service.py +++ b/migrations/versions/0157_add_rate_limit_to_service.py @@ -5,9 +5,8 @@ Revises: 0156_set_temp_letter_contact Create Date: 2018-01-08 16:13:25.733336 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0157_add_rate_limit_to_service" down_revision = "0156_set_temp_letter_contact" diff --git a/migrations/versions/0158_remove_rate_limit_default.py b/migrations/versions/0158_remove_rate_limit_default.py index fe569e117..6adeb529c 100644 --- a/migrations/versions/0158_remove_rate_limit_default.py +++ b/migrations/versions/0158_remove_rate_limit_default.py @@ -5,9 +5,8 @@ Revises: 0157_add_rate_limit_to_service Create Date: 2018-01-09 14:33:08.313893 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0158_remove_rate_limit_default" down_revision = "0157_add_rate_limit_to_service" diff --git a/migrations/versions/0159_add_historical_redact.py b/migrations/versions/0159_add_historical_redact.py index 8a66a36d2..e449b1eaf 100644 --- a/migrations/versions/0159_add_historical_redact.py +++ b/migrations/versions/0159_add_historical_redact.py @@ -12,10 +12,10 @@ from sqlalchemy import text revision = "0159_add_historical_redact" down_revision = "0158_remove_rate_limit_default" -from alembic import op import sqlalchemy as sa -from sqlalchemy.dialects import postgresql +from alembic import op from flask import current_app +from sqlalchemy.dialects import postgresql def upgrade(): diff --git a/migrations/versions/0161_email_branding.py b/migrations/versions/0161_email_branding.py index 78d6ccf8e..41a04208e 100644 --- a/migrations/versions/0161_email_branding.py +++ b/migrations/versions/0161_email_branding.py @@ -5,8 +5,8 @@ Revises: 0159_add_historical_redact Create Date: 2018-01-30 15:35:12.016574 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0161_email_branding" diff --git a/migrations/versions/0162_remove_org.py b/migrations/versions/0162_remove_org.py index 6c75959aa..a4f5795a4 100644 --- a/migrations/versions/0162_remove_org.py +++ b/migrations/versions/0162_remove_org.py @@ -5,8 +5,8 @@ Revises: 0161_email_branding Create Date: 2018-02-06 17:08:11.879844 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0162_remove_org" diff --git a/migrations/versions/0163_add_new_org_model.py b/migrations/versions/0163_add_new_org_model.py index 14a3ea314..7b5cb50e7 100644 --- a/migrations/versions/0163_add_new_org_model.py +++ b/migrations/versions/0163_add_new_org_model.py @@ -5,8 +5,8 @@ Revises: 0162_remove_org Create Date: 2018-02-07 14:03:00.804849 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0163_add_new_org_model" diff --git a/migrations/versions/0164_add_organisation_to_service.py b/migrations/versions/0164_add_organisation_to_service.py index 6e55f8f06..156864a60 100644 --- a/migrations/versions/0164_add_organisation_to_service.py +++ b/migrations/versions/0164_add_organisation_to_service.py @@ -5,8 +5,8 @@ Revises: 0163_add_new_org_model Create Date: 2018-02-09 17:58:34.617206 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0164_add_organisation_to_service" diff --git a/migrations/versions/0166_add_org_user_stuff.py b/migrations/versions/0166_add_org_user_stuff.py index 395706612..4109dd7c7 100644 --- a/migrations/versions/0166_add_org_user_stuff.py +++ b/migrations/versions/0166_add_org_user_stuff.py @@ -5,8 +5,8 @@ Revises: 0164_add_organisation_to_service Create Date: 2018-02-14 17:25:11.747996 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0166_add_org_user_stuff" diff --git a/migrations/versions/0168_hidden_templates.py b/migrations/versions/0168_hidden_templates.py index bbf5063e2..0c3efb1eb 100644 --- a/migrations/versions/0168_hidden_templates.py +++ b/migrations/versions/0168_hidden_templates.py @@ -5,9 +5,8 @@ Revises: 0167_add_precomp_letter_svc_perm Create Date: 2018-02-21 14:05:04.448977 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0168_hidden_templates" down_revision = "0167_add_precomp_letter_svc_perm" diff --git a/migrations/versions/0169_hidden_templates_nullable.py b/migrations/versions/0169_hidden_templates_nullable.py index 9ffc6d72f..76bde31c6 100644 --- a/migrations/versions/0169_hidden_templates_nullable.py +++ b/migrations/versions/0169_hidden_templates_nullable.py @@ -7,7 +7,6 @@ Create Date: 2018-02-21 14:05:04.448977 """ from alembic import op - revision = "0169_hidden_templates_nullable" down_revision = "0168_hidden_templates" diff --git a/migrations/versions/0170_hidden_non_nullable.py b/migrations/versions/0170_hidden_non_nullable.py index 2bae3dd68..8101bf703 100644 --- a/migrations/versions/0170_hidden_non_nullable.py +++ b/migrations/versions/0170_hidden_non_nullable.py @@ -7,7 +7,6 @@ Create Date: 2018-02-21 14:05:04.448977 """ from alembic import op - revision = "0170_hidden_non_nullable" down_revision = "0169_hidden_templates_nullable" diff --git a/migrations/versions/0172_deprioritise_examples.py b/migrations/versions/0172_deprioritise_examples.py index 5620c5fb0..f5c3f291d 100644 --- a/migrations/versions/0172_deprioritise_examples.py +++ b/migrations/versions/0172_deprioritise_examples.py @@ -5,10 +5,10 @@ Revises: 0171_add_org_invite_template Create Date: 2018-02-28 17:09:56.619803 """ -from alembic import op -from app.models import NORMAL import sqlalchemy as sa +from alembic import op +from app.models import NORMAL revision = "0172_deprioritise_examples" down_revision = "0171_add_org_invite_template" diff --git a/migrations/versions/0173_create_daily_sorted_letter.py b/migrations/versions/0173_create_daily_sorted_letter.py index 0fb3686f7..7e6c1b336 100644 --- a/migrations/versions/0173_create_daily_sorted_letter.py +++ b/migrations/versions/0173_create_daily_sorted_letter.py @@ -5,8 +5,8 @@ Revises: 0172_deprioritise_examples Create Date: 2018-03-01 11:53:32.964256 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0173_create_daily_sorted_letter" diff --git a/migrations/versions/0174_add_billing_facts.py b/migrations/versions/0174_add_billing_facts.py index 74b7b5a90..5cf66f7f9 100644 --- a/migrations/versions/0174_add_billing_facts.py +++ b/migrations/versions/0174_add_billing_facts.py @@ -5,8 +5,8 @@ Revises: 0173_create_daily_sorted_letter Create Date: 2018-03-07 12:21:53.098887 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0174_add_billing_facts" diff --git a/migrations/versions/0175_drop_job_statistics_table.py b/migrations/versions/0175_drop_job_statistics_table.py index 287a24506..2d309a3c1 100644 --- a/migrations/versions/0175_drop_job_statistics_table.py +++ b/migrations/versions/0175_drop_job_statistics_table.py @@ -5,8 +5,8 @@ Revises: 0174_add_billing_facts Create Date: 2018-03-12 10:27:09.050837 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0175_drop_job_statistics_table" diff --git a/migrations/versions/0176_alter_billing_columns.py b/migrations/versions/0176_alter_billing_columns.py index 731facb48..c5a5aa942 100644 --- a/migrations/versions/0176_alter_billing_columns.py +++ b/migrations/versions/0176_alter_billing_columns.py @@ -5,8 +5,8 @@ Revises: 0175_drop_job_statistics_table Create Date: 2018-03-12 16:54:30.663897 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0176_alter_billing_columns" down_revision = "0175_drop_job_statistics_table" diff --git a/migrations/versions/0177_add_virus_scan_statuses.py b/migrations/versions/0177_add_virus_scan_statuses.py index 2ba512c88..67aac5296 100644 --- a/migrations/versions/0177_add_virus_scan_statuses.py +++ b/migrations/versions/0177_add_virus_scan_statuses.py @@ -7,7 +7,6 @@ Create Date: 2018-02-21 14:05:04.448977 """ from alembic import op - revision = "0177_add_virus_scan_statuses" down_revision = "0176_alter_billing_columns" diff --git a/migrations/versions/0178_add_filename.py b/migrations/versions/0178_add_filename.py index ed4e9bcc3..571f7cc39 100644 --- a/migrations/versions/0178_add_filename.py +++ b/migrations/versions/0178_add_filename.py @@ -5,9 +5,8 @@ Revises: 0177_add_virus_scan_statuses Create Date: 2018-03-14 16:15:01.886998 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0178_add_filename" down_revision = "0177_add_virus_scan_statuses" diff --git a/migrations/versions/0179_billing_primary_const.py b/migrations/versions/0179_billing_primary_const.py index c812938dc..36e741a2c 100644 --- a/migrations/versions/0179_billing_primary_const.py +++ b/migrations/versions/0179_billing_primary_const.py @@ -5,8 +5,8 @@ Revises: 0178_add_filename Create Date: 2018-03-13 14:52:40.413474 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0179_billing_primary_const" diff --git a/migrations/versions/0181_billing_primary_key.py b/migrations/versions/0181_billing_primary_key.py index 697f4ad3f..4c7dda841 100644 --- a/migrations/versions/0181_billing_primary_key.py +++ b/migrations/versions/0181_billing_primary_key.py @@ -5,8 +5,8 @@ Revises: 0179_billing_primary_const Create Date: 2018-03-21 13:41:26.203712 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0181_billing_primary_key" diff --git a/migrations/versions/0183_alter_primary_key.py b/migrations/versions/0183_alter_primary_key.py index ea7072f78..ba9d172da 100644 --- a/migrations/versions/0183_alter_primary_key.py +++ b/migrations/versions/0183_alter_primary_key.py @@ -5,8 +5,8 @@ Revises: 0182_add_upload_document_perm Create Date: 2018-03-25 21:23:32.403212 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0183_alter_primary_key" diff --git a/migrations/versions/0184_alter_primary_key_1.py b/migrations/versions/0184_alter_primary_key_1.py index d2eab2e37..531cff747 100644 --- a/migrations/versions/0184_alter_primary_key_1.py +++ b/migrations/versions/0184_alter_primary_key_1.py @@ -5,8 +5,8 @@ Revises: 0183_alter_primary_key Create Date: 2018-03-28 16:05:54.648645 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0184_alter_primary_key_1" diff --git a/migrations/versions/0185_add_is_active_to_reply_tos.py b/migrations/versions/0185_add_is_active_to_reply_tos.py index 0c0cc7a79..d1f046d3d 100644 --- a/migrations/versions/0185_add_is_active_to_reply_tos.py +++ b/migrations/versions/0185_add_is_active_to_reply_tos.py @@ -5,9 +5,8 @@ Revises: 0184_alter_primary_key_1 Create Date: 2018-04-10 16:35:41.824981 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0185_add_is_active_to_reply_tos" down_revision = "0184_alter_primary_key_1" diff --git a/migrations/versions/0186_rename_is_active_columns.py b/migrations/versions/0186_rename_is_active_columns.py index 70d96ef6a..7c33544b0 100644 --- a/migrations/versions/0186_rename_is_active_columns.py +++ b/migrations/versions/0186_rename_is_active_columns.py @@ -5,9 +5,8 @@ Revises: 0185_add_is_active_to_reply_tos Create Date: 2018-04-27 16:35:41.824981 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0186_rename_is_active_columns" down_revision = "0185_add_is_active_to_reply_tos" diff --git a/migrations/versions/0188_add_ft_notification_status.py b/migrations/versions/0188_add_ft_notification_status.py index a7978a01f..04f58cc71 100644 --- a/migrations/versions/0188_add_ft_notification_status.py +++ b/migrations/versions/0188_add_ft_notification_status.py @@ -5,8 +5,8 @@ Revises: 0186_rename_is_active_columns Create Date: 2018-05-03 10:10:41.824981 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0188_add_ft_notification_status" diff --git a/migrations/versions/0189_ft_billing_data_type.py b/migrations/versions/0189_ft_billing_data_type.py index dace98a72..e374d1991 100644 --- a/migrations/versions/0189_ft_billing_data_type.py +++ b/migrations/versions/0189_ft_billing_data_type.py @@ -5,8 +5,8 @@ Revises: 0188_add_ft_notification_status Create Date: 2018-05-10 14:57:52.589773 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0189_ft_billing_data_type" down_revision = "0188_add_ft_notification_status" diff --git a/migrations/versions/0192_drop_provider_statistics.py b/migrations/versions/0192_drop_provider_statistics.py index 8cb018e67..513d2be18 100644 --- a/migrations/versions/0192_drop_provider_statistics.py +++ b/migrations/versions/0192_drop_provider_statistics.py @@ -5,8 +5,8 @@ Revises: 0191_ft_billing_pkey Create Date: 2018-05-21 15:18:43.871256 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0192_drop_provider_statistics" diff --git a/migrations/versions/0193_add_ft_billing_timestamps.py b/migrations/versions/0193_add_ft_billing_timestamps.py index 48a7e4ccc..c688ed089 100644 --- a/migrations/versions/0193_add_ft_billing_timestamps.py +++ b/migrations/versions/0193_add_ft_billing_timestamps.py @@ -5,9 +5,8 @@ Revises: 0192_drop_provider_statistics Create Date: 2018-05-22 10:23:21.937262 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0193_add_ft_billing_timestamps" down_revision = "0192_drop_provider_statistics" diff --git a/migrations/versions/0194_ft_billing_created_at.py b/migrations/versions/0194_ft_billing_created_at.py index 52b251395..8bc4d007f 100644 --- a/migrations/versions/0194_ft_billing_created_at.py +++ b/migrations/versions/0194_ft_billing_created_at.py @@ -5,8 +5,8 @@ Revises: 0193_add_ft_billing_timestamps Create Date: 2018-05-22 14:34:27.852096 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0194_ft_billing_created_at" diff --git a/migrations/versions/0195_ft_notification_timestamps.py b/migrations/versions/0195_ft_notification_timestamps.py index 2c35d8c5e..fd8dbf3d2 100644 --- a/migrations/versions/0195_ft_notification_timestamps.py +++ b/migrations/versions/0195_ft_notification_timestamps.py @@ -5,9 +5,8 @@ Revises: 0194_ft_billing_created_at Create Date: 2018-05-22 16:01:53.269137 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0195_ft_notification_timestamps" down_revision = "0194_ft_billing_created_at" diff --git a/migrations/versions/0196_complaints_table_.py b/migrations/versions/0196_complaints_table_.py index df6095d80..a52cf938c 100644 --- a/migrations/versions/0196_complaints_table_.py +++ b/migrations/versions/0196_complaints_table_.py @@ -5,8 +5,8 @@ Revises: 0195_ft_notification_timestamps Create Date: 2018-05-31 14:31:36.649544 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0196_complaints_table" diff --git a/migrations/versions/0197_service_contact_link.py b/migrations/versions/0197_service_contact_link.py index 076153933..fd7dbeb32 100644 --- a/migrations/versions/0197_service_contact_link.py +++ b/migrations/versions/0197_service_contact_link.py @@ -5,9 +5,8 @@ Revises: 0196_complaints_table Create Date: 2018-05-31 15:01:32.977620 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0197_service_contact_link" down_revision = "0196_complaints_table" diff --git a/migrations/versions/0204_service_data_retention.py b/migrations/versions/0204_service_data_retention.py index 7afc59d69..8eb3b220f 100644 --- a/migrations/versions/0204_service_data_retention.py +++ b/migrations/versions/0204_service_data_retention.py @@ -5,8 +5,8 @@ Revises: 0203_fix_old_incomplete_jobs Create Date: 2018-07-10 11:22:01.761829 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0204_service_data_retention" diff --git a/migrations/versions/0205_service_callback_type.py b/migrations/versions/0205_service_callback_type.py index b2dcebce0..624d70c1e 100644 --- a/migrations/versions/0205_service_callback_type.py +++ b/migrations/versions/0205_service_callback_type.py @@ -5,9 +5,8 @@ Revises: 0204_service_data_retention Create Date: 2018-07-17 15:51:10.776698 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0205_service_callback_type" down_revision = "0204_service_data_retention" diff --git a/migrations/versions/0206_assign_callback_type.py b/migrations/versions/0206_assign_callback_type.py index bb77e609a..20b15ac44 100644 --- a/migrations/versions/0206_assign_callback_type.py +++ b/migrations/versions/0206_assign_callback_type.py @@ -5,9 +5,8 @@ Revises: 0205_service_callback_type Create Date: 2018-07-18 10:43:43.864835 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0206_assign_callback_type" down_revision = "0205_service_callback_type" diff --git a/migrations/versions/0207_set_callback_history_type.py b/migrations/versions/0207_set_callback_history_type.py index 49c2a4fca..1aab1ce27 100644 --- a/migrations/versions/0207_set_callback_history_type.py +++ b/migrations/versions/0207_set_callback_history_type.py @@ -5,9 +5,8 @@ Revises: 0206_assign_callback_type Create Date: 2018-07-18 10:43:43.864835 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0207_set_callback_history_type" down_revision = "0206_assign_callback_type" diff --git a/migrations/versions/0210_remove_monthly_billing.py b/migrations/versions/0210_remove_monthly_billing.py index d8a5dc7ae..3dfb7a7ef 100644 --- a/migrations/versions/0210_remove_monthly_billing.py +++ b/migrations/versions/0210_remove_monthly_billing.py @@ -5,8 +5,8 @@ Revises: 0209_add_cancelled_status Create Date: 2018-07-31 16:43:00.568972 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0210_remove_monthly_billing" diff --git a/migrations/versions/0211_email_branding_update_.py b/migrations/versions/0211_email_branding_update_.py index 901f833b0..dedc2dfad 100644 --- a/migrations/versions/0211_email_branding_update_.py +++ b/migrations/versions/0211_email_branding_update_.py @@ -5,9 +5,8 @@ Revises: 0210_remove_monthly_billing Create Date: 2018-07-31 18:00:20.457755 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0211_email_branding_update" down_revision = "0210_remove_monthly_billing" diff --git a/migrations/versions/0212_remove_caseworking.py b/migrations/versions/0212_remove_caseworking.py index c64bb078b..89f382188 100644 --- a/migrations/versions/0212_remove_caseworking.py +++ b/migrations/versions/0212_remove_caseworking.py @@ -7,7 +7,6 @@ Create Date: 2018-07-31 18:00:20.457755 """ from alembic import op - revision = "0212_remove_caseworking" down_revision = "0211_email_branding_update" diff --git a/migrations/versions/0213_brand_colour_domain_.py b/migrations/versions/0213_brand_colour_domain_.py index 06144f205..b966f8f8a 100644 --- a/migrations/versions/0213_brand_colour_domain_.py +++ b/migrations/versions/0213_brand_colour_domain_.py @@ -5,8 +5,8 @@ Revises: 0212_remove_caseworking Create Date: 2018-08-16 16:29:41.374944 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0213_brand_colour_domain" down_revision = "0212_remove_caseworking" diff --git a/migrations/versions/0215_email_brand_type.py b/migrations/versions/0215_email_brand_type.py index 79bf785d6..2b162eae4 100644 --- a/migrations/versions/0215_email_brand_type.py +++ b/migrations/versions/0215_email_brand_type.py @@ -5,8 +5,8 @@ Revises: 0213_brand_colour_domain_ Create Date: 2018-08-23 11:48:00.800968 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0215_email_brand_type" down_revision = "0213_brand_colour_domain" diff --git a/migrations/versions/0216_remove_colours.py b/migrations/versions/0216_remove_colours.py index 30805bd51..f0e46de89 100644 --- a/migrations/versions/0216_remove_colours.py +++ b/migrations/versions/0216_remove_colours.py @@ -3,8 +3,8 @@ Revises: 0215_email_brand_type Create Date: 2018-08-24 13:36:49.346156 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0216_remove_colours" down_revision = "0215_email_brand_type" diff --git a/migrations/versions/0221_nullable_service_branding.py b/migrations/versions/0221_nullable_service_branding.py index cf626f8eb..7649d4a36 100644 --- a/migrations/versions/0221_nullable_service_branding.py +++ b/migrations/versions/0221_nullable_service_branding.py @@ -5,7 +5,6 @@ Create Date: 2018-08-24 13:36:49.346156 """ from alembic import op - revision = "0221_nullable_service_branding" down_revision = "0220_email_brand_type_non_null" diff --git a/migrations/versions/0222_drop_service_branding.py b/migrations/versions/0222_drop_service_branding.py index b32f8663f..ae08e2c0b 100644 --- a/migrations/versions/0222_drop_service_branding.py +++ b/migrations/versions/0222_drop_service_branding.py @@ -3,9 +3,8 @@ Revises: 0221_nullable_service_branding Create Date: 2018-08-24 13:36:49.346156 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0222_drop_service_branding" down_revision = "0221_nullable_service_branding" diff --git a/migrations/versions/0223_add_domain_constraint.py b/migrations/versions/0223_add_domain_constraint.py index 3f99ac3d9..b7f2e1e8f 100644 --- a/migrations/versions/0223_add_domain_constraint.py +++ b/migrations/versions/0223_add_domain_constraint.py @@ -5,7 +5,6 @@ Create Date: 2018-08-24 13:36:49.346156 """ from alembic import op - revision = "0223_add_domain_constraint" down_revision = "0222_drop_service_branding" diff --git a/migrations/versions/0224_returned_letter_status.py b/migrations/versions/0224_returned_letter_status.py index bc56e9ef5..c25ff53b3 100644 --- a/migrations/versions/0224_returned_letter_status.py +++ b/migrations/versions/0224_returned_letter_status.py @@ -7,7 +7,6 @@ Create Date: 2018-08-21 14:44:04.203480 """ from alembic import op - revision = "0224_returned_letter_status" down_revision = "0223_add_domain_constraint" diff --git a/migrations/versions/0226_service_postage.py b/migrations/versions/0226_service_postage.py index 01219337f..28d0fa2c1 100644 --- a/migrations/versions/0226_service_postage.py +++ b/migrations/versions/0226_service_postage.py @@ -5,9 +5,8 @@ Revises: 0224_returned_letter_status Create Date: 2018-09-13 16:23:59.168877 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0226_service_postage" down_revision = "0224_returned_letter_status" diff --git a/migrations/versions/0227_postage_constraints.py b/migrations/versions/0227_postage_constraints.py index 804cf303b..7c07e520a 100644 --- a/migrations/versions/0227_postage_constraints.py +++ b/migrations/versions/0227_postage_constraints.py @@ -3,8 +3,8 @@ Revision ID: 0227_postage_constraints Revises: 0226_service_postage Create Date: 2018-09-13 16:23:59.168877 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0227_postage_constraints" down_revision = "0226_service_postage" diff --git a/migrations/versions/0228_notification_postage.py b/migrations/versions/0228_notification_postage.py index 503642719..98353031e 100644 --- a/migrations/versions/0228_notification_postage.py +++ b/migrations/versions/0228_notification_postage.py @@ -5,9 +5,8 @@ Revises: 0227_postage_constraints Create Date: 2018-09-19 11:42:52.229430 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0228_notification_postage" down_revision = "0227_postage_constraints" diff --git a/migrations/versions/0230_noti_postage_constraint_1.py b/migrations/versions/0230_noti_postage_constraint_1.py index 9bbd39577..9830155c5 100644 --- a/migrations/versions/0230_noti_postage_constraint_1.py +++ b/migrations/versions/0230_noti_postage_constraint_1.py @@ -7,7 +7,6 @@ Create Date: 2018-09-19 11:42:52.229430 """ from alembic import op - revision = "0230_noti_postage_constraint_1" down_revision = "0228_notification_postage" diff --git a/migrations/versions/0231_noti_postage_constraint_2.py b/migrations/versions/0231_noti_postage_constraint_2.py index 93220549c..322212af5 100644 --- a/migrations/versions/0231_noti_postage_constraint_2.py +++ b/migrations/versions/0231_noti_postage_constraint_2.py @@ -7,7 +7,6 @@ Create Date: 2018-09-19 11:42:52.229430 """ from alembic import op - revision = "0230_noti_postage_constraint_2" down_revision = "0230_noti_postage_constraint_1" diff --git a/migrations/versions/0232_noti_postage_constraint_3.py b/migrations/versions/0232_noti_postage_constraint_3.py index 811001668..69f0b2ac2 100644 --- a/migrations/versions/0232_noti_postage_constraint_3.py +++ b/migrations/versions/0232_noti_postage_constraint_3.py @@ -7,7 +7,6 @@ Create Date: 2018-09-19 11:42:52.229430 """ from alembic import op - revision = "0232_noti_postage_constraint_3" down_revision = "0230_noti_postage_constraint_2" diff --git a/migrations/versions/0234_ft_billing_postage.py b/migrations/versions/0234_ft_billing_postage.py index 8ba873883..77f2eee75 100644 --- a/migrations/versions/0234_ft_billing_postage.py +++ b/migrations/versions/0234_ft_billing_postage.py @@ -5,9 +5,8 @@ Revises: 0232_noti_postage_constraint_3 Create Date: 2018-09-28 14:43:26.100884 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0234_ft_billing_postage" down_revision = "0232_noti_postage_constraint_3" diff --git a/migrations/versions/0235_add_postage_to_pk.py b/migrations/versions/0235_add_postage_to_pk.py index cb52d0c0d..fac3ef0f6 100644 --- a/migrations/versions/0235_add_postage_to_pk.py +++ b/migrations/versions/0235_add_postage_to_pk.py @@ -5,9 +5,8 @@ Revises: 0234_ft_billing_postage Create Date: 2018-09-28 15:39:21.115358 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0235_add_postage_to_pk" down_revision = "0234_ft_billing_postage" diff --git a/migrations/versions/0237_add_filename_to_dvla_org.py b/migrations/versions/0237_add_filename_to_dvla_org.py index 5f11842de..9c34d32b0 100644 --- a/migrations/versions/0237_add_filename_to_dvla_org.py +++ b/migrations/versions/0237_add_filename_to_dvla_org.py @@ -5,11 +5,10 @@ Revises: 0235_add_postage_to_pk Create Date: 2018-09-28 15:39:21.115358 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.sql import text - revision = "0237_add_filename_to_dvla_org" down_revision = "0235_add_postage_to_pk" diff --git a/migrations/versions/0238_add_validation_failed.py b/migrations/versions/0238_add_validation_failed.py index f9416aed2..9b597666c 100644 --- a/migrations/versions/0238_add_validation_failed.py +++ b/migrations/versions/0238_add_validation_failed.py @@ -5,9 +5,8 @@ Revises: 0237_add_filename_to_dvla_org Create Date: 2018-09-03 11:24:58.773824 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0238_add_validation_failed" down_revision = "0237_add_filename_to_dvla_org" diff --git a/migrations/versions/0239_add_edit_folder_permission.py b/migrations/versions/0239_add_edit_folder_permission.py index 9b037be0c..b7bad01c7 100644 --- a/migrations/versions/0239_add_edit_folder_permission.py +++ b/migrations/versions/0239_add_edit_folder_permission.py @@ -5,9 +5,8 @@ Revises: 0238_add_validation_failed Create Date: 2018-09-03 11:24:58.773824 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0239_add_edit_folder_permission" down_revision = "0238_add_validation_failed" diff --git a/migrations/versions/0240_dvla_org_non_nullable.py b/migrations/versions/0240_dvla_org_non_nullable.py index fdeab3762..f4000365d 100644 --- a/migrations/versions/0240_dvla_org_non_nullable.py +++ b/migrations/versions/0240_dvla_org_non_nullable.py @@ -5,9 +5,8 @@ Revises: 0239_add_edit_folder_permission Create Date: 2018-10-25 09:16:54.602182 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0240_dvla_org_non_nullable" down_revision = "0239_add_edit_folder_permission" diff --git a/migrations/versions/0242_template_folders.py b/migrations/versions/0242_template_folders.py index 1421156c9..3424e2e2b 100644 --- a/migrations/versions/0242_template_folders.py +++ b/migrations/versions/0242_template_folders.py @@ -5,8 +5,8 @@ Revises: 0240_dvla_org_non_nullable Create Date: 2018-10-26 16:00:40.173840 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0242_template_folders" diff --git a/migrations/versions/0245_archived_flag_jobs.py b/migrations/versions/0245_archived_flag_jobs.py index 91f942cd5..068176dc4 100644 --- a/migrations/versions/0245_archived_flag_jobs.py +++ b/migrations/versions/0245_archived_flag_jobs.py @@ -5,9 +5,8 @@ Revises: 0242_template_folders Create Date: 2018-11-22 16:32:01.105803 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0245_archived_flag_jobs" down_revision = "0242_template_folders" diff --git a/migrations/versions/0248_enable_choose_postage.py b/migrations/versions/0248_enable_choose_postage.py index a5e9e526c..542ed8ec6 100644 --- a/migrations/versions/0248_enable_choose_postage.py +++ b/migrations/versions/0248_enable_choose_postage.py @@ -5,9 +5,8 @@ Revises: 0246_notifications_index Create Date: 2018-12-14 12:09:31.375634 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0248_enable_choose_postage" down_revision = "0246_notifications_index" diff --git a/migrations/versions/0250_drop_stats_template_table.py b/migrations/versions/0250_drop_stats_template_table.py index c4a043ae1..a8af6e9bb 100644 --- a/migrations/versions/0250_drop_stats_template_table.py +++ b/migrations/versions/0250_drop_stats_template_table.py @@ -5,8 +5,8 @@ Revises: 0248_enable_choose_postage Create Date: 2019-01-15 16:47:08.049369 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0250_drop_stats_template_table" diff --git a/migrations/versions/0252_letter_branding_table.py b/migrations/versions/0252_letter_branding_table.py index 7beb1740f..90618c029 100644 --- a/migrations/versions/0252_letter_branding_table.py +++ b/migrations/versions/0252_letter_branding_table.py @@ -6,8 +6,8 @@ Create Date: 2019-01-17 15:45:33.242955 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0252_letter_branding_table" diff --git a/migrations/versions/0253_set_template_postage_.py b/migrations/versions/0253_set_template_postage_.py index e3f176615..8b1ed0596 100644 --- a/migrations/versions/0253_set_template_postage_.py +++ b/migrations/versions/0253_set_template_postage_.py @@ -5,9 +5,8 @@ Revises: 0252_letter_branding_table Create Date: 2019-01-30 16:47:08.599448 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0253_set_template_postage" down_revision = "0252_letter_branding_table" diff --git a/migrations/versions/0254_folders_for_all.py b/migrations/versions/0254_folders_for_all.py index 0cd839e46..115adbc56 100644 --- a/migrations/versions/0254_folders_for_all.py +++ b/migrations/versions/0254_folders_for_all.py @@ -7,7 +7,6 @@ Create Date: 2019-01-08 13:30:48.694881+00 """ from alembic import op - revision = "0254_folders_for_all" down_revision = "0253_set_template_postage" diff --git a/migrations/versions/0256_set_postage_tmplt_hstr.py b/migrations/versions/0256_set_postage_tmplt_hstr.py index 60d2d9744..7c87be4d7 100644 --- a/migrations/versions/0256_set_postage_tmplt_hstr.py +++ b/migrations/versions/0256_set_postage_tmplt_hstr.py @@ -5,9 +5,8 @@ Revises: 0254_folders_for_all Create Date: 2019-02-05 14:51:30.808067 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0256_set_postage_tmplt_hstr" down_revision = "0254_folders_for_all" diff --git a/migrations/versions/0258_service_postage_nullable.py b/migrations/versions/0258_service_postage_nullable.py index ebe0dccfd..0d1a8ed43 100644 --- a/migrations/versions/0258_service_postage_nullable.py +++ b/migrations/versions/0258_service_postage_nullable.py @@ -5,9 +5,8 @@ Revises: 0257_letter_branding_migration Create Date: 2019-02-12 11:52:53.139383 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0258_service_postage_nullable" down_revision = "0257_letter_branding_migration" diff --git a/migrations/versions/0259_remove_service_postage.py b/migrations/versions/0259_remove_service_postage.py index 12ee5c3ec..ae089a54c 100644 --- a/migrations/versions/0259_remove_service_postage.py +++ b/migrations/versions/0259_remove_service_postage.py @@ -5,9 +5,8 @@ Revises: 0258_service_postage_nullable Create Date: 2019-02-11 17:12:22.341599 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0259_remove_service_postage" down_revision = "0258_service_postage_nullable" diff --git a/migrations/versions/0260_remove_dvla_organisation.py b/migrations/versions/0260_remove_dvla_organisation.py index cd6c16f6c..ad781df59 100644 --- a/migrations/versions/0260_remove_dvla_organisation.py +++ b/migrations/versions/0260_remove_dvla_organisation.py @@ -5,9 +5,8 @@ Revises: 0259_remove_service_postage Create Date: 2019-02-12 17:39:02.517571 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0260_remove_dvla_organisation" down_revision = "0259_remove_service_postage" diff --git a/migrations/versions/0261_service_volumes.py b/migrations/versions/0261_service_volumes.py index 900080205..3c7981326 100644 --- a/migrations/versions/0261_service_volumes.py +++ b/migrations/versions/0261_service_volumes.py @@ -5,10 +5,10 @@ Revises: 0260_remove_dvla_organisation Create Date: 2019-02-13 13:45:00.782500 """ -from alembic import op from itertools import product -import sqlalchemy as sa +import sqlalchemy as sa +from alembic import op revision = "0261_service_volumes" down_revision = "0260_remove_dvla_organisation" diff --git a/migrations/versions/0263_remove_edit_folders_2.py b/migrations/versions/0263_remove_edit_folders_2.py index 3191790a6..1b26b9cb3 100644 --- a/migrations/versions/0263_remove_edit_folders_2.py +++ b/migrations/versions/0263_remove_edit_folders_2.py @@ -5,9 +5,8 @@ Revises: 0262_remove_edit_folders Create Date: 2019-02-15 14:38:13.823432 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0263_remove_edit_folders_2" down_revision = "0262_remove_edit_folders" diff --git a/migrations/versions/0264_add_folder_permissions_perm.py b/migrations/versions/0264_add_folder_permissions_perm.py index d720e2bf6..6c4a8358d 100644 --- a/migrations/versions/0264_add_folder_permissions_perm.py +++ b/migrations/versions/0264_add_folder_permissions_perm.py @@ -7,7 +7,6 @@ Create Date: 2019-02-14 11:23:26.694656 """ from alembic import op - revision = "0264_add_folder_permissions_perm" down_revision = "0263_remove_edit_folders_2" diff --git a/migrations/versions/0266_user_folder_perms_table.py b/migrations/versions/0266_user_folder_perms_table.py index 99afbd48c..f714159de 100644 --- a/migrations/versions/0266_user_folder_perms_table.py +++ b/migrations/versions/0266_user_folder_perms_table.py @@ -5,8 +5,8 @@ Revises: 0265_add_confirm_edit_templates Create Date: 2019-02-26 17:00:13.247321 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0266_user_folder_perms_table" diff --git a/migrations/versions/0277_consent_to_research_null.py b/migrations/versions/0277_consent_to_research_null.py index 84645c205..3054d5000 100644 --- a/migrations/versions/0277_consent_to_research_null.py +++ b/migrations/versions/0277_consent_to_research_null.py @@ -5,8 +5,8 @@ Revises: 0266_user_folder_perms_table Create Date: 2019-03-01 13:47:15.720238 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0277_consent_to_research_null" diff --git a/migrations/versions/0278_add_more_stuff_to_orgs.py b/migrations/versions/0278_add_more_stuff_to_orgs.py index 5aa8e0582..592a206f9 100644 --- a/migrations/versions/0278_add_more_stuff_to_orgs.py +++ b/migrations/versions/0278_add_more_stuff_to_orgs.py @@ -5,8 +5,8 @@ Revises: 0277_consent_to_research_null Create Date: 2019-02-26 10:15:22.430340 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0278_add_more_stuff_to_orgs" diff --git a/migrations/versions/0280_invited_user_folder_perms.py b/migrations/versions/0280_invited_user_folder_perms.py index 8de375c13..914e06df1 100644 --- a/migrations/versions/0280_invited_user_folder_perms.py +++ b/migrations/versions/0280_invited_user_folder_perms.py @@ -5,8 +5,8 @@ Revises: 0279_remove_fk_to_users Create Date: 2019-03-11 14:38:28.010082 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0280_invited_user_folder_perms" diff --git a/migrations/versions/0281_non_null_folder_permissions.py b/migrations/versions/0281_non_null_folder_permissions.py index a6a2f4159..1e12454c3 100644 --- a/migrations/versions/0281_non_null_folder_permissions.py +++ b/migrations/versions/0281_non_null_folder_permissions.py @@ -5,8 +5,8 @@ Revises: 0280_invited_user_folder_perms Create Date: 2019-03-20 10:12:24.927129 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0281_non_null_folder_permissions" diff --git a/migrations/versions/0282_add_count_as_live.py b/migrations/versions/0282_add_count_as_live.py index 462126745..142039391 100644 --- a/migrations/versions/0282_add_count_as_live.py +++ b/migrations/versions/0282_add_count_as_live.py @@ -10,8 +10,8 @@ Create Date: 2016-10-25 17:37:27.660723 revision = "0282_add_count_as_live" down_revision = "0281_non_null_folder_permissions" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0283_platform_admin_not_live.py b/migrations/versions/0283_platform_admin_not_live.py index 28afed033..2d92c8334 100644 --- a/migrations/versions/0283_platform_admin_not_live.py +++ b/migrations/versions/0283_platform_admin_not_live.py @@ -12,9 +12,8 @@ from sqlalchemy import text revision = "0283_platform_admin_not_live" down_revision = "0282_add_count_as_live" -from alembic import op import sqlalchemy as sa - +from alembic import op STATEMENT = """ UPDATE diff --git a/migrations/versions/0284_0283_retry.py b/migrations/versions/0284_0283_retry.py index 79ea49dd8..e22f0d850 100644 --- a/migrations/versions/0284_0283_retry.py +++ b/migrations/versions/0284_0283_retry.py @@ -10,8 +10,8 @@ Create Date: 2016-10-25 17:37:27.660723 revision = "0284_0283_retry" down_revision = "0283_platform_admin_not_live" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0285_default_org_branding.py b/migrations/versions/0285_default_org_branding.py index 4c8fcb4a9..c8c6a2df9 100644 --- a/migrations/versions/0285_default_org_branding.py +++ b/migrations/versions/0285_default_org_branding.py @@ -10,8 +10,8 @@ Create Date: 2016-10-25 17:37:27.660723 revision = "0285_default_org_branding" down_revision = "0284_0283_retry" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0286_add_unique_email_name.py b/migrations/versions/0286_add_unique_email_name.py index 35b0cc036..46b571aec 100644 --- a/migrations/versions/0286_add_unique_email_name.py +++ b/migrations/versions/0286_add_unique_email_name.py @@ -5,8 +5,8 @@ Revises: 0285_default_org_branding Create Date: 2019-04-09 13:01:13.892249 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0286_add_unique_email_name" down_revision = "0285_default_org_branding" diff --git a/migrations/versions/0287_drop_branding_domains.py b/migrations/versions/0287_drop_branding_domains.py index fa7efb68e..d8e44dd44 100644 --- a/migrations/versions/0287_drop_branding_domains.py +++ b/migrations/versions/0287_drop_branding_domains.py @@ -5,8 +5,8 @@ Revises: 0286_add_unique_email_name Create Date: 2019-04-05 16:25:11.535816 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0287_drop_branding_domains" diff --git a/migrations/versions/0288_add_go_live_user.py b/migrations/versions/0288_add_go_live_user.py index a940da4ad..d24c1f97b 100644 --- a/migrations/versions/0288_add_go_live_user.py +++ b/migrations/versions/0288_add_go_live_user.py @@ -5,8 +5,8 @@ Revises: 0287_drop_branding_domains Create Date: 2019-04-15 16:50:22.275673 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0288_add_go_live_user" diff --git a/migrations/versions/0290_org_go_live_notes.py b/migrations/versions/0290_org_go_live_notes.py index 9e173d14e..f8775b7a1 100644 --- a/migrations/versions/0290_org_go_live_notes.py +++ b/migrations/versions/0290_org_go_live_notes.py @@ -5,9 +5,8 @@ Revises: 0289_precompiled_for_all Create Date: 2019-05-13 14:55:10.291781 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0290_org_go_live_notes" down_revision = "0289_precompiled_for_all" diff --git a/migrations/versions/0291_remove_unused_index.py b/migrations/versions/0291_remove_unused_index.py index 88be0cf98..b31c01898 100644 --- a/migrations/versions/0291_remove_unused_index.py +++ b/migrations/versions/0291_remove_unused_index.py @@ -5,9 +5,8 @@ Revises: 0290_org_go_live_notes Create Date: 2019-05-16 14:05:18.104274 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0291_remove_unused_index" down_revision = "0290_org_go_live_notes" diff --git a/migrations/versions/0292_give_users_folder_perms.py b/migrations/versions/0292_give_users_folder_perms.py index 753fbb768..5533554fe 100644 --- a/migrations/versions/0292_give_users_folder_perms.py +++ b/migrations/versions/0292_give_users_folder_perms.py @@ -8,7 +8,6 @@ Create Date: 2019-04-01 16:36:53.274394 from alembic import op from sqlalchemy.sql import text - revision = "0292_give_users_folder_perms" down_revision = "0291_remove_unused_index" diff --git a/migrations/versions/0293_drop_complaint_fk.py b/migrations/versions/0293_drop_complaint_fk.py index 143882ac2..0236b5e19 100644 --- a/migrations/versions/0293_drop_complaint_fk.py +++ b/migrations/versions/0293_drop_complaint_fk.py @@ -7,7 +7,6 @@ Create Date: 2019-05-16 14:05:18.104274 """ from alembic import op - revision = "0293_drop_complaint_fk" down_revision = "0292_give_users_folder_perms" diff --git a/migrations/versions/0295_api_key_constraint.py b/migrations/versions/0295_api_key_constraint.py index b09c26299..bc3fb39a3 100644 --- a/migrations/versions/0295_api_key_constraint.py +++ b/migrations/versions/0295_api_key_constraint.py @@ -5,8 +5,8 @@ Revises: 0294_add_verify_reply_to Create Date: 2019-06-04 13:49:50.685493 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0295_api_key_constraint" down_revision = "0294_add_verify_reply_to" diff --git a/migrations/versions/0296_agreement_signed_by_person.py b/migrations/versions/0296_agreement_signed_by_person.py index dfa0460a6..42eca43c8 100644 --- a/migrations/versions/0296_agreement_signed_by_person.py +++ b/migrations/versions/0296_agreement_signed_by_person.py @@ -5,8 +5,8 @@ Revises: 0295_api_key_constraint Create Date: 2019-06-13 16:40:32.982607 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0296_agreement_signed_by_person" down_revision = "0295_api_key_constraint" diff --git a/migrations/versions/0297_template_redacted_fix.py b/migrations/versions/0297_template_redacted_fix.py index 1550adb11..ef4cbe38a 100644 --- a/migrations/versions/0297_template_redacted_fix.py +++ b/migrations/versions/0297_template_redacted_fix.py @@ -7,7 +7,6 @@ Create Date: 2019-06-25 17:02:14.350064 """ from alembic import op - revision = "0297_template_redacted_fix" down_revision = "0296_agreement_signed_by_person" diff --git a/migrations/versions/0299_org_types_table.py b/migrations/versions/0299_org_types_table.py index 5ba68cad5..e25fb1c9d 100644 --- a/migrations/versions/0299_org_types_table.py +++ b/migrations/versions/0299_org_types_table.py @@ -5,9 +5,8 @@ Revises: 0298_add_mou_signed_receipt Create Date: 2019-07-10 16:07:22.019759 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0299_org_types_table" down_revision = "0298_add_mou_signed_receipt" diff --git a/migrations/versions/0300_migrate_org_types.py b/migrations/versions/0300_migrate_org_types.py index 2eb6c700e..9f4bdedf5 100644 --- a/migrations/versions/0300_migrate_org_types.py +++ b/migrations/versions/0300_migrate_org_types.py @@ -7,9 +7,8 @@ Revises: 0299_org_types_table Create Date: 2019-07-24 16:18:27.467361 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0300_migrate_org_types" down_revision = "0299_org_types_table" diff --git a/migrations/versions/0301_upload_letters_permission.py b/migrations/versions/0301_upload_letters_permission.py index 448834976..b41cb1fa4 100644 --- a/migrations/versions/0301_upload_letters_permission.py +++ b/migrations/versions/0301_upload_letters_permission.py @@ -5,9 +5,8 @@ Revises: 0300_migrate_org_types Create Date: 2019-08-05 10:49:27.467361 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0301_upload_letters_permission" down_revision = "0300_migrate_org_types" diff --git a/migrations/versions/0302_add_org_id_to_services.py b/migrations/versions/0302_add_org_id_to_services.py index 1c0acfca2..c1cb28c9d 100644 --- a/migrations/versions/0302_add_org_id_to_services.py +++ b/migrations/versions/0302_add_org_id_to_services.py @@ -5,8 +5,8 @@ Revises: 0301_upload_letters_permission Create Date: 2019-08-06 09:43:57.993510 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0302_add_org_id_to_services" diff --git a/migrations/versions/0303_populate_services_org_id.py b/migrations/versions/0303_populate_services_org_id.py index 3c405322a..17dc0c31c 100644 --- a/migrations/versions/0303_populate_services_org_id.py +++ b/migrations/versions/0303_populate_services_org_id.py @@ -5,8 +5,8 @@ Revises: 0302_add_org_id_to_services Create Date: 2019-08-06 09:43:57.993510 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.sql import text revision = "0303_populate_services_org_id" diff --git a/migrations/versions/0304_remove_org_to_service.py b/migrations/versions/0304_remove_org_to_service.py index 86e9a7812..5c6779304 100644 --- a/migrations/versions/0304_remove_org_to_service.py +++ b/migrations/versions/0304_remove_org_to_service.py @@ -5,8 +5,8 @@ Revises: 0303_populate_services_org_id Create Date: 2019-08-15 14:49:00.754390 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0304_remove_org_to_service" diff --git a/migrations/versions/0307_delete_dm_datetime.py b/migrations/versions/0307_delete_dm_datetime.py index d8efa1422..6f5f67819 100644 --- a/migrations/versions/0307_delete_dm_datetime.py +++ b/migrations/versions/0307_delete_dm_datetime.py @@ -5,8 +5,8 @@ Revises: 0304_remove_org_to_service Create Date: 2019-10-08 10:57:54.824807 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0307_delete_dm_datetime" diff --git a/migrations/versions/0308_delete_loadtesting_provider.py b/migrations/versions/0308_delete_loadtesting_provider.py index 2b643e2db..a7eb82ccf 100644 --- a/migrations/versions/0308_delete_loadtesting_provider.py +++ b/migrations/versions/0308_delete_loadtesting_provider.py @@ -7,6 +7,7 @@ Create Date: 2019-10-22 17:30 """ import uuid + from alembic import op from sqlalchemy.sql import text diff --git a/migrations/versions/0310_returned_letters_table_.py b/migrations/versions/0310_returned_letters_table_.py index 5984ddfd5..93de65f0e 100644 --- a/migrations/versions/0310_returned_letters_table_.py +++ b/migrations/versions/0310_returned_letters_table_.py @@ -5,8 +5,8 @@ Revises: 0309_add_uq_key_row_number Create Date: 2019-12-09 12:13:49.432993 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0310_returned_letters_table" diff --git a/migrations/versions/0311_add_inbound_sms_history.py b/migrations/versions/0311_add_inbound_sms_history.py index 57a321443..93b73b8e2 100644 --- a/migrations/versions/0311_add_inbound_sms_history.py +++ b/migrations/versions/0311_add_inbound_sms_history.py @@ -5,8 +5,8 @@ Revises: 0310_returned_letters_table Create Date: 2019-12-20 15:38:53.358509 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0311_add_inbound_sms_history" diff --git a/migrations/versions/0313_email_access_validated_at.py b/migrations/versions/0313_email_access_validated_at.py index da3214cb7..b470f2eba 100644 --- a/migrations/versions/0313_email_access_validated_at.py +++ b/migrations/versions/0313_email_access_validated_at.py @@ -5,9 +5,8 @@ Revises: 0312_populate_returned_letters Create Date: 2020-01-28 18:03:22.237386 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0313_email_access_validated_at" down_revision = "0312_populate_returned_letters" diff --git a/migrations/versions/0314_populate_email_access.py b/migrations/versions/0314_populate_email_access.py index 63566f16d..8a50d6979 100644 --- a/migrations/versions/0314_populate_email_access.py +++ b/migrations/versions/0314_populate_email_access.py @@ -7,7 +7,6 @@ Create Date: 2020-01-31 10:35:44.524606 """ from alembic import op - revision = "0314_populate_email_access" down_revision = "0313_email_access_validated_at" diff --git a/migrations/versions/0315_document_download_count.py b/migrations/versions/0315_document_download_count.py index d2733cb1e..dd890aa51 100644 --- a/migrations/versions/0315_document_download_count.py +++ b/migrations/versions/0315_document_download_count.py @@ -5,9 +5,8 @@ Revises: 0314_populate_email_access Create Date: 2020-02-12 14:19:18.066425 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0315_document_download_count" down_revision = "0314_populate_email_access" diff --git a/migrations/versions/0316_int_letters_permission.py b/migrations/versions/0316_int_letters_permission.py index 7b5fee461..f71709dcc 100644 --- a/migrations/versions/0316_int_letters_permission.py +++ b/migrations/versions/0316_int_letters_permission.py @@ -5,9 +5,8 @@ Revises: 0315_document_download_count Create Date: 2020-09-13 28:17:17.110495 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0316_int_letters_permission" down_revision = "0315_document_download_count" diff --git a/migrations/versions/0317_uploads_for_all.py b/migrations/versions/0317_uploads_for_all.py index e4673e2df..074d49860 100644 --- a/migrations/versions/0317_uploads_for_all.py +++ b/migrations/versions/0317_uploads_for_all.py @@ -7,7 +7,6 @@ Create Date: 2019-05-13 10:44:51.867661 """ from alembic import op - revision = "0317_uploads_for_all" down_revision = "0316_int_letters_permission" diff --git a/migrations/versions/0318_service_contact_list.py b/migrations/versions/0318_service_contact_list.py index b964e8f53..cafd5f456 100644 --- a/migrations/versions/0318_service_contact_list.py +++ b/migrations/versions/0318_service_contact_list.py @@ -5,8 +5,8 @@ Revises: 0317_uploads_for_all Create Date: 2020-03-12 15:44:30.784031 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0318_service_contact_list" diff --git a/migrations/versions/0319_contact_list_archived.py b/migrations/versions/0319_contact_list_archived.py index 488526337..f80340641 100644 --- a/migrations/versions/0319_contact_list_archived.py +++ b/migrations/versions/0319_contact_list_archived.py @@ -5,8 +5,8 @@ Revises: 0318_service_contact_list Create Date: 2020-03-26 11:16:12.389524 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0319_contact_list_archived" down_revision = "0318_service_contact_list" diff --git a/migrations/versions/0321_drop_postage_constraints.py b/migrations/versions/0321_drop_postage_constraints.py index a102b0c65..0a531419c 100644 --- a/migrations/versions/0321_drop_postage_constraints.py +++ b/migrations/versions/0321_drop_postage_constraints.py @@ -9,7 +9,6 @@ import os from alembic import op - revision = "0321_drop_postage_constraints" down_revision = "0320_optimise_notifications" environment = os.environ["NOTIFY_ENVIRONMENT"] diff --git a/migrations/versions/0322_broadcast_service_perm.py b/migrations/versions/0322_broadcast_service_perm.py index ebeb951b5..21997bc3e 100644 --- a/migrations/versions/0322_broadcast_service_perm.py +++ b/migrations/versions/0322_broadcast_service_perm.py @@ -7,7 +7,6 @@ Create Date: 2020-06-29 11:14:13.183683 """ from alembic import op - revision = "0322_broadcast_service_perm" down_revision = "0321_drop_postage_constraints" diff --git a/migrations/versions/0323_broadcast_message.py b/migrations/versions/0323_broadcast_message.py index 514f03982..c3bc387b3 100644 --- a/migrations/versions/0323_broadcast_message.py +++ b/migrations/versions/0323_broadcast_message.py @@ -5,10 +5,10 @@ Revises: 0322_broadcast_service_perm Create Date: 2020-07-02 11:59:38.734650 """ -from alembic import op import sqlalchemy as sa -from sqlalchemy.sql import column, func +from alembic import op from sqlalchemy.dialects import postgresql +from sqlalchemy.sql import column, func revision = "0323_broadcast_message" down_revision = "0322_broadcast_service_perm" diff --git a/migrations/versions/0326_broadcast_event.py b/migrations/versions/0326_broadcast_event.py index 40ca24f47..b4c263125 100644 --- a/migrations/versions/0326_broadcast_event.py +++ b/migrations/versions/0326_broadcast_event.py @@ -5,8 +5,8 @@ Revises: 0323_broadcast_message Create Date: 2020-07-24 12:40:35.809523 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0326_broadcast_event" diff --git a/migrations/versions/0327_idx_notification_history.py b/migrations/versions/0327_idx_notification_history.py index 25631e34f..3ae88565a 100644 --- a/migrations/versions/0327_idx_notification_history.py +++ b/migrations/versions/0327_idx_notification_history.py @@ -6,6 +6,7 @@ Create Date: 2020-07-28 08:11:07.666708 """ import os + from alembic import op revision = "0327_idx_notification_history" diff --git a/migrations/versions/0329_purge_broadcast_data.py b/migrations/versions/0329_purge_broadcast_data.py index e1961db2d..c1df398a1 100644 --- a/migrations/versions/0329_purge_broadcast_data.py +++ b/migrations/versions/0329_purge_broadcast_data.py @@ -7,7 +7,6 @@ Create Date: 2020-09-07 16:00:27.545673 """ from alembic import op - revision = "0329_purge_broadcast_data" down_revision = "0328_international_letters_perm" diff --git a/migrations/versions/0331_add_broadcast_org.py b/migrations/versions/0331_add_broadcast_org.py index 23136fd04..76e9d8bbe 100644 --- a/migrations/versions/0331_add_broadcast_org.py +++ b/migrations/versions/0331_add_broadcast_org.py @@ -5,10 +5,11 @@ Revises: 0330_broadcast_invite_email Create Date: 2020-09-23 10:11:01.094412 """ -from alembic import op -import sqlalchemy as sa import os +import sqlalchemy as sa +from alembic import op + revision = "0331_add_broadcast_org" down_revision = "0330_broadcast_invite_email" diff --git a/migrations/versions/0332_broadcast_provider_msg.py b/migrations/versions/0332_broadcast_provider_msg.py index fb3c2d856..4fa90b67d 100644 --- a/migrations/versions/0332_broadcast_provider_msg.py +++ b/migrations/versions/0332_broadcast_provider_msg.py @@ -5,8 +5,8 @@ Revises: 0331_add_broadcast_org Create Date: 2020-10-26 16:28:11.917468 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0332_broadcast_provider_msg" diff --git a/migrations/versions/0333_service_broadcast_provider.py b/migrations/versions/0333_service_broadcast_provider.py index 2bca3512f..26a5a5462 100644 --- a/migrations/versions/0333_service_broadcast_provider.py +++ b/migrations/versions/0333_service_broadcast_provider.py @@ -5,8 +5,8 @@ Revises: 0332_broadcast_provider_msg Create Date: 2020-12-01 17:03:18.209780 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0333_service_broadcast_provider" diff --git a/migrations/versions/0334_broadcast_message_number.py b/migrations/versions/0334_broadcast_message_number.py index a2df2c9fb..a1221e1c2 100644 --- a/migrations/versions/0334_broadcast_message_number.py +++ b/migrations/versions/0334_broadcast_message_number.py @@ -5,8 +5,8 @@ Revises: 0333_service_broadcast_provider Create Date: 2020-12-04 15:06:22.544803 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0334_broadcast_message_number" diff --git a/migrations/versions/0335_broadcast_msg_content.py b/migrations/versions/0335_broadcast_msg_content.py index a0fe5daf9..c8506a235 100644 --- a/migrations/versions/0335_broadcast_msg_content.py +++ b/migrations/versions/0335_broadcast_msg_content.py @@ -5,8 +5,8 @@ Revises: 0334_broadcast_message_number Create Date: 2020-12-04 15:06:22.544803 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0335_broadcast_msg_content" diff --git a/migrations/versions/0336_broadcast_msg_content_2.py b/migrations/versions/0336_broadcast_msg_content_2.py index 2455e8993..7b3159454 100644 --- a/migrations/versions/0336_broadcast_msg_content_2.py +++ b/migrations/versions/0336_broadcast_msg_content_2.py @@ -5,8 +5,8 @@ Revises: 0335_broadcast_msg_content Create Date: 2020-12-04 15:06:22.544803 """ -from alembic import op import sqlalchemy as sa +from alembic import op from notifications_utils.template import BroadcastMessageTemplate from sqlalchemy.dialects import postgresql from sqlalchemy.orm.session import Session diff --git a/migrations/versions/0337_broadcast_msg_api.py b/migrations/versions/0337_broadcast_msg_api.py index 39b61d99e..01cb85e8d 100644 --- a/migrations/versions/0337_broadcast_msg_api.py +++ b/migrations/versions/0337_broadcast_msg_api.py @@ -5,8 +5,8 @@ Revises: 0336_broadcast_msg_content_2 Create Date: 2020-12-04 15:06:22.544803 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0337_broadcast_msg_api" diff --git a/migrations/versions/0338_add_notes_to_service.py b/migrations/versions/0338_add_notes_to_service.py index 2f381a2ed..c2ad3b912 100644 --- a/migrations/versions/0338_add_notes_to_service.py +++ b/migrations/versions/0338_add_notes_to_service.py @@ -5,9 +5,8 @@ Revises: 0337_broadcast_msg_api Create Date: 2021-01-13 11:50:06.333369 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0338_add_notes_to_service" down_revision = "0337_broadcast_msg_api" diff --git a/migrations/versions/0339_service_billing_details.py b/migrations/versions/0339_service_billing_details.py index e1a570625..7e814e35f 100644 --- a/migrations/versions/0339_service_billing_details.py +++ b/migrations/versions/0339_service_billing_details.py @@ -5,9 +5,8 @@ Revises: 0338_add_notes_to_service Create Date: 2021-01-20 17:55:46.555460 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0339_service_billing_details" down_revision = "0338_add_notes_to_service" diff --git a/migrations/versions/0340_stub_training_broadcasts.py b/migrations/versions/0340_stub_training_broadcasts.py index ff761e51a..081f7143a 100644 --- a/migrations/versions/0340_stub_training_broadcasts.py +++ b/migrations/versions/0340_stub_training_broadcasts.py @@ -5,9 +5,8 @@ Revises: 0339_service_billing_details Create Date: 2021-01-26 16:48:44.921065 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0340_stub_training_broadcasts" down_revision = "0339_service_billing_details" diff --git a/migrations/versions/0342_service_broadcast_settings.py b/migrations/versions/0342_service_broadcast_settings.py index 90390c231..cc451b871 100644 --- a/migrations/versions/0342_service_broadcast_settings.py +++ b/migrations/versions/0342_service_broadcast_settings.py @@ -5,8 +5,8 @@ Revises: 0340_stub_training_broadcasts Create Date: 2021-01-28 21:30:23.102340 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0342_service_broadcast_settings" diff --git a/migrations/versions/0343_org_billing_details.py b/migrations/versions/0343_org_billing_details.py index a14ca6bf2..d0fb0c105 100644 --- a/migrations/versions/0343_org_billing_details.py +++ b/migrations/versions/0343_org_billing_details.py @@ -5,9 +5,8 @@ Revises: 0342_service_broadcast_settings Create Date: 2021-02-01 14:40:14.809632 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0343_org_billing_details" down_revision = "0342_service_broadcast_settings" diff --git a/migrations/versions/0344_stubbed_not_nullable.py b/migrations/versions/0344_stubbed_not_nullable.py index 2c070204f..d9623bb84 100644 --- a/migrations/versions/0344_stubbed_not_nullable.py +++ b/migrations/versions/0344_stubbed_not_nullable.py @@ -5,9 +5,8 @@ Revises: 0343_org_billing_details Create Date: 2021-02-08 18:10:15.533279 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0344_stubbed_not_nullable" down_revision = "0343_org_billing_details" diff --git a/migrations/versions/0345_move_broadcast_provider.py b/migrations/versions/0345_move_broadcast_provider.py index 7bd588144..3f246340e 100644 --- a/migrations/versions/0345_move_broadcast_provider.py +++ b/migrations/versions/0345_move_broadcast_provider.py @@ -5,8 +5,8 @@ Revises: 0344_stubbed_not_nullable Create Date: 2021-02-09 09:19:07.957980 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy import text from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0348_migrate_broadcast_settings_migrate_broadcast_settings.py b/migrations/versions/0348_migrate_broadcast_settings_migrate_broadcast_settings.py index 8d08e616e..75d431a4c 100644 --- a/migrations/versions/0348_migrate_broadcast_settings_migrate_broadcast_settings.py +++ b/migrations/versions/0348_migrate_broadcast_settings_migrate_broadcast_settings.py @@ -5,8 +5,8 @@ Revises: 0347_add_dvla_volumes_template Create Date: 2021-02-18 15:25:30.667098 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy import text from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0349_add_ft_processing_time.py b/migrations/versions/0349_add_ft_processing_time.py index 62066e46b..d2a0c7a2c 100644 --- a/migrations/versions/0349_add_ft_processing_time.py +++ b/migrations/versions/0349_add_ft_processing_time.py @@ -5,8 +5,8 @@ Revises: 0348_migrate_broadcast_settings Create Date: 2021-02-22 14:05:24.775338 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0349_add_ft_processing_time" diff --git a/migrations/versions/0352_broadcast_provider_types.py b/migrations/versions/0352_broadcast_provider_types.py index 1ac9a2b57..d61f5aad3 100644 --- a/migrations/versions/0352_broadcast_provider_types.py +++ b/migrations/versions/0352_broadcast_provider_types.py @@ -5,8 +5,8 @@ Revises: 0351_unique_key_annual_billing Create Date: 2021-05-05 15:07:22.146657 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy import text revision = "0352_broadcast_provider_types" diff --git a/migrations/versions/0353_broadcast_provider_not_null.py b/migrations/versions/0353_broadcast_provider_not_null.py index a78b68892..ed46213f8 100644 --- a/migrations/versions/0353_broadcast_provider_not_null.py +++ b/migrations/versions/0353_broadcast_provider_not_null.py @@ -5,8 +5,8 @@ Revises: 0352_broadcast_provider_types Create Date: 2021-05-10 15:06:40.046786 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0353_broadcast_provider_not_null" down_revision = "0352_broadcast_provider_types" diff --git a/migrations/versions/0355_add_webauthn_table.py b/migrations/versions/0355_add_webauthn_table.py index c5e704deb..6263dfe9a 100644 --- a/migrations/versions/0355_add_webauthn_table.py +++ b/migrations/versions/0355_add_webauthn_table.py @@ -5,8 +5,8 @@ Revises: 0354_government_channel Create Date: 2021-05-07 17:04:22.017137 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0355_add_webauthn_table" diff --git a/migrations/versions/0359_more_permissions.py b/migrations/versions/0359_more_permissions.py index 6bdf1bc5a..e9ac98ed7 100644 --- a/migrations/versions/0359_more_permissions.py +++ b/migrations/versions/0359_more_permissions.py @@ -5,8 +5,8 @@ Revises: 0358_operator_channel Create Date: 2021-06-15 17:47:16.871071 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0359_more_permissions" down_revision = "0358_operator_channel" diff --git a/migrations/versions/0362_broadcast_msg_event.py b/migrations/versions/0362_broadcast_msg_event.py index bd3656846..b552b9d33 100644 --- a/migrations/versions/0362_broadcast_msg_event.py +++ b/migrations/versions/0362_broadcast_msg_event.py @@ -5,8 +5,8 @@ Revises: 0361_new_user_bcast_permissions Create Date: 2020-12-04 15:06:22.544803 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0362_broadcast_msg_event" diff --git a/migrations/versions/0363_cancelled_by_api_key.py b/migrations/versions/0363_cancelled_by_api_key.py index 9b11dbb55..828944d5d 100644 --- a/migrations/versions/0363_cancelled_by_api_key.py +++ b/migrations/versions/0363_cancelled_by_api_key.py @@ -3,8 +3,8 @@ Revision ID: 0363_cancelled_by_api_key Revises: 0362_broadcast_msg_event Create Date: 2022-02-09 14:05:27.750234 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0363_cancelled_by_api_key" diff --git a/migrations/versions/0364_drop_old_column.py b/migrations/versions/0364_drop_old_column.py index bd43471db..864c552ba 100644 --- a/migrations/versions/0364_drop_old_column.py +++ b/migrations/versions/0364_drop_old_column.py @@ -5,8 +5,8 @@ Revises: 0363_cancelled_by_api_key Create Date: 2022-01-25 18:05:27.750234 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0364_drop_old_column" diff --git a/migrations/versions/0373_add_notifications_view.py b/migrations/versions/0373_add_notifications_view.py index 5e97a0101..88c890bfd 100644 --- a/migrations/versions/0373_add_notifications_view.py +++ b/migrations/versions/0373_add_notifications_view.py @@ -6,8 +6,8 @@ Create Date: 2022-05-18 09:39:45.260951 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0373_add_notifications_view" diff --git a/migrations/versions/0374_fix_reg_template_history.py b/migrations/versions/0374_fix_reg_template_history.py index 307d991a8..7bd0892f5 100644 --- a/migrations/versions/0374_fix_reg_template_history.py +++ b/migrations/versions/0374_fix_reg_template_history.py @@ -14,8 +14,8 @@ from sqlalchemy import text revision = "0374_fix_reg_template_history" down_revision = "0373_add_notifications_view" -from alembic import op import sqlalchemy as sa +from alembic import op service_id = "d6aa2c68-a2d9-4437-ab19-3ae8eb202553" user_id = "6af522d0-2915-4e52-83a3-3690455a5fe6" diff --git a/migrations/versions/0376_add_provider_response.py b/migrations/versions/0376_add_provider_response.py index 58d4f0136..239ed850f 100644 --- a/migrations/versions/0376_add_provider_response.py +++ b/migrations/versions/0376_add_provider_response.py @@ -11,8 +11,8 @@ from datetime import datetime revision = "0376_add_provider_response" down_revision = "0375_fix_service_name" -from alembic import op import sqlalchemy as sa +from alembic import op def upgrade(): diff --git a/migrations/versions/0378_add_org_names.py b/migrations/versions/0378_add_org_names.py index cb3492aed..b55a2dec2 100644 --- a/migrations/versions/0378_add_org_names.py +++ b/migrations/versions/0378_add_org_names.py @@ -5,8 +5,8 @@ Revises: 0377_add_inbound_sms_number Create Date: 2022-09-23 20:04:00.766980 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0378_add_org_names" diff --git a/migrations/versions/0379_remove_broadcasts.py b/migrations/versions/0379_remove_broadcasts.py index 018f51633..d7b891f57 100644 --- a/migrations/versions/0379_remove_broadcasts.py +++ b/migrations/versions/0379_remove_broadcasts.py @@ -5,9 +5,9 @@ Revises: 0378_add_org_names Create Date: 2022-10-25 14:41:29.429928 """ -from alembic import op -import sqlalchemy as sa import psycopg2 +import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0379_remove_broadcasts" diff --git a/migrations/versions/0380_bst_to_local.py b/migrations/versions/0380_bst_to_local.py index 272d4522a..0316fda6a 100644 --- a/migrations/versions/0380_bst_to_local.py +++ b/migrations/versions/0380_bst_to_local.py @@ -5,8 +5,8 @@ Revises: 0379_remove_broadcasts Create Date: 2022-11-21 11:35:51.987539 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0380_bst_to_local" diff --git a/migrations/versions/0381_encrypted_column_types.py b/migrations/versions/0381_encrypted_column_types.py index f9c78922c..b36a89742 100644 --- a/migrations/versions/0381_encrypted_column_types.py +++ b/migrations/versions/0381_encrypted_column_types.py @@ -5,9 +5,8 @@ Revises: 0380_bst_to_local Create Date: 2022-12-09 10:17:03.358405 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0381_encrypted_column_types" down_revision = "0380_bst_to_local" diff --git a/migrations/versions/0383_update_default_templates.py b/migrations/versions/0383_update_default_templates.py index ba4cb3a3b..ff2439555 100644 --- a/migrations/versions/0383_update_default_templates.py +++ b/migrations/versions/0383_update_default_templates.py @@ -6,11 +6,12 @@ Create Date: 2023-01-10 11:42:25.633265 """ import json -from alembic import op + import sqlalchemy as sa +from alembic import op +from flask import current_app from sqlalchemy import text from sqlalchemy.dialects import postgresql -from flask import current_app revision = "0383_update_default_templates.py" down_revision = "0381_encrypted_column_types" diff --git a/migrations/versions/0384_remove_letter_branding_.py b/migrations/versions/0384_remove_letter_branding_.py index 6b909b994..387de0cc4 100644 --- a/migrations/versions/0384_remove_letter_branding_.py +++ b/migrations/versions/0384_remove_letter_branding_.py @@ -5,8 +5,8 @@ Revises: 0383_update_default_templates.py Create Date: 2023-02-09 22:24:07.187569 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0384_remove_letter_branding_" diff --git a/migrations/versions/0385_remove postage_.py b/migrations/versions/0385_remove postage_.py index e9d856937..5bf88197c 100644 --- a/migrations/versions/0385_remove postage_.py +++ b/migrations/versions/0385_remove postage_.py @@ -5,8 +5,8 @@ Revises: 0384_remove_letter_branding_ Create Date: 2023-02-10 12:20:39.411493 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0385_remove_postage_" diff --git a/migrations/versions/0387_remove_letter_perms_.py b/migrations/versions/0387_remove_letter_perms_.py index 7d413b7c1..36bc94beb 100644 --- a/migrations/versions/0387_remove_letter_perms_.py +++ b/migrations/versions/0387_remove_letter_perms_.py @@ -5,8 +5,8 @@ Revises: 0385_remove_postage_ Create Date: 2023-02-17 11:56:00.993409 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0387_remove_letter_perms_" diff --git a/migrations/versions/0388_no_serv_letter_contact.py b/migrations/versions/0388_no_serv_letter_contact.py index 1f34e5511..7b3e7d170 100644 --- a/migrations/versions/0388_no_serv_letter_contact.py +++ b/migrations/versions/0388_no_serv_letter_contact.py @@ -5,8 +5,8 @@ Revises: 0387_remove_letter_perms_ Create Date: 2023-02-17 14:42:52.679425 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0388_no_serv_letter_contact" diff --git a/migrations/versions/0389_no_more_letters.py b/migrations/versions/0389_no_more_letters.py index d030cd916..542a5618b 100644 --- a/migrations/versions/0389_no_more_letters.py +++ b/migrations/versions/0389_no_more_letters.py @@ -5,8 +5,8 @@ Revises: 0388_no_serv_letter_contact Create Date: 2023-02-28 08:58:38.310095 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0389_no_more_letters" diff --git a/migrations/versions/0390_drop_dvla_provider.py b/migrations/versions/0390_drop_dvla_provider.py index 4704162ea..13c299b4f 100644 --- a/migrations/versions/0390_drop_dvla_provider.py +++ b/migrations/versions/0390_drop_dvla_provider.py @@ -7,8 +7,8 @@ Create Date: 2023-02-28 14:25:50.751952 """ import uuid -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy import text from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0391_update_sms_numbers.py b/migrations/versions/0391_update_sms_numbers.py index 43599e2fc..3c0c4828b 100644 --- a/migrations/versions/0391_update_sms_numbers.py +++ b/migrations/versions/0391_update_sms_numbers.py @@ -5,9 +5,9 @@ Revises: 0390_drop_dvla_provider Create Date: 2023-03-01 12:36:38.226954 """ +import sqlalchemy as sa from alembic import op from flask import current_app -import sqlalchemy as sa from sqlalchemy import text revision = "0391_update_sms_numbers" diff --git a/migrations/versions/0392_drop_letter_permissions_.py b/migrations/versions/0392_drop_letter_permissions_.py index 2e0b40a3b..c43d33e3b 100644 --- a/migrations/versions/0392_drop_letter_permissions_.py +++ b/migrations/versions/0392_drop_letter_permissions_.py @@ -7,7 +7,6 @@ Create Date: 2023-03-06 08:55:24.153687 """ from alembic import op - revision = "0392_drop_letter_permissions" down_revision = "0391_update_sms_numbers" diff --git a/migrations/versions/0393_remove_crown.py b/migrations/versions/0393_remove_crown.py index 39807babd..48649a00d 100644 --- a/migrations/versions/0393_remove_crown.py +++ b/migrations/versions/0393_remove_crown.py @@ -5,8 +5,8 @@ Revises: 0392_drop_letter_permissions Create Date: 2023-04-10 14:13:38.207790 """ -from alembic import op import sqlalchemy as sa +from alembic import op revision = "0393_remove_crown" down_revision = "0392_drop_letter_permissions" diff --git a/migrations/versions/0394_remove_contact_list_.py b/migrations/versions/0394_remove_contact_list_.py index fe0acd38a..2cbc8e9f5 100644 --- a/migrations/versions/0394_remove_contact_list_.py +++ b/migrations/versions/0394_remove_contact_list_.py @@ -5,8 +5,8 @@ Revises: 0393_remove_crown Create Date: 2023-04-12 13:12:12.683257 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0394_remove_contact_list" diff --git a/migrations/versions/0395_remove_intl_letters_perm.py b/migrations/versions/0395_remove_intl_letters_perm.py index 5c98c7cb7..6b2f028e0 100644 --- a/migrations/versions/0395_remove_intl_letters_perm.py +++ b/migrations/versions/0395_remove_intl_letters_perm.py @@ -5,8 +5,8 @@ Revises: 0394_remove_contact_list Create Date: 2023-05-23 10:03:10.485368 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0395_remove_intl_letters_perm" diff --git a/migrations/versions/0396_rename_organisation.py b/migrations/versions/0396_rename_organisation.py index 7df90b290..8a652a3e8 100644 --- a/migrations/versions/0396_rename_organisation.py +++ b/migrations/versions/0396_rename_organisation.py @@ -5,9 +5,8 @@ Revises: 0395_add_total_message_limit Create Date: 2023-04-27 14:59:39.428607 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0396_rename_organisation" down_revision = "0395_remove_intl_letters_perm" diff --git a/migrations/versions/0397_rename_organisation_2.py b/migrations/versions/0397_rename_organisation_2.py index e142300ce..fa05b16ca 100644 --- a/migrations/versions/0397_rename_organisation_2.py +++ b/migrations/versions/0397_rename_organisation_2.py @@ -5,8 +5,8 @@ Revises: 0396_rename_organisation Create Date: 2023-07-13 09:33:52.455290 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0397_rename_organisation_2" diff --git a/migrations/versions/0398_agreements_table.py b/migrations/versions/0398_agreements_table.py index 8efd864cd..683bc2f18 100644 --- a/migrations/versions/0398_agreements_table.py +++ b/migrations/versions/0398_agreements_table.py @@ -10,8 +10,8 @@ Create Date: 2016-04-26 13:08:42.892813 revision = "0398_agreements_table" down_revision = "0397_rename_organisation_2" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0399_remove_research_mode.py b/migrations/versions/0399_remove_research_mode.py index 8edf0291c..585e30fef 100644 --- a/migrations/versions/0399_remove_research_mode.py +++ b/migrations/versions/0399_remove_research_mode.py @@ -10,8 +10,8 @@ Create Date: 2016-04-26 13:08:42.892813 revision = "0399_remove_research_mode" down_revision = "0398_agreements_table" -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql diff --git a/migrations/versions/0400_add_total_message_limit.py b/migrations/versions/0400_add_total_message_limit.py index b4410c36b..50f7fd4e1 100644 --- a/migrations/versions/0400_add_total_message_limit.py +++ b/migrations/versions/0400_add_total_message_limit.py @@ -5,9 +5,8 @@ Revises: 0399_remove_research_mode Create Date: 2023-04-24 11:35:22.873930 """ -from alembic import op import sqlalchemy as sa - +from alembic import op revision = "0400_add_total_message_limit" down_revision = "0399_remove_research_mode" diff --git a/migrations/versions/0401_add_e2e_test_user.py b/migrations/versions/0401_add_e2e_test_user.py index 020e746e7..de0f45c0b 100644 --- a/migrations/versions/0401_add_e2e_test_user.py +++ b/migrations/versions/0401_add_e2e_test_user.py @@ -9,8 +9,8 @@ import datetime import os import uuid -from alembic import op import sqlalchemy as sa +from alembic import op from app import db from app.dao.users_dao import get_user_by_email diff --git a/migrations/versions/0402_total_message_limit_default.py b/migrations/versions/0402_total_message_limit_default.py index 75539bcf1..6a1c894c5 100644 --- a/migrations/versions/0402_total_message_limit_default.py +++ b/migrations/versions/0402_total_message_limit_default.py @@ -5,10 +5,9 @@ Revises: 0401_add_e2e_test_user Create Date: 2023-09-18 10:04:58.957374 """ +import sqlalchemy as sa from alembic import op from flask import current_app -import sqlalchemy as sa - revision = "0402_total_message_limit_default" down_revision = "0401_add_e2e_test_user" diff --git a/migrations/versions/0403_add_carrier.py b/migrations/versions/0403_add_carrier.py index b13e66c96..852eb2253 100644 --- a/migrations/versions/0403_add_carrier.py +++ b/migrations/versions/0403_add_carrier.py @@ -4,10 +4,9 @@ Revision ID: 0403_add_carrier Revises: 0402_total_message_limit_default """ +import sqlalchemy as sa from alembic import op from flask import current_app -import sqlalchemy as sa - down_revision = "0402_total_message_limit_default" revision = "0403_add_carrier" diff --git a/migrations/versions/0404_expire_invites.py b/migrations/versions/0404_expire_invites.py index 9c266bfc9..da7ab6a22 100644 --- a/migrations/versions/0404_expire_invites.py +++ b/migrations/versions/0404_expire_invites.py @@ -6,8 +6,9 @@ Create Date: 2023-11-10 15:52:07.348485 """ from re import I -from alembic import op + import sqlalchemy as sa +from alembic import op # Copied pattern for adjusting a enum as defined in 0359_more_permissions diff --git a/migrations/versions/0405_add_preferred_timezone.py b/migrations/versions/0405_add_preferred_timezone.py index f689516bd..4067b1891 100644 --- a/migrations/versions/0405_add_preferred_timezone.py +++ b/migrations/versions/0405_add_preferred_timezone.py @@ -4,10 +4,9 @@ Revision ID: 0405_add_preferred_timezone Revises: 0404_expire_invites """ +import sqlalchemy as sa from alembic import op from flask import current_app -import sqlalchemy as sa - down_revision = "0404_expire_invites" revision = "0405_add_preferred_timezone" diff --git a/migrations/versions/0406_adjust_agreement_model.py b/migrations/versions/0406_adjust_agreement_model.py index 4b91b8c9a..9b8c710f9 100644 --- a/migrations/versions/0406_adjust_agreement_model.py +++ b/migrations/versions/0406_adjust_agreement_model.py @@ -5,8 +5,8 @@ Revises: 0404_expire_invites Create Date: 2023-11-17 15:39:45.470089 """ -from alembic import op import sqlalchemy as sa +from alembic import op from sqlalchemy.dialects import postgresql revision = "0406_adjust_agreement_model" diff --git a/migrations/versions/0407_fix_preferred_timezone.py b/migrations/versions/0407_fix_preferred_timezone.py index 441b4dcef..701f9eda4 100644 --- a/migrations/versions/0407_fix_preferred_timezone.py +++ b/migrations/versions/0407_fix_preferred_timezone.py @@ -10,8 +10,9 @@ revision = "0407_fix_preferred_timezone" def upgrade(): - op.execute("update users set preferred_timezone='US/Eastern' where preferred_timezone=''") - + op.execute( + "update users set preferred_timezone='US/Eastern' where preferred_timezone=''" + ) def downgrade(): diff --git a/migrations/versions/0408_fix_timezone_again.py b/migrations/versions/0408_fix_timezone_again.py index b39cb0191..bed9039dc 100644 --- a/migrations/versions/0408_fix_timezone_again.py +++ b/migrations/versions/0408_fix_timezone_again.py @@ -10,8 +10,9 @@ revision = "0408_fix_timezone_again" def upgrade(): - op.execute("update users set preferred_timezone='US/Eastern' where (preferred_timezone='') is not false") - + op.execute( + "update users set preferred_timezone='US/Eastern' where (preferred_timezone='') is not false" + ) def downgrade(): diff --git a/run_celery.py b/run_celery.py index 62bab0fbf..9f0c4e704 100644 --- a/run_celery.py +++ b/run_celery.py @@ -3,8 +3,7 @@ from flask import Flask # notify_celery is referenced from manifest_delivery_base.yml, and cannot be removed -from app import notify_celery, create_app # noqa - +from app import create_app, notify_celery # noqa application = Flask("delivery") create_app(application) diff --git a/scripts/check_if_new_migration.py b/scripts/check_if_new_migration.py index 06210a537..b28c602c2 100644 --- a/scripts/check_if_new_migration.py +++ b/scripts/check_if_new_migration.py @@ -1,7 +1,8 @@ import os -from os.path import dirname, abspath -import requests import sys +from os.path import abspath, dirname + +import requests def get_latest_db_migration_to_apply(): diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index fb07e8d78..aa7a49626 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -1,6 +1,6 @@ -from functools import partial import json import uuid +from functools import partial import pytest from flask import current_app @@ -208,6 +208,7 @@ def test_resend_expired_invite(client, sample_expired_user, mocker): mock_send = mocker.patch("app.service_invite.rest.send_notification_to_queue") mock_persist = mocker.patch("app.service_invite.rest.persist_notification") from app.notifications.process_notifications import persist_notification + mock_persist.side_effect = partial(persist_notification, simulated=True) auth_header = create_admin_authorization_header() response = client.post( From e4f228ca0d63a19e179b29561299a3e6db44c296 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Dec 2023 22:01:27 -0500 Subject: [PATCH 008/259] More test fixes. Signed-off-by: Cliff Hill --- app/commands.py | 3 ++- app/service_invite/rest.py | 1 - tests/app/service_invite/test_service_invite_rest.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/commands.py b/app/commands.py index 1883a8a57..5f73bc3b6 100644 --- a/app/commands.py +++ b/app/commands.py @@ -325,7 +325,8 @@ def update_jobs_archived_flag(start_date, end_date): ) db.session.commit() current_app.logger.info( - f"jobs: --- Completed took {datetime.now() - start_time}ms. Archived {result.rowcount} jobs for {process_date}" + f"jobs: --- Completed took {datetime.now() - start_time}ms. Archived " + f"{result.rowcount} jobs for {process_date}" ) process_date += timedelta(days=1) diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index dbc706ce1..419713688 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -1,5 +1,4 @@ from datetime import datetime -from re import I from flask import Blueprint, current_app, jsonify, request from itsdangerous import BadData, SignatureExpired diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index aa7a49626..cd499fdb2 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -213,7 +213,6 @@ def test_resend_expired_invite(client, sample_expired_user, mocker): auth_header = create_admin_authorization_header() response = client.post( url, - data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) From 81b5afc983199a8f94f63b93f9d1850f5d178cd5 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 12 Dec 2023 09:13:43 -0500 Subject: [PATCH 009/259] Fixing tests. Signed-off-by: Cliff Hill --- app/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commands.py b/app/commands.py index 5f73bc3b6..32cbd219b 100644 --- a/app/commands.py +++ b/app/commands.py @@ -319,7 +319,7 @@ def update_jobs_archived_flag(start_date, end_date): where created_at >= (date :start + time '00:00:00') and created_at < (date :end + time '00:00:00') - """ + """ result = db.session.execute( sql, {"start": process_date, "end": process_date + timedelta(days=1)} ) From b72ae4374f0d5da65333fc71503bf67727dd73e6 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 13 Dec 2023 10:14:14 -0500 Subject: [PATCH 010/259] Working on tests. Signed-off-by: Cliff Hill --- app/dao/invited_user_dao.py | 2 +- tests/app/service_invite/test_service_invite_rest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/dao/invited_user_dao.py b/app/dao/invited_user_dao.py index c045489e8..ad4ab0a65 100644 --- a/app/dao/invited_user_dao.py +++ b/app/dao/invited_user_dao.py @@ -33,7 +33,7 @@ def expire_invitations_created_more_than_two_days_ago(): db.session.query(InvitedUser) .filter( InvitedUser.created_at <= datetime.utcnow() - timedelta(days=2), - InvitedUser.status.in_(INVITE_PENDING), + InvitedUser.status.in_((INVITE_PENDING,)), ) .update({InvitedUser.status: INVITE_EXPIRED}) ) diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index cd499fdb2..fede2596f 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -204,7 +204,7 @@ def test_get_invited_user_by_service_when_user_does_not_belong_to_the_service( def test_resend_expired_invite(client, sample_expired_user, mocker): - url = f"/service/{sample_expired_user.service_id}/invite/{sample_expired_user.id}" + url = f"/service/{sample_expired_user.service_id}/invite/{sample_expired_user.id}/resend" mock_send = mocker.patch("app.service_invite.rest.send_notification_to_queue") mock_persist = mocker.patch("app.service_invite.rest.persist_notification") from app.notifications.process_notifications import persist_notification From 61d294292bf605a531d5385f0f1f00d6ea14df33 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 15 Dec 2023 13:14:53 -0500 Subject: [PATCH 011/259] Did stuff, fixed things. This seems to work now. Signed-off-by: Cliff Hill --- app/service_invite/rest.py | 2 ++ tests/app/service_invite/test_service_invite_rest.py | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index 419713688..882c6e346 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -30,6 +30,7 @@ def _create_service_invite(invited_user, invite_link_host): template_id = current_app.config["INVITATION_EMAIL_TEMPLATE_ID"] template = dao_get_template_by_id(template_id) + service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"]) saved_notification = persist_notification( @@ -116,6 +117,7 @@ def resend_service_invite(service_id, invited_user_id): current_data = {k: v for k, v in invited_user_schema.dump(fetched).items()} update_dict = invited_user_schema.load(current_data) + save_invited_user(update_dict) _create_service_invite(fetched, current_app.config["ADMIN_BASE_URL"]) diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index fede2596f..e4ac9532c 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -203,7 +203,12 @@ def test_get_invited_user_by_service_when_user_does_not_belong_to_the_service( assert json_resp["result"] == "error" -def test_resend_expired_invite(client, sample_expired_user, mocker): +def test_resend_expired_invite( + client, + sample_expired_user, + invitation_email_template, + mocker, +): url = f"/service/{sample_expired_user.service_id}/invite/{sample_expired_user.id}/resend" mock_send = mocker.patch("app.service_invite.rest.send_notification_to_queue") mock_persist = mocker.patch("app.service_invite.rest.persist_notification") From d3a2d1da27b1c356e994486b0e019cc688774f4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 21:21:24 +0000 Subject: [PATCH 012/259] Bump moto from 4.2.11 to 4.2.12 Bumps [moto](https://github.com/getmoto/moto) from 4.2.11 to 4.2.12. - [Release notes](https://github.com/getmoto/moto/releases) - [Changelog](https://github.com/getmoto/moto/blob/master/CHANGELOG.md) - [Commits](https://github.com/getmoto/moto/compare/4.2.11...4.2.12) --- updated-dependencies: - dependency-name: moto dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 26 +++++++++++++------------- pyproject.toml | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/poetry.lock b/poetry.lock index acaa3dc71..00e589dac 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2299,13 +2299,13 @@ files = [ [[package]] name = "moto" -version = "4.2.11" +version = "4.2.12" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "moto-4.2.11-py2.py3-none-any.whl", hash = "sha256:58c12ab9ee69b6a5d1cddf83611ba4071508f07894317c57844b3ae6dc5bcd38"}, - {file = "moto-4.2.11.tar.gz", hash = "sha256:2da62d52eaa765dfe2762c920f0a88a58f3a09e04581c91db967d92faec848f1"}, + {file = "moto-4.2.12-py2.py3-none-any.whl", hash = "sha256:bdcad46e066a55b7d308a786e5dca863b3cba04c6239c6974135a48d1198b3ab"}, + {file = "moto-4.2.12.tar.gz", hash = "sha256:7c4d37f47becb4a0526b64df54484e988c10fde26861fc3b5c065bc78800cb59"}, ] [package.dependencies] @@ -2320,29 +2320,29 @@ werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.4.2)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] +all = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] apigateway = ["PyYAML (>=5.1)", "ecdsa (!=0.15)", "openapi-spec-validator (>=0.5.0)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"] apigatewayv2 = ["PyYAML (>=5.1)"] appsync = ["graphql-core"] awslambda = ["docker (>=3.0.0)"] batch = ["docker (>=3.0.0)"] -cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.4.2)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] +cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] cognitoidp = ["ecdsa (!=0.15)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"] ds = ["sshpubkeys (>=3.1.0)"] -dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.4.2)"] -dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.4.2)"] +dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"] +dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"] ebs = ["sshpubkeys (>=3.1.0)"] ec2 = ["sshpubkeys (>=3.1.0)"] efs = ["sshpubkeys (>=3.1.0)"] eks = ["sshpubkeys (>=3.1.0)"] glue = ["pyparsing (>=3.0.7)"] iotdata = ["jsondiff (>=1.1.2)"] -proxy = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.4.2)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] -resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.4.2)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "sshpubkeys (>=3.1.0)"] +proxy = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] +resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "sshpubkeys (>=3.1.0)"] route53resolver = ["sshpubkeys (>=3.1.0)"] -s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.4.2)"] -s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.4.2)"] -server = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.4.2)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] +s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.0)"] +s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.0)"] +server = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] ssm = ["PyYAML (>=5.1)"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] @@ -4698,4 +4698,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "e51ae652a803b69b395c41d227dcce26acbc19e202c763e99cf1a76357a2a462" +content-hash = "7cdee4f571ead903e7b83f4c6d903f34bff5dc54197336d47459fd9b0e3f7562" diff --git a/pyproject.toml b/pyproject.toml index 5d884b5da..4aaf10a16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ freezegun = "^1.3.1" honcho = "*" isort = "^5.13.2" jinja2-cli = {version = "==0.8.2", extras = ["yaml"]} -moto = "==4.2.11" +moto = "==4.2.12" pip-audit = "*" pre-commit = "^3.6.0" pytest = "^7.4.3" From e7879dcb572ebf2f099caa130aa58a6c2edc9021 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 03:39:22 +0000 Subject: [PATCH 013/259] Bump freezegun from 1.3.1 to 1.4.0 Bumps [freezegun](https://github.com/spulec/freezegun) from 1.3.1 to 1.4.0. - [Release notes](https://github.com/spulec/freezegun/releases) - [Changelog](https://github.com/spulec/freezegun/blob/master/CHANGELOG) - [Commits](https://github.com/spulec/freezegun/compare/1.3.1...1.4.0) --- updated-dependencies: - dependency-name: freezegun dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 00e589dac..80b1f06ad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1382,13 +1382,13 @@ files = [ [[package]] name = "freezegun" -version = "1.3.1" +version = "1.4.0" description = "Let your Python tests travel through time" optional = false python-versions = ">=3.7" files = [ - {file = "freezegun-1.3.1-py3-none-any.whl", hash = "sha256:065e77a12624d05531afa87ade12a0b9bdb53495c4573893252a055b545ce3ea"}, - {file = "freezegun-1.3.1.tar.gz", hash = "sha256:48984397b3b58ef5dfc645d6a304b0060f612bcecfdaaf45ce8aff0077a6cb6a"}, + {file = "freezegun-1.4.0-py3-none-any.whl", hash = "sha256:55e0fc3c84ebf0a96a5aa23ff8b53d70246479e9a68863f1fcac5a3e52f19dd6"}, + {file = "freezegun-1.4.0.tar.gz", hash = "sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b"}, ] [package.dependencies] @@ -4698,4 +4698,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "7cdee4f571ead903e7b83f4c6d903f34bff5dc54197336d47459fd9b0e3f7562" +content-hash = "3e73b8856280daf5f3132775206d7e73442497f6fdf959592be61ecd22108232" diff --git a/pyproject.toml b/pyproject.toml index 4aaf10a16..5d505bee7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ cloudfoundry-client = "*" exceptiongroup = "==1.2.0" flake8 = "^6.1.0" flake8-bugbear = "^23.12.2" -freezegun = "^1.3.1" +freezegun = "^1.4.0" honcho = "*" isort = "^5.13.2" jinja2-cli = {version = "==0.8.2", extras = ["yaml"]} From b0fc5f0a9871cb5899da20c16ab41a02e66af966 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 03:50:19 +0000 Subject: [PATCH 014/259] Bump lxml from 4.9.3 to 4.9.4 Bumps [lxml](https://github.com/lxml/lxml) from 4.9.3 to 4.9.4. - [Release notes](https://github.com/lxml/lxml/releases) - [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt) - [Commits](https://github.com/lxml/lxml/compare/lxml-4.9.3...lxml-4.9.4) --- updated-dependencies: - dependency-name: lxml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 190 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/poetry.lock b/poetry.lock index 80b1f06ad..416a79d08 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1977,110 +1977,110 @@ testing = ["black", "isort", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)", "twin [[package]] name = "lxml" -version = "4.9.3" +version = "4.9.4" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ - {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, - {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, - {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, - {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, - {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, - {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, - {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, - {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, - {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, - {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, - {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, - {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, - {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, - {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, - {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, - {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, - {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, - {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, - {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, - {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, - {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, - {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, - {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, - {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, - {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, + {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e214025e23db238805a600f1f37bf9f9a15413c7bf5f9d6ae194f84980c78722"}, + {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec53a09aee61d45e7dbe7e91252ff0491b6b5fee3d85b2d45b173d8ab453efc1"}, + {file = "lxml-4.9.4-cp27-cp27m-win32.whl", hash = "sha256:7d1d6c9e74c70ddf524e3c09d9dc0522aba9370708c2cb58680ea40174800013"}, + {file = "lxml-4.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:cb53669442895763e61df5c995f0e8361b61662f26c1b04ee82899c2789c8f69"}, + {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:647bfe88b1997d7ae8d45dabc7c868d8cb0c8412a6e730a7651050b8c7289cf2"}, + {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4d973729ce04784906a19108054e1fd476bc85279a403ea1a72fdb051c76fa48"}, + {file = "lxml-4.9.4-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:056a17eaaf3da87a05523472ae84246f87ac2f29a53306466c22e60282e54ff8"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aaa5c173a26960fe67daa69aa93d6d6a1cd714a6eb13802d4e4bd1d24a530644"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:647459b23594f370c1c01768edaa0ba0959afc39caeeb793b43158bb9bb6a663"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bdd9abccd0927673cffe601d2c6cdad1c9321bf3437a2f507d6b037ef91ea307"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:00e91573183ad273e242db5585b52670eddf92bacad095ce25c1e682da14ed91"}, + {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a602ed9bd2c7d85bd58592c28e101bd9ff9c718fbde06545a70945ffd5d11868"}, + {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:de362ac8bc962408ad8fae28f3967ce1a262b5d63ab8cefb42662566737f1dc7"}, + {file = "lxml-4.9.4-cp310-cp310-win32.whl", hash = "sha256:33714fcf5af4ff7e70a49731a7cc8fd9ce910b9ac194f66eaa18c3cc0a4c02be"}, + {file = "lxml-4.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:d3caa09e613ece43ac292fbed513a4bce170681a447d25ffcbc1b647d45a39c5"}, + {file = "lxml-4.9.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:359a8b09d712df27849e0bcb62c6a3404e780b274b0b7e4c39a88826d1926c28"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:43498ea734ccdfb92e1886dfedaebeb81178a241d39a79d5351ba2b671bff2b2"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4855161013dfb2b762e02b3f4d4a21cc7c6aec13c69e3bffbf5022b3e708dd97"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c71b5b860c5215fdbaa56f715bc218e45a98477f816b46cfde4a84d25b13274e"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9a2b5915c333e4364367140443b59f09feae42184459b913f0f41b9fed55794a"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d82411dbf4d3127b6cde7da0f9373e37ad3a43e89ef374965465928f01c2b979"}, + {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:273473d34462ae6e97c0f4e517bd1bf9588aa67a1d47d93f760a1282640e24ac"}, + {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:389d2b2e543b27962990ab529ac6720c3dded588cc6d0f6557eec153305a3622"}, + {file = "lxml-4.9.4-cp311-cp311-win32.whl", hash = "sha256:8aecb5a7f6f7f8fe9cac0bcadd39efaca8bbf8d1bf242e9f175cbe4c925116c3"}, + {file = "lxml-4.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:c7721a3ef41591341388bb2265395ce522aba52f969d33dacd822da8f018aff8"}, + {file = "lxml-4.9.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:dbcb2dc07308453db428a95a4d03259bd8caea97d7f0776842299f2d00c72fc8"}, + {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:01bf1df1db327e748dcb152d17389cf6d0a8c5d533ef9bab781e9d5037619229"}, + {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e8f9f93a23634cfafbad6e46ad7d09e0f4a25a2400e4a64b1b7b7c0fbaa06d9d"}, + {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f3f00a9061605725df1816f5713d10cd94636347ed651abdbc75828df302b20"}, + {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:953dd5481bd6252bd480d6ec431f61d7d87fdcbbb71b0d2bdcfc6ae00bb6fb10"}, + {file = "lxml-4.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:f1faee2a831fe249e1bae9cbc68d3cd8a30f7e37851deee4d7962b17c410dd56"}, + {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23d891e5bdc12e2e506e7d225d6aa929e0a0368c9916c1fddefab88166e98b20"}, + {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e96a1788f24d03e8d61679f9881a883ecdf9c445a38f9ae3f3f193ab6c591c66"}, + {file = "lxml-4.9.4-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:5557461f83bb7cc718bc9ee1f7156d50e31747e5b38d79cf40f79ab1447afd2d"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:fdb325b7fba1e2c40b9b1db407f85642e32404131c08480dd652110fc908561b"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d74d4a3c4b8f7a1f676cedf8e84bcc57705a6d7925e6daef7a1e54ae543a197"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ac7674d1638df129d9cb4503d20ffc3922bd463c865ef3cb412f2c926108e9a4"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:ddd92e18b783aeb86ad2132d84a4b795fc5ec612e3545c1b687e7747e66e2b53"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bd9ac6e44f2db368ef8986f3989a4cad3de4cd55dbdda536e253000c801bcc7"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bc354b1393dce46026ab13075f77b30e40b61b1a53e852e99d3cc5dd1af4bc85"}, + {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f836f39678cb47c9541f04d8ed4545719dc31ad850bf1832d6b4171e30d65d23"}, + {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:9c131447768ed7bc05a02553d939e7f0e807e533441901dd504e217b76307745"}, + {file = "lxml-4.9.4-cp36-cp36m-win32.whl", hash = "sha256:bafa65e3acae612a7799ada439bd202403414ebe23f52e5b17f6ffc2eb98c2be"}, + {file = "lxml-4.9.4-cp36-cp36m-win_amd64.whl", hash = "sha256:6197c3f3c0b960ad033b9b7d611db11285bb461fc6b802c1dd50d04ad715c225"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:7b378847a09d6bd46047f5f3599cdc64fcb4cc5a5a2dd0a2af610361fbe77b16"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:1343df4e2e6e51182aad12162b23b0a4b3fd77f17527a78c53f0f23573663545"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6dbdacf5752fbd78ccdb434698230c4f0f95df7dd956d5f205b5ed6911a1367c"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:506becdf2ecaebaf7f7995f776394fcc8bd8a78022772de66677c84fb02dd33d"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca8e44b5ba3edb682ea4e6185b49661fc22b230cf811b9c13963c9f982d1d964"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9d9d5726474cbbef279fd709008f91a49c4f758bec9c062dfbba88eab00e3ff9"}, + {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bbdd69e20fe2943b51e2841fc1e6a3c1de460d630f65bde12452d8c97209464d"}, + {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8671622256a0859f5089cbe0ce4693c2af407bc053dcc99aadff7f5310b4aa02"}, + {file = "lxml-4.9.4-cp37-cp37m-win32.whl", hash = "sha256:dd4fda67f5faaef4f9ee5383435048ee3e11ad996901225ad7615bc92245bc8e"}, + {file = "lxml-4.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6bee9c2e501d835f91460b2c904bc359f8433e96799f5c2ff20feebd9bb1e590"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:1f10f250430a4caf84115b1e0f23f3615566ca2369d1962f82bef40dd99cd81a"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b505f2bbff50d261176e67be24e8909e54b5d9d08b12d4946344066d66b3e43"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1449f9451cd53e0fd0a7ec2ff5ede4686add13ac7a7bfa6988ff6d75cff3ebe2"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:4ece9cca4cd1c8ba889bfa67eae7f21d0d1a2e715b4d5045395113361e8c533d"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59bb5979f9941c61e907ee571732219fa4774d5a18f3fa5ff2df963f5dfaa6bc"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b1980dbcaad634fe78e710c8587383e6e3f61dbe146bcbfd13a9c8ab2d7b1192"}, + {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9ae6c3363261021144121427b1552b29e7b59de9d6a75bf51e03bc072efb3c37"}, + {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bcee502c649fa6351b44bb014b98c09cb00982a475a1912a9881ca28ab4f9cd9"}, + {file = "lxml-4.9.4-cp38-cp38-win32.whl", hash = "sha256:a8edae5253efa75c2fc79a90068fe540b197d1c7ab5803b800fccfe240eed33c"}, + {file = "lxml-4.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:701847a7aaefef121c5c0d855b2affa5f9bd45196ef00266724a80e439220e46"}, + {file = "lxml-4.9.4-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:f610d980e3fccf4394ab3806de6065682982f3d27c12d4ce3ee46a8183d64a6a"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aa9b5abd07f71b081a33115d9758ef6077924082055005808f68feccb27616bd"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:365005e8b0718ea6d64b374423e870648ab47c3a905356ab6e5a5ff03962b9a9"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:16b9ec51cc2feab009e800f2c6327338d6ee4e752c76e95a35c4465e80390ccd"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a905affe76f1802edcac554e3ccf68188bea16546071d7583fb1b693f9cf756b"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fd814847901df6e8de13ce69b84c31fc9b3fb591224d6762d0b256d510cbf382"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91bbf398ac8bb7d65a5a52127407c05f75a18d7015a270fdd94bbcb04e65d573"}, + {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f99768232f036b4776ce419d3244a04fe83784bce871b16d2c2e984c7fcea847"}, + {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bb5bd6212eb0edfd1e8f254585290ea1dadc3687dd8fd5e2fd9a87c31915cdab"}, + {file = "lxml-4.9.4-cp39-cp39-win32.whl", hash = "sha256:88f7c383071981c74ec1998ba9b437659e4fd02a3c4a4d3efc16774eb108d0ec"}, + {file = "lxml-4.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:936e8880cc00f839aa4173f94466a8406a96ddce814651075f95837316369899"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:f6c35b2f87c004270fa2e703b872fcc984d714d430b305145c39d53074e1ffe0"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:606d445feeb0856c2b424405236a01c71af7c97e5fe42fbc778634faef2b47e4"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1bdcbebd4e13446a14de4dd1825f1e778e099f17f79718b4aeaf2403624b0f7"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0a08c89b23117049ba171bf51d2f9c5f3abf507d65d016d6e0fa2f37e18c0fc5"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:232fd30903d3123be4c435fb5159938c6225ee8607b635a4d3fca847003134ba"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:231142459d32779b209aa4b4d460b175cadd604fed856f25c1571a9d78114771"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:520486f27f1d4ce9654154b4494cf9307b495527f3a2908ad4cb48e4f7ed7ef7"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:562778586949be7e0d7435fcb24aca4810913771f845d99145a6cee64d5b67ca"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a9e7c6d89c77bb2770c9491d988f26a4b161d05c8ca58f63fb1f1b6b9a74be45"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:786d6b57026e7e04d184313c1359ac3d68002c33e4b1042ca58c362f1d09ff58"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95ae6c5a196e2f239150aa4a479967351df7f44800c93e5a975ec726fef005e2"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:9b556596c49fa1232b0fff4b0e69b9d4083a502e60e404b44341e2f8fb7187f5"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:cc02c06e9e320869d7d1bd323df6dd4281e78ac2e7f8526835d3d48c69060683"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:857d6565f9aa3464764c2cb6a2e3c2e75e1970e877c188f4aeae45954a314e0c"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c42ae7e010d7d6bc51875d768110c10e8a59494855c3d4c348b068f5fb81fdcd"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f10250bb190fb0742e3e1958dd5c100524c2cc5096c67c8da51233f7448dc137"}, + {file = "lxml-4.9.4.tar.gz", hash = "sha256:b1541e50b78e15fa06a2670157a1962ef06591d4c998b998047fff5e3236880e"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.35)"] +source = ["Cython (==0.29.37)"] [[package]] name = "mako" @@ -4698,4 +4698,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "3e73b8856280daf5f3132775206d7e73442497f6fdf959592be61ecd22108232" +content-hash = "af1a5408fc93958169259e70ac8b7ba93e712ccee1433bff43d6ed4937b8642c" diff --git a/pyproject.toml b/pyproject.toml index 5d505bee7..6d34041d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ flask-sqlalchemy = "==3.0.5" gunicorn = {version = "==21.2.0", extras = ["eventlet"]} iso8601 = "==2.1.0" jsonschema = {version = "==4.20.0", extras = ["format"]} -lxml = "==4.9.3" +lxml = "==4.9.4" marshmallow = "==3.20.1" marshmallow-sqlalchemy = "==0.29.0" newrelic = "*" From f66685cb766ba970041e79ff35ecc70802eca2fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 04:06:57 +0000 Subject: [PATCH 015/259] Bump pip-audit from 2.6.1 to 2.6.2 Bumps [pip-audit](https://github.com/pypa/pip-audit) from 2.6.1 to 2.6.2. - [Release notes](https://github.com/pypa/pip-audit/releases) - [Changelog](https://github.com/pypa/pip-audit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pypa/pip-audit/compare/v2.6.1...v2.6.2) --- updated-dependencies: - dependency-name: pip-audit dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 416a79d08..8fee08534 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2802,18 +2802,18 @@ pip = "*" [[package]] name = "pip-audit" -version = "2.6.1" +version = "2.6.2" description = "A tool for scanning Python environments for known vulnerabilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pip_audit-2.6.1-py3-none-any.whl", hash = "sha256:8a32bb67dca6a76c244bbccebed562c0f6957b1fc9d34d59a9ec0fbff0672ae0"}, - {file = "pip_audit-2.6.1.tar.gz", hash = "sha256:55c9bd18b0fe3959f73397db08d257c6012ad1826825e3d74cb6c3f79e95c245"}, + {file = "pip_audit-2.6.2-py3-none-any.whl", hash = "sha256:ac3a4b6e977ef2c574aa8d19a5d71d12201bdb65bba2d67d9df49f53f0be5e7d"}, + {file = "pip_audit-2.6.2.tar.gz", hash = "sha256:0bbd023a199a104b29f949f063a872d41113b5a9048285666820fa35a76a7794"}, ] [package.dependencies] CacheControl = {version = ">=0.13.0", extras = ["filecache"]} -cyclonedx-python-lib = ">=4.0,<5.0" +cyclonedx-python-lib = ">=4,<6" html5lib = ">=1.1" packaging = ">=23.0.0" pip-api = ">=0.0.28" @@ -2825,8 +2825,8 @@ toml = ">=0.10" [package.extras] dev = ["build", "bump (>=1.3.2)", "pip-audit[doc,lint,test]"] doc = ["pdoc"] -lint = ["black (>=22.3.0)", "interrogate", "isort", "mypy", "ruff (<0.0.281)", "types-html5lib", "types-requests", "types-toml"] -test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"] +lint = ["interrogate", "mypy", "ruff (<0.1.9)", "types-html5lib", "types-requests", "types-toml"] +test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"] [[package]] name = "pip-requirements-parser" From ff70dc9b8bc37fa56cdc1eb2c165efc7d0181e0d Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 20 Dec 2023 08:15:19 -0500 Subject: [PATCH 016/259] Filtering the rest endpoint for resending invites by status = pending now. Signed-off-by: Cliff Hill --- app/service_invite/rest.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index 882c6e346..19102c12b 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -14,7 +14,13 @@ from app.dao.invited_user_dao import ( ) from app.dao.templates_dao import dao_get_template_by_id from app.errors import InvalidRequest, register_errors -from app.models import EMAIL_TYPE, INVITE_PENDING, KEY_TYPE_NORMAL, Service +from app.models import ( + EMAIL_TYPE, + INVITE_EXPIRED, + INVITE_PENDING, + KEY_TYPE_NORMAL, + Service, +) from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -109,7 +115,9 @@ def resend_service_invite(service_id, invited_user_id): This ignores the POST data entirely. """ fetched = get_invited_user_by_service_and_id( - service_id=service_id, invited_user_id=invited_user_id + service_id=service_id, + invited_user_id=invited_user_id, + status=INVITE_EXPIRED, ) fetched.created_at = datetime.utcnow() From affc5e7aff5a8bbc562dba5b154e8bca0cabb222 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 20 Dec 2023 09:51:43 -0500 Subject: [PATCH 017/259] Fixing stuff. Signed-off-by: Cliff Hill --- app/dao/invited_user_dao.py | 8 ++++++++ app/service_invite/rest.py | 12 +++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/dao/invited_user_dao.py b/app/dao/invited_user_dao.py index ad4ab0a65..2e807c069 100644 --- a/app/dao/invited_user_dao.py +++ b/app/dao/invited_user_dao.py @@ -16,6 +16,14 @@ def get_invited_user_by_service_and_id(service_id, invited_user_id): ).one() +def get_expired_invite_by_service_and_id(service_id, invited_user_id): + return InvitedUser.query.filter( + InvitedUser.service_id == service_id, + InvitedUser.id == invited_user_id, + InvitedUser.status == INVITE_EXPIRED, + ).one() + + def get_invited_user_by_id(invited_user_id): return InvitedUser.query.filter(InvitedUser.id == invited_user_id).one() diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index 19102c12b..e18d158ac 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -6,6 +6,7 @@ from notifications_utils.url_safe_token import check_token, generate_token from app.config import QueueNames from app.dao.invited_user_dao import ( + get_expired_invite_by_service_and_id, get_expired_invited_users_for_service, get_invited_user_by_id, get_invited_user_by_service_and_id, @@ -14,13 +15,7 @@ from app.dao.invited_user_dao import ( ) from app.dao.templates_dao import dao_get_template_by_id from app.errors import InvalidRequest, register_errors -from app.models import ( - EMAIL_TYPE, - INVITE_EXPIRED, - INVITE_PENDING, - KEY_TYPE_NORMAL, - Service, -) +from app.models import EMAIL_TYPE, INVITE_PENDING, KEY_TYPE_NORMAL, Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -114,10 +109,9 @@ def resend_service_invite(service_id, invited_user_id): Note: This ignores the POST data entirely. """ - fetched = get_invited_user_by_service_and_id( + fetched = get_expired_invite_by_service_and_id( service_id=service_id, invited_user_id=invited_user_id, - status=INVITE_EXPIRED, ) fetched.created_at = datetime.utcnow() From 64de545cb46015fba4dbd6ae00544bed431942ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 21:12:51 +0000 Subject: [PATCH 018/259] Bump alembic from 1.13.0 to 1.13.1 Bumps [alembic](https://github.com/sqlalchemy/alembic) from 1.13.0 to 1.13.1. - [Release notes](https://github.com/sqlalchemy/alembic/releases) - [Changelog](https://github.com/sqlalchemy/alembic/blob/main/CHANGES) - [Commits](https://github.com/sqlalchemy/alembic/commits) --- updated-dependencies: - dependency-name: alembic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8fee08534..92c62d230 100644 --- a/poetry.lock +++ b/poetry.lock @@ -112,13 +112,13 @@ frozenlist = ">=1.1.0" [[package]] name = "alembic" -version = "1.13.0" +version = "1.13.1" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.8" files = [ - {file = "alembic-1.13.0-py3-none-any.whl", hash = "sha256:a23974ea301c3ee52705db809c7413cecd165290c6679b9998dd6c74342ca23a"}, - {file = "alembic-1.13.0.tar.gz", hash = "sha256:ab4b3b94d2e1e5f81e34be8a9b7b7575fc9dd5398fccb0bef351ec9b14872623"}, + {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"}, + {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"}, ] [package.dependencies] @@ -4698,4 +4698,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "af1a5408fc93958169259e70ac8b7ba93e712ccee1433bff43d6ed4937b8642c" +content-hash = "7e34599fef27609d767d36077cc77259086412f52099cd1a4d43f9f991882b9a" diff --git a/pyproject.toml b/pyproject.toml index 6d34041d6..240494682 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ readme = "README.md" [tool.poetry.dependencies] python = ">=3.9,<3.12" -alembic = "==1.13.0" +alembic = "==1.13.1" amqp = "==5.2.0" beautifulsoup4 = "==4.12.2" boto3 = "^1.29.6" From ff4ce0eb5403dbf986793d444368574de026139f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 13:27:02 +0000 Subject: [PATCH 019/259] Bump eventlet from 0.33.3 to 0.34.1 Bumps [eventlet](https://github.com/eventlet/eventlet) from 0.33.3 to 0.34.1. - [Changelog](https://github.com/eventlet/eventlet/blob/master/NEWS) - [Commits](https://github.com/eventlet/eventlet/compare/v0.33.3...v0.34.1) --- updated-dependencies: - dependency-name: eventlet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 16 ++++++++++------ pyproject.toml | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 92c62d230..5d3bea10b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1152,20 +1152,23 @@ pgp = ["gpg"] [[package]] name = "eventlet" -version = "0.33.3" +version = "0.34.1" description = "Highly concurrent networking library" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "eventlet-0.33.3-py2.py3-none-any.whl", hash = "sha256:e43b9ae05ba4bb477a10307699c9aff7ff86121b2640f9184d29059f5a687df8"}, - {file = "eventlet-0.33.3.tar.gz", hash = "sha256:722803e7eadff295347539da363d68ae155b8b26ae6a634474d0a920be73cfda"}, + {file = "eventlet-0.34.1-py3-none-any.whl", hash = "sha256:70370e61abb5488afc2a80391f3b32f2d5b224871071d54bc66d6808161fd055"}, + {file = "eventlet-0.34.1.tar.gz", hash = "sha256:0087763f3ae18a571a5cc60fdd06ceb02fdb9f006693e9ee1e8e8044ae3b470b"}, ] [package.dependencies] dnspython = ">=1.15.0" -greenlet = ">=0.3" +greenlet = ">=1.0" six = ">=1.10.0" +[package.extras] +dev = ["black", "build", "commitizen", "isort", "pip-tools", "pre-commit", "twine"] + [[package]] name = "exceptiongroup" version = "1.2.0" @@ -2012,6 +2015,7 @@ files = [ {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e8f9f93a23634cfafbad6e46ad7d09e0f4a25a2400e4a64b1b7b7c0fbaa06d9d"}, {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f3f00a9061605725df1816f5713d10cd94636347ed651abdbc75828df302b20"}, {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:953dd5481bd6252bd480d6ec431f61d7d87fdcbbb71b0d2bdcfc6ae00bb6fb10"}, + {file = "lxml-4.9.4-cp312-cp312-win32.whl", hash = "sha256:266f655d1baff9c47b52f529b5f6bec33f66042f65f7c56adde3fcf2ed62ae8b"}, {file = "lxml-4.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:f1faee2a831fe249e1bae9cbc68d3cd8a30f7e37851deee4d7962b17c410dd56"}, {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23d891e5bdc12e2e506e7d225d6aa929e0a0368c9916c1fddefab88166e98b20"}, {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e96a1788f24d03e8d61679f9881a883ecdf9c445a38f9ae3f3f193ab6c591c66"}, @@ -4698,4 +4702,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "7e34599fef27609d767d36077cc77259086412f52099cd1a4d43f9f991882b9a" +content-hash = "8c3199bf111319374009c2e509353526fed63f39906874d705d3510226861720" diff --git a/pyproject.toml b/pyproject.toml index 240494682..27dd64bf3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ click-didyoumean = "==0.3.0" click-plugins = "==1.1.1" click-repl = "==0.3.0" deprecated = "==1.2.14" -eventlet = "==0.33.3" +eventlet = "==0.34.1" flask = "~=2.3" flask-bcrypt = "==1.0.1" flask-marshmallow = "==0.14.0" From 75631b94f5cf76a183b5e68db9b595857405ac40 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Thu, 21 Dec 2023 10:16:12 -0500 Subject: [PATCH 020/259] Downgrade eventlet to 0.33.3 This changeset reverts a recent Dependabot update for eventlet back to 0.33.3, the last known working version for deployment. The 0.34.1 release changed a bunch of deployment related things, including an overhaul of the eventlet/__init__.py file that removed the __version__ attribute. This is checked when the deployment takes place in a Cloud Foundry environment (e.g., cloud.gov), ultimately by something internal within gunicorn. It is unclear if the breakdown is with gunicorn itself or with the way that the Cloud Foundry Python buildpack operates during deployment, but it seems that either we will have to wait for an eventlet bug fix and/or updates to gunicorn and the Cloud Foundry Python buildack before we can update eventlet itself again. Signed-off-by: Carlo Costino --- poetry.lock | 25 ++++++------------------- pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5d3bea10b..75626ea69 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1152,23 +1152,20 @@ pgp = ["gpg"] [[package]] name = "eventlet" -version = "0.34.1" +version = "0.33.3" description = "Highly concurrent networking library" optional = false -python-versions = ">=3.7" +python-versions = "*" files = [ - {file = "eventlet-0.34.1-py3-none-any.whl", hash = "sha256:70370e61abb5488afc2a80391f3b32f2d5b224871071d54bc66d6808161fd055"}, - {file = "eventlet-0.34.1.tar.gz", hash = "sha256:0087763f3ae18a571a5cc60fdd06ceb02fdb9f006693e9ee1e8e8044ae3b470b"}, + {file = "eventlet-0.33.3-py2.py3-none-any.whl", hash = "sha256:e43b9ae05ba4bb477a10307699c9aff7ff86121b2640f9184d29059f5a687df8"}, + {file = "eventlet-0.33.3.tar.gz", hash = "sha256:722803e7eadff295347539da363d68ae155b8b26ae6a634474d0a920be73cfda"}, ] [package.dependencies] dnspython = ">=1.15.0" -greenlet = ">=1.0" +greenlet = ">=0.3" six = ">=1.10.0" -[package.extras] -dev = ["black", "build", "commitizen", "isort", "pip-tools", "pre-commit", "twine"] - [[package]] name = "exceptiongroup" version = "1.2.0" @@ -2173,16 +2170,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -4702,4 +4689,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "8c3199bf111319374009c2e509353526fed63f39906874d705d3510226861720" +content-hash = "7e34599fef27609d767d36077cc77259086412f52099cd1a4d43f9f991882b9a" diff --git a/pyproject.toml b/pyproject.toml index 27dd64bf3..240494682 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ click-didyoumean = "==0.3.0" click-plugins = "==1.1.1" click-repl = "==0.3.0" deprecated = "==1.2.14" -eventlet = "==0.34.1" +eventlet = "==0.33.3" flask = "~=2.3" flask-bcrypt = "==1.0.1" flask-marshmallow = "==0.14.0" From 0aeac76b4435db8bbab22faca2faab03f250932d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 21:56:48 +0000 Subject: [PATCH 021/259] Bump setuptools from 69.0.2 to 69.0.3 Bumps [setuptools](https://github.com/pypa/setuptools) from 69.0.2 to 69.0.3. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v69.0.2...v69.0.3) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 18 ++++++++++++++---- pyproject.toml | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 75626ea69..e34671281 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2170,6 +2170,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -3969,13 +3979,13 @@ jeepney = ">=0.6" [[package]] name = "setuptools" -version = "69.0.2" +version = "69.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, - {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, ] [package.extras] @@ -4689,4 +4699,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "7e34599fef27609d767d36077cc77259086412f52099cd1a4d43f9f991882b9a" +content-hash = "2bb8f086937719137ba0c67fe3e57ea3ba093a4fba9d1df50eaf781f7144a776" diff --git a/pyproject.toml b/pyproject.toml index 240494682..f57ed5c06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,7 @@ pytest-cov = "^4.1.0" pytest-xdist = "^3.5.0" radon = "^6.0.1" requests-mock = "^1.11.0" -setuptools = "^69.0.2" +setuptools = "^69.0.3" vulture = "^2.10" From 8461b336ca4e4288ad5778e9806424a555c64dc8 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 29 Dec 2023 09:56:17 -0500 Subject: [PATCH 022/259] Fixing bug. Signed-off-by: Cliff Hill --- app/clients/cloudwatch/aws_cloudwatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/clients/cloudwatch/aws_cloudwatch.py b/app/clients/cloudwatch/aws_cloudwatch.py index 9c1d1e61c..80ba88f87 100644 --- a/app/clients/cloudwatch/aws_cloudwatch.py +++ b/app/clients/cloudwatch/aws_cloudwatch.py @@ -127,7 +127,7 @@ class AwsCloudwatchClient(Client): return ( "failure", message["delivery"]["providerResponse"], - message["delivery"]["phoneCarrier"], + message["delivery"].get("phoneCarrier", "Unknown Carrier"), ) if time_now > (created_at + timedelta(hours=3)): From 976d57b764e52becbdf3910eadf25eaec6cdd981 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 29 Dec 2023 10:02:34 -0500 Subject: [PATCH 023/259] Make test did things. Signed-off-by: Cliff Hill --- migrations/versions/0409_fix_service_name.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/migrations/versions/0409_fix_service_name.py b/migrations/versions/0409_fix_service_name.py index 04c6ba359..6c90b65ce 100644 --- a/migrations/versions/0409_fix_service_name.py +++ b/migrations/versions/0409_fix_service_name.py @@ -27,7 +27,8 @@ def upgrade(): # select_by_val = service_id input_params = {"service_id": service_id} conn.execute( - text("update services set name='Notify.gov' where id =:service_id"), input_params + text("update services set name='Notify.gov' where id =:service_id"), + input_params, ) # table_name = 'services_history' From 3ff6d386601620791ebc8aa9639e4a831c70451d Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 29 Dec 2023 18:11:55 -0500 Subject: [PATCH 024/259] Making the new pinpoint client. Signed-off-by: Cliff Hill --- app/clients/__init__.py | 4 +++- app/clients/email/__init__.py | 5 ++++- app/clients/pinpoint/__init__.py | 0 app/clients/sms/__init__.py | 5 +++++ migrations/versions/0409_fix_service_name.py | 3 ++- 5 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 app/clients/pinpoint/__init__.py diff --git a/app/clients/__init__.py b/app/clients/__init__.py index c6b620517..7ab453318 100644 --- a/app/clients/__init__.py +++ b/app/clients/__init__.py @@ -1,3 +1,5 @@ +from typing import Protocol + from botocore.config import Config AWS_CLIENT_CONFIG = Config( @@ -22,7 +24,7 @@ class ClientException(Exception): pass -class Client(object): +class Client(Protocol): """ Base client for sending notifications. """ diff --git a/app/clients/email/__init__.py b/app/clients/email/__init__.py index 7a2f710a3..1855308b5 100644 --- a/app/clients/email/__init__.py +++ b/app/clients/email/__init__.py @@ -1,3 +1,5 @@ +from abc import abstractmethod, abstractproperty + from app.clients import Client, ClientException @@ -27,9 +29,10 @@ class EmailClient(Client): Base Email client for sending emails. """ + @abstractmethod def send_email(self, *args, **kwargs): raise NotImplementedError("TODO Need to implement.") - @property + @abstractproperty def name(self): raise NotImplementedError("TODO Need to implement.") diff --git a/app/clients/pinpoint/__init__.py b/app/clients/pinpoint/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/clients/sms/__init__.py b/app/clients/sms/__init__.py index 2e7f27cdc..cd4ca57a7 100644 --- a/app/clients/sms/__init__.py +++ b/app/clients/sms/__init__.py @@ -1,3 +1,5 @@ +from abc import abstractmethod + from app.clients import Client, ClientException @@ -18,11 +20,14 @@ class SmsClient(Client): Base Sms client for sending smss. """ + @abstractmethod def init_app(self, *args, **kwargs): raise NotImplementedError("TODO Need to implement.") + @abstractmethod def send_sms(self, *args, **kwargs): raise NotImplementedError("TODO Need to implement.") + @abstractmethod def get_name(self): raise NotImplementedError("TODO Need to implement.") diff --git a/migrations/versions/0409_fix_service_name.py b/migrations/versions/0409_fix_service_name.py index 04c6ba359..6c90b65ce 100644 --- a/migrations/versions/0409_fix_service_name.py +++ b/migrations/versions/0409_fix_service_name.py @@ -27,7 +27,8 @@ def upgrade(): # select_by_val = service_id input_params = {"service_id": service_id} conn.execute( - text("update services set name='Notify.gov' where id =:service_id"), input_params + text("update services set name='Notify.gov' where id =:service_id"), + input_params, ) # table_name = 'services_history' From 904c1c4a50149d3dbed50ef39b1931c7b0a73b3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 15:57:09 +0000 Subject: [PATCH 025/259] Bump black from 23.12.0 to 23.12.1 Bumps [black](https://github.com/psf/black) from 23.12.0 to 23.12.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/compare/23.12.0...23.12.1) --- updated-dependencies: - dependency-name: black dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 48 ++++++++++++++++++++++++------------------------ pyproject.toml | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/poetry.lock b/poetry.lock index e34671281..8fb7fe912 100644 --- a/poetry.lock +++ b/poetry.lock @@ -309,33 +309,33 @@ files = [ [[package]] name = "black" -version = "23.12.0" +version = "23.12.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67f19562d367468ab59bd6c36a72b2c84bc2f16b59788690e02bbcb140a77175"}, - {file = "black-23.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbd75d9f28a7283b7426160ca21c5bd640ca7cd8ef6630b4754b6df9e2da8462"}, - {file = "black-23.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:593596f699ca2dcbbbdfa59fcda7d8ad6604370c10228223cd6cf6ce1ce7ed7e"}, - {file = "black-23.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:12d5f10cce8dc27202e9a252acd1c9a426c83f95496c959406c96b785a92bb7d"}, - {file = "black-23.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e73c5e3d37e5a3513d16b33305713237a234396ae56769b839d7c40759b8a41c"}, - {file = "black-23.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ba09cae1657c4f8a8c9ff6cfd4a6baaf915bb4ef7d03acffe6a2f6585fa1bd01"}, - {file = "black-23.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace64c1a349c162d6da3cef91e3b0e78c4fc596ffde9413efa0525456148873d"}, - {file = "black-23.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:72db37a2266b16d256b3ea88b9affcdd5c41a74db551ec3dd4609a59c17d25bf"}, - {file = "black-23.12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fdf6f23c83078a6c8da2442f4d4eeb19c28ac2a6416da7671b72f0295c4a697b"}, - {file = "black-23.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39dda060b9b395a6b7bf9c5db28ac87b3c3f48d4fdff470fa8a94ab8271da47e"}, - {file = "black-23.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7231670266ca5191a76cb838185d9be59cfa4f5dd401b7c1c70b993c58f6b1b5"}, - {file = "black-23.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:193946e634e80bfb3aec41830f5d7431f8dd5b20d11d89be14b84a97c6b8bc75"}, - {file = "black-23.12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcf91b01ddd91a2fed9a8006d7baa94ccefe7e518556470cf40213bd3d44bbbc"}, - {file = "black-23.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:996650a89fe5892714ea4ea87bc45e41a59a1e01675c42c433a35b490e5aa3f0"}, - {file = "black-23.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdbff34c487239a63d86db0c9385b27cdd68b1bfa4e706aa74bb94a435403672"}, - {file = "black-23.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:97af22278043a6a1272daca10a6f4d36c04dfa77e61cbaaf4482e08f3640e9f0"}, - {file = "black-23.12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ead25c273adfad1095a8ad32afdb8304933efba56e3c1d31b0fee4143a1e424a"}, - {file = "black-23.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c71048345bdbced456cddf1622832276d98a710196b842407840ae8055ade6ee"}, - {file = "black-23.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a832b6e00eef2c13b3239d514ea3b7d5cc3eaa03d0474eedcbbda59441ba5d"}, - {file = "black-23.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:6a82a711d13e61840fb11a6dfecc7287f2424f1ca34765e70c909a35ffa7fb95"}, - {file = "black-23.12.0-py3-none-any.whl", hash = "sha256:a7c07db8200b5315dc07e331dda4d889a56f6bf4db6a9c2a526fa3166a81614f"}, - {file = "black-23.12.0.tar.gz", hash = "sha256:330a327b422aca0634ecd115985c1c7fd7bdb5b5a2ef8aa9888a82e2ebe9437a"}, + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, ] [package.dependencies] @@ -4699,4 +4699,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "2bb8f086937719137ba0c67fe3e57ea3ba093a4fba9d1df50eaf781f7144a776" +content-hash = "7dbf83fd191db81b3f332a5a9c5c91d6f3c83751f0e36500d724f9e6c671c573" diff --git a/pyproject.toml b/pyproject.toml index f57ed5c06..f9d111171 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,7 @@ werkzeug = "^3.0.1" [tool.poetry.group.dev.dependencies] awscli = "^1.29.74" bandit = "*" -black = "^23.12.0" +black = "^23.12.1" cloudfoundry-client = "*" exceptiongroup = "==1.2.0" flake8 = "^6.1.0" From e92f2356243e8ca24d9046d9dadab21d10200702 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 16:47:32 +0000 Subject: [PATCH 026/259] Bump tj-actions/changed-files from 34 to 41 in /.github/workflows Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 34 to 41. - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/v34...v41) --- updated-dependencies: - dependency-name: tj-actions/changed-files dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/deploy-demo.yml | 4 ++-- .github/workflows/deploy-prod.yml | 4 ++-- .github/workflows/deploy.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml index 078f66ed7..b634871f3 100644 --- a/.github/workflows/deploy-demo.yml +++ b/.github/workflows/deploy-demo.yml @@ -18,7 +18,7 @@ jobs: - name: Check for changes to Terraform id: changed-terraform-files - uses: tj-actions/changed-files@v34 + uses: tj-actions/changed-files@v41 with: files: | terraform/demo @@ -73,7 +73,7 @@ jobs: - name: Check for changes to egress config id: changed-egress-config - uses: tj-actions/changed-files@v34 + uses: tj-actions/changed-files@v41 with: files: | deploy-config/egress_proxy/notify-api-demo.*.acl diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 4ef3ffcba..ac3846497 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -22,7 +22,7 @@ jobs: - name: Check for changes to Terraform id: changed-terraform-files - uses: tj-actions/changed-files@v34 + uses: tj-actions/changed-files@v41 with: files: | terraform/production @@ -77,7 +77,7 @@ jobs: - name: Check for changes to egress config id: changed-egress-config - uses: tj-actions/changed-files@v34 + uses: tj-actions/changed-files@v41 with: files: | deploy-config/egress_proxy/notify-api-production.*.acl diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e0df02a6b..7e8d2bc9e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -23,7 +23,7 @@ jobs: - name: Check for changes to Terraform id: changed-terraform-files - uses: tj-actions/changed-files@v34 + uses: tj-actions/changed-files@v41 with: files: | terraform/staging @@ -78,7 +78,7 @@ jobs: - name: Check for changes to egress config id: changed-egress-config - uses: tj-actions/changed-files@v34 + uses: tj-actions/changed-files@v41 with: files: | deploy-config/egress_proxy/notify-api-staging.*.acl From c33bc0974d0fa78e20859d93e9c4698cbe0ae6f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 21:34:31 +0000 Subject: [PATCH 027/259] Bump pytest from 7.4.3 to 7.4.4 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.3 to 7.4.4. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.4.3...7.4.4) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8fb7fe912..7bf19aeaf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3244,13 +3244,13 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -4699,4 +4699,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "7dbf83fd191db81b3f332a5a9c5c91d6f3c83751f0e36500d724f9e6c671c573" +content-hash = "723a71177888e1745d813239e675eb39671cfe0269336efc05444cda55b8f774" diff --git a/pyproject.toml b/pyproject.toml index f9d111171..536e40c5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ jinja2-cli = {version = "==0.8.2", extras = ["yaml"]} moto = "==4.2.12" pip-audit = "*" pre-commit = "^3.6.0" -pytest = "^7.4.3" +pytest = "^7.4.4" pytest-env = "^1.1.3" pytest-mock = "^3.12.0" pytest-cov = "^4.1.0" From 9a534c883153c936fcaa5a6a2910c08c16a8f634 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:10:49 +0000 Subject: [PATCH 028/259] Bump lxml from 4.9.4 to 5.0.0 Bumps [lxml](https://github.com/lxml/lxml) from 4.9.4 to 5.0.0. - [Release notes](https://github.com/lxml/lxml/releases) - [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt) - [Commits](https://github.com/lxml/lxml/compare/lxml-4.9.4...lxml-5.0.0) --- updated-dependencies: - dependency-name: lxml dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- poetry.lock | 187 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 92 insertions(+), 97 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7bf19aeaf..f42b6cb1c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1977,111 +1977,106 @@ testing = ["black", "isort", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)", "twin [[package]] name = "lxml" -version = "4.9.4" +version = "5.0.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ - {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e214025e23db238805a600f1f37bf9f9a15413c7bf5f9d6ae194f84980c78722"}, - {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec53a09aee61d45e7dbe7e91252ff0491b6b5fee3d85b2d45b173d8ab453efc1"}, - {file = "lxml-4.9.4-cp27-cp27m-win32.whl", hash = "sha256:7d1d6c9e74c70ddf524e3c09d9dc0522aba9370708c2cb58680ea40174800013"}, - {file = "lxml-4.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:cb53669442895763e61df5c995f0e8361b61662f26c1b04ee82899c2789c8f69"}, - {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:647bfe88b1997d7ae8d45dabc7c868d8cb0c8412a6e730a7651050b8c7289cf2"}, - {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4d973729ce04784906a19108054e1fd476bc85279a403ea1a72fdb051c76fa48"}, - {file = "lxml-4.9.4-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:056a17eaaf3da87a05523472ae84246f87ac2f29a53306466c22e60282e54ff8"}, - {file = "lxml-4.9.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aaa5c173a26960fe67daa69aa93d6d6a1cd714a6eb13802d4e4bd1d24a530644"}, - {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:647459b23594f370c1c01768edaa0ba0959afc39caeeb793b43158bb9bb6a663"}, - {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bdd9abccd0927673cffe601d2c6cdad1c9321bf3437a2f507d6b037ef91ea307"}, - {file = "lxml-4.9.4-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:00e91573183ad273e242db5585b52670eddf92bacad095ce25c1e682da14ed91"}, - {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a602ed9bd2c7d85bd58592c28e101bd9ff9c718fbde06545a70945ffd5d11868"}, - {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:de362ac8bc962408ad8fae28f3967ce1a262b5d63ab8cefb42662566737f1dc7"}, - {file = "lxml-4.9.4-cp310-cp310-win32.whl", hash = "sha256:33714fcf5af4ff7e70a49731a7cc8fd9ce910b9ac194f66eaa18c3cc0a4c02be"}, - {file = "lxml-4.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:d3caa09e613ece43ac292fbed513a4bce170681a447d25ffcbc1b647d45a39c5"}, - {file = "lxml-4.9.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:359a8b09d712df27849e0bcb62c6a3404e780b274b0b7e4c39a88826d1926c28"}, - {file = "lxml-4.9.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:43498ea734ccdfb92e1886dfedaebeb81178a241d39a79d5351ba2b671bff2b2"}, - {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4855161013dfb2b762e02b3f4d4a21cc7c6aec13c69e3bffbf5022b3e708dd97"}, - {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c71b5b860c5215fdbaa56f715bc218e45a98477f816b46cfde4a84d25b13274e"}, - {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9a2b5915c333e4364367140443b59f09feae42184459b913f0f41b9fed55794a"}, - {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d82411dbf4d3127b6cde7da0f9373e37ad3a43e89ef374965465928f01c2b979"}, - {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:273473d34462ae6e97c0f4e517bd1bf9588aa67a1d47d93f760a1282640e24ac"}, - {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:389d2b2e543b27962990ab529ac6720c3dded588cc6d0f6557eec153305a3622"}, - {file = "lxml-4.9.4-cp311-cp311-win32.whl", hash = "sha256:8aecb5a7f6f7f8fe9cac0bcadd39efaca8bbf8d1bf242e9f175cbe4c925116c3"}, - {file = "lxml-4.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:c7721a3ef41591341388bb2265395ce522aba52f969d33dacd822da8f018aff8"}, - {file = "lxml-4.9.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:dbcb2dc07308453db428a95a4d03259bd8caea97d7f0776842299f2d00c72fc8"}, - {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:01bf1df1db327e748dcb152d17389cf6d0a8c5d533ef9bab781e9d5037619229"}, - {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e8f9f93a23634cfafbad6e46ad7d09e0f4a25a2400e4a64b1b7b7c0fbaa06d9d"}, - {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f3f00a9061605725df1816f5713d10cd94636347ed651abdbc75828df302b20"}, - {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:953dd5481bd6252bd480d6ec431f61d7d87fdcbbb71b0d2bdcfc6ae00bb6fb10"}, - {file = "lxml-4.9.4-cp312-cp312-win32.whl", hash = "sha256:266f655d1baff9c47b52f529b5f6bec33f66042f65f7c56adde3fcf2ed62ae8b"}, - {file = "lxml-4.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:f1faee2a831fe249e1bae9cbc68d3cd8a30f7e37851deee4d7962b17c410dd56"}, - {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23d891e5bdc12e2e506e7d225d6aa929e0a0368c9916c1fddefab88166e98b20"}, - {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e96a1788f24d03e8d61679f9881a883ecdf9c445a38f9ae3f3f193ab6c591c66"}, - {file = "lxml-4.9.4-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:5557461f83bb7cc718bc9ee1f7156d50e31747e5b38d79cf40f79ab1447afd2d"}, - {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:fdb325b7fba1e2c40b9b1db407f85642e32404131c08480dd652110fc908561b"}, - {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d74d4a3c4b8f7a1f676cedf8e84bcc57705a6d7925e6daef7a1e54ae543a197"}, - {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ac7674d1638df129d9cb4503d20ffc3922bd463c865ef3cb412f2c926108e9a4"}, - {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:ddd92e18b783aeb86ad2132d84a4b795fc5ec612e3545c1b687e7747e66e2b53"}, - {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bd9ac6e44f2db368ef8986f3989a4cad3de4cd55dbdda536e253000c801bcc7"}, - {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bc354b1393dce46026ab13075f77b30e40b61b1a53e852e99d3cc5dd1af4bc85"}, - {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f836f39678cb47c9541f04d8ed4545719dc31ad850bf1832d6b4171e30d65d23"}, - {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:9c131447768ed7bc05a02553d939e7f0e807e533441901dd504e217b76307745"}, - {file = "lxml-4.9.4-cp36-cp36m-win32.whl", hash = "sha256:bafa65e3acae612a7799ada439bd202403414ebe23f52e5b17f6ffc2eb98c2be"}, - {file = "lxml-4.9.4-cp36-cp36m-win_amd64.whl", hash = "sha256:6197c3f3c0b960ad033b9b7d611db11285bb461fc6b802c1dd50d04ad715c225"}, - {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:7b378847a09d6bd46047f5f3599cdc64fcb4cc5a5a2dd0a2af610361fbe77b16"}, - {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:1343df4e2e6e51182aad12162b23b0a4b3fd77f17527a78c53f0f23573663545"}, - {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6dbdacf5752fbd78ccdb434698230c4f0f95df7dd956d5f205b5ed6911a1367c"}, - {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:506becdf2ecaebaf7f7995f776394fcc8bd8a78022772de66677c84fb02dd33d"}, - {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca8e44b5ba3edb682ea4e6185b49661fc22b230cf811b9c13963c9f982d1d964"}, - {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9d9d5726474cbbef279fd709008f91a49c4f758bec9c062dfbba88eab00e3ff9"}, - {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bbdd69e20fe2943b51e2841fc1e6a3c1de460d630f65bde12452d8c97209464d"}, - {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8671622256a0859f5089cbe0ce4693c2af407bc053dcc99aadff7f5310b4aa02"}, - {file = "lxml-4.9.4-cp37-cp37m-win32.whl", hash = "sha256:dd4fda67f5faaef4f9ee5383435048ee3e11ad996901225ad7615bc92245bc8e"}, - {file = "lxml-4.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6bee9c2e501d835f91460b2c904bc359f8433e96799f5c2ff20feebd9bb1e590"}, - {file = "lxml-4.9.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:1f10f250430a4caf84115b1e0f23f3615566ca2369d1962f82bef40dd99cd81a"}, - {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b505f2bbff50d261176e67be24e8909e54b5d9d08b12d4946344066d66b3e43"}, - {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1449f9451cd53e0fd0a7ec2ff5ede4686add13ac7a7bfa6988ff6d75cff3ebe2"}, - {file = "lxml-4.9.4-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:4ece9cca4cd1c8ba889bfa67eae7f21d0d1a2e715b4d5045395113361e8c533d"}, - {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59bb5979f9941c61e907ee571732219fa4774d5a18f3fa5ff2df963f5dfaa6bc"}, - {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b1980dbcaad634fe78e710c8587383e6e3f61dbe146bcbfd13a9c8ab2d7b1192"}, - {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9ae6c3363261021144121427b1552b29e7b59de9d6a75bf51e03bc072efb3c37"}, - {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bcee502c649fa6351b44bb014b98c09cb00982a475a1912a9881ca28ab4f9cd9"}, - {file = "lxml-4.9.4-cp38-cp38-win32.whl", hash = "sha256:a8edae5253efa75c2fc79a90068fe540b197d1c7ab5803b800fccfe240eed33c"}, - {file = "lxml-4.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:701847a7aaefef121c5c0d855b2affa5f9bd45196ef00266724a80e439220e46"}, - {file = "lxml-4.9.4-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:f610d980e3fccf4394ab3806de6065682982f3d27c12d4ce3ee46a8183d64a6a"}, - {file = "lxml-4.9.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aa9b5abd07f71b081a33115d9758ef6077924082055005808f68feccb27616bd"}, - {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:365005e8b0718ea6d64b374423e870648ab47c3a905356ab6e5a5ff03962b9a9"}, - {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:16b9ec51cc2feab009e800f2c6327338d6ee4e752c76e95a35c4465e80390ccd"}, - {file = "lxml-4.9.4-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a905affe76f1802edcac554e3ccf68188bea16546071d7583fb1b693f9cf756b"}, - {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fd814847901df6e8de13ce69b84c31fc9b3fb591224d6762d0b256d510cbf382"}, - {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91bbf398ac8bb7d65a5a52127407c05f75a18d7015a270fdd94bbcb04e65d573"}, - {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f99768232f036b4776ce419d3244a04fe83784bce871b16d2c2e984c7fcea847"}, - {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bb5bd6212eb0edfd1e8f254585290ea1dadc3687dd8fd5e2fd9a87c31915cdab"}, - {file = "lxml-4.9.4-cp39-cp39-win32.whl", hash = "sha256:88f7c383071981c74ec1998ba9b437659e4fd02a3c4a4d3efc16774eb108d0ec"}, - {file = "lxml-4.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:936e8880cc00f839aa4173f94466a8406a96ddce814651075f95837316369899"}, - {file = "lxml-4.9.4-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:f6c35b2f87c004270fa2e703b872fcc984d714d430b305145c39d53074e1ffe0"}, - {file = "lxml-4.9.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:606d445feeb0856c2b424405236a01c71af7c97e5fe42fbc778634faef2b47e4"}, - {file = "lxml-4.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1bdcbebd4e13446a14de4dd1825f1e778e099f17f79718b4aeaf2403624b0f7"}, - {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0a08c89b23117049ba171bf51d2f9c5f3abf507d65d016d6e0fa2f37e18c0fc5"}, - {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:232fd30903d3123be4c435fb5159938c6225ee8607b635a4d3fca847003134ba"}, - {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:231142459d32779b209aa4b4d460b175cadd604fed856f25c1571a9d78114771"}, - {file = "lxml-4.9.4-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:520486f27f1d4ce9654154b4494cf9307b495527f3a2908ad4cb48e4f7ed7ef7"}, - {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:562778586949be7e0d7435fcb24aca4810913771f845d99145a6cee64d5b67ca"}, - {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a9e7c6d89c77bb2770c9491d988f26a4b161d05c8ca58f63fb1f1b6b9a74be45"}, - {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:786d6b57026e7e04d184313c1359ac3d68002c33e4b1042ca58c362f1d09ff58"}, - {file = "lxml-4.9.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95ae6c5a196e2f239150aa4a479967351df7f44800c93e5a975ec726fef005e2"}, - {file = "lxml-4.9.4-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:9b556596c49fa1232b0fff4b0e69b9d4083a502e60e404b44341e2f8fb7187f5"}, - {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:cc02c06e9e320869d7d1bd323df6dd4281e78ac2e7f8526835d3d48c69060683"}, - {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:857d6565f9aa3464764c2cb6a2e3c2e75e1970e877c188f4aeae45954a314e0c"}, - {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c42ae7e010d7d6bc51875d768110c10e8a59494855c3d4c348b068f5fb81fdcd"}, - {file = "lxml-4.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f10250bb190fb0742e3e1958dd5c100524c2cc5096c67c8da51233f7448dc137"}, - {file = "lxml-4.9.4.tar.gz", hash = "sha256:b1541e50b78e15fa06a2670157a1962ef06591d4c998b998047fff5e3236880e"}, + {file = "lxml-5.0.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73bfab795d354aaf2f4eb7a5b0db513031734fd371047342d5803834ce19ec18"}, + {file = "lxml-5.0.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cb564bbe55ff0897d9cf1225041a44576d7ae87f06fd60163544c91de2623d3f"}, + {file = "lxml-5.0.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6a5501438dd521bb7e0dde5008c40c7bfcfaafaf86eccb3f9bd27509abb793da"}, + {file = "lxml-5.0.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7ba26a7dc929a1b3487d51bbcb0099afed2fc06e891b82845c8f37a2d7d7fbbd"}, + {file = "lxml-5.0.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:9b59c429e1a2246da86ae237ffc3565efcdc71c281cd38ca8b44d5fb6a3b993a"}, + {file = "lxml-5.0.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:3ffa066db40b0347e48334bd4465de768e295a3525b9a59831228b5f4f93162d"}, + {file = "lxml-5.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8ce8b468ab50f9e944719d1134709ec11fe0d2840891a6cae369e22141b1094c"}, + {file = "lxml-5.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:583c0e15ae06adc81035346ae2abb2e748f0b5197e7740d8af31222db41bbf7b"}, + {file = "lxml-5.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:904d36165848b59c4e04ae5b969072e602bd987485076fca8ec42c6cd7a7aedc"}, + {file = "lxml-5.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ac21aace6712472e77ea9dfc38329f53830c4259ece54c786107105ebb069053"}, + {file = "lxml-5.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f92d73faa0b1a76d1932429d684b7ce95829e93c3eef3715ec9b98ab192c9d31"}, + {file = "lxml-5.0.0-cp310-cp310-win32.whl", hash = "sha256:03290e2f714f2e7431c8430c08b48167f657da7bc689c6248e828ff3c66d5b1b"}, + {file = "lxml-5.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e6cbb68bf70081f036bfc018649cf4b46c4e7eaf7860a277cae92dee2a57f69"}, + {file = "lxml-5.0.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5382612ba2424cea5d2c89e2c29077023d8de88f8d60d5ceff5f76334516df9e"}, + {file = "lxml-5.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:07a900735bad9af7be3085480bf384f68ed5580ba465b39a098e6a882c060d6b"}, + {file = "lxml-5.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:980ba47c8db4b9d870014c7040edb230825b79017a6a27aa54cdb6fcc02d8cc0"}, + {file = "lxml-5.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6507c58431dbd95b50654b3313c5ad54f90e54e5f2cdacf733de61eae478eec5"}, + {file = "lxml-5.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4a45a278518e4308865c1e9dbb2c42ce84fb154efb03adeb16fdae3c1687c7c9"}, + {file = "lxml-5.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:59cea9ba1c675fbd6867ca1078fc717a113e7f5b7644943b74137b7cc55abebf"}, + {file = "lxml-5.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd39ef87fd1f7bb5c4aa53454936e6135cbfe03fe3744e8218be193f9e4fef16"}, + {file = "lxml-5.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e6bb39d91bf932e7520cb5718ae3c2f498052aca53294d5d59fdd9068fe1a7f2"}, + {file = "lxml-5.0.0-cp311-cp311-win32.whl", hash = "sha256:21af2c3862db6f4f486cddf73ec1157b40d5828876c47cd880edcbad8240ea1b"}, + {file = "lxml-5.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:c1249aa4eaced30b59ecf8b8cae0b1ccede04583c74ca7d10b6f8bbead908b2c"}, + {file = "lxml-5.0.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f30e697b6215e759d0824768b2c5b0618d2dc19abe6c67eeed2b0460f52470d1"}, + {file = "lxml-5.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d1bb64646480c36a4aa1b6a44a5b6e33d0fcbeab9f53f1b39072cd3bb2c6243a"}, + {file = "lxml-5.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4e69c36c8618707a90ed3fb6f48a6cc9254ffcdbf7b259e439a5ae5fbf9c5206"}, + {file = "lxml-5.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9ca498f8554a09fbc3a2f8fc4b23261e07bc27bef99b3df98e2570688033f6fc"}, + {file = "lxml-5.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0326e9b8176ea77269fb39e7af4010906e73e9496a9f8eaf06d253b1b1231ceb"}, + {file = "lxml-5.0.0-cp312-cp312-win32.whl", hash = "sha256:5fb988e15378d6e905ca8f60813950a0c56da9469d0e8e5d8fe785b282684ec5"}, + {file = "lxml-5.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:bb58e8f4b2cfe012cd312239b8d5139995fe8f5945c7c26d5fbbbb1ddb9acd47"}, + {file = "lxml-5.0.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:81509dffd8aba3bdb43e90cbd218c9c068a1f4047d97bc9546b3ac9e3a4ae81d"}, + {file = "lxml-5.0.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e675a4b95208e74c34ac0751cc4bab9170e7728b61601fb0f4746892c2bb7e0b"}, + {file = "lxml-5.0.0-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:405e3760f83a8ba3bdb6e622ec79595cdc20db916ce37377bbcb95b5711fa4ca"}, + {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f15844a1b93dcaa09c2b22e22a73384f3ae4502347c3881cfdd674e14ac04e21"}, + {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88f559f8beb6b90e41a7faae4aca4c8173a4819874a9bf8e74c8d7c1d51f3162"}, + {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e8c63f5c7d87e7044880b01851ac4e863c3349e6f6b6ab456fe218d9346e816d"}, + {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:0d277d4717756fe8816f0beeff229cb72f9dd02a43b70e1d3f07c8efadfb9fe1"}, + {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8954da15403db1acfc0544b3c3f963a6ef4e428283ab6555e3e298bbbff1cf6"}, + {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:aebd8fd378e074b22e79cad329dcccd243c40ff1cafaa512d19276c5bb9554e1"}, + {file = "lxml-5.0.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b6d4e148edee59c2ad38af15810dbcb8b5d7b13e5de3509d8cf3edfe74c0adca"}, + {file = "lxml-5.0.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:70ab4e02f7aa5fb4131c8b222a111ce7676f3767e36084fba3a4e7338dc82dcd"}, + {file = "lxml-5.0.0-cp36-cp36m-win32.whl", hash = "sha256:de1a8b54170024cf1c0c2718c82412bca42cd82e390556e3d8031af9541b416f"}, + {file = "lxml-5.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5b39f63edbe7e018c2ac1cf0259ee0dd2355274e8a3003d404699b040782e55e"}, + {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:77b73952534967a4497d9e4f26fbeebfba19950cbc66b7cc3a706214429d8106"}, + {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8cc0a951e5616ac626f7036309c41fb9774adcd4aa7db0886463da1ce5b65edb"}, + {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:4b9d5b01900a760eb3acf6cef50aead4ef2fa79e7ddb927084244e41dfe37b65"}, + {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:173bcead3af5d87c7bca9a030675073ddaad8e0a9f0b04be07cd9390453e7226"}, + {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:44fa9afd632210f1eeda51cf284ed8dbab0c7ec8b008dd39ba02818e0e114e69"}, + {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fef10f27d6318d2d7c88680e113511ddecf09ee4f9559b3623b73ee89fa8f6cc"}, + {file = "lxml-5.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3663542aee845129a981889c19b366beab0b1dadcf5ca164696aabfe1aa51667"}, + {file = "lxml-5.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7188495c1bf71bfda87d78ed50601e72d252119ce11710d6e71ff36e35fea5a0"}, + {file = "lxml-5.0.0-cp37-cp37m-win32.whl", hash = "sha256:6a2de85deabf939b0af89e2e1ea46bfb1239545e2da6f8ac96522755a388025f"}, + {file = "lxml-5.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ea56825c1e23c9c8ea385a191dac75f9160477057285b88c88736d9305e6118f"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:3f908afd0477cace17f941d1b9cfa10b769fe1464770abe4cfb3d9f35378d0f8"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52a9ab31853d3808e7cf0183b3a5f7e8ffd622ea4aee1deb5252dbeaefd5b40d"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c7fe19abb3d3c55a9e65d289b12ad73b3a31a3f0bda3c539a890329ae9973bd6"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:1ef0793e1e2dd221fce7c142177008725680f7b9e4a184ab108d90d5d3ab69b7"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:581a78f299a9f5448b2c3aea904bfcd17c59bf83016d221d7f93f83633bb2ab2"}, + {file = "lxml-5.0.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:affdd833f82334fdb10fc9a1c7b35cdb5a86d0b672b4e14dd542e1fe7bcea894"}, + {file = "lxml-5.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6bba06d8982be0f0f6432d289a8d104417a0ab9ed04114446c4ceb6d4a40c65d"}, + {file = "lxml-5.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:80209b31dd3908bc5b014f540fd192c97ea52ab179713a730456c5baf7ce80c1"}, + {file = "lxml-5.0.0-cp38-cp38-win32.whl", hash = "sha256:dac2733fe4e159b0aae0439db6813b7b1d23ff96d0b34c0107b87faf79208c4e"}, + {file = "lxml-5.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:ee60f33456ff34b2dd1d048a740a2572798356208e4c494301c931de3a0ab3a2"}, + {file = "lxml-5.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:5eff173f0ff408bfa578cbdafd35a7e0ca94d1a9ffe09a8a48e0572d0904d486"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:78d6d8e5b54ed89dc0f0901eaaa579c384ad8d59fa43cc7fb06e9bb89115f8f4"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:71a7cee869578bc17b18050532bb2f0bc682a7b97dda77041741a1bd2febe6c7"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7df433d08d4587dc3932f7fcfc3194519a6824824104854e76441fd3bc000d29"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:793be9b4945c2dfd69828fb5948d7d9569b78e0599e4a2e88d92affeb0ff3aa3"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7cfb6af73602c8d288581df8a225989d7e9d5aab0a174be0e19fcfa800b6797"}, + {file = "lxml-5.0.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bfdc4668ac56687a89ca3eca44231144a2e9d02ba3b877558db74ba20e2bd9fa"}, + {file = "lxml-5.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2992591e2294bb07faf7f5f6d5cb60710c046404f4bfce09fb488b85d2a8f58f"}, + {file = "lxml-5.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4786b0af7511ea614fd86407a52a7bc161aa5772d311d97df2591ed2351de768"}, + {file = "lxml-5.0.0-cp39-cp39-win32.whl", hash = "sha256:016de3b29a262655fc3d2075dc1b2611f84f4c3d97a71d579c883d45e201eee4"}, + {file = "lxml-5.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:52c0acc2f29b0a204efc11a5ed911a74f50a25eb7d7d5069c2b1fd3b3346ce11"}, + {file = "lxml-5.0.0-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:96095bfc0c02072fc89afa67626013a253596ea5118b8a7f4daaae049dafa096"}, + {file = "lxml-5.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:992029258ed719f130d5a9c443d142c32843046f1263f2c492862b2a853be570"}, + {file = "lxml-5.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:db40e85cffd22f7d65dcce30e85af565a66401a6ed22fc0c56ed342cfa4ffc43"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:cfa8a4cdc3765574b7fd0c7cfa5fbd1e2108014c9dfd299c679e5152bea9a55e"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:049fef98d02513c34f5babd07569fc1cf1ed14c0f2fbff18fe72597f977ef3c2"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a85136d0ee18a41c91cc3e2844c683be0e72e6dda4cb58da9e15fcaef3726af7"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:766868f729f3ab84125350f1a0ea2594d8b1628a608a574542a5aff7355b9941"}, + {file = "lxml-5.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:99cad5c912f359e59e921689c04e54662cdd80835d80eeaa931e22612f515df7"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:c90c593aa8dd57d5dab0ef6d7d64af894008971d98e6a41b320fdd75258fbc6e"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8134d5441d1ed6a682e3de3d7a98717a328dce619ee9c4c8b3b91f0cb0eb3e28"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f298ac9149037d6a3d5c74991bded39ac46292520b9c7c182cb102486cc87677"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:894c5f71186b410679aaab5774543fcb9cbabe8893f0b31d11cf28a0740e80be"}, + {file = "lxml-5.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9cd3d6c2c67d4fdcd795e4945e2ba5434909c96640b4cc09453bd0dc7e8e1bac"}, + {file = "lxml-5.0.0.zip", hash = "sha256:2219cbf790e701acf9a21a31ead75f983e73daf0eceb9da6990212e4d20ebefe"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (==0.29.37)"] +source = ["Cython (>=3.0.7)"] [[package]] name = "mako" @@ -4699,4 +4694,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "723a71177888e1745d813239e675eb39671cfe0269336efc05444cda55b8f774" +content-hash = "2ced3799090bd2327e2d2a33a57641fdfe79c57f89344bf0d55d04b4f333142d" diff --git a/pyproject.toml b/pyproject.toml index 536e40c5e..ac108ea20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ flask-sqlalchemy = "==3.0.5" gunicorn = {version = "==21.2.0", extras = ["eventlet"]} iso8601 = "==2.1.0" jsonschema = {version = "==4.20.0", extras = ["format"]} -lxml = "==4.9.4" +lxml = "==5.0.0" marshmallow = "==3.20.1" marshmallow-sqlalchemy = "==0.29.0" newrelic = "*" From ac29da25f45519c8d8f67a4bdad4859a69e2721b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:48:37 +0000 Subject: [PATCH 029/259] Bump eventlet from 0.33.3 to 0.34.2 Bumps [eventlet](https://github.com/eventlet/eventlet) from 0.33.3 to 0.34.2. - [Changelog](https://github.com/eventlet/eventlet/blob/master/NEWS) - [Commits](https://github.com/eventlet/eventlet/compare/v0.33.3...v0.34.2) --- updated-dependencies: - dependency-name: eventlet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 15 +++++++++------ pyproject.toml | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index f42b6cb1c..96337fb29 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1152,20 +1152,23 @@ pgp = ["gpg"] [[package]] name = "eventlet" -version = "0.33.3" +version = "0.34.2" description = "Highly concurrent networking library" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "eventlet-0.33.3-py2.py3-none-any.whl", hash = "sha256:e43b9ae05ba4bb477a10307699c9aff7ff86121b2640f9184d29059f5a687df8"}, - {file = "eventlet-0.33.3.tar.gz", hash = "sha256:722803e7eadff295347539da363d68ae155b8b26ae6a634474d0a920be73cfda"}, + {file = "eventlet-0.34.2-py3-none-any.whl", hash = "sha256:eca0114398d3133f94d16590d205bbb010668a3bc7453fddadb51d63e8c1bac2"}, + {file = "eventlet-0.34.2.tar.gz", hash = "sha256:2115c7c6742e6893bf1347f82915572f8895c911cb5abaad4d3596a7daa847cc"}, ] [package.dependencies] dnspython = ">=1.15.0" -greenlet = ">=0.3" +greenlet = ">=1.0" six = ">=1.10.0" +[package.extras] +dev = ["black", "build", "commitizen", "isort", "pip-tools", "pre-commit", "twine"] + [[package]] name = "exceptiongroup" version = "1.2.0" @@ -4694,4 +4697,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "2ced3799090bd2327e2d2a33a57641fdfe79c57f89344bf0d55d04b4f333142d" +content-hash = "186b2a08e1d3975c13a1ca78390e0416649a36ab80327bbc5c5f6d15f47c118f" diff --git a/pyproject.toml b/pyproject.toml index ac108ea20..9db089d05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ click-didyoumean = "==0.3.0" click-plugins = "==1.1.1" click-repl = "==0.3.0" deprecated = "==1.2.14" -eventlet = "==0.33.3" +eventlet = "==0.34.2" flask = "~=2.3" flask-bcrypt = "==1.0.1" flask-marshmallow = "==0.14.0" From 9fe06e141bc5b83e3385254b0cdeac54f04c8ad6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 17:32:25 +0000 Subject: [PATCH 030/259] Bump notifications-python-client from 8.1.0 to 8.2.0 Bumps [notifications-python-client](https://github.com/alphagov/notifications-python-client) from 8.1.0 to 8.2.0. - [Changelog](https://github.com/alphagov/notifications-python-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/alphagov/notifications-python-client/compare/8.1.0...8.2.0) --- updated-dependencies: - dependency-name: notifications-python-client dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 96337fb29..1cceabfa1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2547,12 +2547,12 @@ setuptools = "*" [[package]] name = "notifications-python-client" -version = "8.1.0" +version = "8.2.0" description = "Python API client for GOV.UK Notify." optional = false python-versions = ">=3.7" files = [ - {file = "notifications_python_client-8.1.0-py3-none-any.whl", hash = "sha256:8aec1f7a4ba592fd699eae899df8ccca9ccafb614f47826c07b318ca903c9c13"}, + {file = "notifications_python_client-8.2.0-py3-none-any.whl", hash = "sha256:8cd8bd01ae603a972a5413c430ca42b16f6481f37d98406c3e9b68c1c192f59e"}, ] [package.dependencies] @@ -4697,4 +4697,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "186b2a08e1d3975c13a1ca78390e0416649a36ab80327bbc5c5f6d15f47c118f" +content-hash = "a5f9bd15a2dc8c11fd394e13e6ed3e50f7b7c7b5830c619e0a7124dee498c8e9" diff --git a/pyproject.toml b/pyproject.toml index 9db089d05..6d3807f7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ lxml = "==5.0.0" marshmallow = "==3.20.1" marshmallow-sqlalchemy = "==0.29.0" newrelic = "*" -notifications-python-client = "==8.1.0" +notifications-python-client = "==8.2.0" notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"} oscrypto = "==1.3.0" packaging = "==23.2" From d714ebcd5d13f3fec63a10a3236cf08e61fc4664 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 4 Jan 2024 09:00:41 -0500 Subject: [PATCH 031/259] Restructured how clients are implemented. Signed-off-by: Cliff Hill --- app/clients/__init__.py | 5 ++++- .../performance_platform_client.py | 2 +- app/clients/sms/__init__.py | 15 ++++++++------- app/clients/sms/aws_sns.py | 3 --- tests/app/clients/test_sms.py | 6 ++++++ 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/clients/__init__.py b/app/clients/__init__.py index 7ab453318..1946f70e6 100644 --- a/app/clients/__init__.py +++ b/app/clients/__init__.py @@ -1,3 +1,4 @@ +from abc import abstractmethod from typing import Protocol from botocore.config import Config @@ -29,7 +30,9 @@ class Client(Protocol): Base client for sending notifications. """ - pass + @abstractmethod + def init_app(self, current_app, *args, **kwargs): + raise NotImplementedError("TODO: Need to implement.") class NotificationProviderClients(object): diff --git a/app/clients/performance_platform/performance_platform_client.py b/app/clients/performance_platform/performance_platform_client.py index 7e3d8c5be..ec0f6b999 100644 --- a/app/clients/performance_platform/performance_platform_client.py +++ b/app/clients/performance_platform/performance_platform_client.py @@ -10,7 +10,7 @@ class PerformancePlatformClient: def active(self): return self._active - def init_app(self, app): + def init_app(self, app, *args, **kwargs): self._active = app.config.get("PERFORMANCE_PLATFORM_ENABLED") if self.active: self.performance_platform_url = app.config.get("PERFORMANCE_PLATFORM_URL") diff --git a/app/clients/sms/__init__.py b/app/clients/sms/__init__.py index cd4ca57a7..97639e970 100644 --- a/app/clients/sms/__init__.py +++ b/app/clients/sms/__init__.py @@ -1,4 +1,5 @@ -from abc import abstractmethod +from abc import abstractmethod, abstractproperty +from typing import final from app.clients import Client, ClientException @@ -20,14 +21,14 @@ class SmsClient(Client): Base Sms client for sending smss. """ - @abstractmethod - def init_app(self, *args, **kwargs): - raise NotImplementedError("TODO Need to implement.") - @abstractmethod def send_sms(self, *args, **kwargs): raise NotImplementedError("TODO Need to implement.") - @abstractmethod - def get_name(self): + @abstractproperty + def name(self): raise NotImplementedError("TODO Need to implement.") + + @final + def get_name(self): + return self.name diff --git a/app/clients/sms/aws_sns.py b/app/clients/sms/aws_sns.py index 1061ec9ce..d75122a7e 100644 --- a/app/clients/sms/aws_sns.py +++ b/app/clients/sms/aws_sns.py @@ -43,9 +43,6 @@ class AwsSnsClient(SmsClient): def name(self): return "sns" - def get_name(self): - return self.name - def _valid_sender_number(self, sender): return sender and re.match(self._valid_sender_regex, sender) diff --git a/tests/app/clients/test_sms.py b/tests/app/clients/test_sms.py index 5718cbc81..f2a5abfed 100644 --- a/tests/app/clients/test_sms.py +++ b/tests/app/clients/test_sms.py @@ -10,6 +10,12 @@ def fake_client(notify_api): def name(self): return "fake" + def init_app(self, current_app, *args, **kwargs): + pass + + def send_sms(self, *args, **kwargs): + pass + fake_client = FakeSmsClient() # fake_client.init_app(notify_api) return fake_client From b88d1f0aa25caa4f72bcef13d473aa08d6527061 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 4 Jan 2024 15:07:50 -0500 Subject: [PATCH 032/259] Minor tweaks Signed-off-by: Cliff Hill --- app/clients/pinpoint/__init__.py | 0 app/clients/sms/__init__.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 app/clients/pinpoint/__init__.py diff --git a/app/clients/pinpoint/__init__.py b/app/clients/pinpoint/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/clients/sms/__init__.py b/app/clients/sms/__init__.py index 97639e970..f3a366648 100644 --- a/app/clients/sms/__init__.py +++ b/app/clients/sms/__init__.py @@ -13,7 +13,7 @@ class SmsClientResponseException(ClientException): self.message = message def __str__(self): - return "Message {}".format(self.message) + return f"Message {self.message}" class SmsClient(Client): From 88379c9e465d30b3d2ea03c209bb77d5a900a049 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 5 Jan 2024 10:35:14 -0800 Subject: [PATCH 033/259] final --- app/aws/s3.py | 45 ++++++++++++++++++++ app/celery/provider_tasks.py | 4 +- app/delivery/send_to_providers.py | 25 +++++++++-- app/user/rest.py | 4 ++ migrations/alembic.ini | 6 +-- poetry.lock | 26 ++++++----- pyproject.toml | 1 + tests/app/aws/test_s3.py | 30 ++++++++++++- tests/app/delivery/test_send_to_providers.py | 27 ++++++++++++ 9 files changed, 148 insertions(+), 20 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 5fa468a4a..c56c4e3a0 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -1,5 +1,6 @@ import botocore from boto3 import Session +from expiringdict import ExpiringDict from flask import current_app from app.clients import AWS_CLIENT_CONFIG @@ -7,6 +8,9 @@ from app.clients import AWS_CLIENT_CONFIG FILE_LOCATION_STRUCTURE = "service-{}-notify/{}.csv" +JOBS = ExpiringDict(max_len=100, max_age_seconds=3600) + + def get_s3_file(bucket_name, file_location, access_key, secret_key, region): s3_file = get_s3_object(bucket_name, file_location, access_key, secret_key, region) return s3_file.get()["Body"].read().decode("utf-8") @@ -62,10 +66,51 @@ def get_job_and_metadata_from_s3(service_id, job_id): def get_job_from_s3(service_id, job_id): + print(f"ENTERE get_job_from_s3 with {service_id} {job_id}") obj = get_s3_object(*get_job_location(service_id, job_id)) + print(f"obj is {obj}") return obj.get()["Body"].read().decode("utf-8") +def get_phone_number_from_s3(service_id, job_id, job_row_number): + # We don't want to constantly pull down a job from s3 every time we need a phone number. + # At the same time we don't want to store it in redis or the db + # So this is a little recycling mechanism to reduce the number of downloads. + print( + f"ENTER GET_PHONE_NUMBER_FROM_S3 WITH JOB_ID {job_id} SERVICE_ID {service_id} JOB_ROW_NUMBER {job_row_number}" + ) + job = JOBS.get(job_id) + print(f"JOB FROM EXPIRINGDICT {job} JOB ID {job_id}") + if job is None: + print("JOB IS NONE") + job = get_job_from_s3(service_id, job_id) + print(f"FRESH JOB {job}") + JOBS[job_id] = job + print("ADDED {job} to EXPRING DICT") + else: + print("JOB IS NOT NONE") + job = job.split("\r\n") + print(f"JOB AFTER SPLIT {job}") + first_row = job[0] + job.pop(0) + first_row = first_row.split(",") + phone_index = 0 + for item in first_row: + if item == "phone number": + break + phone_index = phone_index + 1 + correct_row = job[job_row_number] + correct_row = correct_row.split(",") + + my_phone = correct_row[phone_index] + my_phone = my_phone.replace("(", "") + my_phone = my_phone.replace(")", "") + my_phone = my_phone.replace("-", "") + my_phone = my_phone.replace(" ", "") + my_phone = my_phone.replace("+", "") + return my_phone + + def get_job_metadata_from_s3(service_id, job_id): obj = get_s3_object(*get_job_location(service_id, job_id)) return obj.get()["Metadata"] diff --git a/app/celery/provider_tasks.py b/app/celery/provider_tasks.py index 822b1f119..af310d3d5 100644 --- a/app/celery/provider_tasks.py +++ b/app/celery/provider_tasks.py @@ -31,7 +31,7 @@ DELIVERY_RECEIPT_DELAY_IN_SECONDS = 120 @notify_celery.task( bind=True, name="check_sms_delivery_receipt", - max_retries=48, + max_retries=12, default_retry_delay=300, ) def check_sms_delivery_receipt(self, message_id, notification_id, sent_at): @@ -92,7 +92,7 @@ def check_sms_delivery_receipt(self, message_id, notification_id, sent_at): @notify_celery.task( - bind=True, name="deliver_sms", max_retries=48, default_retry_delay=300 + bind=True, name="deliver_sms", max_retries=12, default_retry_delay=300 ) def deliver_sms(self, notification_id): try: diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index d946ecfae..1a13608e8 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -9,7 +9,8 @@ from notifications_utils.template import ( SMSMessageTemplate, ) -from app import create_uuid, db, notification_provider_clients +from app import create_uuid, db, notification_provider_clients, redis_store +from app.aws.s3 import 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 @@ -65,8 +66,26 @@ def send_sms_to_provider(notification): # providers as a slow down of our providers can cause us to run out of DB connections # Therefore we pull all the data from our DB models into `send_sms_kwargs`now before # closing the session (as otherwise it would be reopened immediately) + + # We start by trying to get the phone number from a job in s3. If we fail, we assume + # the phone number is for the verification code on login, which is not a job. + my_phone = None + try: + my_phone = get_phone_number_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + print(f"MY PHONE FROM JOB {my_phone}") + except BaseException: + my_phone = redis_store.get(f"2facode_{notification.id}") + if my_phone: + my_phone = my_phone.decode("utf-8") + print(f"MY PHONE FROM VERIFY CODE {my_phone}") + if my_phone is None: + raise Exception("what happened to the phone number") send_sms_kwargs = { - "to": notification.normalised_to, + "to": my_phone, "content": str(template), "reference": str(notification.id), "sender": notification.reply_to_text, @@ -101,7 +120,7 @@ def send_email_to_provider(notification): html_email = HTMLEmailTemplate( template_dict, values=notification.personalisation, - **get_html_email_options(service) + **get_html_email_options(service), ) plain_text_email = PlainTextEmailTemplate( diff --git a/app/user/rest.py b/app/user/rest.py index 25714d94a..7941504ee 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -7,6 +7,7 @@ from flask import Blueprint, abort, current_app, jsonify, request from notifications_utils.recipients import is_us_phone_number, use_numeric_sender from sqlalchemy.exc import IntegrityError +from app import redis_store from app.config import QueueNames from app.dao.permissions_dao import permission_dao from app.dao.service_user_dao import dao_get_service_user, dao_update_service_user @@ -337,6 +338,7 @@ def create_2fa_code( reply_to = get_sms_reply_to_for_notify_service(recipient, template) elif template.template_type == EMAIL_TYPE: reply_to = template.service.get_default_reply_to_email_address() + saved_notification = persist_notification( template_id=template.id, template_version=template.version, @@ -348,6 +350,8 @@ def create_2fa_code( key_type=KEY_TYPE_NORMAL, reply_to_text=reply_to, ) + + redis_store.set(f"2facode_{saved_notification.id}", recipient, ex=1800) # Assume that we never want to observe the Notify service's research mode # setting for this notification - we still need to be able to log into the # admin even if we're doing user research using this service: diff --git a/migrations/alembic.ini b/migrations/alembic.ini index c29545c7d..1f996dd97 100644 --- a/migrations/alembic.ini +++ b/migrations/alembic.ini @@ -20,17 +20,17 @@ keys = console keys = generic [logger_root] -level = INFO +level = WARNING handlers = qualname = [logger_sqlalchemy] -level = INFO +level = WARNING handlers = qualname = sqlalchemy.engine [logger_alembic] -level = INFO +level = WARNING handlers = qualname = alembic diff --git a/poetry.lock b/poetry.lock index 1cceabfa1..b61d186e7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1197,6 +1197,20 @@ files = [ [package.extras] testing = ["hatch", "pre-commit", "pytest", "tox"] +[[package]] +name = "expiringdict" +version = "1.2.2" +description = "Dictionary with auto-expiring values for caching purposes" +optional = false +python-versions = "*" +files = [ + {file = "expiringdict-1.2.2-py3-none-any.whl", hash = "sha256:09a5d20bc361163e6432a874edd3179676e935eb81b925eccef48d409a8a45e8"}, + {file = "expiringdict-1.2.2.tar.gz", hash = "sha256:300fb92a7e98f15b05cf9a856c1415b3bc4f2e132be07daa326da6414c23ee09"}, +] + +[package.extras] +tests = ["coverage", "coveralls", "dill", "mock", "nose"] + [[package]] name = "fastjsonschema" version = "2.19.0" @@ -2168,16 +2182,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -4697,4 +4701,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "a5f9bd15a2dc8c11fd394e13e6ed3e50f7b7c7b5830c619e0a7124dee498c8e9" +content-hash = "61f89a02495f30c6ba0deae8c4914dc7e52781dd95d897a4c480b120aa25c1af" diff --git a/pyproject.toml b/pyproject.toml index 6d3807f7b..2126eadb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ click-plugins = "==1.1.1" click-repl = "==0.3.0" deprecated = "==1.2.14" eventlet = "==0.34.2" +expiringdict = "==1.2.2" flask = "~=2.3" flask-bcrypt = "==1.0.1" flask-marshmallow = "==0.14.0" diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index 2989d86a9..f6f03f90a 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -5,7 +5,13 @@ from os import getenv import pytest from botocore.exceptions import ClientError -from app.aws.s3 import file_exists, get_s3_file, remove_csv_object, remove_s3_object +from app.aws.s3 import ( + file_exists, + get_phone_number_from_s3, + get_s3_file, + remove_csv_object, + remove_s3_object, +) default_access_key = getenv("CSV_AWS_ACCESS_KEY_ID") default_secret_key = getenv("CSV_AWS_SECRET_ACCESS_KEY") @@ -39,6 +45,28 @@ def test_get_s3_file_makes_correct_call(notify_api, mocker): ) +@pytest.mark.parametrize( + "job, job_id, job_row_number, expected_phone_number", + [ + ("phone number\r\n+15555555555", "aaa", 0, "15555555555"), + ( + "day of week,favorite color,phone number\r\nmonday,green,15551111111\r\ntuesday,red,15552222222", + "bbb", + 1, + "15552222222", + ), + ], +) +def test_get_phone_number_from_s3( + mocker, job, job_id, job_row_number, expected_phone_number +): + get_job_mock = mocker.patch("app.aws.s3.get_job_from_s3") + get_job_mock.return_value = job + print(f"ABOUT TO CALL GET_PHONE_NUMBER_FROM_S3 WITH JOB_ID {job_id}") + phone_number = get_phone_number_from_s3("service_id", job_id, job_row_number) + assert phone_number == expected_phone_number + + def test_remove_csv_object(notify_api, mocker): get_s3_mock = mocker.patch("app.aws.s3.get_s3_object") remove_csv_object("mykey") diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index e98b8a319..4cbf22b9a 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -94,6 +94,9 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist( mocker.patch("app.aws_sns_client.send_sms") + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" + send_to_providers.send_sms_to_provider(db_notification) aws_sns_client.send_sms.assert_called_once_with( @@ -186,6 +189,9 @@ def test_send_sms_should_use_template_version_from_notification_not_latest( normalised_to="2028675309", ) + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" + mocker.patch("app.aws_sns_client.send_sms") version_on_notification = sample_template.version @@ -266,6 +272,9 @@ def test_should_send_sms_with_downgraded_content(notify_db_session, mocker): mocker.patch("app.aws_sns_client.send_sms") + mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_phone.return_value = "15555555555" + send_to_providers.send_sms_to_provider(db_notification) aws_sns_client.send_sms.assert_called_once_with( @@ -285,6 +294,8 @@ def test_send_sms_should_use_service_sms_sender( template=sample_template, reply_to_text=sms_sender.sms_sender ) expected_sender_name = sms_sender.sms_sender + mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_phone.return_value = "15555555555" send_to_providers.send_sms_to_provider( db_notification, @@ -513,6 +524,9 @@ def test_should_update_billable_units_and_status_according_to_research_mode_and_ if research_mode: sample_template.service.research_mode = True + mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_phone.return_value = "15555555555" + send_to_providers.send_sms_to_provider(notification) assert notification.billable_units == billable_units assert notification.status == expected_status @@ -552,6 +566,9 @@ def test_should_send_sms_to_international_providers( normalised_to="601117224412", ) + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "601117224412" + send_to_providers.send_sms_to_provider(notification_international) aws_sns_client.send_sms.assert_called_once_with( @@ -588,6 +605,9 @@ def test_should_handle_sms_sender_and_prefix_message( template = create_template(service, content="bar") notification = create_notification(template, reply_to_text=sms_sender) + mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_phone.return_value = "15555555555" + send_to_providers.send_sms_to_provider(notification) aws_sns_client.send_sms.assert_called_once_with( @@ -622,6 +642,9 @@ def test_send_sms_to_provider_should_use_normalised_to(mocker, client, sample_te notification = create_notification( template=sample_template, to_field="+12028675309", normalised_to="2028675309" ) + + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" send_to_providers.send_sms_to_provider(notification) send_mock.assert_called_once_with( to=notification.normalised_to, @@ -677,6 +700,10 @@ def test_send_sms_to_provider_should_return_template_if_found_in_redis( notification = create_notification( template=sample_template, to_field="+447700900855", normalised_to="447700900855" ) + + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "447700900855" + send_to_providers.send_sms_to_provider(notification) assert mock_get_template.called is False assert mock_get_service.called is False From a5f78224b21e729e208b33ddd99457bcf770cb46 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 5 Jan 2024 10:39:07 -0800 Subject: [PATCH 034/259] remove print statements --- app/aws/s3.py | 12 ------------ app/delivery/send_to_providers.py | 2 -- tests/app/aws/test_s3.py | 1 - 3 files changed, 15 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index c56c4e3a0..47c307d44 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -66,9 +66,7 @@ def get_job_and_metadata_from_s3(service_id, job_id): def get_job_from_s3(service_id, job_id): - print(f"ENTERE get_job_from_s3 with {service_id} {job_id}") obj = get_s3_object(*get_job_location(service_id, job_id)) - print(f"obj is {obj}") return obj.get()["Body"].read().decode("utf-8") @@ -76,21 +74,11 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): # We don't want to constantly pull down a job from s3 every time we need a phone number. # At the same time we don't want to store it in redis or the db # So this is a little recycling mechanism to reduce the number of downloads. - print( - f"ENTER GET_PHONE_NUMBER_FROM_S3 WITH JOB_ID {job_id} SERVICE_ID {service_id} JOB_ROW_NUMBER {job_row_number}" - ) job = JOBS.get(job_id) - print(f"JOB FROM EXPIRINGDICT {job} JOB ID {job_id}") if job is None: - print("JOB IS NONE") job = get_job_from_s3(service_id, job_id) - print(f"FRESH JOB {job}") JOBS[job_id] = job - print("ADDED {job} to EXPRING DICT") - else: - print("JOB IS NOT NONE") job = job.split("\r\n") - print(f"JOB AFTER SPLIT {job}") first_row = job[0] job.pop(0) first_row = first_row.split(",") diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 1a13608e8..68f151863 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -76,12 +76,10 @@ def send_sms_to_provider(notification): notification.job_id, notification.job_row_number, ) - print(f"MY PHONE FROM JOB {my_phone}") except BaseException: my_phone = redis_store.get(f"2facode_{notification.id}") if my_phone: my_phone = my_phone.decode("utf-8") - print(f"MY PHONE FROM VERIFY CODE {my_phone}") if my_phone is None: raise Exception("what happened to the phone number") send_sms_kwargs = { diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index f6f03f90a..6af212d87 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -62,7 +62,6 @@ def test_get_phone_number_from_s3( ): get_job_mock = mocker.patch("app.aws.s3.get_job_from_s3") get_job_mock.return_value = job - print(f"ABOUT TO CALL GET_PHONE_NUMBER_FROM_S3 WITH JOB_ID {job_id}") phone_number = get_phone_number_from_s3("service_id", job_id, job_row_number) assert phone_number == expected_phone_number From e1dcd12d2d72a8f856dd9c444ff3753dccd0f304 Mon Sep 17 00:00:00 2001 From: Emily Herrick <91496588+em-herrick@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:05:30 -0500 Subject: [PATCH 035/259] Production phone number assignments Edited production phone numbers to assign the ones in use to three pilot partners with live services. --- docs/all.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/all.md b/docs/all.md index 32c2216c4..37a133b36 100644 --- a/docs/all.md +++ b/docs/all.md @@ -985,10 +985,10 @@ Once you have a number, it must be set in the app in one of two ways: ### Current Production Phone Numbers -* +18447952263 - in use as default number. Notify's OTP messages and trial service messages are sent from this number -* +18447891134 - to be used by Pilot Partner 1 -* +18888402596 - to be used by Pilot Partner 2 -* +18555317292 +* +18447952263 - in use as default number. Notify's OTP messages and trial service messages are sent from this number (Also the number for the live service: Federal Test Service) +* +18447891134 - Montgomery County / Ride On +* +18888402596 - Norfolk / DHS +* +18555317292 - Washington State / DHS * +18889046435 * +18447342791 * +18447525067 From 726e0b15fac7a0c4d39c874492e0a635b107780f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 8 Jan 2024 14:31:28 -0800 Subject: [PATCH 036/259] code review feedback --- app/aws/s3.py | 10 ++++------ app/celery/provider_tasks.py | 4 ++-- app/delivery/send_to_providers.py | 7 ++++++- tests/app/aws/test_s3.py | 8 +++++++- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 47c307d44..2a631c326 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -1,3 +1,5 @@ +import re + import botocore from boto3 import Session from expiringdict import ExpiringDict @@ -8,7 +10,7 @@ from app.clients import AWS_CLIENT_CONFIG FILE_LOCATION_STRUCTURE = "service-{}-notify/{}.csv" -JOBS = ExpiringDict(max_len=100, max_age_seconds=3600) +JOBS = ExpiringDict(max_len=100, max_age_seconds=3600 * 4) def get_s3_file(bucket_name, file_location, access_key, secret_key, region): @@ -91,11 +93,7 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): correct_row = correct_row.split(",") my_phone = correct_row[phone_index] - my_phone = my_phone.replace("(", "") - my_phone = my_phone.replace(")", "") - my_phone = my_phone.replace("-", "") - my_phone = my_phone.replace(" ", "") - my_phone = my_phone.replace("+", "") + my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) return my_phone diff --git a/app/celery/provider_tasks.py b/app/celery/provider_tasks.py index af310d3d5..822b1f119 100644 --- a/app/celery/provider_tasks.py +++ b/app/celery/provider_tasks.py @@ -31,7 +31,7 @@ DELIVERY_RECEIPT_DELAY_IN_SECONDS = 120 @notify_celery.task( bind=True, name="check_sms_delivery_receipt", - max_retries=12, + max_retries=48, default_retry_delay=300, ) def check_sms_delivery_receipt(self, message_id, notification_id, sent_at): @@ -92,7 +92,7 @@ def check_sms_delivery_receipt(self, message_id, notification_id, sent_at): @notify_celery.task( - bind=True, name="deliver_sms", max_retries=12, default_retry_delay=300 + bind=True, name="deliver_sms", max_retries=48, default_retry_delay=300 ) def deliver_sms(self, notification_id): try: diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 68f151863..79bf77fd2 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -81,7 +81,12 @@ def send_sms_to_provider(notification): if my_phone: my_phone = my_phone.decode("utf-8") if my_phone is None: - raise Exception("what happened to the phone number") + si = notification.service_id + ji = notification.job_id + jrn = notification.job_row_number + raise Exception( + f"The phone number for (Service ID: {si}; Job ID: {ji}; Job Row Number {jrn} was not found." + ) send_sms_kwargs = { "to": my_phone, "content": str(template), diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index 6af212d87..27df6ac6c 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -50,11 +50,17 @@ def test_get_s3_file_makes_correct_call(notify_api, mocker): [ ("phone number\r\n+15555555555", "aaa", 0, "15555555555"), ( - "day of week,favorite color,phone number\r\nmonday,green,15551111111\r\ntuesday,red,15552222222", + "day of week,favorite color,phone number\r\nmonday,green,1.555.111.1111\r\ntuesday,red,+1 (555) 222-2222", "bbb", 1, "15552222222", ), + ( + "day of week,favorite color,phone number\r\nmonday,green,1.555.111.1111\r\ntuesday,red,+1 (555) 222-2222", + "ccc", + 0, + "15551111111", + ), ], ) def test_get_phone_number_from_s3( From 941e9cce7dabefc253bdcdb3bfc92a834e9a51e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:09:32 +0000 Subject: [PATCH 037/259] Bump lxml from 5.0.0 to 5.1.0 Bumps [lxml](https://github.com/lxml/lxml) from 5.0.0 to 5.1.0. - [Release notes](https://github.com/lxml/lxml/releases) - [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt) - [Commits](https://github.com/lxml/lxml/compare/lxml-5.0.0...lxml-5.1.0) --- updated-dependencies: - dependency-name: lxml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 178 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 88 insertions(+), 92 deletions(-) diff --git a/poetry.lock b/poetry.lock index b61d186e7..8e0b15ce8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1994,99 +1994,85 @@ testing = ["black", "isort", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)", "twin [[package]] name = "lxml" -version = "5.0.0" +version = "5.1.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +python-versions = ">=3.6" files = [ - {file = "lxml-5.0.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73bfab795d354aaf2f4eb7a5b0db513031734fd371047342d5803834ce19ec18"}, - {file = "lxml-5.0.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cb564bbe55ff0897d9cf1225041a44576d7ae87f06fd60163544c91de2623d3f"}, - {file = "lxml-5.0.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6a5501438dd521bb7e0dde5008c40c7bfcfaafaf86eccb3f9bd27509abb793da"}, - {file = "lxml-5.0.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7ba26a7dc929a1b3487d51bbcb0099afed2fc06e891b82845c8f37a2d7d7fbbd"}, - {file = "lxml-5.0.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:9b59c429e1a2246da86ae237ffc3565efcdc71c281cd38ca8b44d5fb6a3b993a"}, - {file = "lxml-5.0.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:3ffa066db40b0347e48334bd4465de768e295a3525b9a59831228b5f4f93162d"}, - {file = "lxml-5.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8ce8b468ab50f9e944719d1134709ec11fe0d2840891a6cae369e22141b1094c"}, - {file = "lxml-5.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:583c0e15ae06adc81035346ae2abb2e748f0b5197e7740d8af31222db41bbf7b"}, - {file = "lxml-5.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:904d36165848b59c4e04ae5b969072e602bd987485076fca8ec42c6cd7a7aedc"}, - {file = "lxml-5.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ac21aace6712472e77ea9dfc38329f53830c4259ece54c786107105ebb069053"}, - {file = "lxml-5.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f92d73faa0b1a76d1932429d684b7ce95829e93c3eef3715ec9b98ab192c9d31"}, - {file = "lxml-5.0.0-cp310-cp310-win32.whl", hash = "sha256:03290e2f714f2e7431c8430c08b48167f657da7bc689c6248e828ff3c66d5b1b"}, - {file = "lxml-5.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e6cbb68bf70081f036bfc018649cf4b46c4e7eaf7860a277cae92dee2a57f69"}, - {file = "lxml-5.0.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:5382612ba2424cea5d2c89e2c29077023d8de88f8d60d5ceff5f76334516df9e"}, - {file = "lxml-5.0.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:07a900735bad9af7be3085480bf384f68ed5580ba465b39a098e6a882c060d6b"}, - {file = "lxml-5.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:980ba47c8db4b9d870014c7040edb230825b79017a6a27aa54cdb6fcc02d8cc0"}, - {file = "lxml-5.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6507c58431dbd95b50654b3313c5ad54f90e54e5f2cdacf733de61eae478eec5"}, - {file = "lxml-5.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:4a45a278518e4308865c1e9dbb2c42ce84fb154efb03adeb16fdae3c1687c7c9"}, - {file = "lxml-5.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:59cea9ba1c675fbd6867ca1078fc717a113e7f5b7644943b74137b7cc55abebf"}, - {file = "lxml-5.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd39ef87fd1f7bb5c4aa53454936e6135cbfe03fe3744e8218be193f9e4fef16"}, - {file = "lxml-5.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e6bb39d91bf932e7520cb5718ae3c2f498052aca53294d5d59fdd9068fe1a7f2"}, - {file = "lxml-5.0.0-cp311-cp311-win32.whl", hash = "sha256:21af2c3862db6f4f486cddf73ec1157b40d5828876c47cd880edcbad8240ea1b"}, - {file = "lxml-5.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:c1249aa4eaced30b59ecf8b8cae0b1ccede04583c74ca7d10b6f8bbead908b2c"}, - {file = "lxml-5.0.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f30e697b6215e759d0824768b2c5b0618d2dc19abe6c67eeed2b0460f52470d1"}, - {file = "lxml-5.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d1bb64646480c36a4aa1b6a44a5b6e33d0fcbeab9f53f1b39072cd3bb2c6243a"}, - {file = "lxml-5.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4e69c36c8618707a90ed3fb6f48a6cc9254ffcdbf7b259e439a5ae5fbf9c5206"}, - {file = "lxml-5.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9ca498f8554a09fbc3a2f8fc4b23261e07bc27bef99b3df98e2570688033f6fc"}, - {file = "lxml-5.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0326e9b8176ea77269fb39e7af4010906e73e9496a9f8eaf06d253b1b1231ceb"}, - {file = "lxml-5.0.0-cp312-cp312-win32.whl", hash = "sha256:5fb988e15378d6e905ca8f60813950a0c56da9469d0e8e5d8fe785b282684ec5"}, - {file = "lxml-5.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:bb58e8f4b2cfe012cd312239b8d5139995fe8f5945c7c26d5fbbbb1ddb9acd47"}, - {file = "lxml-5.0.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:81509dffd8aba3bdb43e90cbd218c9c068a1f4047d97bc9546b3ac9e3a4ae81d"}, - {file = "lxml-5.0.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e675a4b95208e74c34ac0751cc4bab9170e7728b61601fb0f4746892c2bb7e0b"}, - {file = "lxml-5.0.0-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:405e3760f83a8ba3bdb6e622ec79595cdc20db916ce37377bbcb95b5711fa4ca"}, - {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f15844a1b93dcaa09c2b22e22a73384f3ae4502347c3881cfdd674e14ac04e21"}, - {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88f559f8beb6b90e41a7faae4aca4c8173a4819874a9bf8e74c8d7c1d51f3162"}, - {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e8c63f5c7d87e7044880b01851ac4e863c3349e6f6b6ab456fe218d9346e816d"}, - {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:0d277d4717756fe8816f0beeff229cb72f9dd02a43b70e1d3f07c8efadfb9fe1"}, - {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8954da15403db1acfc0544b3c3f963a6ef4e428283ab6555e3e298bbbff1cf6"}, - {file = "lxml-5.0.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:aebd8fd378e074b22e79cad329dcccd243c40ff1cafaa512d19276c5bb9554e1"}, - {file = "lxml-5.0.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b6d4e148edee59c2ad38af15810dbcb8b5d7b13e5de3509d8cf3edfe74c0adca"}, - {file = "lxml-5.0.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:70ab4e02f7aa5fb4131c8b222a111ce7676f3767e36084fba3a4e7338dc82dcd"}, - {file = "lxml-5.0.0-cp36-cp36m-win32.whl", hash = "sha256:de1a8b54170024cf1c0c2718c82412bca42cd82e390556e3d8031af9541b416f"}, - {file = "lxml-5.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5b39f63edbe7e018c2ac1cf0259ee0dd2355274e8a3003d404699b040782e55e"}, - {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:77b73952534967a4497d9e4f26fbeebfba19950cbc66b7cc3a706214429d8106"}, - {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8cc0a951e5616ac626f7036309c41fb9774adcd4aa7db0886463da1ce5b65edb"}, - {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:4b9d5b01900a760eb3acf6cef50aead4ef2fa79e7ddb927084244e41dfe37b65"}, - {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:173bcead3af5d87c7bca9a030675073ddaad8e0a9f0b04be07cd9390453e7226"}, - {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:44fa9afd632210f1eeda51cf284ed8dbab0c7ec8b008dd39ba02818e0e114e69"}, - {file = "lxml-5.0.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fef10f27d6318d2d7c88680e113511ddecf09ee4f9559b3623b73ee89fa8f6cc"}, - {file = "lxml-5.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3663542aee845129a981889c19b366beab0b1dadcf5ca164696aabfe1aa51667"}, - {file = "lxml-5.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7188495c1bf71bfda87d78ed50601e72d252119ce11710d6e71ff36e35fea5a0"}, - {file = "lxml-5.0.0-cp37-cp37m-win32.whl", hash = "sha256:6a2de85deabf939b0af89e2e1ea46bfb1239545e2da6f8ac96522755a388025f"}, - {file = "lxml-5.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ea56825c1e23c9c8ea385a191dac75f9160477057285b88c88736d9305e6118f"}, - {file = "lxml-5.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:3f908afd0477cace17f941d1b9cfa10b769fe1464770abe4cfb3d9f35378d0f8"}, - {file = "lxml-5.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52a9ab31853d3808e7cf0183b3a5f7e8ffd622ea4aee1deb5252dbeaefd5b40d"}, - {file = "lxml-5.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c7fe19abb3d3c55a9e65d289b12ad73b3a31a3f0bda3c539a890329ae9973bd6"}, - {file = "lxml-5.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:1ef0793e1e2dd221fce7c142177008725680f7b9e4a184ab108d90d5d3ab69b7"}, - {file = "lxml-5.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:581a78f299a9f5448b2c3aea904bfcd17c59bf83016d221d7f93f83633bb2ab2"}, - {file = "lxml-5.0.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:affdd833f82334fdb10fc9a1c7b35cdb5a86d0b672b4e14dd542e1fe7bcea894"}, - {file = "lxml-5.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6bba06d8982be0f0f6432d289a8d104417a0ab9ed04114446c4ceb6d4a40c65d"}, - {file = "lxml-5.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:80209b31dd3908bc5b014f540fd192c97ea52ab179713a730456c5baf7ce80c1"}, - {file = "lxml-5.0.0-cp38-cp38-win32.whl", hash = "sha256:dac2733fe4e159b0aae0439db6813b7b1d23ff96d0b34c0107b87faf79208c4e"}, - {file = "lxml-5.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:ee60f33456ff34b2dd1d048a740a2572798356208e4c494301c931de3a0ab3a2"}, - {file = "lxml-5.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:5eff173f0ff408bfa578cbdafd35a7e0ca94d1a9ffe09a8a48e0572d0904d486"}, - {file = "lxml-5.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:78d6d8e5b54ed89dc0f0901eaaa579c384ad8d59fa43cc7fb06e9bb89115f8f4"}, - {file = "lxml-5.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:71a7cee869578bc17b18050532bb2f0bc682a7b97dda77041741a1bd2febe6c7"}, - {file = "lxml-5.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7df433d08d4587dc3932f7fcfc3194519a6824824104854e76441fd3bc000d29"}, - {file = "lxml-5.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:793be9b4945c2dfd69828fb5948d7d9569b78e0599e4a2e88d92affeb0ff3aa3"}, - {file = "lxml-5.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7cfb6af73602c8d288581df8a225989d7e9d5aab0a174be0e19fcfa800b6797"}, - {file = "lxml-5.0.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bfdc4668ac56687a89ca3eca44231144a2e9d02ba3b877558db74ba20e2bd9fa"}, - {file = "lxml-5.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2992591e2294bb07faf7f5f6d5cb60710c046404f4bfce09fb488b85d2a8f58f"}, - {file = "lxml-5.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4786b0af7511ea614fd86407a52a7bc161aa5772d311d97df2591ed2351de768"}, - {file = "lxml-5.0.0-cp39-cp39-win32.whl", hash = "sha256:016de3b29a262655fc3d2075dc1b2611f84f4c3d97a71d579c883d45e201eee4"}, - {file = "lxml-5.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:52c0acc2f29b0a204efc11a5ed911a74f50a25eb7d7d5069c2b1fd3b3346ce11"}, - {file = "lxml-5.0.0-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:96095bfc0c02072fc89afa67626013a253596ea5118b8a7f4daaae049dafa096"}, - {file = "lxml-5.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:992029258ed719f130d5a9c443d142c32843046f1263f2c492862b2a853be570"}, - {file = "lxml-5.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:db40e85cffd22f7d65dcce30e85af565a66401a6ed22fc0c56ed342cfa4ffc43"}, - {file = "lxml-5.0.0-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:cfa8a4cdc3765574b7fd0c7cfa5fbd1e2108014c9dfd299c679e5152bea9a55e"}, - {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:049fef98d02513c34f5babd07569fc1cf1ed14c0f2fbff18fe72597f977ef3c2"}, - {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a85136d0ee18a41c91cc3e2844c683be0e72e6dda4cb58da9e15fcaef3726af7"}, - {file = "lxml-5.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:766868f729f3ab84125350f1a0ea2594d8b1628a608a574542a5aff7355b9941"}, - {file = "lxml-5.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:99cad5c912f359e59e921689c04e54662cdd80835d80eeaa931e22612f515df7"}, - {file = "lxml-5.0.0-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:c90c593aa8dd57d5dab0ef6d7d64af894008971d98e6a41b320fdd75258fbc6e"}, - {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8134d5441d1ed6a682e3de3d7a98717a328dce619ee9c4c8b3b91f0cb0eb3e28"}, - {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f298ac9149037d6a3d5c74991bded39ac46292520b9c7c182cb102486cc87677"}, - {file = "lxml-5.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:894c5f71186b410679aaab5774543fcb9cbabe8893f0b31d11cf28a0740e80be"}, - {file = "lxml-5.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9cd3d6c2c67d4fdcd795e4945e2ba5434909c96640b4cc09453bd0dc7e8e1bac"}, - {file = "lxml-5.0.0.zip", hash = "sha256:2219cbf790e701acf9a21a31ead75f983e73daf0eceb9da6990212e4d20ebefe"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, + {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c"}, + {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05"}, + {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, + {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, + {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e"}, + {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2"}, + {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, + {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, + {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431"}, + {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3"}, + {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8"}, + {file = "lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01"}, + {file = "lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623"}, + {file = "lxml-5.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d"}, + {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7"}, + {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67"}, + {file = "lxml-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd"}, + {file = "lxml-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7"}, + {file = "lxml-5.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936"}, + {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6"}, + {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, + {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, + {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, + {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1"}, + {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, + {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, + {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912"}, + {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14"}, + {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890"}, + {file = "lxml-5.1.0-cp39-cp39-win32.whl", hash = "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e"}, + {file = "lxml-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa"}, + {file = "lxml-5.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea"}, + {file = "lxml-5.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897"}, + {file = "lxml-5.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f"}, + {file = "lxml-5.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f"}, + {file = "lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca"}, ] [package.extras] @@ -2182,6 +2168,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -4701,4 +4697,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "61f89a02495f30c6ba0deae8c4914dc7e52781dd95d897a4c480b120aa25c1af" +content-hash = "3eae5562d8bbf55c63233022553f160719858173a1d7191ed98d264e3ce40fbc" diff --git a/pyproject.toml b/pyproject.toml index 2126eadb8..609cfc396 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ flask-sqlalchemy = "==3.0.5" gunicorn = {version = "==21.2.0", extras = ["eventlet"]} iso8601 = "==2.1.0" jsonschema = {version = "==4.20.0", extras = ["format"]} -lxml = "==5.0.0" +lxml = "==5.1.0" marshmallow = "==3.20.1" marshmallow-sqlalchemy = "==0.29.0" newrelic = "*" From 8acd96e82849d1b58d54455fc35c8697f4b4082c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:37:12 +0000 Subject: [PATCH 038/259] Bump moto from 4.2.12 to 4.2.13 Bumps [moto](https://github.com/getmoto/moto) from 4.2.12 to 4.2.13. - [Release notes](https://github.com/getmoto/moto/releases) - [Changelog](https://github.com/getmoto/moto/blob/master/CHANGELOG.md) - [Commits](https://github.com/getmoto/moto/compare/4.2.12...4.2.13) --- updated-dependencies: - dependency-name: moto dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8e0b15ce8..6ebc730fb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2298,13 +2298,13 @@ files = [ [[package]] name = "moto" -version = "4.2.12" +version = "4.2.13" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "moto-4.2.12-py2.py3-none-any.whl", hash = "sha256:bdcad46e066a55b7d308a786e5dca863b3cba04c6239c6974135a48d1198b3ab"}, - {file = "moto-4.2.12.tar.gz", hash = "sha256:7c4d37f47becb4a0526b64df54484e988c10fde26861fc3b5c065bc78800cb59"}, + {file = "moto-4.2.13-py2.py3-none-any.whl", hash = "sha256:93e0fd13b624bd79115494f833308c3641b2be0fc9f4f18aa9264aa01f6168e0"}, + {file = "moto-4.2.13.tar.gz", hash = "sha256:01aef6a489a725c8d725bd3dc6f70ff1bedaee3e2641752e4b471ff0ede4b4d7"}, ] [package.dependencies] @@ -4697,4 +4697,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "3eae5562d8bbf55c63233022553f160719858173a1d7191ed98d264e3ce40fbc" +content-hash = "a70284fcacadde70db3cf216efc388de74fb67c1f00085abcaee37b52ff6eedd" diff --git a/pyproject.toml b/pyproject.toml index 609cfc396..469f2b35e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ freezegun = "^1.4.0" honcho = "*" isort = "^5.13.2" jinja2-cli = {version = "==0.8.2", extras = ["yaml"]} -moto = "==4.2.12" +moto = "==4.2.13" pip-audit = "*" pre-commit = "^3.6.0" pytest = "^7.4.4" From be950a7322362feea8abb779feb4216c381b7c67 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 9 Jan 2024 13:09:07 -0800 Subject: [PATCH 039/259] no verification code on staging --- app/user/rest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/user/rest.py b/app/user/rest.py index 7941504ee..f5dda2a93 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -352,6 +352,11 @@ def create_2fa_code( ) redis_store.set(f"2facode_{saved_notification.id}", recipient, ex=1800) + stored_recipient = redis_store.get("2facode_{saved_notification.id}") + if stored_recipient: + current_app.logger.info("IN user/rest.py we saved the recipient of the 2facode to redis!") + else: + current_app.logger.info("IN user/rest.py we did NOT save the recipient of the 2facode to redis!") # Assume that we never want to observe the Notify service's research mode # setting for this notification - we still need to be able to log into the # admin even if we're doing user research using this service: From bee880577db8399f99fac5106e96356748da4e39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:32:43 +0000 Subject: [PATCH 040/259] Bump pip-audit from 2.6.2 to 2.6.3 Bumps [pip-audit](https://github.com/pypa/pip-audit) from 2.6.2 to 2.6.3. - [Release notes](https://github.com/pypa/pip-audit/releases) - [Changelog](https://github.com/pypa/pip-audit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pypa/pip-audit/compare/v2.6.2...v2.6.3) --- updated-dependencies: - dependency-name: pip-audit dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6ebc730fb..78c637f53 100644 --- a/poetry.lock +++ b/poetry.lock @@ -982,21 +982,26 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "cyclonedx-python-lib" -version = "4.2.3" -description = "A library for producing CycloneDX SBOM (Software Bill of Materials) files." +version = "6.3.0" +description = "Python library for CycloneDX" optional = false -python-versions = ">=3.7,<4.0" +python-versions = ">=3.8,<4.0" files = [ - {file = "cyclonedx_python_lib-4.2.3-py3-none-any.whl", hash = "sha256:e9b923af525b6acf7bab917a35360b4b562b85dc15fde9eaa500828949adf73a"}, - {file = "cyclonedx_python_lib-4.2.3.tar.gz", hash = "sha256:904068b55d1665f0ea96f38307603cc14f95c3b421f1687fc2411326aefde3a6"}, + {file = "cyclonedx_python_lib-6.3.0-py3-none-any.whl", hash = "sha256:0e73c1036c2f7fc67adc28aef807e6b44340ea70202aab197fb06b20ea165de8"}, + {file = "cyclonedx_python_lib-6.3.0.tar.gz", hash = "sha256:82f2489de3c0cadad5af1ad7fa6b6a185f985746370245d38769699c734533c6"}, ] [package.dependencies] license-expression = ">=30,<31" packageurl-python = ">=0.11" -py-serializable = ">=0.11.1,<0.12.0" +py-serializable = ">=0.16,<0.18" sortedcontainers = ">=2.4.0,<3.0.0" +[package.extras] +json-validation = ["jsonschema[format] (>=4.18,<5.0)"] +validation = ["jsonschema[format] (>=4.18,<5.0)", "lxml (>=4,<6)"] +xml-validation = ["lxml (>=4,<6)"] + [[package]] name = "defusedxml" version = "0.7.1" @@ -2801,18 +2806,18 @@ pip = "*" [[package]] name = "pip-audit" -version = "2.6.2" +version = "2.6.3" description = "A tool for scanning Python environments for known vulnerabilities" optional = false python-versions = ">=3.8" files = [ - {file = "pip_audit-2.6.2-py3-none-any.whl", hash = "sha256:ac3a4b6e977ef2c574aa8d19a5d71d12201bdb65bba2d67d9df49f53f0be5e7d"}, - {file = "pip_audit-2.6.2.tar.gz", hash = "sha256:0bbd023a199a104b29f949f063a872d41113b5a9048285666820fa35a76a7794"}, + {file = "pip_audit-2.6.3-py3-none-any.whl", hash = "sha256:216983210db4a15393f9e80e4d24a805f5767e4c8e0c31fc70c336acc629613b"}, + {file = "pip_audit-2.6.3.tar.gz", hash = "sha256:bd796066f69684b2f4fc2c2b6d222589e23190db0bbde069cea5c2b0be2cc57d"}, ] [package.dependencies] CacheControl = {version = ">=0.13.0", extras = ["filecache"]} -cyclonedx-python-lib = ">=4,<6" +cyclonedx-python-lib = ">=5,<7" html5lib = ">=1.1" packaging = ">=23.0.0" pip-api = ">=0.0.28" @@ -2824,7 +2829,7 @@ toml = ">=0.10" [package.extras] dev = ["build", "bump (>=1.3.2)", "pip-audit[doc,lint,test]"] doc = ["pdoc"] -lint = ["interrogate", "mypy", "ruff (<0.1.9)", "types-html5lib", "types-requests", "types-toml"] +lint = ["interrogate", "mypy", "ruff (<0.1.12)", "types-html5lib", "types-requests", "types-toml"] test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"] [[package]] @@ -3125,13 +3130,13 @@ files = [ [[package]] name = "py-serializable" -version = "0.11.1" +version = "0.17.1" description = "Library for serializing and deserializing Python Objects to and from JSON and XML." optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "py-serializable-0.11.1.tar.gz", hash = "sha256:ba0e1287b9e4f645a5334f1913abd8e647e7250209f84f55dce3909498a6f586"}, - {file = "py_serializable-0.11.1-py3-none-any.whl", hash = "sha256:79e21f0672822e6200b15f45ce9f636e8126466f62dbd7d488c67313c72b5c3e"}, + {file = "py-serializable-0.17.1.tar.gz", hash = "sha256:875bb9c01df77f563dfcd1e75bb4244b5596083d3aad4ccd3fb63e1f5a9d3e5f"}, + {file = "py_serializable-0.17.1-py3-none-any.whl", hash = "sha256:389c2254d912bec3a44acdac667c947d73c59325050d5ae66386e1ed7108a45a"}, ] [package.dependencies] From d3ca06fec21cb8edddd9f361741a93e68d3c92bf Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 9 Jan 2024 13:36:57 -0800 Subject: [PATCH 041/259] fix --- app/delivery/send_to_providers.py | 5 ++++- app/user/rest.py | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 79bf77fd2..f21e3928e 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -77,7 +77,10 @@ def send_sms_to_provider(notification): notification.job_row_number, ) except BaseException: - my_phone = redis_store.get(f"2facode_{notification.id}") + key = f"2facode{notification.id}" + key = key.replace("-", "") + key = key.replace(" ", "") + my_phone = redis_store.get(key) if my_phone: my_phone = my_phone.decode("utf-8") if my_phone is None: diff --git a/app/user/rest.py b/app/user/rest.py index f5dda2a93..a63292cc0 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -351,8 +351,12 @@ def create_2fa_code( reply_to_text=reply_to, ) - redis_store.set(f"2facode_{saved_notification.id}", recipient, ex=1800) - stored_recipient = redis_store.get("2facode_{saved_notification.id}") + key = f"2facode{saved_notification.id}" + key = key.replace("-", "") + key = key.replace(" ", "") + recipient = str(recipient) + redis_store.set(key, recipient) + stored_recipient = redis_store.get(key) if stored_recipient: current_app.logger.info("IN user/rest.py we saved the recipient of the 2facode to redis!") else: From 5366992d1a7d06f504a72c883deb5c2dbbb6584c Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 9 Jan 2024 14:01:03 -0800 Subject: [PATCH 042/259] format --- app/delivery/send_to_providers.py | 5 ++--- app/user/rest.py | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index f21e3928e..be97a83c6 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -77,10 +77,9 @@ def send_sms_to_provider(notification): notification.job_row_number, ) except BaseException: - key = f"2facode{notification.id}" - key = key.replace("-", "") - key = key.replace(" ", "") + key = f"2facode-{notification.id}".replace(" ", "") my_phone = redis_store.get(key) + if my_phone: my_phone = my_phone.decode("utf-8") if my_phone is None: diff --git a/app/user/rest.py b/app/user/rest.py index a63292cc0..d9b23a677 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -351,9 +351,7 @@ def create_2fa_code( reply_to_text=reply_to, ) - key = f"2facode{saved_notification.id}" - key = key.replace("-", "") - key = key.replace(" ", "") + key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) redis_store.set(key, recipient) stored_recipient = redis_store.get(key) From 2f9b98c07e20c3faca8fdb9b98009c4a09ed0f36 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 9 Jan 2024 14:28:24 -0800 Subject: [PATCH 043/259] debug messages --- app/delivery/send_to_providers.py | 2 ++ app/user/rest.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index be97a83c6..8d726e94a 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -82,6 +82,8 @@ def send_sms_to_provider(notification): if my_phone: my_phone = my_phone.decode("utf-8") + # TODO REMOVE + current_app.logger.info(f"IN SEND TO PROVIDERS, WHERE WE GET THE VALUE, KEY IS {key} and value is {my_phone}") if my_phone is None: si = notification.service_id ji = notification.job_id diff --git a/app/user/rest.py b/app/user/rest.py index d9b23a677..c4e593d39 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -353,8 +353,13 @@ def create_2fa_code( key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) + # TODO REMOVE + current_app.logger.info(f"IN REST, WHERE WE SET THE VALUE, KEY IS {key} and value is {recipient}") redis_store.set(key, recipient) stored_recipient = redis_store.get(key) + # TODO REMOVE + current_app.logger.info(f"IN REST, WHERE WE GET THE VALUE, KEY IS {key} and value is {stored_recipient}") + if stored_recipient: current_app.logger.info("IN user/rest.py we saved the recipient of the 2facode to redis!") else: From 572c8ebb8480ed7eb17d10aaa74f02a29693f3c2 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 9 Jan 2024 14:45:11 -0800 Subject: [PATCH 044/259] reformat --- app/delivery/send_to_providers.py | 4 +++- app/user/rest.py | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 8d726e94a..b258c41c4 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -83,7 +83,9 @@ def send_sms_to_provider(notification): if my_phone: my_phone = my_phone.decode("utf-8") # TODO REMOVE - current_app.logger.info(f"IN SEND TO PROVIDERS, WHERE WE GET THE VALUE, KEY IS {key} and value is {my_phone}") + current_app.logger.info( + f"IN SEND TO PROVIDERS, WHERE WE GET THE VALUE, KEY IS {key} and value is {my_phone}" + ) if my_phone is None: si = notification.service_id ji = notification.job_id diff --git a/app/user/rest.py b/app/user/rest.py index c4e593d39..92646a5ae 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -354,16 +354,24 @@ def create_2fa_code( key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) # TODO REMOVE - current_app.logger.info(f"IN REST, WHERE WE SET THE VALUE, KEY IS {key} and value is {recipient}") + current_app.logger.info( + f"IN REST, WHERE WE SET THE VALUE, KEY IS {key} and value is {recipient}" + ) redis_store.set(key, recipient) stored_recipient = redis_store.get(key) # TODO REMOVE - current_app.logger.info(f"IN REST, WHERE WE GET THE VALUE, KEY IS {key} and value is {stored_recipient}") + current_app.logger.info( + f"IN REST, WHERE WE GET THE VALUE, KEY IS {key} and value is {stored_recipient}" + ) if stored_recipient: - current_app.logger.info("IN user/rest.py we saved the recipient of the 2facode to redis!") + current_app.logger.info( + "IN user/rest.py we saved the recipient of the 2facode to redis!" + ) else: - current_app.logger.info("IN user/rest.py we did NOT save the recipient of the 2facode to redis!") + current_app.logger.info( + "IN user/rest.py we did NOT save the recipient of the 2facode to redis!" + ) # Assume that we never want to observe the Notify service's research mode # setting for this notification - we still need to be able to log into the # admin even if we're doing user research using this service: From 3dcb8d194e483e3c25db1d2aa906fbb49f802fa9 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 09:10:52 -0800 Subject: [PATCH 045/259] more debug --- app/user/rest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/user/rest.py b/app/user/rest.py index 92646a5ae..026da6c70 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -351,6 +351,10 @@ def create_2fa_code( reply_to_text=reply_to, ) + current_app.logger.info("TESTING REDIS") + redis_store.set("TESTKEY5", "WORKS") + current_app.logger.info(f"SHOULD SEE THE WORD 'WORKS' HERE: {redis_store.get('TESTKEY5')}") + key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) # TODO REMOVE From f67ddfdd7760c42e6ebeea8437c116db9b10750d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 17:14:01 +0000 Subject: [PATCH 046/259] Bump gitpython from 3.1.40 to 3.1.41 Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.40 to 3.1.41. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.40...3.1.41) --- updated-dependencies: - dependency-name: gitpython dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 78c637f53..43cae9845 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1513,20 +1513,20 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.40" +version = "3.1.41" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, - {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, + {file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"}, + {file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" [package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"] [[package]] name = "govuk-bank-holidays" @@ -2004,6 +2004,7 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2013,6 +2014,7 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2022,6 +2024,7 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2047,8 +2050,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2056,6 +2059,7 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, From fa34e3ab09de28acf74cd328d129f98514ef30d6 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 10 Jan 2024 13:22:19 -0500 Subject: [PATCH 047/259] Update utils to 0.2.5 This changeset updates the notification-utils library to 0.2.5. Signed-off-by: Carlo Costino --- poetry.lock | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index 43cae9845..4352df3d6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2004,7 +2004,6 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2014,7 +2013,6 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2024,7 +2022,6 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2050,8 +2047,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2059,7 +2056,6 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2177,16 +2173,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -2571,7 +2557,7 @@ requests = ">=2.0.0" [[package]] name = "notifications-utils" -version = "0.2.4" +version = "0.2.5" description = "" optional = false python-versions = ">=3.9,<3.12" @@ -2623,7 +2609,7 @@ werkzeug = "^3.0.1" type = "git" url = "https://github.com/GSA/notifications-utils.git" reference = "HEAD" -resolved_reference = "bd604dc32ea80b5d8a1157b09653e4bd4a755a6e" +resolved_reference = "4d18d2c333811fa8c2f8440feca050c21c85f7ff" [[package]] name = "numpy" From 7b2e59dede08c9c76ff2c2050e163dc76a4a6d10 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 10:30:44 -0800 Subject: [PATCH 048/259] switch to raw_get and raw_set to see exceptions --- app/delivery/send_to_providers.py | 2 +- app/user/rest.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index b258c41c4..407e39e66 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -78,7 +78,7 @@ def send_sms_to_provider(notification): ) except BaseException: key = f"2facode-{notification.id}".replace(" ", "") - my_phone = redis_store.get(key) + my_phone = redis_store.raw_get(key) if my_phone: my_phone = my_phone.decode("utf-8") diff --git a/app/user/rest.py b/app/user/rest.py index 026da6c70..e4544c083 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -352,7 +352,7 @@ def create_2fa_code( ) current_app.logger.info("TESTING REDIS") - redis_store.set("TESTKEY5", "WORKS") + redis_store.raw_set("TESTKEY5", "WORKS") current_app.logger.info(f"SHOULD SEE THE WORD 'WORKS' HERE: {redis_store.get('TESTKEY5')}") key = f"2facode-{saved_notification.id}".replace(" ", "") @@ -361,8 +361,8 @@ def create_2fa_code( current_app.logger.info( f"IN REST, WHERE WE SET THE VALUE, KEY IS {key} and value is {recipient}" ) - redis_store.set(key, recipient) - stored_recipient = redis_store.get(key) + redis_store.raw_set(key, recipient) + stored_recipient = redis_store.raw_get(key) # TODO REMOVE current_app.logger.info( f"IN REST, WHERE WE GET THE VALUE, KEY IS {key} and value is {stored_recipient}" From e15122d6f58e343859379d5e93c5bff367c99da8 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 11:00:50 -0800 Subject: [PATCH 049/259] mock redis --- tests/app/user/test_rest_verify.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 7e2dae307..e310ccf0e 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -453,6 +453,11 @@ def test_send_user_email_code( deliver_email = mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") sample_user.auth_type = auth_type + mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") + mock_redis_get.return_value="foo" + + mock_redis_set = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") + admin_request.post( "user.send_user_2fa_code", code_type="email", From f6ba9b4d7478d6358b8569d8a0a5f52f28c1e35d Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 11:14:04 -0800 Subject: [PATCH 050/259] mock redis --- tests/app/user/test_rest_verify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index e310ccf0e..3e1b3250b 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -456,7 +456,7 @@ def test_send_user_email_code( mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") mock_redis_get.return_value="foo" - mock_redis_set = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") + mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") admin_request.post( "user.send_user_2fa_code", From 6f04915ee8d084442f7d3bab16b62c88151e17a3 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 11:20:33 -0800 Subject: [PATCH 051/259] flake8 --- app/user/rest.py | 4 +++- tests/app/user/test_rest_verify.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/user/rest.py b/app/user/rest.py index e4544c083..6cfa8edea 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -353,7 +353,9 @@ def create_2fa_code( current_app.logger.info("TESTING REDIS") redis_store.raw_set("TESTKEY5", "WORKS") - current_app.logger.info(f"SHOULD SEE THE WORD 'WORKS' HERE: {redis_store.get('TESTKEY5')}") + current_app.logger.info( + f"SHOULD SEE THE WORD 'WORKS' HERE: {redis_store.get('TESTKEY5')}" + ) key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 3e1b3250b..d7d42a019 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -454,7 +454,7 @@ def test_send_user_email_code( sample_user.auth_type = auth_type mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") - mock_redis_get.return_value="foo" + mock_redis_get.return_value = "foo" mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") From c422f8eb4d48bf82ea606db86a09eb211069c98c Mon Sep 17 00:00:00 2001 From: Tim Lowden Date: Wed, 10 Jan 2024 14:33:05 -0500 Subject: [PATCH 052/259] Update all.md Adding link to spreadsheet --- docs/all.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/all.md b/docs/all.md index 37a133b36..c74b11af3 100644 --- a/docs/all.md +++ b/docs/all.md @@ -993,6 +993,8 @@ Once you have a number, it must be set in the app in one of two ways: * +18447342791 * +18447525067 +For a full list of phone numbers in trial and production, team members can access a [tracking list here](https://docs.google.com/spreadsheets/d/1lq3Wi_up7EkcKvmwO3oTw30m7kVt1iXvdS3KAp0smh4/edit#gid=0). + Data Storage Policies & Procedures ================================== From 97baa4c184da214eb0631ca72b6131b60f98144f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 11:38:23 -0800 Subject: [PATCH 053/259] flake8 --- tests/app/user/test_rest_verify.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index d7d42a019..f6f29f518 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -206,6 +206,10 @@ def test_send_user_sms_code(client, sample_user, sms_code_template, mocker): """ notify_service = dao_fetch_service_by_id(current_app.config["NOTIFY_SERVICE_ID"]) + mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") + mock_redis_get.return_value = "foo" + + mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") auth_header = create_admin_authorization_header() mocked = mocker.patch("app.user.rest.create_secret_code", return_value="11111") mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") @@ -238,6 +242,11 @@ def test_send_user_code_for_sms_with_optional_to_field( """ Tests POST endpoint /user//sms-code with optional to field """ + + mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") + mock_redis_get.return_value = "foo" + + mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") to_number = "+447119876757" mocked = mocker.patch("app.user.rest.create_secret_code", return_value="11111") mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") @@ -482,6 +491,11 @@ def test_send_user_email_code_with_urlencoded_next_param( ): mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") + mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") + mock_redis_get.return_value = "foo" + + mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") + data = {"to": None, "next": "/services"} admin_request.post( "user.send_user_2fa_code", @@ -556,6 +570,13 @@ def test_user_verify_email_code_fails_if_code_already_used( def test_send_user_2fa_code_sends_from_number_for_international_numbers( client, sample_user, mocker, sms_code_template ): + + + mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") + mock_redis_get.return_value = "foo" + + mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") + sample_user.mobile_number = "+601117224412" auth_header = create_admin_authorization_header() mocker.patch("app.user.rest.create_secret_code", return_value="11111") From ca199c44918710eb071d6d65c8d101509889e8a0 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 11:49:20 -0800 Subject: [PATCH 054/259] flake8 --- tests/app/user/test_rest_verify.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index f6f29f518..bd89b9fd8 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -570,8 +570,6 @@ def test_user_verify_email_code_fails_if_code_already_used( def test_send_user_2fa_code_sends_from_number_for_international_numbers( client, sample_user, mocker, sms_code_template ): - - mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") mock_redis_get.return_value = "foo" From 20ba41310c628fd112519b1294ed8fa142b02aeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 21:38:52 +0000 Subject: [PATCH 055/259] Bump newrelic from 9.3.0 to 9.4.0 Bumps [newrelic](https://github.com/newrelic/newrelic-python-agent) from 9.3.0 to 9.4.0. - [Release notes](https://github.com/newrelic/newrelic-python-agent/releases) - [Commits](https://github.com/newrelic/newrelic-python-agent/compare/v9.3.0...v9.4.0) --- updated-dependencies: - dependency-name: newrelic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 62 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4352df3d6..08e9caa9d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2004,6 +2004,7 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2013,6 +2014,7 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2022,6 +2024,7 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2047,8 +2050,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2056,6 +2059,7 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2173,6 +2177,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -2501,26 +2515,40 @@ files = [ [[package]] name = "newrelic" -version = "9.3.0" +version = "9.4.0" description = "New Relic Python Agent" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "newrelic-9.3.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f25980a8c86bda75344b5b22edd5d6ad41777776e1ed8a495eb6e38e9813b02c"}, - {file = "newrelic-9.3.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:adefb6620c5a5d75b4bf3ec565cc4d91abcb5cc4e5569f5f82ab29fa3d5aa2d9"}, - {file = "newrelic-9.3.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:27056ab8a3cf39787fc1f93f55243749dd25786f65b15032b6fbb3e8534f4c2a"}, - {file = "newrelic-9.3.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:663fa1c074661f93abf681c8f6028de64744c67f004b722835de1372b6bc4d19"}, - {file = "newrelic-9.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83346c8f0bcb8f07f74c88f6073e4d44a2e2b3eeec5b2ebe8c450ae695d02b88"}, - {file = "newrelic-9.3.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2187078d7b0054b30f39dbf891cb2caa71a7046f6d0258fb8c0fcfce70777774"}, - {file = "newrelic-9.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0f5edd4eba3d62742b3b0730bb4f826be015dd7fbb9c455b01c410421661a2"}, - {file = "newrelic-9.3.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8577a0f733174bee70a147f71aa061fb44a593a1be841feffe12dff480c6e02e"}, - {file = "newrelic-9.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:234594655ac0fbe938d34ce5d5d38549d0f5cc11d0552170903ad09574bb4499"}, - {file = "newrelic-9.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce38949404e974b566b21487394f5ea36a1fb80ba36cc4a6e8fb968d2e150ab"}, - {file = "newrelic-9.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a750ffed8aedacdafcb548b3d3f45630a96862db630c9f72520ebbfe91e4e9e0"}, - {file = "newrelic-9.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01d0f0c22b1290dbd2756ef120cfbe154179aae35e1dfc579f8bfd574066105"}, - {file = "newrelic-9.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e860c6eacfdef879f23fbbf7d76d8bbb90b725a1c422f62439c6edfceebc21"}, - {file = "newrelic-9.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4caad3017cc978f3130fe2f3933f233c214a4850a595458b733282b3b7f7e886"}, - {file = "newrelic-9.3.0.tar.gz", hash = "sha256:c2dd685527433f6b6fbffe58f83852b46c24b9713ebb8ee7af647e04c2de3ee4"}, + {file = "newrelic-9.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:ed7a8ab3fb4425d7d55c44517f24f5259eab2045d4b24a21cf9439d9a6f53ffd"}, + {file = "newrelic-9.4.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3d1253c67bc371f8eca386a8d96e0ae29861359ca5b62e7e5c4f87083b8a8930"}, + {file = "newrelic-9.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:04e055c3a4a9b2b2719bc83e5e2935f700f3943025d169873b8952b9576d3962"}, + {file = "newrelic-9.4.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1f28ef38aed097600e149755b4b102edcb18e9b4e270d415154699cec2cb1a05"}, + {file = "newrelic-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f177f27cfbcd3673359ce0d80f8e418fc706b019ba936bbba9aa08585dff5f0"}, + {file = "newrelic-9.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d3d4902431de420baf1c9801c17adb8d6accb01704a6206dcdbb3ab6cc83ca8"}, + {file = "newrelic-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66df7709bc8da0a79aea34eaf3f38410eeed2b84abb53fcac78130a0f4fb7eb4"}, + {file = "newrelic-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89329509f6c37f444eedd4a6abc8e569c0faa74e42538e31288e75610ae5fb4e"}, + {file = "newrelic-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec7da405d968b4f31103d6db407e409ad1cda1403efb22862bdefdd44a9742a"}, + {file = "newrelic-9.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3183168cc1a7d79b2b0be147ea657105a83c631e33edef5a17c469052f9ae944"}, + {file = "newrelic-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dcd592bb0115694ca4c8e4b9264ffc05af10c365dfd95c0b8bc10b89c03bb282"}, + {file = "newrelic-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0f74d88c1b279ffadae325ecebe35a3dab662ae074f868a03cfdc5be5214b3d3"}, + {file = "newrelic-9.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d61eb8972323d57919cefef8ba1faad325a7818939d00ee2bc9698f96a880ca8"}, + {file = "newrelic-9.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72267bf3c2ab493813d7f5fb084a722c52da1493b376c56ff1d6a637dc451bed"}, + {file = "newrelic-9.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:78567ccedf2bdb75dc567bec318e221dcfab4681bd05babc398ce1b78d60f67b"}, + {file = "newrelic-9.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7a0fcbe0a581bea77346e291a0329fe7fce5226252c69d41fd4d91ba4d7c5493"}, + {file = "newrelic-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9162bf466d8ed7c973a597115434855f4e37888c2350d56b25f9b11875d20f2"}, + {file = "newrelic-9.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5d9e8f0f9ef0920dfad046e70dd91fcecab225d1ea5c09369a701c24e622c69"}, + {file = "newrelic-9.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6b63ad134842b92521845864cbc47a7559d189cf8a706220a90974ad2a9510da"}, + {file = "newrelic-9.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:895bfa0e1497dc061139b83dd4a5ff7b40b3eafa62a2e54c5bce2e616c72c762"}, + {file = "newrelic-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b40049de63700f518a9ee4e2b97881c5796e8a72fe9684bd0ae8e11d1ec17755"}, + {file = "newrelic-9.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eb1a8571d0434d62eac401797e835feed1b39a918434ab1e72e486b39bc0e87"}, + {file = "newrelic-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:195bc995b227ed0cdbe2c95dc27ae057002432a7c84532e72ba57a430265054a"}, + {file = "newrelic-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:895d0e78da2b3d5abbde7e8856692944ada2b744b26cdb339d5cbfa4461e9acb"}, + {file = "newrelic-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d11de77ba0e9e7921b21385f8300172248e865cd38c16165ee48adc9af35c746"}, + {file = "newrelic-9.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3f71b218febb7a48a8df048a7248025e1bd86c76a3144602e56d2a9f7d1740d"}, + {file = "newrelic-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f5cecccf3e85dccfa6b5ad7be21ae8940c46c579e2979890d84969546322d715"}, + {file = "newrelic-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f9fa697c355790b52e3180044349bbc2822230ca6f98d6292df4142080cd20de"}, + {file = "newrelic-9.4.0.tar.gz", hash = "sha256:84437107e0d47be070a17b31f48b54abea7d4951b5ca89f5c2416a2fa6ab42b8"}, ] [package.extras] From 38e802da78a9822d26e9027d7cd9037fd2901655 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 10 Jan 2024 14:21:13 -0800 Subject: [PATCH 056/259] set redis enabled --- .github/workflows/deploy.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7e8d2bc9e..cf9a145b6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -62,6 +62,7 @@ jobs: NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }} NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }} + REDIS_ENABLED: 1 with: cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} @@ -75,6 +76,7 @@ jobs: --var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY" --var NOTIFY_E2E_TEST_EMAIL="$NOTIFY_E2E_TEST_EMAIL" --var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD" + --var REDIS_ENABLED="$REDIS_ENABLED" - name: Check for changes to egress config id: changed-egress-config From 0a28abafd52d6a43d7a2b8296b84873cbc756708 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:22:03 +0000 Subject: [PATCH 057/259] Bump marshmallow from 3.20.1 to 3.20.2 Bumps [marshmallow](https://github.com/marshmallow-code/marshmallow) from 3.20.1 to 3.20.2. - [Changelog](https://github.com/marshmallow-code/marshmallow/blob/dev/CHANGELOG.rst) - [Commits](https://github.com/marshmallow-code/marshmallow/compare/3.20.1...3.20.2) --- updated-dependencies: - dependency-name: marshmallow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 14 +++++++------- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 08e9caa9d..8c7d0665e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2221,22 +2221,22 @@ files = [ [[package]] name = "marshmallow" -version = "3.20.1" +version = "3.20.2" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.20.1-py3-none-any.whl", hash = "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c"}, - {file = "marshmallow-3.20.1.tar.gz", hash = "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889"}, + {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"}, + {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"}, ] [package.dependencies] packaging = ">=17.0" [package.extras] -dev = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.13)", "autodocsumm (==0.2.11)", "sphinx (==7.0.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["flake8 (==6.0.0)", "flake8-bugbear (==23.7.10)", "mypy (==1.4.1)", "pre-commit (>=2.4,<4.0)"] +dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["pre-commit (>=2.4,<4.0)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -4720,4 +4720,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "a70284fcacadde70db3cf216efc388de74fb67c1f00085abcaee37b52ff6eedd" +content-hash = "b24b3ba1e6c3daf354471c796ba6f996cf24426c956ce6e860286d3de5c7bf90" diff --git a/pyproject.toml b/pyproject.toml index 469f2b35e..37f761c0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ gunicorn = {version = "==21.2.0", extras = ["eventlet"]} iso8601 = "==2.1.0" jsonschema = {version = "==4.20.0", extras = ["format"]} lxml = "==5.1.0" -marshmallow = "==3.20.1" +marshmallow = "==3.20.2" marshmallow-sqlalchemy = "==0.29.0" newrelic = "*" notifications-python-client = "==8.2.0" From 4e6440b1085afd2baa4b8f9d588a0c706f3b3a7c Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Thu, 11 Jan 2024 11:20:43 -0500 Subject: [PATCH 058/259] Update jinja2 to latest release This changeset updates the jinja2 library to address a pip-audit finding. Signed-off-by: Carlo Costino --- poetry.lock | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index 08e9caa9d..cd5880196 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1822,13 +1822,13 @@ trio = ["async_generator", "trio"] [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -2004,7 +2004,6 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2014,7 +2013,6 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2024,7 +2022,6 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2050,8 +2047,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2059,7 +2056,6 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2177,16 +2173,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, From 92eb3c49cf238fe0114deb9a87fb5d1b3ec9c672 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Thu, 11 Jan 2024 11:15:09 -0500 Subject: [PATCH 059/259] Mirror Admin REDIS_ENABLED config This changeset adjusts the REDIS_ENABLED environment variable to match how the admin app is set up to make sure the API properly connects to the Redis service. Signed-off-by: Carlo Costino --- .github/workflows/deploy.yml | 2 -- deploy-config/demo.yml | 1 + deploy-config/production.yml | 1 + deploy-config/sandbox.yml | 1 + deploy-config/staging.yml | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cf9a145b6..7e8d2bc9e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -62,7 +62,6 @@ jobs: NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }} NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }} NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }} - REDIS_ENABLED: 1 with: cf_username: ${{ secrets.CLOUDGOV_USERNAME }} cf_password: ${{ secrets.CLOUDGOV_PASSWORD }} @@ -76,7 +75,6 @@ jobs: --var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY" --var NOTIFY_E2E_TEST_EMAIL="$NOTIFY_E2E_TEST_EMAIL" --var NOTIFY_E2E_TEST_PASSWORD="$NOTIFY_E2E_TEST_PASSWORD" - --var REDIS_ENABLED="$REDIS_ENABLED" - name: Check for changes to egress config id: changed-egress-config diff --git a/deploy-config/demo.yml b/deploy-config/demo.yml index aede2dcc2..38e238c30 100644 --- a/deploy-config/demo.yml +++ b/deploy-config/demo.yml @@ -6,4 +6,5 @@ worker_memory: 512M scheduler_memory: 256M public_api_route: notify-api-demo.app.cloud.gov admin_base_url: https://notify-demo.app.cloud.gov +redis_enabled: 1 default_toll_free_number: "+18337581259" diff --git a/deploy-config/production.yml b/deploy-config/production.yml index cbbd0f514..2152eae42 100644 --- a/deploy-config/production.yml +++ b/deploy-config/production.yml @@ -6,4 +6,5 @@ worker_memory: 512M scheduler_memory: 256M public_api_route: notify-api.app.cloud.gov admin_base_url: https://beta.notify.gov +redis_enabled: 1 default_toll_free_number: "+18447952263" diff --git a/deploy-config/sandbox.yml b/deploy-config/sandbox.yml index 64a652b44..d94339837 100644 --- a/deploy-config/sandbox.yml +++ b/deploy-config/sandbox.yml @@ -6,6 +6,7 @@ worker_memory: 512M scheduler_memory: 256M public_api_route: notify-api-sandbox.app.cloud.gov admin_base_url: https://notify-sandbox.app.cloud.gov +redis_enabled: 1 default_toll_free_number: "+18885989205" ADMIN_CLIENT_SECRET: sandbox-notify-secret-key DANGEROUS_SALT: sandbox-notify-salt diff --git a/deploy-config/staging.yml b/deploy-config/staging.yml index f6a2de501..c338e7bb1 100644 --- a/deploy-config/staging.yml +++ b/deploy-config/staging.yml @@ -6,4 +6,5 @@ worker_memory: 512M scheduler_memory: 256M public_api_route: notify-api-staging.app.cloud.gov admin_base_url: https://notify-staging.app.cloud.gov +redis_enabled: 1 default_toll_free_number: "+18556438890" From 6332399b5bdd5d01f120d4a03634e9b36e33adf9 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Thu, 11 Jan 2024 12:05:07 -0500 Subject: [PATCH 060/259] Really make sure Redis is enabled This changeset should cover the rest of the missing pieces to make sure Redis connectivity is truly established in the API app. Signed-off-by: Carlo Costino --- app/config.py | 2 +- manifest.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/config.py b/app/config.py index 61feb6ae7..49cbd89c0 100644 --- a/app/config.py +++ b/app/config.py @@ -89,7 +89,7 @@ class Config(object): PAGE_SIZE = 50 API_PAGE_SIZE = 250 REDIS_URL = cloud_config.redis_url - REDIS_ENABLED = getenv("REDIS_ENABLED", "0") == "1" + REDIS_ENABLED = getenv("REDIS_ENABLED", "1") == "1" EXPIRE_CACHE_TEN_MINUTES = 600 EXPIRE_CACHE_EIGHT_DAYS = 8 * 24 * 60 * 60 diff --git a/manifest.yml b/manifest.yml index eb78a7dc1..eb42d7a74 100644 --- a/manifest.yml +++ b/manifest.yml @@ -40,6 +40,7 @@ applications: NEW_RELIC_ENVIRONMENT: ((env)) NEW_RELIC_LICENSE_KEY: ((NEW_RELIC_LICENSE_KEY)) + REDIS_ENABLED: ((redis_enabled)) NOTIFY_ENVIRONMENT: ((env)) API_HOST_NAME: https://((public_api_route)) ADMIN_BASE_URL: ((admin_base_url)) From ea8caf1ad9845e2707af900d956b49fb31e8869f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 11 Jan 2024 09:38:00 -0800 Subject: [PATCH 061/259] remove debugging for 2fa issue --- app/delivery/send_to_providers.py | 5 +---- app/user/rest.py | 22 ---------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 407e39e66..e370a1111 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -82,10 +82,7 @@ def send_sms_to_provider(notification): if my_phone: my_phone = my_phone.decode("utf-8") - # TODO REMOVE - current_app.logger.info( - f"IN SEND TO PROVIDERS, WHERE WE GET THE VALUE, KEY IS {key} and value is {my_phone}" - ) + if my_phone is None: si = notification.service_id ji = notification.job_id diff --git a/app/user/rest.py b/app/user/rest.py index 6cfa8edea..d5746147d 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -351,33 +351,11 @@ def create_2fa_code( reply_to_text=reply_to, ) - current_app.logger.info("TESTING REDIS") - redis_store.raw_set("TESTKEY5", "WORKS") - current_app.logger.info( - f"SHOULD SEE THE WORD 'WORKS' HERE: {redis_store.get('TESTKEY5')}" - ) - key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) - # TODO REMOVE - current_app.logger.info( - f"IN REST, WHERE WE SET THE VALUE, KEY IS {key} and value is {recipient}" - ) redis_store.raw_set(key, recipient) stored_recipient = redis_store.raw_get(key) - # TODO REMOVE - current_app.logger.info( - f"IN REST, WHERE WE GET THE VALUE, KEY IS {key} and value is {stored_recipient}" - ) - if stored_recipient: - current_app.logger.info( - "IN user/rest.py we saved the recipient of the 2facode to redis!" - ) - else: - current_app.logger.info( - "IN user/rest.py we did NOT save the recipient of the 2facode to redis!" - ) # Assume that we never want to observe the Notify service's research mode # setting for this notification - we still need to be able to log into the # admin even if we're doing user research using this service: From a8955ef78fe10de15e375748b8298fcc2e26893e Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 11 Jan 2024 09:50:59 -0800 Subject: [PATCH 062/259] fix flake8 --- app/user/rest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/user/rest.py b/app/user/rest.py index d5746147d..e98151f1e 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -354,7 +354,6 @@ def create_2fa_code( key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) redis_store.raw_set(key, recipient) - stored_recipient = redis_store.raw_get(key) # Assume that we never want to observe the Notify service's research mode # setting for this notification - we still need to be able to log into the From c3cb60f3b098e9d719525214ed83c250181a099e Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 11 Jan 2024 11:11:12 -0800 Subject: [PATCH 063/259] fix registration so email gets sent --- app/delivery/send_to_providers.py | 24 ++++++++++++++---------- app/user/rest.py | 8 +++++++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index e370a1111..d874106d2 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -69,29 +69,30 @@ def send_sms_to_provider(notification): # We start by trying to get the phone number from a job in s3. If we fail, we assume # the phone number is for the verification code on login, which is not a job. - my_phone = None + recipient = None try: - my_phone = get_phone_number_from_s3( + recipient = get_phone_number_from_s3( notification.service_id, notification.job_id, notification.job_row_number, ) except BaseException: + # It is our 2facode, maybe key = f"2facode-{notification.id}".replace(" ", "") - my_phone = redis_store.raw_get(key) + recipient = redis_store.raw_get(key) - if my_phone: - my_phone = my_phone.decode("utf-8") + if recipient: + recipient = recipient.decode("utf-8") - if my_phone is None: + if recipient is None: si = notification.service_id ji = notification.job_id jrn = notification.job_row_number raise Exception( - f"The phone number for (Service ID: {si}; Job ID: {ji}; Job Row Number {jrn} was not found." + f"The recipient for (Service ID: {si}; Job ID: {ji}; Job Row Number {jrn} was not found." ) send_sms_kwargs = { - "to": my_phone, + "to": recipient, "content": str(template), "reference": str(notification.id), "sender": notification.reply_to_text, @@ -132,10 +133,13 @@ def send_email_to_provider(notification): plain_text_email = PlainTextEmailTemplate( template_dict, values=notification.personalisation ) + # Someone needs an email, possibly new registration + recipient = redis_store.get(f"email-address-{notification.id}").decode("utf-8") + if notification.key_type == KEY_TYPE_TEST: notification.reference = str(create_uuid()) update_notification_to_sending(notification, provider) - send_email_response(notification.reference, notification.to) + send_email_response(notification.reference, recipient) else: from_address = '"{}" <{}@{}>'.format( service.name, @@ -145,7 +149,7 @@ def send_email_to_provider(notification): reference = provider.send_email( from_address, - notification.normalised_to, + recipient, plain_text_email.subject, body=str(plain_text_email), html_body=str(html_email), diff --git a/app/user/rest.py b/app/user/rest.py index e98151f1e..303c1a39c 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -353,7 +353,7 @@ def create_2fa_code( key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) - redis_store.raw_set(key, recipient) + redis_store.raw_set(key, recipient, ex=60 * 60) # Assume that we never want to observe the Notify service's research mode # setting for this notification - we still need to be able to log into the @@ -431,6 +431,12 @@ def send_new_user_email_verification(user_id): key_type=KEY_TYPE_NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) + + redis_store.set( + f"email-address-{saved_notification.id}", + str(user_to_send_to.email_address), + ex=60 * 60, + ) current_app.logger.info("Sending notification to queue") send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) From ab0066589c7439eda03118e4f07220248e1072ed Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 11 Jan 2024 11:51:00 -0800 Subject: [PATCH 064/259] fix registration and mock tests for redis --- app/delivery/send_to_providers.py | 5 ++-- tests/app/delivery/test_send_to_providers.py | 29 +++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index d874106d2..ac844f540 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -112,7 +112,6 @@ def send_sms_to_provider(notification): def send_email_to_provider(notification): service = SerialisedService.from_id(notification.service_id) - if not service.active: technical_failure(notification=notification) return @@ -134,8 +133,8 @@ def send_email_to_provider(notification): template_dict, values=notification.personalisation ) # Someone needs an email, possibly new registration - recipient = redis_store.get(f"email-address-{notification.id}").decode("utf-8") - + recipient = redis_store.get(f"email-address-{notification.id}") + recipient = recipient.decode("utf-8") if notification.key_type == KEY_TYPE_TEST: notification.reference = str(create_uuid()) update_notification_to_sending(notification, provider) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index 4cbf22b9a..4dfb336a0 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -85,11 +85,9 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist( ): db_notification = create_notification( template=sample_sms_template_with_html, - to_field="2028675309", personalisation={"name": "Jo"}, status="created", reply_to_text=sample_sms_template_with_html.service.get_default_sms_sender(), - normalised_to="2028675309", ) mocker.patch("app.aws_sns_client.send_sms") @@ -119,11 +117,12 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist( def test_should_send_personalised_template_to_correct_email_provider_and_persist( sample_email_template_with_html, mocker ): + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "jo.smith@example.com".encode("utf-8") + db_notification = create_notification( template=sample_email_template_with_html, - to_field="jo.smith@example.com", personalisation={"name": "Jo"}, - normalised_to="jo.smith@example.com", ) mocker.patch("app.aws_ses_client.send_email", return_value="reference") @@ -313,6 +312,9 @@ def test_send_sms_should_use_service_sms_sender( def test_send_email_to_provider_should_not_send_to_provider_when_status_is_not_created( sample_email_template, mocker ): + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "test@example.com".encode("utf-8") + notification = create_notification(template=sample_email_template, status="sending") mocker.patch("app.aws_ses_client.send_email") mocker.patch("app.delivery.send_to_providers.send_email_response") @@ -327,6 +329,9 @@ def test_send_email_should_use_service_reply_to_email( ): mocker.patch("app.aws_ses_client.send_email", return_value="reference") + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "test@example.com".encode("utf-8") + db_notification = create_notification( template=sample_email_template, reply_to_text="foo@bar.com" ) @@ -622,6 +627,9 @@ def test_should_handle_sms_sender_and_prefix_message( def test_send_email_to_provider_uses_reply_to_from_notification( sample_email_template, mocker ): + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "test@example.com".encode("utf-8") + mocker.patch("app.aws_ses_client.send_email", return_value="reference") db_notification = create_notification( @@ -661,14 +669,14 @@ def test_send_email_to_provider_should_user_normalised_to( send_mock = mocker.patch("app.aws_ses_client.send_email", return_value="reference") notification = create_notification( template=sample_email_template, - to_field="TEST@example.com", - normalised_to="test@example.com", ) + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "test@example.com".encode("utf-8") send_to_providers.send_email_to_provider(notification) send_mock.assert_called_once_with( ANY, - notification.normalised_to, + "test@example.com", ANY, body=ANY, html_body=ANY, @@ -721,6 +729,9 @@ def test_send_email_to_provider_should_return_template_if_found_in_redis( ): from app.schemas import service_schema, template_schema + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "test@example.com".encode("utf-8") + service_dict = service_schema.dump(sample_email_template.service) template_dict = template_schema.dump(sample_email_template) @@ -738,8 +749,6 @@ def test_send_email_to_provider_should_return_template_if_found_in_redis( send_mock = mocker.patch("app.aws_ses_client.send_email", return_value="reference") notification = create_notification( template=sample_email_template, - to_field="TEST@example.com", - normalised_to="test@example.com", ) send_to_providers.send_email_to_provider(notification) @@ -747,7 +756,7 @@ def test_send_email_to_provider_should_return_template_if_found_in_redis( assert mock_get_service.called is False send_mock.assert_called_once_with( ANY, - notification.normalised_to, + "test@example.com", ANY, body=ANY, html_body=ANY, From 6ab8d556e55bfba499bfa47f3a32d5a0a27fa741 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 11 Jan 2024 12:41:24 -0800 Subject: [PATCH 065/259] fix case where we dont know the phoneCarrier for any reason --- app/clients/cloudwatch/aws_cloudwatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/clients/cloudwatch/aws_cloudwatch.py b/app/clients/cloudwatch/aws_cloudwatch.py index 80ba88f87..b4c468581 100644 --- a/app/clients/cloudwatch/aws_cloudwatch.py +++ b/app/clients/cloudwatch/aws_cloudwatch.py @@ -111,7 +111,7 @@ class AwsCloudwatchClient(Client): return ( "success", message["delivery"]["providerResponse"], - message["delivery"]["phoneCarrier"], + message["delivery"].get("phoneCarrier", "Unknown Carrier"), ) log_group_name = ( From efe4cd589c91cbe0d7534972932dec0fe2386ebb Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 11 Jan 2024 13:22:13 -0800 Subject: [PATCH 066/259] more debugging messages --- app/clients/sms/aws_sns.py | 2 ++ app/delivery/send_to_providers.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/app/clients/sms/aws_sns.py b/app/clients/sms/aws_sns.py index d75122a7e..e1c872665 100644 --- a/app/clients/sms/aws_sns.py +++ b/app/clients/sms/aws_sns.py @@ -81,8 +81,10 @@ class AwsSnsClient(SmsClient): PhoneNumber=to, Message=content, MessageAttributes=attributes ) except botocore.exceptions.ClientError as e: + self.current_app.logger.error(e) raise str(e) except Exception as e: + self.current_app.logger(e) raise str(e) finally: elapsed_time = monotonic() - start_time diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index ac844f540..6db858eed 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -99,8 +99,11 @@ def send_sms_to_provider(notification): "international": notification.international, } db.session.close() # no commit needed as no changes to objects have been made above + current_app.logger.info("sending to sms") message_id = provider.send_sms(**send_sms_kwargs) + current_app.logger.info(f"got message_id {message_id}") except Exception as e: + current_app.logger.error(e) notification.billable_units = template.fragment_count dao_update_notification(notification) raise e From 59e8c493e5dc15624de517b8a5d35efddc2b93d0 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 12 Jan 2024 07:30:19 -0800 Subject: [PATCH 067/259] add stats for cache hits/misses and remove debug message --- app/aws/s3.py | 28 ++++++++++++++++++++++++ app/clients/cloudwatch/aws_cloudwatch.py | 1 - 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 2a631c326..86676f3fc 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -5,6 +5,7 @@ from boto3 import Session from expiringdict import ExpiringDict from flask import current_app +from app import redis_store from app.clients import AWS_CLIENT_CONFIG FILE_LOCATION_STRUCTURE = "service-{}-notify/{}.csv" @@ -12,6 +13,9 @@ FILE_LOCATION_STRUCTURE = "service-{}-notify/{}.csv" JOBS = ExpiringDict(max_len=100, max_age_seconds=3600 * 4) +JOBS_CACHE_HITS = "JOBS_CACHE_HITS" +JOBS_CACHE_MISSES = "JOBS_CACHE_MISSES" + def get_s3_file(bucket_name, file_location, access_key, secret_key, region): s3_file = get_s3_object(bucket_name, file_location, access_key, secret_key, region) @@ -72,6 +76,26 @@ def get_job_from_s3(service_id, job_id): return obj.get()["Body"].read().decode("utf-8") +def incr_jobs_cache_misses(): + if not redis_store.get(JOBS_CACHE_MISSES): + redis_store.set(JOBS_CACHE_MISSES, 1) + else: + redis_store.incr(JOBS_CACHE_MISSES) + hits = redis_store.get(JOBS_CACHE_HITS).decode("utf-8") + misses = redis_store.get(JOBS_CACHE_MISSES).decode("utf-8") + current_app.logger.info(f"JOBS CACHE MISS hits {hits} misses {misses}") + + +def incr_jobs_cache_hits(): + if not redis_store.get(JOBS_CACHE_HITS): + redis_store.set(JOBS_CACHE_HITS, 1) + else: + redis_store.incr(JOBS_CACHE_HITS) + hits = redis_store.get(JOBS_CACHE_HITS).decode("utf-8") + misses = redis_store.get(JOBS_CACHE_MISSES).decode("utf-8") + current_app.logger.info(f"JOBS CACHE MISS hits {hits} misses {misses}") + + def get_phone_number_from_s3(service_id, job_id, job_row_number): # We don't want to constantly pull down a job from s3 every time we need a phone number. # At the same time we don't want to store it in redis or the db @@ -80,6 +104,10 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): if job is None: job = get_job_from_s3(service_id, job_id) JOBS[job_id] = job + incr_jobs_cache_misses() + else: + incr_jobs_cache_hits() + job = job.split("\r\n") first_row = job[0] job.pop(0) diff --git a/app/clients/cloudwatch/aws_cloudwatch.py b/app/clients/cloudwatch/aws_cloudwatch.py index b4c468581..4fac575a3 100644 --- a/app/clients/cloudwatch/aws_cloudwatch.py +++ b/app/clients/cloudwatch/aws_cloudwatch.py @@ -107,7 +107,6 @@ class AwsCloudwatchClient(Client): if all_log_events and len(all_log_events) > 0: event = all_log_events[0] message = json.loads(event["message"]) - current_app.logger.info(f"MESSAGE {message}") return ( "success", message["delivery"]["providerResponse"], From 3e6229b87ada3c9bbc42960a3d61cc0bdfc4b3aa Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 12 Jan 2024 07:42:21 -0800 Subject: [PATCH 068/259] fix tests add redis mock --- tests/app/aws/test_s3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index 27df6ac6c..342f6d7df 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -66,6 +66,7 @@ def test_get_s3_file_makes_correct_call(notify_api, mocker): def test_get_phone_number_from_s3( mocker, job, job_id, job_row_number, expected_phone_number ): + mocker.patch("app.aws.s3.redis_store") get_job_mock = mocker.patch("app.aws.s3.get_job_from_s3") get_job_mock.return_value = job phone_number = get_phone_number_from_s3("service_id", job_id, job_row_number) From 590d8458d7ba2e1da1c0f75304179721b2656448 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Fri, 12 Jan 2024 12:22:52 -0500 Subject: [PATCH 069/259] Update notification-utils to 0.2.6 This changeset updates notification-utils to 0.2.6 for dependency updates. Signed-off-by: Carlo Costino --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8002e254e..90209a5e0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2571,7 +2571,7 @@ requests = ">=2.0.0" [[package]] name = "notifications-utils" -version = "0.2.5" +version = "0.2.6" description = "" optional = false python-versions = ">=3.9,<3.12" @@ -2596,7 +2596,7 @@ geojson = "^3.0.1" govuk-bank-holidays = "^0.13" idna = "^3.4" itsdangerous = "^2.1.2" -jinja2 = "^3.1.2" +jinja2 = "^3.1.3" jmespath = "^1.0.1" markupsafe = "^2.1.2" mistune = "==0.8.4" @@ -2623,7 +2623,7 @@ werkzeug = "^3.0.1" type = "git" url = "https://github.com/GSA/notifications-utils.git" reference = "HEAD" -resolved_reference = "4d18d2c333811fa8c2f8440feca050c21c85f7ff" +resolved_reference = "f183d120a8b86e655405694adde1f0d95e4e5a51" [[package]] name = "numpy" From d7971d7976aef4679b0eeed3b0bdf2265cb7d2a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:39:34 +0000 Subject: [PATCH 070/259] Bump eventlet from 0.34.2 to 0.34.3 Bumps [eventlet](https://github.com/eventlet/eventlet) from 0.34.2 to 0.34.3. - [Changelog](https://github.com/eventlet/eventlet/blob/master/NEWS) - [Commits](https://github.com/eventlet/eventlet/compare/v0.34.2...v0.34.3) --- updated-dependencies: - dependency-name: eventlet dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 25 +++++++++++++++++++------ pyproject.toml | 2 +- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 90209a5e0..7f11164d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1157,19 +1157,18 @@ pgp = ["gpg"] [[package]] name = "eventlet" -version = "0.34.2" +version = "0.34.3" description = "Highly concurrent networking library" optional = false python-versions = ">=3.7" files = [ - {file = "eventlet-0.34.2-py3-none-any.whl", hash = "sha256:eca0114398d3133f94d16590d205bbb010668a3bc7453fddadb51d63e8c1bac2"}, - {file = "eventlet-0.34.2.tar.gz", hash = "sha256:2115c7c6742e6893bf1347f82915572f8895c911cb5abaad4d3596a7daa847cc"}, + {file = "eventlet-0.34.3-py3-none-any.whl", hash = "sha256:3093f2822ce2f40792bf9aa7eb8920fe3b90db785d3bea7640c50412969dcfd7"}, + {file = "eventlet-0.34.3.tar.gz", hash = "sha256:ed2d28a64414a001894b3baf5b650f2c9596b00d57f57d4d7a38f9d3d0c252e8"}, ] [package.dependencies] dnspython = ">=1.15.0" greenlet = ">=1.0" -six = ">=1.10.0" [package.extras] dev = ["black", "build", "commitizen", "isort", "pip-tools", "pre-commit", "twine"] @@ -2004,6 +2003,7 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2013,6 +2013,7 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2022,6 +2023,7 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2047,8 +2049,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2056,6 +2058,7 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2173,6 +2176,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -4706,4 +4719,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "b24b3ba1e6c3daf354471c796ba6f996cf24426c956ce6e860286d3de5c7bf90" +content-hash = "de049f487d61d11519ec5c1c3e5066f0e9fa46eb2201891f8733aebd63c1de28" diff --git a/pyproject.toml b/pyproject.toml index 37f761c0c..e887b5d47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ click-didyoumean = "==0.3.0" click-plugins = "==1.1.1" click-repl = "==0.3.0" deprecated = "==1.2.14" -eventlet = "==0.34.2" +eventlet = "==0.34.3" expiringdict = "==1.2.2" flask = "~=2.3" flask-bcrypt = "==1.0.1" From 79c61b39846fda84b089442536dfc90160bde843 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 18:33:08 +0000 Subject: [PATCH 071/259] Bump marshmallow-sqlalchemy from 0.29.0 to 0.30.0 Bumps [marshmallow-sqlalchemy](https://github.com/marshmallow-code/marshmallow-sqlalchemy) from 0.29.0 to 0.30.0. - [Changelog](https://github.com/marshmallow-code/marshmallow-sqlalchemy/blob/dev/CHANGELOG.rst) - [Commits](https://github.com/marshmallow-code/marshmallow-sqlalchemy/compare/0.29.0...0.30.0) --- updated-dependencies: - dependency-name: marshmallow-sqlalchemy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 16 ++++++++-------- pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7f11164d8..c12c2fb9b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2240,13 +2240,13 @@ tests = ["pytest", "pytz", "simplejson"] [[package]] name = "marshmallow-sqlalchemy" -version = "0.29.0" +version = "0.30.0" description = "SQLAlchemy integration with the marshmallow (de)serialization library" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "marshmallow-sqlalchemy-0.29.0.tar.gz", hash = "sha256:3523a774390ef0c1c0f7c708a7519809c5396cf608720f14f55c36f74ff5bbec"}, - {file = "marshmallow_sqlalchemy-0.29.0-py2.py3-none-any.whl", hash = "sha256:3cee0bf61ed10687c0a41448e1916649b28222334a02f7b937c39d1c69c18bee"}, + {file = "marshmallow-sqlalchemy-0.30.0.tar.gz", hash = "sha256:29ad0a4fd1b4a1e52dcb07f9673d284a6b0795141916cc2169d4ee9a5d007347"}, + {file = "marshmallow_sqlalchemy-0.30.0-py2.py3-none-any.whl", hash = "sha256:808c1e95bf72f4491ad2f3498e73116b33074d7dcef79f0e6997d0d37d648ac7"}, ] [package.dependencies] @@ -2255,9 +2255,9 @@ packaging = ">=21.3" SQLAlchemy = ">=1.4.40,<3.0" [package.extras] -dev = ["flake8 (==6.0.0)", "flake8-bugbear (==23.2.13)", "pre-commit (==3.1.0)", "pytest", "pytest-lazy-fixture (>=0.6.2)", "tox"] -docs = ["alabaster (==0.7.13)", "sphinx (==6.1.3)", "sphinx-issues (==3.0.1)"] -lint = ["flake8 (==6.0.0)", "flake8-bugbear (==23.2.13)", "pre-commit (==3.1.0)"] +dev = ["flake8 (==7.0.0)", "flake8-bugbear (==23.12.2)", "pre-commit (==3.6.0)", "pytest", "pytest-lazy-fixture (>=0.6.2)", "tox"] +docs = ["alabaster (==0.7.13)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)"] +lint = ["flake8 (==7.0.0)", "flake8-bugbear (==23.12.2)", "pre-commit (==3.6.0)"] tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"] [[package]] @@ -4719,4 +4719,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "de049f487d61d11519ec5c1c3e5066f0e9fa46eb2201891f8733aebd63c1de28" +content-hash = "e915371224cb1a76603cf5b3e57c14ddce536d933d05ac9520da8e79b4b69665" diff --git a/pyproject.toml b/pyproject.toml index e887b5d47..55a605f11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ iso8601 = "==2.1.0" jsonschema = {version = "==4.20.0", extras = ["format"]} lxml = "==5.1.0" marshmallow = "==3.20.2" -marshmallow-sqlalchemy = "==0.29.0" +marshmallow-sqlalchemy = "==0.30.0" newrelic = "*" notifications-python-client = "==8.2.0" notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"} From 8cd90fb5c4d3ab5f72992c904cd7d55f7e6cd682 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 21:26:37 +0000 Subject: [PATCH 072/259] Bump pip-audit from 2.6.3 to 2.7.0 Bumps [pip-audit](https://github.com/pypa/pip-audit) from 2.6.3 to 2.7.0. - [Release notes](https://github.com/pypa/pip-audit/releases) - [Changelog](https://github.com/pypa/pip-audit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pypa/pip-audit/compare/v2.6.3...v2.7.0) --- updated-dependencies: - dependency-name: pip-audit dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index c12c2fb9b..1209e9933 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2823,13 +2823,13 @@ pip = "*" [[package]] name = "pip-audit" -version = "2.6.3" +version = "2.7.0" description = "A tool for scanning Python environments for known vulnerabilities" optional = false python-versions = ">=3.8" files = [ - {file = "pip_audit-2.6.3-py3-none-any.whl", hash = "sha256:216983210db4a15393f9e80e4d24a805f5767e4c8e0c31fc70c336acc629613b"}, - {file = "pip_audit-2.6.3.tar.gz", hash = "sha256:bd796066f69684b2f4fc2c2b6d222589e23190db0bbde069cea5c2b0be2cc57d"}, + {file = "pip_audit-2.7.0-py3-none-any.whl", hash = "sha256:83e039740653eb9ef1a78b1540ed441600cd88a560588ba2c0a169180685a522"}, + {file = "pip_audit-2.7.0.tar.gz", hash = "sha256:67740c5b1d5d967a258c3dfefc46f9713a2819c48062505ddf4b29de101c2b75"}, ] [package.dependencies] From 3414c8e15a943a168ad4ca94ebdc7a313dac6d73 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 12 Jan 2024 14:28:14 -0800 Subject: [PATCH 073/259] fix redis issue causing error in log --- app/celery/scheduled_tasks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index adea57ed3..8ab8bbcb9 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -98,6 +98,8 @@ def expire_or_delete_invitations(): raise +# TODO THIS IS ACTUALLY DEPRECATED, WE ARE REMOVING PHONE NUMBERS FROM THE DB +# SO THERE WILL BE NO REASON TO KEEP TRACK OF THIS COUNT @notify_celery.task(name="check-db-notification-fails") def check_db_notification_fails(): """ @@ -110,7 +112,7 @@ def check_db_notification_fails(): on a breach. I.e., if the last number was at 23% and the current number is 27%, send an email. But if the last number was 26% and the current is 27%, don't. """ - last_value = redis_store.get("LAST_DB_NOTIFICATION_COUNT") + last_value = redis_store.get("LAST_DB_NOTIFICATION_COUNT").decode("utf-8") if not last_value: last_value = 0 From ef6c117a58457ebaf0f85f21a746cd19c3f3b6d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 22:57:12 +0000 Subject: [PATCH 074/259] Bump newrelic from 9.4.0 to 9.5.0 Bumps [newrelic](https://github.com/newrelic/newrelic-python-agent) from 9.4.0 to 9.5.0. - [Release notes](https://github.com/newrelic/newrelic-python-agent/releases) - [Commits](https://github.com/newrelic/newrelic-python-agent/compare/v9.4.0...v9.5.0) --- updated-dependencies: - dependency-name: newrelic dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 60 ++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1209e9933..aa72aa73b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2514,40 +2514,40 @@ files = [ [[package]] name = "newrelic" -version = "9.4.0" +version = "9.5.0" description = "New Relic Python Agent" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "newrelic-9.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:ed7a8ab3fb4425d7d55c44517f24f5259eab2045d4b24a21cf9439d9a6f53ffd"}, - {file = "newrelic-9.4.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3d1253c67bc371f8eca386a8d96e0ae29861359ca5b62e7e5c4f87083b8a8930"}, - {file = "newrelic-9.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:04e055c3a4a9b2b2719bc83e5e2935f700f3943025d169873b8952b9576d3962"}, - {file = "newrelic-9.4.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1f28ef38aed097600e149755b4b102edcb18e9b4e270d415154699cec2cb1a05"}, - {file = "newrelic-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f177f27cfbcd3673359ce0d80f8e418fc706b019ba936bbba9aa08585dff5f0"}, - {file = "newrelic-9.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d3d4902431de420baf1c9801c17adb8d6accb01704a6206dcdbb3ab6cc83ca8"}, - {file = "newrelic-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66df7709bc8da0a79aea34eaf3f38410eeed2b84abb53fcac78130a0f4fb7eb4"}, - {file = "newrelic-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89329509f6c37f444eedd4a6abc8e569c0faa74e42538e31288e75610ae5fb4e"}, - {file = "newrelic-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ec7da405d968b4f31103d6db407e409ad1cda1403efb22862bdefdd44a9742a"}, - {file = "newrelic-9.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3183168cc1a7d79b2b0be147ea657105a83c631e33edef5a17c469052f9ae944"}, - {file = "newrelic-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dcd592bb0115694ca4c8e4b9264ffc05af10c365dfd95c0b8bc10b89c03bb282"}, - {file = "newrelic-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0f74d88c1b279ffadae325ecebe35a3dab662ae074f868a03cfdc5be5214b3d3"}, - {file = "newrelic-9.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d61eb8972323d57919cefef8ba1faad325a7818939d00ee2bc9698f96a880ca8"}, - {file = "newrelic-9.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72267bf3c2ab493813d7f5fb084a722c52da1493b376c56ff1d6a637dc451bed"}, - {file = "newrelic-9.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:78567ccedf2bdb75dc567bec318e221dcfab4681bd05babc398ce1b78d60f67b"}, - {file = "newrelic-9.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7a0fcbe0a581bea77346e291a0329fe7fce5226252c69d41fd4d91ba4d7c5493"}, - {file = "newrelic-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9162bf466d8ed7c973a597115434855f4e37888c2350d56b25f9b11875d20f2"}, - {file = "newrelic-9.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5d9e8f0f9ef0920dfad046e70dd91fcecab225d1ea5c09369a701c24e622c69"}, - {file = "newrelic-9.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6b63ad134842b92521845864cbc47a7559d189cf8a706220a90974ad2a9510da"}, - {file = "newrelic-9.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:895bfa0e1497dc061139b83dd4a5ff7b40b3eafa62a2e54c5bce2e616c72c762"}, - {file = "newrelic-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b40049de63700f518a9ee4e2b97881c5796e8a72fe9684bd0ae8e11d1ec17755"}, - {file = "newrelic-9.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eb1a8571d0434d62eac401797e835feed1b39a918434ab1e72e486b39bc0e87"}, - {file = "newrelic-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:195bc995b227ed0cdbe2c95dc27ae057002432a7c84532e72ba57a430265054a"}, - {file = "newrelic-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:895d0e78da2b3d5abbde7e8856692944ada2b744b26cdb339d5cbfa4461e9acb"}, - {file = "newrelic-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d11de77ba0e9e7921b21385f8300172248e865cd38c16165ee48adc9af35c746"}, - {file = "newrelic-9.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3f71b218febb7a48a8df048a7248025e1bd86c76a3144602e56d2a9f7d1740d"}, - {file = "newrelic-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f5cecccf3e85dccfa6b5ad7be21ae8940c46c579e2979890d84969546322d715"}, - {file = "newrelic-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f9fa697c355790b52e3180044349bbc2822230ca6f98d6292df4142080cd20de"}, - {file = "newrelic-9.4.0.tar.gz", hash = "sha256:84437107e0d47be070a17b31f48b54abea7d4951b5ca89f5c2416a2fa6ab42b8"}, + {file = "newrelic-9.5.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:87c5ea1db9b2f573c7fb0961b7a856a91db0c9f130303de1839c542586b8dc87"}, + {file = "newrelic-9.5.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8c62c07a69b86c54d3b9ce187551f36683ad8b70b6301273e7c1e89b121b8245"}, + {file = "newrelic-9.5.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:847dd07961adec25c4c1128e8666f72ba0ab03640a44a65bcf25ffebd6fc2a75"}, + {file = "newrelic-9.5.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:12be024a5dfd1667d05d7cce968189e2490b6f6d24f194695365607b2340be20"}, + {file = "newrelic-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c381d300d904e2a5eb60953a29a729dbd4d8213f6dd629cecaa3033db70a9060"}, + {file = "newrelic-9.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2e4ccd5fe97496806fe0ea1acb8b15791c1f012aa44fb047fc8016bcfb80ab9"}, + {file = "newrelic-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:14ef5e94d490315a73cf4bea4cd0186b2ec68009e90ab2c16a0b501942fa8629"}, + {file = "newrelic-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:306e54751fe1eba61884b0ed63e9583711b37e24db9c7b36725317c21aa609f5"}, + {file = "newrelic-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ac2b3603cc781ef58e51a95b0b58220feb3e322c282c758c3bf5430398a47bc"}, + {file = "newrelic-9.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5b0a3fbbd6ef1dca0d2434c8ddd15d945fef2cd343d09b3a352b8de825d2e83"}, + {file = "newrelic-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab045fe37dcfd9c1fefc84f72373c54abd546d7517909fabd6920ea1d87c3f7f"}, + {file = "newrelic-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e54492c5f70cf0951ffc97ddd71b3cf64d68b4d670012d7a7d5ff3dc19417dab"}, + {file = "newrelic-9.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92968b7c6951f3ee0fe85a465ae79d448e587a3d10bf71d85b2cab09aa319776"}, + {file = "newrelic-9.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847d75a08ca3ba356b6d6317618243e546dc7725251a453d4730ac6e9f043b71"}, + {file = "newrelic-9.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec0fe958c5ed52cd8b496339df8661badbd24bdc290dea8744de94da705ccef0"}, + {file = "newrelic-9.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a2af10be7893f60588e2103b2f5ff616baf8132ecce90d9a8c7bfb832da08748"}, + {file = "newrelic-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2556ae2b9c13127dc46a6326c1ea682eae2c2b64b6873cfd4d5e177c7e0ce58a"}, + {file = "newrelic-9.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e9818646a7a72332e5ab3853277187af5b0d51312c51585a5581453fbdc1508"}, + {file = "newrelic-9.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b370179f644c0265e4467221e895dccc75440388c5cce8d6a89d150c6b265021"}, + {file = "newrelic-9.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c514f82942f7759bc4a360d908c4be37b8764a3ff1589ba3ad8b4baf201bad36"}, + {file = "newrelic-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15d4c2dd584c7604a03b2c73464ed3054cd8f0a513ed25832f1a3af4eb6e80ab"}, + {file = "newrelic-9.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2af79b43e3244ce7775eb7a0bf2e8fe2cb1624f6eb1be03cb6c35f9e8a5be77"}, + {file = "newrelic-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1dab3ef8baffebcf3f088a4131d93f3ac0e1fc08fa6045927c6bd2746b4decf4"}, + {file = "newrelic-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78ca19f47b2bc38e18516c2a7c485eeac6979b271a1a5a4ac3b950581ca8a230"}, + {file = "newrelic-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63314277c983cd412b87dfa3ed101b88c40ccab683a98046d76bf44a33cb7637"}, + {file = "newrelic-9.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68917968adba4abde385be1c23d8fdb13714968883c3a3d1a83fb676ea614245"}, + {file = "newrelic-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fb49e3a2e8ab9b509c8d276382f7ce0f6b4e51758b0edec38131ae583532b121"}, + {file = "newrelic-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4638e6c28ebf2b5a8a860ae33105a1816425f6ece0113df626c88e5626b22f41"}, + {file = "newrelic-9.5.0.tar.gz", hash = "sha256:2442449c4798989d92f527a82e60c446821c97896d739492b607bf6d4018ba1b"}, ] [package.extras] From e45d4dbac974649e0f0df96e782698110de9d410 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 12 Jan 2024 15:08:59 -0800 Subject: [PATCH 075/259] fix tests --- app/celery/scheduled_tasks.py | 4 +++- tests/app/celery/test_scheduled_tasks.py | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index 8ab8bbcb9..30d778d41 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -112,9 +112,11 @@ def check_db_notification_fails(): on a breach. I.e., if the last number was at 23% and the current number is 27%, send an email. But if the last number was 26% and the current is 27%, don't. """ - last_value = redis_store.get("LAST_DB_NOTIFICATION_COUNT").decode("utf-8") + last_value = redis_store.get("LAST_DB_NOTIFICATION_COUNT") if not last_value: last_value = 0 + else: + last_value = int(last_value.decode("utf-8")) failed_count = dao_get_failed_notification_count() if failed_count > last_value: diff --git a/tests/app/celery/test_scheduled_tasks.py b/tests/app/celery/test_scheduled_tasks.py index 8ec7d3a13..e8f505b6e 100644 --- a/tests/app/celery/test_scheduled_tasks.py +++ b/tests/app/celery/test_scheduled_tasks.py @@ -72,6 +72,8 @@ def test_should_check_db_notification_fails_task_less_than_25_percent( mock_dao = mocker.patch( "app.celery.scheduled_tasks.dao_get_failed_notification_count" ) + mock_redis = mocker.patch("app.celery.scheduled_tasks.redis_store") + mock_redis.get.return_value = 0 mock_provider = mocker.patch("app.celery.scheduled_tasks.provider_to_use") mock_dao.return_value = 10 check_db_notification_fails() @@ -88,13 +90,13 @@ def test_should_check_db_notification_fails_task_over_50_percent( "app.celery.scheduled_tasks.dao_get_failed_notification_count" ) mock_provider = mocker.patch("app.celery.scheduled_tasks.provider_to_use") - mock_redis = mocker.patch("app.celery.scheduled_tasks.redis_store.get") + mock_redis = mocker.patch("app.celery.scheduled_tasks.redis_store") mock_dao.return_value = 5001 - mock_redis.return_value = 0 + mock_redis.get.return_value = "0".encode("utf-8") check_db_notification_fails() assert mock_provider.call_count == 1 - mock_redis.return_value = 5001 + mock_redis.get.return_value = "5001".encode("utf-8") check_db_notification_fails() assert mock_provider.call_count == 1 From f9f1013f5bd61bf92bce132ba6263ade4b374fdb Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 16 Jan 2024 11:21:24 -0800 Subject: [PATCH 076/259] notify-api-742 don't write phone numbers to db --- app/dao/notifications_dao.py | 7 +- app/user/rest.py | 4 - tests/app/celery/test_tasks.py | 14 +-- .../notification_dao/test_notification_dao.py | 88 +++++++++++-------- tests/app/dao/test_services_dao.py | 3 + tests/app/delivery/test_send_to_providers.py | 6 +- .../test_process_notification.py | 8 +- tests/app/notifications/test_rest.py | 2 +- tests/app/organization/test_rest.py | 2 +- .../public_contracts/test_GET_notification.py | 4 + .../test_send_notification.py | 4 +- tests/app/service/test_rest.py | 35 +++++++- tests/app/service/test_sender.py | 9 +- tests/app/user/test_rest_verify.py | 6 +- .../notifications/test_get_notifications.py | 6 +- .../notifications/test_post_notifications.py | 2 +- 16 files changed, 124 insertions(+), 76 deletions(-) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index ec9ea5053..23905944c 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -72,7 +72,9 @@ def dao_create_notification(notification): notification.id = create_uuid() if not notification.status: notification.status = NOTIFICATION_CREATED - + # notify-api-742 remove phone numbers from db + notification.to = "1" + notification.normalised_to = "1" db.session.add(notification) @@ -179,6 +181,9 @@ def update_notification_status_by_reference(reference, status): @autocommit def dao_update_notification(notification): notification.updated_at = datetime.utcnow() + # notify-api-742 remove phone numbers from db + notification.to = "1" + notification.normalised_to = "1" db.session.add(notification) diff --git a/app/user/rest.py b/app/user/rest.py index 303c1a39c..c7fa8055a 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -401,10 +401,6 @@ def send_new_user_email_verification(user_id): # when registering, we verify all users' email addresses using this function user_to_send_to = get_user_by_id(user_id=user_id) - current_app.logger.info("user_to_send_to is {}".format(user_to_send_to)) - current_app.logger.info( - "user_to_send_to.email_address is {}".format(user_to_send_to.email_address) - ) template = dao_get_template_by_id( current_app.config["NEW_USER_EMAIL_VERIFICATION_TEMPLATE_ID"] diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 80ba897a5..41c613563 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -417,7 +417,7 @@ def test_should_send_template_to_correct_sms_task_and_persist( ) persisted_notification = Notification.query.one() - assert persisted_notification.to == "+447234123123" + assert persisted_notification.to == "1" assert persisted_notification.template_id == sample_template_with_placeholders.id assert ( persisted_notification.template_version @@ -456,7 +456,7 @@ def test_should_save_sms_if_restricted_service_and_valid_number( ) persisted_notification = Notification.query.one() - assert persisted_notification.to == "+12028675309" + assert persisted_notification.to == "1" assert persisted_notification.template_id == template.id assert persisted_notification.template_version == template.version assert persisted_notification.status == "created" @@ -566,7 +566,7 @@ def test_should_save_sms_template_to_and_persist_with_job_id(sample_job, mocker) encryption.encrypt(notification), ) persisted_notification = Notification.query.one() - assert persisted_notification.to == "+447234123123" + assert persisted_notification.to == "1" assert persisted_notification.job_id == sample_job.id assert persisted_notification.template_id == sample_job.template.id assert persisted_notification.status == "created" @@ -631,7 +631,7 @@ def test_should_use_email_template_and_persist( ) persisted_notification = Notification.query.one() - assert persisted_notification.to == "my_email@my_email.com" + assert persisted_notification.to == "1" assert ( persisted_notification.template_id == sample_email_template_with_placeholders.id ) @@ -678,7 +678,7 @@ def test_save_email_should_use_template_version_from_job_not_latest( ) persisted_notification = Notification.query.one() - assert persisted_notification.to == "my_email@my_email.com" + assert persisted_notification.to == "1" assert persisted_notification.template_id == sample_email_template.id assert persisted_notification.template_version == version_on_notification assert persisted_notification.created_at >= now @@ -707,7 +707,7 @@ def test_should_use_email_template_subject_placeholders( encryption.encrypt(notification), ) persisted_notification = Notification.query.one() - assert persisted_notification.to == "my_email@my_email.com" + assert persisted_notification.to == "1" assert ( persisted_notification.template_id == sample_email_template_with_placeholders.id ) @@ -786,7 +786,7 @@ def test_should_use_email_template_and_persist_without_personalisation( encryption.encrypt(notification), ) persisted_notification = Notification.query.one() - assert persisted_notification.to == "my_email@my_email.com" + assert persisted_notification.to == "1" assert persisted_notification.template_id == sample_email_template.id assert persisted_notification.created_at >= now assert not persisted_notification.sent_at diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index a0d19ecd1..13056105a 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -25,7 +25,6 @@ from app.dao.notifications_dao import ( get_notifications_for_service, get_service_ids_with_notifications_on_date, notifications_not_yet_sent, - sanitize_successful_notification_by_id, update_notification_status_by_id, update_notification_status_by_reference, ) @@ -92,36 +91,6 @@ def test_should_by_able_to_update_status_by_id( assert notification.status == "delivered" -def test_should_be_able_to_sanitize_successful_notification( - sample_template, sample_job, sns_provider -): - with freeze_time("2000-01-01 12:00:00"): - data = _notification_json( - sample_template, job_id=sample_job.id, status="sending" - ) - notification = Notification(**data) - notification.to = "15555555555" - notification.normalised_to = "15555555555" - dao_create_notification(notification) - assert notification.status == "sending" - assert notification.normalised_to == "15555555555" - assert notification.to == "15555555555" - - assert Notification.query.get(notification.id).status == "sending" - - with freeze_time("2000-01-02 12:00:00"): - sanitize_successful_notification_by_id( - notification.id, carrier="ATT", provider_response="Don't know what happened" - ) - assert Notification.query.get(notification.id).status == "delivered" - assert Notification.query.get(notification.id).normalised_to == "1" - assert Notification.query.get(notification.id).to == "1" - assert ( - Notification.query.get(notification.id).provider_response - == "Don't know what happened" - ) - - def test_should_not_update_status_by_id_if_not_sending_and_does_not_update_job( sample_job, ): @@ -341,7 +310,7 @@ def test_save_notification_creates_sms(sample_template, sample_job): assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] assert notification_from_db.id - assert data["to"] == notification_from_db.to + assert "1" == notification_from_db.to assert data["job_id"] == notification_from_db.job_id assert data["service"] == notification_from_db.service assert data["template_id"] == notification_from_db.template_id @@ -361,7 +330,7 @@ def test_save_notification_and_create_email(sample_email_template, sample_job): assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] assert notification_from_db.id - assert data["to"] == notification_from_db.to + assert "1" == notification_from_db.to assert data["job_id"] == notification_from_db.job_id assert data["service"] == notification_from_db.service assert data["template_id"] == notification_from_db.template_id @@ -438,7 +407,7 @@ def test_save_notification_and_increment_job(sample_template, sample_job, sns_pr assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] assert notification_from_db.id - assert data["to"] == notification_from_db.to + assert "1" == notification_from_db.to assert data["job_id"] == notification_from_db.job_id assert data["service"] == notification_from_db.service assert data["template_id"] == notification_from_db.template_id @@ -464,7 +433,7 @@ def test_save_notification_and_increment_correct_job(sample_template, sns_provid assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] assert notification_from_db.id - assert data["to"] == notification_from_db.to + assert "1" == notification_from_db.to assert data["job_id"] == notification_from_db.job_id assert data["service"] == notification_from_db.service assert data["template_id"] == notification_from_db.template_id @@ -484,7 +453,7 @@ def test_save_notification_with_no_job(sample_template, sns_provider): assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] assert notification_from_db.id - assert data["to"] == notification_from_db.to + assert "1" == notification_from_db.to assert data["service"] == notification_from_db.service assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version @@ -545,7 +514,7 @@ def test_save_notification_no_job_id(sample_template): assert Notification.query.count() == 1 notification_from_db = Notification.query.all()[0] assert notification_from_db.id - assert data["to"] == notification_from_db.to + assert "1" == notification_from_db.to assert data["service"] == notification_from_db.service assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version @@ -1024,6 +993,9 @@ def test_should_exclude_test_key_notifications_by_default( assert len(all_notifications) == 1 +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_recipient(sample_template): recipient_to_search_for = { "to_field": "+447700900855", @@ -1057,6 +1029,9 @@ def test_dao_get_notifications_by_recipient(sample_template): assert notification1.id == results.items[0].id +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_recipient_is_limited_to_50_results(sample_template): for _ in range(100): create_notification( @@ -1075,6 +1050,9 @@ def test_dao_get_notifications_by_recipient_is_limited_to_50_results(sample_temp assert len(results.items) == 50 +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @pytest.mark.parametrize("search_term", ["JACK", "JACK@gmail.com", "jack@gmail.com"]) def test_dao_get_notifications_by_recipient_is_not_case_sensitive( sample_email_template, search_term @@ -1093,6 +1071,9 @@ def test_dao_get_notifications_by_recipient_is_not_case_sensitive( assert notification.id in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_recipient_matches_partial_emails( sample_email_template, ): @@ -1116,6 +1097,9 @@ def test_dao_get_notifications_by_recipient_matches_partial_emails( assert notification_2.id not in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @pytest.mark.parametrize( "search_term, expected_result_count", [ @@ -1165,6 +1149,9 @@ def test_dao_get_notifications_by_recipient_escapes( ) +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @pytest.mark.parametrize( "search_term, expected_result_count", [ @@ -1215,6 +1202,9 @@ def test_dao_get_notifications_by_reference_escapes_special_character( ) +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @pytest.mark.parametrize( "search_term", [ @@ -1252,6 +1242,9 @@ def test_dao_get_notifications_by_recipient_matches_partial_phone_numbers( assert notification_2.id not in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @pytest.mark.parametrize("to", ["not@email", "123"]) def test_dao_get_notifications_by_recipient_accepts_invalid_phone_numbers_and_email_addresses( sample_template, @@ -1268,6 +1261,9 @@ def test_dao_get_notifications_by_recipient_accepts_invalid_phone_numbers_and_em assert len(results.items) == 0 +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_recipient_ignores_spaces(sample_template): notification1 = create_notification( template=sample_template, to_field="+447700900855", normalised_to="447700900855" @@ -1299,6 +1295,9 @@ def test_dao_get_notifications_by_recipient_ignores_spaces(sample_template): assert notification3.id in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @pytest.mark.parametrize("phone_search", ("202", "7-5", "+1 (202) 867-5309")) @pytest.mark.parametrize( "email_search", @@ -1342,6 +1341,9 @@ def test_dao_get_notifications_by_recipient_searches_across_notification_types( assert results.items[1].id == sms.id +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_reference(notify_db_session): service = create_service() sms_template = create_template(service=service) @@ -1416,6 +1418,9 @@ def test_dao_get_notifications_by_reference(notify_db_session): assert len(results.items) == 0 +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_to_field_filters_status(sample_template): notification = create_notification( template=sample_template, @@ -1441,6 +1446,9 @@ def test_dao_get_notifications_by_to_field_filters_status(sample_template): assert notification.id == notifications.items[0].id +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_to_field_filters_multiple_statuses(sample_template): notification1 = create_notification( template=sample_template, @@ -1468,6 +1476,9 @@ def test_dao_get_notifications_by_to_field_filters_multiple_statuses(sample_temp assert notification2.id in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_dao_get_notifications_by_to_field_returns_all_if_no_status_filter( sample_template, ): @@ -1494,6 +1505,9 @@ def test_dao_get_notifications_by_to_field_returns_all_if_no_status_filter( assert notification2.id in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @freeze_time("2016-01-01 11:10:00") def test_dao_get_notifications_by_to_field_orders_by_created_at_desc(sample_template): notification = partial( diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index a7d630db3..41d003ec7 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -1363,6 +1363,9 @@ def _assert_service_permissions(service_permissions, expected): assert set(expected) == set(p.permission for p in service_permissions) +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) @freeze_time("2019-12-02 12:00:00.000000") def test_dao_find_services_sending_to_tv_numbers(notify_db_session, fake_uuid): service_1 = create_service(service_name="Service 1", service_id=fake_uuid) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index 4dfb336a0..dbae3014b 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -652,10 +652,10 @@ def test_send_sms_to_provider_should_use_normalised_to(mocker, client, sample_te ) mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") - mock_s3.return_value = "2028675309" + mock_s3.return_value = "12028675309" send_to_providers.send_sms_to_provider(notification) send_mock.assert_called_once_with( - to=notification.normalised_to, + to="12028675309", content=ANY, reference=str(notification.id), sender=notification.reply_to_text, @@ -716,7 +716,7 @@ def test_send_sms_to_provider_should_return_template_if_found_in_redis( assert mock_get_template.called is False assert mock_get_service.called is False send_mock.assert_called_once_with( - to=notification.normalised_to, + to="447700900855", content=ANY, reference=str(notification.id), sender=notification.reply_to_text, diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index b68e4503d..87c8da988 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -377,8 +377,8 @@ def test_persist_sms_notification_stores_normalised_number( ) persisted_notification = Notification.query.all()[0] - assert persisted_notification.to == recipient - assert persisted_notification.normalised_to == expected_recipient_normalised + assert persisted_notification.to == "1" + assert persisted_notification.normalised_to == "1" @pytest.mark.parametrize( @@ -401,8 +401,8 @@ def test_persist_email_notification_stores_normalised_email( ) persisted_notification = Notification.query.all()[0] - assert persisted_notification.to == recipient - assert persisted_notification.normalised_to == expected_recipient_normalised + assert persisted_notification.to == "1" + assert persisted_notification.normalised_to == "1" def test_persist_notification_with_billable_units_stores_correct_info(mocker): diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index a3b31beb5..c12132c58 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -159,7 +159,7 @@ def test_get_all_notifications(client, sample_notification): "version": 1, } - assert notifications["notifications"][0]["to"] == "+447700900855" + assert notifications["notifications"][0]["to"] == "1" assert notifications["notifications"][0]["service"] == str( sample_notification.service_id ) diff --git a/tests/app/organization/test_rest.py b/tests/app/organization/test_rest.py index e239dfd3d..25f25ec43 100644 --- a/tests/app/organization/test_rest.py +++ b/tests/app/organization/test_rest.py @@ -551,7 +551,7 @@ def test_post_update_organization_set_mou_emails_signed_by( ) notifications = [x[0][0] for x in queue_mock.call_args_list] - assert {n.template.name: n.to for n in notifications} == templates_and_recipients + # assert {n.template.name: n.to for n in notifications} == templates_and_recipients for n in notifications: # we pass in the same personalisation for all templates (though some templates don't use all fields) diff --git a/tests/app/public_contracts/test_GET_notification.py b/tests/app/public_contracts/test_GET_notification.py index d36704083..ff23762f0 100644 --- a/tests/app/public_contracts/test_GET_notification.py +++ b/tests/app/public_contracts/test_GET_notification.py @@ -1,3 +1,5 @@ +import pytest + from app.dao.api_key_dao import save_model_api_key from app.models import KEY_TYPE_NORMAL, ApiKey from app.v2.notifications.notification_schemas import ( @@ -70,6 +72,7 @@ def test_get_api_sms_contract(client, sample_notification): validate_v0(response_json, "GET_notification_return_sms.json") +@pytest.mark.skip(reason="Update to fetch email from s3") def test_get_api_email_contract(client, sample_email_notification): response_json = return_json_from_response( _get_notification( @@ -92,6 +95,7 @@ def test_get_job_sms_contract(client, sample_notification): validate_v0(response_json, "GET_notification_return_sms.json") +@pytest.mark.skip(reason="Update to fetch email from s3") def test_get_job_email_contract(client, sample_email_notification): response_json = return_json_from_response( _get_notification( diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index e4477fa25..c65614e22 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -791,7 +791,7 @@ def test_should_persist_notification( assert response.status_code == 201 notification = notifications_dao.get_notification_by_id(fake_uuid) - assert notification.to == to + assert notification.to == "1" assert notification.template_id == template.id assert notification.notification_type == template_type @@ -1202,7 +1202,7 @@ def test_should_allow_store_original_number_on_sms_notification( assert notification_id notifications = Notification.query.all() assert len(notifications) == 1 - assert "(202) 867-5309" == notifications[0].to + assert "1" == notifications[0].to def test_should_not_allow_sending_to_international_number_without_international_permission( diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index ce6afc065..219bb853b 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1685,6 +1685,9 @@ def test_get_all_notifications_for_service_in_order_with_post_request( assert response.status_code == 200 +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_get_all_notifications_for_service_filters_notifications_when_using_post_request( client, notify_db_session ): @@ -1725,7 +1728,7 @@ def test_get_all_notifications_for_service_filters_notifications_when_using_post resp = json.loads(response.get_data(as_text=True)) assert len(resp["notifications"]) == 1 - assert resp["notifications"][0]["to"] == returned_notification.to + assert resp["notifications"][0]["to"] == "1" assert resp["notifications"][0]["status"] == returned_notification.status assert response.status_code == 200 @@ -2256,6 +2259,9 @@ def test_get_detailed_services_for_date_range( } +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field( client, sample_template, sample_email_template ): @@ -2281,6 +2287,9 @@ def test_search_for_notification_by_to_field( assert str(notification2.id) == notifications[0]["id"] +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_return_empty_list_if_there_is_no_match( client, sample_template, sample_email_template ): @@ -2299,6 +2308,9 @@ def test_search_for_notification_by_to_field_return_empty_list_if_there_is_no_ma assert len(notifications) == 0 +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_return_multiple_matches( client, sample_template, sample_email_template ): @@ -2333,6 +2345,9 @@ def test_search_for_notification_by_to_field_return_multiple_matches( assert str(notification4.id) not in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_returns_next_link_if_more_than_50( client, sample_template ): @@ -2355,6 +2370,9 @@ def test_search_for_notification_by_to_field_returns_next_link_if_more_than_50( assert "page=2" in response_json["links"]["next"] +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_returns_no_next_link_if_50_or_less( client, sample_template ): @@ -2446,6 +2464,9 @@ def test_update_service_does_not_call_send_notification_when_restricted_not_chan assert not send_notification_mock.called +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_filters_by_status(client, sample_template): notification1 = create_notification( sample_template, @@ -2474,6 +2495,9 @@ def test_search_for_notification_by_to_field_filters_by_status(client, sample_te assert str(notification1.id) in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_filters_by_statuses( client, sample_template ): @@ -2505,6 +2529,9 @@ def test_search_for_notification_by_to_field_filters_by_statuses( assert str(notification2.id) in notification_ids +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_returns_content( client, sample_template_with_placeholders ): @@ -2608,6 +2635,9 @@ def test_get_all_notifications_for_service_includes_template_redacted( # assert resp['notifications'][1]['template']['is_precompiled_letter'] is False +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_returns_personlisation( client, sample_template_with_placeholders ): @@ -2632,6 +2662,9 @@ def test_search_for_notification_by_to_field_returns_personlisation( assert notifications[0]["personalisation"]["name"] == "Foo" +@pytest.mark.skip( + reason="We can't search on recipient if recipient is not kept in the db" +) def test_search_for_notification_by_to_field_returns_notifications_by_type( client, sample_template, sample_email_template ): diff --git a/tests/app/service/test_sender.py b/tests/app/service/test_sender.py index 9056be8a9..fbc8b784b 100644 --- a/tests/app/service/test_sender.py +++ b/tests/app/service/test_sender.py @@ -13,17 +13,15 @@ def test_send_notification_to_service_users_persists_notifications_correctly( ): mocker.patch("app.service.sender.send_notification_to_queue") - user = sample_service.users[0] template = create_template(sample_service, template_type=notification_type) send_notification_to_service_users( service_id=sample_service.id, template_id=template.id ) - to = user.email_address if notification_type == EMAIL_TYPE else user.mobile_number notification = Notification.query.one() assert Notification.query.count() == 1 - assert notification.to == to + assert notification.to == "1" assert str(notification.service_id) == current_app.config["NOTIFY_SERVICE_ID"] assert notification.template.id == template.id assert notification.template.template_type == notification_type @@ -87,10 +85,5 @@ def test_send_notification_to_service_users_sends_to_active_users_only( template = create_template(service, template_type=EMAIL_TYPE) send_notification_to_service_users(service_id=service.id, template_id=template.id) - notifications = Notification.query.all() - notifications_recipients = [notification.to for notification in notifications] assert Notification.query.count() == 2 - assert pending_user.email_address not in notifications_recipients - assert first_active_user.email_address in notifications_recipients - assert second_active_user.email_address in notifications_recipients diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index bd89b9fd8..0a4185416 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -226,7 +226,7 @@ def test_send_user_sms_code(client, sample_user, sms_code_template, mocker): notification = Notification.query.one() assert notification.personalisation == {"verify_code": "11111"} - assert notification.to == sample_user.mobile_number + assert notification.to == "1" assert str(notification.service_id) == current_app.config["NOTIFY_SERVICE_ID"] assert notification.reply_to_text == notify_service.get_default_sms_sender() @@ -261,7 +261,7 @@ def test_send_user_code_for_sms_with_optional_to_field( assert resp.status_code == 204 assert mocked.call_count == 1 notification = Notification.query.first() - assert notification.to == to_number + assert notification.to == "1" app.celery.provider_tasks.deliver_sms.apply_async.assert_called_once_with( ([str(notification.id)]), queue="notify-internal-tasks" ) @@ -479,7 +479,7 @@ def test_send_user_email_code( noti.reply_to_text == email_2fa_code_template.service.get_default_reply_to_email_address() ) - assert noti.to == sample_user.email_address + assert noti.to == "1" assert str(noti.template_id) == current_app.config["EMAIL_2FA_TEMPLATE_ID"] assert noti.personalisation["name"] == "Test User" assert noti.personalisation["url"].startswith(expected_auth_url) diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index 4229b2cc2..82b589e4c 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -289,7 +289,7 @@ def test_get_all_notifications_except_job_notifications_returns_200( "uri": notification.template.get_link(), "version": 1, } - assert json_response["notifications"][0]["phone_number"] == "+447700900855" + assert json_response["notifications"][0]["phone_number"] == "1" assert json_response["notifications"][0]["type"] == "sms" assert not json_response["notifications"][0]["scheduled_for"] @@ -322,7 +322,7 @@ def test_get_all_notifications_with_include_jobs_arg_returns_200( assert json_response["notifications"][0]["id"] == str(notification.id) assert json_response["notifications"][0]["status"] == notification.status - assert json_response["notifications"][0]["phone_number"] == notification.to + assert "1" == notification.to assert ( json_response["notifications"][0]["type"] == notification.template.template_type ) @@ -381,7 +381,7 @@ def test_get_all_notifications_filter_by_template_type(client, sample_service): "uri": notification.template.get_link(), "version": 1, } - assert json_response["notifications"][0]["email_address"] == "don.draper@scdp.biz" + assert json_response["notifications"][0]["email_address"] == "1" assert json_response["notifications"][0]["type"] == "email" diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index cb3957450..f461531bc 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -838,7 +838,7 @@ def test_post_sms_should_persist_supplied_sms_number( notifications = Notification.query.all() assert len(notifications) == 1 notification_id = notifications[0].id - assert "+(44) 77009-00855" == notifications[0].to + assert "1" == notifications[0].to assert resp_json["id"] == str(notification_id) assert mocked.called From c13ed73d231d375dc6115127e8ccb70f12426264 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 17 Jan 2024 09:04:04 -0800 Subject: [PATCH 077/259] substitute phone numbers back in when sending data to reports --- app/job/rest.py | 12 +++++++++++- app/service/rest.py | 11 +++++++++++ tests/app/job/test_rest.py | 20 ++++++++++++++++---- tests/app/service/test_rest.py | 4 ++++ 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/job/rest.py b/app/job/rest.py index 5852d7f63..1aab2ca60 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -2,7 +2,7 @@ import dateutil import pytz from flask import Blueprint, current_app, jsonify, request -from app.aws.s3 import get_job_metadata_from_s3 +from app.aws.s3 import get_job_metadata_from_s3, get_phone_number_from_s3 from app.celery.tasks import process_job from app.config import QueueNames from app.dao.fact_notification_status_dao import fetch_notification_statuses_for_job @@ -76,6 +76,16 @@ def get_all_notifications_for_service_job(service_id, job_id): kwargs["service_id"] = service_id kwargs["job_id"] = job_id + for notification in paginated_notifications.items: + if notification.job_id is not None: + recipient = get_phone_number_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + notification.to = recipient + notification.normalised_to = recipient + notifications = None if data.get("format_for_csv"): notifications = [ diff --git a/app/service/rest.py b/app/service/rest.py index 99d2b4c97..cef10ef1a 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -6,6 +6,7 @@ from sqlalchemy.exc import IntegrityError from sqlalchemy.orm.exc import NoResultFound from werkzeug.datastructures import MultiDict +from app.aws.s3 import get_phone_number_from_s3 from app.config import QueueNames from app.dao import fact_notification_status_dao, notifications_dao from app.dao.annual_billing_dao import set_default_free_allowance_for_service @@ -425,6 +426,16 @@ def get_all_notifications_for_service(service_id): include_one_off=include_one_off, ) + for notification in pagination.items: + if notification.job_id is not None: + recipient = get_phone_number_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + notification.to = recipient + notification.normalised_to = recipient + kwargs = request.args.to_dict() kwargs["service_id"] = service_id diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index 56f2461b1..c48ef89d8 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -448,8 +448,11 @@ def _setup_jobs(template, number_of_jobs=5): def test_get_all_notifications_for_job_in_order_of_job_number( - admin_request, sample_template + admin_request, sample_template, mocker ): + mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") + mock_s3.return_value = "15555555555" + main_job = create_job(sample_template) another_job = create_job(sample_template) @@ -483,8 +486,11 @@ def test_get_all_notifications_for_job_in_order_of_job_number( ], ) def test_get_all_notifications_for_job_filtered_by_status( - admin_request, sample_job, expected_notification_count, status_args + admin_request, sample_job, expected_notification_count, status_args, mocker ): + mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") + mock_s3.return_value = "15555555555" + create_notification(job=sample_job, to_field="1", status="created") resp = admin_request.get( @@ -497,8 +503,11 @@ def test_get_all_notifications_for_job_filtered_by_status( def test_get_all_notifications_for_job_returns_correct_format( - admin_request, sample_notification_with_job + admin_request, sample_notification_with_job, mocker ): + mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") + mock_s3.return_value = "15555555555" + service_id = sample_notification_with_job.service_id job_id = sample_notification_with_job.job_id @@ -813,8 +822,11 @@ def create_10_jobs(template): def test_get_all_notifications_for_job_returns_csv_format( - admin_request, sample_notification_with_job + admin_request, sample_notification_with_job, mocker ): + mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") + mock_s3.return_value = "15555555555" + resp = admin_request.get( "job.get_all_notifications_for_service_job", service_id=sample_notification_with_job.service_id, diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 219bb853b..7a5a92222 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1846,7 +1846,11 @@ def test_get_all_notifications_for_service_including_ones_made_by_jobs( sample_notification, sample_notification_with_job, sample_template, + mocker, ): + mock_s3 = mocker.patch("app.service.rest.get_phone_number_from_s3") + mock_s3.return_value = "1" + # notification from_test_api_key create_notification(sample_template, key_type=KEY_TYPE_TEST) From 4efeb19d022a71424198fe474dce46dd3a10ec71 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 17 Jan 2024 22:57:04 -0500 Subject: [PATCH 078/259] Update try/except block to catch Exception This changeset updates a try/except block to catch Exception instead of BaseException. Using BaseException is an anti-pattern and is not intended for user-defined code; it can also lead to other unintended side effects if not handled properly. This is enough of a concern that the new release of flake8-bugbear now issues warnings when it sees BaseException being caught without an immediate re-raise. Signed-off-by: Carlo Costino --- app/delivery/send_to_providers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 6db858eed..9590f5bd6 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -76,7 +76,7 @@ def send_sms_to_provider(notification): notification.job_id, notification.job_row_number, ) - except BaseException: + except Exception: # It is our 2facode, maybe key = f"2facode-{notification.id}".replace(" ", "") recipient = redis_store.raw_get(key) From 567dd390b4e78de32acc9e7140679f6855603c4e Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 18 Jan 2024 10:03:35 -0800 Subject: [PATCH 079/259] fix personalisation --- app/aws/s3.py | 23 ++++++++++++++ app/dao/notifications_dao.py | 8 +++++ app/job/rest.py | 8 ++++- app/service/rest.py | 7 +++++ app/v2/notifications/get_notifications.py | 7 +++++ tests/app/aws/test_s3.py | 30 +++++++++++++++++++ tests/app/celery/test_tasks.py | 6 ++-- tests/app/user/test_rest_verify.py | 2 -- .../notifications/test_post_notifications.py | 2 ++ 9 files changed, 87 insertions(+), 6 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 86676f3fc..c344279b1 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -124,6 +124,29 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) return my_phone +def get_personalisation_from_s3(service_id, job_id, job_row_number): + job = JOBS.get(job_id) + if job is None: + job = get_job_from_s3(service_id, job_id) + JOBS[job_id] = job + incr_jobs_cache_misses() + else: + incr_jobs_cache_hits() + + job = job.split("\r\n") + first_row = job[0] + job.pop(0) + first_row = first_row.split(",") + correct_row = job[job_row_number] + correct_row = correct_row.split(",") + personalisation_dict = {} + index = 0 + for header in first_row: + personalisation_dict[header] = correct_row[index] + index = index + 1 + print(f"get personalisation returns {personalisation_dict}") + return personalisation_dict + def get_job_metadata_from_s3(service_id, job_id): obj = get_s3_object(*get_job_location(service_id, job_id)) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index ec9ea5053..5e83b60d1 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -73,6 +73,14 @@ def dao_create_notification(notification): if not notification.status: notification.status = NOTIFICATION_CREATED + # notify-api-749 do not write to db + # if we have a verify_code we know this is the authentication notification at login time + # and not csv (containing PII) provided by the user, so allow verify_code to continue to exist + print(f"PERSONALISATION = {notification.personalisation}") + if "verify_code" in str(notification.personalisation): + pass + else: + notification.personalisation="" db.session.add(notification) diff --git a/app/job/rest.py b/app/job/rest.py index 5852d7f63..419f33451 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -2,7 +2,7 @@ import dateutil import pytz from flask import Blueprint, current_app, jsonify, request -from app.aws.s3 import get_job_metadata_from_s3 +from app.aws.s3 import get_job_metadata_from_s3, get_personalisation_from_s3 from app.celery.tasks import process_job from app.config import QueueNames from app.dao.fact_notification_status_dao import fetch_notification_statuses_for_job @@ -87,6 +87,12 @@ def get_all_notifications_for_service_job(service_id, job_id): paginated_notifications.items, many=True ) + for notification in paginated_notifications.items: + if notification.job_id is not None: + notification.personalisation = get_personalisation_from_s3( + notification.service_id, notification.job_id, notification.job_row_number + ) + return ( jsonify( notifications=notifications, diff --git a/app/service/rest.py b/app/service/rest.py index 99d2b4c97..d58e865c4 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -2,6 +2,7 @@ import itertools from datetime import datetime from flask import Blueprint, current_app, jsonify, request +from app.aws.s3 import get_personalisation_from_s3 from sqlalchemy.exc import IntegrityError from sqlalchemy.orm.exc import NoResultFound from werkzeug.datastructures import MultiDict @@ -425,6 +426,12 @@ def get_all_notifications_for_service(service_id): include_one_off=include_one_off, ) + for notification in pagination.items: + if notification.job_id is not None: + notification.personalisation = get_personalisation_from_s3( + notification.service_id, notification.job_id, notification.job_row_number + ) + kwargs = request.args.to_dict() kwargs["service_id"] = service_id diff --git a/app/v2/notifications/get_notifications.py b/app/v2/notifications/get_notifications.py index c12a89f68..13e7ff7bb 100644 --- a/app/v2/notifications/get_notifications.py +++ b/app/v2/notifications/get_notifications.py @@ -1,6 +1,7 @@ from flask import current_app, jsonify, request, url_for from app import api_user, authenticated_service +from app.aws.s3 import get_personalisation_from_s3 from app.dao import notifications_dao from app.schema_validation import validate from app.v2.notifications import v2_notification_blueprint @@ -49,6 +50,12 @@ def get_notifications(): count_pages=False, ) + for notification in paginated_notifications.items: + if notification.job_id is not None: + notification.personalisation = get_personalisation_from_s3( + notification.service_id, notification.job_id, notification.job_row_number + ) + def _build_links(notifications): _links = { "current": url_for(".get_notifications", _external=True, **data), diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index 342f6d7df..879661a8e 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -7,6 +7,7 @@ from botocore.exceptions import ClientError from app.aws.s3 import ( file_exists, + get_personalisation_from_s3, get_phone_number_from_s3, get_s3_file, remove_csv_object, @@ -73,6 +74,35 @@ def test_get_phone_number_from_s3( assert phone_number == expected_phone_number +@pytest.mark.parametrize( + "job, job_id, job_row_number, expected_personalisation", + [ + ("phone number\r\n+15555555555", "aaa", 0, {"phone number": "+15555555555"}), + ( + "day of week,favorite color,phone number\r\nmonday,green,1.555.111.1111\r\ntuesday,red,+1 (555) 222-2222", + "bbb", + 1, + {"day of week": "tuesday", "favorite color": "red", "phone number": "+1 (555) 222-2222"}, + ), + ( + "day of week,favorite color,phone number\r\nmonday,green,1.555.111.1111\r\ntuesday,red,+1 (555) 222-2222", + "ccc", + 0, + {"day of week": "monday", "favorite color": "green", "phone number": "1.555.111.1111"}, + ), + ], +) +def test_get_personalisation_from_s3( + mocker, job, job_id, job_row_number, expected_personalisation +): + mocker.patch("app.aws.s3.redis_store") + get_job_mock = mocker.patch("app.aws.s3.get_job_from_s3") + get_job_mock.return_value = job + personalisation = get_personalisation_from_s3("service_id", job_id, job_row_number) + assert personalisation == expected_personalisation + + + def test_remove_csv_object(notify_api, mocker): get_s3_mock = mocker.patch("app.aws.s3.get_s3_object") remove_csv_object("mykey") diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 80ba897a5..662df8a29 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -428,7 +428,7 @@ def test_should_send_template_to_correct_sms_task_and_persist( assert not persisted_notification.sent_at assert not persisted_notification.sent_by assert not persisted_notification.job_id - assert persisted_notification.personalisation == {"name": "Jo"} + assert persisted_notification.personalisation == {} assert persisted_notification.notification_type == "sms" mocked_deliver_sms.assert_called_once_with( [str(persisted_notification.id)], queue="send-sms-tasks" @@ -644,7 +644,7 @@ def test_should_use_email_template_and_persist( assert persisted_notification.status == "created" assert not persisted_notification.sent_by assert persisted_notification.job_row_number == 1 - assert persisted_notification.personalisation == {"name": "Jo"} + assert persisted_notification.personalisation == {} assert persisted_notification.api_key_id is None assert persisted_notification.key_type == KEY_TYPE_NORMAL assert persisted_notification.notification_type == "email" @@ -714,7 +714,7 @@ def test_should_use_email_template_subject_placeholders( assert persisted_notification.status == "created" assert persisted_notification.created_at >= now assert not persisted_notification.sent_by - assert persisted_notification.personalisation == {"name": "Jo"} + assert persisted_notification.personalisation == {} assert not persisted_notification.reference assert persisted_notification.notification_type == "email" provider_tasks.deliver_email.apply_async.assert_called_once_with( diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index bd89b9fd8..dea7d6f94 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -481,8 +481,6 @@ def test_send_user_email_code( ) assert noti.to == sample_user.email_address assert str(noti.template_id) == current_app.config["EMAIL_2FA_TEMPLATE_ID"] - assert noti.personalisation["name"] == "Test User" - assert noti.personalisation["url"].startswith(expected_auth_url) deliver_email.assert_called_once_with([str(noti.id)], queue="notify-internal-tasks") diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index cb3957450..f23e53e51 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -986,6 +986,7 @@ def test_post_email_notification_with_archived_reply_to_id_returns_400( assert "BadRequestError" in resp_json["errors"][0]["error"] +@pytest.mark.skip(reason="We've removed personalization from db, needs refactor if we want to support this") @pytest.mark.parametrize( "csv_param", ( @@ -1041,6 +1042,7 @@ def test_post_notification_with_document_upload( notification = Notification.query.one() assert notification.status == NOTIFICATION_CREATED + assert notification.personalisation == { "first_link": "abababab-link", "second_link": "cdcdcdcd-link", From 4b98106037459d2e31ce0c75038da9c2330d81a6 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 18 Jan 2024 10:16:14 -0800 Subject: [PATCH 080/259] debug --- app/aws/s3.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/aws/s3.py b/app/aws/s3.py index 86676f3fc..e5311207c 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -101,8 +101,12 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): # At the same time we don't want to store it in redis or the db # So this is a little recycling mechanism to reduce the number of downloads. job = JOBS.get(job_id) + # TODO REMOVE + current_app.logger.info(f"HERE IS THE JOB FROM CACHE {job}") if job is None: job = get_job_from_s3(service_id, job_id) + # TODO REMOVE + current_app.logger.info(f"HERE IS THE JOB FROM S3 {job}") JOBS[job_id] = job incr_jobs_cache_misses() else: @@ -117,9 +121,17 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): if item == "phone number": break phone_index = phone_index + 1 + correct_row = job[job_row_number] correct_row = correct_row.split(",") + # TODO REMOVE + current_app.logger.info( + f"HERE IS THE CORRECT ROW AND PHONE INDEX {correct_row} {phone_index}" + ) + # This could happen if an old job cannot be retrieved from s3 + if len(correct_row) <= phone_index: + return "Unknown Phone" my_phone = correct_row[phone_index] my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) return my_phone From 7997d887c6f56533e8461c3a28894e66cf3272b8 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 18 Jan 2024 10:53:20 -0800 Subject: [PATCH 081/259] fix case sensitivity looking up phone number --- app/aws/s3.py | 10 +--------- tests/app/aws/test_s3.py | 6 ++++++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index e5311207c..acc89ebc0 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -101,12 +101,8 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): # At the same time we don't want to store it in redis or the db # So this is a little recycling mechanism to reduce the number of downloads. job = JOBS.get(job_id) - # TODO REMOVE - current_app.logger.info(f"HERE IS THE JOB FROM CACHE {job}") if job is None: job = get_job_from_s3(service_id, job_id) - # TODO REMOVE - current_app.logger.info(f"HERE IS THE JOB FROM S3 {job}") JOBS[job_id] = job incr_jobs_cache_misses() else: @@ -118,16 +114,12 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): first_row = first_row.split(",") phone_index = 0 for item in first_row: - if item == "phone number": + if item.lower() == "phone number": break phone_index = phone_index + 1 correct_row = job[job_row_number] correct_row = correct_row.split(",") - # TODO REMOVE - current_app.logger.info( - f"HERE IS THE CORRECT ROW AND PHONE INDEX {correct_row} {phone_index}" - ) # This could happen if an old job cannot be retrieved from s3 if len(correct_row) <= phone_index: diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index 342f6d7df..ad01a00c5 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -61,6 +61,12 @@ def test_get_s3_file_makes_correct_call(notify_api, mocker): 0, "15551111111", ), + ( + "Phone number,name,date,time,address,English,Spanish\r\n15553333333,Tim,10/16,2:00 PM,5678 Tom St.,no,yes", + "ddd", + 0, + "15553333333", + ), ], ) def test_get_phone_number_from_s3( From f9120e7d3eb14e4c9c61caa189406bbebae4b149 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 18 Jan 2024 13:54:23 -0800 Subject: [PATCH 082/259] optimize how we look up phone numbers --- app/aws/s3.py | 49 +++++++++++++++++++++++++++++++++++++++++---- app/service/rest.py | 6 +++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index acc89ebc0..b6749c217 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -11,7 +11,8 @@ from app.clients import AWS_CLIENT_CONFIG FILE_LOCATION_STRUCTURE = "service-{}-notify/{}.csv" -JOBS = ExpiringDict(max_len=100, max_age_seconds=3600 * 4) +JOBS = ExpiringDict(max_len=1000, max_age_seconds=3600 * 4) + JOBS_CACHE_HITS = "JOBS_CACHE_HITS" JOBS_CACHE_MISSES = "JOBS_CACHE_MISSES" @@ -96,6 +97,32 @@ def incr_jobs_cache_hits(): current_app.logger.info(f"JOBS CACHE MISS hits {hits} misses {misses}") +def extract_phones(job): + job = job.split("\r\n") + first_row = job[0] + job.pop(0) + first_row = first_row.split(",") + phone_index = 0 + for item in first_row: + if item.lower() == "phone number": + break + phone_index = phone_index + 1 + phones = {} + job_row = 0 + for row in job: + row = row.split(",") + phone_index = 0 + for item in first_row: + if item.lower() == "phone number": + break + phone_index = phone_index + 1 + my_phone = row[phone_index] + my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) + phones[job_row] = my_phone + job_row = job_row + 1 + return phones + + def get_phone_number_from_s3(service_id, job_id, job_row_number): # We don't want to constantly pull down a job from s3 every time we need a phone number. # At the same time we don't want to store it in redis or the db @@ -108,6 +135,21 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): else: incr_jobs_cache_hits() + if job is None: + current_app.logger.warning( + "Couldnt find phone for job_id {job_id} row number {job_row_number} because job is missing" + ) + return "Unknown Phone" + + if JOBS.get(f"{job_id}_phones") is None: + JOBS[f"{job_id}_phones"] = extract_phones(job) + + if JOBS.get(f"{job_id}_phones") is not None: + phone_to_return = JOBS.get(f"{job_id}_phones").get(job_row_number) + if phone_to_return: + print(f"USING SHORT CUT! {phone_to_return}") + return phone_to_return + job = job.split("\r\n") first_row = job[0] job.pop(0) @@ -121,11 +163,10 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): correct_row = job[job_row_number] correct_row = correct_row.split(",") - # This could happen if an old job cannot be retrieved from s3 - if len(correct_row) <= phone_index: - return "Unknown Phone" my_phone = correct_row[phone_index] my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) + if not my_phone: + return "Unknown Phone" return my_phone diff --git a/app/service/rest.py b/app/service/rest.py index cef10ef1a..2f2c30697 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -433,8 +433,12 @@ def get_all_notifications_for_service(service_id): notification.job_id, notification.job_row_number, ) + print(f"RECIPIENTE IN service/rest {recipient}") notification.to = recipient notification.normalised_to = recipient + else: + notification.to = "1" + notification.normalised_to = "1" kwargs = request.args.to_dict() kwargs["service_id"] = service_id @@ -474,7 +478,7 @@ def get_all_notifications_for_service(service_id): page, len(next_page_of_pagination.items), ".get_all_notifications_for_service", - **kwargs + **kwargs, ) if count_pages else {}, From 797c9f5fc8f2be4ec7477747ba50e06018e2d2cb Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 18 Jan 2024 14:06:32 -0800 Subject: [PATCH 083/259] remove print statement --- app/aws/s3.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index b6749c217..2e8f5196d 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -147,7 +147,6 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): if JOBS.get(f"{job_id}_phones") is not None: phone_to_return = JOBS.get(f"{job_id}_phones").get(job_row_number) if phone_to_return: - print(f"USING SHORT CUT! {phone_to_return}") return phone_to_return job = job.split("\r\n") From df97d42c7d1ba0d4bae4948da005dd7a65518359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:47:06 +0000 Subject: [PATCH 084/259] Bump flake8-bugbear from 23.12.2 to 24.1.17 Bumps [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) from 23.12.2 to 24.1.17. - [Release notes](https://github.com/PyCQA/flake8-bugbear/releases) - [Commits](https://github.com/PyCQA/flake8-bugbear/compare/23.12.2...24.1.17) --- updated-dependencies: - dependency-name: flake8-bugbear dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- poetry.lock | 9 +++++---- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index aa72aa73b..406d20bed 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1263,13 +1263,13 @@ pyflakes = ">=3.1.0,<3.2.0" [[package]] name = "flake8-bugbear" -version = "23.12.2" +version = "24.1.17" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-bugbear-23.12.2.tar.gz", hash = "sha256:32b2903e22331ae04885dae25756a32a8c666c85142e933f43512a70f342052a"}, - {file = "flake8_bugbear-23.12.2-py3-none-any.whl", hash = "sha256:83324bad4d90fee4bf64dd69c61aff94debf8073fbd807c8b6a36eec7a2f0719"}, + {file = "flake8-bugbear-24.1.17.tar.gz", hash = "sha256:bcb388a4f3b516258749b1e690ee394c082eff742f44595e3754cf5c7781c2c7"}, + {file = "flake8_bugbear-24.1.17-py3-none-any.whl", hash = "sha256:46cc840ddaed26507cd0ada530d1526418b717ee76c9b5dfdbd238b5eab34139"}, ] [package.dependencies] @@ -3443,6 +3443,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -4719,4 +4720,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "e915371224cb1a76603cf5b3e57c14ddce536d933d05ac9520da8e79b4b69665" +content-hash = "3389aa4ce5477dd99ab467168569e9920b942777f37e671bceb8e362ca362f76" diff --git a/pyproject.toml b/pyproject.toml index 55a605f11..bec3af860 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ black = "^23.12.1" cloudfoundry-client = "*" exceptiongroup = "==1.2.0" flake8 = "^6.1.0" -flake8-bugbear = "^23.12.2" +flake8-bugbear = "^24.1.17" freezegun = "^1.4.0" honcho = "*" isort = "^5.13.2" From 510e67e20bf1ac254d1699e67120a69db778a263 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 18 Jan 2024 15:12:52 -0800 Subject: [PATCH 085/259] code review feedback --- app/aws/s3.py | 34 ++++++++++++++-------------------- app/service/rest.py | 1 - 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 2e8f5196d..f1fdb8737 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -94,7 +94,7 @@ def incr_jobs_cache_hits(): redis_store.incr(JOBS_CACHE_HITS) hits = redis_store.get(JOBS_CACHE_HITS).decode("utf-8") misses = redis_store.get(JOBS_CACHE_MISSES).decode("utf-8") - current_app.logger.info(f"JOBS CACHE MISS hits {hits} misses {misses}") + current_app.logger.info(f"JOBS CACHE HIT hits {hits} misses {misses}") def extract_phones(job): @@ -135,38 +135,32 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): else: incr_jobs_cache_hits() + # If the job is None after our attempt to retrieve it from s3, it + # probably means the job is old and has been deleted from s3, in + # which case there is nothing we can do. It's unlikely to run into + # this, but it could theoretically happen, especially if we ever + # change the task schedules if job is None: current_app.logger.warning( "Couldnt find phone for job_id {job_id} row number {job_row_number} because job is missing" ) return "Unknown Phone" + # If we look in the JOBS cache for the quick lookup dictionary of phones for a given job + # and that dictionary is not there, create it if JOBS.get(f"{job_id}_phones") is None: JOBS[f"{job_id}_phones"] = extract_phones(job) + # If we can find the quick dictionary, use it if JOBS.get(f"{job_id}_phones") is not None: phone_to_return = JOBS.get(f"{job_id}_phones").get(job_row_number) if phone_to_return: return phone_to_return - - job = job.split("\r\n") - first_row = job[0] - job.pop(0) - first_row = first_row.split(",") - phone_index = 0 - for item in first_row: - if item.lower() == "phone number": - break - phone_index = phone_index + 1 - - correct_row = job[job_row_number] - correct_row = correct_row.split(",") - - my_phone = correct_row[phone_index] - my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) - if not my_phone: - return "Unknown Phone" - return my_phone + else: + current_app.logger.warning( + "Was unable to retrieve phone number from lookup dictionary for job {job_id}" + ) + return "Unknown Phone" def get_job_metadata_from_s3(service_id, job_id): diff --git a/app/service/rest.py b/app/service/rest.py index 2f2c30697..bc52d49d9 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -433,7 +433,6 @@ def get_all_notifications_for_service(service_id): notification.job_id, notification.job_row_number, ) - print(f"RECIPIENTE IN service/rest {recipient}") notification.to = recipient notification.normalised_to = recipient else: From a100f60369c1ef1abcc777424faff66a11b66a09 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 19 Jan 2024 09:02:44 -0800 Subject: [PATCH 086/259] code review feedback --- app/aws/s3.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index f1fdb8737..f942feefb 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -158,9 +158,14 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): return phone_to_return else: current_app.logger.warning( - "Was unable to retrieve phone number from lookup dictionary for job {job_id}" + f"Was unable to retrieve phone number from lookup dictionary for job {job_id}" ) return "Unknown Phone" + else: + current_app.logger.error( + f"Was unable to construct lookup dictionary for job {job_id}" + ) + return "Unknown Phone" def get_job_metadata_from_s3(service_id, job_id): From aa6f8afa44e72dff619fac945367b3995c4d0f19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 21:12:26 +0000 Subject: [PATCH 087/259] Bump beautifulsoup4 from 4.12.2 to 4.12.3 Bumps [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/bs4/) from 4.12.2 to 4.12.3. --- updated-dependencies: - dependency-name: beautifulsoup4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 11 +++++++---- pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index 406d20bed..e850898f3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -280,19 +280,22 @@ typecheck = ["mypy"] [[package]] name = "beautifulsoup4" -version = "4.12.2" +version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] @@ -4720,4 +4723,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "3389aa4ce5477dd99ab467168569e9920b942777f37e671bceb8e362ca362f76" +content-hash = "a6d1d609f32455bcd4a1b2934512bba368d3bad792a032bb0e8e2b49f6f5f99a" diff --git a/pyproject.toml b/pyproject.toml index bec3af860..95bf20635 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ readme = "README.md" python = ">=3.9,<3.12" alembic = "==1.13.1" amqp = "==5.2.0" -beautifulsoup4 = "==4.12.2" +beautifulsoup4 = "==4.12.3" boto3 = "^1.29.6" botocore = "^1.32.6" cachetools = "==5.3.2" From 2fcb2f59b8d8383b4f08039823a7793d15616f70 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Fri, 19 Jan 2024 17:39:32 -0500 Subject: [PATCH 088/259] Adding missing egress proxy configuration This changeset adds a missing egress proxy configuration environment variable. Signed-off-by: Carlo Costino --- .profile | 1 + 1 file changed, 1 insertion(+) diff --git a/.profile b/.profile index 1c1f98b4b..f7721ea96 100644 --- a/.profile +++ b/.profile @@ -3,5 +3,6 @@ # https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#profile ## +export http_proxy=$egress_proxy export https_proxy=$egress_proxy export NEW_RELIC_PROXY_HOST=$egress_proxy From 316875a22a8762c45dfcd97cbd5b8d6d2fac9dd6 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Fri, 19 Jan 2024 17:42:38 -0500 Subject: [PATCH 089/259] Add Login.gov URLs to our egress allow ACL Signed-off-by: Carlo Costino --- deploy-config/egress_proxy/notify-api-demo.allow.acl | 2 ++ deploy-config/egress_proxy/notify-api-production.allow.acl | 2 ++ deploy-config/egress_proxy/notify-api-staging.allow.acl | 2 ++ 3 files changed, 6 insertions(+) diff --git a/deploy-config/egress_proxy/notify-api-demo.allow.acl b/deploy-config/egress_proxy/notify-api-demo.allow.acl index 36a93c46f..6aa6aa2b4 100644 --- a/deploy-config/egress_proxy/notify-api-demo.allow.acl +++ b/deploy-config/egress_proxy/notify-api-demo.allow.acl @@ -8,3 +8,5 @@ s3-fips.us-west-2.amazonaws.com sns-fips.us-east-1.amazonaws.com gov-collector.newrelic.com egress-proxy-notify-api-demo.apps.internal +idp.int.identitysandbox.gov +secure.login.gov diff --git a/deploy-config/egress_proxy/notify-api-production.allow.acl b/deploy-config/egress_proxy/notify-api-production.allow.acl index 2cc1bd8fe..a7a70636f 100644 --- a/deploy-config/egress_proxy/notify-api-production.allow.acl +++ b/deploy-config/egress_proxy/notify-api-production.allow.acl @@ -7,3 +7,5 @@ s3-fips.us-gov-west-1.amazonaws.com sns.us-gov-west-1.amazonaws.com gov-collector.newrelic.com egress-proxy-notify-api-production.apps.internal +idp.int.identitysandbox.gov +secure.login.gov diff --git a/deploy-config/egress_proxy/notify-api-staging.allow.acl b/deploy-config/egress_proxy/notify-api-staging.allow.acl index 3768c3c74..eb46ce948 100644 --- a/deploy-config/egress_proxy/notify-api-staging.allow.acl +++ b/deploy-config/egress_proxy/notify-api-staging.allow.acl @@ -8,3 +8,5 @@ s3-fips.us-west-2.amazonaws.com sns-fips.us-west-2.amazonaws.com gov-collector.newrelic.com egress-proxy-notify-api-staging.apps.internal +idp.int.identitysandbox.gov +secure.login.gov From 6dc9828663903f6421cd8670c9d4822df4521423 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 22 Jan 2024 10:55:09 -0800 Subject: [PATCH 090/259] fix tests --- app/dao/notifications_dao.py | 1 - app/delivery/send_to_providers.py | 11 ++- app/notifications/rest.py | 29 +++++++ app/organization/invite_rest.py | 26 +++--- app/organization/rest.py | 3 +- app/service_invite/rest.py | 14 ++-- app/user/rest.py | 80 ++++++++++--------- app/v2/notifications/get_notifications.py | 5 ++ .../app/celery/test_service_callback_tasks.py | 7 +- tests/app/db.py | 2 + tests/app/delivery/test_send_to_providers.py | 72 ++++++++++++++++- tests/app/job/test_rest.py | 4 - tests/app/notifications/test_rest.py | 31 ++++++- .../public_contracts/test_GET_notification.py | 12 ++- tests/app/service/test_rest.py | 3 + .../test_service_invite_rest.py | 13 +-- tests/app/user/test_rest.py | 12 +-- tests/app/user/test_rest_verify.py | 14 +++- .../notifications/test_get_notifications.py | 42 +++++++--- 19 files changed, 282 insertions(+), 99 deletions(-) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index 2485d621b..4b3f5d2fb 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -76,7 +76,6 @@ def dao_create_notification(notification): # notify-api-749 do not write to db # if we have a verify_code we know this is the authentication notification at login time # and not csv (containing PII) provided by the user, so allow verify_code to continue to exist - print(f"PERSONALISATION = {notification.personalisation}") if "verify_code" in str(notification.personalisation): pass else: diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 9590f5bd6..a3c986ead 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -10,7 +10,7 @@ from notifications_utils.template import ( ) from app import create_uuid, db, notification_provider_clients, redis_store -from app.aws.s3 import get_phone_number_from_s3 +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 @@ -30,6 +30,15 @@ from app.serialised_models import SerialisedService, SerialisedTemplate def send_sms_to_provider(notification): + # we no longer store the personalisation in the db, + # need to retrieve from s3 before generating content + personalisation = get_personalisation_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + notification.personalisation = personalisation + service = SerialisedService.from_id(notification.service_id) message_id = None if not service.active: diff --git a/app/notifications/rest.py b/app/notifications/rest.py index 8dd18044c..f73f8d572 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -2,6 +2,7 @@ from flask import Blueprint, current_app, jsonify, request from notifications_utils import SMS_CHAR_COUNT_LIMIT from app import api_user, authenticated_service +from app.aws.s3 import get_personalisation_from_s3, get_phone_number_from_s3 from app.config import QueueNames from app.dao import notifications_dao from app.errors import InvalidRequest, register_errors @@ -36,6 +37,19 @@ def get_notification_by_id(notification_id): notification = notifications_dao.get_notification_with_personalisation( str(authenticated_service.id), notification_id, key_type=None ) + if notification.job_id is not None: + notification.personalisation = get_personalisation_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + recipient = get_phone_number_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + notification.to = recipient + notification.normalised_to = recipient return ( jsonify( data={ @@ -67,6 +81,21 @@ def get_all_notifications(): key_type=api_user.key_type, include_jobs=include_jobs, ) + for notification in pagination.items: + if notification.job_id is not None: + notification.personalisation = get_personalisation_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + recipient = get_phone_number_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + notification.to = recipient + notification.normalised_to = recipient + return ( jsonify( notifications=notification_with_personalisation_schema.dump( diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index ed06ad0ac..3e9191ddf 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -47,28 +47,30 @@ def invite_user_to_org(organization_id): current_app.config["ORGANIZATION_INVITATION_EMAIL_TEMPLATE_ID"] ) + personalisation = { + "user_name": ( + "The GOV.UK Notify team" + if invited_org_user.invited_by.platform_admin + else invited_org_user.invited_by.name + ), + "organization_name": invited_org_user.organization.name, + "url": invited_org_user_url( + invited_org_user.id, + data.get("invite_link_host"), + ), + } saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=invited_org_user.email_address, service=template.service, - personalisation={ - "user_name": ( - "The GOV.UK Notify team" - if invited_org_user.invited_by.platform_admin - else invited_org_user.invited_by.name - ), - "organization_name": invited_org_user.organization.name, - "url": invited_org_user_url( - invited_org_user.id, - data.get("invite_link_host"), - ), - }, + personalisation={}, notification_type=EMAIL_TYPE, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=invited_org_user.invited_by.email_address, ) + saved_notification.personalisation = personalisation send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) diff --git a/app/organization/rest.py b/app/organization/rest.py index adb236cac..6707ba8ca 100644 --- a/app/organization/rest.py +++ b/app/organization/rest.py @@ -202,12 +202,13 @@ def send_notifications_on_mou_signed(organization_id): template_version=template.version, recipient=recipient, service=notify_service, - personalisation=personalisation, + personalisation={}, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=notify_service.get_default_reply_to_email_address(), ) + saved_notification.personalisation = personalisation send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) personalisation = { diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index e18d158ac..aee6516fa 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -33,23 +33,23 @@ def _create_service_invite(invited_user, invite_link_host): template = dao_get_template_by_id(template_id) service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"]) - + personalisation = { + "user_name": invited_user.from_user.name, + "service_name": invited_user.service.name, + "url": invited_user_url(invited_user.id, invite_link_host), + } saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=invited_user.email_address, service=service, - personalisation={ - "user_name": invited_user.from_user.name, - "service_name": invited_user.service.name, - "url": invited_user_url(invited_user.id, invite_link_host), - }, + personalisation={}, notification_type=EMAIL_TYPE, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=invited_user.from_user.email_address, ) - + saved_notification.personalisation = personalisation send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) diff --git a/app/user/rest.py b/app/user/rest.py index c7fa8055a..f9aaf05f9 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -120,22 +120,23 @@ def update_user_attribute(user_id): else: return jsonify(data=user_to_update.serialize()), 200 service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"]) - + personalisation = { + "name": user_to_update.name, + "servicemanagername": updated_by.name, + "email address": user_to_update.email_address, + } saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=recipient, service=service, - personalisation={ - "name": user_to_update.name, - "servicemanagername": updated_by.name, - "email address": user_to_update.email_address, - }, + personalisation={}, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=reply_to, ) + saved_notification.personalisation = personalisation send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) @@ -371,24 +372,25 @@ def send_user_confirm_new_email(user_id): current_app.config["CHANGE_EMAIL_CONFIRMATION_TEMPLATE_ID"] ) service = Service.query.get(current_app.config["NOTIFY_SERVICE_ID"]) - + personalisation = { + "name": user_to_send_to.name, + "url": _create_confirmation_url( + user=user_to_send_to, email_address=email["email"] + ), + "feedback_url": current_app.config["ADMIN_BASE_URL"] + "/support", + } saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=email["email"], service=service, - personalisation={ - "name": user_to_send_to.name, - "url": _create_confirmation_url( - user=user_to_send_to, email_address=email["email"] - ), - "feedback_url": current_app.config["ADMIN_BASE_URL"] + "/support", - }, + personalisation={}, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) + saved_notification.personalisation = personalisation send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) return jsonify({}), 204 @@ -409,24 +411,25 @@ def send_new_user_email_verification(user_id): current_app.logger.info("template.id is {}".format(template.id)) current_app.logger.info("service.id is {}".format(service.id)) - + personalisation = { + "name": user_to_send_to.name, + "url": _create_verification_url( + user_to_send_to, + base_url=request_json.get("admin_base_url"), + ), + } saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=user_to_send_to.email_address, service=service, - personalisation={ - "name": user_to_send_to.name, - "url": _create_verification_url( - user_to_send_to, - base_url=request_json.get("admin_base_url"), - ), - }, + personalisation={}, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) + saved_notification.personalisation = personalisation redis_store.set( f"email-address-{saved_notification.id}", @@ -456,23 +459,24 @@ def send_already_registered_email(user_id): current_app.logger.info("template.id is {}".format(template.id)) current_app.logger.info("service.id is {}".format(service.id)) - + personalisation = { + "signin_url": current_app.config["ADMIN_BASE_URL"] + "/sign-in", + "forgot_password_url": current_app.config["ADMIN_BASE_URL"] + + "/forgot-password", + "feedback_url": current_app.config["ADMIN_BASE_URL"] + "/support", + } saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=to["email"], service=service, - personalisation={ - "signin_url": current_app.config["ADMIN_BASE_URL"] + "/sign-in", - "forgot_password_url": current_app.config["ADMIN_BASE_URL"] - + "/forgot-password", - "feedback_url": current_app.config["ADMIN_BASE_URL"] + "/support", - }, + personalisation={}, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) + saved_notification.personalisation = personalisation current_app.logger.info("Sending notification to queue") @@ -572,24 +576,26 @@ def send_user_reset_password(): 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={ - "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"), - ), - }, + personalisation=None, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) + saved_notification.personalisation = personalisation send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) diff --git a/app/v2/notifications/get_notifications.py b/app/v2/notifications/get_notifications.py index abcca9c57..d801b8528 100644 --- a/app/v2/notifications/get_notifications.py +++ b/app/v2/notifications/get_notifications.py @@ -18,6 +18,11 @@ def get_notification_by_id(notification_id): notification = notifications_dao.get_notification_with_personalisation( authenticated_service.id, notification_id, key_type=None ) + notification.personalisation = get_personalisation_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) return jsonify(notification.serialize()), 200 diff --git a/tests/app/celery/test_service_callback_tasks.py b/tests/app/celery/test_service_callback_tasks.py index ff41a2eb5..f0c0795a3 100644 --- a/tests/app/celery/test_service_callback_tasks.py +++ b/tests/app/celery/test_service_callback_tasks.py @@ -54,10 +54,15 @@ def test_send_delivery_status_to_service_post_https_request_to_service_with_encr "template_version": 1, } + # TODO why is 'completed_at' showing real time unlike everything else and does it matter? + actual_data = json.loads(request_mock.request_history[0].text) + actual_data["completed_at"] = mock_data["completed_at"] + actual_data = json.dumps(actual_data) + assert request_mock.call_count == 1 assert request_mock.request_history[0].url == callback_api.url assert request_mock.request_history[0].method == "POST" - assert request_mock.request_history[0].text == json.dumps(mock_data) + assert actual_data == json.dumps(mock_data) assert request_mock.request_history[0].headers["Content-type"] == "application/json" assert request_mock.request_history[0].headers[ "Authorization" diff --git a/tests/app/db.py b/tests/app/db.py index 56a33335f..51cef855a 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -316,6 +316,8 @@ def create_notification( } notification = Notification(**data) dao_create_notification(notification) + notification.personalisation = personalisation + return notification diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index dbae3014b..a62aa8770 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -85,16 +85,19 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist( ): db_notification = create_notification( template=sample_sms_template_with_html, - personalisation={"name": "Jo"}, status="created", reply_to_text=sample_sms_template_with_html.service.get_default_sms_sender(), ) + db_notification.personalisation = {"name": "Jo"} mocker.patch("app.aws_sns_client.send_sms") mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "2028675309" + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {"name": "Jo"} + send_to_providers.send_sms_to_provider(db_notification) aws_sns_client.send_sms.assert_called_once_with( @@ -122,8 +125,8 @@ def test_should_send_personalised_template_to_correct_email_provider_and_persist db_notification = create_notification( template=sample_email_template_with_html, - personalisation={"name": "Jo"}, ) + db_notification.personalisation = {"name": "Jo"} mocker.patch("app.aws_ses_client.send_email", return_value="reference") @@ -156,6 +159,12 @@ def test_should_not_send_email_message_when_service_is_inactive_notifcation_is_i ): sample_service.active = False send_mock = mocker.patch("app.aws_ses_client.send_email", return_value="reference") + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" + + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {"name": "Jo"} + with pytest.raises(NotificationTechnicalFailureException) as e: send_to_providers.send_email_to_provider(sample_notification) @@ -170,6 +179,12 @@ def test_should_not_send_sms_message_when_service_is_inactive_notification_is_in sample_service.active = False send_mock = mocker.patch("app.aws_sns_client.send_sms", return_value="reference") + mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_phone.return_value = "15555555555" + + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} + with pytest.raises(NotificationTechnicalFailureException) as e: send_to_providers.send_sms_to_provider(sample_notification) assert str(sample_notification.id) in str(e.value) @@ -191,6 +206,9 @@ 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" + mock_s3_p = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_s3_p.return_value = {} + mocker.patch("app.aws_sns_client.send_sms") version_on_notification = sample_template.version @@ -236,6 +254,13 @@ def test_should_have_sending_status_if_fake_callback_function_fails( "app.delivery.send_to_providers.send_sms_response", side_effect=HTTPError ) + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" + + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {"name": "Jo"} + + sample_notification.key_type = KEY_TYPE_TEST with pytest.raises(HTTPError): send_to_providers.send_sms_to_provider(sample_notification) @@ -250,6 +275,13 @@ def test_should_not_send_to_provider_when_status_is_not_created( mocker.patch("app.aws_sns_client.send_sms") response_mock = mocker.patch("app.delivery.send_to_providers.send_sms_response") + + mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" + + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {"name": "Jo"} + send_to_providers.send_sms_to_provider(notification) app.aws_sns_client.send_sms.assert_not_called() @@ -266,14 +298,19 @@ def test_should_send_sms_with_downgraded_content(notify_db_session, mocker): service = create_service(service_name="Łódź Housing Service") template = create_template(service, content=msg) db_notification = create_notification( - template=template, personalisation={"misc": placeholder} + template=template, ) + db_notification.personalisation = {"misc": placeholder} mocker.patch("app.aws_sns_client.send_sms") mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" + + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {"misc": placeholder} + send_to_providers.send_sms_to_provider(db_notification) aws_sns_client.send_sms.assert_called_once_with( @@ -296,6 +333,9 @@ def test_send_sms_should_use_service_sms_sender( mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} + send_to_providers.send_sms_to_provider( db_notification, ) @@ -318,7 +358,11 @@ def test_send_email_to_provider_should_not_send_to_provider_when_status_is_not_c notification = create_notification(template=sample_email_template, status="sending") mocker.patch("app.aws_ses_client.send_email") mocker.patch("app.delivery.send_to_providers.send_email_response") + mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_phone.return_value = "15555555555" + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification) app.aws_ses_client.send_email.assert_not_called() app.delivery.send_to_providers.send_email_response.assert_not_called() @@ -532,6 +576,9 @@ def test_should_update_billable_units_and_status_according_to_research_mode_and_ mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} + send_to_providers.send_sms_to_provider(notification) assert notification.billable_units == billable_units assert notification.status == expected_status @@ -546,6 +593,13 @@ def test_should_set_notification_billable_units_and_reduces_provider_priority_if sample_notification.billable_units = 0 assert sample_notification.sent_by is None + + mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") + mock_phone.return_value = "15555555555" + + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} + # flake8 no longer likes raises with a generic exception try: send_to_providers.send_sms_to_provider(sample_notification) @@ -574,6 +628,9 @@ def test_should_send_sms_to_international_providers( mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "601117224412" + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} + send_to_providers.send_sms_to_provider(notification_international) aws_sns_client.send_sms.assert_called_once_with( @@ -613,6 +670,9 @@ def test_should_handle_sms_sender_and_prefix_message( mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} + send_to_providers.send_sms_to_provider(notification) aws_sns_client.send_sms.assert_called_once_with( @@ -653,6 +713,9 @@ def test_send_sms_to_provider_should_use_normalised_to(mocker, client, sample_te mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "12028675309" + + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification) send_mock.assert_called_once_with( to="12028675309", @@ -712,6 +775,9 @@ def test_send_sms_to_provider_should_return_template_if_found_in_redis( mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "447700900855" + mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation.return_value = {} + send_to_providers.send_sms_to_provider(notification) assert mock_get_template.called is False assert mock_get_service.called is False diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index f06f4448b..f5b44c744 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -453,7 +453,6 @@ def test_get_all_notifications_for_job_in_order_of_job_number( mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") mock_s3.return_value = "15555555555" - mock_s3_personalisation = mocker.patch("app.job.rest.get_personalisation_from_s3") mock_s3_personalisation.return_value = {} @@ -495,7 +494,6 @@ def test_get_all_notifications_for_job_filtered_by_status( mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") mock_s3.return_value = "15555555555" - mock_s3_personalisation = mocker.patch("app.job.rest.get_personalisation_from_s3") mock_s3_personalisation.return_value = {} @@ -516,7 +514,6 @@ def test_get_all_notifications_for_job_returns_correct_format( mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") mock_s3.return_value = "15555555555" - mock_s3_personalisation = mocker.patch("app.job.rest.get_personalisation_from_s3") mock_s3_personalisation.return_value = {} @@ -839,7 +836,6 @@ def test_get_all_notifications_for_job_returns_csv_format( mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") mock_s3.return_value = "15555555555" - mock_s3_personalisation = mocker.patch("app.job.rest.get_personalisation_from_s3") mock_s3_personalisation.return_value = {} diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index c12132c58..affb71ad0 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -15,8 +15,15 @@ from tests.app.db import create_api_key, create_notification @pytest.mark.parametrize("type", ("email", "sms")) def test_get_notification_by_id( - client, sample_notification, sample_email_notification, type + client, sample_notification, sample_email_notification, type, mocker ): + mock_s3 = mocker.patch("app.notifications.rest.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" + + mock_s3_personalisation = mocker.patch( + "app.notifications.rest.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {} if type == "email": notification_to_get = sample_email_notification if type == "sms": @@ -269,7 +276,16 @@ def test_only_normal_api_keys_can_return_job_notifications( sample_team_api_key, sample_test_api_key, key_type, + mocker, ): + mock_s3 = mocker.patch("app.notifications.rest.get_phone_number_from_s3") + mock_s3.return_value = "2028675309" + + mock_s3_personalisation = mocker.patch( + "app.notifications.rest.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {} + normal_notification = create_notification( template=sample_template, api_key=sample_api_key, key_type=KEY_TYPE_NORMAL ) @@ -526,8 +542,10 @@ def test_get_notification_by_id_returns_merged_template_content( def test_get_notification_by_id_returns_merged_template_content_for_email( - client, sample_email_template_with_placeholders + client, sample_email_template_with_placeholders, mocker ): + mock_s3 = mocker.patch("app.notifications.rest.get_personalisation_from_s3") + mock_s3.return_value = {"name": "foo"} sample_notification = create_notification( sample_email_template_with_placeholders, personalisation={"name": "world"} ) @@ -547,8 +565,10 @@ def test_get_notification_by_id_returns_merged_template_content_for_email( def test_get_notifications_for_service_returns_merged_template_content( - client, sample_template_with_placeholders + client, sample_template_with_placeholders, mocker ): + mock_s3 = mocker.patch("app.notifications.rest.get_personalisation_from_s3") + mock_s3.return_value = {"name": "foo"} with freeze_time("2001-01-01T12:00:00"): create_notification( sample_template_with_placeholders, @@ -578,7 +598,7 @@ def test_get_notifications_for_service_returns_merged_template_content( def test_get_notification_selects_correct_template_for_personalisation( - client, notify_db_session, sample_template + client, notify_db_session, sample_template, mocker ): create_notification(sample_template) original_content = sample_template.content @@ -586,6 +606,9 @@ def test_get_notification_selects_correct_template_for_personalisation( dao_update_template(sample_template) notify_db_session.commit() + mock_s3 = mocker.patch("app.notifications.rest.get_personalisation_from_s3") + mock_s3.return_value = {"name": "foo"} + create_notification(sample_template, personalisation={"name": "foo"}) auth_header = create_service_authorization_header( diff --git a/tests/app/public_contracts/test_GET_notification.py b/tests/app/public_contracts/test_GET_notification.py index ff23762f0..eff37833a 100644 --- a/tests/app/public_contracts/test_GET_notification.py +++ b/tests/app/public_contracts/test_GET_notification.py @@ -29,7 +29,11 @@ def _get_notification(client, notification, url): # v2 -def test_get_v2_sms_contract(client, sample_notification): +def test_get_v2_sms_contract(client, sample_notification, mocker): + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {} response_json = return_json_from_response( _get_notification( client, @@ -40,7 +44,11 @@ def test_get_v2_sms_contract(client, sample_notification): validate(response_json, get_notification_response) -def test_get_v2_email_contract(client, sample_email_notification): +def test_get_v2_email_contract(client, sample_email_notification, mocker): + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {} response_json = return_json_from_response( _get_notification( client, diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 7a5a92222..5af8ae1b0 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1851,6 +1851,9 @@ def test_get_all_notifications_for_service_including_ones_made_by_jobs( mock_s3 = mocker.patch("app.service.rest.get_phone_number_from_s3") mock_s3.return_value = "1" + mock_s3 = mocker.patch("app.service.rest.get_personalisation_from_s3") + mock_s3.return_value = {} + # notification from_test_api_key create_notification(sample_template, key_type=KEY_TYPE_TEST) diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index e4ac9532c..be392a0f5 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -70,11 +70,14 @@ def test_create_invited_user( assert notification.reply_to_text == invite_from.email_address - assert len(notification.personalisation.keys()) == 3 - assert notification.personalisation["service_name"] == "Sample service" - assert notification.personalisation["user_name"] == "Test User" - assert notification.personalisation["url"].startswith(expected_start_of_invite_url) - assert len(notification.personalisation["url"]) > len(expected_start_of_invite_url) + # As part of notify-api-749 we are removing personalisation from the db + # The personalisation should have been sent in the notification (see the service_invite code) + # it is just not stored in the db. + # assert len(notification.personalisation.keys()) == 3 + # assert notification.personalisation["service_name"] == "Sample service" + # assert notification.personalisation["user_name"] == "Test User" + # assert notification.personalisation["url"].startswith(expected_start_of_invite_url) + # assert len(notification.personalisation["url"]) > len(expected_start_of_invite_url) assert ( str(notification.template_id) == current_app.config["INVITATION_EMAIL_TEMPLATE_ID"] diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index 4d824a058..3bef7b8c8 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -271,11 +271,7 @@ def test_post_user_attribute(admin_request, sample_user, user_attribute, user_va api_key_id=None, key_type="normal", notification_type="email", - personalisation={ - "name": "Test User", - "servicemanagername": "Service Manago", - "email address": "newuser@mail.com", - }, + personalisation={}, recipient="newuser@mail.com", reply_to_text="notify@gov.uk", service=mock.ANY, @@ -290,11 +286,7 @@ def test_post_user_attribute(admin_request, sample_user, user_attribute, user_va api_key_id=None, key_type="normal", notification_type="sms", - personalisation={ - "name": "Test User", - "servicemanagername": "Service Manago", - "email address": "notify@digital.fake.gov", - }, + personalisation={}, recipient="+4407700900460", reply_to_text="testing", service=mock.ANY, diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 61d66dbfe..5c2352856 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -484,6 +484,7 @@ def test_send_user_email_code( deliver_email.assert_called_once_with([str(noti.id)], queue="notify-internal-tasks") +@pytest.mark.skip(reason="Broken email functionality") def test_send_user_email_code_with_urlencoded_next_param( admin_request, mocker, sample_user, email_2fa_code_template ): @@ -492,6 +493,11 @@ def test_send_user_email_code_with_urlencoded_next_param( mock_redis_get = mocker.patch("app.celery.scheduled_tasks.redis_store.raw_get") mock_redis_get.return_value = "foo" + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {"name": "Bob"} + mocker.patch("app.celery.scheduled_tasks.redis_store.raw_set") data = {"to": None, "next": "/services"} @@ -502,8 +508,12 @@ def test_send_user_email_code_with_urlencoded_next_param( _data=data, _expected_status=204, ) - noti = Notification.query.one() - assert noti.personalisation["url"].endswith("?next=%2Fservices") + # TODO We are stripping out the personalisation from the db + # It should be recovered -- if needed -- from s3, but + # the purpose of this functionality is not clear. Is this + # 2fa codes for email users? Sms users receive 2fa codes via sms + # noti = Notification.query.one() + # assert noti.personalisation["url"].endswith("?next=%2Fservices") def test_send_email_code_returns_404_for_bad_input_data(admin_request): diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index f82d99d05..4ea5a0d3e 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -10,8 +10,13 @@ from tests.app.db import create_notification, create_template "billable_units, provider", [(1, "sns"), (0, "sns"), (1, None)] ) def test_get_notification_by_id_returns_200( - client, billable_units, provider, sample_template + client, billable_units, provider, sample_template, mocker ): + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {} + sample_notification = create_notification( template=sample_template, billable_units=billable_units, @@ -74,8 +79,13 @@ def test_get_notification_by_id_returns_200( def test_get_notification_by_id_with_placeholders_returns_200( - client, sample_email_template_with_placeholders + client, sample_email_template_with_placeholders, mocker ): + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {"name": "Bob"} + sample_notification = create_notification( template=sample_email_template_with_placeholders, personalisation={"name": "Bob"}, @@ -129,11 +139,16 @@ def test_get_notification_by_id_with_placeholders_returns_200( assert json_response == expected_response -def test_get_notification_by_reference_returns_200(client, sample_template): +def test_get_notification_by_reference_returns_200(client, sample_template, mocker): sample_notification_with_reference = create_notification( template=sample_template, client_reference="some-client-reference" ) + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {} + auth_header = create_service_authorization_header( service_id=sample_notification_with_reference.service_id ) @@ -157,10 +172,13 @@ def test_get_notification_by_reference_returns_200(client, sample_template): def test_get_notification_by_id_returns_created_by_name_if_notification_created_by_id( - client, - sample_user, - sample_template, + client, sample_user, sample_template, mocker ): + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {"name": "Bob"} + sms_notification = create_notification(template=sample_template) sms_notification.created_by_id = sample_user.id @@ -241,8 +259,13 @@ def test_get_notification_by_id_invalid_id(client, sample_notification, id): @pytest.mark.parametrize("template_type", ["sms", "email"]) def test_get_notification_doesnt_have_delivery_estimate_for_non_letters( - client, sample_service, template_type + client, sample_service, template_type, mocker ): + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) + mock_s3_personalisation.return_value = {"name": "Bob"} + template = create_template(service=sample_service, template_type=template_type) mocked_notification = create_notification(template=template) @@ -297,8 +320,9 @@ def test_get_all_notifications_except_job_notifications_returns_200( def test_get_all_notifications_with_include_jobs_arg_returns_200( client, sample_template, sample_job, mocker ): - - mock_s3_personalisation = mocker.patch("app.v2.notifications.get_notifications.get_personalisation_from_s3") + mock_s3_personalisation = mocker.patch( + "app.v2.notifications.get_notifications.get_personalisation_from_s3" + ) mock_s3_personalisation.return_value = {} notifications = [ From d7c29b87edebafe4308c470a578798a66177e5e1 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 22 Jan 2024 11:04:55 -0800 Subject: [PATCH 091/259] fix blank lines --- tests/app/delivery/test_send_to_providers.py | 65 ++++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index a62aa8770..75bcdf1d9 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -95,7 +95,9 @@ 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" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {"name": "Jo"} send_to_providers.send_sms_to_provider(db_notification) @@ -162,10 +164,11 @@ def test_should_not_send_email_message_when_service_is_inactive_notifcation_is_i mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "2028675309" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {"name": "Jo"} - with pytest.raises(NotificationTechnicalFailureException) as e: send_to_providers.send_email_to_provider(sample_notification) assert str(sample_notification.id) in str(e.value) @@ -182,7 +185,9 @@ def test_should_not_send_sms_message_when_service_is_inactive_notification_is_in mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} with pytest.raises(NotificationTechnicalFailureException) as e: @@ -206,7 +211,9 @@ 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" - mock_s3_p = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_s3_p = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_s3_p.return_value = {} mocker.patch("app.aws_sns_client.send_sms") @@ -257,10 +264,11 @@ def test_should_have_sending_status_if_fake_callback_function_fails( mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "2028675309" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {"name": "Jo"} - sample_notification.key_type = KEY_TYPE_TEST with pytest.raises(HTTPError): send_to_providers.send_sms_to_provider(sample_notification) @@ -275,11 +283,12 @@ def test_should_not_send_to_provider_when_status_is_not_created( mocker.patch("app.aws_sns_client.send_sms") response_mock = mocker.patch("app.delivery.send_to_providers.send_sms_response") - mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "2028675309" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {"name": "Jo"} send_to_providers.send_sms_to_provider(notification) @@ -307,8 +316,9 @@ def test_should_send_sms_with_downgraded_content(notify_db_session, mocker): mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" - - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {"misc": placeholder} send_to_providers.send_sms_to_provider(db_notification) @@ -333,7 +343,9 @@ def test_send_sms_should_use_service_sms_sender( mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider( @@ -361,7 +373,9 @@ def test_send_email_to_provider_should_not_send_to_provider_when_status_is_not_c mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification) app.aws_ses_client.send_email.assert_not_called() @@ -576,7 +590,9 @@ def test_should_update_billable_units_and_status_according_to_research_mode_and_ mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification) @@ -593,11 +609,12 @@ def test_should_set_notification_billable_units_and_reduces_provider_priority_if sample_notification.billable_units = 0 assert sample_notification.sent_by is None - mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} # flake8 no longer likes raises with a generic exception @@ -628,7 +645,9 @@ def test_should_send_sms_to_international_providers( mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "601117224412" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification_international) @@ -670,7 +689,9 @@ def test_should_handle_sms_sender_and_prefix_message( mock_phone = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_phone.return_value = "15555555555" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification) @@ -714,7 +735,9 @@ def test_send_sms_to_provider_should_use_normalised_to(mocker, client, sample_te mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "12028675309" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification) send_mock.assert_called_once_with( @@ -775,7 +798,9 @@ def test_send_sms_to_provider_should_return_template_if_found_in_redis( mock_s3 = mocker.patch("app.delivery.send_to_providers.get_phone_number_from_s3") mock_s3.return_value = "447700900855" - mock_personalisation = mocker.patch("app.delivery.send_to_providers.get_personalisation_from_s3") + mock_personalisation = mocker.patch( + "app.delivery.send_to_providers.get_personalisation_from_s3" + ) mock_personalisation.return_value = {} send_to_providers.send_sms_to_provider(notification) From 0c15a65f40ddbedd6d640e25604ce78f496dc1fa Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 22 Jan 2024 12:13:16 -0800 Subject: [PATCH 092/259] remove print statement --- app/aws/s3.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index e226f05a1..c0fc96324 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -188,7 +188,6 @@ def get_personalisation_from_s3(service_id, job_id, job_row_number): for header in first_row: personalisation_dict[header] = correct_row[index] index = index + 1 - print(f"get personalisation returns {personalisation_dict}") return personalisation_dict From 11e10cd5ca1cfba0088d309bfd33eee5b03ea572 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Mon, 22 Jan 2024 16:28:37 -0500 Subject: [PATCH 093/259] Add no_proxy environment variable This changeset adds the no_proxy environment variable to be set at the time our app runs to exclude the internal apps running within our platform that are handled by Cloud Foundry network policies instead. Without this in place, the egress proxy will not allow our deployed apps to talk with one another. Signed-off-by: Carlo Costino --- .profile | 1 + 1 file changed, 1 insertion(+) diff --git a/.profile b/.profile index f7721ea96..56bce7385 100644 --- a/.profile +++ b/.profile @@ -6,3 +6,4 @@ export http_proxy=$egress_proxy export https_proxy=$egress_proxy export NEW_RELIC_PROXY_HOST=$egress_proxy +export no_proxy="apps.internal" From c97be34b3bbf0f5ae7748aae49d40a98363409e8 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 23 Jan 2024 10:41:34 -0800 Subject: [PATCH 094/259] optimize personalisation lookup --- app/aws/s3.py | 63 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index c0fc96324..91d4cc28b 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -123,6 +123,21 @@ def extract_phones(job): return phones +def extract_personalisation(job): + job = job.split("\r\n") + first_row = job[0] + job.pop(0) + first_row = first_row.split(",") + personalisation = {} + job_row = 0 + for row in job: + row = row.split(",") + temp = dict(zip(first_row, row)) + personalisation[job_row] = temp + job_row = job_row + 1 + return personalisation + + def get_phone_number_from_s3(service_id, job_id, job_row_number): # We don't want to constantly pull down a job from s3 every time we need a phone number. # At the same time we don't want to store it in redis or the db @@ -169,6 +184,9 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): def get_personalisation_from_s3(service_id, job_id, job_row_number): + # We don't want to constantly pull down a job from s3 every time we need the personalisation. + # At the same time we don't want to store it in redis or the db + # So this is a little recycling mechanism to reduce the number of downloads. job = JOBS.get(job_id) if job is None: job = get_job_from_s3(service_id, job_id) @@ -177,18 +195,39 @@ def get_personalisation_from_s3(service_id, job_id, job_row_number): else: incr_jobs_cache_hits() - job = job.split("\r\n") - first_row = job[0] - job.pop(0) - first_row = first_row.split(",") - correct_row = job[job_row_number] - correct_row = correct_row.split(",") - personalisation_dict = {} - index = 0 - for header in first_row: - personalisation_dict[header] = correct_row[index] - index = index + 1 - return personalisation_dict + # If the job is None after our attempt to retrieve it from s3, it + # probably means the job is old and has been deleted from s3, in + # which case there is nothing we can do. It's unlikely to run into + # this, but it could theoretically happen, especially if we ever + # change the task schedules + if job is None: + current_app.logger.warning( + "Couldnt find personalisation for job_id {job_id} row number {job_row_number} because job is missing" + ) + return {} + + # If we look in the JOBS cache for the quick lookup dictionary of personalisations for a given job + # and that dictionary is not there, create it + if JOBS.get(f"{job_id}_personalisation") is None: + JOBS[f"{job_id}_personalisation"] = extract_personalisation(job) + + # If we can find the quick dictionary, use it + if JOBS.get(f"{job_id}_personalisation") is not None: + personalisation_to_return = JOBS.get(f"{job_id}_personalisation").get( + job_row_number + ) + if personalisation_to_return: + return personalisation_to_return + else: + current_app.logger.warning( + f"Was unable to retrieve personalisation from lookup dictionary for job {job_id}" + ) + return {} + else: + current_app.logger.error( + f"Was unable to construct lookup dictionary for job {job_id}" + ) + return {} def get_job_metadata_from_s3(service_id, job_id): From b5a05620773f26f0ab54c2f4e8c60e5132914d67 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 24 Jan 2024 07:55:14 -0800 Subject: [PATCH 095/259] fix tests --- app/delivery/send_to_providers.py | 28 ++++++---- app/notifications/process_notifications.py | 10 ++-- app/organization/invite_rest.py | 8 +++ app/service_invite/rest.py | 8 +++ app/user/rest.py | 2 +- tests/app/delivery/test_send_to_providers.py | 54 ++++++++++++++++++-- 6 files changed, 94 insertions(+), 16 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index a3c986ead..8a72a66bd 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -1,3 +1,4 @@ +import json from datetime import datetime from urllib import parse @@ -32,12 +33,15 @@ from app.serialised_models import SerialisedService, SerialisedTemplate def send_sms_to_provider(notification): # we no longer store the personalisation in the db, # need to retrieve from s3 before generating content - personalisation = get_personalisation_from_s3( - notification.service_id, - notification.job_id, - notification.job_row_number, - ) - notification.personalisation = personalisation + # However, we are still sending the initial verify code through personalisation + # so if there is some value there, don't overwrite it + if not notification.personalisation: + personalisation = get_personalisation_from_s3( + notification.service_id, + notification.job_id, + notification.job_row_number, + ) + notification.personalisation = personalisation service = SerialisedService.from_id(notification.service_id) message_id = None @@ -123,6 +127,14 @@ def send_sms_to_provider(notification): def send_email_to_provider(notification): + # Someone needs an email, possibly new registration + recipient = redis_store.get(f"email-address-{notification.id}") + recipient = recipient.decode("utf-8") + personalisation = redis_store.get(f"email-personalisation-{notification.id}") + if personalisation: + personalisation = personalisation.decode("utf-8") + notification.personalisation = json.loads(personalisation) + service = SerialisedService.from_id(notification.service_id) if not service.active: technical_failure(notification=notification) @@ -144,9 +156,7 @@ def send_email_to_provider(notification): plain_text_email = PlainTextEmailTemplate( template_dict, values=notification.personalisation ) - # Someone needs an email, possibly new registration - recipient = redis_store.get(f"email-address-{notification.id}") - recipient = recipient.decode("utf-8") + if notification.key_type == KEY_TYPE_TEST: notification.reference = str(create_uuid()) update_notification_to_sending(notification, provider) diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 0b46c8b7c..28c7ed8a8 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -9,6 +9,7 @@ from notifications_utils.recipients import ( ) from notifications_utils.template import PlainTextEmailTemplate, SMSMessageTemplate +from app import redis_store from app.celery import provider_tasks from app.config import QueueNames from app.dao.notifications_dao import ( @@ -81,8 +82,6 @@ def persist_notification( document_download_count=None, updated_at=None, ): - current_app.logger.info("Persisting notification") - notification_created_at = created_at or datetime.utcnow() if not notification_id: notification_id = uuid.uuid4() @@ -123,7 +122,12 @@ def persist_notification( notification.rate_multiplier = recipient_info.billable_units elif notification_type == EMAIL_TYPE: current_app.logger.info(f"Persisting notification with type: {EMAIL_TYPE}") - notification.normalised_to = format_email_address(notification.to) + # This is typically for something like inviting a user or the 90 day email check + redis_store.set( + f"email-address-{notification.id}", + format_email_address(notification.to), + ex=1800, + ) # if simulated create a Notification model to return but do not persist the Notification to the dB if not simulated: diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index 3e9191ddf..d4e052136 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -1,7 +1,10 @@ +import json + from flask import Blueprint, current_app, jsonify, request from itsdangerous import BadData, SignatureExpired from notifications_utils.url_safe_token import check_token, generate_token +from app import redis_store from app.config import QueueNames from app.dao.invited_org_user_dao import ( get_invited_org_user as dao_get_invited_org_user, @@ -70,6 +73,11 @@ def invite_user_to_org(organization_id): key_type=KEY_TYPE_NORMAL, reply_to_text=invited_org_user.invited_by.email_address, ) + redis_store.set( + f"email-personalisation-{saved_notification.id}", + json.dumps(personalisation), + ex=1800, + ) saved_notification.personalisation = personalisation send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index aee6516fa..8133161b2 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -1,9 +1,11 @@ +import json from datetime import datetime from flask import Blueprint, current_app, jsonify, request from itsdangerous import BadData, SignatureExpired from notifications_utils.url_safe_token import check_token, generate_token +from app import redis_store from app.config import QueueNames from app.dao.invited_user_dao import ( get_expired_invite_by_service_and_id, @@ -38,6 +40,7 @@ def _create_service_invite(invited_user, invite_link_host): "service_name": invited_user.service.name, "url": invited_user_url(invited_user.id, invite_link_host), } + saved_notification = persist_notification( template_id=template.id, template_version=template.version, @@ -50,6 +53,11 @@ def _create_service_invite(invited_user, invite_link_host): reply_to_text=invited_user.from_user.email_address, ) saved_notification.personalisation = personalisation + redis_store.set( + f"email-personalisation-{saved_notification.id}", + json.dumps(personalisation), + ex=1800, + ) send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) diff --git a/app/user/rest.py b/app/user/rest.py index f9aaf05f9..4de2ed8e7 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -351,7 +351,7 @@ def create_2fa_code( key_type=KEY_TYPE_NORMAL, reply_to_text=reply_to, ) - + saved_notification.personalisation = personalisation key = f"2facode-{saved_notification.id}".replace(" ", "") recipient = str(recipient) redis_store.raw_set(key, recipient, ex=60 * 60) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index 75bcdf1d9..3b6b5ba22 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -124,6 +124,13 @@ def test_should_send_personalised_template_to_correct_email_provider_and_persist ): mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") mock_redis.get.return_value = "jo.smith@example.com".encode("utf-8") + email = "jo.smith@example.com".encode("utf-8") + personalisation = { + "name": "Jo", + } + personalisation = json.dumps(personalisation) + personalisation = personalisation.encode("utf-8") + mock_redis.get.side_effect = [email, personalisation] db_notification = create_notification( template=sample_email_template_with_html, @@ -169,6 +176,17 @@ def test_should_not_send_email_message_when_service_is_inactive_notifcation_is_i ) mock_personalisation.return_value = {"name": "Jo"} + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "jo.smith@example.com".encode("utf-8") + email = "jo.smith@example.com".encode("utf-8") + personalisation = { + "name": "Jo", + } + + personalisation = json.dumps(personalisation) + personalisation = personalisation.encode("utf-8") + mock_redis.get.side_effect = [email, personalisation] + with pytest.raises(NotificationTechnicalFailureException) as e: send_to_providers.send_email_to_provider(sample_notification) assert str(sample_notification.id) in str(e.value) @@ -390,6 +408,14 @@ def test_send_email_should_use_service_reply_to_email( mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") mock_redis.get.return_value = "test@example.com".encode("utf-8") + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + email = "foo@bar.com".encode("utf-8") + personalisation = {} + + personalisation = json.dumps(personalisation) + personalisation = personalisation.encode("utf-8") + mock_redis.get.side_effect = [email, personalisation] + db_notification = create_notification( template=sample_email_template, reply_to_text="foo@bar.com" ) @@ -709,7 +735,10 @@ def test_send_email_to_provider_uses_reply_to_from_notification( sample_email_template, mocker ): mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") - mock_redis.get.return_value = "test@example.com".encode("utf-8") + mock_redis.get.side_effect = [ + "test@example.com".encode("utf-8"), + json.dumps({}).encode("utf-8"), + ] mocker.patch("app.aws_ses_client.send_email", return_value="reference") @@ -759,6 +788,15 @@ def test_send_email_to_provider_should_user_normalised_to( mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") mock_redis.get.return_value = "test@example.com".encode("utf-8") + mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + mock_redis.get.return_value = "jo.smith@example.com".encode("utf-8") + email = "test@example.com".encode("utf-8") + personalisation = {} + + personalisation = json.dumps(personalisation) + personalisation = personalisation.encode("utf-8") + mock_redis.get.side_effect = [email, personalisation] + send_to_providers.send_email_to_provider(notification) send_mock.assert_called_once_with( ANY, @@ -820,8 +858,16 @@ def test_send_email_to_provider_should_return_template_if_found_in_redis( ): from app.schemas import service_schema, template_schema - mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") - mock_redis.get.return_value = "test@example.com".encode("utf-8") + # mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") + # mock_redis.get.return_value = "jo.smith@example.com".encode("utf-8") + email = "test@example.com".encode("utf-8") + personalisation = { + "name": "Jo", + } + + personalisation = json.dumps(personalisation) + personalisation = personalisation.encode("utf-8") + # mock_redis.get.side_effect = [email, personalisation] service_dict = service_schema.dump(sample_email_template.service) template_dict = template_schema.dump(sample_email_template) @@ -829,6 +875,8 @@ def test_send_email_to_provider_should_return_template_if_found_in_redis( mocker.patch( "app.redis_store.get", side_effect=[ + email, + personalisation, json.dumps({"data": service_dict}).encode("utf-8"), json.dumps({"data": template_dict}).encode("utf-8"), ], From 4ef944d7951b996f4494b0f5410438e894114e6d Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Wed, 24 Jan 2024 10:56:37 -0500 Subject: [PATCH 096/259] 1132 - Capitalize "Pending" in delivery reports --- app/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models.py b/app/models.py index ae2a2d552..077b080a9 100644 --- a/app/models.py +++ b/app/models.py @@ -1763,6 +1763,7 @@ class Notification(db.Model): "temporary-failure": "Unable to find carrier response -- still looking", "permanent-failure": "Unable to find carrier response.", "delivered": "Delivered", + "pending": "Pending", "sending": "Sending", "created": "Sending", "sent": "Sent internationally", From 3157aa2ee1064bbc139681cbf21444002db7550c Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 24 Jan 2024 14:54:15 -0500 Subject: [PATCH 097/259] Update notifications-utils to 0.2.7 This changeset updates the notifications-utils library to 0.2.7. Signed-off-by: Carlo Costino --- poetry.lock | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/poetry.lock b/poetry.lock index e850898f3..2a1c6522d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2006,7 +2006,6 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2016,7 +2015,6 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2026,7 +2024,6 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2052,8 +2049,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2061,7 +2058,6 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2179,16 +2175,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -2587,7 +2573,7 @@ requests = ">=2.0.0" [[package]] name = "notifications-utils" -version = "0.2.6" +version = "0.2.7" description = "" optional = false python-versions = ">=3.9,<3.12" @@ -2639,7 +2625,7 @@ werkzeug = "^3.0.1" type = "git" url = "https://github.com/GSA/notifications-utils.git" reference = "HEAD" -resolved_reference = "f183d120a8b86e655405694adde1f0d95e4e5a51" +resolved_reference = "b6cee72f45dbcd48b59447fa08bbac59e15a7b98" [[package]] name = "numpy" @@ -3446,7 +3432,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, From a790c2f9c7cf9ae739c4b99d93c66247b021288a Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 26 Jan 2024 11:42:30 -0800 Subject: [PATCH 098/259] notify-admin-1158 fix email sending --- app/notifications/process_notifications.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 0b46c8b7c..1ace61df4 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -9,6 +9,7 @@ from notifications_utils.recipients import ( ) from notifications_utils.template import PlainTextEmailTemplate, SMSMessageTemplate +from app import redis_store from app.celery import provider_tasks from app.config import QueueNames from app.dao.notifications_dao import ( @@ -123,7 +124,11 @@ def persist_notification( notification.rate_multiplier = recipient_info.billable_units elif notification_type == EMAIL_TYPE: current_app.logger.info(f"Persisting notification with type: {EMAIL_TYPE}") - notification.normalised_to = format_email_address(notification.to) + redis_store.set( + f"email-address-{notification.id}", + format_email_address(notification.to), + ex=1800, + ) # if simulated create a Notification model to return but do not persist the Notification to the dB if not simulated: From d9ca59710684b49a07764d08746a9bb1d200c411 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:13:57 +0000 Subject: [PATCH 099/259] Bump aiohttp from 3.9.0 to 3.9.2 Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.0 to 3.9.2. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.0...v3.9.2) --- updated-dependencies: - dependency-name: aiohttp dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 171 ++++++++++++++++++++++++++++------------------------ 1 file changed, 93 insertions(+), 78 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2a1c6522d..02fb8b0bb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,87 +2,87 @@ [[package]] name = "aiohttp" -version = "3.9.0" +version = "3.9.2" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6896b8416be9ada4d22cd359d7cb98955576ce863eadad5596b7cdfbf3e17c6c"}, - {file = "aiohttp-3.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1736d87dad8ef46a8ec9cddd349fa9f7bd3a064c47dd6469c0d6763d3d49a4fc"}, - {file = "aiohttp-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c9e5f4d7208cda1a2bb600e29069eecf857e6980d0ccc922ccf9d1372c16f4b"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8488519aa05e636c5997719fe543c8daf19f538f4fa044f3ce94bee608817cff"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ab16c254e2312efeb799bc3c06897f65a133b38b69682bf75d1f1ee1a9c43a9"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a94bde005a8f926d0fa38b88092a03dea4b4875a61fbcd9ac6f4351df1b57cd"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b777c9286b6c6a94f50ddb3a6e730deec327e9e2256cb08b5530db0f7d40fd8"}, - {file = "aiohttp-3.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:571760ad7736b34d05597a1fd38cbc7d47f7b65deb722cb8e86fd827404d1f6b"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:deac0a32aec29608eb25d730f4bc5a261a65b6c48ded1ed861d2a1852577c932"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4ee1b4152bc3190cc40ddd6a14715e3004944263ea208229ab4c297712aa3075"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:3607375053df58ed6f23903aa10cf3112b1240e8c799d243bbad0f7be0666986"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:65b0a70a25456d329a5e1426702dde67be0fb7a4ead718005ba2ca582d023a94"}, - {file = "aiohttp-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a2eb5311a37fe105aa35f62f75a078537e1a9e4e1d78c86ec9893a3c97d7a30"}, - {file = "aiohttp-3.9.0-cp310-cp310-win32.whl", hash = "sha256:2cbc14a13fb6b42d344e4f27746a4b03a2cb0c1c3c5b932b0d6ad8881aa390e3"}, - {file = "aiohttp-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac9669990e2016d644ba8ae4758688534aabde8dbbc81f9af129c3f5f01ca9cd"}, - {file = "aiohttp-3.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f8e05f5163528962ce1d1806fce763ab893b1c5b7ace0a3538cd81a90622f844"}, - {file = "aiohttp-3.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4afa8f71dba3a5a2e1e1282a51cba7341ae76585345c43d8f0e624882b622218"}, - {file = "aiohttp-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f929f4c9b9a00f3e6cc0587abb95ab9c05681f8b14e0fe1daecfa83ea90f8318"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28185e36a78d247c55e9fbea2332d16aefa14c5276a582ce7a896231c6b1c208"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a486ddf57ab98b6d19ad36458b9f09e6022de0381674fe00228ca7b741aacb2f"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70e851f596c00f40a2f00a46126c95c2e04e146015af05a9da3e4867cfc55911"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5b7bf8fe4d39886adc34311a233a2e01bc10eb4e842220235ed1de57541a896"}, - {file = "aiohttp-3.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c67a51ea415192c2e53e4e048c78bab82d21955b4281d297f517707dc836bf3d"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:694df243f394629bcae2d8ed94c589a181e8ba8604159e6e45e7b22e58291113"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3dd8119752dd30dd7bca7d4bc2a92a59be6a003e4e5c2cf7e248b89751b8f4b7"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:eb6dfd52063186ac97b4caa25764cdbcdb4b10d97f5c5f66b0fa95052e744eb7"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d97c3e286d0ac9af6223bc132dc4bad6540b37c8d6c0a15fe1e70fb34f9ec411"}, - {file = "aiohttp-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:816f4db40555026e4cdda604a1088577c1fb957d02f3f1292e0221353403f192"}, - {file = "aiohttp-3.9.0-cp311-cp311-win32.whl", hash = "sha256:3abf0551874fecf95f93b58f25ef4fc9a250669a2257753f38f8f592db85ddea"}, - {file = "aiohttp-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:e18d92c3e9e22553a73e33784fcb0ed484c9874e9a3e96c16a8d6a1e74a0217b"}, - {file = "aiohttp-3.9.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:99ae01fb13a618b9942376df77a1f50c20a281390dad3c56a6ec2942e266220d"}, - {file = "aiohttp-3.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:05857848da443c8c12110d99285d499b4e84d59918a21132e45c3f0804876994"}, - {file = "aiohttp-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:317719d7f824eba55857fe0729363af58e27c066c731bc62cd97bc9c3d9c7ea4"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1e3b3c107ccb0e537f309f719994a55621acd2c8fdf6d5ce5152aed788fb940"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45820ddbb276113ead8d4907a7802adb77548087ff5465d5c554f9aa3928ae7d"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a183f1978802588711aed0dea31e697d760ce9055292db9dc1604daa9a8ded"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a4cd44788ea0b5e6bb8fa704597af3a30be75503a7ed1098bc5b8ffdf6c982"}, - {file = "aiohttp-3.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673343fbc0c1ac44d0d2640addc56e97a052504beacd7ade0dc5e76d3a4c16e8"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e8a3b79b6d186a9c99761fd4a5e8dd575a48d96021f220ac5b5fa856e5dd029"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6777a390e41e78e7c45dab43a4a0196c55c3b8c30eebe017b152939372a83253"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7ae5f99a32c53731c93ac3075abd3e1e5cfbe72fc3eaac4c27c9dd64ba3b19fe"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:f1e4f254e9c35d8965d377e065c4a8a55d396fe87c8e7e8429bcfdeeb229bfb3"}, - {file = "aiohttp-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11ca808f9a6b63485059f5f6e164ef7ec826483c1212a44f268b3653c91237d8"}, - {file = "aiohttp-3.9.0-cp312-cp312-win32.whl", hash = "sha256:de3cc86f4ea8b4c34a6e43a7306c40c1275e52bfa9748d869c6b7d54aa6dad80"}, - {file = "aiohttp-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca4fddf84ac7d8a7d0866664936f93318ff01ee33e32381a115b19fb5a4d1202"}, - {file = "aiohttp-3.9.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f09960b5bb1017d16c0f9e9f7fc42160a5a49fa1e87a175fd4a2b1a1833ea0af"}, - {file = "aiohttp-3.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8303531e2c17b1a494ffaeba48f2da655fe932c4e9a2626c8718403c83e5dd2b"}, - {file = "aiohttp-3.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4790e44f46a4aa07b64504089def5744d3b6780468c4ec3a1a36eb7f2cae9814"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1d7edf74a36de0e5ca50787e83a77cf352f5504eb0ffa3f07000a911ba353fb"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94697c7293199c2a2551e3e3e18438b4cba293e79c6bc2319f5fd652fccb7456"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1b66dbb8a7d5f50e9e2ea3804b01e766308331d0cac76eb30c563ac89c95985"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9623cfd9e85b76b83ef88519d98326d4731f8d71869867e47a0b979ffec61c73"}, - {file = "aiohttp-3.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f32c86dc967ab8c719fd229ce71917caad13cc1e8356ee997bf02c5b368799bf"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f50b4663c3e0262c3a361faf440761fbef60ccdde5fe8545689a4b3a3c149fb4"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dcf71c55ec853826cd70eadb2b6ac62ec577416442ca1e0a97ad875a1b3a0305"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:42fe4fd9f0dfcc7be4248c162d8056f1d51a04c60e53366b0098d1267c4c9da8"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76a86a9989ebf82ee61e06e2bab408aec4ea367dc6da35145c3352b60a112d11"}, - {file = "aiohttp-3.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f9e09a1c83521d770d170b3801eea19b89f41ccaa61d53026ed111cb6f088887"}, - {file = "aiohttp-3.9.0-cp38-cp38-win32.whl", hash = "sha256:a00ce44c21612d185c5275c5cba4bab8d7c1590f248638b667ed8a782fa8cd6f"}, - {file = "aiohttp-3.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5b9345ab92ebe6003ae11d8092ce822a0242146e6fa270889b9ba965457ca40"}, - {file = "aiohttp-3.9.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:98d21092bf2637c5fa724a428a69e8f5955f2182bff61f8036827cf6ce1157bf"}, - {file = "aiohttp-3.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:35a68cd63ca6aaef5707888f17a70c36efe62b099a4e853d33dc2e9872125be8"}, - {file = "aiohttp-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d7f6235c7475658acfc1769d968e07ab585c79f6ca438ddfecaa9a08006aee2"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db04d1de548f7a62d1dd7e7cdf7c22893ee168e22701895067a28a8ed51b3735"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:536b01513d67d10baf6f71c72decdf492fb7433c5f2f133e9a9087379d4b6f31"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c8b0a6487e8109427ccf638580865b54e2e3db4a6e0e11c02639231b41fc0f"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7276fe0017664414fdc3618fca411630405f1aaf0cc3be69def650eb50441787"}, - {file = "aiohttp-3.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23170247ef89ffa842a02bbfdc425028574d9e010611659abeb24d890bc53bb8"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b1a2ea8252cacc7fd51df5a56d7a2bb1986ed39be9397b51a08015727dfb69bd"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d71abc15ff7047412ef26bf812dfc8d0d1020d664617f4913df2df469f26b76"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:2d820162c8c2bdbe97d328cd4f417c955ca370027dce593345e437b2e9ffdc4d"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:2779f5e7c70f7b421915fd47db332c81de365678180a9f3ab404088f87ba5ff9"}, - {file = "aiohttp-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:366bc870d7ac61726f32a489fbe3d1d8876e87506870be66b01aeb84389e967e"}, - {file = "aiohttp-3.9.0-cp39-cp39-win32.whl", hash = "sha256:1df43596b826022b14998f0460926ce261544fedefe0d2f653e1b20f49e96454"}, - {file = "aiohttp-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:9c196b30f1b1aa3363a69dd69079ae9bec96c2965c4707eaa6914ba099fb7d4f"}, - {file = "aiohttp-3.9.0.tar.gz", hash = "sha256:09f23292d29135025e19e8ff4f0a68df078fe4ee013bca0105b2e803989de92d"}, + {file = "aiohttp-3.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:772fbe371788e61c58d6d3d904268e48a594ba866804d08c995ad71b144f94cb"}, + {file = "aiohttp-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:edd4f1af2253f227ae311ab3d403d0c506c9b4410c7fc8d9573dec6d9740369f"}, + {file = "aiohttp-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cfee9287778399fdef6f8a11c9e425e1cb13cc9920fd3a3df8f122500978292b"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc158466f6a980a6095ee55174d1de5730ad7dec251be655d9a6a9dd7ea1ff9"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54ec82f45d57c9a65a1ead3953b51c704f9587440e6682f689da97f3e8defa35"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abeb813a18eb387f0d835ef51f88568540ad0325807a77a6e501fed4610f864e"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc91d07280d7d169f3a0f9179d8babd0ee05c79d4d891447629ff0d7d8089ec2"}, + {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b65e861f4bebfb660f7f0f40fa3eb9f2ab9af10647d05dac824390e7af8f75b7"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04fd8ffd2be73d42bcf55fd78cde7958eeee6d4d8f73c3846b7cba491ecdb570"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3d8d962b439a859b3ded9a1e111a4615357b01620a546bc601f25b0211f2da81"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:8ceb658afd12b27552597cf9a65d9807d58aef45adbb58616cdd5ad4c258c39e"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0e4ee4df741670560b1bc393672035418bf9063718fee05e1796bf867e995fad"}, + {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2dec87a556f300d3211decf018bfd263424f0690fcca00de94a837949fbcea02"}, + {file = "aiohttp-3.9.2-cp310-cp310-win32.whl", hash = "sha256:3e1a800f988ce7c4917f34096f81585a73dbf65b5c39618b37926b1238cf9bc4"}, + {file = "aiohttp-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:ea510718a41b95c236c992b89fdfc3d04cc7ca60281f93aaada497c2b4e05c46"}, + {file = "aiohttp-3.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6aaa6f99256dd1b5756a50891a20f0d252bd7bdb0854c5d440edab4495c9f973"}, + {file = "aiohttp-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a27d8c70ad87bcfce2e97488652075a9bdd5b70093f50b10ae051dfe5e6baf37"}, + {file = "aiohttp-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54287bcb74d21715ac8382e9de146d9442b5f133d9babb7e5d9e453faadd005e"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb3d05569aa83011fcb346b5266e00b04180105fcacc63743fc2e4a1862a891"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8534e7d69bb8e8d134fe2be9890d1b863518582f30c9874ed7ed12e48abe3c4"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bd9d5b989d57b41e4ff56ab250c5ddf259f32db17159cce630fd543376bd96b"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa6904088e6642609981f919ba775838ebf7df7fe64998b1a954fb411ffb4663"}, + {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda42eb410be91b349fb4ee3a23a30ee301c391e503996a638d05659d76ea4c2"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:193cc1ccd69d819562cc7f345c815a6fc51d223b2ef22f23c1a0f67a88de9a72"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b9f1cb839b621f84a5b006848e336cf1496688059d2408e617af33e3470ba204"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:d22a0931848b8c7a023c695fa2057c6aaac19085f257d48baa24455e67df97ec"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4112d8ba61fbd0abd5d43a9cb312214565b446d926e282a6d7da3f5a5aa71d36"}, + {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c4ad4241b52bb2eb7a4d2bde060d31c2b255b8c6597dd8deac2f039168d14fd7"}, + {file = "aiohttp-3.9.2-cp311-cp311-win32.whl", hash = "sha256:ee2661a3f5b529f4fc8a8ffee9f736ae054adfb353a0d2f78218be90617194b3"}, + {file = "aiohttp-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:4deae2c165a5db1ed97df2868ef31ca3cc999988812e82386d22937d9d6fed52"}, + {file = "aiohttp-3.9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6f4cdba12539215aaecf3c310ce9d067b0081a0795dd8a8805fdb67a65c0572a"}, + {file = "aiohttp-3.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:84e843b33d5460a5c501c05539809ff3aee07436296ff9fbc4d327e32aa3a326"}, + {file = "aiohttp-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8008d0f451d66140a5aa1c17e3eedc9d56e14207568cd42072c9d6b92bf19b52"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61c47ab8ef629793c086378b1df93d18438612d3ed60dca76c3422f4fbafa792"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc71f748e12284312f140eaa6599a520389273174b42c345d13c7e07792f4f57"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1c3a4d0ab2f75f22ec80bca62385db2e8810ee12efa8c9e92efea45c1849133"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a87aa0b13bbee025faa59fa58861303c2b064b9855d4c0e45ec70182bbeba1b"}, + {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2cc0d04688b9f4a7854c56c18aa7af9e5b0a87a28f934e2e596ba7e14783192"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1956e3ac376b1711c1533266dec4efd485f821d84c13ce1217d53e42c9e65f08"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:114da29f39eccd71b93a0fcacff178749a5c3559009b4a4498c2c173a6d74dff"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3f17999ae3927d8a9a823a1283b201344a0627272f92d4f3e3a4efe276972fe8"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:f31df6a32217a34ae2f813b152a6f348154f948c83213b690e59d9e84020925c"}, + {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7a75307ffe31329928a8d47eae0692192327c599113d41b278d4c12b54e1bd11"}, + {file = "aiohttp-3.9.2-cp312-cp312-win32.whl", hash = "sha256:972b63d589ff8f305463593050a31b5ce91638918da38139b9d8deaba9e0fed7"}, + {file = "aiohttp-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:200dc0246f0cb5405c80d18ac905c8350179c063ea1587580e3335bfc243ba6a"}, + {file = "aiohttp-3.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:158564d0d1020e0d3fe919a81d97aadad35171e13e7b425b244ad4337fc6793a"}, + {file = "aiohttp-3.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:da1346cd0ccb395f0ed16b113ebb626fa43b7b07fd7344fce33e7a4f04a8897a"}, + {file = "aiohttp-3.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:eaa9256de26ea0334ffa25f1913ae15a51e35c529a1ed9af8e6286dd44312554"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1543e7fb00214fb4ccead42e6a7d86f3bb7c34751ec7c605cca7388e525fd0b4"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:186e94570433a004e05f31f632726ae0f2c9dee4762a9ce915769ce9c0a23d89"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d52d20832ac1560f4510d68e7ba8befbc801a2b77df12bd0cd2bcf3b049e52a4"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c45e4e815ac6af3b72ca2bde9b608d2571737bb1e2d42299fc1ffdf60f6f9a1"}, + {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa906b9bdfd4a7972dd0628dbbd6413d2062df5b431194486a78f0d2ae87bd55"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:68bbee9e17d66f17bb0010aa15a22c6eb28583edcc8b3212e2b8e3f77f3ebe2a"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4c189b64bd6d9a403a1a3f86a3ab3acbc3dc41a68f73a268a4f683f89a4dec1f"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8a7876f794523123bca6d44bfecd89c9fec9ec897a25f3dd202ee7fc5c6525b7"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d23fba734e3dd7b1d679b9473129cd52e4ec0e65a4512b488981a56420e708db"}, + {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b141753be581fab842a25cb319f79536d19c2a51995d7d8b29ee290169868eab"}, + {file = "aiohttp-3.9.2-cp38-cp38-win32.whl", hash = "sha256:103daf41ff3b53ba6fa09ad410793e2e76c9d0269151812e5aba4b9dd674a7e8"}, + {file = "aiohttp-3.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:328918a6c2835861ff7afa8c6d2c70c35fdaf996205d5932351bdd952f33fa2f"}, + {file = "aiohttp-3.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5264d7327c9464786f74e4ec9342afbbb6ee70dfbb2ec9e3dfce7a54c8043aa3"}, + {file = "aiohttp-3.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07205ae0015e05c78b3288c1517afa000823a678a41594b3fdc870878d645305"}, + {file = "aiohttp-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0a1e638cffc3ec4d4784b8b4fd1cf28968febc4bd2718ffa25b99b96a741bd"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d43302a30ba1166325974858e6ef31727a23bdd12db40e725bec0f759abce505"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16a967685907003765855999af11a79b24e70b34dc710f77a38d21cd9fc4f5fe"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fa3ee92cd441d5c2d07ca88d7a9cef50f7ec975f0117cd0c62018022a184308"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b500c5ad9c07639d48615a770f49618130e61be36608fc9bc2d9bae31732b8f"}, + {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c07327b368745b1ce2393ae9e1aafed7073d9199e1dcba14e035cc646c7941bf"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cc7d6502c23a0ec109687bf31909b3fb7b196faf198f8cff68c81b49eb316ea9"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:07be2be7071723c3509ab5c08108d3a74f2181d4964e869f2504aaab68f8d3e8"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:122468f6fee5fcbe67cb07014a08c195b3d4c41ff71e7b5160a7bcc41d585a5f"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:00a9abcea793c81e7f8778ca195a1714a64f6d7436c4c0bb168ad2a212627000"}, + {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a9825fdd64ecac5c670234d80bb52bdcaa4139d1f839165f548208b3779c6c6"}, + {file = "aiohttp-3.9.2-cp39-cp39-win32.whl", hash = "sha256:5422cd9a4a00f24c7244e1b15aa9b87935c85fb6a00c8ac9b2527b38627a9211"}, + {file = "aiohttp-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:7d579dcd5d82a86a46f725458418458fa43686f6a7b252f2966d359033ffc8ab"}, + {file = "aiohttp-3.9.2.tar.gz", hash = "sha256:b0ad0a5e86ce73f5368a164c10ada10504bf91869c05ab75d982c6048217fbf7"}, ] [package.dependencies] @@ -2006,6 +2006,7 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2015,6 +2016,7 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2024,6 +2026,7 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2049,8 +2052,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2058,6 +2061,7 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2175,6 +2179,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -3432,6 +3446,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, From 8c03d142370907d2e88807717be20f0c9eb919f7 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 30 Jan 2024 09:25:05 -0500 Subject: [PATCH 100/259] Update Cloud Foundry Terraform module This changeset updates the Cloud Foundry Terraform to its latest release. Our file were previously referencing a very old version, which was contributing to infrastructure check and deployment failures. Signed-off-by: Carlo Costino --- terraform/bootstrap/providers.tf | 2 +- terraform/demo/providers.tf | 2 +- terraform/development/providers.tf | 2 +- terraform/production/providers.tf | 2 +- terraform/sandbox/providers.tf | 2 +- terraform/shared/egress_space/providers.tf | 2 +- terraform/shared/ses/providers.tf | 2 +- terraform/shared/sns/providers.tf | 2 +- terraform/staging/providers.tf | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/terraform/bootstrap/providers.tf b/terraform/bootstrap/providers.tf index a2d023da3..5dcaece3e 100644 --- a/terraform/bootstrap/providers.tf +++ b/terraform/bootstrap/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.15.5" + version = "0.53.0" } } } diff --git a/terraform/demo/providers.tf b/terraform/demo/providers.tf index 180959761..5c10e8064 100644 --- a/terraform/demo/providers.tf +++ b/terraform/demo/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.15.5" + version = "0.53.0" } } diff --git a/terraform/development/providers.tf b/terraform/development/providers.tf index d8ae4488e..5dcaece3e 100644 --- a/terraform/development/providers.tf +++ b/terraform/development/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.50.7" + version = "0.53.0" } } } diff --git a/terraform/production/providers.tf b/terraform/production/providers.tf index 360f648f4..e2a8431c6 100644 --- a/terraform/production/providers.tf +++ b/terraform/production/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.15.5" + version = "0.53.0" } } diff --git a/terraform/sandbox/providers.tf b/terraform/sandbox/providers.tf index 00ea2888d..3d1e18222 100644 --- a/terraform/sandbox/providers.tf +++ b/terraform/sandbox/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.15.5" + version = "0.53.0" } } diff --git a/terraform/shared/egress_space/providers.tf b/terraform/shared/egress_space/providers.tf index 8db86ca90..21ac567a2 100644 --- a/terraform/shared/egress_space/providers.tf +++ b/terraform/shared/egress_space/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "~> 0.15" + version = "0.53.0" } } } diff --git a/terraform/shared/ses/providers.tf b/terraform/shared/ses/providers.tf index 8db86ca90..21ac567a2 100644 --- a/terraform/shared/ses/providers.tf +++ b/terraform/shared/ses/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "~> 0.15" + version = "0.53.0" } } } diff --git a/terraform/shared/sns/providers.tf b/terraform/shared/sns/providers.tf index 8db86ca90..21ac567a2 100644 --- a/terraform/shared/sns/providers.tf +++ b/terraform/shared/sns/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "~> 0.15" + version = "0.53.0" } } } diff --git a/terraform/staging/providers.tf b/terraform/staging/providers.tf index 181b5fd69..7ceec6593 100644 --- a/terraform/staging/providers.tf +++ b/terraform/staging/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.15.5" + version = "0.53.0" } } From 033f43931364fcb2763550737345e4bd78d3bc9d Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 30 Jan 2024 09:39:54 -0500 Subject: [PATCH 101/259] Updated 18F/terraform-cloudgov to v0.7.1 Signed-off-by: Carlo Costino --- terraform/bootstrap/main.tf | 2 +- terraform/demo/main.tf | 6 +++--- terraform/development/main.tf | 2 +- terraform/production/main.tf | 8 ++++---- terraform/sandbox/main.tf | 6 +++--- terraform/staging/main.tf | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/terraform/bootstrap/main.tf b/terraform/bootstrap/main.tf index 5f30791c6..625cb8093 100644 --- a/terraform/bootstrap/main.tf +++ b/terraform/bootstrap/main.tf @@ -3,7 +3,7 @@ locals { } module "s3" { - source = "github.com/18f/terraform-cloudgov//s3?ref=v0.3.0" + source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" cf_org_name = "gsa-tts-benefits-studio" cf_space_name = "notify-management" diff --git a/terraform/demo/main.tf b/terraform/demo/main.tf index b1224bb4e..615f92670 100644 --- a/terraform/demo/main.tf +++ b/terraform/demo/main.tf @@ -7,7 +7,7 @@ locals { } module "database" { - source = "github.com/18f/terraform-cloudgov//database?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -17,7 +17,7 @@ module "database" { } module "redis" { - source = "github.com/18f/terraform-cloudgov//redis?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -27,7 +27,7 @@ module "redis" { } module "csv_upload_bucket" { - source = "github.com/18f/terraform-cloudgov//s3?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name diff --git a/terraform/development/main.tf b/terraform/development/main.tf index 4dd7d741c..1f45b2b6a 100644 --- a/terraform/development/main.tf +++ b/terraform/development/main.tf @@ -6,7 +6,7 @@ locals { } module "csv_upload_bucket" { - source = "github.com/18f/terraform-cloudgov//s3?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name diff --git a/terraform/production/main.tf b/terraform/production/main.tf index 7476338d2..5a2c520b1 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -7,7 +7,7 @@ locals { } module "database" { - source = "github.com/18f/terraform-cloudgov//database?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -17,7 +17,7 @@ module "database" { } module "redis" { - source = "github.com/18f/terraform-cloudgov//redis?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -27,7 +27,7 @@ module "redis" { } module "csv_upload_bucket" { - source = "github.com/18f/terraform-cloudgov//s3?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -78,7 +78,7 @@ module "sns_sms" { # `cf create-domain gsa-tts-benefits-studio api.notify.gov` ########################################################################### # module "domain" { -# source = "github.com/18f/terraform-cloudgov//domain?ref=v0.2.0" +# source = "github.com/18f/terraform-cloudgov//domain?ref=v0.7.1" # # cf_org_name = local.cf_org_name # cf_space_name = local.cf_space_name diff --git a/terraform/sandbox/main.tf b/terraform/sandbox/main.tf index 4076d3d53..fae30073c 100644 --- a/terraform/sandbox/main.tf +++ b/terraform/sandbox/main.tf @@ -7,7 +7,7 @@ locals { } module "database" { - source = "github.com/18f/terraform-cloudgov//database?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -17,7 +17,7 @@ module "database" { } module "redis" { - source = "github.com/18f/terraform-cloudgov//redis?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -27,7 +27,7 @@ module "redis" { } module "csv_upload_bucket" { - source = "github.com/18f/terraform-cloudgov//s3?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name diff --git a/terraform/staging/main.tf b/terraform/staging/main.tf index 414c18bc9..c46e0d3fa 100644 --- a/terraform/staging/main.tf +++ b/terraform/staging/main.tf @@ -7,7 +7,7 @@ locals { } module "database" { - source = "github.com/18f/terraform-cloudgov//database?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -17,7 +17,7 @@ module "database" { } module "redis" { - source = "github.com/18f/terraform-cloudgov//redis?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name @@ -27,7 +27,7 @@ module "redis" { } module "csv_upload_bucket" { - source = "github.com/18f/terraform-cloudgov//s3?ref=v0.2.0" + source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name From b63130c1bad4d64fe70da9ee392abf1a0895ef74 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 30 Jan 2024 10:35:27 -0500 Subject: [PATCH 102/259] Remove profile attribute from environment providers Signed-off-by: Carlo Costino --- terraform/demo/providers.tf | 2 +- terraform/development/reset.sh | 2 +- terraform/production/providers.tf | 2 +- terraform/sandbox/providers.tf | 2 +- terraform/staging/providers.tf | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/terraform/demo/providers.tf b/terraform/demo/providers.tf index 5c10e8064..44aad9281 100644 --- a/terraform/demo/providers.tf +++ b/terraform/demo/providers.tf @@ -12,7 +12,7 @@ terraform { key = "api.tfstate.demo" encrypt = "true" region = "us-gov-west-1" - profile = "notify-terraform-backend" + # profile = "notify-terraform-backend" } } diff --git a/terraform/development/reset.sh b/terraform/development/reset.sh index fba5f104b..ec5cf25a5 100755 --- a/terraform/development/reset.sh +++ b/terraform/development/reset.sh @@ -51,7 +51,7 @@ if [[ ! -s "secrets.auto.tfvars" ]]; then fi echo "Importing terraform state for $username" -terraform init +terraform init -upgrade key_name=$username-api-dev-key diff --git a/terraform/production/providers.tf b/terraform/production/providers.tf index e2a8431c6..4bed2d44f 100644 --- a/terraform/production/providers.tf +++ b/terraform/production/providers.tf @@ -12,7 +12,7 @@ terraform { key = "api.tfstate.prod" encrypt = "true" region = "us-gov-west-1" - profile = "notify-terraform-backend" + # profile = "notify-terraform-backend" } } diff --git a/terraform/sandbox/providers.tf b/terraform/sandbox/providers.tf index 3d1e18222..3b8234dfe 100644 --- a/terraform/sandbox/providers.tf +++ b/terraform/sandbox/providers.tf @@ -12,7 +12,7 @@ terraform { key = "api.tfstate.sandbox" encrypt = "true" region = "us-gov-west-1" - profile = "notify-terraform-backend" + # profile = "notify-terraform-backend" } } diff --git a/terraform/staging/providers.tf b/terraform/staging/providers.tf index 7ceec6593..808e06dc0 100644 --- a/terraform/staging/providers.tf +++ b/terraform/staging/providers.tf @@ -12,7 +12,7 @@ terraform { key = "api.tfstate.stage" encrypt = "true" region = "us-gov-west-1" - profile = "notify-terraform-backend" + # profile = "notify-terraform-backend" } } From 123b19d83578228be77b8b5ecdc0ccaeb69bdf13 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 30 Jan 2024 10:47:16 -0500 Subject: [PATCH 103/259] Remove the profile attribute entirely This was what was apparently breaking our Terraform actions. I traced this to an undocumented breaking change with the AWS provider; more details can be seen here: https://discuss.hashicorp.com/t/error-error-configuring-terraform-aws-provider-failed-to-get-shared-config-profile-default/39417/2 Signed-off-by: Carlo Costino --- terraform/README.md | 2 ++ terraform/demo/providers.tf | 1 - terraform/production/providers.tf | 1 - terraform/sandbox/providers.tf | 1 - terraform/staging/providers.tf | 1 - 5 files changed, 2 insertions(+), 4 deletions(-) diff --git a/terraform/README.md b/terraform/README.md index a812b2208..40ab78a19 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -88,6 +88,8 @@ The below steps rely on you first configuring access to the Terraform state in s terraform plan ``` + If the `terraform init` command fails, you may need to run `terraform init -upgrade` to make sure new module versions are picked up. + 1. Apply changes with `terraform apply`. 1. Remove the space deployer service instance if it doesn't need to be used again, such as when manually running terraform once. diff --git a/terraform/demo/providers.tf b/terraform/demo/providers.tf index 44aad9281..f13333d3e 100644 --- a/terraform/demo/providers.tf +++ b/terraform/demo/providers.tf @@ -12,7 +12,6 @@ terraform { key = "api.tfstate.demo" encrypt = "true" region = "us-gov-west-1" - # profile = "notify-terraform-backend" } } diff --git a/terraform/production/providers.tf b/terraform/production/providers.tf index 4bed2d44f..499759f48 100644 --- a/terraform/production/providers.tf +++ b/terraform/production/providers.tf @@ -12,7 +12,6 @@ terraform { key = "api.tfstate.prod" encrypt = "true" region = "us-gov-west-1" - # profile = "notify-terraform-backend" } } diff --git a/terraform/sandbox/providers.tf b/terraform/sandbox/providers.tf index 3b8234dfe..d5a3313de 100644 --- a/terraform/sandbox/providers.tf +++ b/terraform/sandbox/providers.tf @@ -12,7 +12,6 @@ terraform { key = "api.tfstate.sandbox" encrypt = "true" region = "us-gov-west-1" - # profile = "notify-terraform-backend" } } diff --git a/terraform/staging/providers.tf b/terraform/staging/providers.tf index 808e06dc0..11dceea7d 100644 --- a/terraform/staging/providers.tf +++ b/terraform/staging/providers.tf @@ -12,7 +12,6 @@ terraform { key = "api.tfstate.stage" encrypt = "true" region = "us-gov-west-1" - # profile = "notify-terraform-backend" } } From 363ecb33d51943fa581c83fa8f1619b94991a58e Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 31 Jan 2024 07:25:11 -0800 Subject: [PATCH 104/259] add debug --- app/aws/s3.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/aws/s3.py b/app/aws/s3.py index f942feefb..7e3e4585a 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -102,6 +102,7 @@ def extract_phones(job): first_row = job[0] job.pop(0) first_row = first_row.split(",") + current_app.logger.info(f"HEADERS {first_row}") phone_index = 0 for item in first_row: if item.lower() == "phone number": @@ -111,11 +112,14 @@ def extract_phones(job): job_row = 0 for row in job: row = row.split(",") + # TODO WHY ARE WE CALCULATING PHONE INDEX IN THE LOOP? phone_index = 0 for item in first_row: if item.lower() == "phone number": break phone_index = phone_index + 1 + current_app.logger.info(f"PHONE INDEX IS NOW {phone_index}") + current_app.logger.info(f"LENGTH OF ROW IS {len(row)}") my_phone = row[phone_index] my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) phones[job_row] = my_phone From 277262ed69fdf211a166b054224ab1421db2c584 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 31 Jan 2024 08:06:09 -0800 Subject: [PATCH 105/259] handle error --- app/aws/s3.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 7e3e4585a..7789d80ed 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -120,9 +120,13 @@ def extract_phones(job): phone_index = phone_index + 1 current_app.logger.info(f"PHONE INDEX IS NOW {phone_index}") current_app.logger.info(f"LENGTH OF ROW IS {len(row)}") - my_phone = row[phone_index] - my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) - phones[job_row] = my_phone + if phone_index <= len(row): + phones[job_row] = "Error: can't retrieve phone number" + current_app.logger.error("Corrupt csv file, missing columns job_id {job_id} service_id {service_id}") + else: + my_phone = row[phone_index] + my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) + phones[job_row] = my_phone job_row = job_row + 1 return phones From fa2b18d5b7b6e6f8e847385b09276d3bce6e982c Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 31 Jan 2024 08:10:48 -0800 Subject: [PATCH 106/259] fix logic --- app/aws/s3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 7789d80ed..0419cada0 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -120,7 +120,7 @@ def extract_phones(job): phone_index = phone_index + 1 current_app.logger.info(f"PHONE INDEX IS NOW {phone_index}") current_app.logger.info(f"LENGTH OF ROW IS {len(row)}") - if phone_index <= len(row): + if phone_index >= len(row): phones[job_row] = "Error: can't retrieve phone number" current_app.logger.error("Corrupt csv file, missing columns job_id {job_id} service_id {service_id}") else: From aecff08d7ae1169be60a59dae1f5b618f1cfc2e3 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 31 Jan 2024 08:20:12 -0800 Subject: [PATCH 107/259] remove cache hit/miss writes to log --- app/aws/s3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 0419cada0..e1c14e1c6 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -84,7 +84,7 @@ def incr_jobs_cache_misses(): redis_store.incr(JOBS_CACHE_MISSES) hits = redis_store.get(JOBS_CACHE_HITS).decode("utf-8") misses = redis_store.get(JOBS_CACHE_MISSES).decode("utf-8") - current_app.logger.info(f"JOBS CACHE MISS hits {hits} misses {misses}") + current_app.logger.debug(f"JOBS CACHE MISS hits {hits} misses {misses}") def incr_jobs_cache_hits(): @@ -94,7 +94,7 @@ def incr_jobs_cache_hits(): redis_store.incr(JOBS_CACHE_HITS) hits = redis_store.get(JOBS_CACHE_HITS).decode("utf-8") misses = redis_store.get(JOBS_CACHE_MISSES).decode("utf-8") - current_app.logger.info(f"JOBS CACHE HIT hits {hits} misses {misses}") + current_app.logger.debug(f"JOBS CACHE HIT hits {hits} misses {misses}") def extract_phones(job): From ab71ccde683093d5a3071363b212646bc0a7dcbe Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 31 Jan 2024 08:53:54 -0800 Subject: [PATCH 108/259] fix fstrings --- app/aws/s3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index e1c14e1c6..13879bf6f 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -150,7 +150,7 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): # change the task schedules if job is None: current_app.logger.warning( - "Couldnt find phone for job_id {job_id} row number {job_row_number} because job is missing" + f"Couldnt find phone for job_id {job_id} row number {job_row_number} because job is missing" ) return "Unknown Phone" From a028be238b37cbe2f6f7d323ab8bc6e4e4898177 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 1 Feb 2024 10:48:59 -0800 Subject: [PATCH 109/259] notify-api-784 remove simulated numbers --- app/aws/s3.py | 4 +++- app/config.py | 3 ++- tests/app/notifications/test_process_notification.py | 5 ++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 13879bf6f..7ddf9828e 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -122,7 +122,9 @@ def extract_phones(job): current_app.logger.info(f"LENGTH OF ROW IS {len(row)}") if phone_index >= len(row): phones[job_row] = "Error: can't retrieve phone number" - current_app.logger.error("Corrupt csv file, missing columns job_id {job_id} service_id {service_id}") + current_app.logger.error( + "Corrupt csv file, missing columns job_id {job_id} service_id {service_id}" + ) else: my_phone = row[phone_index] my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) diff --git a/app/config.py b/app/config.py index 49cbd89c0..b4a5b8aed 100644 --- a/app/config.py +++ b/app/config.py @@ -283,7 +283,8 @@ class Config(object): "simulate-delivered-2@notifications.service.gov.uk", "simulate-delivered-3@notifications.service.gov.uk", ) - SIMULATED_SMS_NUMBERS = ("+12028675000", "+12028675111", "+12028675222") + # 7755 is success, 7167 is failure + SIMULATED_SMS_NUMBERS = ("+14254147755", "+14254147167") FREE_SMS_TIER_FRAGMENT_COUNT = 250000 diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index 87c8da988..bbde98da2 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -254,9 +254,8 @@ def test_send_notification_to_queue_throws_exception_deletes_notification( @pytest.mark.parametrize( "to_address, notification_type, expected", [ - ("+12028675000", "sms", True), - ("+12028675111", "sms", True), - ("+12028675222", "sms", True), + ("+14254147755", "sms", True), + ("+14254147167", "sms", True), ("2028675000", "sms", True), ("2028675111", "sms", True), ("simulate-delivered@notifications.service.gov.uk", "email", True), From 82dd29d457dda520c8efd0d3fe17d7e1f3c924c0 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 1 Feb 2024 12:01:29 -0800 Subject: [PATCH 110/259] handle bom in phone number field --- app/aws/s3.py | 12 ++++-------- tests/app/aws/test_s3.py | 7 +++++++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 13879bf6f..ca283d743 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -105,24 +105,20 @@ def extract_phones(job): current_app.logger.info(f"HEADERS {first_row}") phone_index = 0 for item in first_row: - if item.lower() == "phone number": + # Note: may contain a BOM and look like \ufeffphone number + if "phone number" in item.lower(): break phone_index = phone_index + 1 + phones = {} job_row = 0 for row in job: row = row.split(",") - # TODO WHY ARE WE CALCULATING PHONE INDEX IN THE LOOP? - phone_index = 0 - for item in first_row: - if item.lower() == "phone number": - break - phone_index = phone_index + 1 current_app.logger.info(f"PHONE INDEX IS NOW {phone_index}") current_app.logger.info(f"LENGTH OF ROW IS {len(row)}") if phone_index >= len(row): phones[job_row] = "Error: can't retrieve phone number" - current_app.logger.error("Corrupt csv file, missing columns job_id {job_id} service_id {service_id}") + current_app.logger.error("Corrupt csv file") else: my_phone = row[phone_index] my_phone = re.sub(r"[\+\s\(\)\-\.]*", "", my_phone) diff --git a/tests/app/aws/test_s3.py b/tests/app/aws/test_s3.py index ad01a00c5..a5134501c 100644 --- a/tests/app/aws/test_s3.py +++ b/tests/app/aws/test_s3.py @@ -67,6 +67,13 @@ def test_get_s3_file_makes_correct_call(notify_api, mocker): 0, "15553333333", ), + ( + # simulate file saved with utf8withbom + "\\ufeffPHONE NUMBER,Name\r\n5555555550,T 1\r\n5555555551,T 5,3/31/2024\r\n5555555552,T 2", + "eee", + 2, + "5555555552", + ), ], ) def test_get_phone_number_from_s3( From c45e74a74bb593386e21aa602f748c45a03031b7 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 2 Feb 2024 09:03:54 -0800 Subject: [PATCH 111/259] update sent_at --- app/dao/notifications_dao.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index 23905944c..ededaec04 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -100,6 +100,7 @@ def _update_notification_status( current_status=notification.status, status=status ) notification.status = status + notification.sent_at = datetime.utcnow() if provider_response: notification.provider_response = provider_response if carrier: @@ -325,6 +326,7 @@ def sanitize_successful_notification_by_id(notification_id, carrier, provider_re "notification_id": notification_id, "carrier": carrier, "response": provider_response, + "sent_at": datetime.utcnow(), } db.session.execute(update_query, input_params) From fad3d4f473ea09a26ee510ea6841f3c6b3cb858b Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 2 Feb 2024 09:32:24 -0800 Subject: [PATCH 112/259] fix tests --- tests/app/notifications/test_process_notification.py | 4 +--- .../app/service/send_notification/test_send_notification.py | 2 +- tests/app/v2/notifications/test_post_notifications.py | 5 ++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index bbde98da2..2e302476a 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -256,8 +256,6 @@ def test_send_notification_to_queue_throws_exception_deletes_notification( [ ("+14254147755", "sms", True), ("+14254147167", "sms", True), - ("2028675000", "sms", True), - ("2028675111", "sms", True), ("simulate-delivered@notifications.service.gov.uk", "email", True), ("simulate-delivered-2@notifications.service.gov.uk", "email", True), ("simulate-delivered-3@notifications.service.gov.uk", "email", True), @@ -274,7 +272,7 @@ def test_simulated_recipient(notify_api, to_address, notification_type, expected 'simulate-delivered-2@notifications.service.gov.uk', 'simulate-delivered-2@notifications.service.gov.uk' ) - SIMULATED_SMS_NUMBERS = ('+12028675000', '+12028675111', '+12028675222') + SIMULATED_SMS_NUMBERS = ("+14254147755", "+14254147167") """ formatted_address = None diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index c65614e22..3ffbb8e2e 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -881,7 +881,7 @@ def test_should_not_persist_notification_or_send_email_if_simulated_email( assert Notification.query.count() == 0 -@pytest.mark.parametrize("to_sms", ["2028675000", "2028675111", "+12028675222"]) +@pytest.mark.parametrize("to_sms", ["+14254147755", "+14254147167"]) def test_should_not_persist_notification_or_send_sms_if_simulated_number( client, to_sms, sample_template, mocker ): diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index f461531bc..bb9a58453 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -532,9 +532,8 @@ def test_post_email_notification_returns_201( ("simulate-delivered@notifications.service.gov.uk", EMAIL_TYPE), ("simulate-delivered-2@notifications.service.gov.uk", EMAIL_TYPE), ("simulate-delivered-3@notifications.service.gov.uk", EMAIL_TYPE), - ("2028675000", "sms"), - ("2028675111", "sms"), - ("2028675222", "sms"), + ("+14254147167", "sms"), + ("+14254147755", "sms"), ], ) def test_should_not_persist_or_send_notification_if_simulated_recipient( From 65bc37d20322ffb9113286772e2805de76c0be4f Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 2 Feb 2024 10:29:01 -0800 Subject: [PATCH 113/259] fix fstring --- app/aws/s3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 7ddf9828e..5921141ca 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -123,7 +123,7 @@ def extract_phones(job): if phone_index >= len(row): phones[job_row] = "Error: can't retrieve phone number" current_app.logger.error( - "Corrupt csv file, missing columns job_id {job_id} service_id {service_id}" + "Corrupt csv file, missing columns or possibly a byte order mark in the file" ) else: my_phone = row[phone_index] From ddad4e409eba37d464951c3edf2fcd39e80e8d1c Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 2 Feb 2024 12:51:11 -0800 Subject: [PATCH 114/259] update send_at when status resolves --- app/dao/notifications_dao.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index ededaec04..ee08f9af0 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -319,9 +319,10 @@ def _filter_query(query, filter_dict=None): def sanitize_successful_notification_by_id(notification_id, carrier, provider_response): update_query = """ update notifications set provider_response=:response, carrier=:carrier, - notification_status='delivered', "to"='1', normalised_to='1' + notification_status='delivered', sent_at=:sent_at, "to"='1', normalised_to='1' where id=:notification_id """ + input_params = { "notification_id": notification_id, "carrier": carrier, From 7ff858563e26e6b745a7d907b7f87f9a406aaca3 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 6 Feb 2024 16:32:31 -0500 Subject: [PATCH 115/259] Update utils to 0.2.8 This changeset updates notifications-utils to 0.2.8 to address a cryptography dependency update and Dependabot security alert. Signed-off-by: Carlo Costino --- poetry.lock | 88 +++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/poetry.lock b/poetry.lock index 02fb8b0bb..2d1d30bb1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -940,47 +940,56 @@ files = [ [[package]] name = "cryptography" -version = "41.0.6" +version = "42.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-41.0.6-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:0f27acb55a4e77b9be8d550d762b0513ef3fc658cd3eb15110ebbcbd626db12c"}, - {file = "cryptography-41.0.6-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ae236bb8760c1e55b7a39b6d4d32d2279bc6c7c8500b7d5a13b6fb9fc97be35b"}, - {file = "cryptography-41.0.6-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afda76d84b053923c27ede5edc1ed7d53e3c9f475ebaf63c68e69f1403c405a8"}, - {file = "cryptography-41.0.6-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da46e2b5df770070412c46f87bac0849b8d685c5f2679771de277a422c7d0b86"}, - {file = "cryptography-41.0.6-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ff369dd19e8fe0528b02e8df9f2aeb2479f89b1270d90f96a63500afe9af5cae"}, - {file = "cryptography-41.0.6-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b648fe2a45e426aaee684ddca2632f62ec4613ef362f4d681a9a6283d10e079d"}, - {file = "cryptography-41.0.6-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5daeb18e7886a358064a68dbcaf441c036cbdb7da52ae744e7b9207b04d3908c"}, - {file = "cryptography-41.0.6-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:068bc551698c234742c40049e46840843f3d98ad7ce265fd2bd4ec0d11306596"}, - {file = "cryptography-41.0.6-cp37-abi3-win32.whl", hash = "sha256:2132d5865eea673fe6712c2ed5fb4fa49dba10768bb4cc798345748380ee3660"}, - {file = "cryptography-41.0.6-cp37-abi3-win_amd64.whl", hash = "sha256:48783b7e2bef51224020efb61b42704207dde583d7e371ef8fc2a5fb6c0aabc7"}, - {file = "cryptography-41.0.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8efb2af8d4ba9dbc9c9dd8f04d19a7abb5b49eab1f3694e7b5a16a5fc2856f5c"}, - {file = "cryptography-41.0.6-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c5a550dc7a3b50b116323e3d376241829fd326ac47bc195e04eb33a8170902a9"}, - {file = "cryptography-41.0.6-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:85abd057699b98fce40b41737afb234fef05c67e116f6f3650782c10862c43da"}, - {file = "cryptography-41.0.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f39812f70fc5c71a15aa3c97b2bbe213c3f2a460b79bd21c40d033bb34a9bf36"}, - {file = "cryptography-41.0.6-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:742ae5e9a2310e9dade7932f9576606836ed174da3c7d26bc3d3ab4bd49b9f65"}, - {file = "cryptography-41.0.6-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:35f3f288e83c3f6f10752467c48919a7a94b7d88cc00b0668372a0d2ad4f8ead"}, - {file = "cryptography-41.0.6-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4d03186af98b1c01a4eda396b137f29e4e3fb0173e30f885e27acec8823c1b09"}, - {file = "cryptography-41.0.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b27a7fd4229abef715e064269d98a7e2909ebf92eb6912a9603c7e14c181928c"}, - {file = "cryptography-41.0.6-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:398ae1fc711b5eb78e977daa3cbf47cec20f2c08c5da129b7a296055fbb22aed"}, - {file = "cryptography-41.0.6-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7e00fb556bda398b99b0da289ce7053639d33b572847181d6483ad89835115f6"}, - {file = "cryptography-41.0.6-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:60e746b11b937911dc70d164060d28d273e31853bb359e2b2033c9e93e6f3c43"}, - {file = "cryptography-41.0.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3288acccef021e3c3c10d58933f44e8602cf04dba96d9796d70d537bb2f4bbc4"}, - {file = "cryptography-41.0.6.tar.gz", hash = "sha256:422e3e31d63743855e43e5a6fcc8b4acab860f560f9321b0ee6269cc7ed70cc3"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, + {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, + {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, + {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, + {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, + {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, + {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, ] [package.dependencies] -cffi = ">=1.12" +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] nox = ["nox"] -pep8test = ["black", "check-sdist", "mypy", "ruff"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] sdist = ["build"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] [[package]] @@ -2006,7 +2015,6 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2016,7 +2024,6 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2026,7 +2033,6 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2052,8 +2058,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2061,7 +2067,6 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2179,16 +2184,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -2587,7 +2582,7 @@ requests = ">=2.0.0" [[package]] name = "notifications-utils" -version = "0.2.7" +version = "0.2.8" description = "" optional = false python-versions = ">=3.9,<3.12" @@ -2605,7 +2600,7 @@ certifi = "^2023.7.22" cffi = "^1.16.0" charset-normalizer = "^3.1.0" click = "^8.1.3" -cryptography = "^41.0.6" +cryptography = "^42.0.0" flask = "^2.3.2" flask-redis = "^0.4.0" geojson = "^3.0.1" @@ -2639,7 +2634,7 @@ werkzeug = "^3.0.1" type = "git" url = "https://github.com/GSA/notifications-utils.git" reference = "HEAD" -resolved_reference = "b6cee72f45dbcd48b59447fa08bbac59e15a7b98" +resolved_reference = "a48171e865eb83cf29c75751be7369f396cbe3e2" [[package]] name = "numpy" @@ -3446,7 +3441,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, From afd77cf9fe5157629ba849e357781097e6d2f5e3 Mon Sep 17 00:00:00 2001 From: Andrew Shumway Date: Wed, 7 Feb 2024 14:53:26 -0700 Subject: [PATCH 116/259] Returned months changed to calendar year --- app/dao/date_util.py | 6 +----- tests/app/service/test_statistics.py | 13 ++++++++----- tests/app/service/test_statistics_rest.py | 8 ++++---- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/dao/date_util.py b/app/dao/date_util.py index b338703c4..7aafd711f 100644 --- a/app/dao/date_util.py +++ b/app/dao/date_util.py @@ -3,11 +3,7 @@ from datetime import date, datetime, time, timedelta def get_months_for_financial_year(year): return [ - month - for month in ( - get_months_for_year(4, 13, year) + get_months_for_year(1, 4, year + 1) - ) - if month < datetime.now() + month for month in (get_months_for_year(1, 13, year)) if month < datetime.now() ] diff --git a/tests/app/service/test_statistics.py b/tests/app/service/test_statistics.py index 9787d1a37..8d7566a1f 100644 --- a/tests/app/service/test_statistics.py +++ b/tests/app/service/test_statistics.py @@ -149,10 +149,13 @@ def _stats(requested, delivered, failed): @pytest.mark.parametrize( "year, expected_years", [ - (2018, ["2018-04", "2018-05", "2018-06"]), + (2018, ["2018-01", "2018-02", "2018-03", "2018-04", "2018-05", "2018-06"]), ( 2017, [ + "2017-01", + "2017-02", + "2017-03", "2017-04", "2017-05", "2017-06", @@ -162,9 +165,6 @@ def _stats(requested, delivered, failed): "2017-10", "2017-11", "2017-12", - "2018-01", - "2018-02", - "2018-03", ], ), ], @@ -220,8 +220,11 @@ def test_add_monthly_notification_status_stats(): data["2018-05"]["sms"]["sending"] = 16 add_monthly_notification_status_stats(data, rows) - + # first 3 months are empty assert data == { + "2018-01": {"sms": {}, "email": {}}, + "2018-02": {"sms": {}, "email": {}}, + "2018-03": {"sms": {}, "email": {}}, "2018-04": {"sms": {"sending": 1, "delivered": 2}, "email": {"sending": 4}}, "2018-05": {"sms": {"sending": 24}, "email": {"sending": 32}}, "2018-06": {"sms": {}, "email": {}}, diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index f38d70bd7..7059b3a5b 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -177,6 +177,9 @@ def test_get_monthly_notification_stats_returns_empty_stats_with_correct_dates( assert len(response["data"]) == 12 keys = [ + "2016-01", + "2016-02", + "2016-03", "2016-04", "2016-05", "2016-06", @@ -186,9 +189,6 @@ def test_get_monthly_notification_stats_returns_empty_stats_with_correct_dates( "2016-10", "2016-11", "2016-12", - "2017-01", - "2017-02", - "2017-03", ] assert sorted(response["data"].keys()) == keys for val in response["data"].values(): @@ -266,7 +266,7 @@ def test_get_monthly_notification_stats_combines_todays_data_and_historic_stats( year=2016, ) - assert len(response["data"]) == 3 # apr, may, jun + assert len(response["data"]) == 6 # January to June assert response["data"]["2016-05"] == {"sms": {"delivered": 1}, "email": {}} assert response["data"]["2016-06"] == { "sms": { From f7673aa9684acbed1fd4e90cd93c177a8e3fc305 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Mon, 5 Feb 2024 11:43:44 -0500 Subject: [PATCH 117/259] Add a pull request template This changeset adds a template to the repository for our pull requests. The intention is two-fold: - To make it easier to know what information and details to include in our pull requests - To improve the quality and usefulness of our pull requests This is a start and we will be adjusting this over time as we learn more and refine our process. Signed-off-by: Carlo Costino --- .github/pull_request_template.md | 68 ++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..8f732f160 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,68 @@ + + +## Description + +Please enter a clear description about your proposed changes and what the +expected outcome(s) is/are from there. If there are complex implementation +details within the changes, this is a great place to explain those details using +plain language. + +If there are any caveats, known issues, follow-up items, etc., make a quick note +of them here as well, though more details are probably warranted in the issue +itself in this case. + +## TODO (optional) + +If you're opening a draft PR, it might be helpful to list any outstanding work, +especially if you're asking folks to take a look before it's ready for full +review. In this case, create a small checklist with the outstanding items: + +- [ ] TODO item 1 +- [ ] TODO item 2 +- [ ] TODO item ... + +## Security Considerations + +Please think about the security compliance aspect of your changes and what the +potential impacts might be. + +**NOTE: Please be mindful of sharing sensitive information here! If you're not +sure of what to write, please ask the team first before writing anything here.** + +Relevant details could include (and are not limited to) the following: + +- Handling secrets/credential management (or specifically calling out that there + is nothing to handle) +- Any adjustments to the flow of data in and out the system, or even within it +- Connecting or disconnecting any external services to the application +- Handling of any sensitive information, such as PII +- Handling of information within log statements or other application monitoring + services/hooks +- The inclusion of a new external dependency +- ... (anything else relevant from a security compliance perspective) + +There are some cases where there are no security considerations to be had, e.g., +updating our documentation with publicly available information. In those cases +it is fine to simply put something like this: + +- None; this is a documentation update with publicly available information. From 050d92d8221f9d5b114e48257d1eb8342877a605 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Mon, 5 Feb 2024 13:02:07 -0500 Subject: [PATCH 118/259] Additional detail suggestions for the PR description h/t to @stvnrlly for these suggestions! Co-authored-by: Steven Reilly --- .github/pull_request_template.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8f732f160..bf19e4466 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -27,6 +27,12 @@ expected outcome(s) is/are from there. If there are complex implementation details within the changes, this is a great place to explain those details using plain language. +This should include: + +- Links to issues that this PR addresses +- Screenshots of any visible changes +- Dependency changes + If there are any caveats, known issues, follow-up items, etc., make a quick note of them here as well, though more details are probably warranted in the issue itself in this case. From 6e6c0ee61d6c6198133211c52f6bdd3565c9183b Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Fri, 9 Feb 2024 17:40:14 -0500 Subject: [PATCH 119/259] Final draft adjustments to the template instructions Signed-off-by: Carlo Costino --- .github/pull_request_template.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index bf19e4466..cb6b2c84b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,6 +5,7 @@ have all of the relevant details needed for our work. At the minimum, please be sure to fill in all sections found below and also do the following: +- Provide an appropriate and descriptive title for the pull request - Link the pull request to its corresponding issue (must be done after creating the pull request itself) - Assign yourself as the author @@ -13,13 +14,17 @@ the following: - Select one or more reviewers from the team or mark the pull request as a draft depending on its current state - If the pull request is a draft, please be sure to add reviewers once it is - ready for review + ready for review and mark it ready for review -For each section, please delete the instructions/sample (text that includes this +For each section, please delete the instructions/sample text (that includes this text, though it is wrapped in an HTML comment just in case) and put in your own information. Thank you! --> +*A note to PR reviewers: it may be helpful to review our +[code review documentation](https://github.com/GSA/notifications-api/blob/main/docs/all.md#code-reviews) +to know what to keep in mind while reviewing pull requests.* + ## Description Please enter a clear description about your proposed changes and what the @@ -30,7 +35,7 @@ plain language. This should include: - Links to issues that this PR addresses -- Screenshots of any visible changes +- Screenshots or screen captures of any visible changes, especially for UI work - Dependency changes If there are any caveats, known issues, follow-up items, etc., make a quick note @@ -64,7 +69,7 @@ Relevant details could include (and are not limited to) the following: - Handling of any sensitive information, such as PII - Handling of information within log statements or other application monitoring services/hooks -- The inclusion of a new external dependency +- The inclusion of a new external dependency or the removal of an existing one - ... (anything else relevant from a security compliance perspective) There are some cases where there are no security considerations to be had, e.g., From d9e6ebdbe39ba8fa1e16e2662c0cdc0910d27147 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Mon, 5 Feb 2024 12:44:48 -0500 Subject: [PATCH 120/259] Add code review documentation This changeset adds documentation for what to expect and how to conduct and receive code reviews. We want to improve our team practices and ensure that all members know how to engage and what to expect with code reviews. Furthermore, this can serve as another model and positive example of how to collaborate as a team in the broader open source community. Signed-off-by: Carlo Costino --- docs/all.md | 111 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/docs/all.md b/docs/all.md index c74b11af3..ca3744f14 100644 --- a/docs/all.md +++ b/docs/all.md @@ -36,17 +36,20 @@ - [Celery scheduled tasks](#celery-scheduled-tasks) - [Notify.gov](#notifygov) - [System Description](#system-description) +- [Code Reviews](#code-reviews) + - [For the reviewer](#for-the-reviewer) + - [For the reviewee](#for-the-reviewee) - [Run Book](#run-book) - - [ Alerts, Notifications, Monitoring](#-alerts-notifications-monitoring) - - [ Restaging Apps](#-restaging-apps) - - [ Smoke-testing the App](#-smoke-testing-the-app) - - [ Simulated bulk send testing](#-simulated-bulk-send-testing) - - [ Configuration Management](#-configuration-management) - - [ DNS Changes](#-dns-changes) + - [Alerts, Notifications, Monitoring](#-alerts-notifications-monitoring) + - [Restaging Apps](#-restaging-apps) + - [Smoke-testing the App](#-smoke-testing-the-app) + - [Simulated bulk send testing](#-simulated-bulk-send-testing) + - [Configuration Management](#-configuration-management) + - [DNS Changes](#-dns-changes) - [Exporting test results for compliance monitoring](#exporting-test-results-for-compliance-monitoring) - - [ Known Gotchas](#-known-gotchas) - - [ User Account Management](#-user-account-management) - - [ SMS Phone Number Management](#-sms-phone-number-management) + - [Known Gotchas](#-known-gotchas) + - [User Account Management](#-user-account-management) + - [SMS Phone Number Management](#-sms-phone-number-management) - [Data Storage Policies \& Procedures](#data-storage-policies--procedures) - [Potential PII Locations](#potential-pii-locations) - [Data Retention Policy](#data-retention-policy) @@ -711,10 +714,6 @@ make run-celery-beat ``` - - - - Notify.gov ========= @@ -755,6 +754,92 @@ Notify.gov also provisions and uses two AWS services via a [supplemental service For further details of the system and how it connects to supporting services, see the [application boundary diagram](https://github.com/GSA/us-notify-compliance/blob/main/diagrams/rendered/apps/application.boundary.png) +Code Reviews +============ + +When conducting a code review there are several things to keep in mind to ensure +a quality and valuable review. Remember, we're trying to improve Notify.gov as +best we can; it does us no good if we do not double check that our work meets +our standards, especially before going out the door! + +It also does us no good if we do not treat each other without mutual respect or +consideration either; if there are mistakes or oversights found in a pull +request, or even just suggestions for alternative ways of approaching something, +these become learning opportunities for all parties involved in addition to +modeling positive behavior and practices for the public and broader open source +community. + +Given this basis of approaching code reviews, here are some general guidelines +and suggestions for how to approach a code review from the perspectives of both +the reviewer and the reviewee. + +### For the reviewer + +When performing a code review, please be curious and critical while also being +respectful and appreciative of the work submitted. Code reviews are a chance +to check that things meet our standards and provide learning opportunities. +They are not places for belittling or disparaging someone's work or approach to +a task, and absolutely not the person(s) themselves. + +That said, any responses to the code review should also be respectful and +considerate. Remember, this is a chance to not only improve our work and the +state of Notify.gov, it's also a chance to learn something new! + +**Note: If a response is condescending, derogatory, disrespectful, etc., please +do not hesitate to either speak with the author(s) directly about this or reach +out to a team lead/supervisor for additional help to rectify the issue. Such +behavior and lack of professionalism is not acceptable or tolerated.** + +When performing a code review, it is helpful to keep the following guidelines in +mind: + +- Be on the lookout for any sensitive information and/or leaked credentials, + secrets, PII, etc. +- Ask and call out things that aren't clear to you; it never hurts to double + check your understanding of something! +- Check that things are named descriptively and appropriately and call out + anything that is not. +- Check that comments are present for complex areas when needed. +- Make sure the pull request itself is properly prepared - it has a clear + description, calls out security concerns, and has the necessary labels, flags, + issue link, etc., set on it. +- Do not be shy about using the suggested changes feature in GitHub pull request + comments; this can help save a lot of time! +- Do not be shy about marking a review with the `Request Changes` status - yes, + it looks big and red when it shows up, but this is completely fine and not to + be taken as a personal mark against the author(s) of the pull request! + +### For the reviewee + +When receiving a code review, please remember that someone took the time to look +over all of your work with a critical eye to make sure our standards are being +met and that we're producing the best quality work possible. It's completely +fine if there are specific changes requested and/or other parts are sent back +for additional work! + +That said, the review should also be respectful, helpful, and a learning +opportunity where possible. Remember, this is a chance to not only improve your +work and the state of Notify.gov, it's also a chance to learn something new! + +**Note: If a review is condescending, derogatory, disrespectful, etc., please do +not hesitate to either speak with the reviewer(s) directly about this or reach +out to a team lead/supervisor for additional help to rectify the issue. Such +behavior and lack of professionalism is not acceptable or tolerated.** + +When going over a review, it may be helpful to keep these perspectives in mind: + +- Approach the review with an open mind, curiosity, and appreciation. +- If anything the reviewer(s) mentions is unclear to you, please ask for + clarification and engage them in further dialogue! +- If you disagree with a suggestion or request, please say so and engage in an + open and respecful dialogue to come to a mutual understanding of what the + appropriate next step(S) should be - accept the change, reject the change, + take a different path entirely, etc. +- If there are no issues with any suggested edits or requested changes, make + the necessary adjustments and let the reviewer(s) know when the work is ready + for review again. + + Run Book ======== From aabb8228246e81e70cf840eae24ea4817cdadf85 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Mon, 12 Feb 2024 13:27:29 -0500 Subject: [PATCH 121/259] Added a couple of notes about engaging in conversation h/t to @stvnrlly for the suggestion! Signed-off-by: Carlo Costino --- docs/all.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/all.md b/docs/all.md index ca3744f14..afb1ff796 100644 --- a/docs/all.md +++ b/docs/all.md @@ -809,6 +809,11 @@ mind: it looks big and red when it shows up, but this is completely fine and not to be taken as a personal mark against the author(s) of the pull request! +Additionally, if you find yourself making a lot of comments and/or end up having +several concerns about the overall approach, it will likely be helpful to +schedule time to speak with the author(s) directly and talk through everything. +This can save folks a lot of misunderstanding and back-and-forth! + ### For the reviewee When receiving a code review, please remember that someone took the time to look @@ -839,6 +844,11 @@ When going over a review, it may be helpful to keep these perspectives in mind: the necessary adjustments and let the reviewer(s) know when the work is ready for review again. +Additionally, if you find yourself responding to a lot of things and questioning +the feedback received throughout much of the code review, it will likely be +helpful to schedule time to speak with the reviewer(s) directly and talk through +everything. This can save folks a lot of misunderstanding and back-and-forth! + Run Book ======== From 467a2919f461e789d280b8920bb9cdc3fc6b1970 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 13 Feb 2024 09:39:53 -0800 Subject: [PATCH 122/259] add log warning in dev environment if phone number is opted out (notify-api-539) --- app/clients/cloudwatch/aws_cloudwatch.py | 30 ++++++++++++++++++------ tests/app/clients/test_aws_cloudwatch.py | 22 +++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/app/clients/cloudwatch/aws_cloudwatch.py b/app/clients/cloudwatch/aws_cloudwatch.py index 4fac575a3..0deac089e 100644 --- a/app/clients/cloudwatch/aws_cloudwatch.py +++ b/app/clients/cloudwatch/aws_cloudwatch.py @@ -90,23 +90,39 @@ class AwsCloudwatchClient(Client): account_number = ses_domain_arn.split(":") return account_number + def warn_if_dev_is_opted_out(self, provider_response, notification_id): + if ( + "is opted out" in provider_response.lower() + or "has blocked sms" in provider_response.lower() + ): + if os.getenv("NOTIFY_ENVIRONMENT") in ["development", "test"]: + ansi_red = "\033[31m" + ansi_reset = "\033[0m" + logline = ( + ansi_red + + f"The phone number for notification_id {notification_id} is OPTED OUT. You need to opt back in" + + ansi_reset + ) + current_app.logger.warning(logline) + return logline + return None + def check_sms(self, message_id, notification_id, created_at): - current_app.logger.info(f"CREATED AT = {created_at}") region = cloud_config.sns_region # 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) time_now = datetime.utcnow() log_group_name = f"sns/{region}/{account_number[4]}/DirectPublishToPhoneNumber" - current_app.logger.info( - f"Log group name: {log_group_name} message id: {message_id}" - ) filter_pattern = '{$.notification.messageId="XXXXX"}' filter_pattern = filter_pattern.replace("XXXXX", message_id) all_log_events = self._get_log(filter_pattern, log_group_name, created_at) if all_log_events and len(all_log_events) > 0: event = all_log_events[0] message = json.loads(event["message"]) + self.warn_if_dev_is_opted_out( + message["delivery"]["providerResponse"], notification_id + ) return ( "success", message["delivery"]["providerResponse"], @@ -116,13 +132,13 @@ class AwsCloudwatchClient(Client): log_group_name = ( f"sns/{region}/{account_number[4]}/DirectPublishToPhoneNumber/Failure" ) - 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) if all_failed_events and len(all_failed_events) > 0: - current_app.logger.info("SHOULD RETURN FAILED BECAUSE WE FOUND A FAILURE") event = all_failed_events[0] message = json.loads(event["message"]) - current_app.logger.info(f"MESSAGE {message}") + self.warn_if_dev_is_opted_out( + message["delivery"]["providerResponse"], notification_id + ) return ( "failure", message["delivery"]["providerResponse"], diff --git a/tests/app/clients/test_aws_cloudwatch.py b/tests/app/clients/test_aws_cloudwatch.py index 5eeffb979..2eb70c94b 100644 --- a/tests/app/clients/test_aws_cloudwatch.py +++ b/tests/app/clients/test_aws_cloudwatch.py @@ -1,6 +1,7 @@ # import pytest from datetime import datetime +import pytest from flask import current_app from app import aws_cloudwatch_client @@ -54,6 +55,27 @@ def side_effect(filterPattern, logGroupName, startTime, endTime): return {"events": []} +@pytest.mark.parametrize( + "response, notify_id, expected_message", + [ + ( + "Phone has blocked SMS", + "abc", + "\x1b[31mThe phone number for notification_id abc is OPTED OUT. You need to opt back in\x1b[0m", + ), + ( + "Some phone is opted out", + "xyz", + "\x1b[31mThe phone number for notification_id xyz is OPTED OUT. You need to opt back in\x1b[0m", + ), + ("Phone is A-OK", "123", None), + ], +) +def test_warn_if_dev_is_opted_out(response, notify_id, expected_message): + result = aws_cloudwatch_client.warn_if_dev_is_opted_out(response, notify_id) + assert result == expected_message + + def test_check_sms_success(notify_api, mocker): aws_cloudwatch_client.init_app(current_app) boto_mock = mocker.patch.object(aws_cloudwatch_client, "_client", create=True) From 755f5e085eb241c6b67f9fc163d25ccf6e6b1626 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 13 Feb 2024 16:27:55 -0500 Subject: [PATCH 123/259] Update reviewee to author This changeset adjusts the language slightly to use clearer terms for the individuals involved in a code review. --- docs/all.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/all.md b/docs/all.md index afb1ff796..4803102dd 100644 --- a/docs/all.md +++ b/docs/all.md @@ -38,7 +38,7 @@ - [System Description](#system-description) - [Code Reviews](#code-reviews) - [For the reviewer](#for-the-reviewer) - - [For the reviewee](#for-the-reviewee) + - [For the author](#for-the-author) - [Run Book](#run-book) - [Alerts, Notifications, Monitoring](#-alerts-notifications-monitoring) - [Restaging Apps](#-restaging-apps) @@ -771,7 +771,7 @@ community. Given this basis of approaching code reviews, here are some general guidelines and suggestions for how to approach a code review from the perspectives of both -the reviewer and the reviewee. +the reviewer and the author. ### For the reviewer @@ -814,7 +814,7 @@ several concerns about the overall approach, it will likely be helpful to schedule time to speak with the author(s) directly and talk through everything. This can save folks a lot of misunderstanding and back-and-forth! -### For the reviewee +### For the author When receiving a code review, please remember that someone took the time to look over all of your work with a critical eye to make sure our standards are being From d1d55cca4e6ef1036e9943513cd1a86983c847a3 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 21 Feb 2024 15:50:55 -0500 Subject: [PATCH 124/259] Update utils to 0.2.9 This changeset updates notifications-utils to 0.2.9 to account for a recent cryptography update. Signed-off-by: Carlo Costino --- poetry.lock | 72 ++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2d1d30bb1..3a92d1031 100644 --- a/poetry.lock +++ b/poetry.lock @@ -940,43 +940,43 @@ files = [ [[package]] name = "cryptography" -version = "42.0.2" +version = "42.0.4" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, - {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, - {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, - {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, - {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, - {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, - {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, - {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, - {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, - {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, - {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, - {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, - {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, - {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, + {file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449"}, + {file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b"}, + {file = "cryptography-42.0.4-cp37-abi3-win32.whl", hash = "sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925"}, + {file = "cryptography-42.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923"}, + {file = "cryptography-42.0.4-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0"}, + {file = "cryptography-42.0.4-cp39-abi3-win32.whl", hash = "sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129"}, + {file = "cryptography-42.0.4-cp39-abi3-win_amd64.whl", hash = "sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660"}, + {file = "cryptography-42.0.4.tar.gz", hash = "sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb"}, ] [package.dependencies] @@ -2582,7 +2582,7 @@ requests = ">=2.0.0" [[package]] name = "notifications-utils" -version = "0.2.8" +version = "0.2.9" description = "" optional = false python-versions = ">=3.9,<3.12" @@ -2600,7 +2600,7 @@ certifi = "^2023.7.22" cffi = "^1.16.0" charset-normalizer = "^3.1.0" click = "^8.1.3" -cryptography = "^42.0.0" +cryptography = "^42.0.4" flask = "^2.3.2" flask-redis = "^0.4.0" geojson = "^3.0.1" @@ -2634,7 +2634,7 @@ werkzeug = "^3.0.1" type = "git" url = "https://github.com/GSA/notifications-utils.git" reference = "HEAD" -resolved_reference = "a48171e865eb83cf29c75751be7369f396cbe3e2" +resolved_reference = "df8ece836cad303c5d161edf485cf9bbdc666688" [[package]] name = "numpy" From 1cf2a81d26e6e2237ba07552e3593864d6a513f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:13:35 +0000 Subject: [PATCH 125/259] Bump eventlet from 0.34.3 to 0.35.2 Bumps [eventlet](https://github.com/eventlet/eventlet) from 0.34.3 to 0.35.2. - [Changelog](https://github.com/eventlet/eventlet/blob/master/NEWS) - [Commits](https://github.com/eventlet/eventlet/compare/v0.34.3...v0.35.2) --- updated-dependencies: - dependency-name: eventlet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- poetry.lock | 25 ++++++++++++++++++++----- pyproject.toml | 2 +- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3a92d1031..16542239b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1169,13 +1169,13 @@ pgp = ["gpg"] [[package]] name = "eventlet" -version = "0.34.3" +version = "0.35.2" description = "Highly concurrent networking library" optional = false python-versions = ">=3.7" files = [ - {file = "eventlet-0.34.3-py3-none-any.whl", hash = "sha256:3093f2822ce2f40792bf9aa7eb8920fe3b90db785d3bea7640c50412969dcfd7"}, - {file = "eventlet-0.34.3.tar.gz", hash = "sha256:ed2d28a64414a001894b3baf5b650f2c9596b00d57f57d4d7a38f9d3d0c252e8"}, + {file = "eventlet-0.35.2-py3-none-any.whl", hash = "sha256:8fc1ee60d583f1dd58d6f304bb95fd46d34865ab22f57cb99008a81d61d573db"}, + {file = "eventlet-0.35.2.tar.gz", hash = "sha256:8d1263e20b7f816a046ac60e1d272f9e5bc503f7a34d9adc789f8a85b14fa57d"}, ] [package.dependencies] @@ -2015,6 +2015,7 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2024,6 +2025,7 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2033,6 +2035,7 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2058,8 +2061,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2067,6 +2070,7 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2184,6 +2188,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -3441,6 +3455,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -4717,4 +4732,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "a6d1d609f32455bcd4a1b2934512bba368d3bad792a032bb0e8e2b49f6f5f99a" +content-hash = "bf4fab839d756f6c9426c4b0b80061e77d37a5f4735ba47dfd00655aa44eb2e4" diff --git a/pyproject.toml b/pyproject.toml index 95bf20635..ad068144c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ click-didyoumean = "==0.3.0" click-plugins = "==1.1.1" click-repl = "==0.3.0" deprecated = "==1.2.14" -eventlet = "==0.34.3" +eventlet = "==0.35.2" expiringdict = "==1.2.2" flask = "~=2.3" flask-bcrypt = "==1.0.1" From 9cd68a10dcb5be10a1e73fe7c94258829381305a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:04:42 +0000 Subject: [PATCH 126/259] Bump flake8 from 6.1.0 to 7.0.0 Bumps [flake8](https://github.com/pycqa/flake8) from 6.1.0 to 7.0.0. - [Commits](https://github.com/pycqa/flake8/compare/6.1.0...7.0.0) --- updated-dependencies: - dependency-name: flake8 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- poetry.lock | 16 ++++++++-------- pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/poetry.lock b/poetry.lock index 16542239b..949fefcd2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1259,19 +1259,19 @@ typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" -version = "6.1.0" +version = "7.0.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" +pyflakes = ">=3.2.0,<3.3.0" [[package]] name = "flake8-bugbear" @@ -3206,13 +3206,13 @@ files = [ [[package]] name = "pyflakes" -version = "3.1.0" +version = "3.2.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.8" files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] [[package]] @@ -4732,4 +4732,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "bf4fab839d756f6c9426c4b0b80061e77d37a5f4735ba47dfd00655aa44eb2e4" +content-hash = "fa9cb13d87e7b797c72dd6eb7a669289c28f76e1139ba719e99501793b3db28b" diff --git a/pyproject.toml b/pyproject.toml index ad068144c..8996b23f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ bandit = "*" black = "^23.12.1" cloudfoundry-client = "*" exceptiongroup = "==1.2.0" -flake8 = "^6.1.0" +flake8 = "^7.0.0" flake8-bugbear = "^24.1.17" freezegun = "^1.4.0" honcho = "*" From bfb619d5e9eb54a0f7f67f270caed5334954036d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:18:46 +0000 Subject: [PATCH 127/259] Bump notifications-python-client from 8.2.0 to 9.0.0 Bumps [notifications-python-client](https://github.com/alphagov/notifications-python-client) from 8.2.0 to 9.0.0. - [Changelog](https://github.com/alphagov/notifications-python-client/blob/main/CHANGELOG.md) - [Commits](https://github.com/alphagov/notifications-python-client/compare/8.2.0...9.0.0) --- updated-dependencies: - dependency-name: notifications-python-client dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 949fefcd2..6a1c8af62 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2581,12 +2581,12 @@ setuptools = "*" [[package]] name = "notifications-python-client" -version = "8.2.0" +version = "9.0.0" description = "Python API client for GOV.UK Notify." optional = false python-versions = ">=3.7" files = [ - {file = "notifications_python_client-8.2.0-py3-none-any.whl", hash = "sha256:8cd8bd01ae603a972a5413c430ca42b16f6481f37d98406c3e9b68c1c192f59e"}, + {file = "notifications_python_client-9.0.0-py3-none-any.whl", hash = "sha256:664a5b5da2aa1a00efa8106bfa4855db04da95d79586e5edfb0411637d20d2d9"}, ] [package.dependencies] @@ -4732,4 +4732,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "fa9cb13d87e7b797c72dd6eb7a669289c28f76e1139ba719e99501793b3db28b" +content-hash = "f6d89a19888ec8a666200c9c2552434c0f9efb49fb0d29d69b56707bae47315a" diff --git a/pyproject.toml b/pyproject.toml index 8996b23f6..205d5a8db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ lxml = "==5.1.0" marshmallow = "==3.20.2" marshmallow-sqlalchemy = "==0.30.0" newrelic = "*" -notifications-python-client = "==8.2.0" +notifications-python-client = "==9.0.0" notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"} oscrypto = "==1.3.0" packaging = "==23.2" From f301c7a389c3f477537abd33d8273e68ef0c84a4 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 23 Feb 2024 11:07:13 -0800 Subject: [PATCH 128/259] If a telephone number is unavailable, change text to 'Unavailable' --- app/aws/s3.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index 49fca4d32..dce1a0fd8 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -117,7 +117,7 @@ def extract_phones(job): current_app.logger.info(f"PHONE INDEX IS NOW {phone_index}") current_app.logger.info(f"LENGTH OF ROW IS {len(row)}") if phone_index >= len(row): - phones[job_row] = "Error: can't retrieve phone number" + phones[job_row] = "Unavailable" current_app.logger.error( "Corrupt csv file, missing columns or possibly a byte order mark in the file" ) @@ -151,7 +151,7 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): current_app.logger.warning( f"Couldnt find phone for job_id {job_id} row number {job_row_number} because job is missing" ) - return "Unknown Phone" + return "Unavailable" # If we look in the JOBS cache for the quick lookup dictionary of phones for a given job # and that dictionary is not there, create it @@ -167,12 +167,12 @@ def get_phone_number_from_s3(service_id, job_id, job_row_number): current_app.logger.warning( f"Was unable to retrieve phone number from lookup dictionary for job {job_id}" ) - return "Unknown Phone" + return "Unavailable" else: current_app.logger.error( f"Was unable to construct lookup dictionary for job {job_id}" ) - return "Unknown Phone" + return "Unavailable" def get_job_metadata_from_s3(service_id, job_id): From 6d4064e8887ed764bbfd6c02539e5771dd72901a Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 28 Feb 2024 07:41:13 -0800 Subject: [PATCH 129/259] make sure original_file_name is stored as part of job (notify-admin-1148) --- app/job/rest.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/job/rest.py b/app/job/rest.py index 1aab2ca60..c5df0dddb 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -144,6 +144,7 @@ def get_jobs_by_service(service_id): ) + @job_blueprint.route("", methods=["POST"]) def create_job(service_id): service = dao_fetch_service_by_id(service_id) @@ -151,6 +152,7 @@ def create_job(service_id): raise InvalidRequest("Create job is not allowed: service is inactive ", 403) data = request.get_json() + original_file_name = data["original_file_name"] data.update({"service": service_id}) try: data.update(**get_job_metadata_from_s3(service_id, data["id"])) @@ -173,6 +175,8 @@ def create_job(service_id): data.update({"template_version": template.version}) job = job_schema.load(data) + # See admin #1148, for whatever reason schema loading doesn't load this + job.original_file_name = original_file_name if job.scheduled_for: job.job_status = JOB_STATUS_SCHEDULED From 689d82280b46e4d2a987d4948a63cc11d3b5e35a Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 28 Feb 2024 08:23:56 -0800 Subject: [PATCH 130/259] fix tests --- app/job/rest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/job/rest.py b/app/job/rest.py index c5df0dddb..d86a4ec62 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -144,7 +144,6 @@ def get_jobs_by_service(service_id): ) - @job_blueprint.route("", methods=["POST"]) def create_job(service_id): service = dao_fetch_service_by_id(service_id) @@ -152,7 +151,7 @@ def create_job(service_id): raise InvalidRequest("Create job is not allowed: service is inactive ", 403) data = request.get_json() - original_file_name = data["original_file_name"] + original_file_name = data.get("original_file_name") data.update({"service": service_id}) try: data.update(**get_job_metadata_from_s3(service_id, data["id"])) @@ -176,7 +175,8 @@ def create_job(service_id): job = job_schema.load(data) # See admin #1148, for whatever reason schema loading doesn't load this - job.original_file_name = original_file_name + if original_file_name is not None: + job.original_file_name = original_file_name if job.scheduled_for: job.job_status = JOB_STATUS_SCHEDULED From 820ee5a942fd1e32b68925f01ce53628df689896 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 28 Feb 2024 12:40:52 -0500 Subject: [PATCH 131/259] Cleaning up a lot of things, getting Enums used everywhere. Signed-off-by: Cliff Hill --- app/celery/nightly_tasks.py | 4 +- app/celery/reporting_tasks.py | 4 +- app/celery/research_mode_tasks.py | 4 +- app/celery/scheduled_tasks.py | 7 +- app/celery/tasks.py | 13 ++- app/celery/test_key_tasks.py | 4 +- app/commands.py | 4 +- app/dao/fact_billing_dao.py | 41 ++++--- app/dao/inbound_sms_dao.py | 4 +- app/dao/notifications_dao.py | 13 ++- app/dao/provider_details_dao.py | 4 +- app/dao/services_dao.py | 17 ++- app/dao/uploads_dao.py | 4 +- app/delivery/send_to_providers.py | 7 +- app/models.py | 120 ++++++++++++++------- app/notifications/process_notifications.py | 19 ++-- app/notifications/receive_notifications.py | 4 +- app/notifications/rest.py | 12 +-- app/notifications/validators.py | 20 ++-- app/organization/invite_rest.py | 4 +- app/schemas.py | 4 +- app/service/rest.py | 6 +- app/service/send_notification.py | 6 +- app/service/sender.py | 4 +- app/service/utils.py | 7 +- app/service_invite/rest.py | 4 +- app/template/rest.py | 4 +- app/user/rest.py | 10 +- app/utils.py | 10 +- app/v2/notifications/post_notifications.py | 25 +++-- 30 files changed, 209 insertions(+), 180 deletions(-) diff --git a/app/celery/nightly_tasks.py b/app/celery/nightly_tasks.py index 7e761dcbf..678c9bfa1 100644 --- a/app/celery/nightly_tasks.py +++ b/app/celery/nightly_tasks.py @@ -25,14 +25,14 @@ from app.dao.notifications_dao import ( from app.dao.service_data_retention_dao import ( fetch_service_data_retention_for_all_services_by_notification_type, ) -from app.models import EMAIL_TYPE, SMS_TYPE, FactProcessingTime +from app.models import NotificationType, FactProcessingTime from app.utils import get_midnight_in_utc @notify_celery.task(name="remove_sms_email_jobs") @cronitor("remove_sms_email_jobs") def remove_sms_email_csv_files(): - _remove_csv_files([EMAIL_TYPE, SMS_TYPE]) + _remove_csv_files([NotificationType.EMAIL, NotificationType.SMS]) def _remove_csv_files(job_types): diff --git a/app/celery/reporting_tasks.py b/app/celery/reporting_tasks.py index ec2cd4dce..c7c60f1cf 100644 --- a/app/celery/reporting_tasks.py +++ b/app/celery/reporting_tasks.py @@ -8,7 +8,7 @@ from app.cronitor import cronitor from app.dao.fact_billing_dao import fetch_billing_data_for_day, update_fact_billing from app.dao.fact_notification_status_dao import update_fact_notification_status from app.dao.notifications_dao import get_service_ids_with_notifications_on_date -from app.models import EMAIL_TYPE, SMS_TYPE +from app.models import NotificationType @notify_celery.task(name="create-nightly-billing") @@ -80,7 +80,7 @@ def create_nightly_notification_status(): yesterday = datetime.utcnow().date() - timedelta(days=1) - for notification_type in [SMS_TYPE, EMAIL_TYPE]: + for notification_type in (NotificationType.SMS, NotificationType.EMAIL): days = 4 for i in range(days): diff --git a/app/celery/research_mode_tasks.py b/app/celery/research_mode_tasks.py index 29e2004a1..c662da851 100644 --- a/app/celery/research_mode_tasks.py +++ b/app/celery/research_mode_tasks.py @@ -6,7 +6,7 @@ from requests import HTTPError, request from app.celery.process_ses_receipts_tasks import process_ses_results from app.config import QueueNames from app.dao.notifications_dao import get_notification_by_id -from app.models import SMS_TYPE +from app.models import NotificationType temp_fail = "2028675303" perm_fail = "2028675302" @@ -21,7 +21,7 @@ def send_sms_response(provider, reference): body = sns_callback(reference) headers = {"Content-type": "application/json"} - make_request(SMS_TYPE, provider, body, headers) + make_request(NotificationType.SMS, provider, body, headers) def send_email_response(reference, to): diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index 30d778d41..6ec3370cb 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -35,11 +35,10 @@ from app.dao.services_dao import ( from app.dao.users_dao import delete_codes_older_created_more_than_a_day_ago from app.delivery.send_to_providers import provider_to_use from app.models import ( - EMAIL_TYPE, JOB_STATUS_ERROR, JOB_STATUS_IN_PROGRESS, JOB_STATUS_PENDING, - SMS_TYPE, + NotificationType, Job, ) from app.notifications.process_notifications import send_notification_to_queue @@ -160,7 +159,7 @@ def check_db_notification_fails(): ) # suppress any spam coming from development tier if message and curr_env != "development": - provider = provider_to_use(EMAIL_TYPE, False) + provider = provider_to_use(NotificationType.EMAIL, False) from_address = '"{}" <{}@{}>'.format( "Failed Notification Count Alert", "test_sender", @@ -224,7 +223,7 @@ def check_job_status(): def replay_created_notifications(): # if the notification has not be send after 1 hour, then try to resend. resend_created_notifications_older_than = 60 * 60 - for notification_type in (EMAIL_TYPE, SMS_TYPE): + for notification_type in (NotificationType.EMAIL, NotificationType.SMS): notifications_to_resend = notifications_not_yet_sent( resend_created_notifications_older_than, notification_type ) diff --git a/app/celery/tasks.py b/app/celery/tasks.py index b46637099..4b7d5ccac 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -21,13 +21,12 @@ from app.dao.service_inbound_api_dao import get_service_inbound_api_for_service from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.templates_dao import dao_get_template_by_id from app.models import ( - EMAIL_TYPE, JOB_STATUS_CANCELLED, JOB_STATUS_FINISHED, JOB_STATUS_IN_PROGRESS, JOB_STATUS_PENDING, KEY_TYPE_NORMAL, - SMS_TYPE, + NotificationType, ) from app.notifications.process_notifications import persist_notification from app.notifications.validators import check_service_over_total_message_limit @@ -129,7 +128,7 @@ def process_row(row, template, job, service, sender_id=None): } ) - send_fns = {SMS_TYPE: save_sms, EMAIL_TYPE: save_email} + send_fns = {NotificationType.SMS: save_sms, NotificationType.EMAIL: save_email} send_fn = send_fns[template_type] @@ -205,7 +204,7 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i recipient=notification["to"], service=service, personalisation=notification.get("personalisation"), - notification_type=SMS_TYPE, + notification_type=NotificationType.SMS, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_at=datetime.utcnow(), @@ -265,7 +264,7 @@ def save_email( recipient=notification["to"], service=service, personalisation=notification.get("personalisation"), - notification_type=EMAIL_TYPE, + notification_type=NotificationType.EMAIL, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_at=datetime.utcnow(), @@ -307,12 +306,12 @@ def save_api_email_or_sms(self, encrypted_notification): service = SerialisedService.from_id(notification["service_id"]) q = ( QueueNames.SEND_EMAIL - if notification["notification_type"] == EMAIL_TYPE + if notification["notification_type"] == NotificationType.EMAIL else QueueNames.SEND_SMS ) provider_task = ( provider_tasks.deliver_email - if notification["notification_type"] == EMAIL_TYPE + if notification["notification_type"] == NotificationType.EMAIL else provider_tasks.deliver_sms ) try: diff --git a/app/celery/test_key_tasks.py b/app/celery/test_key_tasks.py index bc7a76acf..a7fdda110 100644 --- a/app/celery/test_key_tasks.py +++ b/app/celery/test_key_tasks.py @@ -6,7 +6,7 @@ from requests import HTTPError, request from app.celery.process_ses_receipts_tasks import process_ses_results from app.config import QueueNames from app.dao.notifications_dao import get_notification_by_id -from app.models import SMS_TYPE +from app.models import NotificationType temp_fail = "2028675303" perm_fail = "2028675302" @@ -21,7 +21,7 @@ def send_sms_response(provider, reference): body = sns_callback(reference) headers = {"Content-type": "application/json"} - make_request(SMS_TYPE, provider, body, headers) + make_request(NotificationType.SMS, provider, body, headers) def send_email_response(reference, to): diff --git a/app/commands.py b/app/commands.py index 32cbd219b..26c48c45b 100644 --- a/app/commands.py +++ b/app/commands.py @@ -52,7 +52,7 @@ from app.dao.users_dao import ( from app.models import ( KEY_TYPE_TEST, NOTIFICATION_CREATED, - SMS_TYPE, + NotificationType, AnnualBilling, Domain, EmailBranding, @@ -519,7 +519,7 @@ def populate_go_live(file_name): @notify_command(name="fix-billable-units") def fix_billable_units(): query = Notification.query.filter( - Notification.notification_type == SMS_TYPE, + Notification.notification_type == NotificationType.SMS, Notification.status != NOTIFICATION_CREATED, Notification.sent_at == None, # noqa Notification.billable_units == 0, diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index f0319141e..b2f53dbc2 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -9,12 +9,11 @@ from app import db from app.dao.date_util import get_calendar_year_dates, get_calendar_year_for_datetime from app.dao.organization_dao import dao_get_organization_live_services from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, NOTIFICATION_STATUS_TYPES_BILLABLE_SMS, NOTIFICATION_STATUS_TYPES_SENT_EMAILS, - SMS_TYPE, + NotificationType, AnnualBilling, FactBilling, NotificationAllTimeView, @@ -53,7 +52,7 @@ def fetch_sms_free_allowance_remainder_until_date(end_date): AnnualBilling.service_id == FactBilling.service_id, FactBilling.local_date >= start_of_year, FactBilling.local_date < end_date, - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, ), ) .filter( @@ -117,7 +116,7 @@ def fetch_sms_billing_for_all_services(start_date, end_date): .filter( FactBilling.local_date >= start_date, FactBilling.local_date <= end_date, - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, ) .group_by( Organization.name, @@ -269,7 +268,7 @@ def query_service_email_usage_for_year(service_id, year): FactBilling.service_id == service_id, FactBilling.local_date >= year_start, FactBilling.local_date <= year_end, - FactBilling.notification_type == EMAIL_TYPE, + FactBilling.notification_type == NotificationType.EMAIL, ) @@ -356,7 +355,7 @@ def query_service_sms_usage_for_year(service_id, year): FactBilling.service_id == service_id, FactBilling.local_date >= year_start, FactBilling.local_date <= year_end, - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, AnnualBilling.financial_year_start == year, ) ) @@ -386,7 +385,7 @@ def fetch_billing_data_for_day(process_day, service_id=None, check_permissions=F services = [Service.query.get(service_id)] for service in services: - for notification_type in (SMS_TYPE, EMAIL_TYPE): + for notification_type in (NotificationType.SMS, NotificationType.EMAIL): if (not check_permissions) or service.has_permission(notification_type): results = _query_for_billing_data( notification_type=notification_type, @@ -465,8 +464,8 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): ) query_funcs = { - SMS_TYPE: _sms_query, - EMAIL_TYPE: _email_query, + NotificationType.SMS: _sms_query, + NotificationType.EMAIL: _email_query, } query = query_funcs[notification_type]() @@ -484,7 +483,7 @@ def get_service_ids_that_need_billing_populated(start_date, end_date): .filter( NotificationHistory.created_at >= start_date, NotificationHistory.created_at <= end_date, - NotificationHistory.notification_type.in_([SMS_TYPE, EMAIL_TYPE]), + NotificationHistory.notification_type.in_([NotificationType.SMS, NotificationType.EMAIL]), NotificationHistory.billable_units != 0, ) .distinct() @@ -495,7 +494,7 @@ def get_service_ids_that_need_billing_populated(start_date, end_date): def get_rate(rates, notification_type, date): start_of_day = get_midnight_in_utc(date) - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: return next( r.rate for r in rates @@ -576,7 +575,7 @@ def fetch_email_usage_for_organization(organization_id, start_date, end_date): .filter( FactBilling.local_date >= start_date, FactBilling.local_date <= end_date, - FactBilling.notification_type == EMAIL_TYPE, + FactBilling.notification_type == NotificationType.EMAIL, Service.organization_id == organization_id, Service.restricted.is_(False), ) @@ -690,7 +689,7 @@ def query_organization_sms_usage_for_year(organization_id, year): Service.id == FactBilling.service_id, FactBilling.local_date >= year_start, FactBilling.local_date <= year_end, - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, ), ) .filter( @@ -784,7 +783,7 @@ def fetch_daily_volumes_for_platform(start_date, end_date): case( [ ( - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, FactBilling.notifications_sent, ) ], @@ -795,7 +794,7 @@ def fetch_daily_volumes_for_platform(start_date, end_date): case( [ ( - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, FactBilling.billable_units, ) ], @@ -806,7 +805,7 @@ def fetch_daily_volumes_for_platform(start_date, end_date): case( [ ( - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, FactBilling.billable_units * FactBilling.rate_multiplier, ) ], @@ -817,7 +816,7 @@ def fetch_daily_volumes_for_platform(start_date, end_date): case( [ ( - FactBilling.notification_type == EMAIL_TYPE, + FactBilling.notification_type == NotificationType.EMAIL, FactBilling.notifications_sent, ) ], @@ -871,7 +870,7 @@ def fetch_daily_sms_provider_volumes_for_platform(start_date, end_date): ).label("sms_cost"), ) .filter( - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, FactBilling.local_date >= start_date, FactBilling.local_date <= end_date, ) @@ -902,7 +901,7 @@ def fetch_volumes_by_service(start_date, end_date): case( [ ( - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, FactBilling.notifications_sent, ) ], @@ -913,7 +912,7 @@ def fetch_volumes_by_service(start_date, end_date): case( [ ( - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, FactBilling.billable_units * FactBilling.rate_multiplier, ) ], @@ -924,7 +923,7 @@ def fetch_volumes_by_service(start_date, end_date): case( [ ( - FactBilling.notification_type == EMAIL_TYPE, + FactBilling.notification_type == NotificationType.EMAIL, FactBilling.notifications_sent, ) ], diff --git a/app/dao/inbound_sms_dao.py b/app/dao/inbound_sms_dao.py index 291c6b0e7..2b5767caa 100644 --- a/app/dao/inbound_sms_dao.py +++ b/app/dao/inbound_sms_dao.py @@ -6,7 +6,7 @@ from sqlalchemy.orm import aliased from app import db from app.dao.dao_utils import autocommit from app.models import ( - SMS_TYPE, + NotificationType, InboundSms, InboundSmsHistory, Service, @@ -130,7 +130,7 @@ def delete_inbound_sms_older_than_retention(): ServiceDataRetention.query.join( ServiceDataRetention.service, Service.inbound_number ) - .filter(ServiceDataRetention.notification_type == SMS_TYPE) + .filter(ServiceDataRetention.notification_type == NotificationType.SMS) .all() ) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index ee08f9af0..c903de270 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -17,7 +17,6 @@ from werkzeug.datastructures import MultiDict from app import create_uuid, db from app.dao.dao_utils import autocommit from app.models import ( - EMAIL_TYPE, KEY_TYPE_TEST, NOTIFICATION_CREATED, NOTIFICATION_FAILED, @@ -27,7 +26,7 @@ from app.models import ( NOTIFICATION_SENDING, NOTIFICATION_SENT, NOTIFICATION_TEMPORARY_FAILURE, - SMS_TYPE, + NotificationType, FactNotificationStatus, Notification, NotificationHistory, @@ -138,7 +137,7 @@ def update_notification_status_by_id( return None if ( - notification.notification_type == SMS_TYPE + notification.notification_type == NotificationType.SMS and notification.international and not country_records_delivery(notification.phone_prefix) ): @@ -445,7 +444,7 @@ def dao_timeout_notifications(cutoff_time, limit=100000): Notification.query.filter( Notification.created_at < cutoff_time, Notification.status.in_(current_statuses), - Notification.notification_type.in_([SMS_TYPE, EMAIL_TYPE]), + Notification.notification_type.in_([NotificationType.SMS, NotificationType.EMAIL]), ) .limit(limit) .all() @@ -485,7 +484,7 @@ def dao_get_notifications_by_recipient_or_reference( page_size=None, error_out=True, ): - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: normalised = try_validate_and_format_phone_number(search_term) for character in {"(", ")", " ", "-"}: @@ -493,7 +492,7 @@ def dao_get_notifications_by_recipient_or_reference( normalised = normalised.lstrip("+0") - elif notification_type == EMAIL_TYPE: + elif notification_type == NotificationType.EMAIL: try: normalised = validate_and_format_email_address(search_term) except InvalidEmailError: @@ -507,7 +506,7 @@ def dao_get_notifications_by_recipient_or_reference( normalised = "".join(search_term.split()).lower() else: - raise TypeError(f"Notification type must be {EMAIL_TYPE}, {SMS_TYPE}, or None") + raise TypeError(f"Notification type must be {NotificationType.EMAIL}, {NotificationType.SMS}, or None") normalised = escape_special_characters(normalised) search_term = escape_special_characters(search_term) diff --git a/app/dao/provider_details_dao.py b/app/dao/provider_details_dao.py index 9964b8c6b..3497e8398 100644 --- a/app/dao/provider_details_dao.py +++ b/app/dao/provider_details_dao.py @@ -6,7 +6,7 @@ from sqlalchemy import asc, desc, func from app import db from app.dao.dao_utils import autocommit from app.models import ( - SMS_TYPE, + NotificationType, FactBilling, ProviderDetails, ProviderDetailsHistory, @@ -126,7 +126,7 @@ def dao_get_provider_stats(): ), ) .filter( - FactBilling.notification_type == SMS_TYPE, + FactBilling.notification_type == NotificationType.SMS, FactBilling.local_date >= first_day_of_the_month, ) .group_by(FactBilling.provider) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index ac28eaabe..00b90e46c 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -14,11 +14,8 @@ from app.dao.service_sms_sender_dao import insert_service_sms_sender from app.dao.service_user_dao import dao_get_service_user from app.dao.template_folder_dao import dao_get_valid_template_folders_by_id from app.models import ( - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, KEY_TYPE_TEST, NOTIFICATION_PERMANENT_FAILURE, - SMS_TYPE, AnnualBilling, ApiKey, FactBilling, @@ -27,11 +24,13 @@ from app.models import ( Job, Notification, NotificationHistory, + NotificationType, Organization, Permission, Service, ServiceEmailReplyTo, ServicePermission, + ServicePermissionType, ServiceSmsSender, Template, TemplateHistory, @@ -46,9 +45,9 @@ from app.utils import ( ) DEFAULT_SERVICE_PERMISSIONS = [ - SMS_TYPE, - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.SMS, + ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, ] @@ -518,7 +517,7 @@ def dao_find_services_sending_to_tv_numbers(start_date, end_date, threshold=500) Notification.created_at >= start_date, Notification.created_at <= end_date, Notification.key_type != KEY_TYPE_TEST, - Notification.notification_type == SMS_TYPE, + Notification.notification_type == NotificationType.SMS, func.substr(Notification.normalised_to, 3, 7) == "7700900", Service.restricted == False, # noqa Service.active == True, # noqa @@ -542,7 +541,7 @@ def dao_find_services_with_high_failure_rates(start_date, end_date, threshold=10 Notification.created_at >= start_date, Notification.created_at <= end_date, Notification.key_type != KEY_TYPE_TEST, - Notification.notification_type == SMS_TYPE, + Notification.notification_type == NotificationType.SMS, Service.restricted == False, # noqa Service.active == True, # noqa ) @@ -570,7 +569,7 @@ def dao_find_services_with_high_failure_rates(start_date, end_date, threshold=10 Notification.created_at >= start_date, Notification.created_at <= end_date, Notification.key_type != KEY_TYPE_TEST, - Notification.notification_type == SMS_TYPE, + Notification.notification_type == NotificationType.SMS, Notification.status == NOTIFICATION_PERMANENT_FAILURE, Service.restricted == False, # noqa Service.active == True, # noqa diff --git a/app/dao/uploads_dao.py b/app/dao/uploads_dao.py index 717876589..6ef166edd 100644 --- a/app/dao/uploads_dao.py +++ b/app/dao/uploads_dao.py @@ -8,7 +8,7 @@ from app import db from app.models import ( JOB_STATUS_CANCELLED, JOB_STATUS_SCHEDULED, - LETTER_TYPE, + NotificationType, NOTIFICATION_CANCELLED, Job, Notification, @@ -90,7 +90,7 @@ def dao_get_uploads_by_service_id(service_id, limit_days=None, page=1, page_size letters_query_filter = [ Notification.service_id == service_id, - Notification.notification_type == LETTER_TYPE, + Notification.notification_type == NotificationType.LETTER, Notification.api_key_id == None, # noqa Notification.status != NOTIFICATION_CANCELLED, Template.hidden == True, # noqa diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 9590f5bd6..49c98b50a 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -19,12 +19,11 @@ from app.exceptions import NotificationTechnicalFailureException from app.models import ( BRANDING_BOTH, BRANDING_ORG_BANNER, - EMAIL_TYPE, KEY_TYPE_TEST, NOTIFICATION_SENDING, NOTIFICATION_STATUS_TYPES_COMPLETED, NOTIFICATION_TECHNICAL_FAILURE, - SMS_TYPE, + NotificationType, ) from app.serialised_models import SerialisedService, SerialisedTemplate @@ -37,7 +36,7 @@ def send_sms_to_provider(notification): return if notification.status == "created": - provider = provider_to_use(SMS_TYPE, notification.international) + provider = provider_to_use(NotificationType.SMS, notification.international) if not provider: technical_failure(notification=notification) return @@ -119,7 +118,7 @@ def send_email_to_provider(notification): technical_failure(notification=notification) return if notification.status == "created": - provider = provider_to_use(EMAIL_TYPE, False) + provider = provider_to_use(NotificationType.EMAIL, False) template_dict = SerialisedTemplate.from_id_and_service_id( template_id=notification.template_id, service_id=service.id, diff --git a/app/models.py b/app/models.py index 077b080a9..92b2871a6 100644 --- a/app/models.py +++ b/app/models.py @@ -29,29 +29,45 @@ from app.utils import ( get_dt_string_or_none, ) -SMS_TYPE = "sms" -EMAIL_TYPE = "email" -LETTER_TYPE = "letter" -TEMPLATE_TYPES = [SMS_TYPE, EMAIL_TYPE] -NOTIFICATION_TYPES = [SMS_TYPE, EMAIL_TYPE] +class TemplateType(Enum): + SMS = "sms" + EMAIL = "email" + + +class NotificationType(Enum): + SMS = "sms" + EMAIL = "email" + LETTER = "letter" -template_types = db.Enum(*TEMPLATE_TYPES, name="template_type") NORMAL = "normal" PRIORITY = "priority" TEMPLATE_PROCESS_TYPE = [NORMAL, PRIORITY] +class TemplateProcessType(Enum): + # TODO: Should Template.process_type be changed to use this? + NORMAL = "normal" + PRIORITY = "priority" SMS_AUTH_TYPE = "sms_auth" EMAIL_AUTH_TYPE = "email_auth" WEBAUTHN_AUTH_TYPE = "webauthn_auth" USER_AUTH_TYPES = [SMS_AUTH_TYPE, EMAIL_AUTH_TYPE, WEBAUTHN_AUTH_TYPE] +class UserAuthType(Enum): + # TODO: Should User.auth_type be changed to use this? + SMS = "sms_auth" + EMAIL = "email_auth" + WEBAUTHN = "webauthn_auth" + DELIVERY_STATUS_CALLBACK_TYPE = "delivery_status" COMPLAINT_CALLBACK_TYPE = "complaint" SERVICE_CALLBACK_TYPES = [DELIVERY_STATUS_CALLBACK_TYPE, COMPLAINT_CALLBACK_TYPE] - +class ServiceCallbackType(Enum): + # TODO: Should ServiceCallbackApi.callback_type be changed to use this? + DELIVERY_STATUS = "delivery_status" + COMPLAINT = "complaint" def filter_null_value_fields(obj): return dict(filter(lambda x: x[1] is not None, obj.items())) @@ -271,6 +287,13 @@ BRANDING_ORG = "org" BRANDING_BOTH = "both" BRANDING_ORG_BANNER = "org_banner" BRANDING_TYPES = [BRANDING_ORG, BRANDING_BOTH, BRANDING_ORG_BANNER] +class BrandingType(Enum): + # TODO: Should EmailBranding.branding_type be changed to use this? + GOVUK = "govuk" # Deprecated outside migrations + ORG = "org" + BOTH = "both" + ORG_BANNER = "org_banner" + class BrandingTypes(db.Model): @@ -326,23 +349,15 @@ service_email_branding = db.Table( ) -INTERNATIONAL_SMS_TYPE = "international_sms" -INBOUND_SMS_TYPE = "inbound_sms" -SCHEDULE_NOTIFICATIONS = "schedule_notifications" -EMAIL_AUTH = "email_auth" -UPLOAD_DOCUMENT = "upload_document" -EDIT_FOLDER_PERMISSIONS = "edit_folder_permissions" - -SERVICE_PERMISSION_TYPES = [ - EMAIL_TYPE, - SMS_TYPE, - INTERNATIONAL_SMS_TYPE, - INBOUND_SMS_TYPE, - SCHEDULE_NOTIFICATIONS, - EMAIL_AUTH, - UPLOAD_DOCUMENT, - EDIT_FOLDER_PERMISSIONS, -] +class ServicePermissionType(Enum): + EMAIL = "email" + SMS = "sms" + INTERNATIONAL_SMS = "international_sms" + INBOUND_SMS = "inbound_sms" + SCHEDULE_NOTIFICATIONS = "schedule_notifications" + EMAIL_AUTH = "email_auth" + UPLOAD_DOCUMENT = "upload_document" + EDIT_FOLDER_PERMISSIONS = "edit_folder_permissions" class ServicePermissionTypes(db.Model): @@ -787,11 +802,14 @@ class ServicePermission(db.Model): ) -MOBILE_TYPE = "mobile" -EMAIL_TYPE = "email" +# MOBILE_TYPE = "mobile" +# EMAIL_TYPE = "email" -GUEST_LIST_RECIPIENT_TYPE = [MOBILE_TYPE, EMAIL_TYPE] -guest_list_recipient_types = db.Enum(*GUEST_LIST_RECIPIENT_TYPE, name="recipient_type") +class GuestListRecipientType(Enum): + MOBILE = "mobile" + EMAIL = "email" + +guest_list_recipient_types = db.Enum(GuestListRecipientType, name="recipient_type") class ServiceGuestList(db.Model): @@ -811,11 +829,11 @@ class ServiceGuestList(db.Model): instance = cls(service_id=service_id, recipient_type=recipient_type) try: - if recipient_type == MOBILE_TYPE: + if recipient_type == GuestListRecipientType.MOBILE: instance.recipient = validate_phone_number( recipient, international=True ) - elif recipient_type == EMAIL_TYPE: + elif recipient_type == GuestListRecipientType.EMAIL: instance.recipient = validate_email_address(recipient) else: raise ValueError("Invalid recipient type") @@ -981,6 +999,12 @@ class ApiKey(db.Model, Versioned): KEY_TYPE_NORMAL = "normal" KEY_TYPE_TEAM = "team" KEY_TYPE_TEST = "test" +class KeyType(Enum): + # TODO: Should Key Types be rewritten to use this? + NORMAL = "normal" + TEAM = "team" + TEST = "test" + class KeyTypes(db.Model): @@ -1076,7 +1100,7 @@ class TemplateBase(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = db.Column(db.String(255), nullable=False) - template_type = db.Column(template_types, nullable=False) + template_type = db.Column(db.Enum(TemplateType, name="template_type"), nullable=False) created_at = db.Column( db.DateTime, nullable=False, default=datetime.datetime.utcnow ) @@ -1131,9 +1155,9 @@ class TemplateBase(db.Model): ) def get_reply_to_text(self): - if self.template_type == EMAIL_TYPE: + if self.template_type == TemplateType.EMAIL: return self.service.get_default_reply_to_email_address() - elif self.template_type == SMS_TYPE: + elif self.template_type == TemplateType.SMS: return try_validate_and_format_phone_number( self.service.get_default_sms_sender() ) @@ -1141,9 +1165,9 @@ class TemplateBase(db.Model): return None def _as_utils_template(self): - if self.template_type == EMAIL_TYPE: + if self.template_type == TemplateType.EMAIL: return PlainTextEmailTemplate(self.__dict__) - if self.template_type == SMS_TYPE: + if self.template_type == TemplateType.SMS: return SMSMessageTemplate(self.__dict__) def _as_utils_template_with_personalisation(self, values): @@ -1160,7 +1184,7 @@ class TemplateBase(db.Model): "created_by": self.created_by.email_address, "version": self.version, "body": self.content, - "subject": self.subject if self.template_type == EMAIL_TYPE else None, + "subject": self.subject if self.template_type == TemplateType.EMAIL else None, "name": self.name, "personalisation": { key: { @@ -1266,8 +1290,9 @@ SES_PROVIDER = "ses" SMS_PROVIDERS = [SNS_PROVIDER] EMAIL_PROVIDERS = [SES_PROVIDER] PROVIDERS = SMS_PROVIDERS + EMAIL_PROVIDERS +# TODO: What about these? -notification_types = db.Enum(*NOTIFICATION_TYPES, name="notification_type") +notification_types = db.Enum(NotificationType, name="notification_type") class ProviderDetails(db.Model): @@ -1330,6 +1355,17 @@ JOB_STATUS_TYPES = [ JOB_STATUS_SENT_TO_DVLA, JOB_STATUS_ERROR, ] +class JobStatusType(Enum): + # TODO: Should Job.job_status be changed to use this? + PENDING = "pending" + IN_PROGRESS = "in progress" + FINISHED = "finished" + SENDING_LIMITS_EXCEEDED = "sending limits exceeded" + SCHEDULED = "scheduled" + CANCELLED = "cancelled" + READY_TO_SEND = "ready to send" + SENT_TO_DVLA = "sent to dvla" + ERROR = "error" class JobStatus(db.Model): @@ -1396,7 +1432,9 @@ class Job(db.Model): archived = db.Column(db.Boolean, nullable=False, default=False) -VERIFY_CODE_TYPES = [EMAIL_TYPE, SMS_TYPE] +class VerifyCodeType(Enum): + EMAIL = "email" + SMS = "sms" class VerifyCode(db.Model): @@ -1409,7 +1447,7 @@ class VerifyCode(db.Model): user = db.relationship("User", backref=db.backref("verify_codes", lazy="dynamic")) _code = db.Column(db.String, nullable=False) code_type = db.Column( - db.Enum(*VERIFY_CODE_TYPES, name="verify_code_types"), + db.Enum(VerifyCodeType, name="verify_code_types"), index=False, unique=False, nullable=False, @@ -1812,8 +1850,8 @@ class Notification(db.Model): serialized = { "id": self.id, "reference": self.client_reference, - "email_address": self.to if self.notification_type == EMAIL_TYPE else None, - "phone_number": self.to if self.notification_type == SMS_TYPE else None, + "email_address": self.to if self.notification_type == NotificationType.EMAIL else None, + "phone_number": self.to if self.notification_type == NotificationType.SMS else None, "line_1": None, "line_2": None, "line_3": None, diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 1ace61df4..8d02795f1 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -17,17 +17,16 @@ from app.dao.notifications_dao import ( dao_delete_notifications_by_id, ) from app.models import ( - EMAIL_TYPE, KEY_TYPE_TEST, NOTIFICATION_CREATED, - SMS_TYPE, Notification, + NotificationType, ) from app.v2.errors import BadRequestError def create_content_for_notification(template, personalisation): - if template.template_type == EMAIL_TYPE: + if template.template_type == NotificationType.EMAIL: template_object = PlainTextEmailTemplate( { "content": template.content, @@ -36,7 +35,7 @@ def create_content_for_notification(template, personalisation): }, personalisation, ) - if template.template_type == SMS_TYPE: + if template.template_type == NotificationType.SMS: template_object = SMSMessageTemplate( { "content": template.content, @@ -113,7 +112,7 @@ def persist_notification( updated_at=updated_at, ) - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: formatted_recipient = validate_and_format_phone_number( recipient, international=True ) @@ -122,8 +121,8 @@ def persist_notification( notification.international = recipient_info.international notification.phone_prefix = recipient_info.country_prefix notification.rate_multiplier = recipient_info.billable_units - elif notification_type == EMAIL_TYPE: - current_app.logger.info(f"Persisting notification with type: {EMAIL_TYPE}") + elif notification_type == NotificationType.EMAIL: + current_app.logger.info(f"Persisting notification with type: {NotificationType.EMAIL}") redis_store.set( f"email-address-{notification.id}", format_email_address(notification.to), @@ -153,11 +152,11 @@ def send_notification_to_queue_detached( if key_type == KEY_TYPE_TEST: print("send_notification_to_queue_detached key is test key") - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: if not queue: queue = QueueNames.SEND_SMS deliver_task = provider_tasks.deliver_sms - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: if not queue: queue = QueueNames.SEND_EMAIL deliver_task = provider_tasks.deliver_email @@ -183,7 +182,7 @@ def send_notification_to_queue(notification, queue=None): def simulated_recipient(to_address, notification_type): - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: formatted_simulated_numbers = [ validate_and_format_phone_number(number) for number in current_app.config["SIMULATED_SMS_NUMBERS"] diff --git a/app/notifications/receive_notifications.py b/app/notifications/receive_notifications.py index 2d9a064ab..04e652ebd 100644 --- a/app/notifications/receive_notifications.py +++ b/app/notifications/receive_notifications.py @@ -6,7 +6,7 @@ from app.config import QueueNames from app.dao.inbound_sms_dao import dao_create_inbound_sms from app.dao.services_dao import dao_fetch_service_by_inbound_number from app.errors import InvalidRequest, register_errors -from app.models import INBOUND_SMS_TYPE, SMS_TYPE, InboundSms +from app.models import InboundSms, ServicePermissionType from app.notifications.sns_handlers import sns_notification_handler receive_notifications_blueprint = Blueprint("receive_notifications", __name__) @@ -125,4 +125,4 @@ def fetch_potential_service(inbound_number, provider_name): def has_inbound_sms_permissions(permissions): str_permissions = [p.permission for p in permissions] - return set([INBOUND_SMS_TYPE, SMS_TYPE]).issubset(set(str_permissions)) + return {ServicePermissionType.INBOUND_SMS, ServicePermissionType.SMS}.issubset(set(str_permissions)) diff --git a/app/notifications/rest.py b/app/notifications/rest.py index 8dd18044c..b1ea4e837 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -5,7 +5,7 @@ from app import api_user, authenticated_service from app.config import QueueNames from app.dao import notifications_dao from app.errors import InvalidRequest, register_errors -from app.models import EMAIL_TYPE, KEY_TYPE_TEAM, PRIORITY, SMS_TYPE +from app.models import KEY_TYPE_TEAM, PRIORITY, NotificationType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -84,13 +84,13 @@ def get_all_notifications(): @notifications.route("/notifications/", methods=["POST"]) def send_notification(notification_type): - if notification_type not in [SMS_TYPE, EMAIL_TYPE]: - msg = "{} notification type is not supported".format(notification_type) + if notification_type not in {NotificationType.SMS, NotificationType.EMAIL}: + msg = f"{notification_type} notification type is not supported" raise InvalidRequest(msg, 400) notification_form = ( sms_template_notification_schema - if notification_type == SMS_TYPE + if notification_type == NotificationType.SMS else email_notification_schema ).load(request.get_json()) @@ -116,7 +116,7 @@ def send_notification(notification_type): status_code=400, ) - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: check_if_service_can_send_to_number( authenticated_service, notification_form["to"] ) @@ -191,7 +191,7 @@ def create_template_object_for_notification(template, personalisation): raise InvalidRequest(errors, status_code=400) if ( - template_object.template_type == SMS_TYPE + template_object.template_type == NotificationType.SMS and template_object.is_message_too_long() ): message = "Content has a character count greater than the limit of {}".format( diff --git a/app/notifications/validators.py b/app/notifications/validators.py index bb3e08c5d..6e65f6658 100644 --- a/app/notifications/validators.py +++ b/app/notifications/validators.py @@ -16,12 +16,12 @@ from app.dao.notifications_dao import dao_get_notification_count_for_service from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.models import ( - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, KEY_TYPE_TEAM, KEY_TYPE_TEST, - SMS_TYPE, + NotificationType, ServicePermission, + ServicePermissionType, + TemplateType, ) from app.notifications.process_notifications import create_content_for_notification from app.serialised_models import SerialisedTemplate @@ -151,13 +151,13 @@ def validate_and_format_recipient( send_to, key_type, service, allow_guest_list_recipients ) - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: international_phone_info = check_if_service_can_send_to_number(service, send_to) return validate_and_format_phone_number( number=send_to, international=international_phone_info.international ) - elif notification_type == EMAIL_TYPE: + elif notification_type == NotificationType.EMAIL: return validate_and_format_email_address(email_address=send_to) @@ -171,7 +171,7 @@ def check_if_service_can_send_to_number(service, number): if ( international_phone_info.international - and INTERNATIONAL_SMS_TYPE not in permissions + and ServicePermissionType.INTERNATIONAL_SMS not in permissions ): raise BadRequestError(message="Cannot send to international mobile numbers") else: @@ -181,12 +181,12 @@ def check_if_service_can_send_to_number(service, number): def check_is_message_too_long(template_with_content): if template_with_content.is_message_too_long(): message = "Your message is too long. " - if template_with_content.template_type == SMS_TYPE: + if template_with_content.template_type == TemplateType.SMS: message += ( f"Text messages cannot be longer than {SMS_CHAR_COUNT_LIMIT} characters. " f"Your message is {template_with_content.content_count_without_prefix} characters long." ) - elif template_with_content.template_type == EMAIL_TYPE: + elif template_with_content.template_type == TemplateType.EMAIL: message += ( f"Emails cannot be longer than 2000000 bytes. " f"Your message is {template_with_content.content_size_in_bytes} bytes." @@ -226,9 +226,9 @@ def validate_template( def check_reply_to(service_id, reply_to_id, type_): - if type_ == EMAIL_TYPE: + if type_ == NotificationType.EMAIL: return check_service_email_reply_to_id(service_id, reply_to_id, type_) - elif type_ == SMS_TYPE: + elif type_ == NotificationType.SMS: return check_service_sms_sender_id(service_id, reply_to_id, type_) diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index ed06ad0ac..69587ea3e 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -13,7 +13,7 @@ from app.dao.invited_org_user_dao import ( ) from app.dao.templates_dao import dao_get_template_by_id from app.errors import InvalidRequest, register_errors -from app.models import EMAIL_TYPE, KEY_TYPE_NORMAL, InvitedOrganizationUser +from app.models import KEY_TYPE_NORMAL, InvitedOrganizationUser, NotificationType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -64,7 +64,7 @@ def invite_user_to_org(organization_id): data.get("invite_link_host"), ), }, - notification_type=EMAIL_TYPE, + notification_type=NotificationType.EMAIL, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=invited_org_user.invited_by.email_address, diff --git a/app/schemas.py b/app/schemas.py index 3c60d7a07..5ac1af48b 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -382,7 +382,7 @@ class TemplateSchema(BaseTemplateSchema, UUIDsAsStringsMixin): @validates_schema def validate_type(self, data, **kwargs): - if data.get("template_type") == models.EMAIL_TYPE: + if data.get("template_type") == models.TemplateType.EMAIL: subject = data.get("subject") if not subject or subject.strip() == "": raise ValidationError("Invalid template subject", "subject") @@ -628,7 +628,7 @@ class NotificationWithPersonalisationSchema(NotificationWithTemplateSchema): in_data["template"], in_data["personalisation"] ) in_data["body"] = template.content_with_placeholders_filled_in - if in_data["template"]["template_type"] != models.SMS_TYPE: + if in_data["template"]["template_type"] != models.TemplateType.SMS: in_data["subject"] = template.subject in_data["content_char_count"] = None else: diff --git a/app/service/rest.py b/app/service/rest.py index bc52d49d9..ec7002990 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -633,7 +633,7 @@ def get_detailed_services( @service_blueprint.route("//guest-list", methods=["GET"]) def get_guest_list(service_id): - from app.models import EMAIL_TYPE, MOBILE_TYPE + from app.models import GuestListRecipientType service = dao_fetch_service_by_id(service_id) @@ -643,10 +643,10 @@ def get_guest_list(service_id): guest_list = dao_fetch_service_guest_list(service.id) return jsonify( email_addresses=[ - item.recipient for item in guest_list if item.recipient_type == EMAIL_TYPE + item.recipient for item in guest_list if item.recipient_type == GuestListRecipientType.EMAIL ], phone_numbers=[ - item.recipient for item in guest_list if item.recipient_type == MOBILE_TYPE + item.recipient for item in guest_list if item.recipient_type == GuestListRecipientType.MOBILE ], ) diff --git a/app/service/send_notification.py b/app/service/send_notification.py index f01056fee..f1da3ac67 100644 --- a/app/service/send_notification.py +++ b/app/service/send_notification.py @@ -6,7 +6,7 @@ from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id_and_service_id from app.dao.users_dao import get_user_by_id -from app.models import EMAIL_TYPE, KEY_TYPE_NORMAL, PRIORITY, SMS_TYPE +from app.models import KEY_TYPE_NORMAL, PRIORITY, NotificationType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -94,10 +94,10 @@ def get_reply_to_text(notification_type, sender_id, service, template): reply_to = None if sender_id: try: - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: message = "Reply to email address not found" reply_to = dao_get_reply_to_by_id(service.id, sender_id).email_address - elif notification_type == SMS_TYPE: + elif notification_type == NotificationType.SMS: message = "SMS sender not found" reply_to = dao_get_service_sms_senders_by_id( service.id, sender_id diff --git a/app/service/sender.py b/app/service/sender.py index e1dbc7296..ed89d71d5 100644 --- a/app/service/sender.py +++ b/app/service/sender.py @@ -6,7 +6,7 @@ from app.dao.services_dao import ( dao_fetch_service_by_id, ) from app.dao.templates_dao import dao_get_template_by_id -from app.models import EMAIL_TYPE, KEY_TYPE_NORMAL +from app.models import KEY_TYPE_NORMAL, TemplateType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -29,7 +29,7 @@ def send_notification_to_service_users( template_id=template.id, template_version=template.version, recipient=user.email_address - if template.template_type == EMAIL_TYPE + if template.template_type == TemplateType.EMAIL else user.mobile_number, service=notify_service, personalisation=personalisation, diff --git a/app/service/utils.py b/app/service/utils.py index 7aecb4eef..c323bdc06 100644 --- a/app/service/utils.py +++ b/app/service/utils.py @@ -4,11 +4,10 @@ from notifications_utils.recipients import allowed_to_send_to from app.dao.services_dao import dao_fetch_service_by_id from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - MOBILE_TYPE, + GuestListRecipientType, ServiceGuestList, ) @@ -21,8 +20,8 @@ def get_guest_list_objects(service_id, request_json): return [ ServiceGuestList.from_string(service_id, type, recipient) for type, recipient in ( - get_recipients_from_request(request_json, "phone_numbers", MOBILE_TYPE) - + get_recipients_from_request(request_json, "email_addresses", EMAIL_TYPE) + get_recipients_from_request(request_json, "phone_numbers", GuestListRecipientType.MOBILE) + + get_recipients_from_request(request_json, "email_addresses", GuestListRecipientType.EMAIL) ) ] diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index e18d158ac..f7e32339d 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -15,7 +15,7 @@ from app.dao.invited_user_dao import ( ) from app.dao.templates_dao import dao_get_template_by_id from app.errors import InvalidRequest, register_errors -from app.models import EMAIL_TYPE, INVITE_PENDING, KEY_TYPE_NORMAL, Service +from app.models import INVITE_PENDING, KEY_TYPE_NORMAL, NotificationType, Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -44,7 +44,7 @@ def _create_service_invite(invited_user, invite_link_host): "service_name": invited_user.service.name, "url": invited_user_url(invited_user.id, invite_link_host), }, - notification_type=EMAIL_TYPE, + notification_type=NotificationType.EMAIL, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=invited_user.from_user.email_address, diff --git a/app/template/rest.py b/app/template/rest.py index 06523c4ae..9ef8e65a7 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -14,7 +14,7 @@ from app.dao.templates_dao import ( dao_update_template, ) from app.errors import InvalidRequest, register_errors -from app.models import SMS_TYPE, Template +from app.models import Template, TemplateType from app.notifications.validators import check_reply_to, service_has_permission from app.schema_validation import validate from app.schemas import ( @@ -36,7 +36,7 @@ register_errors(template_blueprint) def _content_count_greater_than_limit(content, template_type): - if template_type == SMS_TYPE: + if template_type == TemplateType.SMS: template = SMSMessageTemplate( {"content": content, "template_type": template_type} ) diff --git a/app/user/rest.py b/app/user/rest.py index c7fa8055a..3192613e6 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -33,7 +33,7 @@ from app.dao.users_dao import ( use_user_code, ) from app.errors import InvalidRequest, register_errors -from app.models import EMAIL_TYPE, KEY_TYPE_NORMAL, SMS_TYPE, Permission, Service +from app.models import KEY_TYPE_NORMAL, Notification, NotificationType, Permission, Service, TemplateType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -276,10 +276,10 @@ def send_user_2fa_code(user_id, code_type): ) else: data = request.get_json() - if code_type == SMS_TYPE: + if NotificationType(code_type) == NotificationType.SMS: validate(data, post_send_user_sms_code_schema) send_user_sms_code(user_to_send_to, data) - elif code_type == EMAIL_TYPE: + elif NotificationType(code_type) == NotificationType.EMAIL: validate(data, post_send_user_email_code_schema) send_user_email_code(user_to_send_to, data) else: @@ -334,9 +334,9 @@ def create_2fa_code( # save the code in the VerifyCode table create_user_code(user_to_send_to, secret_code, template.template_type) reply_to = None - if template.template_type == SMS_TYPE: + if template.template_type == TemplateType.SMS: reply_to = get_sms_reply_to_for_notify_service(recipient, template) - elif template.template_type == EMAIL_TYPE: + elif template.template_type == TemplateType.EMAIL: reply_to = template.service.get_default_reply_to_email_address() saved_notification = persist_notification( diff --git a/app/utils.py b/app/utils.py index bcd8c864b..e922a34f4 100644 --- a/app/utils.py +++ b/app/utils.py @@ -41,11 +41,11 @@ def url_with_token(data, url, config, base_url=None): def get_template_instance(template, values): - from app.models import EMAIL_TYPE, SMS_TYPE + from app.models import TemplateType return { - SMS_TYPE: SMSMessageTemplate, - EMAIL_TYPE: HTMLEmailTemplate, + TemplateType.SMS: SMSMessageTemplate, + TemplateType.EMAIL: HTMLEmailTemplate, }[ template["template_type"] ](template, values) @@ -80,10 +80,10 @@ def get_month_from_utc_column(column): def get_public_notify_type_text(notify_type, plural=False): - from app.models import SMS_TYPE, UPLOAD_DOCUMENT + from app.models import NotificationType, UPLOAD_DOCUMENT notify_type_text = notify_type - if notify_type == SMS_TYPE: + if notify_type == NotificationType.SMS: notify_type_text = "text message" elif notify_type == UPLOAD_DOCUMENT: notify_type_text = "document" diff --git a/app/v2/notifications/post_notifications.py b/app/v2/notifications/post_notifications.py index 4f70c5410..1af6e1068 100644 --- a/app/v2/notifications/post_notifications.py +++ b/app/v2/notifications/post_notifications.py @@ -11,12 +11,11 @@ from app.celery.tasks import save_api_email, save_api_sms from app.clients.document_download import DocumentDownloadError from app.config import QueueNames from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, NOTIFICATION_CREATED, PRIORITY, - SMS_TYPE, Notification, + NotificationType, ) from app.notifications.process_notifications import ( persist_notification, @@ -52,9 +51,9 @@ from app.v2.utils import get_valid_json def post_notification(notification_type): request_json = get_valid_json() - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: form = validate(request_json, post_email_request) - elif notification_type == SMS_TYPE: + elif notification_type == NotificationType.SMS: form = validate(request_json, post_sms_request) else: abort(404) @@ -99,7 +98,7 @@ def process_sms_or_email_notification( notification_id = uuid.uuid4() form_send_to = ( form["email_address"] - if notification_type == EMAIL_TYPE + if notification_type == NotificationType.EMAIL else form["phone_number"] ) @@ -137,7 +136,7 @@ def process_sms_or_email_notification( if ( service.high_volume and api_user.key_type == KEY_TYPE_NORMAL - and notification_type in [EMAIL_TYPE, SMS_TYPE] + and notification_type in {NotificationType.EMAIL, NotificationType.SMS} ): # Put service with high volumes of notifications onto a queue # To take the pressure off the db for API requests put the notification for our high volume service onto a queue @@ -215,7 +214,7 @@ def save_email_or_sms_to_queue( "template_id": str(template.id), "template_version": template.version, "to": form["email_address"] - if notification_type == EMAIL_TYPE + if notification_type == NotificationType.EMAIL else form["phone_number"], "service_id": str(service_id), "personalisation": personalisation, @@ -230,9 +229,9 @@ def save_email_or_sms_to_queue( } encrypted = encryption.encrypt(data) - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: save_api_email.apply_async([encrypted], queue=QueueNames.SAVE_API_EMAIL) - elif notification_type == SMS_TYPE: + elif notification_type == NotificationType.SMS: save_api_sms.apply_async([encrypted], queue=QueueNames.SAVE_API_SMS) return Notification(**data) @@ -278,7 +277,7 @@ def process_document_uploads(personalisation_data, service, simulated=False): def get_reply_to_text(notification_type, form, template): reply_to = None - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: service_email_reply_to_id = form.get("email_reply_to_id", None) reply_to = ( check_service_email_reply_to_id( @@ -289,7 +288,7 @@ def get_reply_to_text(notification_type, form, template): or template.reply_to_text ) - elif notification_type == SMS_TYPE: + elif notification_type == NotificationType.SMS: service_sms_sender_id = form.get("sms_sender_id", None) sms_sender_id = check_service_sms_sender_id( str(authenticated_service.id), service_sms_sender_id, notification_type @@ -312,12 +311,12 @@ def create_response_for_post_notification( reply_to, template_with_content, ): - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: create_resp_partial = functools.partial( create_post_sms_response_from_notification, from_number=reply_to, ) - elif notification_type == EMAIL_TYPE: + elif notification_type == NotificationType.EMAIL: create_resp_partial = functools.partial( create_post_email_response_from_notification, subject=template_with_content.subject, From 43f18eed6a1cb03ddb961711d7523974f475e86d Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 28 Feb 2024 12:41:57 -0500 Subject: [PATCH 132/259] More changes for enums. Signed-off-by: Cliff Hill --- app/celery/nightly_tasks.py | 2 +- app/celery/scheduled_tasks.py | 2 +- app/commands.py | 2 +- app/dao/fact_billing_dao.py | 6 ++- app/dao/inbound_sms_dao.py | 2 +- app/dao/notifications_dao.py | 10 +++-- app/dao/provider_details_dao.py | 2 +- app/dao/uploads_dao.py | 2 +- app/models.py | 41 ++++++++++++++----- app/notifications/process_notifications.py | 4 +- app/notifications/receive_notifications.py | 4 +- app/service/rest.py | 8 +++- app/service/utils.py | 8 +++- app/user/rest.py | 8 +++- app/utils.py | 6 +-- tests/app/celery/test_nightly_tasks.py | 6 +-- tests/app/celery/test_reporting_tasks.py | 23 +++++------ tests/app/celery/test_tasks.py | 28 ++++++------- tests/app/conftest.py | 27 ++++++------ .../dao/test_fact_notification_status_dao.py | 25 ++++++----- tests/app/dao/test_service_guest_list_dao.py | 8 ++-- tests/app/db.py | 23 ++++++----- tests/app/test_commands.py | 4 +- tests/app/test_model.py | 17 ++++---- 24 files changed, 157 insertions(+), 111 deletions(-) diff --git a/app/celery/nightly_tasks.py b/app/celery/nightly_tasks.py index 678c9bfa1..f3ee22bf6 100644 --- a/app/celery/nightly_tasks.py +++ b/app/celery/nightly_tasks.py @@ -25,7 +25,7 @@ from app.dao.notifications_dao import ( from app.dao.service_data_retention_dao import ( fetch_service_data_retention_for_all_services_by_notification_type, ) -from app.models import NotificationType, FactProcessingTime +from app.models import FactProcessingTime, NotificationType from app.utils import get_midnight_in_utc diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index 6ec3370cb..314e1f310 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -38,8 +38,8 @@ from app.models import ( JOB_STATUS_ERROR, JOB_STATUS_IN_PROGRESS, JOB_STATUS_PENDING, - NotificationType, Job, + NotificationType, ) from app.notifications.process_notifications import send_notification_to_queue diff --git a/app/commands.py b/app/commands.py index 26c48c45b..4d084e845 100644 --- a/app/commands.py +++ b/app/commands.py @@ -52,11 +52,11 @@ from app.dao.users_dao import ( from app.models import ( KEY_TYPE_TEST, NOTIFICATION_CREATED, - NotificationType, AnnualBilling, Domain, EmailBranding, Notification, + NotificationType, Organization, Service, Template, diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index b2f53dbc2..06b537643 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -13,11 +13,11 @@ from app.models import ( KEY_TYPE_TEAM, NOTIFICATION_STATUS_TYPES_BILLABLE_SMS, NOTIFICATION_STATUS_TYPES_SENT_EMAILS, - NotificationType, AnnualBilling, FactBilling, NotificationAllTimeView, NotificationHistory, + NotificationType, Organization, Rate, Service, @@ -483,7 +483,9 @@ def get_service_ids_that_need_billing_populated(start_date, end_date): .filter( NotificationHistory.created_at >= start_date, NotificationHistory.created_at <= end_date, - NotificationHistory.notification_type.in_([NotificationType.SMS, NotificationType.EMAIL]), + NotificationHistory.notification_type.in_( + [NotificationType.SMS, NotificationType.EMAIL] + ), NotificationHistory.billable_units != 0, ) .distinct() diff --git a/app/dao/inbound_sms_dao.py b/app/dao/inbound_sms_dao.py index 2b5767caa..5143746d9 100644 --- a/app/dao/inbound_sms_dao.py +++ b/app/dao/inbound_sms_dao.py @@ -6,9 +6,9 @@ from sqlalchemy.orm import aliased from app import db from app.dao.dao_utils import autocommit from app.models import ( - NotificationType, InboundSms, InboundSmsHistory, + NotificationType, Service, ServiceDataRetention, ) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index c903de270..42ac8671c 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -26,10 +26,10 @@ from app.models import ( NOTIFICATION_SENDING, NOTIFICATION_SENT, NOTIFICATION_TEMPORARY_FAILURE, - NotificationType, FactNotificationStatus, Notification, NotificationHistory, + NotificationType, ) from app.utils import ( escape_special_characters, @@ -444,7 +444,9 @@ def dao_timeout_notifications(cutoff_time, limit=100000): Notification.query.filter( Notification.created_at < cutoff_time, Notification.status.in_(current_statuses), - Notification.notification_type.in_([NotificationType.SMS, NotificationType.EMAIL]), + Notification.notification_type.in_( + [NotificationType.SMS, NotificationType.EMAIL] + ), ) .limit(limit) .all() @@ -506,7 +508,9 @@ def dao_get_notifications_by_recipient_or_reference( normalised = "".join(search_term.split()).lower() else: - raise TypeError(f"Notification type must be {NotificationType.EMAIL}, {NotificationType.SMS}, or None") + raise TypeError( + f"Notification type must be {NotificationType.EMAIL}, {NotificationType.SMS}, or None" + ) normalised = escape_special_characters(normalised) search_term = escape_special_characters(search_term) diff --git a/app/dao/provider_details_dao.py b/app/dao/provider_details_dao.py index 3497e8398..270f8731e 100644 --- a/app/dao/provider_details_dao.py +++ b/app/dao/provider_details_dao.py @@ -6,8 +6,8 @@ from sqlalchemy import asc, desc, func from app import db from app.dao.dao_utils import autocommit from app.models import ( - NotificationType, FactBilling, + NotificationType, ProviderDetails, ProviderDetailsHistory, User, diff --git a/app/dao/uploads_dao.py b/app/dao/uploads_dao.py index 6ef166edd..200d41805 100644 --- a/app/dao/uploads_dao.py +++ b/app/dao/uploads_dao.py @@ -8,10 +8,10 @@ from app import db from app.models import ( JOB_STATUS_CANCELLED, JOB_STATUS_SCHEDULED, - NotificationType, NOTIFICATION_CANCELLED, Job, Notification, + NotificationType, ServiceDataRetention, Template, ) diff --git a/app/models.py b/app/models.py index 92b2871a6..29fde4ffd 100644 --- a/app/models.py +++ b/app/models.py @@ -44,6 +44,8 @@ class NotificationType(Enum): NORMAL = "normal" PRIORITY = "priority" TEMPLATE_PROCESS_TYPE = [NORMAL, PRIORITY] + + class TemplateProcessType(Enum): # TODO: Should Template.process_type be changed to use this? NORMAL = "normal" @@ -54,6 +56,8 @@ SMS_AUTH_TYPE = "sms_auth" EMAIL_AUTH_TYPE = "email_auth" WEBAUTHN_AUTH_TYPE = "webauthn_auth" USER_AUTH_TYPES = [SMS_AUTH_TYPE, EMAIL_AUTH_TYPE, WEBAUTHN_AUTH_TYPE] + + class UserAuthType(Enum): # TODO: Should User.auth_type be changed to use this? SMS = "sms_auth" @@ -64,10 +68,13 @@ class UserAuthType(Enum): DELIVERY_STATUS_CALLBACK_TYPE = "delivery_status" COMPLAINT_CALLBACK_TYPE = "complaint" SERVICE_CALLBACK_TYPES = [DELIVERY_STATUS_CALLBACK_TYPE, COMPLAINT_CALLBACK_TYPE] -class ServiceCallbackType(Enum): - # TODO: Should ServiceCallbackApi.callback_type be changed to use this? - DELIVERY_STATUS = "delivery_status" - COMPLAINT = "complaint" + + +# class ServiceCallbackType(Enum): +# # TODO: Should ServiceCallbackApi.callback_type be changed to use this? +# DELIVERY_STATUS = "delivery_status" +# COMPLAINT = "complaint" + def filter_null_value_fields(obj): return dict(filter(lambda x: x[1] is not None, obj.items())) @@ -287,6 +294,8 @@ BRANDING_ORG = "org" BRANDING_BOTH = "both" BRANDING_ORG_BANNER = "org_banner" BRANDING_TYPES = [BRANDING_ORG, BRANDING_BOTH, BRANDING_ORG_BANNER] + + class BrandingType(Enum): # TODO: Should EmailBranding.branding_type be changed to use this? GOVUK = "govuk" # Deprecated outside migrations @@ -295,7 +304,6 @@ class BrandingType(Enum): ORG_BANNER = "org_banner" - class BrandingTypes(db.Model): __tablename__ = "branding_type" name = db.Column(db.String(255), primary_key=True) @@ -805,10 +813,12 @@ class ServicePermission(db.Model): # MOBILE_TYPE = "mobile" # EMAIL_TYPE = "email" + class GuestListRecipientType(Enum): MOBILE = "mobile" EMAIL = "email" + guest_list_recipient_types = db.Enum(GuestListRecipientType, name="recipient_type") @@ -999,6 +1009,8 @@ class ApiKey(db.Model, Versioned): KEY_TYPE_NORMAL = "normal" KEY_TYPE_TEAM = "team" KEY_TYPE_TEST = "test" + + class KeyType(Enum): # TODO: Should Key Types be rewritten to use this? NORMAL = "normal" @@ -1006,7 +1018,6 @@ class KeyType(Enum): TEST = "test" - class KeyTypes(db.Model): __tablename__ = "key_types" @@ -1100,7 +1111,9 @@ class TemplateBase(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = db.Column(db.String(255), nullable=False) - template_type = db.Column(db.Enum(TemplateType, name="template_type"), nullable=False) + template_type = db.Column( + db.Enum(TemplateType, name="template_type"), nullable=False + ) created_at = db.Column( db.DateTime, nullable=False, default=datetime.datetime.utcnow ) @@ -1184,7 +1197,9 @@ class TemplateBase(db.Model): "created_by": self.created_by.email_address, "version": self.version, "body": self.content, - "subject": self.subject if self.template_type == TemplateType.EMAIL else None, + "subject": self.subject + if self.template_type == TemplateType.EMAIL + else None, "name": self.name, "personalisation": { key: { @@ -1355,6 +1370,8 @@ JOB_STATUS_TYPES = [ JOB_STATUS_SENT_TO_DVLA, JOB_STATUS_ERROR, ] + + class JobStatusType(Enum): # TODO: Should Job.job_status be changed to use this? PENDING = "pending" @@ -1850,8 +1867,12 @@ class Notification(db.Model): serialized = { "id": self.id, "reference": self.client_reference, - "email_address": self.to if self.notification_type == NotificationType.EMAIL else None, - "phone_number": self.to if self.notification_type == NotificationType.SMS else None, + "email_address": self.to + if self.notification_type == NotificationType.EMAIL + else None, + "phone_number": self.to + if self.notification_type == NotificationType.SMS + else None, "line_1": None, "line_2": None, "line_3": None, diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 8d02795f1..24a24bc8d 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -122,7 +122,9 @@ def persist_notification( notification.phone_prefix = recipient_info.country_prefix notification.rate_multiplier = recipient_info.billable_units elif notification_type == NotificationType.EMAIL: - current_app.logger.info(f"Persisting notification with type: {NotificationType.EMAIL}") + current_app.logger.info( + f"Persisting notification with type: {NotificationType.EMAIL}" + ) redis_store.set( f"email-address-{notification.id}", format_email_address(notification.to), diff --git a/app/notifications/receive_notifications.py b/app/notifications/receive_notifications.py index 04e652ebd..c1031417b 100644 --- a/app/notifications/receive_notifications.py +++ b/app/notifications/receive_notifications.py @@ -125,4 +125,6 @@ def fetch_potential_service(inbound_number, provider_name): def has_inbound_sms_permissions(permissions): str_permissions = [p.permission for p in permissions] - return {ServicePermissionType.INBOUND_SMS, ServicePermissionType.SMS}.issubset(set(str_permissions)) + return {ServicePermissionType.INBOUND_SMS, ServicePermissionType.SMS}.issubset( + set(str_permissions) + ) diff --git a/app/service/rest.py b/app/service/rest.py index ec7002990..355978b5d 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -643,10 +643,14 @@ def get_guest_list(service_id): guest_list = dao_fetch_service_guest_list(service.id) return jsonify( email_addresses=[ - item.recipient for item in guest_list if item.recipient_type == GuestListRecipientType.EMAIL + item.recipient + for item in guest_list + if item.recipient_type == GuestListRecipientType.EMAIL ], phone_numbers=[ - item.recipient for item in guest_list if item.recipient_type == GuestListRecipientType.MOBILE + item.recipient + for item in guest_list + if item.recipient_type == GuestListRecipientType.MOBILE ], ) diff --git a/app/service/utils.py b/app/service/utils.py index c323bdc06..3b21b90ee 100644 --- a/app/service/utils.py +++ b/app/service/utils.py @@ -20,8 +20,12 @@ def get_guest_list_objects(service_id, request_json): return [ ServiceGuestList.from_string(service_id, type, recipient) for type, recipient in ( - get_recipients_from_request(request_json, "phone_numbers", GuestListRecipientType.MOBILE) - + get_recipients_from_request(request_json, "email_addresses", GuestListRecipientType.EMAIL) + get_recipients_from_request( + request_json, "phone_numbers", GuestListRecipientType.MOBILE + ) + + get_recipients_from_request( + request_json, "email_addresses", GuestListRecipientType.EMAIL + ) ) ] diff --git a/app/user/rest.py b/app/user/rest.py index 3192613e6..278eaf396 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -33,7 +33,13 @@ from app.dao.users_dao import ( use_user_code, ) from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_NORMAL, Notification, NotificationType, Permission, Service, TemplateType +from app.models import ( + KEY_TYPE_NORMAL, + NotificationType, + Permission, + Service, + TemplateType, +) from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/utils.py b/app/utils.py index e922a34f4..eea303250 100644 --- a/app/utils.py +++ b/app/utils.py @@ -46,9 +46,7 @@ def get_template_instance(template, values): return { TemplateType.SMS: SMSMessageTemplate, TemplateType.EMAIL: HTMLEmailTemplate, - }[ - template["template_type"] - ](template, values) + }[template["template_type"]](template, values) def get_midnight_in_utc(date): @@ -80,7 +78,7 @@ def get_month_from_utc_column(column): def get_public_notify_type_text(notify_type, plural=False): - from app.models import NotificationType, UPLOAD_DOCUMENT + from app.models import UPLOAD_DOCUMENT, NotificationType notify_type_text = notify_type if notify_type == NotificationType.SMS: diff --git a/tests/app/celery/test_nightly_tasks.py b/tests/app/celery/test_nightly_tasks.py index 63f4b22fd..9caef3532 100644 --- a/tests/app/celery/test_nightly_tasks.py +++ b/tests/app/celery/test_nightly_tasks.py @@ -17,7 +17,7 @@ from app.celery.nightly_tasks import ( save_daily_notification_processing_time, timeout_notifications, ) -from app.models import EMAIL_TYPE, SMS_TYPE, FactProcessingTime, Job +from app.models import FactProcessingTime, Job, NotificationType from tests.app.db import ( create_job, create_notification, @@ -95,10 +95,10 @@ def test_will_remove_csv_files_for_jobs_older_than_retention_period( service_1 = create_service(service_name="service 1") service_2 = create_service(service_name="service 2") create_service_data_retention( - service=service_1, notification_type=SMS_TYPE, days_of_retention=3 + service=service_1, notification_type=NotificationType.SMS, days_of_retention=3 ) create_service_data_retention( - service=service_2, notification_type=EMAIL_TYPE, days_of_retention=30 + service=service_2, notification_type=NotificationType.EMAIL, days_of_retention=30 ) sms_template_service_1 = create_template(service=service_1) email_template_service_1 = create_template(service=service_1, template_type="email") diff --git a/tests/app/celery/test_reporting_tasks.py b/tests/app/celery/test_reporting_tasks.py index 2dee56c27..70eb44b8b 100644 --- a/tests/app/celery/test_reporting_tasks.py +++ b/tests/app/celery/test_reporting_tasks.py @@ -14,15 +14,14 @@ from app.celery.reporting_tasks import ( from app.config import QueueNames from app.dao.fact_billing_dao import get_rate from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, NOTIFICATION_TYPES, - SMS_TYPE, FactBilling, FactNotificationStatus, Notification, + NotificationType, ) from tests.app.db import ( create_notification, @@ -36,9 +35,9 @@ from tests.app.db import ( def mocker_get_rate( non_letter_rates, notification_type, local_date, rate_multiplier=None ): - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: return Decimal(1.33) - elif notification_type == EMAIL_TYPE: + elif notification_type == NotificationType.EMAIL: return Decimal(0) @@ -83,7 +82,7 @@ def test_create_nightly_notification_status_triggers_tasks( kwargs={ "service_id": sample_service.id, "process_day": "2019-07-31", - "notification_type": SMS_TYPE, + "notification_type": NotificationType.SMS, }, queue=QueueNames.REPORTING, ) @@ -94,8 +93,8 @@ def test_create_nightly_notification_status_triggers_tasks( "notification_date, expected_types_aggregated", [ ("2019-08-01", set()), - ("2019-07-31", {EMAIL_TYPE, SMS_TYPE}), - ("2019-07-28", {EMAIL_TYPE, SMS_TYPE}), + ("2019-07-31", {NotificationType.EMAIL, NotificationType.SMS}), + ("2019-07-28", {NotificationType.EMAIL, NotificationType.SMS}), ("2019-07-21", set()), ], ) @@ -148,7 +147,7 @@ def test_create_nightly_billing_for_day_checks_history( assert len(records) == 1 record = records[0] - assert record.notification_type == SMS_TYPE + assert record.notification_type == NotificationType.SMS assert record.notifications_sent == 2 @@ -321,14 +320,14 @@ def test_create_nightly_billing_for_day_null_sent_by_sms( def test_get_rate_for_sms_and_email(notify_db_session): non_letter_rates = [ - create_rate(datetime(2017, 12, 1), 0.15, SMS_TYPE), - create_rate(datetime(2017, 12, 1), 0, EMAIL_TYPE), + create_rate(datetime(2017, 12, 1), 0.15, NotificationType.SMS), + create_rate(datetime(2017, 12, 1), 0, NotificationType.EMAIL), ] - rate = get_rate(non_letter_rates, SMS_TYPE, date(2018, 1, 1)) + rate = get_rate(non_letter_rates, NotificationType.SMS, date(2018, 1, 1)) assert rate == Decimal(0.15) - rate = get_rate(non_letter_rates, EMAIL_TYPE, date(2018, 1, 1)) + rate = get_rate(non_letter_rates, NotificationType.EMAIL, date(2018, 1, 1)) assert rate == Decimal(0) diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 41c613563..b9e08ce30 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -30,15 +30,15 @@ from app.celery.tasks import ( from app.config import QueueNames from app.dao import jobs_dao, service_email_reply_to_dao, service_sms_sender_dao from app.models import ( - EMAIL_TYPE, JOB_STATUS_ERROR, JOB_STATUS_FINISHED, JOB_STATUS_IN_PROGRESS, KEY_TYPE_NORMAL, NOTIFICATION_CREATED, - SMS_TYPE, Job, Notification, + NotificationType, + TemplateType, ) from app.serialised_models import SerialisedService, SerialisedTemplate from app.utils import DATETIME_FORMAT @@ -305,8 +305,8 @@ def test_should_process_all_sms_job(sample_job_with_placeholdered_template, mock @pytest.mark.parametrize( "template_type, expected_function, expected_queue", [ - (SMS_TYPE, "save_sms", "database-tasks"), - (EMAIL_TYPE, "save_email", "database-tasks"), + (TemplateType.SMS, "save_sms", "database-tasks"), + (TemplateType.EMAIL, "save_email", "database-tasks"), ], ) def test_process_row_sends_letter_task( @@ -362,7 +362,7 @@ def test_process_row_when_sender_id_is_provided(mocker, fake_uuid): mocker.patch("app.celery.tasks.create_uuid", return_value="noti_uuid") task_mock = mocker.patch("app.celery.tasks.save_sms.apply_async") encrypt_mock = mocker.patch("app.celery.tasks.encryption.encrypt") - template = Mock(id="template_id", template_type=SMS_TYPE) + template = Mock(id="template_id", template_type=TemplateType.SMS) job = Mock(id="job_id", template_version="temp_vers") service = Mock(id="service_id", research_mode=False) @@ -1384,8 +1384,8 @@ def test_process_incomplete_jobs_sets_status_to_in_progress_and_resets_processin def test_save_api_email_or_sms(mocker, sample_service, notification_type): template = ( create_template(sample_service) - if notification_type == SMS_TYPE - else create_template(sample_service, template_type=EMAIL_TYPE) + if notification_type == NotificationType.SMS + else create_template(sample_service, template_type=TemplateType.EMAIL) ) mock_provider_task = mocker.patch( f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" @@ -1407,7 +1407,7 @@ def test_save_api_email_or_sms(mocker, sample_service, notification_type): "created_at": datetime.utcnow().strftime(DATETIME_FORMAT), } - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: data.update({"to": "jane.citizen@example.com"}) expected_queue = QueueNames.SEND_EMAIL else: @@ -1417,7 +1417,7 @@ def test_save_api_email_or_sms(mocker, sample_service, notification_type): encrypted = encryption.encrypt(data) assert len(Notification.query.all()) == 0 - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: save_api_email(encrypted_notification=encrypted) else: save_api_sms(encrypted_notification=encrypted) @@ -1436,8 +1436,8 @@ def test_save_api_email_dont_retry_if_notification_already_exists( ): template = ( create_template(sample_service) - if notification_type == SMS_TYPE - else create_template(sample_service, template_type=EMAIL_TYPE) + if notification_type == NotificationType.SMS + else create_template(sample_service, template_type=TemplateType.EMAIL) ) mock_provider_task = mocker.patch( f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" @@ -1459,7 +1459,7 @@ def test_save_api_email_dont_retry_if_notification_already_exists( "created_at": datetime.utcnow().strftime(DATETIME_FORMAT), } - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: data.update({"to": "jane.citizen@example.com"}) expected_queue = QueueNames.SEND_EMAIL else: @@ -1469,14 +1469,14 @@ def test_save_api_email_dont_retry_if_notification_already_exists( encrypted = encryption.encrypt(data) assert len(Notification.query.all()) == 0 - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: save_api_email(encrypted_notification=encrypted) else: save_api_sms(encrypted_notification=encrypted) notifications = Notification.query.all() assert len(notifications) == 1 # call the task again with the same notification - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: save_api_email(encrypted_notification=encrypted) else: save_api_sms(encrypted_notification=encrypted) diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 249dcdcf8..db1b3914c 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -19,14 +19,13 @@ from app.dao.templates_dao import dao_create_template from app.dao.users_dao import create_secret_code, create_user_code from app.history_meta import create_history from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, NOTIFICATION_STATUS_TYPES_COMPLETED, SERVICE_PERMISSION_TYPES, - SMS_TYPE, ApiKey, + GuestListRecipientType, InvitedUser, Job, Notification, @@ -38,8 +37,10 @@ from app.models import ( Service, ServiceEmailReplyTo, ServiceGuestList, + ServicePermissionType, Template, TemplateHistory, + TemplateType, ) from tests import create_admin_authorization_header from tests.app.db import ( @@ -246,7 +247,7 @@ def _sample_service_full_permissions(notify_db_session): @pytest.fixture(scope="function") def sample_template(sample_user): service = create_service( - service_permissions=[EMAIL_TYPE, SMS_TYPE], check_if_service_exists=True + service_permissions=[ServicePermissionType.EMAIL, ServicePermissionType.SMS], check_if_service_exists=True ) data = { @@ -268,9 +269,9 @@ def sample_template(sample_user): @pytest.fixture(scope="function") def sample_template_without_sms_permission(notify_db_session): service = create_service( - service_permissions=[EMAIL_TYPE], check_if_service_exists=True + service_permissions=[ServicePermissionType.EMAIL], check_if_service_exists=True ) - return create_template(service, template_type=SMS_TYPE) + return create_template(service, template_type=TemplateType.SMS) @pytest.fixture(scope="function") @@ -293,12 +294,12 @@ def sample_sms_template_with_html(sample_service): def sample_email_template(sample_user): service = create_service( user=sample_user, - service_permissions=[EMAIL_TYPE, SMS_TYPE], + service_permissions=[ServicePermissionType.EMAIL, ServicePermissionType.SMS], check_if_service_exists=True, ) data = { "name": "Email Template Name", - "template_type": EMAIL_TYPE, + "template_type": TemplateType.EMAIL, "content": "This is a template", "service": service, "created_by": sample_user, @@ -312,16 +313,16 @@ def sample_email_template(sample_user): @pytest.fixture(scope="function") def sample_template_without_email_permission(notify_db_session): service = create_service( - service_permissions=[SMS_TYPE], check_if_service_exists=True + service_permissions=[ServicePermissionType.SMS], check_if_service_exists=True ) - return create_template(service, template_type=EMAIL_TYPE) + return create_template(service, template_type=TemplateType.EMAIL) @pytest.fixture(scope="function") def sample_email_template_with_placeholders(sample_service): return create_template( sample_service, - template_type=EMAIL_TYPE, + template_type=TemplateType.EMAIL, subject="((name))", content="Hello ((name))\nThis is an email from GOV.UK", ) @@ -331,7 +332,7 @@ def sample_email_template_with_placeholders(sample_service): def sample_email_template_with_html(sample_service): return create_template( sample_service, - template_type=EMAIL_TYPE, + template_type=TemplateType.EMAIL, subject="((name)) some HTML", content="Hello ((name))\nThis is an email from GOV.UK with some HTML", ) @@ -480,7 +481,7 @@ def sample_notification(notify_db_session): def sample_email_notification(notify_db_session): created_at = datetime.utcnow() service = create_service(check_if_service_exists=True) - template = create_template(service, template_type=EMAIL_TYPE) + template = create_template(service, template_type=TemplateType.EMAIL) job = create_job(template) notification_id = uuid.uuid4() @@ -843,7 +844,7 @@ def notify_service(notify_db_session, sample_user): def sample_service_guest_list(notify_db_session): service = create_service(check_if_service_exists=True) guest_list_user = ServiceGuestList.from_string( - service.id, EMAIL_TYPE, "guest_list_user@digital.fake.gov" + service.id, GuestListRecipientType.EMAIL, "guest_list_user@digital.fake.gov" ) notify_db_session.add(guest_list_user) diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index a38d3e3ec..c5586be34 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -18,7 +18,6 @@ from app.dao.fact_notification_status_dao import ( update_fact_notification_status, ) from app.models import ( - EMAIL_TYPE, KEY_TYPE_TEAM, KEY_TYPE_TEST, NOTIFICATION_CREATED, @@ -30,8 +29,8 @@ from app.models import ( NOTIFICATION_SENT, NOTIFICATION_TECHNICAL_FAILURE, NOTIFICATION_TEMPORARY_FAILURE, - SMS_TYPE, FactNotificationStatus, + TemplateType, ) from tests.app.db import ( create_ft_notification_status, @@ -167,9 +166,9 @@ def test_fetch_notification_status_for_service_for_today_and_7_previous_days( notify_db_session, ): service_1 = create_service(service_name="service_1") - sms_template = create_template(service=service_1, template_type=SMS_TYPE) - sms_template_2 = create_template(service=service_1, template_type=SMS_TYPE) - email_template = create_template(service=service_1, template_type=EMAIL_TYPE) + sms_template = create_template(service=service_1, template_type=TemplateType.SMS) + sms_template_2 = create_template(service=service_1, template_type=TemplateType.SMS) + email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) create_ft_notification_status(date(2018, 10, 25), "sms", service_1, count=8) @@ -222,15 +221,15 @@ def test_fetch_notification_status_by_template_for_service_for_today_and_7_previ ): service_1 = create_service(service_name="service_1") sms_template = create_template( - template_name="sms Template 1", service=service_1, template_type=SMS_TYPE + template_name="sms Template 1", service=service_1, template_type=TemplateType.SMS ) sms_template_2 = create_template( - template_name="sms Template 2", service=service_1, template_type=SMS_TYPE + template_name="sms Template 2", service=service_1, template_type=TemplateType.SMS ) - email_template = create_template(service=service_1, template_type=EMAIL_TYPE) + email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) # create unused email template - create_template(service=service_1, template_type=EMAIL_TYPE) + create_template(service=service_1, template_type=TemplateType.EMAIL) create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=11) @@ -323,8 +322,8 @@ def test_fetch_notification_status_totals_for_all_services_works_in_est( notify_db_session, ): service_1 = create_service(service_name="service_1") - sms_template = create_template(service=service_1, template_type=SMS_TYPE) - email_template = create_template(service=service_1, template_type=EMAIL_TYPE) + sms_template = create_template(service=service_1, template_type=TemplateType.SMS) + email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) create_notification( sms_template, created_at=datetime(2018, 4, 20, 12, 0, 0), status="delivered" @@ -367,8 +366,8 @@ def test_fetch_notification_status_totals_for_all_services_works_in_est( def set_up_data(): service_2 = create_service(service_name="service_2") service_1 = create_service(service_name="service_1") - sms_template = create_template(service=service_1, template_type=SMS_TYPE) - email_template = create_template(service=service_1, template_type=EMAIL_TYPE) + sms_template = create_template(service=service_1, template_type=TemplateType.SMS) + email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) create_ft_notification_status(date(2018, 10, 24), "sms", service_1, count=8) create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) create_ft_notification_status( diff --git a/tests/app/dao/test_service_guest_list_dao.py b/tests/app/dao/test_service_guest_list_dao.py index 5d8e97bd3..3fdd88227 100644 --- a/tests/app/dao/test_service_guest_list_dao.py +++ b/tests/app/dao/test_service_guest_list_dao.py @@ -5,7 +5,7 @@ from app.dao.service_guest_list_dao import ( dao_fetch_service_guest_list, dao_remove_service_guest_list, ) -from app.models import EMAIL_TYPE, ServiceGuestList +from app.models import GuestListRecipientType, ServiceGuestList from tests.app.db import create_service @@ -21,7 +21,7 @@ def test_fetch_service_guest_list_ignores_other_service(sample_service_guest_lis def test_add_and_commit_guest_list_contacts_saves_data(sample_service): guest_list = ServiceGuestList.from_string( - sample_service.id, EMAIL_TYPE, "foo@example.com" + sample_service.id, GuestListRecipientType.EMAIL, "foo@example.com" ) dao_add_and_commit_guest_list_contacts([guest_list]) @@ -37,10 +37,10 @@ def test_remove_service_guest_list_only_removes_for_my_service(notify_db_session dao_add_and_commit_guest_list_contacts( [ ServiceGuestList.from_string( - service_1.id, EMAIL_TYPE, "service1@example.com" + service_1.id, GuestListRecipientType.EMAIL, "service1@example.com" ), ServiceGuestList.from_string( - service_2.id, EMAIL_TYPE, "service2@example.com" + service_2.id, GuestListRecipientType.EMAIL, "service2@example.com" ), ] ) diff --git a/tests/app/db.py b/tests/app/db.py index 56a33335f..b8bcbc539 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -27,10 +27,7 @@ from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template, dao_update_template from app.dao.users_dao import save_model_user from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, - MOBILE_TYPE, - SMS_TYPE, AnnualBilling, ApiKey, Complaint, @@ -39,6 +36,7 @@ from app.models import ( FactBilling, FactNotificationStatus, FactProcessingTime, + GuestListRecipientType, InboundNumber, InboundSms, InvitedOrganizationUser, @@ -55,9 +53,11 @@ from app.models import ( ServiceGuestList, ServiceInboundApi, ServicePermission, + ServicePermissionType, ServiceSmsSender, Template, TemplateFolder, + TemplateType, User, WebauthnCredential, ) @@ -193,7 +193,7 @@ def create_service_with_defined_sms_sender(sms_sender_value="1234567", *args, ** def create_template( service, - template_type=SMS_TYPE, + template_type=TemplateType.SMS, template_name=None, subject="Template subject", content="Dear Sir/Madam, Hello. Yours Truly, The Government.", @@ -215,7 +215,7 @@ def create_template( "folder": folder, "process_type": process_type, } - if template_type != SMS_TYPE: + if template_type != TemplateType.SMS: data["subject"] = subject template = Template(**data) dao_create_template(template) @@ -262,7 +262,7 @@ def create_notification( if to_field is None: to_field = ( "+447700900855" - if template.template_type == SMS_TYPE + if template.template_type == TemplateType.SMS else "test@example.com" ) @@ -415,9 +415,10 @@ def create_job( return job -def create_service_permission(service_id, permission=EMAIL_TYPE): +def create_service_permission(service_id, permission=ServicePermissionType.EMAIL): dao_add_service_permission( - service_id if service_id else create_service().id, permission + service_id if service_id else create_service().id, + permission, ) service_permissions = ServicePermission.query.all() @@ -724,15 +725,15 @@ def create_process_time( def create_service_guest_list(service, email_address=None, mobile_number=None): if email_address: guest_list_user = ServiceGuestList.from_string( - service.id, EMAIL_TYPE, email_address + service.id, GuestListRecipientType.EMAIL, email_address ) elif mobile_number: guest_list_user = ServiceGuestList.from_string( - service.id, MOBILE_TYPE, mobile_number + service.id, GuestListRecipientType.MOBILE, mobile_number ) else: guest_list_user = ServiceGuestList.from_string( - service.id, EMAIL_TYPE, "guest_list_user@digital.fake.gov" + service.id, GuestListRecipientType.EMAIL, "guest_list_user@digital.fake.gov" ) db.session.add(guest_list_user) diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index c13ac3dd3..45d836105 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -23,10 +23,10 @@ from app.dao.users_dao import get_user_by_email from app.models import ( KEY_TYPE_NORMAL, NOTIFICATION_DELIVERED, - SMS_TYPE, AnnualBilling, Job, Notification, + NotificationType, Organization, Service, Template, @@ -314,7 +314,7 @@ def test_fix_billable_units(notify_db_session, notify_api, sample_template): create_notification(template=sample_template) notification = Notification.query.one() notification.billable_units = 0 - notification.notification_type = SMS_TYPE + notification.notification_type = NotificationType.SMS notification.status = NOTIFICATION_DELIVERED notification.sent_at = None notification.key_type = KEY_TYPE_NORMAL diff --git a/tests/app/test_model.py b/tests/app/test_model.py index faab07182..f893ab14c 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -6,23 +6,22 @@ from sqlalchemy.exc import IntegrityError from app import encryption from app.models import ( - EMAIL_TYPE, - MOBILE_TYPE, NOTIFICATION_CREATED, NOTIFICATION_FAILED, NOTIFICATION_PENDING, NOTIFICATION_STATUS_TYPES_FAILED, NOTIFICATION_TECHNICAL_FAILURE, - SMS_TYPE, Agreement, AgreementStatus, AgreementType, AnnualBilling, + GuestListRecipientType, Notification, NotificationHistory, Service, ServiceGuestList, ServicePermission, + TemplateType, User, VerifyCode, filter_null_value_fields, @@ -43,7 +42,7 @@ from tests.app.db import ( @pytest.mark.parametrize("mobile_number", ["+447700900855", "+12348675309"]) def test_should_build_service_guest_list_from_mobile_number(mobile_number): service_guest_list = ServiceGuestList.from_string( - "service_id", MOBILE_TYPE, mobile_number + "service_id", GuestListRecipientType.MOBILE, mobile_number ) assert service_guest_list.recipient == mobile_number @@ -52,7 +51,7 @@ def test_should_build_service_guest_list_from_mobile_number(mobile_number): @pytest.mark.parametrize("email_address", ["test@example.com"]) def test_should_build_service_guest_list_from_email_address(email_address): service_guest_list = ServiceGuestList.from_string( - "service_id", EMAIL_TYPE, email_address + "service_id", GuestListRecipientType.EMAIL, email_address ) assert service_guest_list.recipient == email_address @@ -60,7 +59,11 @@ def test_should_build_service_guest_list_from_email_address(email_address): @pytest.mark.parametrize( "contact, recipient_type", - [("", None), ("07700dsadsad", MOBILE_TYPE), ("gmail.com", EMAIL_TYPE)], + [ + ("", None), + ("07700dsadsad", GuestListRecipientType.MOBILE), + ("gmail.com", GuestListRecipientType.EMAIL), + ], ) def test_should_not_build_service_guest_list_from_invalid_contact( recipient_type, contact @@ -201,7 +204,7 @@ def test_notification_personalisation_setter_always_sets_empty_dict( def test_notification_subject_is_none_for_sms(sample_service): - template = create_template(service=sample_service, template_type=SMS_TYPE) + template = create_template(service=sample_service, template_type=TemplateType.SMS) notification = create_notification(template=template) assert notification.subject is None From df866e40f75d680ca1a61110712d2ee00ddc4cce Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 10:26:40 -0500 Subject: [PATCH 133/259] More Enum goodness. Signed-off-by: Cliff Hill --- app/models.py | 1 + .../notification_dao/test_notification_dao.py | 11 +- .../dao/test_fact_notification_status_dao.py | 3 +- tests/app/dao/test_jobs_dao.py | 6 +- tests/app/dao/test_service_permissions_dao.py | 29 ++-- tests/app/dao/test_services_dao.py | 59 ++++---- tests/app/dao/test_uploads_dao.py | 4 +- .../test_process_notification.py | 6 +- .../test_receive_notification.py | 30 ++-- tests/app/notifications/test_validators.py | 48 ++++--- tests/app/platform_stats/test_rest.py | 9 +- .../test_send_notification.py | 76 ++++++---- .../test_send_one_off_notification.py | 22 +-- tests/app/service/test_rest.py | 135 +++++++++++------- tests/app/service/test_sender.py | 10 +- tests/app/service/test_service_guest_list.py | 12 +- tests/app/service/test_statistics_rest.py | 19 +-- tests/app/template/test_rest.py | 47 +++--- tests/app/user/test_rest_verify.py | 9 +- .../test_notification_schemas.py | 4 +- .../notifications/test_post_notifications.py | 31 ++-- tests/app/v2/template/test_get_template.py | 12 +- tests/app/v2/template/test_post_template.py | 10 +- .../app/v2/template/test_template_schemas.py | 10 +- tests/app/v2/templates/test_get_templates.py | 18 +-- .../v2/templates/test_templates_schemas.py | 36 ++--- 26 files changed, 379 insertions(+), 278 deletions(-) diff --git a/app/models.py b/app/models.py index 29fde4ffd..a656a926c 100644 --- a/app/models.py +++ b/app/models.py @@ -33,6 +33,7 @@ from app.utils import ( class TemplateType(Enum): SMS = "sms" EMAIL = "email" + LETTER = "letter" class NotificationType(Enum): diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index 13056105a..f167ca65f 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -36,10 +36,10 @@ from app.models import ( NOTIFICATION_DELIVERED, NOTIFICATION_SENT, NOTIFICATION_STATUS_TYPES, - SMS_TYPE, Job, Notification, NotificationHistory, + NotificationType, ) from tests.app.db import ( create_ft_notification_status, @@ -1748,7 +1748,10 @@ def test_get_service_ids_with_notifications_on_date_respects_gmt_bst( sample_template, created_at_utc, date_to_check, expected_count ): create_notification(template=sample_template, created_at=created_at_utc) - service_ids = get_service_ids_with_notifications_on_date(SMS_TYPE, date_to_check) + service_ids = get_service_ids_with_notifications_on_date( + NotificationType.SMS, + date_to_check, + ) assert len(service_ids) == expected_count @@ -1759,8 +1762,8 @@ def test_get_service_ids_with_notifications_on_date_checks_ft_status( create_ft_notification_status(template=sample_template, local_date="2022-01-02") assert ( - len(get_service_ids_with_notifications_on_date(SMS_TYPE, date(2022, 1, 1))) == 1 + len(get_service_ids_with_notifications_on_date(NotificationType.SMS, date(2022, 1, 1))) == 1 ) assert ( - len(get_service_ids_with_notifications_on_date(SMS_TYPE, date(2022, 1, 2))) == 1 + len(get_service_ids_with_notifications_on_date(NotificationType.SMS, date(2022, 1, 2))) == 1 ) diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index c5586be34..1f0e6ab84 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -30,6 +30,7 @@ from app.models import ( NOTIFICATION_TECHNICAL_FAILURE, NOTIFICATION_TEMPORARY_FAILURE, FactNotificationStatus, + NotificationType, TemplateType, ) from tests.app.db import ( @@ -853,7 +854,7 @@ def test_update_fact_notification_status_respects_gmt_bst( expected_count, ): create_notification(template=sample_template, created_at=created_at_utc) - update_fact_notification_status(process_day, SMS_TYPE, sample_service.id) + update_fact_notification_status(process_day, NotificationType.SMS, sample_service.id) assert ( FactNotificationStatus.query.filter_by( diff --git a/tests/app/dao/test_jobs_dao.py b/tests/app/dao/test_jobs_dao.py index 0b1374614..b7afa713c 100644 --- a/tests/app/dao/test_jobs_dao.py +++ b/tests/app/dao/test_jobs_dao.py @@ -18,7 +18,7 @@ from app.dao.jobs_dao import ( find_jobs_with_missing_rows, find_missing_row_for_job, ) -from app.models import JOB_STATUS_FINISHED, SMS_TYPE, Job +from app.models import JOB_STATUS_FINISHED, Job, NotificationType, TemplateType from tests.app.db import ( create_job, create_notification, @@ -344,7 +344,7 @@ def test_get_jobs_for_service_doesnt_return_test_messages( def test_should_get_jobs_seven_days_old_by_scheduled_for_date(sample_service): six_days_ago = datetime.utcnow() - timedelta(days=6) eight_days_ago = datetime.utcnow() - timedelta(days=8) - sms_template = create_template(sample_service, template_type=SMS_TYPE) + sms_template = create_template(sample_service, template_type=TemplateType.SMS) create_job(sms_template, created_at=eight_days_ago) create_job(sms_template, created_at=eight_days_ago, scheduled_for=eight_days_ago) @@ -352,7 +352,7 @@ def test_should_get_jobs_seven_days_old_by_scheduled_for_date(sample_service): sms_template, created_at=eight_days_ago, scheduled_for=six_days_ago ) - jobs = dao_get_jobs_older_than_data_retention(notification_types=[SMS_TYPE]) + jobs = dao_get_jobs_older_than_data_retention(notification_types=[NotificationType.SMS]) assert len(jobs) == 2 assert job_to_remain.id not in [job.id for job in jobs] diff --git a/tests/app/dao/test_service_permissions_dao.py b/tests/app/dao/test_service_permissions_dao.py index d2298aa43..df87575ae 100644 --- a/tests/app/dao/test_service_permissions_dao.py +++ b/tests/app/dao/test_service_permissions_dao.py @@ -4,7 +4,7 @@ from app.dao.service_permissions_dao import ( dao_fetch_service_permissions, dao_remove_service_permission, ) -from app.models import EMAIL_TYPE, INBOUND_SMS_TYPE, INTERNATIONAL_SMS_TYPE, SMS_TYPE +from app.models import ServicePermissionType from tests.app.db import create_service, create_service_permission @@ -15,22 +15,23 @@ def service_without_permissions(notify_db_session): def test_create_service_permission(service_without_permissions): service_permissions = create_service_permission( - service_id=service_without_permissions.id, permission=SMS_TYPE + service_id=service_without_permissions.id, permission=ServicePermissionType.SMS ) assert len(service_permissions) == 1 assert service_permissions[0].service_id == service_without_permissions.id - assert service_permissions[0].permission == SMS_TYPE + assert service_permissions[0].permission == ServicePermissionType.SMS def test_fetch_service_permissions_gets_service_permissions( service_without_permissions, ): create_service_permission( - service_id=service_without_permissions.id, permission=INTERNATIONAL_SMS_TYPE + service_id=service_without_permissions.id, + permission=ServicePermissionType.INTERNATIONAL_SMS, ) create_service_permission( - service_id=service_without_permissions.id, permission=SMS_TYPE + service_id=service_without_permissions.id, permission=ServicePermissionType.SMS ) service_permissions = dao_fetch_service_permissions(service_without_permissions.id) @@ -40,22 +41,30 @@ def test_fetch_service_permissions_gets_service_permissions( sp.service_id == service_without_permissions.id for sp in service_permissions ) assert all( - sp.permission in [INTERNATIONAL_SMS_TYPE, SMS_TYPE] + sp.permission in { + ServicePermissionType.INTERNATIONAL_SMS, + ServicePermissionType.SMS, + } for sp in service_permissions ) def test_remove_service_permission(service_without_permissions): create_service_permission( - service_id=service_without_permissions.id, permission=EMAIL_TYPE + service_id=service_without_permissions.id, + permission=ServicePermissionType.EMAIL, ) create_service_permission( - service_id=service_without_permissions.id, permission=INBOUND_SMS_TYPE + service_id=service_without_permissions.id, + permission=ServicePermissionType.INBOUND_SMS, ) - dao_remove_service_permission(service_without_permissions.id, EMAIL_TYPE) + dao_remove_service_permission( + service_without_permissions.id, + ServicePermissionType.EMAIL, + ) permissions = dao_fetch_service_permissions(service_without_permissions.id) assert len(permissions) == 1 - assert permissions[0].permission == INBOUND_SMS_TYPE + assert permissions[0].permission == ServicePermissionType.INBOUND_SMS assert permissions[0].service_id == service_without_permissions.id diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 41d003ec7..641a274a5 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -41,12 +41,9 @@ from app.dao.services_dao import ( ) from app.dao.users_dao import create_user_code, save_model_user from app.models import ( - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - SMS_TYPE, ApiKey, InvitedUser, Job, @@ -56,6 +53,7 @@ from app.models import ( Permission, Service, ServicePermission, + ServicePermissionType, ServiceUser, Template, TemplateHistory, @@ -601,9 +599,9 @@ def test_create_service_returns_service_with_default_permissions(notify_db_sessi _assert_service_permissions( service.permissions, ( - SMS_TYPE, - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.SMS, + ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, ), ) @@ -612,17 +610,17 @@ def test_create_service_returns_service_with_default_permissions(notify_db_sessi "permission_to_remove, permissions_remaining", [ ( - SMS_TYPE, + ServicePermissionType.SMS, ( - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, ), ), ( - EMAIL_TYPE, + ServicePermissionType.EMAIL, ( - SMS_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.SMS, + ServicePermissionType.INTERNATIONAL_SMS, ), ), ], @@ -641,10 +639,17 @@ def test_remove_permission_from_service_by_id_returns_service_with_correct_permi def test_removing_all_permission_returns_service_with_no_permissions(notify_db_session): service = create_service() - dao_remove_service_permission(service_id=service.id, permission=SMS_TYPE) - dao_remove_service_permission(service_id=service.id, permission=EMAIL_TYPE) dao_remove_service_permission( - service_id=service.id, permission=INTERNATIONAL_SMS_TYPE + service_id=service.id, + permission=ServicePermissionType.SMS, + ) + dao_remove_service_permission( + service_id=service.id, + permission=ServicePermissionType.EMAIL, + ) + dao_remove_service_permission( + service_id=service.id, + permission=ServicePermissionType.INTERNATIONAL_SMS, ) service = dao_fetch_service_by_id(service.id) @@ -734,16 +739,16 @@ def test_update_service_permission_creates_a_history_record_with_current_data( service, user, service_permissions=[ - SMS_TYPE, - # EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.SMS, + # ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, ], ) assert Service.query.count() == 1 service.permissions.append( - ServicePermission(service_id=service.id, permission=EMAIL_TYPE) + ServicePermission(service_id=service.id, permission=ServicePermissionType.EMAIL) ) dao_update_service(service) @@ -757,9 +762,9 @@ def test_update_service_permission_creates_a_history_record_with_current_data( _assert_service_permissions( service.permissions, ( - SMS_TYPE, - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.SMS, + ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, ), ) @@ -775,8 +780,8 @@ def test_update_service_permission_creates_a_history_record_with_current_data( _assert_service_permissions( service.permissions, ( - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, ), ) @@ -831,9 +836,9 @@ def test_delete_service_and_associated_objects(notify_db_session): assert ServicePermission.query.count() == len( ( - SMS_TYPE, - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.SMS, + ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, ) ) diff --git a/tests/app/dao/test_uploads_dao.py b/tests/app/dao/test_uploads_dao.py index b0e144960..016d7a92e 100644 --- a/tests/app/dao/test_uploads_dao.py +++ b/tests/app/dao/test_uploads_dao.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from freezegun import freeze_time from app.dao.uploads_dao import dao_get_uploads_by_service_id -from app.models import JOB_STATUS_IN_PROGRESS, LETTER_TYPE +from app.models import JOB_STATUS_IN_PROGRESS from tests.app.db import ( create_job, create_notification, @@ -29,7 +29,7 @@ def create_uploaded_letter(letter_template, service, status="created", created_a def create_uploaded_template(service): return create_template( service, - template_type=LETTER_TYPE, + template_type=TemplateType.LETTER, template_name="Pre-compiled PDF", subject="Pre-compiled PDF", content="", diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index 2e302476a..d0ea87574 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -11,7 +11,7 @@ from notifications_utils.recipients import ( ) from sqlalchemy.exc import SQLAlchemyError -from app.models import SMS_TYPE, Notification, NotificationHistory +from app.models import Notification, NotificationHistory, ServicePermissionType, TemplateType from app.notifications.process_notifications import ( create_content_for_notification, persist_notification, @@ -403,8 +403,8 @@ def test_persist_email_notification_stores_normalised_email( def test_persist_notification_with_billable_units_stores_correct_info(mocker): - service = create_service(service_permissions=[SMS_TYPE]) - template = create_template(service, template_type=SMS_TYPE) + service = create_service(service_permissions=[ServicePermissionType.SMS]) + template = create_template(service, template_type=TemplateType.SMS) mocker.patch("app.dao.templates_dao.dao_get_template_by_id", return_value=template) persist_notification( template_id=template.id, diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index 01ae9e566..fdc1450f2 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -5,7 +5,7 @@ from unittest import mock import pytest from flask import json -from app.models import EMAIL_TYPE, INBOUND_SMS_TYPE, SMS_TYPE, InboundSms +from app.models import InboundSms, ServicePermissionType from app.notifications.receive_notifications import ( create_inbound_sms_object, fetch_potential_service, @@ -72,8 +72,8 @@ def test_receive_notification_returns_received_to_sns( @pytest.mark.parametrize( "permissions", [ - [SMS_TYPE], - [INBOUND_SMS_TYPE], + [ServicePermissionType.SMS], + [ServicePermissionType.INBOUND_SMS], ], ) def test_receive_notification_from_sns_without_permissions_does_not_persist( @@ -139,9 +139,9 @@ def test_receive_notification_without_permissions_does_not_create_inbound_even_w @pytest.mark.parametrize( "permissions,expected_response", [ - ([SMS_TYPE, INBOUND_SMS_TYPE], True), - ([INBOUND_SMS_TYPE], False), - ([SMS_TYPE], False), + ([ServicePermissionType.SMS, ServicePermissionType.INBOUND_SMS], True), + ([ServicePermissionType.INBOUND_SMS], False), + ([ServicePermissionType.SMS], False), ], ) def test_check_permissions_for_inbound_sms( @@ -256,12 +256,20 @@ def test_receive_notification_error_if_not_single_matching_service( create_service_with_inbound_number( inbound_number="dog", service_name="a", - service_permissions=[EMAIL_TYPE, SMS_TYPE, INBOUND_SMS_TYPE], + service_permissions=[ + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + ServicePermissionType.INBOUND_SMS, + ], ) create_service_with_inbound_number( inbound_number="bar", service_name="b", - service_permissions=[EMAIL_TYPE, SMS_TYPE, INBOUND_SMS_TYPE], + service_permissions=[ + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + ServicePermissionType.INBOUND_SMS + ], ) data = { @@ -303,7 +311,11 @@ def test_sns_inbound_sms_auth( create_service_with_inbound_number( service_name="b", inbound_number="07111111111", - service_permissions=[EMAIL_TYPE, SMS_TYPE, INBOUND_SMS_TYPE], + service_permissions=[ + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + ServicePermissionType.INBOUND_SMS, + ], ) data = { diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index 9643a0d0f..a3a098e4d 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -5,7 +5,7 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT import app from app.dao import templates_dao -from app.models import EMAIL_TYPE, KEY_TYPE_NORMAL, SMS_TYPE +from app.models import KEY_TYPE_NORMAL, NotificationType, ServicePermissionType, TemplateType from app.notifications.process_notifications import create_content_for_notification from app.notifications.sns_cert_validator import ( VALID_SNS_TOPICS, @@ -89,7 +89,11 @@ def test_check_application_over_retention_limit_fails( @pytest.mark.parametrize( - "template_type, notification_type", [(EMAIL_TYPE, EMAIL_TYPE), (SMS_TYPE, SMS_TYPE)] + "template_type, notification_type", + [ + (TemplateType.EMAIL, NotificationType.EMAIL), + (TemplateType.SMS, NotificationType.SMS), + ] ) def test_check_template_is_for_notification_type_pass(template_type, notification_type): assert ( @@ -101,7 +105,11 @@ def test_check_template_is_for_notification_type_pass(template_type, notificatio @pytest.mark.parametrize( - "template_type, notification_type", [(SMS_TYPE, EMAIL_TYPE), (EMAIL_TYPE, SMS_TYPE)] + "template_type, notification_type", + [ + (TemplateType.SMS, NotificationType.EMAIL), + (TemplateType.EMAIL, NotificationType.SMS), + ] ) def test_check_template_is_for_notification_type_fails_when_template_type_does_not_match_notification_type( template_type, notification_type @@ -548,11 +556,14 @@ def test_validate_and_format_recipient_fails_when_international_number_and_servi key_type, notify_db_session, ): - service = create_service(service_permissions=[SMS_TYPE]) + service = create_service(service_permissions=[ServicePermissionType.SMS]) service_model = SerialisedService.from_id(service.id) with pytest.raises(BadRequestError) as e: validate_and_format_recipient( - "+20-12-1234-1234", key_type, service_model, SMS_TYPE + "+20-12-1234-1234", + key_type, + service_model, + NotificationType.SMS, ) assert e.value.status_code == 400 assert e.value.message == "Cannot send to international mobile numbers" @@ -565,14 +576,14 @@ def test_validate_and_format_recipient_succeeds_with_international_numbers_if_se ): service_model = SerialisedService.from_id(sample_service_full_permissions.id) result = validate_and_format_recipient( - "+4407513332413", key_type, service_model, SMS_TYPE + "+4407513332413", key_type, service_model, NotificationType.SMS ) assert result == "+447513332413" def test_validate_and_format_recipient_fails_when_no_recipient(): with pytest.raises(BadRequestError) as e: - validate_and_format_recipient(None, "key_type", "service", "SMS_TYPE") + validate_and_format_recipient(None, "key_type", "service", "NotificationType.SMS") assert e.value.status_code == 400 assert e.value.message == "Recipient can't be empty" @@ -586,7 +597,7 @@ def test_check_service_email_reply_to_where_email_reply_to_is_found(sample_servi reply_to_address = create_reply_to_email(sample_service, "test@test.com") assert ( check_service_email_reply_to_id( - sample_service.id, reply_to_address.id, EMAIL_TYPE + sample_service.id, reply_to_address.id, NotificationType.EMAIL ) == "test@test.com" ) @@ -597,7 +608,7 @@ def test_check_service_email_reply_to_id_where_service_id_is_not_found( ): reply_to_address = create_reply_to_email(sample_service, "test@test.com") with pytest.raises(BadRequestError) as e: - check_service_email_reply_to_id(fake_uuid, reply_to_address.id, EMAIL_TYPE) + check_service_email_reply_to_id(fake_uuid, reply_to_address.id, NotificationType.EMAIL) assert e.value.status_code == 400 assert ( e.value.message @@ -611,7 +622,7 @@ def test_check_service_email_reply_to_id_where_reply_to_id_is_not_found( sample_service, fake_uuid ): with pytest.raises(BadRequestError) as e: - check_service_email_reply_to_id(sample_service.id, fake_uuid, EMAIL_TYPE) + check_service_email_reply_to_id(sample_service.id, fake_uuid, NotificationType.EMAIL) assert e.value.status_code == 400 assert ( e.value.message @@ -628,10 +639,11 @@ def test_check_service_sms_sender_id_where_sms_sender_id_is_none(notification_ty def test_check_service_sms_sender_id_where_sms_sender_id_is_found(sample_service): sms_sender = create_service_sms_sender(service=sample_service, sms_sender="123456") - assert ( - check_service_sms_sender_id(sample_service.id, sms_sender.id, SMS_TYPE) - == "123456" - ) + assert check_service_sms_sender_id( + sample_service.id, + sms_sender.id, + NotificationType.SMS, + ) == "123456" def test_check_service_sms_sender_id_where_service_id_is_not_found( @@ -639,7 +651,7 @@ def test_check_service_sms_sender_id_where_service_id_is_not_found( ): sms_sender = create_service_sms_sender(service=sample_service, sms_sender="123456") with pytest.raises(BadRequestError) as e: - check_service_sms_sender_id(fake_uuid, sms_sender.id, SMS_TYPE) + check_service_sms_sender_id(fake_uuid, sms_sender.id, NotificationType.SMS) assert e.value.status_code == 400 assert ( e.value.message @@ -653,7 +665,7 @@ def test_check_service_sms_sender_id_where_sms_sender_is_not_found( sample_service, fake_uuid ): with pytest.raises(BadRequestError) as e: - check_service_sms_sender_id(sample_service.id, fake_uuid, SMS_TYPE) + check_service_sms_sender_id(sample_service.id, fake_uuid, NotificationType.SMS) assert e.value.status_code == 400 assert ( e.value.message @@ -671,14 +683,14 @@ def test_check_reply_to_with_empty_reply_to(sample_service, notification_type): def test_check_reply_to_email_type(sample_service): reply_to_address = create_reply_to_email(sample_service, "test@test.com") assert ( - check_reply_to(sample_service.id, reply_to_address.id, EMAIL_TYPE) + check_reply_to(sample_service.id, reply_to_address.id, NotificationType.EMAIL) == "test@test.com" ) def test_check_reply_to_sms_type(sample_service): sms_sender = create_service_sms_sender(service=sample_service, sms_sender="123456") - assert check_reply_to(sample_service.id, sms_sender.id, SMS_TYPE) == "123456" + assert check_reply_to(sample_service.id, sms_sender.id, NotificationType.SMS) == "123456" def test_check_if_service_can_send_files_by_email_raises_if_no_contact_link_set( diff --git a/tests/app/platform_stats/test_rest.py b/tests/app/platform_stats/test_rest.py index cef9677e7..9c5e3ece8 100644 --- a/tests/app/platform_stats/test_rest.py +++ b/tests/app/platform_stats/test_rest.py @@ -4,7 +4,7 @@ import pytest from freezegun import freeze_time from app.errors import InvalidRequest -from app.models import EMAIL_TYPE, SMS_TYPE +from app.models import TemplateType from app.platform_stats.rest import validate_date_range_is_within_a_financial_year from tests.app.db import ( create_ft_billing, @@ -59,8 +59,11 @@ def test_get_platform_stats_validates_the_date(admin_request): @freeze_time("2018-10-31 14:00") def test_get_platform_stats_with_real_query(admin_request, notify_db_session): service_1 = create_service(service_name="service_1") - sms_template = create_template(service=service_1, template_type=SMS_TYPE) - email_template = create_template(service=service_1, template_type=EMAIL_TYPE) + sms_template = create_template(service=service_1, template_type=TemplateType.SMS) + email_template = create_template( + service=service_1, + template_type=TemplateType.EMAIL, + ) create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) create_ft_notification_status(date(2018, 10, 29), "email", service_1, count=3) diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index 3ffbb8e2e..254886bf4 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -14,15 +14,15 @@ from app.dao.services_dao import dao_update_service from app.dao.templates_dao import dao_get_all_templates_for_service, dao_update_template from app.errors import InvalidRequest from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - SMS_TYPE, ApiKey, Notification, NotificationHistory, + NotificationType, Template, + TemplateType, ) from app.service.send_notification import send_one_off_notification from app.v2.errors import RateLimitError @@ -37,7 +37,7 @@ from tests.app.db import ( ) -@pytest.mark.parametrize("template_type", [SMS_TYPE, EMAIL_TYPE]) +@pytest.mark.parametrize("template_type", [TemplateType.SMS, TemplateType.EMAIL]) def test_create_notification_should_reject_if_missing_required_fields( notify_api, sample_api_key, mocker, template_type ): @@ -96,7 +96,11 @@ def test_should_reject_bad_phone_numbers(notify_api, sample_template, mocker): @pytest.mark.parametrize( - "template_type, to", [(SMS_TYPE, "+447700900855"), (EMAIL_TYPE, "ok@ok.com")] + "template_type, to", + [ + (TemplateType.SMS, "+447700900855"), + (TemplateType.EMAIL, "ok@ok.com"), + ] ) def test_send_notification_invalid_template_id( notify_api, sample_template, mocker, fake_uuid, template_type, to @@ -281,8 +285,8 @@ def test_should_not_send_notification_for_archived_template( @pytest.mark.parametrize( "template_type, to", [ - (SMS_TYPE, "+447700900855"), - (EMAIL_TYPE, "not-someone-we-trust@email-address.com"), + (TemplateType.SMS, "+447700900855"), + (TemplateType.EMAIL, "not-someone-we-trust@email-address.com"), ], ) def test_should_not_send_notification_if_restricted_and_not_a_service_user( @@ -294,7 +298,9 @@ def test_should_not_send_notification_if_restricted_and_not_a_service_user( "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type) ) template = ( - sample_template if template_type == SMS_TYPE else sample_email_template + sample_template + if template_type == TemplateType.SMS + else sample_email_template ) template.service.restricted = True dao_update_service(template.service) @@ -322,7 +328,7 @@ def test_should_not_send_notification_if_restricted_and_not_a_service_user( ] == json_resp["message"]["to"] -@pytest.mark.parametrize("template_type", [SMS_TYPE, EMAIL_TYPE]) +@pytest.mark.parametrize("template_type", [TemplateType.SMS, TemplateType.EMAIL]) def test_should_send_notification_if_restricted_and_a_service_user( notify_api, sample_template, sample_email_template, template_type, mocker ): @@ -333,11 +339,11 @@ def test_should_send_notification_if_restricted_and_a_service_user( ) template = ( - sample_template if template_type == SMS_TYPE else sample_email_template + sample_template if template_type == TemplateType.SMS else sample_email_template ) to = ( template.service.created_by.mobile_number - if template_type == SMS_TYPE + if template_type == TemplateType.SMS else template.service.created_by.email_address ) template.service.restricted = True @@ -358,7 +364,7 @@ def test_should_send_notification_if_restricted_and_a_service_user( assert response.status_code == 201 -@pytest.mark.parametrize("template_type", [SMS_TYPE, EMAIL_TYPE]) +@pytest.mark.parametrize("template_type", [TemplateType.SMS, TemplateType.EMAIL]) def test_should_not_allow_template_from_another_service( notify_api, service_factory, sample_user, mocker, template_type ): @@ -379,7 +385,7 @@ def test_should_not_allow_template_from_another_service( ) to = ( sample_user.mobile_number - if template_type == SMS_TYPE + if template_type == TemplateType.SMS else sample_user.email_address ) data = {"to": to, "template": service_2_templates[0].id} @@ -496,8 +502,8 @@ def test_should_allow_api_call_if_under_day_limit_regardless_of_type( mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") service = create_service(restricted=restricted, message_limit=2) - email_template = create_template(service, template_type=EMAIL_TYPE) - sms_template = create_template(service, template_type=SMS_TYPE) + email_template = create_template(service, template_type=TemplateType.EMAIL) + sms_template = create_template(service, template_type=TemplateType.SMS) create_notification(template=email_template) data = {"to": sample_user.mobile_number, "template": str(sms_template.id)} @@ -518,7 +524,7 @@ def test_should_not_return_html_in_body(notify_api, sample_service, mocker): with notify_api.test_client() as client: mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") email_template = create_template( - sample_service, template_type=EMAIL_TYPE, content="hello\nthere" + sample_service, template_type=TemplateType.EMAIL, content="hello\nthere" ) data = {"to": "ok@ok.com", "template": str(email_template.id)} @@ -742,7 +748,10 @@ def test_should_send_sms_if_team_api_key_and_a_service_user( @pytest.mark.parametrize( "template_type,queue_name", - [(SMS_TYPE, "send-sms-tasks"), (EMAIL_TYPE, "send-email-tasks")], + [ + (TemplateType.SMS, "send-sms-tasks"), + (TemplateType.EMAIL, "send-email-tasks"), + ], ) def test_should_persist_notification( client, @@ -760,10 +769,10 @@ def test_should_persist_notification( "app.notifications.process_notifications.uuid.uuid4", return_value=fake_uuid ) - template = sample_template if template_type == SMS_TYPE else sample_email_template + template = sample_template if template_type == TemplateType.SMS else sample_email_template to = ( sample_template.service.created_by.mobile_number - if template_type == SMS_TYPE + if template_type == TemplateType.SMS else sample_email_template.service.created_by.email_address ) data = {"to": to, "template": template.id} @@ -798,7 +807,7 @@ def test_should_persist_notification( @pytest.mark.parametrize( "template_type,queue_name", - [(SMS_TYPE, "send-sms-tasks"), (EMAIL_TYPE, "send-email-tasks")], + [(TemplateType.SMS, "send-sms-tasks"), (TemplateType.EMAIL, "send-email-tasks")], ) def test_should_delete_notification_and_return_error_if_redis_fails( client, @@ -817,10 +826,10 @@ def test_should_delete_notification_and_return_error_if_redis_fails( "app.notifications.process_notifications.uuid.uuid4", return_value=fake_uuid ) - template = sample_template if template_type == SMS_TYPE else sample_email_template + template = sample_template if template_type == TemplateType.SMS else sample_email_template to = ( sample_template.service.created_by.mobile_number - if template_type == SMS_TYPE + if template_type == TemplateType.SMS else sample_email_template.service.created_by.email_address ) data = {"to": to, "template": template.id} @@ -907,7 +916,10 @@ def test_should_not_persist_notification_or_send_sms_if_simulated_number( @pytest.mark.parametrize("key_type", [KEY_TYPE_NORMAL, KEY_TYPE_TEAM]) @pytest.mark.parametrize( "notification_type, to", - [(SMS_TYPE, "2028675300"), (EMAIL_TYPE, "non_guest_list_recipient@mail.com")], + [ + (TemplateType.SMS, "2028675300"), + (TemplateType.EMAIL, "non_guest_list_recipient@mail.com"), + ], ) def test_should_not_send_notification_to_non_guest_list_recipient_in_trial_mode( client, sample_service_guest_list, notification_type, to, key_type, mocker @@ -962,8 +974,8 @@ def test_should_not_send_notification_to_non_guest_list_recipient_in_trial_mode( @pytest.mark.parametrize( "notification_type, to, normalized_to", [ - (SMS_TYPE, "2028675300", "+12028675300"), - (EMAIL_TYPE, "guest_list_recipient@mail.com", None), + (NotificationType.SMS, "2028675300", "+12028675300"), + (NotificationType.EMAIL, "guest_list_recipient@mail.com", None), ], ) def test_should_send_notification_to_guest_list_recipient( @@ -983,9 +995,9 @@ def test_should_send_notification_to_guest_list_recipient( "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) ) template = create_template(sample_service, template_type=notification_type) - if notification_type == SMS_TYPE: + if notification_type == NotificationType.SMS: service_guest_list = create_service_guest_list(sample_service, mobile_number=to) - elif notification_type == EMAIL_TYPE: + elif notification_type == NotificationType.EMAIL: service_guest_list = create_service_guest_list(sample_service, email_address=to) assert service_guest_list.service_id == sample_service.id @@ -1022,8 +1034,8 @@ def test_should_send_notification_to_guest_list_recipient( @pytest.mark.parametrize( "notification_type, template_type, to", [ - (EMAIL_TYPE, SMS_TYPE, "notify@digital.fake.gov"), - (SMS_TYPE, EMAIL_TYPE, "+12028675309"), + (NotificationType.EMAIL, TemplateType.SMS, "notify@digital.fake.gov"), + (NotificationType.SMS, TemplateType.EMAIL, "+12028675309"), ], ) def test_should_error_if_notification_type_does_not_match_template_type( @@ -1070,7 +1082,11 @@ def test_create_template_doesnt_raise_with_too_much_personalisation( @pytest.mark.parametrize( - "template_type, should_error", [(SMS_TYPE, True), (EMAIL_TYPE, False)] + "template_type, should_error", + [ + (TemplateType.SMS, True), + (TemplateType.EMAIL, False), + ] ) def test_create_template_raises_invalid_request_when_content_too_large( sample_service, template_type, should_error @@ -1338,7 +1354,7 @@ def test_post_notification_should_set_reply_to_text( ) template = create_template(sample_service, template_type=notification_type) expected_reply_to = current_app.config["FROM_NUMBER"] - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: expected_reply_to = "reply_to@gov.uk" create_reply_to_email( service=sample_service, email_address=expected_reply_to, is_default=True diff --git a/tests/app/service/send_notification/test_send_one_off_notification.py b/tests/app/service/send_notification/test_send_one_off_notification.py index b631420d4..c7b369444 100644 --- a/tests/app/service/send_notification/test_send_one_off_notification.py +++ b/tests/app/service/send_notification/test_send_one_off_notification.py @@ -8,13 +8,13 @@ from notifications_utils.recipients import InvalidPhoneError from app.config import QueueNames from app.dao.service_guest_list_dao import dao_add_and_commit_guest_list_contacts from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, - MOBILE_TYPE, PRIORITY, - SMS_TYPE, + GuestListRecipientType, Notification, + NotificationType, ServiceGuestList, + TemplateType, ) from app.service.send_notification import send_one_off_notification from app.v2.errors import BadRequestError @@ -69,7 +69,7 @@ def test_send_one_off_notification_calls_persist_correctly_for_sms( service = create_service() template = create_template( service=service, - template_type=SMS_TYPE, + template_type=TemplateType.SMS, content="Hello (( Name))\nYour thing is due soon", ) @@ -88,7 +88,7 @@ def test_send_one_off_notification_calls_persist_correctly_for_sms( recipient=post_data["to"], service=template.service, personalisation={"name": "foo"}, - notification_type=SMS_TYPE, + notification_type=NotificationType.SMS, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=str(service.created_by_id), @@ -104,7 +104,7 @@ def test_send_one_off_notification_calls_persist_correctly_for_international_sms service = create_service(service_permissions=["sms", "international_sms"]) template = create_template( service=service, - template_type=SMS_TYPE, + template_type=TemplateType.SMS, ) post_data = { @@ -125,7 +125,7 @@ def test_send_one_off_notification_calls_persist_correctly_for_email( service = create_service() template = create_template( service=service, - template_type=EMAIL_TYPE, + template_type=TemplateType.EMAIL, subject="Test subject", content="Hello (( Name))\nYour thing is due soon", ) @@ -145,7 +145,7 @@ def test_send_one_off_notification_calls_persist_correctly_for_email( recipient=post_data["to"], service=template.service, personalisation={"name": "foo"}, - notification_type=EMAIL_TYPE, + notification_type=NotificationType.EMAIL, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=str(service.created_by_id), @@ -203,7 +203,7 @@ def test_send_one_off_notification_raises_if_cant_send_to_recipient( template = create_template(service=service) dao_add_and_commit_guest_list_contacts( [ - ServiceGuestList.from_string(service.id, MOBILE_TYPE, "2028765309"), + ServiceGuestList.from_string(service.id, GuestListRecipientType.MOBILE, "2028765309"), ] ) @@ -286,7 +286,7 @@ def test_send_one_off_notification_should_add_email_reply_to_text_for_notificati def test_send_one_off_sms_notification_should_use_sms_sender_reply_to_text( sample_service, celery_mock ): - template = create_template(service=sample_service, template_type=SMS_TYPE) + template = create_template(service=sample_service, template_type=TemplateType.SMS) sms_sender = create_service_sms_sender( service=sample_service, sms_sender="2028675309", is_default=False ) @@ -310,7 +310,7 @@ def test_send_one_off_sms_notification_should_use_sms_sender_reply_to_text( def test_send_one_off_sms_notification_should_use_default_service_reply_to_text( sample_service, celery_mock ): - template = create_template(service=sample_service, template_type=SMS_TYPE) + template = create_template(service=sample_service, template_type=TemplateType.SMS) sample_service.service_sms_senders[0].is_default = False create_service_sms_sender( service=sample_service, sms_sender="2028675309", is_default=True diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 7a5a92222..d9ab3fd07 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -15,23 +15,21 @@ from app.dao.services_dao import dao_add_user_to_service, dao_remove_user_from_s from app.dao.templates_dao import dao_redact_template from app.dao.users_dao import save_model_user from app.models import ( - EMAIL_AUTH_TYPE, - EMAIL_TYPE, - INBOUND_SMS_TYPE, - INTERNATIONAL_SMS_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - SMS_TYPE, AnnualBilling, EmailBranding, InboundNumber, Notification, + NotificationType, Permission, Service, ServiceEmailReplyTo, ServicePermission, + ServicePermissionType, ServiceSmsSender, + TemplateType, User, ) from tests import create_admin_authorization_header @@ -285,9 +283,9 @@ def test_get_service_list_has_default_permissions(admin_request, service_factory assert all( set(json["permissions"]) == { - EMAIL_TYPE, - SMS_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + ServicePermissionType.INTERNATIONAL_SMS, } for json in json_resp["data"] ) @@ -301,9 +299,9 @@ def test_get_service_by_id_has_default_service_permissions( ) assert set(json_resp["data"]["permissions"]) == { - EMAIL_TYPE, - SMS_TYPE, - INTERNATIONAL_SMS_TYPE, + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + ServicePermissionType.INTERNATIONAL_SMS, } @@ -769,7 +767,7 @@ def test_update_service_flags(client, sample_service): json_resp = resp.json assert resp.status_code == 200 assert json_resp["data"]["name"] == sample_service.name - data = {"permissions": [INTERNATIONAL_SMS_TYPE]} + data = {"permissions": {ServicePermissionType.INTERNATIONAL_SMS}} auth_header = create_admin_authorization_header() @@ -780,7 +778,9 @@ def test_update_service_flags(client, sample_service): ) result = resp.json assert resp.status_code == 200 - assert set(result["data"]["permissions"]) == set([INTERNATIONAL_SMS_TYPE]) + assert set(result["data"]["permissions"]) == { + ServicePermissionType.INTERNATIONAL_SMS + } @pytest.mark.parametrize( @@ -854,7 +854,7 @@ def test_update_service_flags_with_service_without_default_service_permissions( ): auth_header = create_admin_authorization_header() data = { - "permissions": [INTERNATIONAL_SMS_TYPE], + "permissions": {ServicePermissionType.INTERNATIONAL_SMS}, } resp = client.post( @@ -865,7 +865,9 @@ def test_update_service_flags_with_service_without_default_service_permissions( result = resp.json assert resp.status_code == 200 - assert set(result["data"]["permissions"]) == set([INTERNATIONAL_SMS_TYPE]) + assert set(result["data"]["permissions"]) == { + ServicePermissionType.INTERNATIONAL_SMS, + } def test_update_service_flags_will_remove_service_permissions( @@ -874,12 +876,18 @@ def test_update_service_flags_will_remove_service_permissions( auth_header = create_admin_authorization_header() service = create_service( - service_permissions=[SMS_TYPE, EMAIL_TYPE, INTERNATIONAL_SMS_TYPE] + service_permissions={ + ServicePermissionType.SMS, + ServicePermissionType.EMAIL, + ServicePermissionType.INTERNATIONAL_SMS, + } ) - assert INTERNATIONAL_SMS_TYPE in [p.permission for p in service.permissions] + assert ServicePermissionType.INTERNATIONAL_SMS in { + p.permission for p in service.permissions + } - data = {"permissions": [SMS_TYPE, EMAIL_TYPE]} + data = {"permissions": {ServicePermissionType.SMS, ServicePermissionType.EMAIL}} resp = client.post( "/service/{}".format(service.id), @@ -889,10 +897,13 @@ def test_update_service_flags_will_remove_service_permissions( result = resp.json assert resp.status_code == 200 - assert INTERNATIONAL_SMS_TYPE not in result["data"]["permissions"] + assert ServicePermissionType.INTERNATIONAL_SMS not in result["data"]["permissions"] permissions = ServicePermission.query.filter_by(service_id=service.id).all() - assert set([p.permission for p in permissions]) == set([SMS_TYPE, EMAIL_TYPE]) + assert {p.permission for p in permissions} == { + ServicePermissionType.SMS, + ServicePermissionType.EMAIL, + } def test_update_permissions_will_override_permission_flags( @@ -900,7 +911,7 @@ def test_update_permissions_will_override_permission_flags( ): auth_header = create_admin_authorization_header() - data = {"permissions": [INTERNATIONAL_SMS_TYPE]} + data = {"permissions": {ServicePermissionType.INTERNATIONAL_SMS}} resp = client.post( "/service/{}".format(service_with_no_permissions.id), @@ -910,7 +921,9 @@ def test_update_permissions_will_override_permission_flags( result = resp.json assert resp.status_code == 200 - assert set(result["data"]["permissions"]) == set([INTERNATIONAL_SMS_TYPE]) + assert set(result["data"]["permissions"]) == { + ServicePermissionType.INTERNATIONAL_SMS + } def test_update_service_permissions_will_add_service_permissions( @@ -918,7 +931,7 @@ def test_update_service_permissions_will_add_service_permissions( ): auth_header = create_admin_authorization_header() - data = {"permissions": [EMAIL_TYPE, SMS_TYPE]} + data = {"permissions": {ServicePermissionType.EMAIL, ServicePermissionType.SMS}} resp = client.post( "/service/{}".format(sample_service.id), @@ -928,17 +941,20 @@ def test_update_service_permissions_will_add_service_permissions( result = resp.json assert resp.status_code == 200 - assert set(result["data"]["permissions"]) == set([SMS_TYPE, EMAIL_TYPE]) + assert set(result["data"]["permissions"]) == { + ServicePermissionType.SMS, + ServicePermissionType.EMAIL, + } @pytest.mark.parametrize( "permission_to_add", [ - (EMAIL_TYPE), - (SMS_TYPE), - (INTERNATIONAL_SMS_TYPE), - (INBOUND_SMS_TYPE), - (EMAIL_AUTH_TYPE), + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + ServicePermissionType.INTERNATIONAL_SMS, + ServicePermissionType.INBOUND_SMS, + ServicePermissionType.EMAIL_AUTH, ], ) def test_add_service_permission_will_add_permission( @@ -968,7 +984,13 @@ def test_update_permissions_with_an_invalid_permission_will_raise_error( auth_header = create_admin_authorization_header() invalid_permission = "invalid_permission" - data = {"permissions": [EMAIL_TYPE, SMS_TYPE, invalid_permission]} + data = { + "permissions": { + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + invalid_permission, + } + } resp = client.post( "/service/{}".format(sample_service.id), @@ -990,7 +1012,13 @@ def test_update_permissions_with_duplicate_permissions_will_raise_error( ): auth_header = create_admin_authorization_header() - data = {"permissions": [EMAIL_TYPE, SMS_TYPE, SMS_TYPE]} + data = { + "permissions": { + ServicePermissionType.EMAIL, + ServicePermissionType.SMS, + ServicePermissionType.SMS, + } + } resp = client.post( "/service/{}".format(sample_service.id), @@ -1002,7 +1030,7 @@ def test_update_permissions_with_duplicate_permissions_will_raise_error( assert resp.status_code == 400 assert result["result"] == "error" assert ( - "Duplicate Service Permission: ['{}']".format(SMS_TYPE) + f"Duplicate Service Permission: ['{ServicePermissionType.SMS}']" in result["message"]["permissions"] ) @@ -1695,7 +1723,7 @@ def test_get_all_notifications_for_service_filters_notifications_when_using_post service_2 = create_service(service_name="2") service_1_sms_template = create_template(service_1) - service_1_email_template = create_template(service_1, template_type=EMAIL_TYPE) + service_1_email_template = create_template(service_1, template_type=TemplateType.EMAIL) service_2_sms_template = create_template(service_2) returned_notification = create_notification( @@ -2040,8 +2068,11 @@ def test_get_detailed_service( service = resp.json["data"] assert service["id"] == str(sample_service.id) assert "statistics" in service.keys() - assert set(service["statistics"].keys()) == {SMS_TYPE, EMAIL_TYPE} - assert service["statistics"][SMS_TYPE] == stats + assert set(service["statistics"].keys()) == { + NotificationType.SMS.value, + NotificationType.EMAIL.value, + } + assert service["statistics"][NotificationType.SMS.value] == stats def test_get_services_with_detailed_flag(client, sample_template): @@ -2060,8 +2091,8 @@ def test_get_services_with_detailed_flag(client, sample_template): assert data[0]["name"] == "Sample service" assert data[0]["id"] == str(notifications[0].service_id) assert data[0]["statistics"] == { - EMAIL_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, - SMS_TYPE: {"delivered": 0, "failed": 0, "requested": 3}, + NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 3}, } @@ -2083,8 +2114,8 @@ def test_get_services_with_detailed_flag_excluding_from_test_key( data = resp.json["data"] assert len(data) == 1 assert data[0]["statistics"] == { - EMAIL_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, - SMS_TYPE: {"delivered": 0, "failed": 0, "requested": 2}, + NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 2}, } @@ -2153,13 +2184,13 @@ def test_get_detailed_services_groups_by_service(notify_db_session): assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - EMAIL_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, - SMS_TYPE: {"delivered": 1, "failed": 0, "requested": 3}, + NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS.value: {"delivered": 1, "failed": 0, "requested": 3}, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - EMAIL_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, - SMS_TYPE: {"delivered": 0, "failed": 0, "requested": 1}, + NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 1}, } @@ -2182,13 +2213,13 @@ def test_get_detailed_services_includes_services_with_no_notifications( assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - EMAIL_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, - SMS_TYPE: {"delivered": 0, "failed": 0, "requested": 1}, + NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 1}, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - EMAIL_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, - SMS_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 0}, } @@ -2208,8 +2239,8 @@ def test_get_detailed_services_only_includes_todays_notifications(sample_templat assert len(data) == 1 assert data[0]["statistics"] == { - EMAIL_TYPE: {"delivered": 0, "failed": 0, "requested": 0}, - SMS_TYPE: {"delivered": 0, "failed": 0, "requested": 3}, + NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 3}, } @@ -2251,12 +2282,12 @@ def test_get_detailed_services_for_date_range( ) assert len(data) == 1 - assert data[0]["statistics"][EMAIL_TYPE] == { + assert data[0]["statistics"][NotificationType.EMAIL.value] == { "delivered": 0, "failed": 0, "requested": 0, } - assert data[0]["statistics"][SMS_TYPE] == { + assert data[0]["statistics"][NotificationType.SMS.value] == { "delivered": 2, "failed": 0, "requested": 2, @@ -2622,7 +2653,7 @@ def test_get_all_notifications_for_service_includes_template_redacted( # TODO: check whether all hidden templates are also precompiled letters # def test_get_all_notifications_for_service_includes_template_hidden(admin_request, sample_service): -# letter_template = create_template(sample_service, template_type=LETTER_TYPE) +# letter_template = create_template(sample_service, template_type=TemplateType.LETTER) # with freeze_time('2000-01-01'): # letter_noti = create_notification(letter_template) diff --git a/tests/app/service/test_sender.py b/tests/app/service/test_sender.py index fbc8b784b..5f28fefed 100644 --- a/tests/app/service/test_sender.py +++ b/tests/app/service/test_sender.py @@ -2,12 +2,12 @@ import pytest from flask import current_app from app.dao.services_dao import dao_add_user_to_service -from app.models import EMAIL_TYPE, SMS_TYPE, Notification +from app.models import Notification, NotificationType, TemplateType from app.service.sender import send_notification_to_service_users from tests.app.db import create_service, create_template, create_user -@pytest.mark.parametrize("notification_type", [EMAIL_TYPE, SMS_TYPE]) +@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS]) def test_send_notification_to_service_users_persists_notifications_correctly( notify_service, notification_type, sample_service, mocker ): @@ -37,7 +37,7 @@ def test_send_notification_to_service_users_sends_to_queue( ): send_mock = mocker.patch("app.service.sender.send_notification_to_queue") - template = create_template(sample_service, template_type=EMAIL_TYPE) + template = create_template(sample_service, template_type=NotificationType.EMAIL) send_notification_to_service_users( service_id=sample_service.id, template_id=template.id ) @@ -54,7 +54,7 @@ def test_send_notification_to_service_users_includes_user_fields_in_personalisat user = sample_service.users[0] - template = create_template(sample_service, template_type=EMAIL_TYPE) + template = create_template(sample_service, template_type=TemplateType.EMAIL) send_notification_to_service_users( service_id=sample_service.id, template_id=template.id, @@ -82,7 +82,7 @@ def test_send_notification_to_service_users_sends_to_active_users_only( service = create_service(user=first_active_user) dao_add_user_to_service(service, second_active_user) dao_add_user_to_service(service, pending_user) - template = create_template(service, template_type=EMAIL_TYPE) + template = create_template(service, template_type=TemplateType.EMAIL) send_notification_to_service_users(service_id=service.id, template_id=template.id) diff --git a/tests/app/service/test_service_guest_list.py b/tests/app/service/test_service_guest_list.py index 0e74bce2a..918a8e399 100644 --- a/tests/app/service/test_service_guest_list.py +++ b/tests/app/service/test_service_guest_list.py @@ -2,7 +2,7 @@ import json import uuid from app.dao.service_guest_list_dao import dao_add_and_commit_guest_list_contacts -from app.models import EMAIL_TYPE, MOBILE_TYPE, ServiceGuestList +from app.models import GuestListRecipientType, ServiceGuestList from tests import create_admin_authorization_header @@ -24,11 +24,15 @@ def test_get_guest_list_separates_emails_and_phones(client, sample_service): dao_add_and_commit_guest_list_contacts( [ ServiceGuestList.from_string( - sample_service.id, EMAIL_TYPE, "service@example.com" + sample_service.id, + GuestListRecipientType.EMAIL, + "service@example.com", ), - ServiceGuestList.from_string(sample_service.id, MOBILE_TYPE, "2028675309"), + ServiceGuestList.from_string(sample_service.id, GuestListRecipientType.MOBILE, "2028675309"), ServiceGuestList.from_string( - sample_service.id, MOBILE_TYPE, "+1800-555-5555" + sample_service.id, + GuestListRecipientType.MOBILE, + "+1800-555-5555", ), ] ) diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index 7059b3a5b..b0cd42b3b 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -5,11 +5,11 @@ import pytest from freezegun import freeze_time from app.models import ( - EMAIL_TYPE, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - SMS_TYPE, + NotificationType, + TemplateType, ) from tests.app.db import ( create_ft_notification_status, @@ -58,7 +58,7 @@ def test_get_template_usage_by_month_returns_two_templates( ): template_one = create_template( sample_service, - template_type=SMS_TYPE, + template_type=TemplateType.SMS, template_name="TEST TEMPLATE", hidden=True, ) @@ -123,8 +123,11 @@ def test_get_service_notification_statistics( today_only=today_only, ) - assert set(resp["data"].keys()) == {SMS_TYPE, EMAIL_TYPE} - assert resp["data"][SMS_TYPE] == stats + assert set(resp["data"].keys()) == { + NotificationType.SMS.value, + NotificationType.EMAIL.value, + } + assert resp["data"][NotificationType.SMS.value] == stats def test_get_service_notification_statistics_with_unknown_service(admin_request): @@ -133,8 +136,8 @@ def test_get_service_notification_statistics_with_unknown_service(admin_request) ) assert resp["data"] == { - SMS_TYPE: {"requested": 0, "delivered": 0, "failed": 0}, - EMAIL_TYPE: {"requested": 0, "delivered": 0, "failed": 0}, + NotificationType.SMS.value: {"requested": 0, "delivered": 0, "failed": 0}, + NotificationType.EMAIL.value: {"requested": 0, "delivered": 0, "failed": 0}, } @@ -198,7 +201,7 @@ def test_get_monthly_notification_stats_returns_empty_stats_with_correct_dates( def test_get_monthly_notification_stats_returns_stats(admin_request, sample_service): sms_t1 = create_template(sample_service) sms_t2 = create_template(sample_service) - email_template = create_template(sample_service, template_type=EMAIL_TYPE) + email_template = create_template(sample_service, template_type=TemplateType.EMAIL) create_ft_notification_status(datetime(2016, 6, 1), template=sms_t1) create_ft_notification_status(datetime(2016, 6, 2), template=sms_t1) diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index d4619ea3f..cc145ca0b 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -9,7 +9,7 @@ from freezegun import freeze_time from notifications_utils import SMS_CHAR_COUNT_LIMIT from app.dao.templates_dao import dao_get_template_by_id, dao_redact_template -from app.models import EMAIL_TYPE, SMS_TYPE, Template, TemplateHistory +from app.models import ServicePermissionType, Template, TemplateHistory, TemplateType from tests import create_admin_authorization_header from tests.app.db import create_service, create_template, create_template_folder @@ -17,8 +17,8 @@ from tests.app.db import create_service, create_template, create_template_folder @pytest.mark.parametrize( "template_type, subject", [ - (SMS_TYPE, None), - (EMAIL_TYPE, "subject"), + (TemplateType.SMS, None), + (TemplateType.EMAIL, "subject"), ], ) def test_should_create_a_new_template_for_a_service( @@ -149,7 +149,7 @@ def test_should_raise_error_if_service_does_not_exist_on_create( ): data = { "name": "my template", - "template_type": SMS_TYPE, + "template_type": TemplateType.SMS, "content": "template content", "service": fake_uuid, "created_by": str(sample_user.id), @@ -172,14 +172,14 @@ def test_should_raise_error_if_service_does_not_exist_on_create( "permissions, template_type, subject, expected_error", [ ( - [EMAIL_TYPE], - SMS_TYPE, + [ServicePermissionType.EMAIL], + TemplateType.SMS, None, {"template_type": ["Creating text message templates is not allowed"]}, ), ( - [SMS_TYPE], - EMAIL_TYPE, + [ServicePermissionType.SMS], + TemplateType.EMAIL, "subject", {"template_type": ["Creating email templates is not allowed"]}, ), @@ -217,13 +217,13 @@ def test_should_raise_error_on_create_if_no_permission( "template_type, permissions, expected_error", [ ( - SMS_TYPE, - [EMAIL_TYPE], + TemplateType.SMS, + [ServicePermissionType.EMAIL], {"template_type": ["Updating text message templates is not allowed"]}, ), ( - EMAIL_TYPE, - [SMS_TYPE], + TemplateType.EMAIL, + [ServicePErmissionType.SMS], {"template_type": ["Updating email templates is not allowed"]}, ), ], @@ -261,7 +261,7 @@ def test_should_error_if_created_by_missing(client, sample_user, sample_service) service_id = str(sample_service.id) data = { "name": "my template", - "template_type": SMS_TYPE, + "template_type": TemplateType.SMS, "content": "template content", "service": service_id, } @@ -295,7 +295,7 @@ def test_should_be_error_if_service_does_not_exist_on_update(client, fake_uuid): assert json_resp["message"] == "No result found" -@pytest.mark.parametrize("template_type", [EMAIL_TYPE]) +@pytest.mark.parametrize("template_type", [TemplateType.EMAIL]) def test_must_have_a_subject_on_an_email_template( client, sample_user, sample_service, template_type ): @@ -412,7 +412,7 @@ def test_should_be_able_to_get_all_templates_for_a_service( ): data = { "name": "my template 1", - "template_type": EMAIL_TYPE, + "template_type": TemplateType.EMAIL, "subject": "subject 1", "content": "template content", "service": str(sample_service.id), @@ -421,7 +421,7 @@ def test_should_be_able_to_get_all_templates_for_a_service( data_1 = json.dumps(data) data = { "name": "my template 2", - "template_type": EMAIL_TYPE, + "template_type": TemplateType.EMAIL, "subject": "subject 2", "content": "template content", "service": str(sample_service.id), @@ -529,8 +529,8 @@ def test_should_get_return_all_fields_by_default( @pytest.mark.parametrize( "template_type, expected_content", ( - (EMAIL_TYPE, None), - (SMS_TYPE, None), + (TemplateType.EMAIL, None), + (TemplateType.SMS, None), ), ) def test_should_not_return_content_and_subject_if_requested( @@ -566,9 +566,9 @@ def test_should_not_return_content_and_subject_if_requested( ( "about your ((thing))", "hello ((name)) we’ve received your ((thing))", - EMAIL_TYPE, + TemplateType.EMAIL, ), - (None, "hello ((name)) we’ve received your ((thing))", SMS_TYPE), + (None, "hello ((name)) we’ve received your ((thing))", TemplateType.SMS), ], ) def test_should_get_a_single_template( @@ -640,7 +640,10 @@ def test_should_preview_a_single_template( expected_error, ): template = create_template( - sample_service, template_type=EMAIL_TYPE, subject=subject, content=content + sample_service, + template_type=TemplateType.EMAIL, + subject=subject, + content=content, ) response = client.get( @@ -687,7 +690,7 @@ def test_should_return_404_if_no_templates_for_service_with_id( assert json_resp["message"] == "No result found" -@pytest.mark.parametrize("template_type", (SMS_TYPE,)) +@pytest.mark.parametrize("template_type", (TemplateType.SMS,)) def test_create_400_for_over_limit_content( client, notify_api, diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 0a4185416..7744a8316 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -11,12 +11,11 @@ from app import db from app.dao.services_dao import dao_fetch_service_by_id from app.dao.users_dao import create_user_code from app.models import ( - EMAIL_TYPE, - SMS_TYPE, USER_AUTH_TYPES, Notification, User, VerifyCode, + VerifyCodeType, ) from tests import create_admin_authorization_header @@ -103,7 +102,7 @@ def test_user_verify_code_rejects_good_code_if_too_many_failed_logins( @freeze_time("2020-04-01 12:00") -@pytest.mark.parametrize("code_type", [EMAIL_TYPE, SMS_TYPE]) +@pytest.mark.parametrize("code_type", [VerifyCodeType.EMAIL, VerifyCodeType.SMS]) def test_user_verify_code_expired_code_and_increments_failed_login_count( code_type, admin_request, sample_user ): @@ -527,7 +526,7 @@ def test_user_verify_email_code(admin_request, sample_user, auth_type): sample_user.email_access_validated_at = datetime.utcnow() - timedelta(days=1) sample_user.auth_type = auth_type magic_code = str(uuid.uuid4()) - verify_code = create_user_code(sample_user, magic_code, EMAIL_TYPE) + verify_code = create_user_code(sample_user, magic_code, VerifyCodeType.EMAIL) data = {"code_type": "email", "code": magic_code} @@ -544,7 +543,7 @@ def test_user_verify_email_code(admin_request, sample_user, auth_type): assert sample_user.current_session_id is not None -@pytest.mark.parametrize("code_type", [EMAIL_TYPE, SMS_TYPE]) +@pytest.mark.parametrize("code_type", [VerifyCodeType.EMAIL, VerifyCodeType.SMS]) @freeze_time("2016-01-01T12:00:00") def test_user_verify_email_code_fails_if_code_already_used( admin_request, sample_user, code_type diff --git a/tests/app/v2/notifications/test_notification_schemas.py b/tests/app/v2/notifications/test_notification_schemas.py index 8ea8ad1c5..fab4e22e7 100644 --- a/tests/app/v2/notifications/test_notification_schemas.py +++ b/tests/app/v2/notifications/test_notification_schemas.py @@ -5,7 +5,7 @@ from flask import json from freezegun import freeze_time from jsonschema import ValidationError -from app.models import EMAIL_TYPE, NOTIFICATION_CREATED +from app.models import NOTIFICATION_CREATED, TemplateType from app.schema_validation import validate from app.v2.notifications.notification_schemas import get_notifications_request from app.v2.notifications.notification_schemas import ( @@ -20,7 +20,7 @@ valid_get_json = {} valid_get_with_optionals_json = { "reference": "test reference", "status": [NOTIFICATION_CREATED], - "template_type": [EMAIL_TYPE], + "template_type": [TemplateType.EMAIL], "include_jobs": "true", "older_than": "a5149c32-f03b-4711-af49-ad6993797d45", } diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index bb9a58453..36bfb5d67 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -9,11 +9,10 @@ from flask import current_app, json from app.dao import templates_dao from app.dao.service_sms_sender_dao import dao_update_service_sms_sender from app.models import ( - EMAIL_TYPE, - INTERNATIONAL_SMS_TYPE, NOTIFICATION_CREATED, - SMS_TYPE, Notification, + NotificationType, + ServicePermissionType, ) from app.schema_validation import validate from app.v2.errors import RateLimitError @@ -529,9 +528,9 @@ def test_post_email_notification_returns_201( @pytest.mark.parametrize( "recipient, notification_type", [ - ("simulate-delivered@notifications.service.gov.uk", EMAIL_TYPE), - ("simulate-delivered-2@notifications.service.gov.uk", EMAIL_TYPE), - ("simulate-delivered-3@notifications.service.gov.uk", EMAIL_TYPE), + ("simulate-delivered@notifications.service.gov.uk", NotificationType.EMAIL), + ("simulate-delivered-2@notifications.service.gov.uk", NotificationType.EMAIL), + ("simulate-delivered-3@notifications.service.gov.uk", NotificationType.EMAIL), ("+14254147167", "sms"), ("+14254147755", "sms"), ], @@ -655,7 +654,7 @@ def test_post_sms_notification_returns_400_if_not_allowed_to_send_int_sms( client, notify_db_session, ): - service = create_service(service_permissions=[SMS_TYPE]) + service = create_service(service_permissions=[ServicePermissionType.SMS]) template = create_template(service=service) data = {"phone_number": "+20-12-1234-1234", "template_id": template.id} @@ -762,7 +761,7 @@ def test_post_sms_notification_returns_400_if_number_not_in_guest_list( notify_db_session, client, restricted ): service = create_service( - restricted=restricted, service_permissions=[SMS_TYPE, INTERNATIONAL_SMS_TYPE] + restricted=restricted, service_permissions=[ServicePermissionType.SMS, ServicePermissionType.INTERNATIONAL_SMS] ) template = create_template(service=service) create_api_key(service=service, key_type="team") @@ -860,7 +859,7 @@ def test_post_notification_raises_bad_request_if_not_valid_notification_type( def test_post_notification_with_wrong_type_of_sender( client, sample_template, sample_email_template, notification_type, fake_uuid ): - if notification_type == EMAIL_TYPE: + if notification_type == NotificationType.EMAIL: template = sample_email_template form_label = "sms_sender_id" data = { @@ -868,7 +867,7 @@ def test_post_notification_with_wrong_type_of_sender( "template_id": str(sample_email_template.id), form_label: fake_uuid, } - elif notification_type == SMS_TYPE: + elif notification_type == ServicePermissionType.SMS: template = sample_template form_label = "email_reply_to_id" data = { @@ -997,7 +996,7 @@ def test_post_email_notification_with_archived_reply_to_id_returns_400( def test_post_notification_with_document_upload( client, notify_db_session, mocker, csv_param ): - service = create_service(service_permissions=[EMAIL_TYPE]) + service = create_service(service_permissions=[ServicePermissionType.EMAIL]) service.contact_link = "contact.me@gov.uk" template = create_template( service=service, @@ -1055,7 +1054,7 @@ def test_post_notification_with_document_upload( def test_post_notification_with_document_upload_simulated( client, notify_db_session, mocker ): - service = create_service(service_permissions=[EMAIL_TYPE]) + service = create_service(service_permissions=[ServicePermissionType.EMAIL]) service.contact_link = "contact.me@gov.uk" template = create_template( service=service, template_type="email", content="Document: ((document))" @@ -1092,7 +1091,7 @@ def test_post_notification_with_document_upload_simulated( def test_post_notification_without_document_upload_permission( client, notify_db_session, mocker ): - service = create_service(service_permissions=[EMAIL_TYPE]) + service = create_service(service_permissions=[ServicePermissionType.EMAIL]) template = create_template( service=service, template_type="email", content="Document: ((document))" ) @@ -1234,7 +1233,7 @@ def test_post_notifications_saves_email_or_sms_to_queue( } data.update( {"email_address": "joe.citizen@example.com"} - ) if notification_type == EMAIL_TYPE else data.update( + ) if notification_type == NotificationType.EMAIL else data.update( {"phone_number": "+447700900855"} ) @@ -1297,7 +1296,7 @@ def test_post_notifications_saves_email_or_sms_normally_if_saving_to_queue_fails } data.update( {"email_address": "joe.citizen@example.com"} - ) if notification_type == EMAIL_TYPE else data.update( + ) if notification_type == NotificationType.EMAIL else data.update( {"phone_number": "+447700900855"} ) @@ -1353,7 +1352,7 @@ def test_post_notifications_doesnt_use_save_queue_for_test_notifications( } data.update( {"email_address": "joe.citizen@example.com"} - ) if notification_type == EMAIL_TYPE else data.update( + ) if notification_type == NotificationType.EMAIL else data.update( {"phone_number": "+447700900855"} ) response = client.post( diff --git a/tests/app/v2/template/test_get_template.py b/tests/app/v2/template/test_get_template.py index a49ab2438..9fe698649 100644 --- a/tests/app/v2/template/test_get_template.py +++ b/tests/app/v2/template/test_get_template.py @@ -1,7 +1,7 @@ import pytest from flask import json -from app.models import EMAIL_TYPE, SMS_TYPE, TEMPLATE_TYPES +from app.models import TemplateType from app.utils import DATETIME_FORMAT from tests import create_service_authorization_header from tests.app.db import create_template @@ -12,8 +12,8 @@ valid_version_params = [None, 1] @pytest.mark.parametrize( "tmp_type, expected_name, expected_subject", [ - (SMS_TYPE, "sms Template Name", None), - (EMAIL_TYPE, "email Template Name", "Template subject"), + (TemplateType.SMS, "sms Template Name", None), + (TemplateType.EMAIL, "email Template Name", "Template subject"), ], ) @pytest.mark.parametrize("version", valid_version_params) @@ -56,7 +56,7 @@ def test_get_template_by_id_returns_200( [ ( { - "template_type": SMS_TYPE, + "template_type": TemplateType.SMS, "content": "Hello ((placeholder)) ((conditional??yes))", }, { @@ -66,7 +66,7 @@ def test_get_template_by_id_returns_200( ), ( { - "template_type": EMAIL_TYPE, + "template_type": TemplateType.EMAIL, "subject": "((subject))", "content": "((content))", }, @@ -120,7 +120,7 @@ def test_get_template_with_non_existent_template_id_returns_404( } -@pytest.mark.parametrize("tmp_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("tmp_type", list(TemplateType)) def test_get_template_with_non_existent_version_returns_404( client, sample_service, tmp_type ): diff --git a/tests/app/v2/template/test_post_template.py b/tests/app/v2/template/test_post_template.py index 8985dd623..f9c5b7ff1 100644 --- a/tests/app/v2/template/test_post_template.py +++ b/tests/app/v2/template/test_post_template.py @@ -1,7 +1,7 @@ import pytest from flask import json -from app.models import EMAIL_TYPE, TEMPLATE_TYPES +from app.models import TemplateType from tests import create_service_authorization_header from tests.app.db import create_template @@ -59,7 +59,7 @@ valid_post = [ ] -@pytest.mark.parametrize("tmp_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("tmp_type", list(TemplateType)) @pytest.mark.parametrize( "subject,content,post_data,expected_subject,expected_content,expected_html", valid_post, @@ -93,7 +93,7 @@ def test_valid_post_template_returns_200( assert resp_json["id"] == str(template.id) - if tmp_type == EMAIL_TYPE: + if tmp_type == TemplateType.EMAIL: assert expected_subject in resp_json["subject"] assert resp_json["html"] == expected_html else: @@ -105,7 +105,7 @@ def test_valid_post_template_returns_200( def test_email_templates_not_rendered_into_content(client, sample_service): template = create_template( sample_service, - template_type=EMAIL_TYPE, + template_type=TemplateType.EMAIL, subject="Test", content=("Hello\n" "\r\n" "\r\n" "\n" "# This is a heading\n" "\n" "Paragraph"), ) @@ -125,7 +125,7 @@ def test_email_templates_not_rendered_into_content(client, sample_service): assert resp_json["body"] == template.content -@pytest.mark.parametrize("tmp_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("tmp_type", list(TemplateType)) def test_invalid_post_template_returns_400(client, sample_service, tmp_type): template = create_template( sample_service, diff --git a/tests/app/v2/template/test_template_schemas.py b/tests/app/v2/template/test_template_schemas.py index 75cf014e0..3c00e62b8 100644 --- a/tests/app/v2/template/test_template_schemas.py +++ b/tests/app/v2/template/test_template_schemas.py @@ -4,7 +4,7 @@ import pytest from flask import json from jsonschema.exceptions import ValidationError -from app.models import EMAIL_TYPE, SMS_TYPE, TEMPLATE_TYPES +from app.models import TemplateType from app.schema_validation import validate from app.v2.template.template_schemas import ( get_template_by_id_request, @@ -15,7 +15,7 @@ from app.v2.template.template_schemas import ( valid_json_get_response = { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-01-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -26,7 +26,7 @@ valid_json_get_response = { valid_json_get_response_with_optionals = { "id": str(uuid.uuid4()), - "type": EMAIL_TYPE, + "type": TemplateType.EMAIL, "created_at": "2017-01-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -114,7 +114,7 @@ def test_get_template_request_schema_against_invalid_args_is_invalid( assert error["message"] in error_message -@pytest.mark.parametrize("template_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("template_type", list(TemplateType)) @pytest.mark.parametrize( "response", [valid_json_get_response, valid_json_get_response_with_optionals] ) @@ -149,7 +149,7 @@ def test_post_template_preview_against_invalid_args_is_invalid(args, error_messa assert error["message"] in error_messages -@pytest.mark.parametrize("template_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("template_type", list(TemplateType)) @pytest.mark.parametrize( "response", [valid_json_post_response, valid_json_post_response_with_optionals] ) diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index 17dc5d1b3..c718a76d4 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -3,7 +3,7 @@ from itertools import product import pytest from flask import json -from app.models import EMAIL_TYPE, TEMPLATE_TYPES +from app.models import TemplateType from tests import create_service_authorization_header from tests.app.db import create_template @@ -13,10 +13,10 @@ def test_get_all_templates_returns_200(client, sample_service): create_template( sample_service, template_type=tmp_type, - subject="subject_{}".format(name) if tmp_type == EMAIL_TYPE else "", + subject="subject_{}".format(name) if tmp_type == TemplateType.EMAIL else "", template_name=name, ) - for name, tmp_type in product(("A", "B", "C"), TEMPLATE_TYPES) + for name, tmp_type in product(("A", "B", "C"), TemplateType) ] auth_header = create_service_authorization_header(service_id=sample_service.id) @@ -37,18 +37,18 @@ def test_get_all_templates_returns_200(client, sample_service): assert template["id"] == str(templates[index].id) assert template["body"] == templates[index].content assert template["type"] == templates[index].template_type - if templates[index].template_type == EMAIL_TYPE: + if templates[index].template_type == TemplateType.EMAIL: assert template["subject"] == templates[index].subject -@pytest.mark.parametrize("tmp_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("tmp_type", list(TemplateType)) def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tmp_type): templates = [ create_template( sample_service, template_type=tmp_type, template_name="Template {}".format(i), - subject="subject_{}".format(i) if tmp_type == EMAIL_TYPE else "", + subject="subject_{}".format(i) if tmp_type == TemplateType.EMAIL else "", ) for i in range(3) ] @@ -71,11 +71,11 @@ def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tm assert template["id"] == str(templates[index].id) assert template["body"] == templates[index].content assert template["type"] == tmp_type - if templates[index].template_type == EMAIL_TYPE: + if templates[index].template_type == TemplateType.EMAIL: assert template["subject"] == templates[index].subject -@pytest.mark.parametrize("tmp_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("tmp_type", list(TemplateType)) def test_get_correct_num_templates_for_valid_type_returns_200( client, sample_service, tmp_type ): @@ -85,7 +85,7 @@ def test_get_correct_num_templates_for_valid_type_returns_200( for _ in range(num_templates): templates.append(create_template(sample_service, template_type=tmp_type)) - for other_type in TEMPLATE_TYPES: + for other_type in TemplateType: if other_type != tmp_type: templates.append(create_template(sample_service, template_type=other_type)) diff --git a/tests/app/v2/templates/test_templates_schemas.py b/tests/app/v2/templates/test_templates_schemas.py index 1bdf715f2..71ac84df2 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -4,7 +4,7 @@ import pytest from flask import json from jsonschema.exceptions import ValidationError -from app.models import EMAIL_TYPE, SMS_TYPE, TEMPLATE_TYPES +from app.models import TemplateType from app.schema_validation import validate from app.v2.templates.templates_schemas import ( get_all_template_request, @@ -16,7 +16,7 @@ valid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-01-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -26,7 +26,7 @@ valid_json_get_all_response = [ }, { "id": str(uuid.uuid4()), - "type": EMAIL_TYPE, + "type": TemplateType.EMAIL, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": 2, @@ -41,7 +41,7 @@ valid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": 2, @@ -60,7 +60,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": "invalid_id", - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -77,7 +77,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": "invalid_version", @@ -94,7 +94,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "invalid_created_at", "updated_at": None, "version": 1, @@ -111,7 +111,7 @@ invalid_json_get_all_response = [ { "templates": [ { - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -128,7 +128,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -160,7 +160,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "updated_at": None, "version": 1, "created_by": "someone@test.com", @@ -176,7 +176,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "version": 1, "created_by": "someone@test.com", @@ -192,7 +192,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "created_by": "someone@test.com", @@ -208,7 +208,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -224,7 +224,7 @@ invalid_json_get_all_response = [ "templates": [ { "id": str(uuid.uuid4()), - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "version": 1, @@ -239,7 +239,7 @@ invalid_json_get_all_response = [ { "templates": [ { - "type": SMS_TYPE, + "type": TemplateType.SMS, "created_at": "2017-02-10T18:25:43.511Z", "updated_at": None, "created_by": "someone@test.com", @@ -256,19 +256,19 @@ invalid_json_get_all_response = [ ] -@pytest.mark.parametrize("template_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("template_type", list(TemplateType)) def test_get_all_template_request_schema_against_no_args_is_valid(template_type): data = {} assert validate(data, get_all_template_request) == data -@pytest.mark.parametrize("template_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("template_type", list(TemplateType)) def test_get_all_template_request_schema_against_valid_args_is_valid(template_type): data = {"type": template_type} assert validate(data, get_all_template_request) == data -@pytest.mark.parametrize("template_type", TEMPLATE_TYPES) +@pytest.mark.parametrize("template_type", list(TemplateType)) def test_get_all_template_request_schema_against_invalid_args_is_invalid(template_type): data = {"type": "unknown"} From df10c4693c861dca489ef5cf19abed878f251649 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 10:27:31 -0500 Subject: [PATCH 134/259] Formatting goodness. Signed-off-by: Cliff Hill --- tests/app/celery/test_nightly_tasks.py | 4 +- tests/app/conftest.py | 3 +- .../notification_dao/test_notification_dao.py | 14 ++++++- .../dao/test_fact_notification_status_dao.py | 28 +++++++++---- tests/app/dao/test_jobs_dao.py | 4 +- tests/app/dao/test_service_permissions_dao.py | 3 +- tests/app/dao/test_uploads_dao.py | 2 +- .../test_process_notification.py | 7 +++- .../test_receive_notification.py | 2 +- tests/app/notifications/test_validators.py | 41 +++++++++++++------ .../test_send_notification.py | 16 +++++--- .../test_send_one_off_notification.py | 4 +- tests/app/service/test_rest.py | 4 +- tests/app/service/test_sender.py | 4 +- tests/app/service/test_service_guest_list.py | 4 +- tests/app/template/test_rest.py | 2 +- tests/app/user/test_rest_verify.py | 8 +--- .../notifications/test_post_notifications.py | 6 ++- 18 files changed, 110 insertions(+), 46 deletions(-) diff --git a/tests/app/celery/test_nightly_tasks.py b/tests/app/celery/test_nightly_tasks.py index 9caef3532..83d74660c 100644 --- a/tests/app/celery/test_nightly_tasks.py +++ b/tests/app/celery/test_nightly_tasks.py @@ -98,7 +98,9 @@ def test_will_remove_csv_files_for_jobs_older_than_retention_period( service=service_1, notification_type=NotificationType.SMS, days_of_retention=3 ) create_service_data_retention( - service=service_2, notification_type=NotificationType.EMAIL, days_of_retention=30 + service=service_2, + notification_type=NotificationType.EMAIL, + days_of_retention=30, ) sms_template_service_1 = create_template(service=service_1) email_template_service_1 = create_template(service=service_1, template_type="email") diff --git a/tests/app/conftest.py b/tests/app/conftest.py index db1b3914c..5d7280c90 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -247,7 +247,8 @@ def _sample_service_full_permissions(notify_db_session): @pytest.fixture(scope="function") def sample_template(sample_user): service = create_service( - service_permissions=[ServicePermissionType.EMAIL, ServicePermissionType.SMS], check_if_service_exists=True + service_permissions=[ServicePermissionType.EMAIL, ServicePermissionType.SMS], + check_if_service_exists=True, ) data = { diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index f167ca65f..bf8005626 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -1762,8 +1762,18 @@ def test_get_service_ids_with_notifications_on_date_checks_ft_status( create_ft_notification_status(template=sample_template, local_date="2022-01-02") assert ( - len(get_service_ids_with_notifications_on_date(NotificationType.SMS, date(2022, 1, 1))) == 1 + len( + get_service_ids_with_notifications_on_date( + NotificationType.SMS, date(2022, 1, 1) + ) + ) + == 1 ) assert ( - len(get_service_ids_with_notifications_on_date(NotificationType.SMS, date(2022, 1, 2))) == 1 + len( + get_service_ids_with_notifications_on_date( + NotificationType.SMS, date(2022, 1, 2) + ) + ) + == 1 ) diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index 1f0e6ab84..bf9772fae 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -169,7 +169,9 @@ def test_fetch_notification_status_for_service_for_today_and_7_previous_days( service_1 = create_service(service_name="service_1") sms_template = create_template(service=service_1, template_type=TemplateType.SMS) sms_template_2 = create_template(service=service_1, template_type=TemplateType.SMS) - email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) + email_template = create_template( + service=service_1, template_type=TemplateType.EMAIL + ) create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) create_ft_notification_status(date(2018, 10, 25), "sms", service_1, count=8) @@ -222,12 +224,18 @@ def test_fetch_notification_status_by_template_for_service_for_today_and_7_previ ): service_1 = create_service(service_name="service_1") sms_template = create_template( - template_name="sms Template 1", service=service_1, template_type=TemplateType.SMS + template_name="sms Template 1", + service=service_1, + template_type=TemplateType.SMS, ) sms_template_2 = create_template( - template_name="sms Template 2", service=service_1, template_type=TemplateType.SMS + template_name="sms Template 2", + service=service_1, + template_type=TemplateType.SMS, + ) + email_template = create_template( + service=service_1, template_type=TemplateType.EMAIL ) - email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) # create unused email template create_template(service=service_1, template_type=TemplateType.EMAIL) @@ -324,7 +332,9 @@ def test_fetch_notification_status_totals_for_all_services_works_in_est( ): service_1 = create_service(service_name="service_1") sms_template = create_template(service=service_1, template_type=TemplateType.SMS) - email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) + email_template = create_template( + service=service_1, template_type=TemplateType.EMAIL + ) create_notification( sms_template, created_at=datetime(2018, 4, 20, 12, 0, 0), status="delivered" @@ -368,7 +378,9 @@ def set_up_data(): service_2 = create_service(service_name="service_2") service_1 = create_service(service_name="service_1") sms_template = create_template(service=service_1, template_type=TemplateType.SMS) - email_template = create_template(service=service_1, template_type=TemplateType.EMAIL) + email_template = create_template( + service=service_1, template_type=TemplateType.EMAIL + ) create_ft_notification_status(date(2018, 10, 24), "sms", service_1, count=8) create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) create_ft_notification_status( @@ -854,7 +866,9 @@ def test_update_fact_notification_status_respects_gmt_bst( expected_count, ): create_notification(template=sample_template, created_at=created_at_utc) - update_fact_notification_status(process_day, NotificationType.SMS, sample_service.id) + update_fact_notification_status( + process_day, NotificationType.SMS, sample_service.id + ) assert ( FactNotificationStatus.query.filter_by( diff --git a/tests/app/dao/test_jobs_dao.py b/tests/app/dao/test_jobs_dao.py index b7afa713c..0f1cc394a 100644 --- a/tests/app/dao/test_jobs_dao.py +++ b/tests/app/dao/test_jobs_dao.py @@ -352,7 +352,9 @@ def test_should_get_jobs_seven_days_old_by_scheduled_for_date(sample_service): sms_template, created_at=eight_days_ago, scheduled_for=six_days_ago ) - jobs = dao_get_jobs_older_than_data_retention(notification_types=[NotificationType.SMS]) + jobs = dao_get_jobs_older_than_data_retention( + notification_types=[NotificationType.SMS] + ) assert len(jobs) == 2 assert job_to_remain.id not in [job.id for job in jobs] diff --git a/tests/app/dao/test_service_permissions_dao.py b/tests/app/dao/test_service_permissions_dao.py index df87575ae..cf550b2b0 100644 --- a/tests/app/dao/test_service_permissions_dao.py +++ b/tests/app/dao/test_service_permissions_dao.py @@ -41,7 +41,8 @@ def test_fetch_service_permissions_gets_service_permissions( sp.service_id == service_without_permissions.id for sp in service_permissions ) assert all( - sp.permission in { + sp.permission + in { ServicePermissionType.INTERNATIONAL_SMS, ServicePermissionType.SMS, } diff --git a/tests/app/dao/test_uploads_dao.py b/tests/app/dao/test_uploads_dao.py index 016d7a92e..d444c38fe 100644 --- a/tests/app/dao/test_uploads_dao.py +++ b/tests/app/dao/test_uploads_dao.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from freezegun import freeze_time from app.dao.uploads_dao import dao_get_uploads_by_service_id -from app.models import JOB_STATUS_IN_PROGRESS +from app.models import JOB_STATUS_IN_PROGRESS, TemplateType from tests.app.db import ( create_job, create_notification, diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index d0ea87574..fde2291f8 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -11,7 +11,12 @@ from notifications_utils.recipients import ( ) from sqlalchemy.exc import SQLAlchemyError -from app.models import Notification, NotificationHistory, ServicePermissionType, TemplateType +from app.models import ( + Notification, + NotificationHistory, + ServicePermissionType, + TemplateType, +) from app.notifications.process_notifications import ( create_content_for_notification, persist_notification, diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index fdc1450f2..4e12e5536 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -268,7 +268,7 @@ def test_receive_notification_error_if_not_single_matching_service( service_permissions=[ ServicePermissionType.EMAIL, ServicePermissionType.SMS, - ServicePermissionType.INBOUND_SMS + ServicePermissionType.INBOUND_SMS, ], ) diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index a3a098e4d..8eeb858c0 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -5,7 +5,12 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT import app from app.dao import templates_dao -from app.models import KEY_TYPE_NORMAL, NotificationType, ServicePermissionType, TemplateType +from app.models import ( + KEY_TYPE_NORMAL, + NotificationType, + ServicePermissionType, + TemplateType, +) from app.notifications.process_notifications import create_content_for_notification from app.notifications.sns_cert_validator import ( VALID_SNS_TOPICS, @@ -93,7 +98,7 @@ def test_check_application_over_retention_limit_fails( [ (TemplateType.EMAIL, NotificationType.EMAIL), (TemplateType.SMS, NotificationType.SMS), - ] + ], ) def test_check_template_is_for_notification_type_pass(template_type, notification_type): assert ( @@ -109,7 +114,7 @@ def test_check_template_is_for_notification_type_pass(template_type, notificatio [ (TemplateType.SMS, NotificationType.EMAIL), (TemplateType.EMAIL, NotificationType.SMS), - ] + ], ) def test_check_template_is_for_notification_type_fails_when_template_type_does_not_match_notification_type( template_type, notification_type @@ -583,7 +588,9 @@ def test_validate_and_format_recipient_succeeds_with_international_numbers_if_se def test_validate_and_format_recipient_fails_when_no_recipient(): with pytest.raises(BadRequestError) as e: - validate_and_format_recipient(None, "key_type", "service", "NotificationType.SMS") + validate_and_format_recipient( + None, "key_type", "service", "NotificationType.SMS" + ) assert e.value.status_code == 400 assert e.value.message == "Recipient can't be empty" @@ -608,7 +615,9 @@ def test_check_service_email_reply_to_id_where_service_id_is_not_found( ): reply_to_address = create_reply_to_email(sample_service, "test@test.com") with pytest.raises(BadRequestError) as e: - check_service_email_reply_to_id(fake_uuid, reply_to_address.id, NotificationType.EMAIL) + check_service_email_reply_to_id( + fake_uuid, reply_to_address.id, NotificationType.EMAIL + ) assert e.value.status_code == 400 assert ( e.value.message @@ -622,7 +631,9 @@ def test_check_service_email_reply_to_id_where_reply_to_id_is_not_found( sample_service, fake_uuid ): with pytest.raises(BadRequestError) as e: - check_service_email_reply_to_id(sample_service.id, fake_uuid, NotificationType.EMAIL) + check_service_email_reply_to_id( + sample_service.id, fake_uuid, NotificationType.EMAIL + ) assert e.value.status_code == 400 assert ( e.value.message @@ -639,11 +650,14 @@ def test_check_service_sms_sender_id_where_sms_sender_id_is_none(notification_ty def test_check_service_sms_sender_id_where_sms_sender_id_is_found(sample_service): sms_sender = create_service_sms_sender(service=sample_service, sms_sender="123456") - assert check_service_sms_sender_id( - sample_service.id, - sms_sender.id, - NotificationType.SMS, - ) == "123456" + assert ( + check_service_sms_sender_id( + sample_service.id, + sms_sender.id, + NotificationType.SMS, + ) + == "123456" + ) def test_check_service_sms_sender_id_where_service_id_is_not_found( @@ -690,7 +704,10 @@ def test_check_reply_to_email_type(sample_service): def test_check_reply_to_sms_type(sample_service): sms_sender = create_service_sms_sender(service=sample_service, sms_sender="123456") - assert check_reply_to(sample_service.id, sms_sender.id, NotificationType.SMS) == "123456" + assert ( + check_reply_to(sample_service.id, sms_sender.id, NotificationType.SMS) + == "123456" + ) def test_check_if_service_can_send_files_by_email_raises_if_no_contact_link_set( diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index 254886bf4..c3468a2c5 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -100,7 +100,7 @@ def test_should_reject_bad_phone_numbers(notify_api, sample_template, mocker): [ (TemplateType.SMS, "+447700900855"), (TemplateType.EMAIL, "ok@ok.com"), - ] + ], ) def test_send_notification_invalid_template_id( notify_api, sample_template, mocker, fake_uuid, template_type, to @@ -339,7 +339,9 @@ def test_should_send_notification_if_restricted_and_a_service_user( ) template = ( - sample_template if template_type == TemplateType.SMS else sample_email_template + sample_template + if template_type == TemplateType.SMS + else sample_email_template ) to = ( template.service.created_by.mobile_number @@ -769,7 +771,9 @@ def test_should_persist_notification( "app.notifications.process_notifications.uuid.uuid4", return_value=fake_uuid ) - template = sample_template if template_type == TemplateType.SMS else sample_email_template + template = ( + sample_template if template_type == TemplateType.SMS else sample_email_template + ) to = ( sample_template.service.created_by.mobile_number if template_type == TemplateType.SMS @@ -826,7 +830,9 @@ def test_should_delete_notification_and_return_error_if_redis_fails( "app.notifications.process_notifications.uuid.uuid4", return_value=fake_uuid ) - template = sample_template if template_type == TemplateType.SMS else sample_email_template + template = ( + sample_template if template_type == TemplateType.SMS else sample_email_template + ) to = ( sample_template.service.created_by.mobile_number if template_type == TemplateType.SMS @@ -1086,7 +1092,7 @@ def test_create_template_doesnt_raise_with_too_much_personalisation( [ (TemplateType.SMS, True), (TemplateType.EMAIL, False), - ] + ], ) def test_create_template_raises_invalid_request_when_content_too_large( sample_service, template_type, should_error diff --git a/tests/app/service/send_notification/test_send_one_off_notification.py b/tests/app/service/send_notification/test_send_one_off_notification.py index c7b369444..9f4ccfbc1 100644 --- a/tests/app/service/send_notification/test_send_one_off_notification.py +++ b/tests/app/service/send_notification/test_send_one_off_notification.py @@ -203,7 +203,9 @@ def test_send_one_off_notification_raises_if_cant_send_to_recipient( template = create_template(service=service) dao_add_and_commit_guest_list_contacts( [ - ServiceGuestList.from_string(service.id, GuestListRecipientType.MOBILE, "2028765309"), + ServiceGuestList.from_string( + service.id, GuestListRecipientType.MOBILE, "2028765309" + ), ] ) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d9ab3fd07..9b3fd378a 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1723,7 +1723,9 @@ def test_get_all_notifications_for_service_filters_notifications_when_using_post service_2 = create_service(service_name="2") service_1_sms_template = create_template(service_1) - service_1_email_template = create_template(service_1, template_type=TemplateType.EMAIL) + service_1_email_template = create_template( + service_1, template_type=TemplateType.EMAIL + ) service_2_sms_template = create_template(service_2) returned_notification = create_notification( diff --git a/tests/app/service/test_sender.py b/tests/app/service/test_sender.py index 5f28fefed..dc17e1eb8 100644 --- a/tests/app/service/test_sender.py +++ b/tests/app/service/test_sender.py @@ -7,7 +7,9 @@ from app.service.sender import send_notification_to_service_users from tests.app.db import create_service, create_template, create_user -@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS]) +@pytest.mark.parametrize( + "notification_type", [NotificationType.EMAIL, NotificationType.SMS] +) def test_send_notification_to_service_users_persists_notifications_correctly( notify_service, notification_type, sample_service, mocker ): diff --git a/tests/app/service/test_service_guest_list.py b/tests/app/service/test_service_guest_list.py index 918a8e399..cd8ec7c1d 100644 --- a/tests/app/service/test_service_guest_list.py +++ b/tests/app/service/test_service_guest_list.py @@ -28,7 +28,9 @@ def test_get_guest_list_separates_emails_and_phones(client, sample_service): GuestListRecipientType.EMAIL, "service@example.com", ), - ServiceGuestList.from_string(sample_service.id, GuestListRecipientType.MOBILE, "2028675309"), + ServiceGuestList.from_string( + sample_service.id, GuestListRecipientType.MOBILE, "2028675309" + ), ServiceGuestList.from_string( sample_service.id, GuestListRecipientType.MOBILE, diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index cc145ca0b..bca08034a 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -223,7 +223,7 @@ def test_should_raise_error_on_create_if_no_permission( ), ( TemplateType.EMAIL, - [ServicePErmissionType.SMS], + [ServicePermissionType.SMS], {"template_type": ["Updating email templates is not allowed"]}, ), ], diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 7744a8316..4c063e7b5 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -10,13 +10,7 @@ import app.celery.tasks from app import db from app.dao.services_dao import dao_fetch_service_by_id from app.dao.users_dao import create_user_code -from app.models import ( - USER_AUTH_TYPES, - Notification, - User, - VerifyCode, - VerifyCodeType, -) +from app.models import USER_AUTH_TYPES, Notification, User, VerifyCode, VerifyCodeType from tests import create_admin_authorization_header diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index 36bfb5d67..d683895ee 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -761,7 +761,11 @@ def test_post_sms_notification_returns_400_if_number_not_in_guest_list( notify_db_session, client, restricted ): service = create_service( - restricted=restricted, service_permissions=[ServicePermissionType.SMS, ServicePermissionType.INTERNATIONAL_SMS] + restricted=restricted, + service_permissions=[ + ServicePermissionType.SMS, + ServicePermissionType.INTERNATIONAL_SMS, + ], ) template = create_template(service=service) create_api_key(service=service, key_type="team") From 908d695b54b7d900b72e2a75da93739429bb4732 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 10 Jan 2024 09:48:32 -0500 Subject: [PATCH 135/259] More cleanup. Signed-off-by: Cliff Hill --- app/models.py | 1 - app/service/statistics.py | 8 ++++---- app/template/template_schemas.py | 6 +++--- app/v2/notifications/notification_schemas.py | 7 +++++-- app/v2/template/template_schemas.py | 6 +++--- app/v2/templates/templates_schemas.py | 4 ++-- tests/app/celery/test_reporting_tasks.py | 3 +-- tests/app/conftest.py | 3 +-- tests/app/test_utils.py | 6 ++++-- 9 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/models.py b/app/models.py index a656a926c..29fde4ffd 100644 --- a/app/models.py +++ b/app/models.py @@ -33,7 +33,6 @@ from app.utils import ( class TemplateType(Enum): SMS = "sms" EMAIL = "email" - LETTER = "letter" class NotificationType(Enum): diff --git a/app/service/statistics.py b/app/service/statistics.py index c61a3c55f..37e66f362 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -2,7 +2,7 @@ from collections import defaultdict from datetime import datetime from app.dao.date_util import get_months_for_financial_year -from app.models import NOTIFICATION_STATUS_TYPES, NOTIFICATION_TYPES +from app.models import NOTIFICATION_STATUS_TYPES, TemplateType def format_statistics(statistics): @@ -40,7 +40,7 @@ def format_admin_stats(statistics): def create_stats_dict(): stats_dict = {} - for template in NOTIFICATION_TYPES: + for template in TemplateType: stats_dict[template] = {} for status in ("total", "test-key"): @@ -78,7 +78,7 @@ def format_monthly_template_notification_stats(year, rows): def create_zeroed_stats_dicts(): return { template_type: {status: 0 for status in ("requested", "delivered", "failed")} - for template_type in NOTIFICATION_TYPES + for template_type in TemplateType } @@ -103,7 +103,7 @@ def create_empty_monthly_notification_status_stats_dict(year): # nested dicts - data[month][template type][status] = count return { start.strftime("%Y-%m"): { - template_type: defaultdict(int) for template_type in NOTIFICATION_TYPES + template_type: defaultdict(int) for template_type in TemplateType } for start in utc_month_starts } diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py index c8843a343..c19c7fe75 100644 --- a/app/template/template_schemas.py +++ b/app/template/template_schemas.py @@ -1,4 +1,4 @@ -from app.models import TEMPLATE_PROCESS_TYPE, TEMPLATE_TYPES +from app.models import TEMPLATE_PROCESS_TYPE, TemplateType from app.schema_validation.definitions import nullable_uuid, uuid post_create_template_schema = { @@ -8,7 +8,7 @@ post_create_template_schema = { "title": "payload for POST /service//template", "properties": { "name": {"type": "string"}, - "template_type": {"enum": TEMPLATE_TYPES}, + "template_type": {"enum": [e.value for e in TemplateType]}, "service": uuid, "process_type": {"enum": TEMPLATE_PROCESS_TYPE}, "content": {"type": "string"}, @@ -29,7 +29,7 @@ post_update_template_schema = { "properties": { "id": uuid, "name": {"type": "string"}, - "template_type": {"enum": TEMPLATE_TYPES}, + "template_type": {"enum": [e.value for e in TemplateType]}, "service": uuid, "process_type": {"enum": TEMPLATE_PROCESS_TYPE}, "content": {"type": "string"}, diff --git a/app/v2/notifications/notification_schemas.py b/app/v2/notifications/notification_schemas.py index 91671bf23..d64c38d86 100644 --- a/app/v2/notifications/notification_schemas.py +++ b/app/v2/notifications/notification_schemas.py @@ -1,4 +1,4 @@ -from app.models import NOTIFICATION_STATUS_TYPES, NOTIFICATION_TYPES +from app.models import NOTIFICATION_STATUS_TYPES, TemplateType from app.schema_validation.definitions import personalisation, uuid template = { @@ -81,7 +81,10 @@ get_notifications_request = { "properties": { "reference": {"type": "string"}, "status": {"type": "array", "items": {"enum": NOTIFICATION_STATUS_TYPES}}, - "template_type": {"type": "array", "items": {"enum": NOTIFICATION_TYPES}}, + "template_type": { + "type": "array", + "items": {"enum": [e.value for e in TemplateType]}, + }, "include_jobs": {"enum": ["true", "True"]}, "older_than": uuid, }, diff --git a/app/v2/template/template_schemas.py b/app/v2/template/template_schemas.py index 1865a561e..d6799e458 100644 --- a/app/v2/template/template_schemas.py +++ b/app/v2/template/template_schemas.py @@ -1,4 +1,4 @@ -from app.models import TEMPLATE_TYPES +from app.models import TemplateType from app.schema_validation.definitions import personalisation, uuid get_template_by_id_request = { @@ -17,7 +17,7 @@ get_template_by_id_response = { "title": "reponse v2/template", "properties": { "id": uuid, - "type": {"enum": TEMPLATE_TYPES}, + "type": {"enum": [e.value for e in TemplateType]}, "created_at": { "format": "date-time", "type": "string", @@ -62,7 +62,7 @@ post_template_preview_response = { "title": "reponse v2/template/{id}/preview", "properties": { "id": uuid, - "type": {"enum": TEMPLATE_TYPES}, + "type": {"enum": [e.value for e in TemplateType]}, "version": {"type": "integer"}, "body": {"type": "string"}, "subject": {"type": ["string", "null"]}, diff --git a/app/v2/templates/templates_schemas.py b/app/v2/templates/templates_schemas.py index e5496a90d..57dd8a08f 100644 --- a/app/v2/templates/templates_schemas.py +++ b/app/v2/templates/templates_schemas.py @@ -1,11 +1,11 @@ -from app.models import TEMPLATE_TYPES +from app.models import TemplateType from app.v2.template.template_schemas import get_template_by_id_response as template get_all_template_request = { "$schema": "http://json-schema.org/draft-07/schema#", "description": "request schema for parameters allowed when getting all templates", "type": "object", - "properties": {"type": {"enum": TEMPLATE_TYPES}}, + "properties": {"type": {"enum": [e.value for e in TemplateType]}}, "additionalProperties": False, } diff --git a/tests/app/celery/test_reporting_tasks.py b/tests/app/celery/test_reporting_tasks.py index 70eb44b8b..d4d57da4f 100644 --- a/tests/app/celery/test_reporting_tasks.py +++ b/tests/app/celery/test_reporting_tasks.py @@ -17,7 +17,6 @@ from app.models import ( KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - NOTIFICATION_TYPES, FactBilling, FactNotificationStatus, Notification, @@ -109,7 +108,7 @@ def test_create_nightly_notification_status_triggers_relevant_tasks( "app.celery.reporting_tasks.create_nightly_notification_status_for_service_and_day" ).apply_async - for notification_type in NOTIFICATION_TYPES: + for notification_type in NotificationType: template = create_template(sample_service, template_type=notification_type) create_notification(template=template, created_at=notification_date) diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 5d7280c90..e0bc81bdd 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -23,7 +23,6 @@ from app.models import ( KEY_TYPE_TEAM, KEY_TYPE_TEST, NOTIFICATION_STATUS_TYPES_COMPLETED, - SERVICE_PERMISSION_TYPES, ApiKey, GuestListRecipientType, InvitedUser, @@ -237,7 +236,7 @@ def sample_service(sample_user): def _sample_service_full_permissions(notify_db_session): service = create_service( service_name="sample service full permissions", - service_permissions=set(SERVICE_PERMISSION_TYPES), + service_permissions=set(ServicePermissionType), check_if_service_exists=True, ) create_inbound_number("12345", service_id=service.id) diff --git a/tests/app/test_utils.py b/tests/app/test_utils.py index dff502c4a..c78620e7f 100644 --- a/tests/app/test_utils.py +++ b/tests/app/test_utils.py @@ -4,7 +4,7 @@ from datetime import date, datetime import pytest from freezegun import freeze_time -from app.models import UPLOAD_DOCUMENT +from app.models import ServicePermissionType from app.utils import ( format_sequential_number, get_midnight_for_day_before, @@ -89,7 +89,9 @@ def test_get_uuid_string_or_none(): def test_get_public_notify_type_text(): - assert get_public_notify_type_text(UPLOAD_DOCUMENT) == "document" + assert ( + get_public_notify_type_text(ServicePermissionType.UPLOAD_DOCUMENT) == "document" + ) # This method is used for simulating bulk sends. We use localstack and run on a developer's machine to do the From ac9591ec7c3b6a573f9f1dc032586869e6df3317 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 10 Jan 2024 11:18:33 -0500 Subject: [PATCH 136/259] More tweaks, trying to get tests to be clean. Signed-off-by: Cliff Hill --- app/billing/billing_schemas.py | 2 +- app/celery/nightly_tasks.py | 4 ++-- app/clients/__init__.py | 7 ++++--- app/dao/fact_notification_status_dao.py | 5 +++-- app/dao/provider_details_dao.py | 2 +- app/dao/services_dao.py | 4 ++-- app/inbound_sms/rest.py | 5 +++-- app/models.py | 1 + app/performance_platform/total_sent_notifications.py | 5 +++-- app/service/service_data_retention_schema.py | 4 +++- app/template/template_schemas.py | 2 +- app/user/rest.py | 3 ++- app/v2/notifications/notification_schemas.py | 2 +- tests/app/v2/template/test_get_template.py | 2 +- tests/app/v2/template/test_post_template.py | 4 ++-- tests/app/v2/template/test_template_schemas.py | 4 ++-- tests/app/v2/templates/test_get_templates.py | 4 ++-- tests/app/v2/templates/test_templates_schemas.py | 6 +++--- 18 files changed, 37 insertions(+), 29 deletions(-) diff --git a/app/billing/billing_schemas.py b/app/billing/billing_schemas.py index 9b6b6f821..9884be11f 100644 --- a/app/billing/billing_schemas.py +++ b/app/billing/billing_schemas.py @@ -25,7 +25,7 @@ def serialize_ft_billing_remove_emails(rows): "charged_units": row.charged_units, } for row in rows - if row.notification_type != "email" + if row.notification_type != NotificationType.EMAIL ] diff --git a/app/celery/nightly_tasks.py b/app/celery/nightly_tasks.py index f3ee22bf6..3a4e8fa59 100644 --- a/app/celery/nightly_tasks.py +++ b/app/celery/nightly_tasks.py @@ -69,13 +69,13 @@ def delete_notifications_older_than_retention(): @notify_celery.task(name="delete-sms-notifications") @cronitor("delete-sms-notifications") def delete_sms_notifications_older_than_retention(): - _delete_notifications_older_than_retention_by_type("sms") + _delete_notifications_older_than_retention_by_type(NotificationType.SMS) @notify_celery.task(name="delete-email-notifications") @cronitor("delete-email-notifications") def delete_email_notifications_older_than_retention(): - _delete_notifications_older_than_retention_by_type("email") + _delete_notifications_older_than_retention_by_type(NotificationType.EMAIL) def _delete_notifications_older_than_retention_by_type(notification_type): diff --git a/app/clients/__init__.py b/app/clients/__init__.py index 1946f70e6..70449b45d 100644 --- a/app/clients/__init__.py +++ b/app/clients/__init__.py @@ -1,6 +1,7 @@ from abc import abstractmethod from typing import Protocol +from app.models import NotificationType from botocore.config import Config AWS_CLIENT_CONFIG = Config( @@ -53,10 +54,10 @@ class NotificationProviderClients(object): return self.email_clients.get(name) def get_client_by_name_and_type(self, name, notification_type): - assert notification_type in ["email", "sms"] # nosec B101 + assert notification_type in {NotificationType.EMAIL, NotificationType.SMS} # nosec B101 - if notification_type == "email": + if notification_type == NotificationType.EMAIL: return self.get_email_client(name) - if notification_type == "sms": + if notification_type == NotificationType.SMS: return self.get_sms_client(name) diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index 2b0ca08d9..68284436b 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -23,6 +23,7 @@ from app.models import ( NOTIFICATION_TEMPORARY_FAILURE, FactNotificationStatus, Notification, + NotificationType, NotificationAllTimeView, Service, Template, @@ -467,7 +468,7 @@ def get_total_notifications_for_date_range(start_date, end_date): case( [ ( - FactNotificationStatus.notification_type == "email", + FactNotificationStatus.notification_type == NotificationType.EMAIL, FactNotificationStatus.notification_count, ) ], @@ -478,7 +479,7 @@ def get_total_notifications_for_date_range(start_date, end_date): case( [ ( - FactNotificationStatus.notification_type == "sms", + FactNotificationStatus.notification_type == NotificationType.SMS, FactNotificationStatus.notification_count, ) ], diff --git a/app/dao/provider_details_dao.py b/app/dao/provider_details_dao.py index 270f8731e..d98aadcd2 100644 --- a/app/dao/provider_details_dao.py +++ b/app/dao/provider_details_dao.py @@ -62,7 +62,7 @@ def _get_sms_providers_for_update(time_threshold): # get current priority of both providers q = ( ProviderDetails.query.filter( - ProviderDetails.notification_type == "sms", ProviderDetails.active + ProviderDetails.notification_type == NotificationType.SMS, ProviderDetails.active ) .with_for_update() .all() diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 00b90e46c..07ba4b1e0 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -106,7 +106,7 @@ def dao_fetch_live_services_data(): case( [ ( - this_year_ft_billing.c.notification_type == "email", + this_year_ft_billing.c.notification_type == NotificationType.EMAIL, func.sum(this_year_ft_billing.c.notifications_sent), ) ], @@ -115,7 +115,7 @@ def dao_fetch_live_services_data(): case( [ ( - this_year_ft_billing.c.notification_type == "sms", + this_year_ft_billing.c.notification_type == NotificationType.SMS, func.sum(this_year_ft_billing.c.notifications_sent), ) ], diff --git a/app/inbound_sms/rest.py b/app/inbound_sms/rest.py index 3dcb077c3..2a6f16277 100644 --- a/app/inbound_sms/rest.py +++ b/app/inbound_sms/rest.py @@ -11,6 +11,7 @@ from app.dao.service_data_retention_dao import ( ) from app.errors import register_errors from app.inbound_sms.inbound_sms_schemas import get_inbound_sms_for_service_schema +from app.models import NotificationType from app.schema_validation import validate inbound_sms = Blueprint( @@ -31,7 +32,7 @@ def post_inbound_sms_for_service(service_id): # user_number = try_validate_and_format_phone_number(user_number, international=True) inbound_data_retention = fetch_service_data_retention_by_notification_type( - service_id, "sms" + service_id, NotificationType.SMS ) limit_days = ( inbound_data_retention.days_of_retention if inbound_data_retention else 7 @@ -49,7 +50,7 @@ def get_most_recent_inbound_sms_for_service(service_id): page = request.args.get("page", 1) inbound_data_retention = fetch_service_data_retention_by_notification_type( - service_id, "sms" + service_id, NotificationType.SMS ) limit_days = ( inbound_data_retention.days_of_retention if inbound_data_retention else 7 diff --git a/app/models.py b/app/models.py index 29fde4ffd..a656a926c 100644 --- a/app/models.py +++ b/app/models.py @@ -33,6 +33,7 @@ from app.utils import ( class TemplateType(Enum): SMS = "sms" EMAIL = "email" + LETTER = "letter" class NotificationType(Enum): diff --git a/app/performance_platform/total_sent_notifications.py b/app/performance_platform/total_sent_notifications.py index 7221d4c24..514d32096 100644 --- a/app/performance_platform/total_sent_notifications.py +++ b/app/performance_platform/total_sent_notifications.py @@ -2,6 +2,7 @@ from app import performance_platform_client from app.dao.fact_notification_status_dao import ( get_total_sent_notifications_for_day_and_type, ) +from app.models import NotificationType # TODO: is this obsolete? it doesn't seem to be used anywhere @@ -19,8 +20,8 @@ def send_total_notifications_sent_for_day_stats(start_time, notification_type, c # TODO: is this obsolete? it doesn't seem to be used anywhere def get_total_sent_notifications_for_day(day): - email_count = get_total_sent_notifications_for_day_and_type(day, "email") - sms_count = get_total_sent_notifications_for_day_and_type(day, "sms") + email_count = get_total_sent_notifications_for_day_and_type(day, NotificationType.EMAIL) + sms_count = get_total_sent_notifications_for_day_and_type(day, NotificationType.SMS) return { "email": email_count, diff --git a/app/service/service_data_retention_schema.py b/app/service/service_data_retention_schema.py index 53a4cd6d9..7490519f5 100644 --- a/app/service/service_data_retention_schema.py +++ b/app/service/service_data_retention_schema.py @@ -1,3 +1,5 @@ +from app.models import NotificationType + add_service_data_retention_request = { "$schema": "http://json-schema.org/draft-07/schema#", "description": "POST service data retention schema", @@ -5,7 +7,7 @@ add_service_data_retention_request = { "type": "object", "properties": { "days_of_retention": {"type": "integer"}, - "notification_type": {"enum": ["sms", "email"]}, + "notification_type": {"enum": [NotificationType.SMS.value, NotificationType.EMAIL.value]}, }, "required": ["days_of_retention", "notification_type"], } diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py index c19c7fe75..e94863b4d 100644 --- a/app/template/template_schemas.py +++ b/app/template/template_schemas.py @@ -16,7 +16,7 @@ post_create_template_schema = { "created_by": uuid, "parent_folder_id": uuid, }, - "if": {"properties": {"template_type": {"enum": ["email"]}}}, + "if": {"properties": {"template_type": {"enum": [TemplateType.EMAIL.value]}}}, "then": {"required": ["subject"]}, "required": ["name", "template_type", "content", "service", "created_by"], } diff --git a/app/user/rest.py b/app/user/rest.py index 278eaf396..d8b455d4c 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -39,6 +39,7 @@ from app.models import ( Permission, Service, TemplateType, + VerifyCodeType, ) from app.notifications.process_notifications import ( persist_notification, @@ -226,7 +227,7 @@ def verify_user_code(user_id): user_to_verify.current_session_id = str(uuid.uuid4()) user_to_verify.logged_in_at = datetime.utcnow() - if data["code_type"] == "email": + if data["code_type"] == VerifyCodeType.EMAIL: user_to_verify.email_access_validated_at = datetime.utcnow() user_to_verify.failed_login_count = 0 save_model_user(user_to_verify) diff --git a/app/v2/notifications/notification_schemas.py b/app/v2/notifications/notification_schemas.py index d64c38d86..9bfa2b9cb 100644 --- a/app/v2/notifications/notification_schemas.py +++ b/app/v2/notifications/notification_schemas.py @@ -41,7 +41,7 @@ get_notification_response = { "line_5": {"type": ["string", "null"]}, "line_6": {"type": ["string", "null"]}, "postcode": {"type": ["string", "null"]}, - "type": {"enum": ["sms", "email"]}, + "type": {"enum": [e.value for e in TemplateType]}, "status": {"type": "string"}, "template": template, "body": {"type": "string"}, diff --git a/tests/app/v2/template/test_get_template.py b/tests/app/v2/template/test_get_template.py index 9fe698649..c22756f6e 100644 --- a/tests/app/v2/template/test_get_template.py +++ b/tests/app/v2/template/test_get_template.py @@ -120,7 +120,7 @@ def test_get_template_with_non_existent_template_id_returns_404( } -@pytest.mark.parametrize("tmp_type", list(TemplateType)) +@pytest.mark.parametrize("tmp_type", TemplateType) def test_get_template_with_non_existent_version_returns_404( client, sample_service, tmp_type ): diff --git a/tests/app/v2/template/test_post_template.py b/tests/app/v2/template/test_post_template.py index f9c5b7ff1..57c9bb868 100644 --- a/tests/app/v2/template/test_post_template.py +++ b/tests/app/v2/template/test_post_template.py @@ -59,7 +59,7 @@ valid_post = [ ] -@pytest.mark.parametrize("tmp_type", list(TemplateType)) +@pytest.mark.parametrize("tmp_type", TemplateType) @pytest.mark.parametrize( "subject,content,post_data,expected_subject,expected_content,expected_html", valid_post, @@ -125,7 +125,7 @@ def test_email_templates_not_rendered_into_content(client, sample_service): assert resp_json["body"] == template.content -@pytest.mark.parametrize("tmp_type", list(TemplateType)) +@pytest.mark.parametrize("tmp_type", TemplateType) def test_invalid_post_template_returns_400(client, sample_service, tmp_type): template = create_template( sample_service, diff --git a/tests/app/v2/template/test_template_schemas.py b/tests/app/v2/template/test_template_schemas.py index 3c00e62b8..43bfbc27d 100644 --- a/tests/app/v2/template/test_template_schemas.py +++ b/tests/app/v2/template/test_template_schemas.py @@ -114,7 +114,7 @@ def test_get_template_request_schema_against_invalid_args_is_invalid( assert error["message"] in error_message -@pytest.mark.parametrize("template_type", list(TemplateType)) +@pytest.mark.parametrize("template_type", TemplateType) @pytest.mark.parametrize( "response", [valid_json_get_response, valid_json_get_response_with_optionals] ) @@ -149,7 +149,7 @@ def test_post_template_preview_against_invalid_args_is_invalid(args, error_messa assert error["message"] in error_messages -@pytest.mark.parametrize("template_type", list(TemplateType)) +@pytest.mark.parametrize("template_type", TemplateType) @pytest.mark.parametrize( "response", [valid_json_post_response, valid_json_post_response_with_optionals] ) diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index c718a76d4..016dde637 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -41,7 +41,7 @@ def test_get_all_templates_returns_200(client, sample_service): assert template["subject"] == templates[index].subject -@pytest.mark.parametrize("tmp_type", list(TemplateType)) +@pytest.mark.parametrize("tmp_type", TemplateType) def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tmp_type): templates = [ create_template( @@ -75,7 +75,7 @@ def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tm assert template["subject"] == templates[index].subject -@pytest.mark.parametrize("tmp_type", list(TemplateType)) +@pytest.mark.parametrize("tmp_type", TemplateType) def test_get_correct_num_templates_for_valid_type_returns_200( client, sample_service, tmp_type ): diff --git a/tests/app/v2/templates/test_templates_schemas.py b/tests/app/v2/templates/test_templates_schemas.py index 71ac84df2..50d22e463 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -256,19 +256,19 @@ invalid_json_get_all_response = [ ] -@pytest.mark.parametrize("template_type", list(TemplateType)) +@pytest.mark.parametrize("template_type", TemplateType) def test_get_all_template_request_schema_against_no_args_is_valid(template_type): data = {} assert validate(data, get_all_template_request) == data -@pytest.mark.parametrize("template_type", list(TemplateType)) +@pytest.mark.parametrize("template_type", TemplateType) def test_get_all_template_request_schema_against_valid_args_is_valid(template_type): data = {"type": template_type} assert validate(data, get_all_template_request) == data -@pytest.mark.parametrize("template_type", list(TemplateType)) +@pytest.mark.parametrize("template_type", TemplateType) def test_get_all_template_request_schema_against_invalid_args_is_invalid(template_type): data = {"type": "unknown"} From 3982f061b6442d1e5395460c182b5a8c0e1ccc1e Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 10 Jan 2024 12:32:25 -0500 Subject: [PATCH 137/259] Made enums.py for all the enums to avoid cyclic imports. Signed-off-by: Cliff Hill --- app/billing/billing_schemas.py | 2 + app/celery/nightly_tasks.py | 3 +- app/celery/reporting_tasks.py | 2 +- app/celery/research_mode_tasks.py | 2 +- app/celery/scheduled_tasks.py | 9 +- app/celery/tasks.py | 2 +- app/celery/test_key_tasks.py | 2 +- app/clients/__init__.py | 8 +- app/commands.py | 2 +- app/dao/fact_billing_dao.py | 2 +- app/dao/fact_notification_status_dao.py | 8 +- app/dao/inbound_sms_dao.py | 9 +- app/dao/notifications_dao.py | 2 +- app/dao/provider_details_dao.py | 12 +-- app/dao/services_dao.py | 8 +- app/dao/uploads_dao.py | 2 +- app/delivery/send_to_providers.py | 2 +- app/enums.py | 69 ++++++++++++++ app/inbound_sms/rest.py | 2 +- app/models.py | 92 ++++--------------- app/notifications/process_notifications.py | 8 +- app/notifications/rest.py | 3 +- app/notifications/validators.py | 10 +- app/organization/invite_rest.py | 3 +- .../total_sent_notifications.py | 6 +- app/service/send_notification.py | 3 +- app/service/service_data_retention_schema.py | 6 +- app/service_invite/rest.py | 3 +- app/user/rest.py | 10 +- app/utils.py | 3 +- app/v2/notifications/post_notifications.py | 9 +- 31 files changed, 149 insertions(+), 155 deletions(-) create mode 100644 app/enums.py diff --git a/app/billing/billing_schemas.py b/app/billing/billing_schemas.py index 9884be11f..966c274a0 100644 --- a/app/billing/billing_schemas.py +++ b/app/billing/billing_schemas.py @@ -1,5 +1,7 @@ from datetime import datetime +from app.enums import NotificationType + create_or_update_free_sms_fragment_limit_schema = { "$schema": "http://json-schema.org/draft-07/schema#", "description": "POST annual billing schema", diff --git a/app/celery/nightly_tasks.py b/app/celery/nightly_tasks.py index 3a4e8fa59..1c208104b 100644 --- a/app/celery/nightly_tasks.py +++ b/app/celery/nightly_tasks.py @@ -25,7 +25,8 @@ from app.dao.notifications_dao import ( from app.dao.service_data_retention_dao import ( fetch_service_data_retention_for_all_services_by_notification_type, ) -from app.models import FactProcessingTime, NotificationType +from app.enums import NotificationType +from app.models import FactProcessingTime from app.utils import get_midnight_in_utc diff --git a/app/celery/reporting_tasks.py b/app/celery/reporting_tasks.py index c7c60f1cf..aa8f6fece 100644 --- a/app/celery/reporting_tasks.py +++ b/app/celery/reporting_tasks.py @@ -8,7 +8,7 @@ from app.cronitor import cronitor from app.dao.fact_billing_dao import fetch_billing_data_for_day, update_fact_billing from app.dao.fact_notification_status_dao import update_fact_notification_status from app.dao.notifications_dao import get_service_ids_with_notifications_on_date -from app.models import NotificationType +from app.enums import NotificationType @notify_celery.task(name="create-nightly-billing") diff --git a/app/celery/research_mode_tasks.py b/app/celery/research_mode_tasks.py index c662da851..58f34c6a6 100644 --- a/app/celery/research_mode_tasks.py +++ b/app/celery/research_mode_tasks.py @@ -6,7 +6,7 @@ from requests import HTTPError, request from app.celery.process_ses_receipts_tasks import process_ses_results from app.config import QueueNames from app.dao.notifications_dao import get_notification_by_id -from app.models import NotificationType +from app.enums import NotificationType temp_fail = "2028675303" perm_fail = "2028675302" diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index 314e1f310..fb0f39caa 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -34,13 +34,8 @@ from app.dao.services_dao import ( ) from app.dao.users_dao import delete_codes_older_created_more_than_a_day_ago from app.delivery.send_to_providers import provider_to_use -from app.models import ( - JOB_STATUS_ERROR, - JOB_STATUS_IN_PROGRESS, - JOB_STATUS_PENDING, - Job, - NotificationType, -) +from app.enums import NotificationType +from app.models import JOB_STATUS_ERROR, JOB_STATUS_IN_PROGRESS, JOB_STATUS_PENDING, Job from app.notifications.process_notifications import send_notification_to_queue MAX_NOTIFICATION_FAILS = 10000 diff --git a/app/celery/tasks.py b/app/celery/tasks.py index 4b7d5ccac..34b1f13e0 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -20,13 +20,13 @@ from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_inbound_api_dao import get_service_inbound_api_for_service from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.templates_dao import dao_get_template_by_id +from app.enums import NotificationType from app.models import ( JOB_STATUS_CANCELLED, JOB_STATUS_FINISHED, JOB_STATUS_IN_PROGRESS, JOB_STATUS_PENDING, KEY_TYPE_NORMAL, - NotificationType, ) from app.notifications.process_notifications import persist_notification from app.notifications.validators import check_service_over_total_message_limit diff --git a/app/celery/test_key_tasks.py b/app/celery/test_key_tasks.py index a7fdda110..f18976a5f 100644 --- a/app/celery/test_key_tasks.py +++ b/app/celery/test_key_tasks.py @@ -6,7 +6,7 @@ from requests import HTTPError, request from app.celery.process_ses_receipts_tasks import process_ses_results from app.config import QueueNames from app.dao.notifications_dao import get_notification_by_id -from app.models import NotificationType +from app.enums import NotificationType temp_fail = "2028675303" perm_fail = "2028675302" diff --git a/app/clients/__init__.py b/app/clients/__init__.py index 70449b45d..66e014354 100644 --- a/app/clients/__init__.py +++ b/app/clients/__init__.py @@ -1,9 +1,10 @@ from abc import abstractmethod from typing import Protocol -from app.models import NotificationType from botocore.config import Config +from app.enums import NotificationType + AWS_CLIENT_CONFIG = Config( # This config is required to enable S3 to connect to FIPS-enabled # endpoints. See https://aws.amazon.com/compliance/fips/ for more @@ -54,7 +55,10 @@ class NotificationProviderClients(object): return self.email_clients.get(name) def get_client_by_name_and_type(self, name, notification_type): - assert notification_type in {NotificationType.EMAIL, NotificationType.SMS} # nosec B101 + assert notification_type in { + NotificationType.EMAIL, + NotificationType.SMS, + } # nosec B101 if notification_type == NotificationType.EMAIL: return self.get_email_client(name) diff --git a/app/commands.py b/app/commands.py index 4d084e845..7541766af 100644 --- a/app/commands.py +++ b/app/commands.py @@ -49,6 +49,7 @@ from app.dao.users_dao import ( delete_user_verify_codes, get_user_by_email, ) +from app.enums import NotificationType from app.models import ( KEY_TYPE_TEST, NOTIFICATION_CREATED, @@ -56,7 +57,6 @@ from app.models import ( Domain, EmailBranding, Notification, - NotificationType, Organization, Service, Template, diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index 06b537643..696022619 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -8,6 +8,7 @@ from sqlalchemy.sql.expression import case, literal from app import db from app.dao.date_util import get_calendar_year_dates, get_calendar_year_for_datetime from app.dao.organization_dao import dao_get_organization_live_services +from app.enums import NotificationType from app.models import ( KEY_TYPE_NORMAL, KEY_TYPE_TEAM, @@ -17,7 +18,6 @@ from app.models import ( FactBilling, NotificationAllTimeView, NotificationHistory, - NotificationType, Organization, Rate, Service, diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index 68284436b..c6c6b06dd 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -7,6 +7,7 @@ from sqlalchemy.types import DateTime, Integer from app import db from app.dao.dao_utils import autocommit +from app.enums import NotificationType from app.models import ( KEY_TYPE_NORMAL, KEY_TYPE_TEAM, @@ -23,7 +24,6 @@ from app.models import ( NOTIFICATION_TEMPORARY_FAILURE, FactNotificationStatus, Notification, - NotificationType, NotificationAllTimeView, Service, Template, @@ -468,7 +468,8 @@ def get_total_notifications_for_date_range(start_date, end_date): case( [ ( - FactNotificationStatus.notification_type == NotificationType.EMAIL, + FactNotificationStatus.notification_type + == NotificationType.EMAIL, FactNotificationStatus.notification_count, ) ], @@ -479,7 +480,8 @@ def get_total_notifications_for_date_range(start_date, end_date): case( [ ( - FactNotificationStatus.notification_type == NotificationType.SMS, + FactNotificationStatus.notification_type + == NotificationType.SMS, FactNotificationStatus.notification_count, ) ], diff --git a/app/dao/inbound_sms_dao.py b/app/dao/inbound_sms_dao.py index 5143746d9..9b33ff0c8 100644 --- a/app/dao/inbound_sms_dao.py +++ b/app/dao/inbound_sms_dao.py @@ -5,13 +5,8 @@ from sqlalchemy.orm import aliased from app import db from app.dao.dao_utils import autocommit -from app.models import ( - InboundSms, - InboundSmsHistory, - NotificationType, - Service, - ServiceDataRetention, -) +from app.enums import NotificationType +from app.models import InboundSms, InboundSmsHistory, Service, ServiceDataRetention from app.utils import midnight_n_days_ago diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index 42ac8671c..a144ee7cc 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -16,6 +16,7 @@ from werkzeug.datastructures import MultiDict from app import create_uuid, db from app.dao.dao_utils import autocommit +from app.enums import NotificationType from app.models import ( KEY_TYPE_TEST, NOTIFICATION_CREATED, @@ -29,7 +30,6 @@ from app.models import ( FactNotificationStatus, Notification, NotificationHistory, - NotificationType, ) from app.utils import ( escape_special_characters, diff --git a/app/dao/provider_details_dao.py b/app/dao/provider_details_dao.py index d98aadcd2..0cb22adcd 100644 --- a/app/dao/provider_details_dao.py +++ b/app/dao/provider_details_dao.py @@ -5,13 +5,8 @@ from sqlalchemy import asc, desc, func from app import db from app.dao.dao_utils import autocommit -from app.models import ( - FactBilling, - NotificationType, - ProviderDetails, - ProviderDetailsHistory, - User, -) +from app.enums import NotificationType +from app.models import FactBilling, ProviderDetails, ProviderDetailsHistory, User def get_provider_details_by_id(provider_details_id): @@ -62,7 +57,8 @@ def _get_sms_providers_for_update(time_threshold): # get current priority of both providers q = ( ProviderDetails.query.filter( - ProviderDetails.notification_type == NotificationType.SMS, ProviderDetails.active + ProviderDetails.notification_type == NotificationType.SMS, + ProviderDetails.active, ) .with_for_update() .all() diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 07ba4b1e0..e9f346735 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -13,6 +13,7 @@ from app.dao.organization_dao import dao_get_organization_by_email_address from app.dao.service_sms_sender_dao import insert_service_sms_sender from app.dao.service_user_dao import dao_get_service_user from app.dao.template_folder_dao import dao_get_valid_template_folders_by_id +from app.enums import NotificationType from app.models import ( KEY_TYPE_TEST, NOTIFICATION_PERMANENT_FAILURE, @@ -24,7 +25,6 @@ from app.models import ( Job, Notification, NotificationHistory, - NotificationType, Organization, Permission, Service, @@ -106,7 +106,8 @@ def dao_fetch_live_services_data(): case( [ ( - this_year_ft_billing.c.notification_type == NotificationType.EMAIL, + this_year_ft_billing.c.notification_type + == NotificationType.EMAIL, func.sum(this_year_ft_billing.c.notifications_sent), ) ], @@ -115,7 +116,8 @@ def dao_fetch_live_services_data(): case( [ ( - this_year_ft_billing.c.notification_type == NotificationType.SMS, + this_year_ft_billing.c.notification_type + == NotificationType.SMS, func.sum(this_year_ft_billing.c.notifications_sent), ) ], diff --git a/app/dao/uploads_dao.py b/app/dao/uploads_dao.py index 200d41805..1f1c5138b 100644 --- a/app/dao/uploads_dao.py +++ b/app/dao/uploads_dao.py @@ -5,13 +5,13 @@ from flask import current_app from sqlalchemy import String, and_, desc, func, literal, text from app import db +from app.enums import NotificationType from app.models import ( JOB_STATUS_CANCELLED, JOB_STATUS_SCHEDULED, NOTIFICATION_CANCELLED, Job, Notification, - NotificationType, ServiceDataRetention, Template, ) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 49c98b50a..be9d295da 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -15,6 +15,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.enums import NotificationType from app.exceptions import NotificationTechnicalFailureException from app.models import ( BRANDING_BOTH, @@ -23,7 +24,6 @@ from app.models import ( NOTIFICATION_SENDING, NOTIFICATION_STATUS_TYPES_COMPLETED, NOTIFICATION_TECHNICAL_FAILURE, - NotificationType, ) from app.serialised_models import SerialisedService, SerialisedTemplate diff --git a/app/enums.py b/app/enums.py new file mode 100644 index 000000000..3a6ce9355 --- /dev/null +++ b/app/enums.py @@ -0,0 +1,69 @@ +from enum import Enum + + +class TemplateType(Enum): + SMS = "sms" + EMAIL = "email" + LETTER = "letter" + + +class NotificationType(Enum): + SMS = "sms" + EMAIL = "email" + LETTER = "letter" + + +class UserAuthType(Enum): + SMS = "sms_auth" + EMAIL = "email_auth" + WEBAUTHN = "webauthn_auth" + + +class ServiceCallbackType(Enum): + # TODO: Should ServiceCallbackApi.callback_type be changed to use this? + DELIVERY_STATUS = "delivery_status" + COMPLAINT = "complaint" + + +class ServicePermissionType(Enum): + EMAIL = "email" + SMS = "sms" + INTERNATIONAL_SMS = "international_sms" + INBOUND_SMS = "inbound_sms" + SCHEDULE_NOTIFICATIONS = "schedule_notifications" + EMAIL_AUTH = "email_auth" + UPLOAD_DOCUMENT = "upload_document" + EDIT_FOLDER_PERMISSIONS = "edit_folder_permissions" + + +class GuestListRecipientType(Enum): + MOBILE = "mobile" + EMAIL = "email" + + +class KeyType(Enum): + NORMAL = "normal" + TEAM = "team" + TEST = "test" + + +class JobStatusType(Enum): + PENDING = "pending" + IN_PROGRESS = "in progress" + FINISHED = "finished" + SENDING_LIMITS_EXCEEDED = "sending limits exceeded" + SCHEDULED = "scheduled" + CANCELLED = "cancelled" + READY_TO_SEND = "ready to send" + SENT_TO_DVLA = "sent to dvla" + ERROR = "error" + + +class AgreementType(Enum): + MOU = "MOU" + IAA = "IAA" + + +class AgreementStatus(Enum): + ACTIVE = "active" + EXPIRED = "expired" diff --git a/app/inbound_sms/rest.py b/app/inbound_sms/rest.py index 2a6f16277..7f8742a16 100644 --- a/app/inbound_sms/rest.py +++ b/app/inbound_sms/rest.py @@ -9,9 +9,9 @@ from app.dao.inbound_sms_dao import ( from app.dao.service_data_retention_dao import ( fetch_service_data_retention_by_notification_type, ) +from app.enums import NotificationType from app.errors import register_errors from app.inbound_sms.inbound_sms_schemas import get_inbound_sms_for_service_schema -from app.models import NotificationType from app.schema_validation import validate inbound_sms = Blueprint( diff --git a/app/models.py b/app/models.py index a656a926c..90dea83a6 100644 --- a/app/models.py +++ b/app/models.py @@ -21,6 +21,13 @@ from sqlalchemy.orm import validates from sqlalchemy.orm.collections import attribute_mapped_collection from app import db, encryption +from app.enums import ( # JobStatusType,; KeyType,; ServicePermissionType,; UserAuthType, + AgreementStatus, + AgreementType, + GuestListRecipientType, + NotificationType, + TemplateType, +) from app.hashing import check_hash, hashpw from app.history_meta import Versioned from app.utils import ( @@ -29,19 +36,6 @@ from app.utils import ( get_dt_string_or_none, ) - -class TemplateType(Enum): - SMS = "sms" - EMAIL = "email" - LETTER = "letter" - - -class NotificationType(Enum): - SMS = "sms" - EMAIL = "email" - LETTER = "letter" - - NORMAL = "normal" PRIORITY = "priority" TEMPLATE_PROCESS_TYPE = [NORMAL, PRIORITY] @@ -53,28 +47,18 @@ class TemplateProcessType(Enum): PRIORITY = "priority" +# TODO: Change this SMS_AUTH_TYPE = "sms_auth" EMAIL_AUTH_TYPE = "email_auth" WEBAUTHN_AUTH_TYPE = "webauthn_auth" USER_AUTH_TYPES = [SMS_AUTH_TYPE, EMAIL_AUTH_TYPE, WEBAUTHN_AUTH_TYPE] -class UserAuthType(Enum): - # TODO: Should User.auth_type be changed to use this? - SMS = "sms_auth" - EMAIL = "email_auth" - WEBAUTHN = "webauthn_auth" - - +# TODO: Change this DELIVERY_STATUS_CALLBACK_TYPE = "delivery_status" COMPLAINT_CALLBACK_TYPE = "complaint" SERVICE_CALLBACK_TYPES = [DELIVERY_STATUS_CALLBACK_TYPE, COMPLAINT_CALLBACK_TYPE] - - -# class ServiceCallbackType(Enum): -# # TODO: Should ServiceCallbackApi.callback_type be changed to use this? -# DELIVERY_STATUS = "delivery_status" -# COMPLAINT = "complaint" +# Need to import ServiceCallbackType from app.enums def filter_null_value_fields(obj): @@ -290,6 +274,7 @@ user_folder_permissions = db.Table( ) +# TODO: Change this BRANDING_GOVUK = "govuk" # Deprecated outside migrations BRANDING_ORG = "org" BRANDING_BOTH = "both" @@ -358,17 +343,7 @@ service_email_branding = db.Table( ) -class ServicePermissionType(Enum): - EMAIL = "email" - SMS = "sms" - INTERNATIONAL_SMS = "international_sms" - INBOUND_SMS = "inbound_sms" - SCHEDULE_NOTIFICATIONS = "schedule_notifications" - EMAIL_AUTH = "email_auth" - UPLOAD_DOCUMENT = "upload_document" - EDIT_FOLDER_PERMISSIONS = "edit_folder_permissions" - - +# TODO: This need to be changed class ServicePermissionTypes(db.Model): __tablename__ = "service_permission_types" @@ -386,6 +361,7 @@ class Domain(db.Model): ) +# TODO: Change this ORGANIZATION_TYPES = ["federal", "state", "other"] @@ -811,15 +787,6 @@ class ServicePermission(db.Model): ) -# MOBILE_TYPE = "mobile" -# EMAIL_TYPE = "email" - - -class GuestListRecipientType(Enum): - MOBILE = "mobile" - EMAIL = "email" - - guest_list_recipient_types = db.Enum(GuestListRecipientType, name="recipient_type") @@ -1007,18 +974,12 @@ class ApiKey(db.Model, Versioned): self._secret = encryption.encrypt(str(secret)) +# TODO: This needs to be changed KEY_TYPE_NORMAL = "normal" KEY_TYPE_TEAM = "team" KEY_TYPE_TEST = "test" -class KeyType(Enum): - # TODO: Should Key Types be rewritten to use this? - NORMAL = "normal" - TEAM = "team" - TEST = "test" - - class KeyTypes(db.Model): __tablename__ = "key_types" @@ -1373,19 +1334,6 @@ JOB_STATUS_TYPES = [ ] -class JobStatusType(Enum): - # TODO: Should Job.job_status be changed to use this? - PENDING = "pending" - IN_PROGRESS = "in progress" - FINISHED = "finished" - SENDING_LIMITS_EXCEEDED = "sending limits exceeded" - SCHEDULED = "scheduled" - CANCELLED = "cancelled" - READY_TO_SEND = "ready to send" - SENT_TO_DVLA = "sent to dvla" - ERROR = "error" - - class JobStatus(db.Model): __tablename__ = "job_status" @@ -1986,6 +1934,7 @@ INVITED_USER_STATUS_TYPES = [ INVITE_CANCELLED, INVITE_EXPIRED, ] +# TODO: Change these class InviteStatusType(db.Model): @@ -2091,6 +2040,7 @@ PERMISSION_LIST = [ PLATFORM_ADMIN, VIEW_ACTIVITY, ] +# TODO: Change These class Permission(db.Model): @@ -2442,16 +2392,6 @@ class WebauthnCredential(db.Model): } -class AgreementType(Enum): - MOU = "MOU" - IAA = "IAA" - - -class AgreementStatus(Enum): - ACTIVE = "active" - EXPIRED = "expired" - - class Agreement(db.Model): __tablename__ = "agreements" id = db.Column( diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 24a24bc8d..b9474508f 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -16,12 +16,8 @@ from app.dao.notifications_dao import ( dao_create_notification, dao_delete_notifications_by_id, ) -from app.models import ( - KEY_TYPE_TEST, - NOTIFICATION_CREATED, - Notification, - NotificationType, -) +from app.enums import NotificationType +from app.models import KEY_TYPE_TEST, NOTIFICATION_CREATED, Notification from app.v2.errors import BadRequestError diff --git a/app/notifications/rest.py b/app/notifications/rest.py index b1ea4e837..0d0b70db7 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -4,8 +4,9 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT from app import api_user, authenticated_service from app.config import QueueNames from app.dao import notifications_dao +from app.enums import NotificationType from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_TEAM, PRIORITY, NotificationType +from app.models import KEY_TYPE_TEAM, PRIORITY from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/notifications/validators.py b/app/notifications/validators.py index 6e65f6658..20bd2bc07 100644 --- a/app/notifications/validators.py +++ b/app/notifications/validators.py @@ -15,14 +15,8 @@ from app import redis_store from app.dao.notifications_dao import dao_get_notification_count_for_service from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id -from app.models import ( - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - NotificationType, - ServicePermission, - ServicePermissionType, - TemplateType, -) +from app.enums import NotificationType, ServicePermissionType, TemplateType +from app.models import KEY_TYPE_TEAM, KEY_TYPE_TEST, ServicePermission from app.notifications.process_notifications import create_content_for_notification from app.serialised_models import SerialisedTemplate from app.service.utils import service_allowed_to_send_to diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index 69587ea3e..2de961e43 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -12,8 +12,9 @@ from app.dao.invited_org_user_dao import ( save_invited_org_user, ) from app.dao.templates_dao import dao_get_template_by_id +from app.enums import NotificationType from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_NORMAL, InvitedOrganizationUser, NotificationType +from app.models import KEY_TYPE_NORMAL, InvitedOrganizationUser from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/performance_platform/total_sent_notifications.py b/app/performance_platform/total_sent_notifications.py index 514d32096..3d8806b7b 100644 --- a/app/performance_platform/total_sent_notifications.py +++ b/app/performance_platform/total_sent_notifications.py @@ -2,7 +2,7 @@ from app import performance_platform_client from app.dao.fact_notification_status_dao import ( get_total_sent_notifications_for_day_and_type, ) -from app.models import NotificationType +from app.enums import NotificationType # TODO: is this obsolete? it doesn't seem to be used anywhere @@ -20,7 +20,9 @@ def send_total_notifications_sent_for_day_stats(start_time, notification_type, c # TODO: is this obsolete? it doesn't seem to be used anywhere def get_total_sent_notifications_for_day(day): - email_count = get_total_sent_notifications_for_day_and_type(day, NotificationType.EMAIL) + email_count = get_total_sent_notifications_for_day_and_type( + day, NotificationType.EMAIL + ) sms_count = get_total_sent_notifications_for_day_and_type(day, NotificationType.SMS) return { diff --git a/app/service/send_notification.py b/app/service/send_notification.py index f1da3ac67..8f767e36a 100644 --- a/app/service/send_notification.py +++ b/app/service/send_notification.py @@ -6,7 +6,8 @@ from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id_and_service_id from app.dao.users_dao import get_user_by_id -from app.models import KEY_TYPE_NORMAL, PRIORITY, NotificationType +from app.enums import NotificationType +from app.models import KEY_TYPE_NORMAL, PRIORITY from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/service/service_data_retention_schema.py b/app/service/service_data_retention_schema.py index 7490519f5..da732c284 100644 --- a/app/service/service_data_retention_schema.py +++ b/app/service/service_data_retention_schema.py @@ -1,4 +1,4 @@ -from app.models import NotificationType +from app.enums import NotificationType add_service_data_retention_request = { "$schema": "http://json-schema.org/draft-07/schema#", @@ -7,7 +7,9 @@ add_service_data_retention_request = { "type": "object", "properties": { "days_of_retention": {"type": "integer"}, - "notification_type": {"enum": [NotificationType.SMS.value, NotificationType.EMAIL.value]}, + "notification_type": { + "enum": [NotificationType.SMS.value, NotificationType.EMAIL.value] + }, }, "required": ["days_of_retention", "notification_type"], } diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index f7e32339d..9c4dcc972 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -14,8 +14,9 @@ from app.dao.invited_user_dao import ( save_invited_user, ) from app.dao.templates_dao import dao_get_template_by_id +from app.enums import NotificationType from app.errors import InvalidRequest, register_errors -from app.models import INVITE_PENDING, KEY_TYPE_NORMAL, NotificationType, Service +from app.models import INVITE_PENDING, KEY_TYPE_NORMAL, Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/user/rest.py b/app/user/rest.py index d8b455d4c..3ac1b1d18 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -32,15 +32,9 @@ from app.dao.users_dao import ( update_user_password, use_user_code, ) +from app.enums import NotificationType, TemplateType, VerifyCodeType from app.errors import InvalidRequest, register_errors -from app.models import ( - KEY_TYPE_NORMAL, - NotificationType, - Permission, - Service, - TemplateType, - VerifyCodeType, -) +from app.models import KEY_TYPE_NORMAL, Permission, Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/utils.py b/app/utils.py index eea303250..fd2f0a393 100644 --- a/app/utils.py +++ b/app/utils.py @@ -78,7 +78,8 @@ def get_month_from_utc_column(column): def get_public_notify_type_text(notify_type, plural=False): - from app.models import UPLOAD_DOCUMENT, NotificationType + from app.enums import NotificationType + from app.models import UPLOAD_DOCUMENT notify_type_text = notify_type if notify_type == NotificationType.SMS: diff --git a/app/v2/notifications/post_notifications.py b/app/v2/notifications/post_notifications.py index 1af6e1068..0226eb911 100644 --- a/app/v2/notifications/post_notifications.py +++ b/app/v2/notifications/post_notifications.py @@ -10,13 +10,8 @@ from app import api_user, authenticated_service, document_download_client, encry from app.celery.tasks import save_api_email, save_api_sms from app.clients.document_download import DocumentDownloadError from app.config import QueueNames -from app.models import ( - KEY_TYPE_NORMAL, - NOTIFICATION_CREATED, - PRIORITY, - Notification, - NotificationType, -) +from app.enums import NotificationType +from app.models import KEY_TYPE_NORMAL, NOTIFICATION_CREATED, PRIORITY, Notification from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue_detached, From 985ad27b3e2e312ccbb8f896c71a733d613db452 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 10 Jan 2024 12:45:03 -0500 Subject: [PATCH 138/259] Getting imports right to use app.enums Signed-off-by: Cliff Hill --- app/dao/services_dao.py | 3 +-- app/notifications/receive_notifications.py | 3 ++- app/schemas.py | 5 +++-- app/service/rest.py | 2 +- app/service/sender.py | 3 ++- app/service/statistics.py | 3 ++- app/service/utils.py | 9 ++------- app/template/rest.py | 3 ++- app/template/template_schemas.py | 3 ++- app/utils.py | 2 +- app/v2/notifications/notification_schemas.py | 3 ++- app/v2/template/template_schemas.py | 2 +- app/v2/templates/templates_schemas.py | 2 +- 13 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index e9f346735..bdc5e6ee7 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -13,7 +13,7 @@ from app.dao.organization_dao import dao_get_organization_by_email_address from app.dao.service_sms_sender_dao import insert_service_sms_sender from app.dao.service_user_dao import dao_get_service_user from app.dao.template_folder_dao import dao_get_valid_template_folders_by_id -from app.enums import NotificationType +from app.enums import NotificationType, ServicePermissionType from app.models import ( KEY_TYPE_TEST, NOTIFICATION_PERMANENT_FAILURE, @@ -30,7 +30,6 @@ from app.models import ( Service, ServiceEmailReplyTo, ServicePermission, - ServicePermissionType, ServiceSmsSender, Template, TemplateHistory, diff --git a/app/notifications/receive_notifications.py b/app/notifications/receive_notifications.py index c1031417b..820d66cb4 100644 --- a/app/notifications/receive_notifications.py +++ b/app/notifications/receive_notifications.py @@ -5,8 +5,9 @@ from app.celery import tasks from app.config import QueueNames from app.dao.inbound_sms_dao import dao_create_inbound_sms from app.dao.services_dao import dao_fetch_service_by_inbound_number +from app.enums import ServicePermissionType from app.errors import InvalidRequest, register_errors -from app.models import InboundSms, ServicePermissionType +from app.models import InboundSms from app.notifications.sns_handlers import sns_notification_handler receive_notifications_blueprint = Blueprint("receive_notifications", __name__) diff --git a/app/schemas.py b/app/schemas.py index 5ac1af48b..0161e4901 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -24,6 +24,7 @@ from notifications_utils.recipients import ( from app import ma, models from app.dao.permissions_dao import permission_dao +from app.enums import TemplateType from app.models import ServicePermission from app.utils import DATETIME_FORMAT_NO_TIMEZONE, get_template_instance @@ -382,7 +383,7 @@ class TemplateSchema(BaseTemplateSchema, UUIDsAsStringsMixin): @validates_schema def validate_type(self, data, **kwargs): - if data.get("template_type") == models.TemplateType.EMAIL: + if data.get("template_type") == TemplateType.EMAIL: subject = data.get("subject") if not subject or subject.strip() == "": raise ValidationError("Invalid template subject", "subject") @@ -628,7 +629,7 @@ class NotificationWithPersonalisationSchema(NotificationWithTemplateSchema): in_data["template"], in_data["personalisation"] ) in_data["body"] = template.content_with_placeholders_filled_in - if in_data["template"]["template_type"] != models.TemplateType.SMS: + if in_data["template"]["template_type"] != TemplateType.SMS: in_data["subject"] = template.subject in_data["content_char_count"] = None else: diff --git a/app/service/rest.py b/app/service/rest.py index 355978b5d..4132e995f 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -633,7 +633,7 @@ def get_detailed_services( @service_blueprint.route("//guest-list", methods=["GET"]) def get_guest_list(service_id): - from app.models import GuestListRecipientType + from app.enums import GuestListRecipientType service = dao_fetch_service_by_id(service_id) diff --git a/app/service/sender.py b/app/service/sender.py index ed89d71d5..4e96f48c0 100644 --- a/app/service/sender.py +++ b/app/service/sender.py @@ -6,7 +6,8 @@ from app.dao.services_dao import ( dao_fetch_service_by_id, ) from app.dao.templates_dao import dao_get_template_by_id -from app.models import KEY_TYPE_NORMAL, TemplateType +from app.enums import TemplateType +from app.models import KEY_TYPE_NORMAL from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/service/statistics.py b/app/service/statistics.py index 37e66f362..2c569de09 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -2,7 +2,8 @@ from collections import defaultdict from datetime import datetime from app.dao.date_util import get_months_for_financial_year -from app.models import NOTIFICATION_STATUS_TYPES, TemplateType +from app.enums import TemplateType +from app.models import NOTIFICATION_STATUS_TYPES def format_statistics(statistics): diff --git a/app/service/utils.py b/app/service/utils.py index 3b21b90ee..be8e7259a 100644 --- a/app/service/utils.py +++ b/app/service/utils.py @@ -3,13 +3,8 @@ import itertools from notifications_utils.recipients import allowed_to_send_to from app.dao.services_dao import dao_fetch_service_by_id -from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - GuestListRecipientType, - ServiceGuestList, -) +from app.enums import GuestListRecipientType +from app.models import KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, ServiceGuestList def get_recipients_from_request(request_json, key, type): diff --git a/app/template/rest.py b/app/template/rest.py index 9ef8e65a7..750b93891 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -13,8 +13,9 @@ from app.dao.templates_dao import ( dao_redact_template, dao_update_template, ) +from app.enums import TemplateType from app.errors import InvalidRequest, register_errors -from app.models import Template, TemplateType +from app.models import Template from app.notifications.validators import check_reply_to, service_has_permission from app.schema_validation import validate from app.schemas import ( diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py index e94863b4d..6ac11c781 100644 --- a/app/template/template_schemas.py +++ b/app/template/template_schemas.py @@ -1,4 +1,5 @@ -from app.models import TEMPLATE_PROCESS_TYPE, TemplateType +from app.enums import TemplateType +from app.models import TEMPLATE_PROCESS_TYPE from app.schema_validation.definitions import nullable_uuid, uuid post_create_template_schema = { diff --git a/app/utils.py b/app/utils.py index fd2f0a393..fc68acd86 100644 --- a/app/utils.py +++ b/app/utils.py @@ -41,7 +41,7 @@ def url_with_token(data, url, config, base_url=None): def get_template_instance(template, values): - from app.models import TemplateType + from app.enums import TemplateType return { TemplateType.SMS: SMSMessageTemplate, diff --git a/app/v2/notifications/notification_schemas.py b/app/v2/notifications/notification_schemas.py index 9bfa2b9cb..83419da93 100644 --- a/app/v2/notifications/notification_schemas.py +++ b/app/v2/notifications/notification_schemas.py @@ -1,4 +1,5 @@ -from app.models import NOTIFICATION_STATUS_TYPES, TemplateType +from app.enums import TemplateType +from app.models import NOTIFICATION_STATUS_TYPES from app.schema_validation.definitions import personalisation, uuid template = { diff --git a/app/v2/template/template_schemas.py b/app/v2/template/template_schemas.py index d6799e458..8d0cf300d 100644 --- a/app/v2/template/template_schemas.py +++ b/app/v2/template/template_schemas.py @@ -1,4 +1,4 @@ -from app.models import TemplateType +from app.enums import TemplateType from app.schema_validation.definitions import personalisation, uuid get_template_by_id_request = { diff --git a/app/v2/templates/templates_schemas.py b/app/v2/templates/templates_schemas.py index 57dd8a08f..70aea7522 100644 --- a/app/v2/templates/templates_schemas.py +++ b/app/v2/templates/templates_schemas.py @@ -1,4 +1,4 @@ -from app.models import TemplateType +from app.enums import TemplateType from app.v2.template.template_schemas import get_template_by_id_response as template get_all_template_request = { From f1206410878da88e4af763c32b5d900b099d9a4d Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 12 Jan 2024 16:05:18 -0500 Subject: [PATCH 139/259] Getting permission types configured. Signed-off-by: Cliff Hill --- app/dao/invited_user_dao.py | 9 ++-- app/dao/permissions_dao.py | 25 ++-------- app/dao/services_dao.py | 8 +--- app/enums.py | 58 +++++++++++++++++++++- app/models.py | 96 ++++++------------------------------- 5 files changed, 81 insertions(+), 115 deletions(-) diff --git a/app/dao/invited_user_dao.py b/app/dao/invited_user_dao.py index 2e807c069..9ab54e0da 100644 --- a/app/dao/invited_user_dao.py +++ b/app/dao/invited_user_dao.py @@ -1,7 +1,8 @@ from datetime import datetime, timedelta from app import db -from app.models import INVITE_EXPIRED, INVITE_PENDING, InvitedUser +from app.enums import InvitedUserStatusType +from app.models import InvitedUser def save_invited_user(invited_user): @@ -20,7 +21,7 @@ def get_expired_invite_by_service_and_id(service_id, invited_user_id): return InvitedUser.query.filter( InvitedUser.service_id == service_id, InvitedUser.id == invited_user_id, - InvitedUser.status == INVITE_EXPIRED, + InvitedUser.status == InvitedUserStatusType.EXPIRED, ).one() @@ -41,9 +42,9 @@ def expire_invitations_created_more_than_two_days_ago(): db.session.query(InvitedUser) .filter( InvitedUser.created_at <= datetime.utcnow() - timedelta(days=2), - InvitedUser.status.in_((INVITE_PENDING,)), + InvitedUser.status.in_((InvitedUserStatusType.PENDING,)), ) - .update({InvitedUser.status: INVITE_EXPIRED}) + .update({InvitedUser.status: InvitedUserStatusType.EXPIRED}) ) db.session.commit() return expired diff --git a/app/dao/permissions_dao.py b/app/dao/permissions_dao.py index 88bca6443..5f6f91583 100644 --- a/app/dao/permissions_dao.py +++ b/app/dao/permissions_dao.py @@ -1,26 +1,7 @@ from app import db from app.dao import DAOClass -from app.models import ( - MANAGE_API_KEYS, - MANAGE_SETTINGS, - MANAGE_TEMPLATES, - MANAGE_USERS, - SEND_EMAILS, - SEND_TEXTS, - VIEW_ACTIVITY, - Permission, -) - -# Default permissions for a service -default_service_permissions = [ - MANAGE_USERS, - MANAGE_TEMPLATES, - MANAGE_SETTINGS, - SEND_TEXTS, - SEND_EMAILS, - MANAGE_API_KEYS, - VIEW_ACTIVITY, -] +from app.enums import PermissionType +from app.models import Permission class PermissionDAO(DAOClass): @@ -28,7 +9,7 @@ class PermissionDAO(DAOClass): model = Permission def add_default_service_permissions_for_user(self, user, service): - for name in default_service_permissions: + for name in PermissionType.defaults: permission = Permission(permission=name, user=user, service=service) self.create_instance(permission, _commit=False) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index bdc5e6ee7..9da8b567d 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -43,12 +43,6 @@ from app.utils import ( get_midnight_in_utc, ) -DEFAULT_SERVICE_PERMISSIONS = [ - ServicePermissionType.SMS, - ServicePermissionType.EMAIL, - ServicePermissionType.INTERNATIONAL_SMS, -] - def dao_fetch_all_services(only_active=False): query = Service.query.order_by(asc(Service.created_at)).options(joinedload("users")) @@ -278,7 +272,7 @@ def dao_create_service( raise ValueError("Can't create a service without a user") if service_permissions is None: - service_permissions = DEFAULT_SERVICE_PERMISSIONS + service_permissions = ServicePermissionType.defaults organization = dao_get_organization_by_email_address(user.email_address) diff --git a/app/enums.py b/app/enums.py index 3a6ce9355..65eb4c067 100644 --- a/app/enums.py +++ b/app/enums.py @@ -13,6 +13,12 @@ class NotificationType(Enum): LETTER = "letter" +class TemplateProcessType(Enum): + # TODO: Should Template.process_type be changed to use this? + NORMAL = "normal" + PRIORITY = "priority" + + class UserAuthType(Enum): SMS = "sms_auth" EMAIL = "email_auth" @@ -20,11 +26,33 @@ class UserAuthType(Enum): class ServiceCallbackType(Enum): - # TODO: Should ServiceCallbackApi.callback_type be changed to use this? DELIVERY_STATUS = "delivery_status" COMPLAINT = "complaint" +class PermissionType(Enum): + MANAGE_USERS = "manage_users" + MANAGE_TEMPLATES = "manage_templates" + MANAGE_SETTINGS = "manage_settings" + SEND_TEXTS = "send_texts" + SEND_EMAILS = "send_emails" + MANAGE_API_KEYS = "manage_api_keys" + PLATFORM_ADMIN = "platform_admin" + VIEW_ACTIVITY = "view_activity" + + @property + def defaults(self) -> tuple["PermissionType", ...]: + cls = type(self) + return ( + cls.MANAGE_USERS, + cls.MANAGE_TEMPLATES, + cls.MANAGE_SETTINGS, + cls.SEND_TEXTS, + cls.SEND_EMAILS, + cls.MANAGE_API_KEYS, + cls.VIEW_ACTIVITY, + ) + class ServicePermissionType(Enum): EMAIL = "email" SMS = "sms" @@ -35,6 +63,14 @@ class ServicePermissionType(Enum): UPLOAD_DOCUMENT = "upload_document" EDIT_FOLDER_PERMISSIONS = "edit_folder_permissions" + @property + def defaults(self) -> tuple["ServicePermissionType", ...]: + cls = type(self) + return ( + cls.SMS, + cls.EMAIL, + cls.INTERNATIONAL_SMS, + ) class GuestListRecipientType(Enum): MOBILE = "mobile" @@ -59,6 +95,26 @@ class JobStatusType(Enum): ERROR = "error" +class InvitedUserStatusType(Enum): + PENDING = "pending" + ACCEPTED = "accepted" + CANCELLED = "cancelled" + EXPIRED = "expired" + + +class BrandingType(Enum): + # TODO: Should EmailBranding.branding_type be changed to use this? + GOVUK = "govuk" # Deprecated outside migrations + ORG = "org" + BOTH = "both" + ORG_BANNER = "org_banner" + + +class VerifyCodeType(Enum): + EMAIL = "email" + SMS = "sms" + + class AgreementType(Enum): MOU = "MOU" IAA = "IAA" diff --git a/app/models.py b/app/models.py index 90dea83a6..1b9d6548e 100644 --- a/app/models.py +++ b/app/models.py @@ -1,7 +1,6 @@ import datetime import itertools import uuid -from enum import Enum from flask import current_app, url_for from notifications_utils.clients.encryption.encryption_client import EncryptionError @@ -21,12 +20,16 @@ from sqlalchemy.orm import validates from sqlalchemy.orm.collections import attribute_mapped_collection from app import db, encryption -from app.enums import ( # JobStatusType,; KeyType,; ServicePermissionType,; UserAuthType, +from app.enums import ( # JobStatusType,; KeyType,; UserAuthType,; TemplateProcessType, AgreementStatus, AgreementType, GuestListRecipientType, + InvitedUserStatusType, NotificationType, + PermissionType, + ServicePermissionType, TemplateType, + VerifyCodeType, ) from app.hashing import check_hash, hashpw from app.history_meta import Versioned @@ -36,17 +39,12 @@ from app.utils import ( get_dt_string_or_none, ) +# TODO: Change this NORMAL = "normal" PRIORITY = "priority" TEMPLATE_PROCESS_TYPE = [NORMAL, PRIORITY] -class TemplateProcessType(Enum): - # TODO: Should Template.process_type be changed to use this? - NORMAL = "normal" - PRIORITY = "priority" - - # TODO: Change this SMS_AUTH_TYPE = "sms_auth" EMAIL_AUTH_TYPE = "email_auth" @@ -282,14 +280,6 @@ BRANDING_ORG_BANNER = "org_banner" BRANDING_TYPES = [BRANDING_ORG, BRANDING_BOTH, BRANDING_ORG_BANNER] -class BrandingType(Enum): - # TODO: Should EmailBranding.branding_type be changed to use this? - GOVUK = "govuk" # Deprecated outside migrations - ORG = "org" - BOTH = "both" - ORG_BANNER = "org_banner" - - class BrandingTypes(db.Model): __tablename__ = "branding_type" name = db.Column(db.String(255), primary_key=True) @@ -343,13 +333,6 @@ service_email_branding = db.Table( ) -# TODO: This need to be changed -class ServicePermissionTypes(db.Model): - __tablename__ = "service_permission_types" - - name = db.Column(db.String(255), primary_key=True) - - class Domain(db.Model): __tablename__ = "domain" domain = db.Column(db.String(255), primary_key=True) @@ -766,12 +749,12 @@ class ServicePermission(db.Model): index=True, nullable=False, ) - permission = db.Column( - db.String(255), - db.ForeignKey("service_permission_types.name"), + permission = db.Enum( + PermissionType, + name="permission_type", index=True, primary_key=True, - nullable=False, + nullable=False ) created_at = db.Column( db.DateTime, default=datetime.datetime.utcnow, nullable=False @@ -1398,11 +1381,6 @@ class Job(db.Model): archived = db.Column(db.Boolean, nullable=False, default=False) -class VerifyCodeType(Enum): - EMAIL = "email" - SMS = "sms" - - class VerifyCode(db.Model): __tablename__ = "verify_codes" @@ -1924,25 +1902,6 @@ class NotificationHistory(db.Model, HistoryModel): self.status = original.status -INVITE_PENDING = "pending" -INVITE_ACCEPTED = "accepted" -INVITE_CANCELLED = "cancelled" -INVITE_EXPIRED = "expired" -INVITED_USER_STATUS_TYPES = [ - INVITE_PENDING, - INVITE_ACCEPTED, - INVITE_CANCELLED, - INVITE_EXPIRED, -] -# TODO: Change these - - -class InviteStatusType(db.Model): - __tablename__ = "invite_status_type" - - name = db.Column(db.String, primary_key=True) - - class InvitedUser(db.Model): __tablename__ = "invited_users" @@ -1964,9 +1923,9 @@ class InvitedUser(db.Model): default=datetime.datetime.utcnow, ) status = db.Column( - db.Enum(*INVITED_USER_STATUS_TYPES, name="invited_users_status_types"), + db.Enum(InvitedUserStatusType, name="invited_users_status_types"), nullable=False, - default=INVITE_PENDING, + default=InvitedUserStatusType.PENDING, ) permissions = db.Column(db.String, nullable=False) auth_type = db.Column( @@ -2002,10 +1961,9 @@ class InvitedOrganizationUser(db.Model): ) status = db.Column( - db.String, - db.ForeignKey("invite_status_type.name"), + db.Enum(InvitedUserStatusType, name="invited_users_status_types"), nullable=False, - default=INVITE_PENDING, + default=InvitedUserStatusType.PENDING, ) def serialize(self): @@ -2019,30 +1977,6 @@ class InvitedOrganizationUser(db.Model): } -# Service Permissions -MANAGE_USERS = "manage_users" -MANAGE_TEMPLATES = "manage_templates" -MANAGE_SETTINGS = "manage_settings" -SEND_TEXTS = "send_texts" -SEND_EMAILS = "send_emails" -MANAGE_API_KEYS = "manage_api_keys" -PLATFORM_ADMIN = "platform_admin" -VIEW_ACTIVITY = "view_activity" - -# List of permissions -PERMISSION_LIST = [ - MANAGE_USERS, - MANAGE_TEMPLATES, - MANAGE_SETTINGS, - SEND_TEXTS, - SEND_EMAILS, - MANAGE_API_KEYS, - PLATFORM_ADMIN, - VIEW_ACTIVITY, -] -# TODO: Change These - - class Permission(db.Model): __tablename__ = "permissions" @@ -2061,7 +1995,7 @@ class Permission(db.Model): ) user = db.relationship("User") permission = db.Column( - db.Enum(*PERMISSION_LIST, name="permission_types"), + db.Enum(ServicePermissionType, name="permission_types"), index=False, unique=False, nullable=False, From 661904e2cd1019b55e98b5de7a00a2ff0a44d6bd Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 12 Jan 2024 17:27:31 -0500 Subject: [PATCH 140/259] More fixes for enums. Signed-off-by: Cliff Hill --- app/enums.py | 4 +++- app/models.py | 50 +++++++++++++++++++++++++------------------- app/service/rest.py | 6 +++--- app/service/utils.py | 6 +++--- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/app/enums.py b/app/enums.py index 65eb4c067..224fe469b 100644 --- a/app/enums.py +++ b/app/enums.py @@ -53,6 +53,7 @@ class PermissionType(Enum): cls.VIEW_ACTIVITY, ) + class ServicePermissionType(Enum): EMAIL = "email" SMS = "sms" @@ -72,7 +73,8 @@ class ServicePermissionType(Enum): cls.INTERNATIONAL_SMS, ) -class GuestListRecipientType(Enum): + +class RecipientType(Enum): MOBILE = "mobile" EMAIL = "email" diff --git a/app/models.py b/app/models.py index 1b9d6548e..550512e65 100644 --- a/app/models.py +++ b/app/models.py @@ -23,10 +23,9 @@ from app import db, encryption from app.enums import ( # JobStatusType,; KeyType,; UserAuthType,; TemplateProcessType, AgreementStatus, AgreementType, - GuestListRecipientType, InvitedUserStatusType, NotificationType, - PermissionType, + RecipientType, ServicePermissionType, TemplateType, VerifyCodeType, @@ -749,12 +748,11 @@ class ServicePermission(db.Model): index=True, nullable=False, ) - permission = db.Enum( - PermissionType, - name="permission_type", + permission = db.Column( + db.Enum(ServicePermissionType, name="service_permission_types"), index=True, primary_key=True, - nullable=False + nullable=False, ) created_at = db.Column( db.DateTime, default=datetime.datetime.utcnow, nullable=False @@ -770,9 +768,6 @@ class ServicePermission(db.Model): ) -guest_list_recipient_types = db.Enum(GuestListRecipientType, name="recipient_type") - - class ServiceGuestList(db.Model): __tablename__ = "service_whitelist" @@ -781,7 +776,9 @@ class ServiceGuestList(db.Model): UUID(as_uuid=True), db.ForeignKey("services.id"), index=True, nullable=False ) service = db.relationship("Service", backref="guest_list") - recipient_type = db.Column(guest_list_recipient_types, nullable=False) + recipient_type = db.Column( + db.Enum(RecipientType, name="recipient_types"), nullable=False + ) recipient = db.Column(db.String(255), nullable=False) created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) @@ -790,11 +787,11 @@ class ServiceGuestList(db.Model): instance = cls(service_id=service_id, recipient_type=recipient_type) try: - if recipient_type == GuestListRecipientType.MOBILE: + if recipient_type == RecipientType.MOBILE: instance.recipient = validate_phone_number( recipient, international=True ) - elif recipient_type == GuestListRecipientType.EMAIL: + elif recipient_type == RecipientType.EMAIL: instance.recipient = validate_email_address(recipient) else: raise ValueError("Invalid recipient type") @@ -1252,8 +1249,6 @@ EMAIL_PROVIDERS = [SES_PROVIDER] PROVIDERS = SMS_PROVIDERS + EMAIL_PROVIDERS # TODO: What about these? -notification_types = db.Enum(NotificationType, name="notification_type") - class ProviderDetails(db.Model): __tablename__ = "provider_details" @@ -1262,7 +1257,9 @@ class ProviderDetails(db.Model): display_name = db.Column(db.String, nullable=False) identifier = db.Column(db.String, nullable=False) priority = db.Column(db.Integer, nullable=False) - notification_type = db.Column(notification_types, nullable=False) + notification_type = db.Column( + db.Enum(NotificationType, name="notification_type"), nullable=False + ) active = db.Column(db.Boolean, default=False, nullable=False) version = db.Column(db.Integer, default=1, nullable=False) updated_at = db.Column( @@ -1282,7 +1279,10 @@ class ProviderDetailsHistory(db.Model, HistoryModel): display_name = db.Column(db.String, nullable=False) identifier = db.Column(db.String, nullable=False) priority = db.Column(db.Integer, nullable=False) - notification_type = db.Column(notification_types, nullable=False) + notification_type = db.Column( + db.Enum(NotificationType, name="notification_type"), + nullable=False, + ) active = db.Column(db.Boolean, nullable=False) version = db.Column(db.Integer, primary_key=True, nullable=False) updated_at = db.Column( @@ -1533,7 +1533,7 @@ class NotificationAllTimeView(db.Model): api_key_id = db.Column(UUID(as_uuid=True)) key_type = db.Column(db.String) billable_units = db.Column(db.Integer) - notification_type = db.Column(notification_types) + notification_type = db.Column(db.Enum(NotificationType, name="notification_type")) created_at = db.Column(db.DateTime) sent_at = db.Column(db.DateTime) sent_by = db.Column(db.String) @@ -1574,7 +1574,9 @@ class Notification(db.Model): db.String, db.ForeignKey("key_types.name"), unique=False, nullable=False ) billable_units = db.Column(db.Integer, nullable=False, default=0) - notification_type = db.Column(notification_types, nullable=False) + notification_type = db.Column( + db.Enum(NotificationType, name="notification_type"), nullable=False + ) created_at = db.Column(db.DateTime, index=True, unique=False, nullable=False) sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True) sent_by = db.Column(db.String, nullable=True) @@ -1847,7 +1849,9 @@ class NotificationHistory(db.Model, HistoryModel): db.String, db.ForeignKey("key_types.name"), unique=False, nullable=False ) billable_units = db.Column(db.Integer, nullable=False, default=0) - notification_type = db.Column(notification_types, nullable=False) + notification_type = db.Column( + db.Enum(NotificationType, name="notification_type"), nullable=False + ) created_at = db.Column(db.DateTime, unique=False, nullable=False) sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True) sent_by = db.Column(db.String, nullable=True) @@ -2036,7 +2040,9 @@ class Rate(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) valid_from = db.Column(db.DateTime, nullable=False) rate = db.Column(db.Float(asdecimal=False), nullable=False) - notification_type = db.Column(notification_types, index=True, nullable=False) + notification_type = db.Column( + db.Enum(NotificationType, name="notification_type"), index=True, nullable=False + ) def __str__(self): the_string = "{}".format(self.rate) @@ -2259,7 +2265,9 @@ class ServiceDataRetention(db.Model): collection_class=attribute_mapped_collection("notification_type"), ), ) - notification_type = db.Column(notification_types, nullable=False) + notification_type = db.Column( + db.Enum(NotificationType, name="notification_type"), nullable=False + ) days_of_retention = db.Column(db.Integer, nullable=False) created_at = db.Column( db.DateTime, nullable=False, default=datetime.datetime.utcnow diff --git a/app/service/rest.py b/app/service/rest.py index 4132e995f..8f0515c6b 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -633,7 +633,7 @@ def get_detailed_services( @service_blueprint.route("//guest-list", methods=["GET"]) def get_guest_list(service_id): - from app.enums import GuestListRecipientType + from app.enums import RecipientType service = dao_fetch_service_by_id(service_id) @@ -645,12 +645,12 @@ def get_guest_list(service_id): email_addresses=[ item.recipient for item in guest_list - if item.recipient_type == GuestListRecipientType.EMAIL + if item.recipient_type == RecipientType.EMAIL ], phone_numbers=[ item.recipient for item in guest_list - if item.recipient_type == GuestListRecipientType.MOBILE + if item.recipient_type == RecipientType.MOBILE ], ) diff --git a/app/service/utils.py b/app/service/utils.py index be8e7259a..61e41c859 100644 --- a/app/service/utils.py +++ b/app/service/utils.py @@ -3,7 +3,7 @@ import itertools from notifications_utils.recipients import allowed_to_send_to from app.dao.services_dao import dao_fetch_service_by_id -from app.enums import GuestListRecipientType +from app.enums import RecipientType from app.models import KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, ServiceGuestList @@ -16,10 +16,10 @@ def get_guest_list_objects(service_id, request_json): ServiceGuestList.from_string(service_id, type, recipient) for type, recipient in ( get_recipients_from_request( - request_json, "phone_numbers", GuestListRecipientType.MOBILE + request_json, "phone_numbers", RecipientType.MOBILE ) + get_recipients_from_request( - request_json, "email_addresses", GuestListRecipientType.EMAIL + request_json, "email_addresses", RecipientType.EMAIL ) ) ] From a177042e776569b6793c9ee758e95db2303c7c30 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 12 Jan 2024 17:46:00 -0500 Subject: [PATCH 141/259] Formatting cleanup for models.py Signed-off-by: Cliff Hill --- app/dao/invited_user_dao.py | 8 +- app/enums.py | 4 +- app/models.py | 417 +++++++++++++++++++++++++++--------- app/user/rest.py | 4 +- 4 files changed, 319 insertions(+), 114 deletions(-) diff --git a/app/dao/invited_user_dao.py b/app/dao/invited_user_dao.py index 9ab54e0da..ab83c2534 100644 --- a/app/dao/invited_user_dao.py +++ b/app/dao/invited_user_dao.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from app import db -from app.enums import InvitedUserStatusType +from app.enums import InvitedUserStatus from app.models import InvitedUser @@ -21,7 +21,7 @@ def get_expired_invite_by_service_and_id(service_id, invited_user_id): return InvitedUser.query.filter( InvitedUser.service_id == service_id, InvitedUser.id == invited_user_id, - InvitedUser.status == InvitedUserStatusType.EXPIRED, + InvitedUser.status == InvitedUserStatus.EXPIRED, ).one() @@ -42,9 +42,9 @@ def expire_invitations_created_more_than_two_days_ago(): db.session.query(InvitedUser) .filter( InvitedUser.created_at <= datetime.utcnow() - timedelta(days=2), - InvitedUser.status.in_((InvitedUserStatusType.PENDING,)), + InvitedUser.status.in_((InvitedUserStatus.PENDING,)), ) - .update({InvitedUser.status: InvitedUserStatusType.EXPIRED}) + .update({InvitedUser.status: InvitedUserStatus.EXPIRED}) ) db.session.commit() return expired diff --git a/app/enums.py b/app/enums.py index 224fe469b..ade2856d3 100644 --- a/app/enums.py +++ b/app/enums.py @@ -97,7 +97,7 @@ class JobStatusType(Enum): ERROR = "error" -class InvitedUserStatusType(Enum): +class InvitedUserStatus(Enum): PENDING = "pending" ACCEPTED = "accepted" CANCELLED = "cancelled" @@ -112,7 +112,7 @@ class BrandingType(Enum): ORG_BANNER = "org_banner" -class VerifyCodeType(Enum): +class CodeType(Enum): EMAIL = "email" SMS = "sms" diff --git a/app/models.py b/app/models.py index 550512e65..e62ab3ca7 100644 --- a/app/models.py +++ b/app/models.py @@ -23,12 +23,13 @@ from app import db, encryption from app.enums import ( # JobStatusType,; KeyType,; UserAuthType,; TemplateProcessType, AgreementStatus, AgreementType, - InvitedUserStatusType, + CodeType, + InvitedUserStatus, NotificationType, + PermissionType, RecipientType, ServicePermissionType, TemplateType, - VerifyCodeType, ) from app.hashing import check_hash, hashpw from app.history_meta import Versioned @@ -130,7 +131,11 @@ class User(db.Model): default=datetime.datetime.utcnow, ) preferred_timezone = db.Column( - db.Text, nullable=True, index=False, unique=False, default="US/Eastern" + db.Text, + nullable=True, + index=False, + unique=False, + default="US/Eastern", ) # either email auth or a mobile number must be provided @@ -140,7 +145,9 @@ class User(db.Model): services = db.relationship("Service", secondary="user_to_service", backref="users") organizations = db.relationship( - "Organization", secondary="user_to_organization", backref="users" + "Organization", + secondary="user_to_organization", + backref="users", ) @validates("mobile_number") @@ -232,11 +239,15 @@ class ServiceUser(db.Model): __tablename__ = "user_to_service" user_id = db.Column(UUID(as_uuid=True), db.ForeignKey("users.id"), primary_key=True) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), primary_key=True + UUID(as_uuid=True), + db.ForeignKey("services.id"), + primary_key=True, ) - __table_args__ = ( - UniqueConstraint("user_id", "service_id", name="uix_user_to_service"), + __table_args__ = UniqueConstraint( + "user_id", + "service_id", + name="uix_user_to_service", ) @@ -362,10 +373,14 @@ class Organization(db.Model): name = db.Column(db.String(255), nullable=False, unique=True, index=True) active = db.Column(db.Boolean, nullable=False, default=True) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) agreement_signed = db.Column(db.Boolean, nullable=True) agreement_signed_at = db.Column(db.DateTime, nullable=True) @@ -388,9 +403,7 @@ class Organization(db.Model): ) request_to_go_live_notes = db.Column(db.Text) - domains = db.relationship( - "Domain", - ) + domains = db.relationship("Domain") email_branding = db.relationship("EmailBranding") email_branding_id = db.Column( @@ -497,16 +510,26 @@ class Service(db.Model, Versioned): onupdate=datetime.datetime.utcnow, ) active = db.Column( - db.Boolean, index=False, unique=False, nullable=False, default=True + db.Boolean, + index=False, + unique=False, + nullable=False, + default=True, ) message_limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False) total_message_limit = db.Column( - db.BigInteger, index=False, unique=False, nullable=False + db.BigInteger, + index=False, + unique=False, + nullable=False, ) restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False) email_from = db.Column(db.Text, index=False, unique=True, nullable=False) created_by_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("users.id"), + index=True, + nullable=False, ) created_by = db.relationship("User", foreign_keys=[created_by_id]) prefix_sms = db.Column(db.Boolean, nullable=False, default=True) @@ -523,13 +546,18 @@ class Service(db.Model, Versioned): consent_to_research = db.Column(db.Boolean, nullable=True) count_as_live = db.Column(db.Boolean, nullable=False, default=True) go_live_user_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=True + UUID(as_uuid=True), + db.ForeignKey("users.id"), + nullable=True, ) go_live_user = db.relationship("User", foreign_keys=[go_live_user_id]) go_live_at = db.Column(db.DateTime, nullable=True) organization_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("organization.id"), index=True, nullable=True + UUID(as_uuid=True), + db.ForeignKey("organization.id"), + index=True, + nullable=True, ) organization = db.relationship("Organization", backref="services") @@ -588,7 +616,10 @@ class Service(db.Model, Versioned): class AnnualBilling(db.Model): __tablename__ = "annual_billing" id = db.Column( - UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=False + UUID(as_uuid=True), + primary_key=True, + default=uuid.uuid4, + unique=False, ) service_id = db.Column( UUID(as_uuid=True), @@ -598,22 +629,35 @@ class AnnualBilling(db.Model): nullable=False, ) financial_year_start = db.Column( - db.Integer, nullable=False, default=True, unique=False + db.Integer, + nullable=False, + default=True, + unique=False, ) free_sms_fragment_limit = db.Column( - db.Integer, nullable=False, index=False, unique=False + db.Integer, + nullable=False, + index=False, + unique=False, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) UniqueConstraint( - "financial_year_start", "service_id", name="ix_annual_billing_service_id" + "financial_year_start", + "service_id", + name="ix_annual_billing_service_id", ) service = db.relationship( - Service, backref=db.backref("annual_billing", uselist=True) + Service, + backref=db.backref("annual_billing", uselist=True), ) __table_args__ = ( @@ -659,16 +703,25 @@ class InboundNumber(db.Model): nullable=True, ) service = db.relationship( - Service, backref=db.backref("inbound_number", uselist=False) + Service, + backref=db.backref("inbound_number", uselist=False), ) active = db.Column( - db.Boolean, index=False, unique=False, nullable=False, default=True + db.Boolean, + index=False, + unique=False, + nullable=False, + default=True, ) created_at = db.Column( - db.DateTime, default=datetime.datetime.utcnow, nullable=False + db.DateTime, + default=datetime.datetime.utcnow, + nullable=False, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) def serialize(self): @@ -699,7 +752,8 @@ class ServiceSmsSender(db.Model): unique=False, ) service = db.relationship( - Service, backref=db.backref("service_sms_senders", uselist=True) + Service, + backref=db.backref("service_sms_senders", uselist=True), ) is_default = db.Column(db.Boolean, nullable=False, default=True) archived = db.Column(db.Boolean, nullable=False, default=False) @@ -711,13 +765,18 @@ class ServiceSmsSender(db.Model): nullable=True, ) inbound_number = db.relationship( - InboundNumber, backref=db.backref("inbound_number", uselist=False) + InboundNumber, + backref=db.backref("inbound_number", uselist=False), ) created_at = db.Column( - db.DateTime, default=datetime.datetime.utcnow, nullable=False + db.DateTime, + default=datetime.datetime.utcnow, + nullable=False, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) def get_reply_to_text(self): @@ -749,17 +808,20 @@ class ServicePermission(db.Model): nullable=False, ) permission = db.Column( - db.Enum(ServicePermissionType, name="service_permission_types"), + db.Enum(ServicePermissionType, name="service_permission_type"), index=True, primary_key=True, nullable=False, ) created_at = db.Column( - db.DateTime, default=datetime.datetime.utcnow, nullable=False + db.DateTime, + default=datetime.datetime.utcnow, + nullable=False, ) service_permission_types = db.relationship( - Service, backref=db.backref("permissions", cascade="all, delete-orphan") + Service, + backref=db.backref("permissions", cascade="all, delete-orphan"), ) def __repr__(self): @@ -773,11 +835,15 @@ class ServiceGuestList(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + index=True, + nullable=False, ) service = db.relationship("Service", backref="guest_list") recipient_type = db.Column( - db.Enum(RecipientType, name="recipient_types"), nullable=False + db.Enum(RecipientType, name="recipient_type"), + nullable=False, ) recipient = db.Column(db.String(255), nullable=False) created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) @@ -820,12 +886,17 @@ class ServiceInboundApi(db.Model, Versioned): url = db.Column(db.String(), nullable=False) _bearer_token = db.Column("bearer_token", db.String(), nullable=False) created_at = db.Column( - db.DateTime, default=datetime.datetime.utcnow, nullable=False + db.DateTime, + default=datetime.datetime.utcnow, + nullable=False, ) updated_at = db.Column(db.DateTime, nullable=True) updated_by = db.relationship("User") updated_by_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("users.id"), + index=True, + nullable=False, ) @property @@ -852,26 +923,38 @@ class ServiceCallbackApi(db.Model, Versioned): __tablename__ = "service_callback_api" id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + index=True, + nullable=False, ) service = db.relationship("Service", backref="service_callback_api") url = db.Column(db.String(), nullable=False) callback_type = db.Column( - db.String(), db.ForeignKey("service_callback_type.name"), nullable=True + db.String(), + db.ForeignKey("service_callback_type.name"), + nullable=True, ) _bearer_token = db.Column("bearer_token", db.String(), nullable=False) created_at = db.Column( - db.DateTime, default=datetime.datetime.utcnow, nullable=False + db.DateTime, + default=datetime.datetime.utcnow, + nullable=False, ) updated_at = db.Column(db.DateTime, nullable=True) updated_by = db.relationship("User") updated_by_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("users.id"), + index=True, + nullable=False, ) __table_args__ = ( UniqueConstraint( - "service_id", "callback_type", name="uix_service_callback_type" + "service_id", + "callback_type", + name="uix_service_callback_type", ), ) @@ -908,11 +991,17 @@ class ApiKey(db.Model, Versioned): name = db.Column(db.String(255), nullable=False) _secret = db.Column("secret", db.String(255), unique=True, nullable=False) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + index=True, + nullable=False, ) service = db.relationship("Service", backref="api_keys") key_type = db.Column( - db.String(255), db.ForeignKey("key_types.name"), index=True, nullable=False + db.String(255), + db.ForeignKey("key_types.name"), + index=True, + nullable=False, ) expiry_date = db.Column(db.DateTime) created_at = db.Column( @@ -931,7 +1020,10 @@ class ApiKey(db.Model, Versioned): ) created_by = db.relationship("User") created_by_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("users.id"), + index=True, + nullable=False, ) __table_args__ = ( @@ -976,11 +1068,15 @@ class TemplateFolder(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), nullable=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + nullable=False, ) name = db.Column(db.String, nullable=False) parent_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("template_folder.id"), nullable=True + UUID(as_uuid=True), + db.ForeignKey("template_folder.id"), + nullable=True, ) service = db.relationship("Service", backref="all_template_folders") @@ -1054,10 +1150,13 @@ class TemplateBase(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = db.Column(db.String(255), nullable=False) template_type = db.Column( - db.Enum(TemplateType, name="template_type"), nullable=False + db.Enum(TemplateType, name="template_type"), + nullable=False, ) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column(db.DateTime, onupdate=datetime.datetime.utcnow) content = db.Column(db.Text, nullable=False) @@ -1203,10 +1302,15 @@ class TemplateRedacted(db.Model): ) redact_personalisation = db.Column(db.Boolean, nullable=False, default=False) updated_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_by_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=False, index=True + UUID(as_uuid=True), + db.ForeignKey("users.id"), + nullable=False, + index=True, ) updated_by = db.relationship("User") @@ -1258,15 +1362,21 @@ class ProviderDetails(db.Model): identifier = db.Column(db.String, nullable=False) priority = db.Column(db.Integer, nullable=False) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), nullable=False + db.Enum(NotificationType, name="notification_type"), + nullable=False, ) active = db.Column(db.Boolean, default=False, nullable=False) version = db.Column(db.Integer, default=1, nullable=False) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) created_by_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=True + UUID(as_uuid=True), + db.ForeignKey("users.id"), + index=True, + nullable=True, ) created_by = db.relationship("User") supports_international = db.Column(db.Boolean, nullable=False, default=False) @@ -1391,7 +1501,7 @@ class VerifyCode(db.Model): user = db.relationship("User", backref=db.backref("verify_codes", lazy="dynamic")) _code = db.Column(db.String, nullable=False) code_type = db.Column( - db.Enum(VerifyCodeType, name="verify_code_types"), + db.Enum(CodeType, name="code_type"), index=False, unique=False, nullable=False, @@ -1555,27 +1665,38 @@ class Notification(db.Model): to = db.Column(db.String, nullable=False) normalised_to = db.Column(db.String, nullable=True) job_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("jobs.id"), index=True, unique=False + UUID(as_uuid=True), + db.ForeignKey("jobs.id"), + index=True, + unique=False, ) job = db.relationship("Job", backref=db.backref("notifications", lazy="dynamic")) job_row_number = db.Column(db.Integer, nullable=True) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), unique=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + unique=False, ) service = db.relationship("Service") template_id = db.Column(UUID(as_uuid=True), index=True, unique=False) template_version = db.Column(db.Integer, nullable=False) template = db.relationship("TemplateHistory") api_key_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("api_keys.id"), unique=False + UUID(as_uuid=True), + db.ForeignKey("api_keys.id"), + unique=False, ) api_key = db.relationship("ApiKey") key_type = db.Column( - db.String, db.ForeignKey("key_types.name"), unique=False, nullable=False + db.String, + db.ForeignKey("key_types.name"), + unique=False, + nullable=False, ) billable_units = db.Column(db.Integer, nullable=False, default=0) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), nullable=False + db.Enum(NotificationType, name="notification_type"), + nullable=False, ) created_at = db.Column(db.DateTime, index=True, unique=False, nullable=False) sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True) @@ -1831,26 +1952,37 @@ class NotificationHistory(db.Model, HistoryModel): id = db.Column(UUID(as_uuid=True), primary_key=True) job_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("jobs.id"), index=True, unique=False + UUID(as_uuid=True), + db.ForeignKey("jobs.id"), + index=True, + unique=False, ) job = db.relationship("Job") job_row_number = db.Column(db.Integer, nullable=True) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), unique=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + unique=False, ) service = db.relationship("Service") template_id = db.Column(UUID(as_uuid=True), unique=False) template_version = db.Column(db.Integer, nullable=False) api_key_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("api_keys.id"), unique=False + UUID(as_uuid=True), + db.ForeignKey("api_keys.id"), + unique=False, ) api_key = db.relationship("ApiKey") key_type = db.Column( - db.String, db.ForeignKey("key_types.name"), unique=False, nullable=False + db.String, + db.ForeignKey("key_types.name"), + unique=False, + nullable=False, ) billable_units = db.Column(db.Integer, nullable=False, default=0) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), nullable=False + db.Enum(NotificationType, name="notification_type"), + nullable=False, ) created_at = db.Column(db.DateTime, unique=False, nullable=False) sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True) @@ -1912,11 +2044,17 @@ class InvitedUser(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) email_address = db.Column(db.String(255), nullable=False) user_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("users.id"), + index=True, + nullable=False, ) from_user = db.relationship("User") service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), index=True, unique=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + index=True, + unique=False, ) service = db.relationship("Service") created_at = db.Column( @@ -1927,9 +2065,9 @@ class InvitedUser(db.Model): default=datetime.datetime.utcnow, ) status = db.Column( - db.Enum(InvitedUserStatusType, name="invited_users_status_types"), + db.Enum(InvitedUserStatus, name="invited_user_status"), nullable=False, - default=InvitedUserStatusType.PENDING, + default=InvitedUserStatus.PENDING, ) permissions = db.Column(db.String, nullable=False) auth_type = db.Column( @@ -1953,21 +2091,27 @@ class InvitedOrganizationUser(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) email_address = db.Column(db.String(255), nullable=False) invited_by_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=False + UUID(as_uuid=True), + db.ForeignKey("users.id"), + nullable=False, ) invited_by = db.relationship("User") organization_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("organization.id"), nullable=False + UUID(as_uuid=True), + db.ForeignKey("organization.id"), + nullable=False, ) organization = db.relationship("Organization") created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) status = db.Column( - db.Enum(InvitedUserStatusType, name="invited_users_status_types"), + db.Enum(InvitedUserStatus, name="invited_users_status"), nullable=False, - default=InvitedUserStatusType.PENDING, + default=InvitedUserStatus.PENDING, ) def serialize(self): @@ -1995,11 +2139,14 @@ class Permission(db.Model): ) service = db.relationship("Service") user_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("users.id"), + index=True, + nullable=False, ) user = db.relationship("User") permission = db.Column( - db.Enum(ServicePermissionType, name="permission_types"), + db.Enum(PermissionType, name="permission_type"), index=False, unique=False, nullable=False, @@ -2014,7 +2161,10 @@ class Permission(db.Model): __table_args__ = ( UniqueConstraint( - "service_id", "user_id", "permission", name="uix_service_user_permission" + "service_id", + "user_id", + "permission", + name="uix_service_user_permission", ), ) @@ -2041,7 +2191,9 @@ class Rate(db.Model): valid_from = db.Column(db.DateTime, nullable=False) rate = db.Column(db.Float(asdecimal=False), nullable=False) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), index=True, nullable=False + db.Enum(NotificationType, name="notification_type"), + index=True, + nullable=False, ) def __str__(self): @@ -2056,18 +2208,26 @@ class InboundSms(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), index=True, nullable=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + index=True, + nullable=False, ) service = db.relationship("Service", backref="inbound_sms") notify_number = db.Column( - db.String, nullable=False + db.String, + nullable=False, ) # the service's number, that the msg was sent to user_number = db.Column( - db.String, nullable=False, index=True + db.String, + nullable=False, + index=True, ) # the end user's number, that the msg was sent from provider_date = db.Column(db.DateTime) provider_reference = db.Column(db.String) @@ -2098,7 +2258,10 @@ class InboundSmsHistory(db.Model, HistoryModel): id = db.Column(UUID(as_uuid=True), primary_key=True) created_at = db.Column(db.DateTime, index=True, unique=False, nullable=False) service_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("services.id"), index=True, unique=False + UUID(as_uuid=True), + db.ForeignKey("services.id"), + index=True, + unique=False, ) service = db.relationship("Service") notify_number = db.Column(db.String, nullable=False) @@ -2125,10 +2288,14 @@ class ServiceEmailReplyTo(db.Model): is_default = db.Column(db.Boolean, nullable=False, default=True) archived = db.Column(db.Boolean, nullable=False, default=False) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) def serialize(self): @@ -2154,10 +2321,16 @@ class FactBilling(db.Model): local_date = db.Column(db.Date, nullable=False, primary_key=True, index=True) template_id = db.Column( - UUID(as_uuid=True), nullable=False, primary_key=True, index=True + UUID(as_uuid=True), + nullable=False, + primary_key=True, + index=True, ) service_id = db.Column( - UUID(as_uuid=True), nullable=False, primary_key=True, index=True + UUID(as_uuid=True), + nullable=False, + primary_key=True, + index=True, ) notification_type = db.Column(db.Text, nullable=False, primary_key=True) provider = db.Column(db.Text, nullable=False, primary_key=True) @@ -2167,10 +2340,14 @@ class FactBilling(db.Model): billable_units = db.Column(db.Integer(), nullable=True) notifications_sent = db.Column(db.Integer(), nullable=True) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) @@ -2179,7 +2356,10 @@ class FactNotificationStatus(db.Model): local_date = db.Column(db.Date, index=True, primary_key=True, nullable=False) template_id = db.Column( - UUID(as_uuid=True), primary_key=True, index=True, nullable=False + UUID(as_uuid=True), + primary_key=True, + index=True, + nullable=False, ) service_id = db.Column( UUID(as_uuid=True), @@ -2193,10 +2373,14 @@ class FactNotificationStatus(db.Model): notification_status = db.Column(db.Text, primary_key=True, nullable=False) notification_count = db.Column(db.Integer(), nullable=False) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) @@ -2207,10 +2391,14 @@ class FactProcessingTime(db.Model): messages_total = db.Column(db.Integer(), nullable=False) messages_within_10_secs = db.Column(db.Integer(), nullable=False) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) @@ -2231,7 +2419,9 @@ class Complaint(db.Model): complaint_type = db.Column(db.Text, nullable=True) complaint_date = db.Column(db.DateTime, nullable=True) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) def serialize(self): @@ -2266,14 +2456,19 @@ class ServiceDataRetention(db.Model): ), ) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), nullable=False + db.Enum(NotificationType, name="notification_type"), + nullable=False, ) days_of_retention = db.Column(db.Integer, nullable=False) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) __table_args__ = ( @@ -2302,7 +2497,10 @@ class WebauthnCredential(db.Model): __tablename__ = "webauthn_credential" id = db.Column( - UUID(as_uuid=True), primary_key=True, nullable=False, default=uuid.uuid4 + UUID(as_uuid=True), + primary_key=True, + nullable=False, + default=uuid.uuid4, ) user_id = db.Column(UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=False) @@ -2317,10 +2515,14 @@ class WebauthnCredential(db.Model): registration_response = db.Column(db.String, nullable=False) created_at = db.Column( - db.DateTime, nullable=False, default=datetime.datetime.utcnow + db.DateTime, + nullable=False, + default=datetime.datetime.utcnow, ) updated_at = db.Column( - db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow + db.DateTime, + nullable=True, + onupdate=datetime.datetime.utcnow, ) def serialize(self): @@ -2337,17 +2539,20 @@ class WebauthnCredential(db.Model): class Agreement(db.Model): __tablename__ = "agreements" id = db.Column( - UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=False + UUID(as_uuid=True), + primary_key=True, + default=uuid.uuid4, + unique=False, ) type = db.Column( - db.Enum(AgreementType, name="agreement_types"), + db.Enum(AgreementType, name="agreement_type"), index=False, unique=False, nullable=False, ) partner_name = db.Column(db.String(255), nullable=False, unique=True, index=True) status = db.Column( - db.Enum(AgreementStatus, name="agreement_statuses"), + db.Enum(AgreementStatus, name="agreement_status"), index=False, unique=False, nullable=False, diff --git a/app/user/rest.py b/app/user/rest.py index 3ac1b1d18..8c9419d9b 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -32,7 +32,7 @@ from app.dao.users_dao import ( update_user_password, use_user_code, ) -from app.enums import NotificationType, TemplateType, VerifyCodeType +from app.enums import CodeType, NotificationType, TemplateType from app.errors import InvalidRequest, register_errors from app.models import KEY_TYPE_NORMAL, Permission, Service from app.notifications.process_notifications import ( @@ -221,7 +221,7 @@ def verify_user_code(user_id): user_to_verify.current_session_id = str(uuid.uuid4()) user_to_verify.logged_in_at = datetime.utcnow() - if data["code_type"] == VerifyCodeType.EMAIL: + if data["code_type"] == CodeType.EMAIL: user_to_verify.email_access_validated_at = datetime.utcnow() user_to_verify.failed_login_count = 0 save_model_user(user_to_verify) From 69a9accfcac413c6c6b61cc9241df6c62aa56b59 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 10:28:15 -0500 Subject: [PATCH 142/259] Getting NotificationStatus implemented everywhere. Signed-off-by: Cliff Hill --- app/celery/process_ses_receipts_tasks.py | 5 +- app/celery/provider_tasks.py | 19 +- app/commands.py | 5 +- app/dao/fact_billing_dao.py | 12 +- app/dao/fact_notification_status_dao.py | 30 +-- app/dao/notifications_dao.py | 38 ++-- app/dao/services_dao.py | 5 +- app/dao/uploads_dao.py | 5 +- app/delivery/send_to_providers.py | 11 +- app/enums.py | 99 ++++++++- app/models.py | 206 ++----------------- app/notifications/process_notifications.py | 6 +- app/service/statistics.py | 5 +- app/template/template_schemas.py | 7 +- app/v2/notifications/notification_schemas.py | 5 +- app/v2/notifications/post_notifications.py | 6 +- 16 files changed, 179 insertions(+), 285 deletions(-) diff --git a/app/celery/process_ses_receipts_tasks.py b/app/celery/process_ses_receipts_tasks.py index a89c6f67d..f88360668 100644 --- a/app/celery/process_ses_receipts_tasks.py +++ b/app/celery/process_ses_receipts_tasks.py @@ -20,7 +20,8 @@ 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 NOTIFICATION_PENDING, NOTIFICATION_SENDING, Complaint +from app.enums import NotificationStatus +from app.models import Complaint @notify_celery.task( @@ -76,7 +77,7 @@ def process_ses_results(self, response): f"SES bounce for notification ID {notification.id}: {bounce_message}" ) - if notification.status not in {NOTIFICATION_SENDING, NOTIFICATION_PENDING}: + if notification.status not in {NotificationStatus.SENDING, NotificationStatus.PENDING}: notifications_dao._duplicate_update_warning( notification, notification_status ) diff --git a/app/celery/provider_tasks.py b/app/celery/provider_tasks.py index 822b1f119..1f90b0024 100644 --- a/app/celery/provider_tasks.py +++ b/app/celery/provider_tasks.py @@ -15,13 +15,8 @@ from app.dao.notifications_dao import ( update_notification_status_by_id, ) from app.delivery import send_to_providers +from app.enum import NotificationStatus from app.exceptions import NotificationTechnicalFailureException -from app.models import ( - NOTIFICATION_DELIVERED, - NOTIFICATION_FAILED, - 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 # receipts @@ -67,12 +62,12 @@ def check_sms_delivery_receipt(self, message_id, notification_id, sent_at): raise self.retry(exc=ntfe) if status == "success": - status = NOTIFICATION_DELIVERED + status = NotificationStatus.DELIVERED elif status == "failure": - status = NOTIFICATION_FAILED + status = NotificationStatus.FAILED # if status is not success or failure the client raised an exception and this method will retry - if status == NOTIFICATION_DELIVERED: + if status == NotificationStatus.DELIVERED: sanitize_successful_notification_by_id( notification_id, carrier=carrier, provider_response=provider_response ) @@ -126,7 +121,7 @@ def deliver_sms(self, notification_id): ) except Exception as e: update_notification_status_by_id( - notification_id, NOTIFICATION_TEMPORARY_FAILURE + notification_id, NotificationStatus.TEMPORARY_FAILURE, ) if isinstance(e, SmsClientResponseException): current_app.logger.warning( @@ -151,7 +146,7 @@ def deliver_sms(self, notification_id): ) ) update_notification_status_by_id( - notification_id, NOTIFICATION_TECHNICAL_FAILURE + notification_id, NotificationStatus.TECHNICAL_FAILURE, ) raise NotificationTechnicalFailureException(message) @@ -194,6 +189,6 @@ def deliver_email(self, notification_id): ) ) update_notification_status_by_id( - notification_id, NOTIFICATION_TECHNICAL_FAILURE + notification_id, NotificationStatus.TECHNICAL_FAILURE, ) raise NotificationTechnicalFailureException(message) diff --git a/app/commands.py b/app/commands.py index 7541766af..1deec6dce 100644 --- a/app/commands.py +++ b/app/commands.py @@ -49,10 +49,9 @@ from app.dao.users_dao import ( delete_user_verify_codes, get_user_by_email, ) -from app.enums import NotificationType +from app.enums import NotificationType, NotificationStatus from app.models import ( KEY_TYPE_TEST, - NOTIFICATION_CREATED, AnnualBilling, Domain, EmailBranding, @@ -520,7 +519,7 @@ def populate_go_live(file_name): def fix_billable_units(): query = Notification.query.filter( Notification.notification_type == NotificationType.SMS, - Notification.status != NOTIFICATION_CREATED, + Notification.status != NotificationStatus.CREATED, Notification.sent_at == None, # noqa Notification.billable_units == 0, Notification.key_type != KEY_TYPE_TEST, diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index 696022619..4ef418374 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -8,12 +8,10 @@ from sqlalchemy.sql.expression import case, literal from app import db from app.dao.date_util import get_calendar_year_dates, get_calendar_year_for_datetime from app.dao.organization_dao import dao_get_organization_live_services -from app.enums import NotificationType +from app.enums import NotificationStatus, NotificationType from app.models import ( KEY_TYPE_NORMAL, KEY_TYPE_TEAM, - NOTIFICATION_STATUS_TYPES_BILLABLE_SMS, - NOTIFICATION_STATUS_TYPES_SENT_EMAILS, AnnualBilling, FactBilling, NotificationAllTimeView, @@ -412,9 +410,7 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): func.count().label("notifications_sent"), ) .filter( - NotificationAllTimeView.status.in_( - NOTIFICATION_STATUS_TYPES_SENT_EMAILS - ), + NotificationAllTimeView.status.in_(NotificationStatus.sent_emails), NotificationAllTimeView.key_type.in_((KEY_TYPE_NORMAL, KEY_TYPE_TEAM)), NotificationAllTimeView.created_at >= start_date, NotificationAllTimeView.created_at < end_date, @@ -446,9 +442,7 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): func.count().label("notifications_sent"), ) .filter( - NotificationAllTimeView.status.in_( - NOTIFICATION_STATUS_TYPES_BILLABLE_SMS - ), + NotificationAllTimeView.status.in_(NotificationStatus.billable_sms), NotificationAllTimeView.key_type.in_((KEY_TYPE_NORMAL, KEY_TYPE_TEAM)), NotificationAllTimeView.created_at >= start_date, NotificationAllTimeView.created_at < end_date, diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index c6c6b06dd..13843f8d9 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -7,21 +7,11 @@ from sqlalchemy.types import DateTime, Integer from app import db from app.dao.dao_utils import autocommit -from app.enums import NotificationType +from app.enums import NotificationType, NotificationStatus from app.models import ( KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - NOTIFICATION_CANCELLED, - NOTIFICATION_CREATED, - NOTIFICATION_DELIVERED, - NOTIFICATION_FAILED, - NOTIFICATION_PENDING, - NOTIFICATION_PERMANENT_FAILURE, - NOTIFICATION_SENDING, - NOTIFICATION_SENT, - NOTIFICATION_TECHNICAL_FAILURE, - NOTIFICATION_TEMPORARY_FAILURE, FactNotificationStatus, Notification, NotificationAllTimeView, @@ -386,7 +376,7 @@ def fetch_monthly_template_usage_for_service(start_date, end_date, service_id): FactNotificationStatus.local_date >= start_date, FactNotificationStatus.local_date <= end_date, FactNotificationStatus.key_type != KEY_TYPE_TEST, - FactNotificationStatus.notification_status != NOTIFICATION_CANCELLED, + FactNotificationStatus.notification_status != NotificationStatus.CANCELLED, ) .group_by( FactNotificationStatus.template_id, @@ -423,7 +413,7 @@ def fetch_monthly_template_usage_for_service(start_date, end_date, service_id): Notification.created_at >= today, Notification.service_id == service_id, Notification.key_type != KEY_TYPE_TEST, - Notification.status != NOTIFICATION_CANCELLED, + Notification.status != NotificationStatus.CANCELLED, ) .group_by( Notification.template_id, @@ -517,7 +507,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): [ ( FactNotificationStatus.notification_status.in_( - [NOTIFICATION_SENDING, NOTIFICATION_PENDING] + [NotificationStatus.SENDING, NotificationStatus.PENDING] ), FactNotificationStatus.notification_count, ) @@ -530,7 +520,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): [ ( FactNotificationStatus.notification_status - == NOTIFICATION_DELIVERED, + == NotificationStatus.DELIVERED, FactNotificationStatus.notification_count, ) ], @@ -542,7 +532,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): [ ( FactNotificationStatus.notification_status.in_( - [NOTIFICATION_TECHNICAL_FAILURE, NOTIFICATION_FAILED] + [NotificationStatus.TECHNICAL_FAILURE, NotificationStatus.FAILED] ), FactNotificationStatus.notification_count, ) @@ -555,7 +545,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): [ ( FactNotificationStatus.notification_status - == NOTIFICATION_TEMPORARY_FAILURE, + == NotificationStatus.TEMPORARY_FAILURE, FactNotificationStatus.notification_count, ) ], @@ -567,7 +557,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): [ ( FactNotificationStatus.notification_status - == NOTIFICATION_PERMANENT_FAILURE, + == NotificationStatus.PERMANENT_FAILURE, FactNotificationStatus.notification_count, ) ], @@ -579,7 +569,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): [ ( FactNotificationStatus.notification_status - == NOTIFICATION_SENT, + == NotificationStatus.SENT, FactNotificationStatus.notification_count, ) ], @@ -589,7 +579,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): ) .join(Service, FactNotificationStatus.service_id == Service.id) .filter( - FactNotificationStatus.notification_status != NOTIFICATION_CREATED, + FactNotificationStatus.notification_status != NotificationStatus.CREATED, Service.active.is_(True), FactNotificationStatus.key_type != KEY_TYPE_TEST, Service.restricted.is_(False), diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index a144ee7cc..2293b6c2e 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -16,17 +16,9 @@ from werkzeug.datastructures import MultiDict from app import create_uuid, db from app.dao.dao_utils import autocommit -from app.enums import NotificationType +from app.enums import NotificationType, NotificationStatus from app.models import ( KEY_TYPE_TEST, - NOTIFICATION_CREATED, - NOTIFICATION_FAILED, - NOTIFICATION_PENDING, - NOTIFICATION_PENDING_VIRUS_CHECK, - NOTIFICATION_PERMANENT_FAILURE, - NOTIFICATION_SENDING, - NOTIFICATION_SENT, - NOTIFICATION_TEMPORARY_FAILURE, FactNotificationStatus, Notification, NotificationHistory, @@ -70,7 +62,7 @@ def dao_create_notification(notification): # need to populate defaulted fields before we create the notification history object notification.id = create_uuid() if not notification.status: - notification.status = NOTIFICATION_CREATED + notification.status = NotificationStatus.CREATED # notify-api-742 remove phone numbers from db notification.to = "1" notification.normalised_to = "1" @@ -85,10 +77,10 @@ def country_records_delivery(phone_prefix): def _decide_permanent_temporary_failure(current_status, status): # If we go from pending to delivered we need to set failure type as temporary-failure if ( - current_status == NOTIFICATION_PENDING - and status == NOTIFICATION_PERMANENT_FAILURE + current_status == NotificationStatus.PENDING + and status == NotificationStatus.PERMANENT_FAILURE ): - status = NOTIFICATION_TEMPORARY_FAILURE + status = NotificationStatus.TEMPORARY_FAILURE return status @@ -127,11 +119,11 @@ def update_notification_status_by_id( return None if notification.status not in { - NOTIFICATION_CREATED, - NOTIFICATION_SENDING, - NOTIFICATION_PENDING, - NOTIFICATION_SENT, - NOTIFICATION_PENDING_VIRUS_CHECK, + NotificationStatus.CREATED, + NotificationStatus.SENDING, + NotificationStatus.PENDING, + NotificationStatus.SENT, + NotificationStatus.PENDING_VIRUS_CHECK, }: _duplicate_update_warning(notification, status) return None @@ -171,7 +163,7 @@ def update_notification_status_by_reference(reference, status): ) return None - if notification.status not in {NOTIFICATION_SENDING, NOTIFICATION_PENDING}: + if notification.status not in {NotificationStatus.SENDING, NotificationStatus.PENDING}: _duplicate_update_warning(notification, status) return None @@ -209,7 +201,7 @@ def dao_get_notification_count_for_service(*, service_id): def dao_get_failed_notification_count(): - failed_count = Notification.query.filter_by(status=NOTIFICATION_FAILED).count() + failed_count = Notification.query.filter_by(status=NotificationStatus.FAILED).count() return failed_count @@ -437,8 +429,8 @@ def dao_timeout_notifications(cutoff_time, limit=100000): if they're still sending from before the specified cutoff_time. """ updated_at = datetime.utcnow() - current_statuses = [NOTIFICATION_SENDING, NOTIFICATION_PENDING] - new_status = NOTIFICATION_TEMPORARY_FAILURE + current_statuses = [NotificationStatus.SENDING, NotificationStatus.PENDING] + new_status = NotificationStatus.TEMPORARY_FAILURE notifications = ( Notification.query.filter( @@ -608,7 +600,7 @@ def notifications_not_yet_sent(should_be_sending_after_seconds, notification_typ notifications = Notification.query.filter( Notification.created_at <= older_than_date, Notification.notification_type == notification_type, - Notification.status == NOTIFICATION_CREATED, + Notification.status == NotificationStatus.CREATED, ).all() return notifications diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 9da8b567d..d0a4b4580 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -13,10 +13,9 @@ from app.dao.organization_dao import dao_get_organization_by_email_address from app.dao.service_sms_sender_dao import insert_service_sms_sender from app.dao.service_user_dao import dao_get_service_user from app.dao.template_folder_dao import dao_get_valid_template_folders_by_id -from app.enums import NotificationType, ServicePermissionType +from app.enums import NotificationStatus, NotificationType, ServicePermissionType from app.models import ( KEY_TYPE_TEST, - NOTIFICATION_PERMANENT_FAILURE, AnnualBilling, ApiKey, FactBilling, @@ -565,7 +564,7 @@ def dao_find_services_with_high_failure_rates(start_date, end_date, threshold=10 Notification.created_at <= end_date, Notification.key_type != KEY_TYPE_TEST, Notification.notification_type == NotificationType.SMS, - Notification.status == NOTIFICATION_PERMANENT_FAILURE, + Notification.status == NotificationStatus.PERMANENT_FAILURE, Service.restricted == False, # noqa Service.active == True, # noqa ) diff --git a/app/dao/uploads_dao.py b/app/dao/uploads_dao.py index 1f1c5138b..d29421e09 100644 --- a/app/dao/uploads_dao.py +++ b/app/dao/uploads_dao.py @@ -5,11 +5,10 @@ from flask import current_app from sqlalchemy import String, and_, desc, func, literal, text from app import db -from app.enums import NotificationType +from app.enums import NotificationStatus, NotificationType from app.models import ( JOB_STATUS_CANCELLED, JOB_STATUS_SCHEDULED, - NOTIFICATION_CANCELLED, Job, Notification, ServiceDataRetention, @@ -92,7 +91,7 @@ def dao_get_uploads_by_service_id(service_id, limit_days=None, page=1, page_size Notification.service_id == service_id, Notification.notification_type == NotificationType.LETTER, Notification.api_key_id == None, # noqa - Notification.status != NOTIFICATION_CANCELLED, + Notification.status != NotificationStatus.CANCELLED, Template.hidden == True, # noqa Notification.created_at >= today - func.coalesce(ServiceDataRetention.days_of_retention, 7), diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index be9d295da..439c238b6 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -15,15 +15,12 @@ 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.enums import NotificationType +from app.enums import NotificationStatus, NotificationType from app.exceptions import NotificationTechnicalFailureException from app.models import ( BRANDING_BOTH, BRANDING_ORG_BANNER, KEY_TYPE_TEST, - NOTIFICATION_SENDING, - NOTIFICATION_STATUS_TYPES_COMPLETED, - NOTIFICATION_TECHNICAL_FAILURE, ) from app.serialised_models import SerialisedService, SerialisedTemplate @@ -163,8 +160,8 @@ def send_email_to_provider(notification): def update_notification_to_sending(notification, provider): notification.sent_at = datetime.utcnow() notification.sent_by = provider.name - if notification.status not in NOTIFICATION_STATUS_TYPES_COMPLETED: - notification.status = NOTIFICATION_SENDING + if notification.status not in NotificationStatus.completed: + notification.status = NotificationStatus.SENDING dao_update_notification(notification) @@ -245,7 +242,7 @@ def get_html_email_options(service): def technical_failure(notification): - notification.status = NOTIFICATION_TECHNICAL_FAILURE + notification.status = NotificationStatus.TECHNICAL_FAILURE dao_update_notification(notification) raise NotificationTechnicalFailureException( "Send {} for notification id {} to provider is not allowed: service {} is inactive".format( diff --git a/app/enums.py b/app/enums.py index ade2856d3..aeb865cd1 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,4 +1,6 @@ from enum import Enum +from functools import lru_cache +from xml.sax.handler import property_interning_dict class TemplateType(Enum): @@ -19,17 +21,108 @@ class TemplateProcessType(Enum): PRIORITY = "priority" -class UserAuthType(Enum): +class AuthType(Enum): SMS = "sms_auth" EMAIL = "email_auth" WEBAUTHN = "webauthn_auth" -class ServiceCallbackType(Enum): +class CallbackType(Enum): DELIVERY_STATUS = "delivery_status" COMPLAINT = "complaint" +class OrganizationType(Enum): + FEDERAL = "federal" + STATE = "state" + OTHER = "other" + + +class NotificationStatus(Enum): + CANCELLED = "cancelled" + CREATED = "created" + SENDING = "sending" + SENT = "sent" + DELIVERED = "delivered" + PENDING = "pending" + FAILED = "failed" + TECHNICAL_FAILURE = "technical-failure" + TEMPORARY_FAILURE = "temporary-failure" + PERMANENT_FAILURE = "permanent-failure" + PENDING_VIRUS_CHECK = "pending-virus-check" + VALIDATION_FAILED = "validation-failed" + VIRUS_SCAN_FAILED = "virus-scan-failed" + + @property + def failed(self) -> tuple["NotificationStatus", ...]: + cls = type(self) + return ( + cls.TECHNICAL_FAILURE, + cls.TEMPORARY_FAILURE, + cls.PERMANENT_FAILURE, + cls.VALIDATION_FAILED, + cls.VIRUS_SCAN_FAILED, + ) + + @property + def completed(self) -> tuple["NotificationStatus", ...]: + cls = type(self) + return ( + cls.SENT, + cls.DELIVERED, + cls.FAILED, + cls.TECHNICAL_FAILURE, + cls.TEMPORARY_FAILURE, + cls.PERMANENT_FAILURE, + cls.CANCELLED, + ) + + @property + def success(self) -> tuple["NotificationStatus", ...]: + cls = type(self) + return (cls.SENT, cls.DELIVERED) + + @property + def billable(self) -> tuple["NotificationStatus", ...]: + cls = type(self) + return ( + cls.SENDING, + cls.SENT, + cls.DELIVERED, + cls.PENDING, + cls.FAILED, + cls.TEMPORARY_FAILURE, + cls.PERMANENT_FAILURE, + ) + + @property + def billable_sms(self) -> tuple["NotificationStatus", ...]: + cls = type(self) + return ( + cls.SENDING, + cls.SENT, # internationally + cls.DELIVERED, + cls.PENDING, + cls.TEMPORARY_FAILURE, + cls.PERMANENT_FAILURE, + ) + + @property + def sent_emails(self) -> tuple["NotificationStatus", ...]: + cls = type(self) + return ( + cls.SENDING, + cls.DELIVERED, + cls.TEMPORARY_FAILURE, + cls.PERMANENT_FAILURE, + ) + + @property + @lru_cache + def non_billable(self) -> tuple["NotificationStatus", ...]: + return tuple(set(type(self)) - set(self.billable)) + + class PermissionType(Enum): MANAGE_USERS = "manage_users" MANAGE_TEMPLATES = "manage_templates" @@ -104,7 +197,7 @@ class InvitedUserStatus(Enum): EXPIRED = "expired" -class BrandingType(Enum): +class BrandType(Enum): # TODO: Should EmailBranding.branding_type be changed to use this? GOVUK = "govuk" # Deprecated outside migrations ORG = "org" diff --git a/app/models.py b/app/models.py index e62ab3ca7..770b5f752 100644 --- a/app/models.py +++ b/app/models.py @@ -20,15 +20,22 @@ from sqlalchemy.orm import validates from sqlalchemy.orm.collections import attribute_mapped_collection from app import db, encryption -from app.enums import ( # JobStatusType,; KeyType,; UserAuthType,; TemplateProcessType, +from app.enums import ( AgreementStatus, AgreementType, + AuthType, + BrandType, + CallbackType, CodeType, InvitedUserStatus, + KeyType, + NotificationStatus, NotificationType, + OrganizationType, PermissionType, RecipientType, ServicePermissionType, + TemplateProcessType, TemplateType, ) from app.hashing import check_hash, hashpw @@ -39,25 +46,6 @@ from app.utils import ( get_dt_string_or_none, ) -# TODO: Change this -NORMAL = "normal" -PRIORITY = "priority" -TEMPLATE_PROCESS_TYPE = [NORMAL, PRIORITY] - - -# TODO: Change this -SMS_AUTH_TYPE = "sms_auth" -EMAIL_AUTH_TYPE = "email_auth" -WEBAUTHN_AUTH_TYPE = "webauthn_auth" -USER_AUTH_TYPES = [SMS_AUTH_TYPE, EMAIL_AUTH_TYPE, WEBAUTHN_AUTH_TYPE] - - -# TODO: Change this -DELIVERY_STATUS_CALLBACK_TYPE = "delivery_status" -COMPLAINT_CALLBACK_TYPE = "complaint" -SERVICE_CALLBACK_TYPES = [DELIVERY_STATUS_CALLBACK_TYPE, COMPLAINT_CALLBACK_TYPE] -# Need to import ServiceCallbackType from app.enums - def filter_null_value_fields(obj): return dict(filter(lambda x: x[1] is not None, obj.items())) @@ -117,11 +105,10 @@ class User(db.Model): platform_admin = db.Column(db.Boolean, nullable=False, default=False) current_session_id = db.Column(UUID(as_uuid=True), nullable=True) auth_type = db.Column( - db.String, - db.ForeignKey("auth_type.name"), + db.Enum(AuthType, name="auth_type"), index=True, nullable=False, - default=SMS_AUTH_TYPE, + default=AuthType.SMS, ) email_access_validated_at = db.Column( db.DateTime, @@ -282,19 +269,6 @@ user_folder_permissions = db.Table( ) -# TODO: Change this -BRANDING_GOVUK = "govuk" # Deprecated outside migrations -BRANDING_ORG = "org" -BRANDING_BOTH = "both" -BRANDING_ORG_BANNER = "org_banner" -BRANDING_TYPES = [BRANDING_ORG, BRANDING_BOTH, BRANDING_ORG_BANNER] - - -class BrandingTypes(db.Model): - __tablename__ = "branding_type" - name = db.Column(db.String(255), primary_key=True) - - class EmailBranding(db.Model): __tablename__ = "email_branding" id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) @@ -303,11 +277,10 @@ class EmailBranding(db.Model): name = db.Column(db.String(255), unique=True, nullable=False) text = db.Column(db.String(255), nullable=True) brand_type = db.Column( - db.String(255), - db.ForeignKey("branding_type.name"), + db.Enum(BrandType, name="brand_type"), index=True, nullable=False, - default=BRANDING_ORG, + default=BrandType.ORG, ) def serialize(self): @@ -354,17 +327,6 @@ class Domain(db.Model): ) -# TODO: Change this -ORGANIZATION_TYPES = ["federal", "state", "other"] - - -class OrganizationTypes(db.Model): - __tablename__ = "organization_types" - - name = db.Column(db.String(255), primary_key=True) - annual_free_sms_fragment_limit = db.Column(db.BigInteger, nullable=False) - - class Organization(db.Model): __tablename__ = "organization" id = db.Column( @@ -396,8 +358,7 @@ class Organization(db.Model): ) agreement_signed_version = db.Column(db.Float, nullable=True) organization_type = db.Column( - db.String(255), - db.ForeignKey("organization_types.name"), + db.Enum(OrganizationType, name="organization_type"), unique=False, nullable=True, ) @@ -931,8 +892,7 @@ class ServiceCallbackApi(db.Model, Versioned): service = db.relationship("Service", backref="service_callback_api") url = db.Column(db.String(), nullable=False) callback_type = db.Column( - db.String(), - db.ForeignKey("service_callback_type.name"), + db.Enum(CallbackType, name="callback_type"), nullable=True, ) _bearer_token = db.Column("bearer_token", db.String(), nullable=False) @@ -978,12 +938,6 @@ class ServiceCallbackApi(db.Model, Versioned): } -class ServiceCallbackType(db.Model): - __tablename__ = "service_callback_type" - - name = db.Column(db.String, primary_key=True) - - class ApiKey(db.Model, Versioned): __tablename__ = "api_keys" @@ -1046,18 +1000,6 @@ class ApiKey(db.Model, Versioned): self._secret = encryption.encrypt(str(secret)) -# TODO: This needs to be changed -KEY_TYPE_NORMAL = "normal" -KEY_TYPE_TEAM = "team" -KEY_TYPE_TEST = "test" - - -class KeyTypes(db.Model): - __tablename__ = "key_types" - - name = db.Column(db.String(255), primary_key=True) - - class TemplateProcessTypes(db.Model): __tablename__ = "template_process_type" name = db.Column(db.String(255), primary_key=True) @@ -1183,8 +1125,7 @@ class TemplateBase(db.Model): @declared_attr def process_type(cls): return db.Column( - db.String(255), - db.ForeignKey("template_process_type.name"), + db.Enum(TemplateProcessType, name="template_process_type"), index=True, nullable=False, default=NORMAL, @@ -1528,99 +1469,6 @@ class VerifyCode(db.Model): return check_hash(cde, self._code) -NOTIFICATION_CANCELLED = "cancelled" -NOTIFICATION_CREATED = "created" -NOTIFICATION_SENDING = "sending" -NOTIFICATION_SENT = "sent" -NOTIFICATION_DELIVERED = "delivered" -NOTIFICATION_PENDING = "pending" -NOTIFICATION_FAILED = "failed" -NOTIFICATION_TECHNICAL_FAILURE = "technical-failure" -NOTIFICATION_TEMPORARY_FAILURE = "temporary-failure" -NOTIFICATION_PERMANENT_FAILURE = "permanent-failure" -NOTIFICATION_PENDING_VIRUS_CHECK = "pending-virus-check" -NOTIFICATION_VALIDATION_FAILED = "validation-failed" -NOTIFICATION_VIRUS_SCAN_FAILED = "virus-scan-failed" - -NOTIFICATION_STATUS_TYPES_FAILED = [ - NOTIFICATION_TECHNICAL_FAILURE, - NOTIFICATION_TEMPORARY_FAILURE, - NOTIFICATION_PERMANENT_FAILURE, - NOTIFICATION_VALIDATION_FAILED, - NOTIFICATION_VIRUS_SCAN_FAILED, -] - -NOTIFICATION_STATUS_TYPES_COMPLETED = [ - NOTIFICATION_SENT, - NOTIFICATION_DELIVERED, - NOTIFICATION_FAILED, - NOTIFICATION_TECHNICAL_FAILURE, - NOTIFICATION_TEMPORARY_FAILURE, - NOTIFICATION_PERMANENT_FAILURE, - NOTIFICATION_CANCELLED, -] - -NOTIFICATION_STATUS_SUCCESS = [NOTIFICATION_SENT, NOTIFICATION_DELIVERED] - -NOTIFICATION_STATUS_TYPES_BILLABLE = [ - NOTIFICATION_SENDING, - NOTIFICATION_SENT, - NOTIFICATION_DELIVERED, - NOTIFICATION_PENDING, - NOTIFICATION_FAILED, - NOTIFICATION_TEMPORARY_FAILURE, - NOTIFICATION_PERMANENT_FAILURE, -] - -NOTIFICATION_STATUS_TYPES_BILLABLE_SMS = [ - NOTIFICATION_SENDING, - NOTIFICATION_SENT, # internationally - NOTIFICATION_DELIVERED, - NOTIFICATION_PENDING, - NOTIFICATION_TEMPORARY_FAILURE, - NOTIFICATION_PERMANENT_FAILURE, -] - -# we don't really have a concept of billable emails - however the ft billing table only includes emails that we have -# actually sent. -NOTIFICATION_STATUS_TYPES_SENT_EMAILS = [ - NOTIFICATION_SENDING, - NOTIFICATION_DELIVERED, - NOTIFICATION_TEMPORARY_FAILURE, - NOTIFICATION_PERMANENT_FAILURE, -] - -NOTIFICATION_STATUS_TYPES = [ - NOTIFICATION_CANCELLED, - NOTIFICATION_CREATED, - NOTIFICATION_SENDING, - NOTIFICATION_SENT, - NOTIFICATION_DELIVERED, - NOTIFICATION_PENDING, - NOTIFICATION_FAILED, - NOTIFICATION_TECHNICAL_FAILURE, - NOTIFICATION_TEMPORARY_FAILURE, - NOTIFICATION_PERMANENT_FAILURE, - NOTIFICATION_PENDING_VIRUS_CHECK, - NOTIFICATION_VALIDATION_FAILED, - NOTIFICATION_VIRUS_SCAN_FAILED, -] - -NOTIFICATION_STATUS_TYPES_NON_BILLABLE = list( - set(NOTIFICATION_STATUS_TYPES) - set(NOTIFICATION_STATUS_TYPES_BILLABLE) -) - -NOTIFICATION_STATUS_TYPES_ENUM = db.Enum( - *NOTIFICATION_STATUS_TYPES, name="notify_status_type" -) - - -class NotificationStatusTypes(db.Model): - __tablename__ = "notification_status_types" - - name = db.Column(db.String(), primary_key=True) - - class NotificationAllTimeView(db.Model): """ WARNING: this view is a union of rows in "notifications" and @@ -1688,8 +1536,7 @@ class Notification(db.Model): ) api_key = db.relationship("ApiKey") key_type = db.Column( - db.String, - db.ForeignKey("key_types.name"), + db.Enum(KeyType, name="key_type"), unique=False, nullable=False, ) @@ -1709,9 +1556,7 @@ class Notification(db.Model): onupdate=datetime.datetime.utcnow, ) status = db.Column( - "notification_status", - db.Text, - db.ForeignKey("notification_status_types.name"), + db.Enum(NotificationStatus, name="notify_status_type"), nullable=True, default="created", key="status", # http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Column @@ -1778,7 +1623,7 @@ class Notification(db.Model): self._personalisation = encryption.encrypt(personalisation or {}) def completed_at(self): - if self.status in NOTIFICATION_STATUS_TYPES_COMPLETED: + if self.status in NotificationStatus.completed: return self.updated_at.strftime(DATETIME_FORMAT) return None @@ -1818,8 +1663,8 @@ class Notification(db.Model): def _substitute_status_str(_status): return ( - NOTIFICATION_STATUS_TYPES_FAILED - if _status == NOTIFICATION_FAILED + NotificationStatus.failed + if _status == NotificationStatus.FAILED else [_status] ) @@ -2071,11 +1916,10 @@ class InvitedUser(db.Model): ) permissions = db.Column(db.String, nullable=False) auth_type = db.Column( - db.String, - db.ForeignKey("auth_type.name"), + db.Enum(AuthType, name="auth_type"), index=True, nullable=False, - default=SMS_AUTH_TYPE, + default=AuthType.SMS, ) folder_permissions = db.Column(JSONB(none_as_null=True), nullable=False, default=[]) @@ -2310,12 +2154,6 @@ class ServiceEmailReplyTo(db.Model): } -class AuthType(db.Model): - __tablename__ = "auth_type" - - name = db.Column(db.String, primary_key=True) - - class FactBilling(db.Model): __tablename__ = "ft_billing" diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index b9474508f..aa8c2166b 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -16,8 +16,8 @@ from app.dao.notifications_dao import ( dao_create_notification, dao_delete_notifications_by_id, ) -from app.enums import NotificationType -from app.models import KEY_TYPE_TEST, NOTIFICATION_CREATED, Notification +from app.enums import NotificationType, NotificationStatus +from app.models import KEY_TYPE_TEST, Notification from app.v2.errors import BadRequestError @@ -71,7 +71,7 @@ def persist_notification( notification_id=None, simulated=False, created_by_id=None, - status=NOTIFICATION_CREATED, + status=NotificationStatus.CREATED, reply_to_text=None, billable_units=None, document_download_count=None, diff --git a/app/service/statistics.py b/app/service/statistics.py index 2c569de09..fb22982d8 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -2,8 +2,7 @@ from collections import defaultdict from datetime import datetime from app.dao.date_util import get_months_for_financial_year -from app.enums import TemplateType -from app.models import NOTIFICATION_STATUS_TYPES +from app.enums import TemplateType, NotificationStatus def format_statistics(statistics): @@ -69,7 +68,7 @@ def format_monthly_template_notification_stats(year, rows): stats[formatted_month][str(row.template_id)] = { "name": row.name, "type": row.template_type, - "counts": dict.fromkeys(NOTIFICATION_STATUS_TYPES, 0), + "counts": dict.fromkeys([e.value for e in NotificationStatus], 0), } stats[formatted_month][str(row.template_id)]["counts"][row.status] += row.count diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py index 6ac11c781..944d35676 100644 --- a/app/template/template_schemas.py +++ b/app/template/template_schemas.py @@ -1,5 +1,4 @@ -from app.enums import TemplateType -from app.models import TEMPLATE_PROCESS_TYPE +from app.enums import TemplateType, TemplateProcessType from app.schema_validation.definitions import nullable_uuid, uuid post_create_template_schema = { @@ -11,7 +10,7 @@ post_create_template_schema = { "name": {"type": "string"}, "template_type": {"enum": [e.value for e in TemplateType]}, "service": uuid, - "process_type": {"enum": TEMPLATE_PROCESS_TYPE}, + "process_type": {"enum": [e.value for e in TemplateProcessType]}, "content": {"type": "string"}, "subject": {"type": "string"}, "created_by": uuid, @@ -32,7 +31,7 @@ post_update_template_schema = { "name": {"type": "string"}, "template_type": {"enum": [e.value for e in TemplateType]}, "service": uuid, - "process_type": {"enum": TEMPLATE_PROCESS_TYPE}, + "process_type": {"enum": [e.value for e in TemplateProcessType]}, "content": {"type": "string"}, "subject": {"type": "string"}, "reply_to": nullable_uuid, diff --git a/app/v2/notifications/notification_schemas.py b/app/v2/notifications/notification_schemas.py index 83419da93..eb0e22eb3 100644 --- a/app/v2/notifications/notification_schemas.py +++ b/app/v2/notifications/notification_schemas.py @@ -1,5 +1,4 @@ -from app.enums import TemplateType -from app.models import NOTIFICATION_STATUS_TYPES +from app.enums import TemplateType, NotificationStatus from app.schema_validation.definitions import personalisation, uuid template = { @@ -81,7 +80,7 @@ get_notifications_request = { "type": "object", "properties": { "reference": {"type": "string"}, - "status": {"type": "array", "items": {"enum": NOTIFICATION_STATUS_TYPES}}, + "status": {"type": "array", "items": {"enum": [e.value for e in NotificationStatus]}}, "template_type": { "type": "array", "items": {"enum": [e.value for e in TemplateType]}, diff --git a/app/v2/notifications/post_notifications.py b/app/v2/notifications/post_notifications.py index 0226eb911..a1caa03b0 100644 --- a/app/v2/notifications/post_notifications.py +++ b/app/v2/notifications/post_notifications.py @@ -10,8 +10,8 @@ from app import api_user, authenticated_service, document_download_client, encry from app.celery.tasks import save_api_email, save_api_sms from app.clients.document_download import DocumentDownloadError from app.config import QueueNames -from app.enums import NotificationType -from app.models import KEY_TYPE_NORMAL, NOTIFICATION_CREATED, PRIORITY, Notification +from app.enums import NotificationType, NotificationStatus +from app.models import KEY_TYPE_NORMAL, PRIORITY, Notification from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue_detached, @@ -219,7 +219,7 @@ def save_email_or_sms_to_queue( "client_reference": form.get("reference", None), "reply_to_text": reply_to_text, "document_download_count": document_download_count, - "status": NOTIFICATION_CREATED, + "status": NotificationStatus.CREATED, "created_at": datetime.utcnow().strftime(DATETIME_FORMAT), } encrypted = encryption.encrypt(data) From db3761609be8595f9affb2886e2d5538362d7535 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Mon, 15 Jan 2024 14:22:56 -0500 Subject: [PATCH 143/259] Job Status changed. Signed-off-by: Cliff Hill --- app/celery/scheduled_tasks.py | 10 +++++----- app/celery/tasks.py | 20 +++++++------------- app/dao/jobs_dao.py | 14 ++++++-------- app/dao/uploads_dao.py | 6 ++---- app/enums.py | 2 +- app/job/rest.py | 8 ++++---- app/models.py | 31 ++----------------------------- 7 files changed, 27 insertions(+), 64 deletions(-) diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index fb0f39caa..65be85a03 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -34,8 +34,8 @@ from app.dao.services_dao import ( ) from app.dao.users_dao import delete_codes_older_created_more_than_a_day_ago from app.delivery.send_to_providers import provider_to_use -from app.enums import NotificationType -from app.models import JOB_STATUS_ERROR, JOB_STATUS_IN_PROGRESS, JOB_STATUS_PENDING, Job +from app.enums import NotificationType, JobStatus +from app.models import Job from app.notifications.process_notifications import send_notification_to_queue MAX_NOTIFICATION_FAILS = 10000 @@ -186,11 +186,11 @@ def check_job_status(): thirty_five_minutes_ago = datetime.utcnow() - timedelta(minutes=35) incomplete_in_progress_jobs = Job.query.filter( - Job.job_status == JOB_STATUS_IN_PROGRESS, + Job.job_status == JobStatus.IN_PROGRESS, between(Job.processing_started, thirty_five_minutes_ago, thirty_minutes_ago), ) incomplete_pending_jobs = Job.query.filter( - Job.job_status == JOB_STATUS_PENDING, + Job.job_status == JobStatus.PENDING, Job.scheduled_for.isnot(None), between(Job.scheduled_for, thirty_five_minutes_ago, thirty_minutes_ago), ) @@ -205,7 +205,7 @@ def check_job_status(): # if they haven't been re-processed in time. job_ids = [] for job in jobs_not_complete_after_30_minutes: - job.job_status = JOB_STATUS_ERROR + job.job_status = JobStatus.ERROR dao_update_job(job) job_ids.append(str(job.id)) diff --git a/app/celery/tasks.py b/app/celery/tasks.py index 34b1f13e0..17b13ffc1 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -20,14 +20,8 @@ from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_inbound_api_dao import get_service_inbound_api_for_service from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.templates_dao import dao_get_template_by_id -from app.enums import NotificationType -from app.models import ( - JOB_STATUS_CANCELLED, - JOB_STATUS_FINISHED, - JOB_STATUS_IN_PROGRESS, - JOB_STATUS_PENDING, - KEY_TYPE_NORMAL, -) +from app.enums import NotificationType, JobStatus +from app.models import KEY_TYPE_NORMAL from app.notifications.process_notifications import persist_notification from app.notifications.validators import check_service_over_total_message_limit from app.serialised_models import SerialisedService, SerialisedTemplate @@ -46,17 +40,17 @@ def process_job(job_id, sender_id=None): ) ) - if job.job_status != JOB_STATUS_PENDING: + if job.job_status != JobStatus.PENDING: return service = job.service - job.job_status = JOB_STATUS_IN_PROGRESS + job.job_status = JobStatus.IN_PROGRESS job.processing_started = start dao_update_job(job) if not service.active: - job.job_status = JOB_STATUS_CANCELLED + job.job_status = JobStatus.CANCELLED dao_update_job(job) current_app.logger.warning( "Job {} has been cancelled, service {} is inactive".format( @@ -85,7 +79,7 @@ def process_job(job_id, sender_id=None): def job_complete(job, resumed=False, start=None): - job.job_status = JOB_STATUS_FINISHED + job.job_status = JobStatus.FINISHED finished = datetime.utcnow() job.processing_finished = finished @@ -432,7 +426,7 @@ def process_incomplete_jobs(job_ids): # reset the processing start time so that the check_job_status scheduled task doesn't pick this job up again for job in jobs: - job.job_status = JOB_STATUS_IN_PROGRESS + job.job_status = JobStatus.IN_PROGRESS job.processing_started = datetime.utcnow() dao_update_job(job) diff --git a/app/dao/jobs_dao.py b/app/dao/jobs_dao.py index a68143f47..209fe76d6 100644 --- a/app/dao/jobs_dao.py +++ b/app/dao/jobs_dao.py @@ -5,10 +5,8 @@ from flask import current_app from sqlalchemy import and_, asc, desc, func from app import db +from app.enums import JobStatus from app.models import ( - JOB_STATUS_FINISHED, - JOB_STATUS_PENDING, - JOB_STATUS_SCHEDULED, FactNotificationStatus, Job, Notification, @@ -85,7 +83,7 @@ def dao_get_scheduled_job_stats( ) .filter( Job.service_id == service_id, - Job.job_status == JOB_STATUS_SCHEDULED, + Job.job_status == JobStatus.SCHEDULED, ) .one() ) @@ -111,7 +109,7 @@ def dao_set_scheduled_jobs_to_pending(): """ jobs = ( Job.query.filter( - Job.job_status == JOB_STATUS_SCHEDULED, + Job.job_status == JobStatus.SCHEDULED, Job.scheduled_for < datetime.utcnow(), ) .order_by(asc(Job.scheduled_for)) @@ -120,7 +118,7 @@ def dao_set_scheduled_jobs_to_pending(): ) for job in jobs: - job.job_status = JOB_STATUS_PENDING + job.job_status = JobStatus.PENDING db.session.add_all(jobs) db.session.commit() @@ -132,7 +130,7 @@ def dao_get_future_scheduled_job_by_id_and_service_id(job_id, service_id): return Job.query.filter( Job.service_id == service_id, Job.id == job_id, - Job.job_status == JOB_STATUS_SCHEDULED, + Job.job_status == JobStatus.SCHEDULED, Job.scheduled_for > datetime.utcnow(), ).one() @@ -200,7 +198,7 @@ def find_jobs_with_missing_rows(): jobs_with_rows_missing = ( db.session.query(Job) .filter( - Job.job_status == JOB_STATUS_FINISHED, + Job.job_status == JobStatus.FINISHED, Job.processing_finished < ten_minutes_ago, Job.processing_finished > yesterday, Job.id == Notification.job_id, diff --git a/app/dao/uploads_dao.py b/app/dao/uploads_dao.py index d29421e09..740d0d884 100644 --- a/app/dao/uploads_dao.py +++ b/app/dao/uploads_dao.py @@ -5,10 +5,8 @@ from flask import current_app from sqlalchemy import String, and_, desc, func, literal, text from app import db -from app.enums import NotificationStatus, NotificationType +from app.enums import NotificationStatus, NotificationType, JobStatus from app.models import ( - JOB_STATUS_CANCELLED, - JOB_STATUS_SCHEDULED, Job, Notification, ServiceDataRetention, @@ -52,7 +50,7 @@ def dao_get_uploads_by_service_id(service_id, limit_days=None, page=1, page_size Job.service_id == service_id, Job.original_file_name != current_app.config["TEST_MESSAGE_FILENAME"], Job.original_file_name != current_app.config["ONE_OFF_MESSAGE_FILENAME"], - Job.job_status.notin_([JOB_STATUS_CANCELLED, JOB_STATUS_SCHEDULED]), + Job.job_status.notin_([JobStatus.CANCELLED, JobStatus.SCHEDULED]), func.coalesce(Job.processing_started, Job.created_at) >= today - func.coalesce(ServiceDataRetention.days_of_retention, 7), ] diff --git a/app/enums.py b/app/enums.py index aeb865cd1..b842e1e59 100644 --- a/app/enums.py +++ b/app/enums.py @@ -178,7 +178,7 @@ class KeyType(Enum): TEST = "test" -class JobStatusType(Enum): +class JobStatus(Enum): PENDING = "pending" IN_PROGRESS = "in progress" FINISHED = "finished" diff --git a/app/job/rest.py b/app/job/rest.py index 1aab2ca60..10d96ae48 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -22,7 +22,7 @@ from app.dao.notifications_dao import ( from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id from app.errors import InvalidRequest, register_errors -from app.models import JOB_STATUS_CANCELLED, JOB_STATUS_PENDING, JOB_STATUS_SCHEDULED +from app.enums import JobStatus from app.schemas import ( job_schema, notification_with_template_schema, @@ -53,7 +53,7 @@ def get_job_by_service_and_job_id(service_id, job_id): @job_blueprint.route("//cancel", methods=["POST"]) def cancel_job(service_id, job_id): job = dao_get_future_scheduled_job_by_id_and_service_id(job_id, service_id) - job.job_status = JOB_STATUS_CANCELLED + job.job_status = JobStatus.CANCELLED dao_update_job(job) return get_job_by_service_and_job_id(service_id, job_id) @@ -175,13 +175,13 @@ def create_job(service_id): job = job_schema.load(data) if job.scheduled_for: - job.job_status = JOB_STATUS_SCHEDULED + job.job_status = JobStatus.SCHEDULED dao_create_job(job) sender_id = data.get("sender_id") - if job.job_status == JOB_STATUS_PENDING: + if job.job_status == JobStatus.PENDING: process_job.apply_async( [str(job.id)], {"sender_id": sender_id}, queue=QueueNames.JOBS ) diff --git a/app/models.py b/app/models.py index 770b5f752..bf624c173 100644 --- a/app/models.py +++ b/app/models.py @@ -28,6 +28,7 @@ from app.enums import ( CallbackType, CodeType, InvitedUserStatus, + JobStatus, KeyType, NotificationStatus, NotificationType, @@ -1346,33 +1347,6 @@ class ProviderDetailsHistory(db.Model, HistoryModel): supports_international = db.Column(db.Boolean, nullable=False, default=False) -JOB_STATUS_PENDING = "pending" -JOB_STATUS_IN_PROGRESS = "in progress" -JOB_STATUS_FINISHED = "finished" -JOB_STATUS_SENDING_LIMITS_EXCEEDED = "sending limits exceeded" -JOB_STATUS_SCHEDULED = "scheduled" -JOB_STATUS_CANCELLED = "cancelled" -JOB_STATUS_READY_TO_SEND = "ready to send" -JOB_STATUS_SENT_TO_DVLA = "sent to dvla" -JOB_STATUS_ERROR = "error" -JOB_STATUS_TYPES = [ - JOB_STATUS_PENDING, - JOB_STATUS_IN_PROGRESS, - JOB_STATUS_FINISHED, - JOB_STATUS_SENDING_LIMITS_EXCEEDED, - JOB_STATUS_SCHEDULED, - JOB_STATUS_CANCELLED, - JOB_STATUS_READY_TO_SEND, - JOB_STATUS_SENT_TO_DVLA, - JOB_STATUS_ERROR, -] - - -class JobStatus(db.Model): - __tablename__ = "job_status" - - name = db.Column(db.String(255), primary_key=True) - class Job(db.Model): __tablename__ = "jobs" @@ -1423,8 +1397,7 @@ class Job(db.Model): ) scheduled_for = db.Column(db.DateTime, index=True, unique=False, nullable=True) job_status = db.Column( - db.String(255), - db.ForeignKey("job_status.name"), + db.Enum(JobStatus, name="job_status"), index=True, nullable=False, default="pending", From 7416de2a280c44e71e3d2aff01c72fe2a8c7d495 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 10:28:50 -0500 Subject: [PATCH 144/259] KeyType implemented. Signed-off-by: Cliff Hill --- app/celery/tasks.py | 15 ++++++------ app/commands.py | 5 ++-- app/dao/fact_billing_dao.py | 8 +++---- app/dao/fact_notification_status_dao.py | 27 ++++++++++------------ app/dao/notifications_dao.py | 15 ++++++------ app/dao/services_dao.py | 13 +++++------ app/delivery/send_to_providers.py | 7 +++--- app/models.py | 9 -------- app/notifications/process_notifications.py | 8 +++---- app/notifications/rest.py | 7 +++--- app/notifications/validators.py | 10 ++++---- app/organization/invite_rest.py | 6 ++--- app/organization/rest.py | 5 ++-- app/service/rest.py | 5 ++-- app/service/send_notification.py | 10 ++++---- app/service/sender.py | 5 ++-- app/service/utils.py | 12 +++++----- app/service_invite/rest.py | 8 +++---- app/user/rest.py | 16 ++++++------- app/v2/notifications/post_notifications.py | 8 +++---- 20 files changed, 90 insertions(+), 109 deletions(-) diff --git a/app/celery/tasks.py b/app/celery/tasks.py index 17b13ffc1..5aa9657eb 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -20,8 +20,7 @@ from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_inbound_api_dao import get_service_inbound_api_for_service from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.templates_dao import dao_get_template_by_id -from app.enums import NotificationType, JobStatus -from app.models import KEY_TYPE_NORMAL +from app.enums import NotificationType, JobStatus, KeyType from app.notifications.process_notifications import persist_notification from app.notifications.validators import check_service_over_total_message_limit from app.serialised_models import SerialisedService, SerialisedTemplate @@ -145,7 +144,7 @@ def process_row(row, template, job, service, sender_id=None): def __total_sending_limits_for_job_exceeded(service, job, job_id): try: - total_sent = check_service_over_total_message_limit(KEY_TYPE_NORMAL, service) + total_sent = check_service_over_total_message_limit(KeyType.NORMAL, service) if total_sent + job.notification_count > service.total_message_limit: raise TotalRequestsError(service.total_message_limit) else: @@ -179,7 +178,7 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i else: reply_to_text = template.reply_to_text - if not service_allowed_to_send_to(notification["to"], service, KEY_TYPE_NORMAL): + if not service_allowed_to_send_to(notification["to"], service, KeyType.NORMAL): current_app.logger.debug( "SMS {} failed as restricted service".format(notification_id) ) @@ -200,7 +199,7 @@ def save_sms(self, service_id, notification_id, encrypted_notification, sender_i personalisation=notification.get("personalisation"), notification_type=NotificationType.SMS, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, created_at=datetime.utcnow(), created_by_id=created_by_id, job_id=notification.get("job", None), @@ -245,7 +244,7 @@ def save_email( else: reply_to_text = template.reply_to_text - if not service_allowed_to_send_to(notification["to"], service, KEY_TYPE_NORMAL): + if not service_allowed_to_send_to(notification["to"], service, KeyType.NORMAL): current_app.logger.info( "Email {} failed as restricted service".format(notification_id) ) @@ -260,7 +259,7 @@ def save_email( personalisation=notification.get("personalisation"), notification_type=NotificationType.EMAIL, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, created_at=datetime.utcnow(), job_id=notification.get("job", None), job_row_number=notification.get("row_number", None), @@ -319,7 +318,7 @@ def save_api_email_or_sms(self, encrypted_notification): notification_type=notification["notification_type"], client_reference=notification["client_reference"], api_key_id=notification.get("api_key_id"), - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, created_at=notification["created_at"], reply_to_text=notification["reply_to_text"], status=notification["status"], diff --git a/app/commands.py b/app/commands.py index 1deec6dce..3b7eb4bd2 100644 --- a/app/commands.py +++ b/app/commands.py @@ -49,9 +49,8 @@ from app.dao.users_dao import ( delete_user_verify_codes, get_user_by_email, ) -from app.enums import NotificationType, NotificationStatus +from app.enums import NotificationType, NotificationStatus, KeyType from app.models import ( - KEY_TYPE_TEST, AnnualBilling, Domain, EmailBranding, @@ -522,7 +521,7 @@ def fix_billable_units(): Notification.status != NotificationStatus.CREATED, Notification.sent_at == None, # noqa Notification.billable_units == 0, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, ) for notification in query.all(): diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index 4ef418374..6ddc13b40 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -8,10 +8,8 @@ from sqlalchemy.sql.expression import case, literal from app import db from app.dao.date_util import get_calendar_year_dates, get_calendar_year_for_datetime from app.dao.organization_dao import dao_get_organization_live_services -from app.enums import NotificationStatus, NotificationType +from app.enums import NotificationStatus, NotificationType, KeyType from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, AnnualBilling, FactBilling, NotificationAllTimeView, @@ -411,7 +409,7 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): ) .filter( NotificationAllTimeView.status.in_(NotificationStatus.sent_emails), - NotificationAllTimeView.key_type.in_((KEY_TYPE_NORMAL, KEY_TYPE_TEAM)), + NotificationAllTimeView.key_type.in_((KeyType.NORMAL, KeyType.TEAM)), NotificationAllTimeView.created_at >= start_date, NotificationAllTimeView.created_at < end_date, NotificationAllTimeView.notification_type == notification_type, @@ -443,7 +441,7 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): ) .filter( NotificationAllTimeView.status.in_(NotificationStatus.billable_sms), - NotificationAllTimeView.key_type.in_((KEY_TYPE_NORMAL, KEY_TYPE_TEAM)), + NotificationAllTimeView.key_type.in_((KeyType.NORMAL, KeyType.TEAM)), NotificationAllTimeView.created_at >= start_date, NotificationAllTimeView.created_at < end_date, NotificationAllTimeView.notification_type == notification_type, diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index 13843f8d9..e1414a28f 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -7,11 +7,8 @@ from sqlalchemy.types import DateTime, Integer from app import db from app.dao.dao_utils import autocommit -from app.enums import NotificationType, NotificationStatus +from app.enums import NotificationType, NotificationStatus, KeyType from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, FactNotificationStatus, Notification, NotificationAllTimeView, @@ -55,7 +52,7 @@ def update_fact_notification_status(process_day, notification_type, service_id): NotificationAllTimeView.created_at < end_date, NotificationAllTimeView.notification_type == notification_type, NotificationAllTimeView.service_id == service_id, - NotificationAllTimeView.key_type.in_((KEY_TYPE_NORMAL, KEY_TYPE_TEAM)), + NotificationAllTimeView.key_type.in_((KeyType.NORMAL, KeyType.TEAM)), ) .group_by( NotificationAllTimeView.template_id, @@ -95,7 +92,7 @@ def fetch_notification_status_for_service_by_month(start_date, end_date, service FactNotificationStatus.service_id == service_id, FactNotificationStatus.local_date >= start_date, FactNotificationStatus.local_date < end_date, - FactNotificationStatus.key_type != KEY_TYPE_TEST, + FactNotificationStatus.key_type != KeyType.TEST, ) .group_by( func.date_trunc("month", FactNotificationStatus.local_date).label("month"), @@ -120,7 +117,7 @@ def fetch_notification_status_for_service_for_day(fetch_day, service_id): Notification.created_at < get_midnight_in_utc(fetch_day + timedelta(days=1)), Notification.service_id == service_id, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, ) .group_by(Notification.notification_type, Notification.status) .all() @@ -144,7 +141,7 @@ def fetch_notification_status_for_service_for_today_and_7_previous_days( ).filter( FactNotificationStatus.service_id == service_id, FactNotificationStatus.local_date >= start_date, - FactNotificationStatus.key_type != KEY_TYPE_TEST, + FactNotificationStatus.key_type != KeyType.TEST, ) stats_for_today = ( @@ -157,7 +154,7 @@ def fetch_notification_status_for_service_for_today_and_7_previous_days( .filter( Notification.created_at >= get_midnight_in_utc(now), Notification.service_id == service_id, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, ) .group_by( Notification.notification_type, @@ -294,7 +291,7 @@ def fetch_stats_for_all_services_by_date_range( ) ) if not include_from_test_key: - stats = stats.filter(FactNotificationStatus.key_type != KEY_TYPE_TEST) + stats = stats.filter(FactNotificationStatus.key_type != KeyType.TEST) if start_date <= datetime.utcnow().date() <= end_date: today = get_midnight_in_utc(datetime.utcnow()) @@ -313,7 +310,7 @@ def fetch_stats_for_all_services_by_date_range( ) ) if not include_from_test_key: - subquery = subquery.filter(Notification.key_type != KEY_TYPE_TEST) + subquery = subquery.filter(Notification.key_type != KeyType.TEST) subquery = subquery.subquery() stats_for_today = db.session.query( @@ -375,7 +372,7 @@ def fetch_monthly_template_usage_for_service(start_date, end_date, service_id): FactNotificationStatus.service_id == service_id, FactNotificationStatus.local_date >= start_date, FactNotificationStatus.local_date <= end_date, - FactNotificationStatus.key_type != KEY_TYPE_TEST, + FactNotificationStatus.key_type != KeyType.TEST, FactNotificationStatus.notification_status != NotificationStatus.CANCELLED, ) .group_by( @@ -412,7 +409,7 @@ def fetch_monthly_template_usage_for_service(start_date, end_date, service_id): .filter( Notification.created_at >= today, Notification.service_id == service_id, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, Notification.status != NotificationStatus.CANCELLED, ) .group_by( @@ -480,7 +477,7 @@ def get_total_notifications_for_date_range(start_date, end_date): ).label("sms"), ) .filter( - FactNotificationStatus.key_type != KEY_TYPE_TEST, + FactNotificationStatus.key_type != KeyType.TEST, ) .group_by(FactNotificationStatus.local_date) .order_by(FactNotificationStatus.local_date) @@ -581,7 +578,7 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): .filter( FactNotificationStatus.notification_status != NotificationStatus.CREATED, Service.active.is_(True), - FactNotificationStatus.key_type != KEY_TYPE_TEST, + FactNotificationStatus.key_type != KeyType.TEST, Service.restricted.is_(False), FactNotificationStatus.local_date >= start_date, FactNotificationStatus.local_date <= end_date, diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index 2293b6c2e..fa14d6c05 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -16,9 +16,8 @@ from werkzeug.datastructures import MultiDict from app import create_uuid, db from app.dao.dao_utils import autocommit -from app.enums import NotificationType, NotificationStatus +from app.enums import NotificationType, NotificationStatus, KeyType from app.models import ( - KEY_TYPE_TEST, FactNotificationStatus, Notification, NotificationHistory, @@ -36,7 +35,7 @@ def dao_get_last_date_template_was_used(template_id, service_id): .filter( Notification.service_id == service_id, Notification.template_id == template_id, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, ) .scalar() ) @@ -48,7 +47,7 @@ def dao_get_last_date_template_was_used(template_id, service_id): db.session.query(functions.max(FactNotificationStatus.local_date)) .filter( FactNotificationStatus.template_id == template_id, - FactNotificationStatus.key_type != KEY_TYPE_TEST, + FactNotificationStatus.key_type != KeyType.TEST, ) .scalar() ) @@ -269,7 +268,7 @@ def get_notifications_for_service( if key_type is not None: filters.append(Notification.key_type == key_type) elif not include_from_test_key: - filters.append(Notification.key_type != KEY_TYPE_TEST) + filters.append(Notification.key_type != KeyType.TEST) if client_reference is not None: filters.append(Notification.client_reference == client_reference) @@ -409,7 +408,7 @@ def move_notifications_to_notification_history( Notification.notification_type == notification_type, Notification.service_id == service_id, Notification.created_at < timestamp_to_delete_backwards_from, - Notification.key_type == KEY_TYPE_TEST, + Notification.key_type == KeyType.TEST, ).delete(synchronize_session=False) db.session.commit() @@ -513,7 +512,7 @@ def dao_get_notifications_by_recipient_or_reference( Notification.normalised_to.like("%{}%".format(normalised)), Notification.client_reference.ilike("%{}%".format(search_term)), ), - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, ] if statuses: @@ -576,7 +575,7 @@ def dao_get_notifications_processing_time_stats(start_date, end_date): Notification.created_at >= start_date, Notification.created_at < end_date, Notification.api_key_id.isnot(None), - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, ) .one() ) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index d0a4b4580..a2f960bf7 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -13,9 +13,8 @@ from app.dao.organization_dao import dao_get_organization_by_email_address from app.dao.service_sms_sender_dao import insert_service_sms_sender from app.dao.service_user_dao import dao_get_service_user from app.dao.template_folder_dao import dao_get_valid_template_folders_by_id -from app.enums import NotificationStatus, NotificationType, ServicePermissionType +from app.enums import NotificationStatus, NotificationType, ServicePermissionType, KeyType from app.models import ( - KEY_TYPE_TEST, AnnualBilling, ApiKey, FactBilling, @@ -405,7 +404,7 @@ def dao_fetch_todays_stats_for_service(service_id): ) .filter( Notification.service_id == service_id, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, Notification.created_at >= start_date, ) .group_by( @@ -439,7 +438,7 @@ def dao_fetch_todays_stats_for_all_services( ) if not include_from_test_key: - subquery = subquery.filter(Notification.key_type != KEY_TYPE_TEST) + subquery = subquery.filter(Notification.key_type != KeyType.TEST) subquery = subquery.subquery() @@ -510,7 +509,7 @@ def dao_find_services_sending_to_tv_numbers(start_date, end_date, threshold=500) Notification.service_id == Service.id, Notification.created_at >= start_date, Notification.created_at <= end_date, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, Notification.notification_type == NotificationType.SMS, func.substr(Notification.normalised_to, 3, 7) == "7700900", Service.restricted == False, # noqa @@ -534,7 +533,7 @@ def dao_find_services_with_high_failure_rates(start_date, end_date, threshold=10 Notification.service_id == Service.id, Notification.created_at >= start_date, Notification.created_at <= end_date, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, Notification.notification_type == NotificationType.SMS, Service.restricted == False, # noqa Service.active == True, # noqa @@ -562,7 +561,7 @@ def dao_find_services_with_high_failure_rates(start_date, end_date, threshold=10 Notification.service_id == Service.id, Notification.created_at >= start_date, Notification.created_at <= end_date, - Notification.key_type != KEY_TYPE_TEST, + Notification.key_type != KeyType.TEST, Notification.notification_type == NotificationType.SMS, Notification.status == NotificationStatus.PERMANENT_FAILURE, Service.restricted == False, # noqa diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 439c238b6..9910bc0b4 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -15,12 +15,11 @@ 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.enums import NotificationStatus, NotificationType +from app.enums import NotificationStatus, NotificationType, KeyType from app.exceptions import NotificationTechnicalFailureException from app.models import ( BRANDING_BOTH, BRANDING_ORG_BANNER, - KEY_TYPE_TEST, ) from app.serialised_models import SerialisedService, SerialisedTemplate @@ -50,7 +49,7 @@ def send_sms_to_provider(notification): prefix=service.name, show_prefix=service.prefix_sms, ) - if notification.key_type == KEY_TYPE_TEST: + if notification.key_type == KeyType.TEST: update_notification_to_sending(notification, provider) send_sms_response(provider.name, str(notification.id)) @@ -134,7 +133,7 @@ def send_email_to_provider(notification): # Someone needs an email, possibly new registration recipient = redis_store.get(f"email-address-{notification.id}") recipient = recipient.decode("utf-8") - if notification.key_type == KEY_TYPE_TEST: + if notification.key_type == KeyType.TEST: notification.reference = str(create_uuid()) update_notification_to_sending(notification, provider) send_email_response(notification.reference, recipient) diff --git a/app/models.py b/app/models.py index bf624c173..3def65feb 100644 --- a/app/models.py +++ b/app/models.py @@ -1287,15 +1287,6 @@ class TemplateHistory(TemplateBase): ) -SNS_PROVIDER = "sns" -SES_PROVIDER = "ses" - -SMS_PROVIDERS = [SNS_PROVIDER] -EMAIL_PROVIDERS = [SES_PROVIDER] -PROVIDERS = SMS_PROVIDERS + EMAIL_PROVIDERS -# TODO: What about these? - - class ProviderDetails(db.Model): __tablename__ = "provider_details" diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index aa8c2166b..114f5b6ae 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -16,8 +16,8 @@ from app.dao.notifications_dao import ( dao_create_notification, dao_delete_notifications_by_id, ) -from app.enums import NotificationType, NotificationStatus -from app.models import KEY_TYPE_TEST, Notification +from app.enums import NotificationType, NotificationStatus, KeyType +from app.models import Notification from app.v2.errors import BadRequestError @@ -131,7 +131,7 @@ def persist_notification( if not simulated: current_app.logger.info("Firing dao_create_notification") dao_create_notification(notification) - if key_type != KEY_TYPE_TEST and current_app.config["REDIS_ENABLED"]: + if key_type != KeyType.TEST and current_app.config["REDIS_ENABLED"]: current_app.logger.info( "Redis enabled, querying cache key for service id: {}".format( service.id @@ -147,7 +147,7 @@ def persist_notification( def send_notification_to_queue_detached( key_type, notification_type, notification_id, queue=None ): - if key_type == KEY_TYPE_TEST: + if key_type == KeyType.TEST: print("send_notification_to_queue_detached key is test key") if notification_type == NotificationType.SMS: diff --git a/app/notifications/rest.py b/app/notifications/rest.py index 0d0b70db7..cec142e76 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -4,9 +4,8 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT from app import api_user, authenticated_service from app.config import QueueNames from app.dao import notifications_dao -from app.enums import NotificationType +from app.enums import NotificationType, KeyType, TemplateProcessType from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_TEAM, PRIORITY from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -137,7 +136,7 @@ def send_notification(notification_type): reply_to_text=template.reply_to_text, ) if not simulated: - queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None + queue_name = QueueNames.PRIORITY if template.process_type == TemplateProcessType.PRIORITY else None send_notification_to_queue(notification=notification_model, queue=queue_name) else: @@ -171,7 +170,7 @@ def get_notification_return_data(notification_id, notification, template): def _service_allowed_to_send_to(notification, service): if not service_allowed_to_send_to(notification["to"], service, api_user.key_type): - if api_user.key_type == KEY_TYPE_TEAM: + if api_user.key_type == KeyType.TEAM: message = "Can’t send to this recipient using a team-only API key" else: message = ( diff --git a/app/notifications/validators.py b/app/notifications/validators.py index 20bd2bc07..c8b47e98f 100644 --- a/app/notifications/validators.py +++ b/app/notifications/validators.py @@ -15,8 +15,8 @@ from app import redis_store from app.dao.notifications_dao import dao_get_notification_count_for_service from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id -from app.enums import NotificationType, ServicePermissionType, TemplateType -from app.models import KEY_TYPE_TEAM, KEY_TYPE_TEST, ServicePermission +from app.enums import NotificationType, ServicePermissionType, TemplateType, KeyType +from app.models import ServicePermission from app.notifications.process_notifications import create_content_for_notification from app.serialised_models import SerialisedTemplate from app.service.utils import service_allowed_to_send_to @@ -40,7 +40,7 @@ def check_service_over_api_rate_limit(service, api_key): def check_service_over_total_message_limit(key_type, service): - if key_type == KEY_TYPE_TEST or not current_app.config["REDIS_ENABLED"]: + if key_type == KeyType.TEST or not current_app.config["REDIS_ENABLED"]: return 0 cache_key = total_limit_cache_key(service.id) @@ -61,7 +61,7 @@ def check_service_over_total_message_limit(key_type, service): def check_application_over_retention_limit(key_type, service): - if key_type == KEY_TYPE_TEST or not current_app.config["REDIS_ENABLED"]: + if key_type == KeyType.TEST or not current_app.config["REDIS_ENABLED"]: return 0 total_stats = dao_get_notification_count_for_service(service_id=service.id) @@ -104,7 +104,7 @@ def service_can_send_to_recipient( if not service_allowed_to_send_to( send_to, service, key_type, allow_guest_list_recipients ): - if key_type == KEY_TYPE_TEAM: + if key_type == KeyType.TEAM: message = "Can’t send to this recipient using a team-only API key" else: message = ( diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index 2de961e43..d95ba7d5d 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -12,9 +12,9 @@ from app.dao.invited_org_user_dao import ( save_invited_org_user, ) from app.dao.templates_dao import dao_get_template_by_id -from app.enums import NotificationType +from app.enums import NotificationType, KeyType from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_NORMAL, InvitedOrganizationUser +from app.models import InvitedOrganizationUser from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -67,7 +67,7 @@ def invite_user_to_org(organization_id): }, notification_type=NotificationType.EMAIL, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=invited_org_user.invited_by.email_address, ) diff --git a/app/organization/rest.py b/app/organization/rest.py index adb236cac..ffd6c7a7d 100644 --- a/app/organization/rest.py +++ b/app/organization/rest.py @@ -20,8 +20,9 @@ from app.dao.organization_dao import ( from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id from app.dao.users_dao import get_user_by_id +from app.enums import KeyType from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_NORMAL, Organization +from app.models import Organization from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -205,7 +206,7 @@ def send_notifications_on_mou_signed(organization_id): personalisation=personalisation, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=notify_service.get_default_reply_to_email_address(), ) send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) diff --git a/app/service/rest.py b/app/service/rest.py index 8f0515c6b..f7dcdd092 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -73,8 +73,9 @@ from app.dao.services_dao import ( ) from app.dao.templates_dao import dao_get_template_by_id from app.dao.users_dao import get_user_by_id +from app.enums import KeyType from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_NORMAL, EmailBranding, Permission, Service +from app.models import EmailBranding, Permission, Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -782,7 +783,7 @@ def verify_reply_to_email_address(service_id): personalisation="", notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=notify_service.get_default_reply_to_email_address(), ) diff --git a/app/service/send_notification.py b/app/service/send_notification.py index 8f767e36a..4bf13bec0 100644 --- a/app/service/send_notification.py +++ b/app/service/send_notification.py @@ -6,8 +6,8 @@ from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id_and_service_id from app.dao.users_dao import get_user_by_id -from app.enums import NotificationType -from app.models import KEY_TYPE_NORMAL, PRIORITY +from app.enums import NotificationType, KeyType +from app.models import PRIORITY from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -46,11 +46,11 @@ def send_one_off_notification(service_id, post_data): validate_template(template.id, personalisation, service, template.template_type) - check_service_over_total_message_limit(KEY_TYPE_NORMAL, service) + check_service_over_total_message_limit(KeyType.NORMAL, service) validate_and_format_recipient( send_to=post_data["to"], - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, service=service, notification_type=template.template_type, allow_guest_list_recipients=False, @@ -74,7 +74,7 @@ def send_one_off_notification(service_id, post_data): personalisation=personalisation, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, created_by_id=post_data["created_by"], reply_to_text=reply_to, reference=create_one_off_reference(template.template_type), diff --git a/app/service/sender.py b/app/service/sender.py index 4e96f48c0..43240407c 100644 --- a/app/service/sender.py +++ b/app/service/sender.py @@ -6,8 +6,7 @@ from app.dao.services_dao import ( dao_fetch_service_by_id, ) from app.dao.templates_dao import dao_get_template_by_id -from app.enums import TemplateType -from app.models import KEY_TYPE_NORMAL +from app.enums import TemplateType, KeyType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -36,7 +35,7 @@ def send_notification_to_service_users( personalisation=personalisation, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=notify_service.get_default_reply_to_email_address(), ) send_notification_to_queue(notification, queue=QueueNames.NOTIFY) diff --git a/app/service/utils.py b/app/service/utils.py index 61e41c859..76b88d496 100644 --- a/app/service/utils.py +++ b/app/service/utils.py @@ -3,8 +3,8 @@ import itertools from notifications_utils.recipients import allowed_to_send_to from app.dao.services_dao import dao_fetch_service_by_id -from app.enums import RecipientType -from app.models import KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, ServiceGuestList +from app.enums import RecipientType, KeyType +from app.models import ServiceGuestList def get_recipients_from_request(request_json, key, type): @@ -28,10 +28,10 @@ def get_guest_list_objects(service_id, request_json): def service_allowed_to_send_to( recipient, service, key_type, allow_guest_list_recipients=True ): - if key_type == KEY_TYPE_TEST: + if key_type == KeyType.TEST: return True - if key_type == KEY_TYPE_NORMAL and not service.restricted: + if key_type == KeyType.NORMAL and not service.restricted: return True # Revert back to the ORM model here so we can get some things which @@ -45,8 +45,8 @@ def service_allowed_to_send_to( member.recipient for member in service.guest_list if allow_guest_list_recipients ] - if (key_type == KEY_TYPE_NORMAL and service.restricted) or ( - key_type == KEY_TYPE_TEAM + if (key_type == KeyType.NORMAL and service.restricted) or ( + key_type == KeyType.TEAM ): return allowed_to_send_to( recipient, itertools.chain(team_members, guest_list_members) diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index 9c4dcc972..ee2eed6d6 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -14,9 +14,9 @@ from app.dao.invited_user_dao import ( save_invited_user, ) from app.dao.templates_dao import dao_get_template_by_id -from app.enums import NotificationType +from app.enums import InvitedUserStatus, NotificationType, KeyType from app.errors import InvalidRequest, register_errors -from app.models import INVITE_PENDING, KEY_TYPE_NORMAL, Service +from app.models import Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -47,7 +47,7 @@ def _create_service_invite(invited_user, invite_link_host): }, notification_type=NotificationType.EMAIL, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=invited_user.from_user.email_address, ) @@ -116,7 +116,7 @@ def resend_service_invite(service_id, invited_user_id): ) fetched.created_at = datetime.utcnow() - fetched.status = INVITE_PENDING + fetched.status = InvitedUserStatus.PENDING current_data = {k: v for k, v in invited_user_schema.dump(fetched).items()} update_dict = invited_user_schema.load(current_data) diff --git a/app/user/rest.py b/app/user/rest.py index 8c9419d9b..33beac4d5 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -32,9 +32,9 @@ from app.dao.users_dao import ( update_user_password, use_user_code, ) -from app.enums import CodeType, NotificationType, TemplateType +from app.enums import CodeType, NotificationType, TemplateType, KeyType from app.errors import InvalidRequest, register_errors -from app.models import KEY_TYPE_NORMAL, Permission, Service +from app.models import Permission, Service from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -134,7 +134,7 @@ def update_user_attribute(user_id): }, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=reply_to, ) @@ -348,7 +348,7 @@ def create_2fa_code( personalisation=personalisation, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=reply_to, ) @@ -387,7 +387,7 @@ def send_user_confirm_new_email(user_id): }, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) @@ -425,7 +425,7 @@ def send_new_user_email_verification(user_id): }, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) @@ -471,7 +471,7 @@ def send_already_registered_email(user_id): }, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) @@ -588,7 +588,7 @@ def send_user_reset_password(): }, notification_type=template.template_type, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, reply_to_text=service.get_default_reply_to_email_address(), ) diff --git a/app/v2/notifications/post_notifications.py b/app/v2/notifications/post_notifications.py index a1caa03b0..b290c6d63 100644 --- a/app/v2/notifications/post_notifications.py +++ b/app/v2/notifications/post_notifications.py @@ -10,8 +10,8 @@ from app import api_user, authenticated_service, document_download_client, encry from app.celery.tasks import save_api_email, save_api_sms from app.clients.document_download import DocumentDownloadError from app.config import QueueNames -from app.enums import NotificationType, NotificationStatus -from app.models import KEY_TYPE_NORMAL, PRIORITY, Notification +from app.enums import NotificationType, NotificationStatus, KeyType, TemplateProcessType +from app.models import Notification from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue_detached, @@ -130,7 +130,7 @@ def process_sms_or_email_notification( if ( service.high_volume - and api_user.key_type == KEY_TYPE_NORMAL + and api_user.key_type == KeyType.NORMAL and notification_type in {NotificationType.EMAIL, NotificationType.SMS} ): # Put service with high volumes of notifications onto a queue @@ -177,7 +177,7 @@ def process_sms_or_email_notification( ) if not simulated: - queue_name = QueueNames.PRIORITY if template_process_type == PRIORITY else None + queue_name = QueueNames.PRIORITY if template_process_type == TemplateProcessType.PRIORITY else None send_notification_to_queue_detached( key_type=api_user.key_type, notification_type=notification_type, From ab7387acd8319eee8765fe1d66c6ca54aad8f11e Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Mon, 15 Jan 2024 16:45:55 -0500 Subject: [PATCH 145/259] All string "constants" in app.models converted to app.enums. Signed-off-by: Cliff Hill --- app/dao/service_callback_api_dao.py | 7 +++---- app/dao/users_dao.py | 5 +++-- app/delivery/send_to_providers.py | 10 +++------- app/organization/organization_schema.py | 8 ++++---- app/service/callback_rest.py | 9 +++------ app/service/send_notification.py | 5 ++--- app/utils.py | 5 ++--- 7 files changed, 20 insertions(+), 29 deletions(-) diff --git a/app/dao/service_callback_api_dao.py b/app/dao/service_callback_api_dao.py index 2a4f3ff3c..e9f42692d 100644 --- a/app/dao/service_callback_api_dao.py +++ b/app/dao/service_callback_api_dao.py @@ -2,9 +2,8 @@ from datetime import datetime from app import create_uuid, db from app.dao.dao_utils import autocommit, version_class +from app.enums import CallbackType from app.models import ( - COMPLAINT_CALLBACK_TYPE, - DELIVERY_STATUS_CALLBACK_TYPE, ServiceCallbackApi, ) @@ -40,13 +39,13 @@ def get_service_callback_api(service_callback_api_id, service_id): def get_service_delivery_status_callback_api_for_service(service_id): return ServiceCallbackApi.query.filter_by( - service_id=service_id, callback_type=DELIVERY_STATUS_CALLBACK_TYPE + service_id=service_id, callback_type=CallbackType.DELIVERY_STATUS, ).first() def get_service_complaint_callback_api_for_service(service_id): return ServiceCallbackApi.query.filter_by( - service_id=service_id, callback_type=COMPLAINT_CALLBACK_TYPE + service_id=service_id, callback_type=CallbackType.COMPLAINT, ).first() diff --git a/app/dao/users_dao.py b/app/dao/users_dao.py index 49e2c4d39..44098b1a6 100644 --- a/app/dao/users_dao.py +++ b/app/dao/users_dao.py @@ -9,8 +9,9 @@ from app import db from app.dao.dao_utils import autocommit from app.dao.permissions_dao import permission_dao from app.dao.service_user_dao import dao_get_service_users_by_user_id +from app.enums import AuthType from app.errors import InvalidRequest -from app.models import EMAIL_AUTH_TYPE, User, VerifyCode +from app.models import User, VerifyCode from app.utils import escape_special_characters, get_archived_db_column_value @@ -171,7 +172,7 @@ def dao_archive_user(user): user.organizations = [] - user.auth_type = EMAIL_AUTH_TYPE + user.auth_type = AuthType.EMAIL user.email_address = get_archived_db_column_value(user.email_address) user.mobile_number = None user.password = str(uuid.uuid4()) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 9910bc0b4..03d3ed3cc 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -15,12 +15,8 @@ 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.enums import NotificationStatus, NotificationType, KeyType +from app.enums import NotificationStatus, NotificationType, KeyType, BrandType from app.exceptions import NotificationTechnicalFailureException -from app.models import ( - BRANDING_BOTH, - BRANDING_ORG_BANNER, -) from app.serialised_models import SerialisedService, SerialisedTemplate @@ -231,8 +227,8 @@ def get_html_email_options(service): ) return { - "govuk_banner": branding.brand_type == BRANDING_BOTH, - "brand_banner": branding.brand_type == BRANDING_ORG_BANNER, + "govuk_banner": branding.brand_type == BrandType.BOTH, + "brand_banner": branding.brand_type == BrandType.ORG_BANNER, "brand_colour": branding.colour, "brand_logo": logo_url, "brand_text": branding.text, diff --git a/app/organization/organization_schema.py b/app/organization/organization_schema.py index fccfc1a8d..ad88fbeef 100644 --- a/app/organization/organization_schema.py +++ b/app/organization/organization_schema.py @@ -1,4 +1,4 @@ -from app.models import INVITED_USER_STATUS_TYPES, ORGANIZATION_TYPES +from app.enums import InvitedUserStatus, OrganizationType from app.schema_validation.definitions import uuid post_create_organization_schema = { @@ -8,7 +8,7 @@ post_create_organization_schema = { "properties": { "name": {"type": "string"}, "active": {"type": ["boolean", "null"]}, - "organization_type": {"enum": ORGANIZATION_TYPES}, + "organization_type": {"enum": [e.value for e in OrganizationType]}, }, "required": ["name", "organization_type"], } @@ -20,7 +20,7 @@ post_update_organization_schema = { "properties": { "name": {"type": ["string", "null"]}, "active": {"type": ["boolean", "null"]}, - "organization_type": {"enum": ORGANIZATION_TYPES}, + "organization_type": {"enum": [e.value for e in OrganizationType]}, }, "required": [], } @@ -51,6 +51,6 @@ post_update_invited_org_user_status_schema = { "$schema": "http://json-schema.org/draft-07/schema#", "description": "POST update organization invite schema", "type": "object", - "properties": {"status": {"enum": INVITED_USER_STATUS_TYPES}}, + "properties": {"status": {"enum": [e.value for e in InvitedUserStatus]}}, "required": ["status"], } diff --git a/app/service/callback_rest.py b/app/service/callback_rest.py index 94da0aead..ab25e3ca6 100644 --- a/app/service/callback_rest.py +++ b/app/service/callback_rest.py @@ -13,12 +13,9 @@ from app.dao.service_inbound_api_dao import ( reset_service_inbound_api, save_service_inbound_api, ) +from app.enums import CallbackType from app.errors import InvalidRequest, register_errors -from app.models import ( - DELIVERY_STATUS_CALLBACK_TYPE, - ServiceCallbackApi, - ServiceInboundApi, -) +from app.models import ServiceCallbackApi, ServiceInboundApi from app.schema_validation import validate from app.service.service_callback_api_schema import ( create_service_callback_api_schema, @@ -90,7 +87,7 @@ def create_service_callback_api(service_id): data = request.get_json() validate(data, create_service_callback_api_schema) data["service_id"] = service_id - data["callback_type"] = DELIVERY_STATUS_CALLBACK_TYPE + data["callback_type"] = CallbackType.DELIVERY_STATUS callback_api = ServiceCallbackApi(**data) try: save_service_callback_api(callback_api) diff --git a/app/service/send_notification.py b/app/service/send_notification.py index 4bf13bec0..d0a9914cb 100644 --- a/app/service/send_notification.py +++ b/app/service/send_notification.py @@ -6,8 +6,7 @@ from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id_and_service_id from app.dao.users_dao import get_user_by_id -from app.enums import NotificationType, KeyType -from app.models import PRIORITY +from app.enums import NotificationType, KeyType, TemplateProcessType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -81,7 +80,7 @@ def send_one_off_notification(service_id, post_data): client_reference=client_reference, ) - queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None + queue_name = QueueNames.PRIORITY if template.process_type == TemplateProcessType.PRIORITY else None send_notification_to_queue( notification=notification, diff --git a/app/utils.py b/app/utils.py index fc68acd86..833dce55f 100644 --- a/app/utils.py +++ b/app/utils.py @@ -78,13 +78,12 @@ def get_month_from_utc_column(column): def get_public_notify_type_text(notify_type, plural=False): - from app.enums import NotificationType - from app.models import UPLOAD_DOCUMENT + from app.enums import NotificationType, ServicePermissionType notify_type_text = notify_type if notify_type == NotificationType.SMS: notify_type_text = "text message" - elif notify_type == UPLOAD_DOCUMENT: + elif notify_type == ServicePermissionType.UPLOAD_DOCUMENT: notify_type_text = "document" return "{}{}".format(notify_type_text, "s" if plural else "") From 9523cc1d975d3088051098676c218de754a389b7 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 16 Jan 2024 07:37:21 -0500 Subject: [PATCH 146/259] Cleaning up with black, isort, flake8. Signed-off-by: Cliff Hill --- app/celery/process_ses_receipts_tasks.py | 5 ++++- app/celery/provider_tasks.py | 9 ++++++--- app/celery/scheduled_tasks.py | 2 +- app/celery/tasks.py | 2 +- app/commands.py | 2 +- app/dao/fact_billing_dao.py | 2 +- app/dao/fact_notification_status_dao.py | 7 +++++-- app/dao/notifications_dao.py | 17 +++++++++-------- app/dao/service_callback_api_dao.py | 10 +++++----- app/dao/services_dao.py | 7 ++++++- app/dao/uploads_dao.py | 9 ++------- app/delivery/send_to_providers.py | 2 +- app/enums.py | 10 ++++++---- app/job/rest.py | 2 +- app/models.py | 8 +------- app/notifications/process_notifications.py | 2 +- app/notifications/rest.py | 8 ++++++-- app/notifications/validators.py | 2 +- app/organization/invite_rest.py | 2 +- app/service/send_notification.py | 8 ++++++-- app/service/sender.py | 2 +- app/service/statistics.py | 2 +- app/service/utils.py | 2 +- app/service_invite/rest.py | 2 +- app/template/template_schemas.py | 2 +- app/user/rest.py | 2 +- app/v2/notifications/notification_schemas.py | 7 +++++-- app/v2/notifications/post_notifications.py | 8 ++++++-- 28 files changed, 82 insertions(+), 61 deletions(-) diff --git a/app/celery/process_ses_receipts_tasks.py b/app/celery/process_ses_receipts_tasks.py index f88360668..8c5de1f62 100644 --- a/app/celery/process_ses_receipts_tasks.py +++ b/app/celery/process_ses_receipts_tasks.py @@ -77,7 +77,10 @@ def process_ses_results(self, response): f"SES bounce for notification ID {notification.id}: {bounce_message}" ) - if notification.status not in {NotificationStatus.SENDING, NotificationStatus.PENDING}: + if notification.status not in { + NotificationStatus.SENDING, + NotificationStatus.PENDING, + }: notifications_dao._duplicate_update_warning( notification, notification_status ) diff --git a/app/celery/provider_tasks.py b/app/celery/provider_tasks.py index 1f90b0024..6f72dccc2 100644 --- a/app/celery/provider_tasks.py +++ b/app/celery/provider_tasks.py @@ -121,7 +121,8 @@ def deliver_sms(self, notification_id): ) except Exception as e: update_notification_status_by_id( - notification_id, NotificationStatus.TEMPORARY_FAILURE, + notification_id, + NotificationStatus.TEMPORARY_FAILURE, ) if isinstance(e, SmsClientResponseException): current_app.logger.warning( @@ -146,7 +147,8 @@ def deliver_sms(self, notification_id): ) ) update_notification_status_by_id( - notification_id, NotificationStatus.TECHNICAL_FAILURE, + notification_id, + NotificationStatus.TECHNICAL_FAILURE, ) raise NotificationTechnicalFailureException(message) @@ -189,6 +191,7 @@ def deliver_email(self, notification_id): ) ) update_notification_status_by_id( - notification_id, NotificationStatus.TECHNICAL_FAILURE, + notification_id, + NotificationStatus.TECHNICAL_FAILURE, ) raise NotificationTechnicalFailureException(message) diff --git a/app/celery/scheduled_tasks.py b/app/celery/scheduled_tasks.py index 65be85a03..1742a310c 100644 --- a/app/celery/scheduled_tasks.py +++ b/app/celery/scheduled_tasks.py @@ -34,7 +34,7 @@ from app.dao.services_dao import ( ) from app.dao.users_dao import delete_codes_older_created_more_than_a_day_ago from app.delivery.send_to_providers import provider_to_use -from app.enums import NotificationType, JobStatus +from app.enums import JobStatus, NotificationType from app.models import Job from app.notifications.process_notifications import send_notification_to_queue diff --git a/app/celery/tasks.py b/app/celery/tasks.py index 5aa9657eb..b7cb43c6d 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -20,7 +20,7 @@ from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_inbound_api_dao import get_service_inbound_api_for_service from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.templates_dao import dao_get_template_by_id -from app.enums import NotificationType, JobStatus, KeyType +from app.enums import JobStatus, KeyType, NotificationType from app.notifications.process_notifications import persist_notification from app.notifications.validators import check_service_over_total_message_limit from app.serialised_models import SerialisedService, SerialisedTemplate diff --git a/app/commands.py b/app/commands.py index 3b7eb4bd2..8f5a8b15d 100644 --- a/app/commands.py +++ b/app/commands.py @@ -49,7 +49,7 @@ from app.dao.users_dao import ( delete_user_verify_codes, get_user_by_email, ) -from app.enums import NotificationType, NotificationStatus, KeyType +from app.enums import KeyType, NotificationStatus, NotificationType from app.models import ( AnnualBilling, Domain, diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index 6ddc13b40..23ad14b41 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -8,7 +8,7 @@ from sqlalchemy.sql.expression import case, literal from app import db from app.dao.date_util import get_calendar_year_dates, get_calendar_year_for_datetime from app.dao.organization_dao import dao_get_organization_live_services -from app.enums import NotificationStatus, NotificationType, KeyType +from app.enums import KeyType, NotificationStatus, NotificationType from app.models import ( AnnualBilling, FactBilling, diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index e1414a28f..ee2e7c626 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -7,7 +7,7 @@ from sqlalchemy.types import DateTime, Integer from app import db from app.dao.dao_utils import autocommit -from app.enums import NotificationType, NotificationStatus, KeyType +from app.enums import KeyType, NotificationStatus, NotificationType from app.models import ( FactNotificationStatus, Notification, @@ -529,7 +529,10 @@ def fetch_monthly_notification_statuses_per_service(start_date, end_date): [ ( FactNotificationStatus.notification_status.in_( - [NotificationStatus.TECHNICAL_FAILURE, NotificationStatus.FAILED] + [ + NotificationStatus.TECHNICAL_FAILURE, + NotificationStatus.FAILED, + ] ), FactNotificationStatus.notification_count, ) diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index fa14d6c05..9ddf0bcb9 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -16,12 +16,8 @@ from werkzeug.datastructures import MultiDict from app import create_uuid, db from app.dao.dao_utils import autocommit -from app.enums import NotificationType, NotificationStatus, KeyType -from app.models import ( - FactNotificationStatus, - Notification, - NotificationHistory, -) +from app.enums import KeyType, NotificationStatus, NotificationType +from app.models import FactNotificationStatus, Notification, NotificationHistory from app.utils import ( escape_special_characters, get_midnight_in_utc, @@ -162,7 +158,10 @@ def update_notification_status_by_reference(reference, status): ) return None - if notification.status not in {NotificationStatus.SENDING, NotificationStatus.PENDING}: + if notification.status not in { + NotificationStatus.SENDING, + NotificationStatus.PENDING, + }: _duplicate_update_warning(notification, status) return None @@ -200,7 +199,9 @@ def dao_get_notification_count_for_service(*, service_id): def dao_get_failed_notification_count(): - failed_count = Notification.query.filter_by(status=NotificationStatus.FAILED).count() + failed_count = Notification.query.filter_by( + status=NotificationStatus.FAILED + ).count() return failed_count diff --git a/app/dao/service_callback_api_dao.py b/app/dao/service_callback_api_dao.py index e9f42692d..75ea69f09 100644 --- a/app/dao/service_callback_api_dao.py +++ b/app/dao/service_callback_api_dao.py @@ -3,9 +3,7 @@ from datetime import datetime from app import create_uuid, db from app.dao.dao_utils import autocommit, version_class from app.enums import CallbackType -from app.models import ( - ServiceCallbackApi, -) +from app.models import ServiceCallbackApi @autocommit @@ -39,13 +37,15 @@ def get_service_callback_api(service_callback_api_id, service_id): def get_service_delivery_status_callback_api_for_service(service_id): return ServiceCallbackApi.query.filter_by( - service_id=service_id, callback_type=CallbackType.DELIVERY_STATUS, + service_id=service_id, + callback_type=CallbackType.DELIVERY_STATUS, ).first() def get_service_complaint_callback_api_for_service(service_id): return ServiceCallbackApi.query.filter_by( - service_id=service_id, callback_type=CallbackType.COMPLAINT, + service_id=service_id, + callback_type=CallbackType.COMPLAINT, ).first() diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index a2f960bf7..54b33927c 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -13,7 +13,12 @@ from app.dao.organization_dao import dao_get_organization_by_email_address from app.dao.service_sms_sender_dao import insert_service_sms_sender from app.dao.service_user_dao import dao_get_service_user from app.dao.template_folder_dao import dao_get_valid_template_folders_by_id -from app.enums import NotificationStatus, NotificationType, ServicePermissionType, KeyType +from app.enums import ( + KeyType, + NotificationStatus, + NotificationType, + ServicePermissionType, +) from app.models import ( AnnualBilling, ApiKey, diff --git a/app/dao/uploads_dao.py b/app/dao/uploads_dao.py index 740d0d884..cdbe9d247 100644 --- a/app/dao/uploads_dao.py +++ b/app/dao/uploads_dao.py @@ -5,13 +5,8 @@ from flask import current_app from sqlalchemy import String, and_, desc, func, literal, text from app import db -from app.enums import NotificationStatus, NotificationType, JobStatus -from app.models import ( - Job, - Notification, - ServiceDataRetention, - Template, -) +from app.enums import JobStatus, NotificationStatus, NotificationType +from app.models import Job, Notification, ServiceDataRetention, Template from app.utils import midnight_n_days_ago diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 03d3ed3cc..33c0cd505 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -15,7 +15,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.enums import NotificationStatus, NotificationType, KeyType, BrandType +from app.enums import BrandType, KeyType, NotificationStatus, NotificationType from app.exceptions import NotificationTechnicalFailureException from app.serialised_models import SerialisedService, SerialisedTemplate diff --git a/app/enums.py b/app/enums.py index b842e1e59..dc8ae1200 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,6 +1,4 @@ from enum import Enum -from functools import lru_cache -from xml.sax.handler import property_interning_dict class TemplateType(Enum): @@ -118,9 +116,13 @@ class NotificationStatus(Enum): ) @property - @lru_cache def non_billable(self) -> tuple["NotificationStatus", ...]: - return tuple(set(type(self)) - set(self.billable)) + self._non_billable: tuple["NotificationStatus", ...] + try: + return self._non_billable + except AttributeError: + self._non_billable = tuple(set(type(self)) - set(self.billable)) + return self._non_billable class PermissionType(Enum): diff --git a/app/job/rest.py b/app/job/rest.py index 10d96ae48..4893baa9d 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -21,8 +21,8 @@ from app.dao.notifications_dao import ( ) from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id -from app.errors import InvalidRequest, register_errors from app.enums import JobStatus +from app.errors import InvalidRequest, register_errors from app.schemas import ( job_schema, notification_with_template_schema, diff --git a/app/models.py b/app/models.py index 3def65feb..9cbf1f3c6 100644 --- a/app/models.py +++ b/app/models.py @@ -1001,11 +1001,6 @@ class ApiKey(db.Model, Versioned): self._secret = encryption.encrypt(str(secret)) -class TemplateProcessTypes(db.Model): - __tablename__ = "template_process_type" - name = db.Column(db.String(255), primary_key=True) - - class TemplateFolder(db.Model): __tablename__ = "template_folder" @@ -1129,7 +1124,7 @@ class TemplateBase(db.Model): db.Enum(TemplateProcessType, name="template_process_type"), index=True, nullable=False, - default=NORMAL, + default=TemplateProcessType.NORMAL, ) redact_personalisation = association_proxy( @@ -1338,7 +1333,6 @@ class ProviderDetailsHistory(db.Model, HistoryModel): supports_international = db.Column(db.Boolean, nullable=False, default=False) - class Job(db.Model): __tablename__ = "jobs" diff --git a/app/notifications/process_notifications.py b/app/notifications/process_notifications.py index 114f5b6ae..e28c39bee 100644 --- a/app/notifications/process_notifications.py +++ b/app/notifications/process_notifications.py @@ -16,7 +16,7 @@ from app.dao.notifications_dao import ( dao_create_notification, dao_delete_notifications_by_id, ) -from app.enums import NotificationType, NotificationStatus, KeyType +from app.enums import KeyType, NotificationStatus, NotificationType from app.models import Notification from app.v2.errors import BadRequestError diff --git a/app/notifications/rest.py b/app/notifications/rest.py index cec142e76..a732c3aef 100644 --- a/app/notifications/rest.py +++ b/app/notifications/rest.py @@ -4,7 +4,7 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT from app import api_user, authenticated_service from app.config import QueueNames from app.dao import notifications_dao -from app.enums import NotificationType, KeyType, TemplateProcessType +from app.enums import KeyType, NotificationType, TemplateProcessType from app.errors import InvalidRequest, register_errors from app.notifications.process_notifications import ( persist_notification, @@ -136,7 +136,11 @@ def send_notification(notification_type): reply_to_text=template.reply_to_text, ) if not simulated: - queue_name = QueueNames.PRIORITY if template.process_type == TemplateProcessType.PRIORITY else None + queue_name = ( + QueueNames.PRIORITY + if template.process_type == TemplateProcessType.PRIORITY + else None + ) send_notification_to_queue(notification=notification_model, queue=queue_name) else: diff --git a/app/notifications/validators.py b/app/notifications/validators.py index c8b47e98f..94fca2e02 100644 --- a/app/notifications/validators.py +++ b/app/notifications/validators.py @@ -15,7 +15,7 @@ from app import redis_store from app.dao.notifications_dao import dao_get_notification_count_for_service from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id -from app.enums import NotificationType, ServicePermissionType, TemplateType, KeyType +from app.enums import KeyType, NotificationType, ServicePermissionType, TemplateType from app.models import ServicePermission from app.notifications.process_notifications import create_content_for_notification from app.serialised_models import SerialisedTemplate diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index d95ba7d5d..18874e9a2 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -12,7 +12,7 @@ from app.dao.invited_org_user_dao import ( save_invited_org_user, ) from app.dao.templates_dao import dao_get_template_by_id -from app.enums import NotificationType, KeyType +from app.enums import KeyType, NotificationType from app.errors import InvalidRequest, register_errors from app.models import InvitedOrganizationUser from app.notifications.process_notifications import ( diff --git a/app/service/send_notification.py b/app/service/send_notification.py index d0a9914cb..66bc88358 100644 --- a/app/service/send_notification.py +++ b/app/service/send_notification.py @@ -6,7 +6,7 @@ from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id from app.dao.services_dao import dao_fetch_service_by_id from app.dao.templates_dao import dao_get_template_by_id_and_service_id from app.dao.users_dao import get_user_by_id -from app.enums import NotificationType, KeyType, TemplateProcessType +from app.enums import KeyType, NotificationType, TemplateProcessType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, @@ -80,7 +80,11 @@ def send_one_off_notification(service_id, post_data): client_reference=client_reference, ) - queue_name = QueueNames.PRIORITY if template.process_type == TemplateProcessType.PRIORITY else None + queue_name = ( + QueueNames.PRIORITY + if template.process_type == TemplateProcessType.PRIORITY + else None + ) send_notification_to_queue( notification=notification, diff --git a/app/service/sender.py b/app/service/sender.py index 43240407c..78a759864 100644 --- a/app/service/sender.py +++ b/app/service/sender.py @@ -6,7 +6,7 @@ from app.dao.services_dao import ( dao_fetch_service_by_id, ) from app.dao.templates_dao import dao_get_template_by_id -from app.enums import TemplateType, KeyType +from app.enums import KeyType, TemplateType from app.notifications.process_notifications import ( persist_notification, send_notification_to_queue, diff --git a/app/service/statistics.py b/app/service/statistics.py index fb22982d8..ef538b45f 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -2,7 +2,7 @@ from collections import defaultdict from datetime import datetime from app.dao.date_util import get_months_for_financial_year -from app.enums import TemplateType, NotificationStatus +from app.enums import NotificationStatus, TemplateType def format_statistics(statistics): diff --git a/app/service/utils.py b/app/service/utils.py index 76b88d496..2e22b5309 100644 --- a/app/service/utils.py +++ b/app/service/utils.py @@ -3,7 +3,7 @@ import itertools from notifications_utils.recipients import allowed_to_send_to from app.dao.services_dao import dao_fetch_service_by_id -from app.enums import RecipientType, KeyType +from app.enums import KeyType, RecipientType from app.models import ServiceGuestList diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index ee2eed6d6..90661a99a 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -14,7 +14,7 @@ from app.dao.invited_user_dao import ( save_invited_user, ) from app.dao.templates_dao import dao_get_template_by_id -from app.enums import InvitedUserStatus, NotificationType, KeyType +from app.enums import InvitedUserStatus, KeyType, NotificationType from app.errors import InvalidRequest, register_errors from app.models import Service from app.notifications.process_notifications import ( diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py index 944d35676..d03a83b93 100644 --- a/app/template/template_schemas.py +++ b/app/template/template_schemas.py @@ -1,4 +1,4 @@ -from app.enums import TemplateType, TemplateProcessType +from app.enums import TemplateProcessType, TemplateType from app.schema_validation.definitions import nullable_uuid, uuid post_create_template_schema = { diff --git a/app/user/rest.py b/app/user/rest.py index 33beac4d5..8ea746e4b 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -32,7 +32,7 @@ from app.dao.users_dao import ( update_user_password, use_user_code, ) -from app.enums import CodeType, NotificationType, TemplateType, KeyType +from app.enums import CodeType, KeyType, NotificationType, TemplateType from app.errors import InvalidRequest, register_errors from app.models import Permission, Service from app.notifications.process_notifications import ( diff --git a/app/v2/notifications/notification_schemas.py b/app/v2/notifications/notification_schemas.py index eb0e22eb3..0d737407e 100644 --- a/app/v2/notifications/notification_schemas.py +++ b/app/v2/notifications/notification_schemas.py @@ -1,4 +1,4 @@ -from app.enums import TemplateType, NotificationStatus +from app.enums import NotificationStatus, TemplateType from app.schema_validation.definitions import personalisation, uuid template = { @@ -80,7 +80,10 @@ get_notifications_request = { "type": "object", "properties": { "reference": {"type": "string"}, - "status": {"type": "array", "items": {"enum": [e.value for e in NotificationStatus]}}, + "status": { + "type": "array", + "items": {"enum": [e.value for e in NotificationStatus]}, + }, "template_type": { "type": "array", "items": {"enum": [e.value for e in TemplateType]}, diff --git a/app/v2/notifications/post_notifications.py b/app/v2/notifications/post_notifications.py index b290c6d63..72e8fe46f 100644 --- a/app/v2/notifications/post_notifications.py +++ b/app/v2/notifications/post_notifications.py @@ -10,7 +10,7 @@ from app import api_user, authenticated_service, document_download_client, encry from app.celery.tasks import save_api_email, save_api_sms from app.clients.document_download import DocumentDownloadError from app.config import QueueNames -from app.enums import NotificationType, NotificationStatus, KeyType, TemplateProcessType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateProcessType from app.models import Notification from app.notifications.process_notifications import ( persist_notification, @@ -177,7 +177,11 @@ def process_sms_or_email_notification( ) if not simulated: - queue_name = QueueNames.PRIORITY if template_process_type == TemplateProcessType.PRIORITY else None + queue_name = ( + QueueNames.PRIORITY + if template_process_type == TemplateProcessType.PRIORITY + else None + ) send_notification_to_queue_detached( key_type=api_user.key_type, notification_type=notification_type, From 8c6046b03b9430f985eaeaeb6902aac54bb8ea1a Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 16 Jan 2024 14:46:17 -0500 Subject: [PATCH 147/259] Cleaning up tests. Signed-off-by: Cliff Hill --- app/dao/fact_billing_dao.py | 6 +- app/delivery/send_to_providers.py | 2 +- app/enums.py | 14 ++--- app/models.py | 4 +- tests/__init__.py | 5 +- tests/app/celery/test_reporting_tasks.py | 17 +++--- tests/app/celery/test_scheduled_tasks.py | 45 +++++++------- tests/app/celery/test_test_key_tasks.py | 9 +-- tests/app/conftest.py | 32 ++++------ .../notification_dao/test_notification_dao.py | 59 ++++++++----------- ...t_notification_dao_delete_notifications.py | 12 ++-- tests/app/dao/test_api_key_dao.py | 15 ++--- tests/app/dao/test_fact_billing_dao.py | 5 +- .../dao/test_fact_notification_status_dao.py | 42 +++++-------- tests/app/dao/test_invited_user_dao.py | 15 ++--- tests/app/dao/test_jobs_dao.py | 17 +++--- tests/app/dao/test_services_dao.py | 22 ++++--- tests/app/dao/test_uploads_dao.py | 6 +- tests/app/db.py | 17 +++--- tests/app/delivery/test_send_to_providers.py | 44 ++++++-------- tests/app/email_branding/test_rest.py | 7 ++- tests/app/job/test_rest.py | 8 +-- .../test_process_notification.py | 8 +-- .../test_receive_notification.py | 3 +- tests/app/notifications/test_rest.py | 59 ++++++++++--------- tests/app/notifications/test_validators.py | 9 +-- tests/app/platform_stats/test_rest.py | 2 +- .../public_contracts/test_GET_notification.py | 5 +- tests/app/service/test_api_key_endpoints.py | 7 ++- tests/app/service/test_rest.py | 21 +++---- tests/app/test_commands.py | 8 +-- tests/app/test_model.py | 56 ++++++++++-------- tests/app/test_utils.py | 2 +- 33 files changed, 268 insertions(+), 315 deletions(-) diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index 23ad14b41..01448cb3b 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -408,7 +408,7 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): func.count().label("notifications_sent"), ) .filter( - NotificationAllTimeView.status.in_(NotificationStatus.sent_emails), + NotificationAllTimeView.status.in_(NotificationStatus.sent_email_types), NotificationAllTimeView.key_type.in_((KeyType.NORMAL, KeyType.TEAM)), NotificationAllTimeView.created_at >= start_date, NotificationAllTimeView.created_at < end_date, @@ -440,7 +440,9 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): func.count().label("notifications_sent"), ) .filter( - NotificationAllTimeView.status.in_(NotificationStatus.billable_sms), + NotificationAllTimeView.status.in_( + NotificationStatus.billable_sms_types + ), NotificationAllTimeView.key_type.in_((KeyType.NORMAL, KeyType.TEAM)), NotificationAllTimeView.created_at >= start_date, NotificationAllTimeView.created_at < end_date, diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 33c0cd505..f6725a516 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -155,7 +155,7 @@ def send_email_to_provider(notification): def update_notification_to_sending(notification, provider): notification.sent_at = datetime.utcnow() notification.sent_by = provider.name - if notification.status not in NotificationStatus.completed: + if notification.status not in NotificationStatus.completed_types: notification.status = NotificationStatus.SENDING dao_update_notification(notification) diff --git a/app/enums.py b/app/enums.py index dc8ae1200..e9785b8d2 100644 --- a/app/enums.py +++ b/app/enums.py @@ -52,7 +52,7 @@ class NotificationStatus(Enum): VIRUS_SCAN_FAILED = "virus-scan-failed" @property - def failed(self) -> tuple["NotificationStatus", ...]: + def failed_types(self) -> tuple["NotificationStatus", ...]: cls = type(self) return ( cls.TECHNICAL_FAILURE, @@ -63,7 +63,7 @@ class NotificationStatus(Enum): ) @property - def completed(self) -> tuple["NotificationStatus", ...]: + def completed_types(self) -> tuple["NotificationStatus", ...]: cls = type(self) return ( cls.SENT, @@ -76,12 +76,12 @@ class NotificationStatus(Enum): ) @property - def success(self) -> tuple["NotificationStatus", ...]: + def success_types(self) -> tuple["NotificationStatus", ...]: cls = type(self) return (cls.SENT, cls.DELIVERED) @property - def billable(self) -> tuple["NotificationStatus", ...]: + def billable_types(self) -> tuple["NotificationStatus", ...]: cls = type(self) return ( cls.SENDING, @@ -94,7 +94,7 @@ class NotificationStatus(Enum): ) @property - def billable_sms(self) -> tuple["NotificationStatus", ...]: + def billable_sms_types(self) -> tuple["NotificationStatus", ...]: cls = type(self) return ( cls.SENDING, @@ -106,7 +106,7 @@ class NotificationStatus(Enum): ) @property - def sent_emails(self) -> tuple["NotificationStatus", ...]: + def sent_email_types(self) -> tuple["NotificationStatus", ...]: cls = type(self) return ( cls.SENDING, @@ -116,7 +116,7 @@ class NotificationStatus(Enum): ) @property - def non_billable(self) -> tuple["NotificationStatus", ...]: + def non_billable_types(self) -> tuple["NotificationStatus", ...]: self._non_billable: tuple["NotificationStatus", ...] try: return self._non_billable diff --git a/app/models.py b/app/models.py index 9cbf1f3c6..f870268f2 100644 --- a/app/models.py +++ b/app/models.py @@ -1581,7 +1581,7 @@ class Notification(db.Model): self._personalisation = encryption.encrypt(personalisation or {}) def completed_at(self): - if self.status in NotificationStatus.completed: + if self.status in NotificationStatus.completed_types: return self.updated_at.strftime(DATETIME_FORMAT) return None @@ -1621,7 +1621,7 @@ class Notification(db.Model): def _substitute_status_str(_status): return ( - NotificationStatus.failed + NotificationStatus.failed_types if _status == NotificationStatus.FAILED else [_status] ) diff --git a/tests/__init__.py b/tests/__init__.py index ab21bbc0d..1105a427e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,10 +5,11 @@ from notifications_python_client.authentication import create_jwt_token from app.dao.api_key_dao import save_model_api_key from app.dao.services_dao import dao_fetch_service_by_id -from app.models import KEY_TYPE_NORMAL, ApiKey +from app.enums import KeyType +from app.models import ApiKey -def create_service_authorization_header(service_id, key_type=KEY_TYPE_NORMAL): +def create_service_authorization_header(service_id, key_type=KeyType.NORMAL): client_id = str(service_id) secrets = ApiKey.query.filter_by(service_id=service_id, key_type=key_type).all() diff --git a/tests/app/celery/test_reporting_tasks.py b/tests/app/celery/test_reporting_tasks.py index d4d57da4f..d07fba8f1 100644 --- a/tests/app/celery/test_reporting_tasks.py +++ b/tests/app/celery/test_reporting_tasks.py @@ -13,14 +13,11 @@ from app.celery.reporting_tasks import ( ) from app.config import QueueNames from app.dao.fact_billing_dao import get_rate +from app.enums import NotificationType, KeyType from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, FactBilling, FactNotificationStatus, Notification, - NotificationType, ) from tests.app.db import ( create_notification, @@ -431,12 +428,12 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio # team API key notifications are included create_notification( - template=second_template, status="sending", key_type=KEY_TYPE_TEAM + template=second_template, status="sending", key_type=KeyType.TEAM ) # test notifications are ignored create_notification( - template=second_template, status="sending", key_type=KEY_TYPE_TEST + template=second_template, status="sending", key_type=KeyType.TEST ) # historical notifications are included @@ -471,7 +468,7 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert email_delivered_row.notification_type == "email" assert email_delivered_row.notification_status == "delivered" assert email_delivered_row.notification_count == 1 - assert email_delivered_row.key_type == KEY_TYPE_NORMAL + assert email_delivered_row.key_type == KeyType.NORMAL email_sending_row = new_fact_data[1] assert email_sending_row.template_id == second_template.id @@ -479,7 +476,7 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert email_sending_row.notification_type == "email" assert email_sending_row.notification_status == "failed" assert email_sending_row.notification_count == 1 - assert email_sending_row.key_type == KEY_TYPE_NORMAL + assert email_sending_row.key_type == KeyType.NORMAL email_failure_row = new_fact_data[2] assert email_failure_row.local_date == process_day @@ -489,7 +486,7 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert email_failure_row.notification_type == "email" assert email_failure_row.notification_status == "sending" assert email_failure_row.notification_count == 1 - assert email_failure_row.key_type == KEY_TYPE_TEAM + assert email_failure_row.key_type == KeyType.TEAM sms_delivered_row = new_fact_data[3] assert sms_delivered_row.template_id == first_template.id @@ -497,7 +494,7 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert sms_delivered_row.notification_type == "sms" assert sms_delivered_row.notification_status == "delivered" assert sms_delivered_row.notification_count == 1 - assert sms_delivered_row.key_type == KEY_TYPE_NORMAL + assert sms_delivered_row.key_type == KeyType.NORMAL def test_create_nightly_notification_status_for_service_and_day_overwrites_old_data( diff --git a/tests/app/celery/test_scheduled_tasks.py b/tests/app/celery/test_scheduled_tasks.py index e8f505b6e..e140c58cf 100644 --- a/tests/app/celery/test_scheduled_tasks.py +++ b/tests/app/celery/test_scheduled_tasks.py @@ -19,12 +19,7 @@ from app.celery.scheduled_tasks import ( ) from app.config import QueueNames, Test from app.dao.jobs_dao import dao_get_job_by_id -from app.models import ( - JOB_STATUS_ERROR, - JOB_STATUS_FINISHED, - JOB_STATUS_IN_PROGRESS, - JOB_STATUS_PENDING, -) +from app.enums import JobStatus from tests.app import load_example_csv from tests.app.db import create_job, create_notification, create_template @@ -156,7 +151,7 @@ def test_check_job_status_task_calls_process_incomplete_jobs(mocker, sample_temp notification_count=3, created_at=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) create_notification(template=sample_template, job=job) check_job_status() @@ -174,7 +169,7 @@ def test_check_job_status_task_calls_process_incomplete_jobs_when_scheduled_job_ created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) check_job_status() @@ -190,7 +185,7 @@ def test_check_job_status_task_calls_process_incomplete_jobs_for_pending_schedul notification_count=3, created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_PENDING, + job_status=JobStatus.PENDING, ) check_job_status() @@ -207,7 +202,7 @@ def test_check_job_status_task_does_not_call_process_incomplete_jobs_for_non_sch template=sample_template, notification_count=3, created_at=datetime.utcnow() - timedelta(hours=2), - job_status=JOB_STATUS_PENDING, + job_status=JobStatus.PENDING, ) check_job_status() @@ -224,7 +219,7 @@ def test_check_job_status_task_calls_process_incomplete_jobs_for_multiple_jobs( created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) job_2 = create_job( template=sample_template, @@ -232,7 +227,7 @@ def test_check_job_status_task_calls_process_incomplete_jobs_for_multiple_jobs( created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) check_job_status() @@ -249,21 +244,21 @@ def test_check_job_status_task_only_sends_old_tasks(mocker, sample_template): created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) create_job( template=sample_template, notification_count=3, created_at=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=29), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) create_job( template=sample_template, notification_count=3, created_at=datetime.utcnow() - timedelta(minutes=50), scheduled_for=datetime.utcnow() - timedelta(minutes=29), - job_status=JOB_STATUS_PENDING, + job_status=JobStatus.PENDING, ) check_job_status() @@ -279,21 +274,21 @@ def test_check_job_status_task_sets_jobs_to_error(mocker, sample_template): created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) job_2 = create_job( template=sample_template, notification_count=3, created_at=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=29), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) check_job_status() # job 2 not in celery task mock_celery.assert_called_once_with([[str(job.id)]], queue=QueueNames.JOBS) - assert job.job_status == JOB_STATUS_ERROR - assert job_2.job_status == JOB_STATUS_IN_PROGRESS + assert job.job_status == JobStatus.ERROR + assert job_2.job_status == JobStatus.IN_PROGRESS def test_replay_created_notifications(notify_db_session, sample_service, mocker): @@ -352,14 +347,14 @@ def test_check_job_status_task_does_not_raise_error(sample_template): created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, ) create_job( template=sample_template, notification_count=3, created_at=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, ) check_job_status() @@ -389,7 +384,7 @@ def test_check_for_missing_rows_in_completed_jobs_ignores_old_and_new_jobs( job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - offset, ) for i in range(0, 4): @@ -411,7 +406,7 @@ def test_check_for_missing_rows_in_completed_jobs(mocker, sample_email_template) job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=20), ) for i in range(0, 4): @@ -438,7 +433,7 @@ def test_check_for_missing_rows_in_completed_jobs_calls_save_email( job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=20), ) for i in range(0, 4): @@ -468,7 +463,7 @@ def test_check_for_missing_rows_in_completed_jobs_uses_sender_id( job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=20), ) for i in range(0, 4): diff --git a/tests/app/celery/test_test_key_tasks.py b/tests/app/celery/test_test_key_tasks.py index 8c55dc1cf..9d2b3109c 100644 --- a/tests/app/celery/test_test_key_tasks.py +++ b/tests/app/celery/test_test_key_tasks.py @@ -12,7 +12,8 @@ from app.celery.test_key_tasks import ( sns_callback, ) from app.config import QueueNames -from app.models import NOTIFICATION_DELIVERED, NOTIFICATION_FAILED, Notification +from app.enums import NotificationStatus +from app.models import Notification from tests.conftest import Matcher dvla_response_file_matcher = Matcher( @@ -28,7 +29,7 @@ def test_make_sns_callback(notify_api, rmock, mocker): ) n = Notification() n.id = 1234 - n.status = NOTIFICATION_DELIVERED + n.status = NotificationStatus.DELIVERED get_notification_by_id.return_value = n rmock.request("POST", endpoint, json={"status": "success"}, status_code=200) send_sms_response("sns", "1234") @@ -45,7 +46,7 @@ def test_callback_logs_on_api_call_failure(notify_api, rmock, mocker): ) n = Notification() n.id = 1234 - n.status = NOTIFICATION_FAILED + n.status = NotificationStatus.FAILED get_notification_by_id.return_value = n rmock.request( @@ -81,7 +82,7 @@ def test_delivered_sns_callback(mocker): ) n = Notification() n.id = 1234 - n.status = NOTIFICATION_DELIVERED + n.status = NotificationStatus.DELIVERED get_notification_by_id.return_value = n data = json.loads(sns_callback("1234")) diff --git a/tests/app/conftest.py b/tests/app/conftest.py index e0bc81bdd..eae33fad4 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -17,14 +17,10 @@ from app.dao.organization_dao import dao_create_organization from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template from app.dao.users_dao import create_secret_code, create_user_code +from app.enums import KeyType, NotificationStatus, ServicePermissionType, TemplateType, RecipientType from app.history_meta import create_history from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - NOTIFICATION_STATUS_TYPES_COMPLETED, ApiKey, - GuestListRecipientType, InvitedUser, Job, Notification, @@ -36,10 +32,8 @@ from app.models import ( Service, ServiceEmailReplyTo, ServiceGuestList, - ServicePermissionType, Template, TemplateHistory, - TemplateType, ) from tests import create_admin_authorization_header from tests.app.db import ( @@ -77,7 +71,7 @@ def create_sample_notification( billable_units=1, personalisation=None, api_key=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, sent_by=None, international=False, client_reference=None, @@ -129,7 +123,7 @@ def create_sample_notification( "key_type": api_key.key_type if api_key else key_type, "sent_by": sent_by, "updated_at": created_at - if status in NOTIFICATION_STATUS_TYPES_COMPLETED + if status in NotificationStatus.completed_types else None, "client_reference": client_reference, "rate_multiplier": rate_multiplier, @@ -345,7 +339,7 @@ def sample_api_key(notify_db_session): "service": service, "name": uuid.uuid4(), "created_by": service.created_by, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } api_key = ApiKey(**data) save_model_api_key(api_key) @@ -356,14 +350,14 @@ def sample_api_key(notify_db_session): def sample_test_api_key(sample_api_key): service = create_service(check_if_service_exists=True) - return create_api_key(service, key_type=KEY_TYPE_TEST) + return create_api_key(service, key_type=KeyType.TEST) @pytest.fixture(scope="function") def sample_team_api_key(sample_api_key): service = create_service(check_if_service_exists=True) - return create_api_key(service, key_type=KEY_TYPE_TEAM) + return create_api_key(service, key_type=KeyType.TEAM) @pytest.fixture(scope="function") @@ -426,7 +420,7 @@ def sample_notification_with_job(notify_db_session): billable_units=1, personalisation=None, api_key=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, ) @@ -437,10 +431,10 @@ def sample_notification(notify_db_session): template = create_template(service=service) api_key = ApiKey.query.filter( - ApiKey.service == template.service, ApiKey.key_type == KEY_TYPE_NORMAL + ApiKey.service == template.service, ApiKey.key_type == KeyType.NORMAL ).first() if not api_key: - api_key = create_api_key(template.service, key_type=KEY_TYPE_NORMAL) + api_key = create_api_key(template.service, key_type=KeyType.NORMAL) notification_id = uuid.uuid4() to = "+447700900855" @@ -504,7 +498,7 @@ def sample_email_notification(notify_db_session): "personalisation": None, "notification_type": template.template_type, "api_key_id": None, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, "job_row_number": 1, } notification = Notification(**data) @@ -517,7 +511,7 @@ def sample_notification_history(notify_db_session, sample_template): created_at = datetime.utcnow() sent_at = datetime.utcnow() notification_type = sample_template.template_type - api_key = create_api_key(sample_template.service, key_type=KEY_TYPE_NORMAL) + api_key = create_api_key(sample_template.service, key_type=KeyType.NORMAL) notification_history = NotificationHistory( id=uuid.uuid4(), @@ -527,7 +521,7 @@ def sample_notification_history(notify_db_session, sample_template): status="created", created_at=created_at, notification_type=notification_type, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, api_key=api_key, api_key_id=api_key and api_key.id, sent_at=sent_at, @@ -844,7 +838,7 @@ def notify_service(notify_db_session, sample_user): def sample_service_guest_list(notify_db_session): service = create_service(check_if_service_exists=True) guest_list_user = ServiceGuestList.from_string( - service.id, GuestListRecipientType.EMAIL, "guest_list_user@digital.fake.gov" + service.id, RecipientType.EMAIL, "guest_list_user@digital.fake.gov" ) notify_db_session.add(guest_list_user) diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index bf8005626..5f6bf78f1 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -28,19 +28,8 @@ from app.dao.notifications_dao import ( update_notification_status_by_id, update_notification_status_by_reference, ) -from app.models import ( - JOB_STATUS_IN_PROGRESS, - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - NOTIFICATION_DELIVERED, - NOTIFICATION_SENT, - NOTIFICATION_STATUS_TYPES, - Job, - Notification, - NotificationHistory, - NotificationType, -) +from app.enums import JobStatus, KeyType, NotificationStatus, NotificationType +from app.models import Job, Notification, NotificationHistory from tests.app.db import ( create_ft_notification_status, create_job, @@ -139,13 +128,13 @@ def test_should_not_update_status_by_reference_if_from_country_with_no_delivery_ sample_template, ): notification = create_notification( - sample_template, status=NOTIFICATION_SENT, reference="foo" + sample_template, status=NotificationStatus.SENT, reference="foo" ) res = update_notification_status_by_reference("foo", "failed") assert res is None - assert notification.status == NOTIFICATION_SENT + assert notification.status == NotificationStatus.SENT def test_should_not_update_status_by_id_if_sent_to_country_with_unknown_delivery_receipts( @@ -153,7 +142,7 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_unknown_delivery ): notification = create_notification( sample_template, - status=NOTIFICATION_SENT, + status=NotificationStatus.SENT, international=True, phone_prefix="249", # sudan has no delivery receipts (or at least, that we know about) ) @@ -161,7 +150,7 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_unknown_delivery res = update_notification_status_by_id(notification.id, "delivered") assert res is None - assert notification.status == NOTIFICATION_SENT + assert notification.status == NotificationStatus.SENT def test_should_not_update_status_by_id_if_sent_to_country_with_carrier_delivery_receipts( @@ -169,7 +158,7 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_carrier_delivery ): notification = create_notification( sample_template, - status=NOTIFICATION_SENT, + status=NotificationStatus.SENT, international=True, phone_prefix="1", # americans only have carrier delivery receipts ) @@ -177,7 +166,7 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_carrier_delivery res = update_notification_status_by_id(notification.id, "delivered") assert res is None - assert notification.status == NOTIFICATION_SENT + assert notification.status == NotificationStatus.SENT def test_should_not_update_status_by_id_if_sent_to_country_with_delivery_receipts( @@ -185,7 +174,7 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_delivery_receipt ): notification = create_notification( sample_template, - status=NOTIFICATION_SENT, + status=NotificationStatus.SENT, international=True, phone_prefix="7", # russians have full delivery receipts ) @@ -193,7 +182,7 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_delivery_receipt res = update_notification_status_by_id(notification.id, "delivered") assert res == notification - assert notification.status == NOTIFICATION_DELIVERED + assert notification.status == NotificationStatus.DELIVERED def test_should_not_update_status_by_reference_if_not_sending(sample_template): @@ -540,15 +529,15 @@ def test_get_all_notifications_for_job_by_status(sample_job): get_notifications_for_job, sample_job.service.id, sample_job.id ) - for status in NOTIFICATION_STATUS_TYPES: + for status in NotificationStatus: create_notification(template=sample_job.template, job=sample_job, status=status) - # assert len(notifications().items) == len(NOTIFICATION_STATUS_TYPES) + # assert len(notifications().items) == len(NotificationStatus) assert len(notifications(filter_dict={"status": status}).items) == 1 assert ( - len(notifications(filter_dict={"status": NOTIFICATION_STATUS_TYPES[:3]}).items) + len(notifications(filter_dict={"status": NotificationStatus[:3]}).items) == 3 ) @@ -681,7 +670,7 @@ def _notification_json(sample_template, job_id=None, id=None, status=None): "created_at": datetime.utcnow(), "billable_units": 1, "notification_type": sample_template.template_type, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } if job_id: data.update({"job_id": job_id}) @@ -861,13 +850,13 @@ def test_get_notifications_with_a_live_api_key_type( # only those created with normal API key, no jobs all_notifications = get_notifications_for_service( - sample_job.service.id, limit_days=1, key_type=KEY_TYPE_NORMAL + sample_job.service.id, limit_days=1, key_type=KeyType.NORMAL ).items assert len(all_notifications) == 1 # only those created with normal API key, with jobs all_notifications = get_notifications_for_service( - sample_job.service.id, limit_days=1, include_jobs=True, key_type=KEY_TYPE_NORMAL + sample_job.service.id, limit_days=1, include_jobs=True, key_type=KeyType.NORMAL ).items assert len(all_notifications) == 2 @@ -899,13 +888,13 @@ def test_get_notifications_with_a_test_api_key_type( # only those created with test API key, no jobs all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, key_type=KEY_TYPE_TEST + sample_job.service_id, limit_days=1, key_type=KeyType.TEST ).items assert len(all_notifications) == 1 # only those created with test API key, no jobs, even when requested all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, include_jobs=True, key_type=KEY_TYPE_TEST + sample_job.service_id, limit_days=1, include_jobs=True, key_type=KeyType.TEST ).items assert len(all_notifications) == 1 @@ -937,13 +926,13 @@ def test_get_notifications_with_a_team_api_key_type( # only those created with team API key, no jobs all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, key_type=KEY_TYPE_TEAM + sample_job.service_id, limit_days=1, key_type=KeyType.TEAM ).items assert len(all_notifications) == 1 # only those created with team API key, no jobs, even when requested all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, include_jobs=True, key_type=KEY_TYPE_TEAM + sample_job.service_id, limit_days=1, include_jobs=True, key_type=KeyType.TEAM ).items assert len(all_notifications) == 1 @@ -988,7 +977,7 @@ def test_should_exclude_test_key_notifications_by_default( assert len(all_notifications) == 3 all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, key_type=KEY_TYPE_TEST + sample_job.service_id, limit_days=1, key_type=KeyType.TEST ).items assert len(all_notifications) == 1 @@ -1006,7 +995,7 @@ def test_dao_get_notifications_by_recipient(sample_template): template=sample_template, **recipient_to_search_for ) create_notification( - template=sample_template, key_type=KEY_TYPE_TEST, **recipient_to_search_for + template=sample_template, key_type=KeyType.TEST, **recipient_to_search_for ) create_notification( template=sample_template, @@ -1538,7 +1527,7 @@ def test_dao_get_last_notification_added_for_job_id_valid_job_id(sample_template created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) create_notification(sample_template, job, 0) create_notification(sample_template, job, 1) @@ -1554,7 +1543,7 @@ def test_dao_get_last_notification_added_for_job_id_no_notifications(sample_temp created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) assert dao_get_last_notification_added_for_job_id(job.id) is None diff --git a/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py b/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py index 11d99f205..fd25ac2de 100644 --- a/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py +++ b/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py @@ -7,10 +7,8 @@ from app.dao.notifications_dao import ( insert_notification_history_delete_notifications, move_notifications_to_notification_history, ) +from app.enums import KeyType from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, Notification, NotificationHistory, ) @@ -137,13 +135,13 @@ def test_move_notifications_just_deletes_test_key_notifications(sample_template) delete_time = datetime(2020, 6, 1, 12) one_second_before = delete_time - timedelta(seconds=1) create_notification( - template=sample_template, created_at=one_second_before, key_type=KEY_TYPE_NORMAL + template=sample_template, created_at=one_second_before, key_type=KeyType.NORMAL ) create_notification( - template=sample_template, created_at=one_second_before, key_type=KEY_TYPE_TEAM + template=sample_template, created_at=one_second_before, key_type=KeyType.TEAM ) create_notification( - template=sample_template, created_at=one_second_before, key_type=KEY_TYPE_TEST + template=sample_template, created_at=one_second_before, key_type=KeyType.TEST ) result = move_notifications_to_notification_history( @@ -156,7 +154,7 @@ def test_move_notifications_just_deletes_test_key_notifications(sample_template) assert NotificationHistory.query.count() == 2 assert ( NotificationHistory.query.filter( - NotificationHistory.key_type == KEY_TYPE_TEST + NotificationHistory.key_type == KeyType.TEST ).count() == 0 ) diff --git a/tests/app/dao/test_api_key_dao.py b/tests/app/dao/test_api_key_dao.py index c8dde2a84..3bbe758e3 100644 --- a/tests/app/dao/test_api_key_dao.py +++ b/tests/app/dao/test_api_key_dao.py @@ -11,7 +11,8 @@ from app.dao.api_key_dao import ( get_unsigned_secrets, save_model_api_key, ) -from app.models import KEY_TYPE_NORMAL, ApiKey +from app.enums import KeyType +from app.models import ApiKey def test_save_api_key_should_create_new_api_key_and_history(sample_service): @@ -20,7 +21,7 @@ def test_save_api_key_should_create_new_api_key_and_history(sample_service): "service": sample_service, "name": sample_service.name, "created_by": sample_service.created_by, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } ) save_model_api_key(api_key) @@ -92,7 +93,7 @@ def test_should_not_allow_duplicate_key_names_per_service(sample_api_key, fake_u "service": sample_api_key.service, "name": sample_api_key.name, "created_by": sample_api_key.created_by, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } ) with pytest.raises(IntegrityError): @@ -105,7 +106,7 @@ def test_save_api_key_can_create_key_with_same_name_if_other_is_expired(sample_s "service": sample_service, "name": "normal api key", "created_by": sample_service.created_by, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, "expiry_date": datetime.utcnow(), } ) @@ -115,7 +116,7 @@ def test_save_api_key_can_create_key_with_same_name_if_other_is_expired(sample_s "service": sample_service, "name": "normal api key", "created_by": sample_service.created_by, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } ) save_model_api_key(api_key) @@ -134,7 +135,7 @@ def test_save_api_key_should_not_create_new_service_history(sample_service): "service": sample_service, "name": sample_service.name, "created_by": sample_service.created_by, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } ) save_model_api_key(api_key) @@ -151,7 +152,7 @@ def test_should_not_return_revoked_api_keys_older_than_7_days( "service": sample_service, "name": sample_service.name, "created_by": sample_service.created_by, - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, "expiry_date": datetime.utcnow() - timedelta(days=days_old), } ) diff --git a/tests/app/dao/test_fact_billing_dao.py b/tests/app/dao/test_fact_billing_dao.py index c9bf630ad..90bfb6d69 100644 --- a/tests/app/dao/test_fact_billing_dao.py +++ b/tests/app/dao/test_fact_billing_dao.py @@ -21,7 +21,8 @@ from app.dao.fact_billing_dao import ( query_organization_sms_usage_for_year, ) from app.dao.organization_dao import dao_add_service_to_organization -from app.models import NOTIFICATION_STATUS_TYPES, FactBilling +from app.enums import NotificationStatus +from app.models import FactBilling from tests.app.db import ( create_annual_billing, create_ft_billing, @@ -301,7 +302,7 @@ def test_fetch_billing_data_for_day_bills_correctly_for_status(notify_db_session service = create_service() sms_template = create_template(service=service, template_type="sms") email_template = create_template(service=service, template_type="email") - for status in NOTIFICATION_STATUS_TYPES: + for status in NotificationStatus: create_notification(template=sms_template, status=status) create_notification(template=email_template, status=status) today = datetime.utcnow() diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index bf9772fae..b924574bd 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -17,18 +17,8 @@ from app.dao.fact_notification_status_dao import ( get_total_notifications_for_date_range, update_fact_notification_status, ) +from app.enums import KeyType, NotificationStatus from app.models import ( - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - NOTIFICATION_CREATED, - NOTIFICATION_DELIVERED, - NOTIFICATION_FAILED, - NOTIFICATION_PENDING, - NOTIFICATION_PERMANENT_FAILURE, - NOTIFICATION_SENDING, - NOTIFICATION_SENT, - NOTIFICATION_TECHNICAL_FAILURE, - NOTIFICATION_TEMPORARY_FAILURE, FactNotificationStatus, NotificationType, TemplateType, @@ -63,7 +53,7 @@ def test_fetch_notification_status_for_service_by_month(notify_db_session): create_ft_notification_status(date(2018, 1, 3), "sms", service_2) # not included - test keys create_ft_notification_status( - date(2018, 1, 3), "sms", service_1, key_type=KEY_TYPE_TEST + date(2018, 1, 3), "sms", service_1, key_type=KeyType.TEST ) results = sorted( @@ -118,7 +108,7 @@ def test_fetch_notification_status_for_service_for_day(notify_db_session): create_notification( service_1.templates[0], created_at=datetime(2018, 6, 1, 12, 0, 0), - key_type=KEY_TYPE_TEAM, + key_type=KeyType.TEAM, ) create_notification( service_1.templates[0], @@ -130,7 +120,7 @@ def test_fetch_notification_status_for_service_for_day(notify_db_session): create_notification( service_1.templates[0], created_at=datetime(2018, 6, 1, 12, 0, 0), - key_type=KEY_TYPE_TEST, + key_type=KeyType.TEST, ) # wrong service @@ -638,69 +628,69 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): date(2019, 4, 30), notification_type="sms", service=service_one, - notification_status=NOTIFICATION_DELIVERED, + notification_status=NotificationStatus.DELIVERED, ) create_ft_notification_status( date(2019, 3, 1), notification_type="email", service=service_one, - notification_status=NOTIFICATION_SENDING, + notification_status=NotificationStatus.SENDING, count=4, ) create_ft_notification_status( date(2019, 3, 1), notification_type="email", service=service_one, - notification_status=NOTIFICATION_PENDING, + notification_status=NotificationStatus.PENDING, count=1, ) create_ft_notification_status( date(2019, 3, 2), notification_type="email", service=service_one, - notification_status=NOTIFICATION_TECHNICAL_FAILURE, + notification_status=NotificationStatus.TECHNICAL_FAILURE, count=2, ) create_ft_notification_status( date(2019, 3, 7), notification_type="email", service=service_one, - notification_status=NOTIFICATION_FAILED, + notification_status=NotificationStatus.FAILED, count=1, ) create_ft_notification_status( date(2019, 3, 10), notification_type="sms", service=service_two, - notification_status=NOTIFICATION_PERMANENT_FAILURE, + notification_status=NotificationStatus.PERMANENT_FAILURE, count=1, ) create_ft_notification_status( date(2019, 3, 10), notification_type="sms", service=service_two, - notification_status=NOTIFICATION_PERMANENT_FAILURE, + notification_status=NotificationStatus.PERMANENT_FAILURE, count=1, ) create_ft_notification_status( date(2019, 3, 13), notification_type="sms", service=service_one, - notification_status=NOTIFICATION_SENT, + notification_status=NotificationStatus.SENT, count=1, ) create_ft_notification_status( date(2019, 4, 1), notification_type="sms", service=service_two, - notification_status=NOTIFICATION_TEMPORARY_FAILURE, + notification_status=NotificationStatus.TEMPORARY_FAILURE, count=10, ) create_ft_notification_status( date(2019, 3, 31), notification_type="sms", service=service_one, - notification_status=NOTIFICATION_DELIVERED, + notification_status=NotificationStatus.DELIVERED, ) results = fetch_monthly_notification_statuses_per_service( @@ -784,13 +774,13 @@ def test_fetch_monthly_notification_statuses_per_service_for_rows_that_should_be create_ft_notification_status( date(2019, 3, 15), service=valid_service, - notification_status=NOTIFICATION_CREATED, + notification_status=NotificationStatus.CREATED, ) # notification created by inactive service create_ft_notification_status(date(2019, 3, 15), service=inactive_service) # notification created with test key create_ft_notification_status( - date(2019, 3, 12), service=valid_service, key_type=KEY_TYPE_TEST + date(2019, 3, 12), service=valid_service, key_type=KeyType.TEST ) # notification created by trial mode service create_ft_notification_status(date(2019, 3, 19), service=restricted_service) diff --git a/tests/app/dao/test_invited_user_dao.py b/tests/app/dao/test_invited_user_dao.py index 4242484c7..df29992fc 100644 --- a/tests/app/dao/test_invited_user_dao.py +++ b/tests/app/dao/test_invited_user_dao.py @@ -12,7 +12,8 @@ from app.dao.invited_user_dao import ( get_invited_users_for_service, save_invited_user, ) -from app.models import INVITE_EXPIRED, InvitedUser +from app.enums import InvitedUserStatus +from app.models import InvitedUser from tests.app.db import create_invited_user @@ -122,11 +123,11 @@ def test_should_delete_all_invitations_more_than_one_day_old( make_invitation(sample_user, sample_service, age=timedelta(hours=48)) make_invitation(sample_user, sample_service, age=timedelta(hours=48)) assert ( - len(InvitedUser.query.filter(InvitedUser.status != INVITE_EXPIRED).all()) == 2 + len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 2 ) expire_invitations_created_more_than_two_days_ago() assert ( - len(InvitedUser.query.filter(InvitedUser.status != INVITE_EXPIRED).all()) == 0 + len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 0 ) @@ -149,20 +150,20 @@ def test_should_not_delete_invitations_less_than_two_days_old( ) assert ( - len(InvitedUser.query.filter(InvitedUser.status != INVITE_EXPIRED).all()) == 2 + len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 2 ) expire_invitations_created_more_than_two_days_ago() assert ( - len(InvitedUser.query.filter(InvitedUser.status != INVITE_EXPIRED).all()) == 1 + len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 1 ) assert ( - InvitedUser.query.filter(InvitedUser.status != INVITE_EXPIRED) + InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED) .first() .email_address == "valid@2.com" ) assert ( - InvitedUser.query.filter(InvitedUser.status == INVITE_EXPIRED) + InvitedUser.query.filter(InvitedUser.status == InvitedUserStatus.EXPIRED) .first() .email_address == "expired@1.com" diff --git a/tests/app/dao/test_jobs_dao.py b/tests/app/dao/test_jobs_dao.py index 0f1cc394a..f9eac45f6 100644 --- a/tests/app/dao/test_jobs_dao.py +++ b/tests/app/dao/test_jobs_dao.py @@ -18,7 +18,8 @@ from app.dao.jobs_dao import ( find_jobs_with_missing_rows, find_missing_row_for_job, ) -from app.models import JOB_STATUS_FINISHED, Job, NotificationType, TemplateType +from app.enums import JobStatus +from app.models import Job, NotificationType, TemplateType from tests.app.db import ( create_job, create_notification, @@ -379,7 +380,7 @@ def test_find_jobs_with_missing_rows(sample_email_template): healthy_job = create_job( template=sample_email_template, notification_count=3, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=20), ) for i in range(0, 3): @@ -387,7 +388,7 @@ def test_find_jobs_with_missing_rows(sample_email_template): job_with_missing_rows = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=20), ) for i in range(0, 4): @@ -405,7 +406,7 @@ def test_find_jobs_with_missing_rows_returns_nothing_for_a_job_completed_less_th job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=9), ) for i in range(0, 4): @@ -422,7 +423,7 @@ def test_find_jobs_with_missing_rows_returns_nothing_for_a_job_completed_more_th job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(days=1), ) for i in range(0, 4): @@ -455,7 +456,7 @@ def test_find_missing_row_for_job(sample_email_template): job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=11), ) create_notification(job=job, job_row_number=0) @@ -472,7 +473,7 @@ def test_find_missing_row_for_job_more_than_one_missing_row(sample_email_templat job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=11), ) create_notification(job=job, job_row_number=0) @@ -491,7 +492,7 @@ def test_find_missing_row_for_job_return_none_when_row_isnt_missing( job = create_job( template=sample_email_template, notification_count=5, - job_status=JOB_STATUS_FINISHED, + job_status=JobStatus.FINISHED, processing_finished=datetime.utcnow() - timedelta(minutes=11), ) for i in range(0, 5): diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 641a274a5..cf59d5287 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -40,10 +40,8 @@ from app.dao.services_dao import ( get_services_by_partial_name, ) from app.dao.users_dao import create_user_code, save_model_user +from app.enums import KeyType from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, ApiKey, InvitedUser, Job, @@ -982,9 +980,9 @@ def test_dao_fetch_todays_stats_for_service(notify_db_session): def test_dao_fetch_todays_stats_for_service_should_ignore_test_key(notify_db_session): service = create_service() template = create_template(service=service) - live_api_key = create_api_key(service=service, key_type=KEY_TYPE_NORMAL) - team_api_key = create_api_key(service=service, key_type=KEY_TYPE_TEAM) - test_api_key = create_api_key(service=service, key_type=KEY_TYPE_TEST) + live_api_key = create_api_key(service=service, key_type=KeyType.NORMAL) + team_api_key = create_api_key(service=service, key_type=KeyType.TEAM) + test_api_key = create_api_key(service=service, key_type=KeyType.TEST) # two created email, one failed email, and one created sms create_notification( @@ -1224,9 +1222,9 @@ def test_dao_fetch_todays_stats_for_all_services_includes_all_keys_by_default( notify_db_session, ): template = create_template(service=create_service()) - create_notification(template=template, key_type=KEY_TYPE_NORMAL) - create_notification(template=template, key_type=KEY_TYPE_TEAM) - create_notification(template=template, key_type=KEY_TYPE_TEST) + create_notification(template=template, key_type=KeyType.NORMAL) + create_notification(template=template, key_type=KeyType.TEAM) + create_notification(template=template, key_type=KeyType.TEST) stats = dao_fetch_todays_stats_for_all_services() @@ -1238,9 +1236,9 @@ def test_dao_fetch_todays_stats_for_all_services_can_exclude_from_test_key( notify_db_session, ): template = create_template(service=create_service()) - create_notification(template=template, key_type=KEY_TYPE_NORMAL) - create_notification(template=template, key_type=KEY_TYPE_TEAM) - create_notification(template=template, key_type=KEY_TYPE_TEST) + create_notification(template=template, key_type=KeyType.NORMAL) + create_notification(template=template, key_type=KeyType.TEAM) + create_notification(template=template, key_type=KeyType.TEST) stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=False) diff --git a/tests/app/dao/test_uploads_dao.py b/tests/app/dao/test_uploads_dao.py index d444c38fe..c95b2f000 100644 --- a/tests/app/dao/test_uploads_dao.py +++ b/tests/app/dao/test_uploads_dao.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from freezegun import freeze_time from app.dao.uploads_dao import dao_get_uploads_by_service_id -from app.models import JOB_STATUS_IN_PROGRESS, TemplateType +from app.enums import JobStatus, TemplateType from tests.app.db import ( create_job, create_notification, @@ -89,13 +89,13 @@ def test_get_uploads_orders_by_processing_started_desc(sample_template): sample_template, processing_started=datetime.utcnow() - timedelta(days=1), created_at=days_ago, - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) upload_2 = create_job( sample_template, processing_started=datetime.utcnow() - timedelta(days=2), created_at=days_ago, - job_status=JOB_STATUS_IN_PROGRESS, + job_status=JobStatus.IN_PROGRESS, ) results = dao_get_uploads_by_service_id(service_id=sample_template.service_id).items diff --git a/tests/app/db.py b/tests/app/db.py index b8bcbc539..a5a2c4e58 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -26,8 +26,8 @@ from app.dao.service_sms_sender_dao import ( from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template, dao_update_template from app.dao.users_dao import save_model_user +from app.enums import KeyType, RecipientType, TemplateType, ServicePermissionType from app.models import ( - KEY_TYPE_NORMAL, AnnualBilling, ApiKey, Complaint, @@ -36,7 +36,6 @@ from app.models import ( FactBilling, FactNotificationStatus, FactProcessingTime, - GuestListRecipientType, InboundNumber, InboundSms, InvitedOrganizationUser, @@ -53,11 +52,9 @@ from app.models import ( ServiceGuestList, ServiceInboundApi, ServicePermission, - ServicePermissionType, ServiceSmsSender, Template, TemplateFolder, - TemplateType, User, WebauthnCredential, ) @@ -240,7 +237,7 @@ def create_notification( billable_units=1, personalisation=None, api_key=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, sent_by=None, client_reference=None, rate_multiplier=None, @@ -330,7 +327,7 @@ def create_notification_history( updated_at=None, billable_units=1, api_key=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, sent_by=None, client_reference=None, rate_multiplier=None, @@ -519,7 +516,7 @@ def create_rate(start_date, value, notification_type): return rate -def create_api_key(service, key_type=KEY_TYPE_NORMAL, key_name=None): +def create_api_key(service, key_type=KeyType.NORMAL, key_name=None): id_ = uuid.uuid4() name = key_name if key_name else "{} api key {}".format(key_type, id_) @@ -725,15 +722,15 @@ def create_process_time( def create_service_guest_list(service, email_address=None, mobile_number=None): if email_address: guest_list_user = ServiceGuestList.from_string( - service.id, GuestListRecipientType.EMAIL, email_address + service.id, RecipientType.EMAIL, email_address ) elif mobile_number: guest_list_user = ServiceGuestList.from_string( - service.id, GuestListRecipientType.MOBILE, mobile_number + service.id, RecipientType.MOBILE, mobile_number ) else: guest_list_user = ServiceGuestList.from_string( - service.id, GuestListRecipientType.EMAIL, "guest_list_user@digital.fake.gov" + service.id, RecipientType.EMAIL, "guest_list_user@digital.fake.gov" ) db.session.add(guest_list_user) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index dbae3014b..09dc0f39a 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -14,17 +14,9 @@ from app.dao import notifications_dao from app.dao.provider_details_dao import get_provider_details_by_identifier from app.delivery import send_to_providers from app.delivery.send_to_providers import get_html_email_options, get_logo_url +from app.enums import BrandType, KeyType from app.exceptions import NotificationTechnicalFailureException -from app.models import ( - BRANDING_BOTH, - BRANDING_ORG, - BRANDING_ORG_BANNER, - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - EmailBranding, - Notification, -) +from app.models import EmailBranding, Notification from app.serialised_models import SerialisedService from tests.app.db import ( create_email_branding, @@ -236,7 +228,7 @@ def test_should_have_sending_status_if_fake_callback_function_fails( "app.delivery.send_to_providers.send_sms_response", side_effect=HTTPError ) - sample_notification.key_type = KEY_TYPE_TEST + sample_notification.key_type = KeyType.TEST with pytest.raises(HTTPError): send_to_providers.send_sms_to_provider(sample_notification) assert sample_notification.status == "sending" @@ -357,7 +349,7 @@ def test_get_html_email_renderer_should_return_for_normal_service(sample_service @pytest.mark.parametrize( "branding_type, govuk_banner", - [(BRANDING_ORG, False), (BRANDING_BOTH, True), (BRANDING_ORG_BANNER, False)], + [(BrandType.ORG, False), (BrandType.BOTH, True), (BrandType.ORG_BANNER, False)], ) def test_get_html_email_renderer_with_branding_details( branding_type, govuk_banner, notify_db_session, sample_service @@ -380,7 +372,7 @@ def test_get_html_email_renderer_with_branding_details( assert options["brand_text"] == "League of Justice" assert options["brand_name"] == "Justice League" - if branding_type == BRANDING_ORG_BANNER: + if branding_type == BrandType.ORG_BANNER: assert options["brand_banner"] is True else: assert options["brand_banner"] is False @@ -405,7 +397,7 @@ def test_get_html_email_renderer_prepends_logo_path(notify_api): ) email_branding = EmailBranding( - brand_type=BRANDING_ORG, + brand_type=BrandType.ORG, colour="#000000", logo="justice-league.png", name="Justice League", @@ -429,7 +421,7 @@ def test_get_html_email_renderer_handles_email_branding_without_logo(notify_api) ) email_branding = EmailBranding( - brand_type=BRANDING_ORG_BANNER, + brand_type=BrandType.ORG_BANNER, colour="#000000", logo=None, name="Justice League", @@ -499,19 +491,19 @@ def test_update_notification_to_sending_does_not_update_status_from_a_final_stat def __update_notification(notification_to_update, research_mode, expected_status): - if research_mode or notification_to_update.key_type == KEY_TYPE_TEST: + if research_mode or notification_to_update.key_type == KeyType.TEST: notification_to_update.status = expected_status @pytest.mark.parametrize( "research_mode,key_type, billable_units, expected_status", [ - (True, KEY_TYPE_NORMAL, 0, "delivered"), - (False, KEY_TYPE_NORMAL, 1, "sending"), - (False, KEY_TYPE_TEST, 0, "sending"), - (True, KEY_TYPE_TEST, 0, "sending"), - (True, KEY_TYPE_TEAM, 0, "delivered"), - (False, KEY_TYPE_TEAM, 1, "sending"), + (True, KeyType.NORMAL, 0, "delivered"), + (False, KeyType.NORMAL, 1, "sending"), + (False, KeyType.TEST, 0, "sending"), + (True, KeyType.TEST, 0, "sending"), + (True, KeyType.TEAM, 0, "delivered"), + (False, KeyType.TEAM, 1, "sending"), ], ) def test_should_update_billable_units_and_status_according_to_research_mode_and_key_type( @@ -773,8 +765,8 @@ def test_get_html_email_options_return_email_branding_from_serialised_service( email_options = get_html_email_options(service) assert email_options is not None assert email_options == { - "govuk_banner": branding.brand_type == BRANDING_BOTH, - "brand_banner": branding.brand_type == BRANDING_ORG_BANNER, + "govuk_banner": branding.brand_type == BrandType.BOTH, + "brand_banner": branding.brand_type == BrandType.ORG_BANNER, "brand_colour": branding.colour, "brand_logo": get_logo_url(current_app.config["ADMIN_BASE_URL"], branding.logo), "brand_text": branding.text, @@ -788,8 +780,8 @@ def test_get_html_email_options_add_email_branding_from_service(sample_service): email_options = get_html_email_options(sample_service) assert email_options is not None assert email_options == { - "govuk_banner": branding.brand_type == BRANDING_BOTH, - "brand_banner": branding.brand_type == BRANDING_ORG_BANNER, + "govuk_banner": branding.brand_type == BrandType.BOTH, + "brand_banner": branding.brand_type == BrandType.ORG_BANNER, "brand_colour": branding.colour, "brand_logo": get_logo_url(current_app.config["ADMIN_BASE_URL"], branding.logo), "brand_text": branding.text, diff --git a/tests/app/email_branding/test_rest.py b/tests/app/email_branding/test_rest.py index 97dc28a8a..0b68e9a4d 100644 --- a/tests/app/email_branding/test_rest.py +++ b/tests/app/email_branding/test_rest.py @@ -1,6 +1,7 @@ import pytest -from app.models import BRANDING_ORG, EmailBranding +from app.enums import BrandType +from app.models import EmailBranding from tests.app.db import create_email_branding @@ -59,7 +60,7 @@ def test_post_create_email_branding(admin_request, notify_db_session): "name": "test email_branding", "colour": "#0000ff", "logo": "/images/test_x2.png", - "brand_type": BRANDING_ORG, + "brand_type": BrandType.ORG, } response = admin_request.post( "email_branding.create_email_branding", _data=data, _expected_status=201 @@ -82,7 +83,7 @@ def test_post_create_email_branding_without_brand_type_defaults( response = admin_request.post( "email_branding.create_email_branding", _data=data, _expected_status=201 ) - assert BRANDING_ORG == response["data"]["brand_type"] + assert BrandType.ORG == response["data"]["brand_type"] def test_post_create_email_branding_without_logo_is_ok( diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index c48ef89d8..3babf13e8 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -9,7 +9,7 @@ from freezegun import freeze_time import app.celery.tasks from app.dao.templates_dao import dao_update_template -from app.models import JOB_STATUS_PENDING, JOB_STATUS_TYPES +from app.enums import JobStatus from tests import create_admin_authorization_header from tests.app.db import ( create_ft_notification_status, @@ -782,11 +782,11 @@ def test_get_jobs_accepts_page_parameter(admin_request, sample_template): @pytest.mark.parametrize( "statuses_filter, expected_statuses", [ - ("", JOB_STATUS_TYPES), - ("pending", [JOB_STATUS_PENDING]), + ("", JobStatus.TYPES), + ("pending", [JobStatus.PENDING]), ( "pending, in progress, finished, sending limits exceeded, scheduled, cancelled, ready to send, sent to dvla, error", # noqa - JOB_STATUS_TYPES, + JobStatus.TYPES, ), # bad statuses are accepted, just return no data ("foo", []), diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index fde2291f8..71f8cb419 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -11,12 +11,8 @@ from notifications_utils.recipients import ( ) from sqlalchemy.exc import SQLAlchemyError -from app.models import ( - Notification, - NotificationHistory, - ServicePermissionType, - TemplateType, -) +from app.enums import ServicePermissionType, TemplateType +from app.models import Notification, NotificationHistory from app.notifications.process_notifications import ( create_content_for_notification, persist_notification, diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index 4e12e5536..6718227fd 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -5,7 +5,8 @@ from unittest import mock import pytest from flask import json -from app.models import InboundSms, ServicePermissionType +from app.models import InboundSms +from app.enums import ServicePermissionType from app.notifications.receive_notifications import ( create_inbound_sms_object, fetch_potential_service, diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index c12132c58..b349c193f 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -8,7 +8,8 @@ from notifications_python_client.authentication import create_jwt_token from app.dao.api_key_dao import save_model_api_key from app.dao.notifications_dao import dao_update_notification from app.dao.templates_dao import dao_update_template -from app.models import KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, ApiKey +from app.enums import KeyType +from app.models import ApiKey from tests import create_service_authorization_header from tests.app.db import create_api_key, create_notification @@ -80,12 +81,12 @@ def test_get_notifications_empty_result(client, sample_api_key): @pytest.mark.parametrize( "api_key_type,notification_key_type", [ - (KEY_TYPE_NORMAL, KEY_TYPE_TEAM), - (KEY_TYPE_NORMAL, KEY_TYPE_TEST), - (KEY_TYPE_TEST, KEY_TYPE_NORMAL), - (KEY_TYPE_TEST, KEY_TYPE_TEAM), - (KEY_TYPE_TEAM, KEY_TYPE_NORMAL), - (KEY_TYPE_TEAM, KEY_TYPE_TEST), + (KeyType.NORMAL, KeyType.TEAM), + (KeyType.NORMAL, KeyType.TEST), + (KeyType.TEST, KeyType.NORMAL), + (KeyType.TEST, KeyType.TEAM), + (KeyType.TEAM, KeyType.NORMAL), + (KeyType.TEAM, KeyType.TEST), ], ) def test_get_notification_from_different_api_key_works( @@ -107,7 +108,7 @@ def test_get_notification_from_different_api_key_works( assert response.status_code == 200 -@pytest.mark.parametrize("key_type", [KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST]) +@pytest.mark.parametrize("key_type", [KeyType.NORMAL, KeyType.TEAM, KeyType.TEST]) def test_get_notification_from_different_api_key_of_same_type_succeeds( client, sample_notification, key_type ): @@ -190,7 +191,7 @@ def test_normal_api_key_returns_notifications_created_from_jobs_and_from_api( } -@pytest.mark.parametrize("key_type", [KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST]) +@pytest.mark.parametrize("key_type", [KeyType.NORMAL, KeyType.TEAM, KeyType.TEST]) def test_get_all_notifications_only_returns_notifications_of_matching_type( client, sample_template, @@ -200,19 +201,19 @@ def test_get_all_notifications_only_returns_notifications_of_matching_type( key_type, ): normal_notification = create_notification( - sample_template, api_key=sample_api_key, key_type=KEY_TYPE_NORMAL + sample_template, api_key=sample_api_key, key_type=KeyType.NORMAL ) team_notification = create_notification( - sample_template, api_key=sample_team_api_key, key_type=KEY_TYPE_TEAM + sample_template, api_key=sample_team_api_key, key_type=KeyType.TEAM ) test_notification = create_notification( - sample_template, api_key=sample_test_api_key, key_type=KEY_TYPE_TEST + sample_template, api_key=sample_test_api_key, key_type=KeyType.TEST ) notification_objs = { - KEY_TYPE_NORMAL: normal_notification, - KEY_TYPE_TEAM: team_notification, - KEY_TYPE_TEST: test_notification, + KeyType.NORMAL: normal_notification, + KeyType.TEAM: team_notification, + KeyType.TEST: test_notification, } response = client.get( @@ -227,13 +228,13 @@ def test_get_all_notifications_only_returns_notifications_of_matching_type( assert notifications[0]["id"] == str(notification_objs[key_type].id) -@pytest.mark.parametrize("key_type", [KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST]) +@pytest.mark.parametrize("key_type", [KeyType.NORMAL, KeyType.TEAM, KeyType.TEST]) def test_do_not_return_job_notifications_by_default( client, sample_template, sample_job, key_type ): - team_api_key = create_api_key(sample_template.service, KEY_TYPE_TEAM) - normal_api_key = create_api_key(sample_template.service, KEY_TYPE_NORMAL) - test_api_key = create_api_key(sample_template.service, KEY_TYPE_TEST) + team_api_key = create_api_key(sample_template.service, KeyType.TEAM) + normal_api_key = create_api_key(sample_template.service, KeyType.NORMAL) + test_api_key = create_api_key(sample_template.service, KeyType.TEST) create_notification(sample_template, job=sample_job) normal_notification = create_notification(sample_template, api_key=normal_api_key) @@ -241,9 +242,9 @@ def test_do_not_return_job_notifications_by_default( test_notification = create_notification(sample_template, api_key=test_api_key) notification_objs = { - KEY_TYPE_NORMAL: normal_notification, - KEY_TYPE_TEAM: team_notification, - KEY_TYPE_TEST: test_notification, + KeyType.NORMAL: normal_notification, + KeyType.TEAM: team_notification, + KeyType.TEST: test_notification, } response = client.get( @@ -259,7 +260,7 @@ def test_do_not_return_job_notifications_by_default( @pytest.mark.parametrize( - "key_type", [(KEY_TYPE_NORMAL, 2), (KEY_TYPE_TEAM, 1), (KEY_TYPE_TEST, 1)] + "key_type", [(KeyType.NORMAL, 2), (KeyType.TEAM, 1), (KeyType.TEST, 1)] ) def test_only_normal_api_keys_can_return_job_notifications( client, @@ -271,19 +272,19 @@ def test_only_normal_api_keys_can_return_job_notifications( key_type, ): normal_notification = create_notification( - template=sample_template, api_key=sample_api_key, key_type=KEY_TYPE_NORMAL + template=sample_template, api_key=sample_api_key, key_type=KeyType.NORMAL ) team_notification = create_notification( - template=sample_template, api_key=sample_team_api_key, key_type=KEY_TYPE_TEAM + template=sample_template, api_key=sample_team_api_key, key_type=KeyType.TEAM ) test_notification = create_notification( - template=sample_template, api_key=sample_test_api_key, key_type=KEY_TYPE_TEST + template=sample_template, api_key=sample_test_api_key, key_type=KeyType.TEST ) notification_objs = { - KEY_TYPE_NORMAL: normal_notification, - KEY_TYPE_TEAM: team_notification, - KEY_TYPE_TEST: test_notification, + KeyType.NORMAL: normal_notification, + KeyType.TEAM: team_notification, + KeyType.TEST: test_notification, } response = client.get( diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index 8eeb858c0..bc49f5ecb 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -5,12 +5,7 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT import app from app.dao import templates_dao -from app.models import ( - KEY_TYPE_NORMAL, - NotificationType, - ServicePermissionType, - TemplateType, -) +from app.enums import KeyType, NotificationType, ServicePermissionType, TemplateType from app.notifications.process_notifications import create_content_for_notification from app.notifications.sns_cert_validator import ( VALID_SNS_TOPICS, @@ -783,6 +778,6 @@ def test_check_service_over_total_message_limit(mocker, sample_service): get_redis_mock = mocker.patch("app.notifications.validators.redis_store.get") get_redis_mock.return_value = None service_stats = check_service_over_total_message_limit( - KEY_TYPE_NORMAL, sample_service + KeyType.NORMAL, sample_service ) assert service_stats == 0 diff --git a/tests/app/platform_stats/test_rest.py b/tests/app/platform_stats/test_rest.py index 9c5e3ece8..790bae5d0 100644 --- a/tests/app/platform_stats/test_rest.py +++ b/tests/app/platform_stats/test_rest.py @@ -4,7 +4,7 @@ import pytest from freezegun import freeze_time from app.errors import InvalidRequest -from app.models import TemplateType +from app.enums import TemplateType from app.platform_stats.rest import validate_date_range_is_within_a_financial_year from tests.app.db import ( create_ft_billing, diff --git a/tests/app/public_contracts/test_GET_notification.py b/tests/app/public_contracts/test_GET_notification.py index ff23762f0..ddaaff9e2 100644 --- a/tests/app/public_contracts/test_GET_notification.py +++ b/tests/app/public_contracts/test_GET_notification.py @@ -1,7 +1,8 @@ import pytest from app.dao.api_key_dao import save_model_api_key -from app.models import KEY_TYPE_NORMAL, ApiKey +from app.enums import KeyType +from app.models import ApiKey from app.v2.notifications.notification_schemas import ( get_notification_response, get_notifications_response, @@ -17,7 +18,7 @@ def _get_notification(client, notification, url): service=notification.service, name="api_key", created_by=notification.service.created_by, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, ) ) auth_header = create_service_authorization_header( diff --git a/tests/app/service/test_api_key_endpoints.py b/tests/app/service/test_api_key_endpoints.py index 01c6d1f18..8ca0e374d 100644 --- a/tests/app/service/test_api_key_endpoints.py +++ b/tests/app/service/test_api_key_endpoints.py @@ -3,7 +3,8 @@ import json from flask import url_for from app.dao.api_key_dao import expire_api_key -from app.models import KEY_TYPE_NORMAL, ApiKey +from app.enums import KeyType +from app.models import ApiKey from tests import create_admin_authorization_header from tests.app.db import create_api_key, create_service, create_user @@ -14,7 +15,7 @@ def test_api_key_should_create_new_api_key_for_service(notify_api, sample_servic data = { "name": "some secret name", "created_by": str(sample_service.created_by.id), - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } auth_header = create_admin_authorization_header() response = client.post( @@ -86,7 +87,7 @@ def test_api_key_should_create_multiple_new_api_key_for_service( data = { "name": "some secret name", "created_by": str(sample_service.created_by.id), - "key_type": KEY_TYPE_NORMAL, + "key_type": KeyType.NORMAL, } auth_header = create_admin_authorization_header() response = client.post( diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 9b3fd378a..5bad34814 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -14,22 +14,17 @@ from app.dao.service_user_dao import dao_get_service_user from app.dao.services_dao import dao_add_user_to_service, dao_remove_user_from_service from app.dao.templates_dao import dao_redact_template from app.dao.users_dao import save_model_user +from app.enums import KeyType, ServicePermissionType, TemplateType, NotificationType from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, AnnualBilling, EmailBranding, InboundNumber, Notification, - NotificationType, Permission, Service, ServiceEmailReplyTo, ServicePermission, - ServicePermissionType, ServiceSmsSender, - TemplateType, User, ) from tests import create_admin_authorization_header @@ -1882,7 +1877,7 @@ def test_get_all_notifications_for_service_including_ones_made_by_jobs( mock_s3.return_value = "1" # notification from_test_api_key - create_notification(sample_template, key_type=KEY_TYPE_TEST) + create_notification(sample_template, key_type=KeyType.TEST) auth_header = create_admin_authorization_header() @@ -2081,7 +2076,7 @@ def test_get_services_with_detailed_flag(client, sample_template): notifications = [ create_notification(sample_template), create_notification(sample_template), - create_notification(sample_template, key_type=KEY_TYPE_TEST), + create_notification(sample_template, key_type=KeyType.TEST), ] resp = client.get( "/service?detailed=True", headers=[create_admin_authorization_header()] @@ -2101,11 +2096,11 @@ def test_get_services_with_detailed_flag(client, sample_template): def test_get_services_with_detailed_flag_excluding_from_test_key( client, sample_template ): - create_notification(sample_template, key_type=KEY_TYPE_NORMAL) - create_notification(sample_template, key_type=KEY_TYPE_TEAM) - create_notification(sample_template, key_type=KEY_TYPE_TEST) - create_notification(sample_template, key_type=KEY_TYPE_TEST) - create_notification(sample_template, key_type=KEY_TYPE_TEST) + create_notification(sample_template, key_type=KeyType.NORMAL) + create_notification(sample_template, key_type=KeyType.TEAM) + create_notification(sample_template, key_type=KeyType.TEST) + create_notification(sample_template, key_type=KeyType.TEST) + create_notification(sample_template, key_type=KeyType.TEST) resp = client.get( "/service?detailed=True&include_from_test_key=False", diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index 45d836105..858039ee0 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -20,13 +20,11 @@ from app.commands import ( ) from app.dao.inbound_numbers_dao import dao_get_available_inbound_numbers from app.dao.users_dao import get_user_by_email +from app.enums import KeyType, NotificationStatus, NotificationType from app.models import ( - KEY_TYPE_NORMAL, - NOTIFICATION_DELIVERED, AnnualBilling, Job, Notification, - NotificationType, Organization, Service, Template, @@ -315,9 +313,9 @@ def test_fix_billable_units(notify_db_session, notify_api, sample_template): notification = Notification.query.one() notification.billable_units = 0 notification.notification_type = NotificationType.SMS - notification.status = NOTIFICATION_DELIVERED + notification.status = NotificationStatus.DELIVERED notification.sent_at = None - notification.key_type = KEY_TYPE_NORMAL + notification.key_type = KeyType.NORMAL notify_db_session.commit() diff --git a/tests/app/test_model.py b/tests/app/test_model.py index f893ab14c..11f9df493 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -4,24 +4,23 @@ import pytest from freezegun import freeze_time from sqlalchemy.exc import IntegrityError -from app import encryption -from app.models import ( - NOTIFICATION_CREATED, - NOTIFICATION_FAILED, - NOTIFICATION_PENDING, - NOTIFICATION_STATUS_TYPES_FAILED, - NOTIFICATION_TECHNICAL_FAILURE, - Agreement, +from app.enums import ( + NotificationStatus, + RecipientType, + TemplateType, AgreementStatus, AgreementType, +) + +from app import encryption +from app.models import ( + Agreement, AnnualBilling, - GuestListRecipientType, Notification, NotificationHistory, Service, ServiceGuestList, ServicePermission, - TemplateType, User, VerifyCode, filter_null_value_fields, @@ -42,7 +41,7 @@ from tests.app.db import ( @pytest.mark.parametrize("mobile_number", ["+447700900855", "+12348675309"]) def test_should_build_service_guest_list_from_mobile_number(mobile_number): service_guest_list = ServiceGuestList.from_string( - "service_id", GuestListRecipientType.MOBILE, mobile_number + "service_id", RecipientType.MOBILE, mobile_number ) assert service_guest_list.recipient == mobile_number @@ -76,30 +75,37 @@ def test_should_not_build_service_guest_list_from_invalid_contact( "initial_statuses, expected_statuses", [ # passing in single statuses as strings - (NOTIFICATION_FAILED, NOTIFICATION_STATUS_TYPES_FAILED), - (NOTIFICATION_CREATED, [NOTIFICATION_CREATED]), - (NOTIFICATION_TECHNICAL_FAILURE, [NOTIFICATION_TECHNICAL_FAILURE]), + (NotificationStatus.FAILED, NotificationStatus.failed_types), + (NotificationStatus.CREATED, [NotificationStatus.CREATED]), + (NotificationStatus.TECHNICAL_FAILURE, [NotificationStatus.TECHNICAL_FAILURE]), # passing in lists containing single statuses - ([NOTIFICATION_FAILED], NOTIFICATION_STATUS_TYPES_FAILED), - ([NOTIFICATION_CREATED], [NOTIFICATION_CREATED]), - ([NOTIFICATION_TECHNICAL_FAILURE], [NOTIFICATION_TECHNICAL_FAILURE]), + ([NotificationStatus.FAILED], NotificationStatus.failed_types), + ([NotificationStatus.CREATED], [NotificationStatus.CREATED]), + ( + [NotificationStatus.TECHNICAL_FAILURE], + [NotificationStatus.TECHNICAL_FAILURE], + ), # passing in lists containing multiple statuses ( - [NOTIFICATION_FAILED, NOTIFICATION_CREATED], - NOTIFICATION_STATUS_TYPES_FAILED + [NOTIFICATION_CREATED], + [NotificationStatus.FAILED, NotificationStatus.CREATED], + list(NotificationStatus.failed_types) + [NotificationStatus.CREATED], ), ( - [NOTIFICATION_CREATED, NOTIFICATION_PENDING], - [NOTIFICATION_CREATED, NOTIFICATION_PENDING], + [NotificationStatus.CREATED, NotificationStatus.PENDING], + [NotificationStatus.CREATED, NotificationStatus.PENDING], ), ( - [NOTIFICATION_CREATED, NOTIFICATION_TECHNICAL_FAILURE], - [NOTIFICATION_CREATED, NOTIFICATION_TECHNICAL_FAILURE], + [NotificationStatus.CREATED, NotificationStatus.TECHNICAL_FAILURE], + [NotificationStatus.CREATED, NotificationStatus.TECHNICAL_FAILURE], ), # checking we don't end up with duplicates ( - [NOTIFICATION_FAILED, NOTIFICATION_CREATED, NOTIFICATION_TECHNICAL_FAILURE], - NOTIFICATION_STATUS_TYPES_FAILED + [NOTIFICATION_CREATED], + [ + NotificationStatus.FAILED, + NotificationStatus.CREATED, + NotificationStatus.TECHNICAL_FAILURE, + ], + list(NotificationStatus.failed_types) + [NotificationStatus.CREATED], ), ], ) diff --git a/tests/app/test_utils.py b/tests/app/test_utils.py index c78620e7f..b39d3a2c0 100644 --- a/tests/app/test_utils.py +++ b/tests/app/test_utils.py @@ -4,7 +4,7 @@ from datetime import date, datetime import pytest from freezegun import freeze_time -from app.models import ServicePermissionType +from app.enums import ServicePermissionType from app.utils import ( format_sequential_number, get_midnight_for_day_before, From 26bc6198f8985d050790788d1afb229c89b20c15 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 16 Jan 2024 15:12:57 -0500 Subject: [PATCH 148/259] Cleaning up tests. Signed-off-by: Cliff Hill --- tests/app/celery/test_reporting_tasks.py | 8 +-- tests/app/conftest.py | 8 ++- .../notification_dao/test_notification_dao.py | 5 +- ...t_notification_dao_delete_notifications.py | 5 +- .../dao/test_fact_notification_status_dao.py | 6 +- tests/app/dao/test_invited_user_dao.py | 28 ++++++-- tests/app/db.py | 2 +- .../test_receive_notification.py | 2 +- tests/app/platform_stats/test_rest.py | 2 +- .../test_send_notification.py | 35 ++++----- .../test_send_one_off_notification.py | 19 +++-- tests/app/service/test_rest.py | 2 +- tests/app/service/test_sender.py | 3 +- tests/app/service/test_service_guest_list.py | 9 +-- tests/app/service/test_statistics_rest.py | 14 ++-- .../test_service_invite_rest.py | 11 +-- tests/app/template/test_rest.py | 3 +- tests/app/test_model.py | 13 ++-- tests/app/user/test_rest.py | 71 ++++++++++--------- tests/app/user/test_rest_verify.py | 13 ++-- .../test_notification_schemas.py | 4 +- tests/app/v2/template/test_get_template.py | 2 +- .../app/v2/template/test_template_schemas.py | 2 +- .../v2/templates/test_templates_schemas.py | 2 +- 24 files changed, 136 insertions(+), 133 deletions(-) diff --git a/tests/app/celery/test_reporting_tasks.py b/tests/app/celery/test_reporting_tasks.py index d07fba8f1..7045ce1a7 100644 --- a/tests/app/celery/test_reporting_tasks.py +++ b/tests/app/celery/test_reporting_tasks.py @@ -13,12 +13,8 @@ from app.celery.reporting_tasks import ( ) from app.config import QueueNames from app.dao.fact_billing_dao import get_rate -from app.enums import NotificationType, KeyType -from app.models import ( - FactBilling, - FactNotificationStatus, - Notification, -) +from app.enums import KeyType, NotificationType +from app.models import FactBilling, FactNotificationStatus, Notification from tests.app.db import ( create_notification, create_notification_history, diff --git a/tests/app/conftest.py b/tests/app/conftest.py index eae33fad4..d579563c0 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -17,7 +17,13 @@ from app.dao.organization_dao import dao_create_organization from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template from app.dao.users_dao import create_secret_code, create_user_code -from app.enums import KeyType, NotificationStatus, ServicePermissionType, TemplateType, RecipientType +from app.enums import ( + KeyType, + NotificationStatus, + RecipientType, + ServicePermissionType, + TemplateType, +) from app.history_meta import create_history from app.models import ( ApiKey, diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index 5f6bf78f1..b45940ac6 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -536,10 +536,7 @@ def test_get_all_notifications_for_job_by_status(sample_job): assert len(notifications(filter_dict={"status": status}).items) == 1 - assert ( - len(notifications(filter_dict={"status": NotificationStatus[:3]}).items) - == 3 - ) + assert len(notifications(filter_dict={"status": NotificationStatus[:3]}).items) == 3 def test_dao_get_notification_count_for_job_id(notify_db_session): diff --git a/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py b/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py index fd25ac2de..807996748 100644 --- a/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py +++ b/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py @@ -8,10 +8,7 @@ from app.dao.notifications_dao import ( move_notifications_to_notification_history, ) from app.enums import KeyType -from app.models import ( - Notification, - NotificationHistory, -) +from app.models import Notification, NotificationHistory from tests.app.db import ( create_notification, create_notification_history, diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index b924574bd..79d3eba9d 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -18,11 +18,7 @@ from app.dao.fact_notification_status_dao import ( update_fact_notification_status, ) from app.enums import KeyType, NotificationStatus -from app.models import ( - FactNotificationStatus, - NotificationType, - TemplateType, -) +from app.models import FactNotificationStatus, NotificationType, TemplateType from tests.app.db import ( create_ft_notification_status, create_job, diff --git a/tests/app/dao/test_invited_user_dao.py b/tests/app/dao/test_invited_user_dao.py index df29992fc..de595c415 100644 --- a/tests/app/dao/test_invited_user_dao.py +++ b/tests/app/dao/test_invited_user_dao.py @@ -123,11 +123,21 @@ def test_should_delete_all_invitations_more_than_one_day_old( make_invitation(sample_user, sample_service, age=timedelta(hours=48)) make_invitation(sample_user, sample_service, age=timedelta(hours=48)) assert ( - len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 2 + len( + InvitedUser.query.filter( + InvitedUser.status != InvitedUserStatus.EXPIRED + ).all() + ) + == 2 ) expire_invitations_created_more_than_two_days_ago() assert ( - len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 0 + len( + InvitedUser.query.filter( + InvitedUser.status != InvitedUserStatus.EXPIRED + ).all() + ) + == 0 ) @@ -150,11 +160,21 @@ def test_should_not_delete_invitations_less_than_two_days_old( ) assert ( - len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 2 + len( + InvitedUser.query.filter( + InvitedUser.status != InvitedUserStatus.EXPIRED + ).all() + ) + == 2 ) expire_invitations_created_more_than_two_days_ago() assert ( - len(InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED).all()) == 1 + len( + InvitedUser.query.filter( + InvitedUser.status != InvitedUserStatus.EXPIRED + ).all() + ) + == 1 ) assert ( InvitedUser.query.filter(InvitedUser.status != InvitedUserStatus.EXPIRED) diff --git a/tests/app/db.py b/tests/app/db.py index a5a2c4e58..7f18de861 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -26,7 +26,7 @@ from app.dao.service_sms_sender_dao import ( from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template, dao_update_template from app.dao.users_dao import save_model_user -from app.enums import KeyType, RecipientType, TemplateType, ServicePermissionType +from app.enums import KeyType, RecipientType, ServicePermissionType, TemplateType from app.models import ( AnnualBilling, ApiKey, diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index 6718227fd..c95088803 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -5,8 +5,8 @@ from unittest import mock import pytest from flask import json -from app.models import InboundSms from app.enums import ServicePermissionType +from app.models import InboundSms from app.notifications.receive_notifications import ( create_inbound_sms_object, fetch_potential_service, diff --git a/tests/app/platform_stats/test_rest.py b/tests/app/platform_stats/test_rest.py index 790bae5d0..88ed93564 100644 --- a/tests/app/platform_stats/test_rest.py +++ b/tests/app/platform_stats/test_rest.py @@ -3,8 +3,8 @@ from datetime import date, datetime import pytest from freezegun import freeze_time -from app.errors import InvalidRequest from app.enums import TemplateType +from app.errors import InvalidRequest from app.platform_stats.rest import validate_date_range_is_within_a_financial_year from tests.app.db import ( create_ft_billing, diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index c3468a2c5..74db646a0 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -12,18 +12,9 @@ from app.dao import notifications_dao from app.dao.api_key_dao import save_model_api_key from app.dao.services_dao import dao_update_service from app.dao.templates_dao import dao_get_all_templates_for_service, dao_update_template +from app.enums import KeyType, NotificationType, TemplateType from app.errors import InvalidRequest -from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - ApiKey, - Notification, - NotificationHistory, - NotificationType, - Template, - TemplateType, -) +from app.models import ApiKey, Notification, NotificationHistory, Template from app.service.send_notification import send_one_off_notification from app.v2.errors import RateLimitError from tests import create_service_authorization_header @@ -557,7 +548,7 @@ def test_should_not_send_email_if_team_api_key_and_not_a_service_user( } auth_header = create_service_authorization_header( - service_id=sample_email_template.service_id, key_type=KEY_TYPE_TEAM + service_id=sample_email_template.service_id, key_type=KeyType.TEAM ) response = client.post( @@ -587,7 +578,7 @@ def test_should_not_send_sms_if_team_api_key_and_not_a_service_user( } auth_header = create_service_authorization_header( - service_id=sample_template.service_id, key_type=KEY_TYPE_TEAM + service_id=sample_template.service_id, key_type=KeyType.TEAM ) response = client.post( @@ -618,7 +609,7 @@ def test_should_send_email_if_team_api_key_and_a_service_user( "template": sample_email_template.id, } auth_header = create_service_authorization_header( - service_id=sample_email_template.service_id, key_type=KEY_TYPE_TEAM + service_id=sample_email_template.service_id, key_type=KeyType.TEAM ) response = client.post( @@ -650,7 +641,7 @@ def test_should_send_sms_to_anyone_with_test_key( service=sample_template.service, name="test_key", created_by=sample_template.created_by, - key_type=KEY_TYPE_TEST, + key_type=KeyType.TEST, ) save_model_api_key(api_key) auth_header = create_jwt_token( @@ -688,7 +679,7 @@ def test_should_send_email_to_anyone_with_test_key( service=sample_email_template.service, name="test_key", created_by=sample_email_template.created_by, - key_type=KEY_TYPE_TEST, + key_type=KeyType.TEST, ) save_model_api_key(api_key) auth_header = create_jwt_token( @@ -726,7 +717,7 @@ def test_should_send_sms_if_team_api_key_and_a_service_user( service=sample_template.service, name="team_key", created_by=sample_template.created_by, - key_type=KEY_TYPE_TEAM, + key_type=KeyType.TEAM, ) save_model_api_key(api_key) auth_header = create_jwt_token( @@ -784,7 +775,7 @@ def test_should_persist_notification( service=template.service, name="team_key", created_by=template.created_by, - key_type=KEY_TYPE_TEAM, + key_type=KeyType.TEAM, ) save_model_api_key(api_key) auth_header = create_jwt_token( @@ -843,7 +834,7 @@ def test_should_delete_notification_and_return_error_if_redis_fails( service=template.service, name="team_key", created_by=template.created_by, - key_type=KEY_TYPE_TEAM, + key_type=KeyType.TEAM, ) save_model_api_key(api_key) auth_header = create_jwt_token( @@ -919,7 +910,7 @@ def test_should_not_persist_notification_or_send_sms_if_simulated_number( assert Notification.query.count() == 0 -@pytest.mark.parametrize("key_type", [KEY_TYPE_NORMAL, KEY_TYPE_TEAM]) +@pytest.mark.parametrize("key_type", [KeyType.NORMAL, KeyType.TEAM]) @pytest.mark.parametrize( "notification_type, to", [ @@ -964,7 +955,7 @@ def test_should_not_send_notification_to_non_guest_list_recipient_in_trial_mode( "Can’t send to this recipient when service is in trial mode " "– see https://www.notifications.service.gov.uk/trial-mode" ) - if key_type == KEY_TYPE_NORMAL + if key_type == KeyType.NORMAL else ("Can’t send to this recipient using a team-only API key") ) @@ -976,7 +967,7 @@ def test_should_not_send_notification_to_non_guest_list_recipient_in_trial_mode( @pytest.mark.parametrize("service_restricted", [True, False]) -@pytest.mark.parametrize("key_type", [KEY_TYPE_NORMAL, KEY_TYPE_TEAM]) +@pytest.mark.parametrize("key_type", [KeyType.NORMAL, KeyType.TEAM]) @pytest.mark.parametrize( "notification_type, to, normalized_to", [ diff --git a/tests/app/service/send_notification/test_send_one_off_notification.py b/tests/app/service/send_notification/test_send_one_off_notification.py index 9f4ccfbc1..9e342369d 100644 --- a/tests/app/service/send_notification/test_send_one_off_notification.py +++ b/tests/app/service/send_notification/test_send_one_off_notification.py @@ -7,15 +7,14 @@ from notifications_utils.recipients import InvalidPhoneError from app.config import QueueNames from app.dao.service_guest_list_dao import dao_add_and_commit_guest_list_contacts -from app.models import ( - KEY_TYPE_NORMAL, - PRIORITY, - GuestListRecipientType, - Notification, +from app.enums import ( + KeyType, NotificationType, - ServiceGuestList, + RecipientType, + TemplateProcessType, TemplateType, ) +from app.models import Notification, ServiceGuestList from app.service.send_notification import send_one_off_notification from app.v2.errors import BadRequestError from tests.app.db import ( @@ -90,7 +89,7 @@ def test_send_one_off_notification_calls_persist_correctly_for_sms( personalisation={"name": "foo"}, notification_type=NotificationType.SMS, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, created_by_id=str(service.created_by_id), reply_to_text="testing", reference=None, @@ -147,7 +146,7 @@ def test_send_one_off_notification_calls_persist_correctly_for_email( personalisation={"name": "foo"}, notification_type=NotificationType.EMAIL, api_key_id=None, - key_type=KEY_TYPE_NORMAL, + key_type=KeyType.NORMAL, created_by_id=str(service.created_by_id), reply_to_text=None, reference=None, @@ -160,7 +159,7 @@ def test_send_one_off_notification_honors_priority( ): service = create_service() template = create_template(service=service) - template.process_type = PRIORITY + template.process_type = TemplateProcessType.PRIORITY post_data = { "template_id": str(template.id), @@ -204,7 +203,7 @@ def test_send_one_off_notification_raises_if_cant_send_to_recipient( dao_add_and_commit_guest_list_contacts( [ ServiceGuestList.from_string( - service.id, GuestListRecipientType.MOBILE, "2028765309" + service.id, RecipientType.MOBILE, "2028765309" ), ] ) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 5bad34814..c7cf7e87d 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -14,7 +14,7 @@ from app.dao.service_user_dao import dao_get_service_user from app.dao.services_dao import dao_add_user_to_service, dao_remove_user_from_service from app.dao.templates_dao import dao_redact_template from app.dao.users_dao import save_model_user -from app.enums import KeyType, ServicePermissionType, TemplateType, NotificationType +from app.enums import KeyType, NotificationType, ServicePermissionType, TemplateType from app.models import ( AnnualBilling, EmailBranding, diff --git a/tests/app/service/test_sender.py b/tests/app/service/test_sender.py index dc17e1eb8..caae265c8 100644 --- a/tests/app/service/test_sender.py +++ b/tests/app/service/test_sender.py @@ -2,7 +2,8 @@ import pytest from flask import current_app from app.dao.services_dao import dao_add_user_to_service -from app.models import Notification, NotificationType, TemplateType +from app.enums import NotificationType, TemplateType +from app.models import Notification from app.service.sender import send_notification_to_service_users from tests.app.db import create_service, create_template, create_user diff --git a/tests/app/service/test_service_guest_list.py b/tests/app/service/test_service_guest_list.py index cd8ec7c1d..5d86a06c2 100644 --- a/tests/app/service/test_service_guest_list.py +++ b/tests/app/service/test_service_guest_list.py @@ -2,7 +2,8 @@ import json import uuid from app.dao.service_guest_list_dao import dao_add_and_commit_guest_list_contacts -from app.models import GuestListRecipientType, ServiceGuestList +from app.enums import RecipientType +from app.models import ServiceGuestList from tests import create_admin_authorization_header @@ -25,15 +26,15 @@ def test_get_guest_list_separates_emails_and_phones(client, sample_service): [ ServiceGuestList.from_string( sample_service.id, - GuestListRecipientType.EMAIL, + RecipientType.EMAIL, "service@example.com", ), ServiceGuestList.from_string( - sample_service.id, GuestListRecipientType.MOBILE, "2028675309" + sample_service.id, RecipientType.MOBILE, "2028675309" ), ServiceGuestList.from_string( sample_service.id, - GuestListRecipientType.MOBILE, + RecipientType.MOBILE, "+1800-555-5555", ), ] diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index b0cd42b3b..f0d8eda4a 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -4,13 +4,7 @@ from datetime import date, datetime import pytest from freezegun import freeze_time -from app.models import ( - KEY_TYPE_NORMAL, - KEY_TYPE_TEAM, - KEY_TYPE_TEST, - NotificationType, - TemplateType, -) +from app.enums import KeyType, NotificationType, TemplateType from tests.app.db import ( create_ft_notification_status, create_notification, @@ -285,13 +279,13 @@ def test_get_monthly_notification_stats_ignores_test_keys( admin_request, sample_service ): create_ft_notification_status( - datetime(2016, 6, 1), service=sample_service, key_type=KEY_TYPE_NORMAL, count=1 + datetime(2016, 6, 1), service=sample_service, key_type=KeyType.NORMAL, count=1 ) create_ft_notification_status( - datetime(2016, 6, 1), service=sample_service, key_type=KEY_TYPE_TEAM, count=2 + datetime(2016, 6, 1), service=sample_service, key_type=KeyType.TEAM, count=2 ) create_ft_notification_status( - datetime(2016, 6, 1), service=sample_service, key_type=KEY_TYPE_TEST, count=4 + datetime(2016, 6, 1), service=sample_service, key_type=KeyType.TEST, count=4 ) response = admin_request.get( diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index e4ac9532c..964a8acc8 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -7,7 +7,8 @@ from flask import current_app from freezegun import freeze_time from notifications_utils.url_safe_token import generate_token -from app.models import EMAIL_AUTH_TYPE, SMS_AUTH_TYPE, Notification +from app.enums import AuthType +from app.models import Notification from tests import create_admin_authorization_header from tests.app.db import create_invited_user @@ -39,7 +40,7 @@ def test_create_invited_user( email_address=email_address, from_user=str(invite_from.id), permissions="send_messages,manage_service,manage_api_keys", - auth_type=EMAIL_AUTH_TYPE, + auth_type=AuthType.EMAIL, folder_permissions=["folder_1", "folder_2", "folder_3"], **extra_args, ) @@ -58,7 +59,7 @@ def test_create_invited_user( json_resp["data"]["permissions"] == "send_messages,manage_service,manage_api_keys" ) - assert json_resp["data"]["auth_type"] == EMAIL_AUTH_TYPE + assert json_resp["data"]["auth_type"] == AuthType.EMAIL assert json_resp["data"]["id"] assert json_resp["data"]["folder_permissions"] == [ "folder_1", @@ -107,7 +108,7 @@ def test_create_invited_user_without_auth_type( _expected_status=201, ) - assert json_resp["data"]["auth_type"] == SMS_AUTH_TYPE + assert json_resp["data"]["auth_type"] == AuthType.SMS def test_create_invited_user_invalid_email(client, sample_service, mocker, fake_uuid): @@ -161,7 +162,7 @@ def test_get_all_invited_users_by_service(client, notify_db_session, sample_serv for invite in json_resp["data"]: assert invite["service"] == str(sample_service.id) assert invite["from_user"] == str(invite_from.id) - assert invite["auth_type"] == SMS_AUTH_TYPE + assert invite["auth_type"] == AuthType.SMS assert invite["id"] diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index bca08034a..311d0e4d7 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -9,7 +9,8 @@ from freezegun import freeze_time from notifications_utils import SMS_CHAR_COUNT_LIMIT from app.dao.templates_dao import dao_get_template_by_id, dao_redact_template -from app.models import ServicePermissionType, Template, TemplateHistory, TemplateType +from app.enums import ServicePermissionType, TemplateType +from app.models import Template, TemplateHistory from tests import create_admin_authorization_header from tests.app.db import create_service, create_template, create_template_folder diff --git a/tests/app/test_model.py b/tests/app/test_model.py index 11f9df493..47dd315ed 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -4,15 +4,14 @@ import pytest from freezegun import freeze_time from sqlalchemy.exc import IntegrityError +from app import encryption from app.enums import ( + AgreementStatus, + AgreementType, NotificationStatus, RecipientType, TemplateType, - AgreementStatus, - AgreementType, ) - -from app import encryption from app.models import ( Agreement, AnnualBilling, @@ -50,7 +49,7 @@ def test_should_build_service_guest_list_from_mobile_number(mobile_number): @pytest.mark.parametrize("email_address", ["test@example.com"]) def test_should_build_service_guest_list_from_email_address(email_address): service_guest_list = ServiceGuestList.from_string( - "service_id", GuestListRecipientType.EMAIL, email_address + "service_id", RecipientType.EMAIL, email_address ) assert service_guest_list.recipient == email_address @@ -60,8 +59,8 @@ def test_should_build_service_guest_list_from_email_address(email_address): "contact, recipient_type", [ ("", None), - ("07700dsadsad", GuestListRecipientType.MOBILE), - ("gmail.com", GuestListRecipientType.EMAIL), + ("07700dsadsad", RecipientType.MOBILE), + ("gmail.com", RecipientType.EMAIL), ], ) def test_should_not_build_service_guest_list_from_invalid_contact( diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index 4d824a058..2e06b2b9b 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -9,15 +9,8 @@ from freezegun import freeze_time from app.dao.permissions_dao import default_service_permissions from app.dao.service_user_dao import dao_get_service_user, dao_update_service_user -from app.models import ( - EMAIL_AUTH_TYPE, - MANAGE_SETTINGS, - MANAGE_TEMPLATES, - SMS_AUTH_TYPE, - Notification, - Permission, - User, -) +from app.enums import AuthType, PermissionType +from app.models import Notification, Permission, User from tests.app.db import ( create_organization, create_service, @@ -71,7 +64,7 @@ def test_get_user(admin_request, sample_service, sample_organization): assert fetched["mobile_number"] == sample_user.mobile_number assert fetched["email_address"] == sample_user.email_address assert fetched["state"] == sample_user.state - assert fetched["auth_type"] == SMS_AUTH_TYPE + assert fetched["auth_type"] == AuthType.SMS assert fetched["permissions"].keys() == {str(sample_service.id)} assert fetched["services"] == [str(sample_service.id)] assert fetched["organizations"] == [str(sample_organization.id)] @@ -117,7 +110,7 @@ def test_post_user(admin_request, notify_db_session): "state": "active", "failed_login_count": 0, "permissions": {}, - "auth_type": EMAIL_AUTH_TYPE, + "auth_type": AuthType.EMAIL, } json_resp = admin_request.post("user.create_user", _data=data, _expected_status=201) @@ -125,7 +118,7 @@ def test_post_user(admin_request, notify_db_session): assert user.check_password("password") assert json_resp["data"]["email_address"] == user.email_address assert json_resp["data"]["id"] == str(user.id) - assert user.auth_type == EMAIL_AUTH_TYPE + assert user.auth_type == AuthType.EMAIL def test_post_user_without_auth_type(admin_request, notify_db_session): @@ -142,7 +135,7 @@ def test_post_user_without_auth_type(admin_request, notify_db_session): user = User.query.filter_by(email_address="user@digital.fake.gov").first() assert json_resp["data"]["id"] == str(user.id) - assert user.auth_type == SMS_AUTH_TYPE + assert user.auth_type == AuthType.SMS def test_post_user_missing_attribute_email(admin_request, notify_db_session): @@ -194,12 +187,12 @@ def test_can_create_user_with_email_auth_and_no_mobile( "email_address": "user@digital.fake.gov", "password": "password", "mobile_number": None, - "auth_type": EMAIL_AUTH_TYPE, + "auth_type": AuthType.EMAIL, } json_resp = admin_request.post("user.create_user", _data=data, _expected_status=201) - assert json_resp["data"]["auth_type"] == EMAIL_AUTH_TYPE + assert json_resp["data"]["auth_type"] == AuthType.EMAIL assert json_resp["data"]["mobile_number"] is None @@ -211,7 +204,7 @@ def test_cannot_create_user_with_sms_auth_and_no_mobile( "email_address": "user@digital.fake.gov", "password": "password", "mobile_number": None, - "auth_type": SMS_AUTH_TYPE, + "auth_type": AuthType.SMS, } json_resp = admin_request.post("user.create_user", _data=data, _expected_status=400) @@ -228,7 +221,7 @@ def test_cannot_create_user_with_empty_strings(admin_request, notify_db_session) "email_address": "", "password": "password", "mobile_number": "", - "auth_type": EMAIL_AUTH_TYPE, + "auth_type": AuthType.EMAIL, } resp = admin_request.post("user.create_user", _data=data, _expected_status=400) assert resp["message"] == { @@ -465,21 +458,27 @@ def test_set_user_permissions(admin_request, sample_user, sample_service): "user.set_permissions", user_id=str(sample_user.id), service_id=str(sample_service.id), - _data={"permissions": [{"permission": MANAGE_SETTINGS}]}, + _data={ + "permissions": [ + {"permission": PermissionType.PermissionType.MANAGE_SETTINGS} + ] + }, _expected_status=204, ) - permission = Permission.query.filter_by(permission=MANAGE_SETTINGS).first() + permission = Permission.query.filter_by( + permission=PermissionType.MANAGE_SETTINGS + ).first() assert permission.user == sample_user assert permission.service == sample_service - assert permission.permission == MANAGE_SETTINGS + assert permission.permission == PermissionType.MANAGE_SETTINGS def test_set_user_permissions_multiple(admin_request, sample_user, sample_service): data = { "permissions": [ - {"permission": MANAGE_SETTINGS}, - {"permission": MANAGE_TEMPLATES}, + {"permission": PermissionType.MANAGE_SETTINGS}, + {"permission": PermissionType.MANAGE_TEMPLATES}, ] } admin_request.post( @@ -490,18 +489,22 @@ def test_set_user_permissions_multiple(admin_request, sample_user, sample_servic _expected_status=204, ) - permission = Permission.query.filter_by(permission=MANAGE_SETTINGS).first() + permission = Permission.query.filter_by( + permission=PermissionType.MANAGE_SETTINGS + ).first() assert permission.user == sample_user assert permission.service == sample_service - assert permission.permission == MANAGE_SETTINGS - permission = Permission.query.filter_by(permission=MANAGE_TEMPLATES).first() + assert permission.permission == PermissionType.MANAGE_SETTINGS + permission = Permission.query.filter_by( + permission=PermissionType.MANAGE_TEMPLATES + ).first() assert permission.user == sample_user assert permission.service == sample_service - assert permission.permission == MANAGE_TEMPLATES + assert permission.permission == PermissionType.MANAGE_TEMPLATES def test_set_user_permissions_remove_old(admin_request, sample_user, sample_service): - data = {"permissions": [{"permission": MANAGE_SETTINGS}]} + data = {"permissions": [{"permission": PermissionType.MANAGE_SETTINGS}]} admin_request.post( "user.set_permissions", @@ -513,7 +516,7 @@ def test_set_user_permissions_remove_old(admin_request, sample_user, sample_serv query = Permission.query.filter_by(user=sample_user) assert query.count() == 1 - assert query.first().permission == MANAGE_SETTINGS + assert query.first().permission == PermissionType.MANAGE_SETTINGS def test_set_user_folder_permissions(admin_request, sample_user, sample_service): @@ -893,23 +896,23 @@ def test_update_user_auth_type(admin_request, sample_user): def test_can_set_email_auth_and_remove_mobile_at_same_time(admin_request, sample_user): - sample_user.auth_type = SMS_AUTH_TYPE + sample_user.auth_type = AuthType.SMS admin_request.post( "user.update_user_attribute", user_id=sample_user.id, _data={ "mobile_number": None, - "auth_type": EMAIL_AUTH_TYPE, + "auth_type": AuthType.EMAIL, }, ) assert sample_user.mobile_number is None - assert sample_user.auth_type == EMAIL_AUTH_TYPE + assert sample_user.auth_type == AuthType.EMAIL def test_cannot_remove_mobile_if_sms_auth(admin_request, sample_user): - sample_user.auth_type = SMS_AUTH_TYPE + sample_user.auth_type = AuthType.SMS json_resp = admin_request.post( "user.update_user_attribute", @@ -925,7 +928,7 @@ def test_cannot_remove_mobile_if_sms_auth(admin_request, sample_user): def test_can_remove_mobile_if_email_auth(admin_request, sample_user): - sample_user.auth_type = EMAIL_AUTH_TYPE + sample_user.auth_type = AuthType.EMAIL admin_request.post( "user.update_user_attribute", @@ -939,7 +942,7 @@ def test_can_remove_mobile_if_email_auth(admin_request, sample_user): def test_cannot_update_user_with_mobile_number_as_empty_string( admin_request, sample_user ): - sample_user.auth_type = EMAIL_AUTH_TYPE + sample_user.auth_type = AuthType.EMAIL resp = admin_request.post( "user.update_user_attribute", diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 4c063e7b5..bb0e06ec3 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -10,7 +10,8 @@ import app.celery.tasks from app import db from app.dao.services_dao import dao_fetch_service_by_id from app.dao.users_dao import create_user_code -from app.models import USER_AUTH_TYPES, Notification, User, VerifyCode, VerifyCodeType +from app.enums import AuthType, CodeType +from app.models import Notification, User, VerifyCode from tests import create_admin_authorization_header @@ -96,7 +97,7 @@ def test_user_verify_code_rejects_good_code_if_too_many_failed_logins( @freeze_time("2020-04-01 12:00") -@pytest.mark.parametrize("code_type", [VerifyCodeType.EMAIL, VerifyCodeType.SMS]) +@pytest.mark.parametrize("code_type", [CodeType.EMAIL, CodeType.SMS]) def test_user_verify_code_expired_code_and_increments_failed_login_count( code_type, admin_request, sample_user ): @@ -425,7 +426,7 @@ def test_reset_failed_login_count_returns_404_when_user_does_not_exist(client): # we send sms_auth users and webauthn_auth users email code to validate their email access -@pytest.mark.parametrize("auth_type", USER_AUTH_TYPES) +@pytest.mark.parametrize("auth_type", AuthType) @pytest.mark.parametrize( "data, expected_auth_url", ( @@ -514,13 +515,13 @@ def test_send_email_code_returns_404_for_bad_input_data(admin_request): @freeze_time("2016-01-01T12:00:00") # we send sms_auth and webauthn_auth users email code to validate their email access -@pytest.mark.parametrize("auth_type", USER_AUTH_TYPES) +@pytest.mark.parametrize("auth_type", AuthType) def test_user_verify_email_code(admin_request, sample_user, auth_type): sample_user.logged_in_at = datetime.utcnow() - timedelta(days=1) sample_user.email_access_validated_at = datetime.utcnow() - timedelta(days=1) sample_user.auth_type = auth_type magic_code = str(uuid.uuid4()) - verify_code = create_user_code(sample_user, magic_code, VerifyCodeType.EMAIL) + verify_code = create_user_code(sample_user, magic_code, CodeType.EMAIL) data = {"code_type": "email", "code": magic_code} @@ -537,7 +538,7 @@ def test_user_verify_email_code(admin_request, sample_user, auth_type): assert sample_user.current_session_id is not None -@pytest.mark.parametrize("code_type", [VerifyCodeType.EMAIL, VerifyCodeType.SMS]) +@pytest.mark.parametrize("code_type", [CodeType.EMAIL, CodeType.SMS]) @freeze_time("2016-01-01T12:00:00") def test_user_verify_email_code_fails_if_code_already_used( admin_request, sample_user, code_type diff --git a/tests/app/v2/notifications/test_notification_schemas.py b/tests/app/v2/notifications/test_notification_schemas.py index fab4e22e7..24bcec93e 100644 --- a/tests/app/v2/notifications/test_notification_schemas.py +++ b/tests/app/v2/notifications/test_notification_schemas.py @@ -5,7 +5,7 @@ from flask import json from freezegun import freeze_time from jsonschema import ValidationError -from app.models import NOTIFICATION_CREATED, TemplateType +from app.enums import NotificationStatus, TemplateType from app.schema_validation import validate from app.v2.notifications.notification_schemas import get_notifications_request from app.v2.notifications.notification_schemas import ( @@ -19,7 +19,7 @@ valid_get_json = {} valid_get_with_optionals_json = { "reference": "test reference", - "status": [NOTIFICATION_CREATED], + "status": [NotificationStatus.CREATED], "template_type": [TemplateType.EMAIL], "include_jobs": "true", "older_than": "a5149c32-f03b-4711-af49-ad6993797d45", diff --git a/tests/app/v2/template/test_get_template.py b/tests/app/v2/template/test_get_template.py index c22756f6e..2a14f5543 100644 --- a/tests/app/v2/template/test_get_template.py +++ b/tests/app/v2/template/test_get_template.py @@ -1,7 +1,7 @@ import pytest from flask import json -from app.models import TemplateType +from app.enums import TemplateType from app.utils import DATETIME_FORMAT from tests import create_service_authorization_header from tests.app.db import create_template diff --git a/tests/app/v2/template/test_template_schemas.py b/tests/app/v2/template/test_template_schemas.py index 43bfbc27d..eff8348f2 100644 --- a/tests/app/v2/template/test_template_schemas.py +++ b/tests/app/v2/template/test_template_schemas.py @@ -4,7 +4,7 @@ import pytest from flask import json from jsonschema.exceptions import ValidationError -from app.models import TemplateType +from app.enums import TemplateType from app.schema_validation import validate from app.v2.template.template_schemas import ( get_template_by_id_request, diff --git a/tests/app/v2/templates/test_templates_schemas.py b/tests/app/v2/templates/test_templates_schemas.py index 50d22e463..dd2681e38 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -4,7 +4,7 @@ import pytest from flask import json from jsonschema.exceptions import ValidationError -from app.models import TemplateType +from app.enums import TemplateType from app.schema_validation import validate from app.v2.templates.templates_schemas import ( get_all_template_request, From bd705ed1885f1bcb1baa495a95b6a852499572de Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 16 Jan 2024 15:47:55 -0500 Subject: [PATCH 149/259] More cleanup. Signed-off-by: Cliff Hill --- app/celery/provider_tasks.py | 2 +- app/dao/fact_billing_dao.py | 6 ++-- app/dao/permissions_dao.py | 2 +- app/dao/services_dao.py | 2 +- app/delivery/send_to_providers.py | 2 +- app/enums.py | 46 ++++++++++++------------------- app/models.py | 14 ++++++---- tests/app/conftest.py | 2 +- tests/app/test_model.py | 8 +++--- 9 files changed, 38 insertions(+), 46 deletions(-) diff --git a/app/celery/provider_tasks.py b/app/celery/provider_tasks.py index 6f72dccc2..3610126d4 100644 --- a/app/celery/provider_tasks.py +++ b/app/celery/provider_tasks.py @@ -15,7 +15,7 @@ from app.dao.notifications_dao import ( update_notification_status_by_id, ) from app.delivery import send_to_providers -from app.enum import NotificationStatus +from app.enums import NotificationStatus from app.exceptions import NotificationTechnicalFailureException # This is the amount of time to wait after sending an sms message before we check the aws logs and look for delivery diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index 01448cb3b..5904fe9a0 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -408,7 +408,9 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): func.count().label("notifications_sent"), ) .filter( - NotificationAllTimeView.status.in_(NotificationStatus.sent_email_types), + NotificationAllTimeView.status.in_( + NotificationStatus.sent_email_types() + ), NotificationAllTimeView.key_type.in_((KeyType.NORMAL, KeyType.TEAM)), NotificationAllTimeView.created_at >= start_date, NotificationAllTimeView.created_at < end_date, @@ -441,7 +443,7 @@ def _query_for_billing_data(notification_type, start_date, end_date, service): ) .filter( NotificationAllTimeView.status.in_( - NotificationStatus.billable_sms_types + NotificationStatus.billable_sms_types() ), NotificationAllTimeView.key_type.in_((KeyType.NORMAL, KeyType.TEAM)), NotificationAllTimeView.created_at >= start_date, diff --git a/app/dao/permissions_dao.py b/app/dao/permissions_dao.py index 5f6f91583..fb00172e0 100644 --- a/app/dao/permissions_dao.py +++ b/app/dao/permissions_dao.py @@ -9,7 +9,7 @@ class PermissionDAO(DAOClass): model = Permission def add_default_service_permissions_for_user(self, user, service): - for name in PermissionType.defaults: + for name in PermissionType.defaults(): permission = Permission(permission=name, user=user, service=service) self.create_instance(permission, _commit=False) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 54b33927c..7925602b1 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -275,7 +275,7 @@ def dao_create_service( raise ValueError("Can't create a service without a user") if service_permissions is None: - service_permissions = ServicePermissionType.defaults + service_permissions = ServicePermissionType.defaults() organization = dao_get_organization_by_email_address(user.email_address) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index f6725a516..95d122032 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -155,7 +155,7 @@ def send_email_to_provider(notification): def update_notification_to_sending(notification, provider): notification.sent_at = datetime.utcnow() notification.sent_by = provider.name - if notification.status not in NotificationStatus.completed_types: + if notification.status not in NotificationStatus.completed_types(): notification.status = NotificationStatus.SENDING dao_update_notification(notification) diff --git a/app/enums.py b/app/enums.py index e9785b8d2..860132129 100644 --- a/app/enums.py +++ b/app/enums.py @@ -51,9 +51,8 @@ class NotificationStatus(Enum): VALIDATION_FAILED = "validation-failed" VIRUS_SCAN_FAILED = "virus-scan-failed" - @property - def failed_types(self) -> tuple["NotificationStatus", ...]: - cls = type(self) + @classmethod + def failed_types(cls) -> tuple["NotificationStatus", ...]: return ( cls.TECHNICAL_FAILURE, cls.TEMPORARY_FAILURE, @@ -62,9 +61,8 @@ class NotificationStatus(Enum): cls.VIRUS_SCAN_FAILED, ) - @property - def completed_types(self) -> tuple["NotificationStatus", ...]: - cls = type(self) + @classmethod + def completed_types(cls) -> tuple["NotificationStatus", ...]: return ( cls.SENT, cls.DELIVERED, @@ -75,14 +73,12 @@ class NotificationStatus(Enum): cls.CANCELLED, ) - @property - def success_types(self) -> tuple["NotificationStatus", ...]: - cls = type(self) + @classmethod + def success_types(cls) -> tuple["NotificationStatus", ...]: return (cls.SENT, cls.DELIVERED) - @property - def billable_types(self) -> tuple["NotificationStatus", ...]: - cls = type(self) + @classmethod + def billable_types(cls) -> tuple["NotificationStatus", ...]: return ( cls.SENDING, cls.SENT, @@ -93,9 +89,8 @@ class NotificationStatus(Enum): cls.PERMANENT_FAILURE, ) - @property - def billable_sms_types(self) -> tuple["NotificationStatus", ...]: - cls = type(self) + @classmethod + def billable_sms_types(cls) -> tuple["NotificationStatus", ...]: return ( cls.SENDING, cls.SENT, # internationally @@ -105,9 +100,8 @@ class NotificationStatus(Enum): cls.PERMANENT_FAILURE, ) - @property - def sent_email_types(self) -> tuple["NotificationStatus", ...]: - cls = type(self) + @classmethod + def sent_email_types(cls) -> tuple["NotificationStatus", ...]: return ( cls.SENDING, cls.DELIVERED, @@ -115,14 +109,9 @@ class NotificationStatus(Enum): cls.PERMANENT_FAILURE, ) - @property - def non_billable_types(self) -> tuple["NotificationStatus", ...]: - self._non_billable: tuple["NotificationStatus", ...] - try: - return self._non_billable - except AttributeError: - self._non_billable = tuple(set(type(self)) - set(self.billable)) - return self._non_billable + @classmethod + def non_billable_types(cls) -> tuple["NotificationStatus", ...]: + return tuple({i for i in cls} - set(cls.billable_types())) class PermissionType(Enum): @@ -135,9 +124,8 @@ class PermissionType(Enum): PLATFORM_ADMIN = "platform_admin" VIEW_ACTIVITY = "view_activity" - @property - def defaults(self) -> tuple["PermissionType", ...]: - cls = type(self) + @classmethod + def defaults(cls) -> tuple["PermissionType", ...]: return ( cls.MANAGE_USERS, cls.MANAGE_TEMPLATES, diff --git a/app/models.py b/app/models.py index f870268f2..241f1ffd0 100644 --- a/app/models.py +++ b/app/models.py @@ -232,10 +232,12 @@ class ServiceUser(db.Model): primary_key=True, ) - __table_args__ = UniqueConstraint( - "user_id", - "service_id", - name="uix_user_to_service", + __table_args__ = ( + UniqueConstraint( + "user_id", + "service_id", + name="uix_user_to_service", + ), ) @@ -1581,7 +1583,7 @@ class Notification(db.Model): self._personalisation = encryption.encrypt(personalisation or {}) def completed_at(self): - if self.status in NotificationStatus.completed_types: + if self.status in NotificationStatus.completed_types(): return self.updated_at.strftime(DATETIME_FORMAT) return None @@ -1621,7 +1623,7 @@ class Notification(db.Model): def _substitute_status_str(_status): return ( - NotificationStatus.failed_types + NotificationStatus.failed_types() if _status == NotificationStatus.FAILED else [_status] ) diff --git a/tests/app/conftest.py b/tests/app/conftest.py index d579563c0..a47bf23b1 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -129,7 +129,7 @@ def create_sample_notification( "key_type": api_key.key_type if api_key else key_type, "sent_by": sent_by, "updated_at": created_at - if status in NotificationStatus.completed_types + if status in NotificationStatus.completed_types() else None, "client_reference": client_reference, "rate_multiplier": rate_multiplier, diff --git a/tests/app/test_model.py b/tests/app/test_model.py index 47dd315ed..fec227dc1 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -74,11 +74,11 @@ def test_should_not_build_service_guest_list_from_invalid_contact( "initial_statuses, expected_statuses", [ # passing in single statuses as strings - (NotificationStatus.FAILED, NotificationStatus.failed_types), + (NotificationStatus.FAILED, NotificationStatus.failed_types()), (NotificationStatus.CREATED, [NotificationStatus.CREATED]), (NotificationStatus.TECHNICAL_FAILURE, [NotificationStatus.TECHNICAL_FAILURE]), # passing in lists containing single statuses - ([NotificationStatus.FAILED], NotificationStatus.failed_types), + ([NotificationStatus.FAILED], NotificationStatus.failed_types()), ([NotificationStatus.CREATED], [NotificationStatus.CREATED]), ( [NotificationStatus.TECHNICAL_FAILURE], @@ -87,7 +87,7 @@ def test_should_not_build_service_guest_list_from_invalid_contact( # passing in lists containing multiple statuses ( [NotificationStatus.FAILED, NotificationStatus.CREATED], - list(NotificationStatus.failed_types) + [NotificationStatus.CREATED], + list(NotificationStatus.failed_types()) + [NotificationStatus.CREATED], ), ( [NotificationStatus.CREATED, NotificationStatus.PENDING], @@ -104,7 +104,7 @@ def test_should_not_build_service_guest_list_from_invalid_contact( NotificationStatus.CREATED, NotificationStatus.TECHNICAL_FAILURE, ], - list(NotificationStatus.failed_types) + [NotificationStatus.CREATED], + list(NotificationStatus.failed_types()) + [NotificationStatus.CREATED], ), ], ) From e0b9ac0827fd60abc2ef37543d6ca952b0f4921b Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 16 Jan 2024 15:52:44 -0500 Subject: [PATCH 150/259] Even more cleanup. Signed-off-by: Cliff Hill --- tests/app/celery/test_tasks.py | 55 +++++++++---------- tests/app/job/test_rest.py | 4 +- .../notifications/test_post_notifications.py | 14 ++--- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index b9e08ce30..c615ee586 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -29,17 +29,14 @@ from app.celery.tasks import ( ) from app.config import QueueNames from app.dao import jobs_dao, service_email_reply_to_dao, service_sms_sender_dao -from app.models import ( - JOB_STATUS_ERROR, - JOB_STATUS_FINISHED, - JOB_STATUS_IN_PROGRESS, - KEY_TYPE_NORMAL, - NOTIFICATION_CREATED, - Job, - Notification, +from app.enums import ( + JobStatus, + KeyType, + NotificationStatus, NotificationType, TemplateType, ) +from app.models import Job, Notification from app.serialised_models import SerialisedService, SerialisedTemplate from app.utils import DATETIME_FORMAT from tests.app import load_example_csv @@ -575,7 +572,7 @@ def test_should_save_sms_template_to_and_persist_with_job_id(sample_job, mocker) assert not persisted_notification.sent_by assert persisted_notification.job_row_number == 2 assert persisted_notification.api_key_id is None - assert persisted_notification.key_type == KEY_TYPE_NORMAL + assert persisted_notification.key_type == KeyType.NORMAL assert persisted_notification.notification_type == "sms" provider_tasks.deliver_sms.apply_async.assert_called_once_with( @@ -646,7 +643,7 @@ def test_should_use_email_template_and_persist( assert persisted_notification.job_row_number == 1 assert persisted_notification.personalisation == {"name": "Jo"} assert persisted_notification.api_key_id is None - assert persisted_notification.key_type == KEY_TYPE_NORMAL + assert persisted_notification.key_type == KeyType.NORMAL assert persisted_notification.notification_type == "email" provider_tasks.deliver_email.apply_async.assert_called_once_with( @@ -1145,7 +1142,7 @@ def test_process_incomplete_job_sms(mocker, sample_template): created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) create_notification(sample_template, job, 0) @@ -1157,7 +1154,7 @@ def test_process_incomplete_job_sms(mocker, sample_template): completed_job = Job.query.filter(Job.id == job.id).one() - assert completed_job.job_status == JOB_STATUS_FINISHED + assert completed_job.job_status == JobStatus.FINISHED assert ( save_sms.call_count == 8 @@ -1177,7 +1174,7 @@ def test_process_incomplete_job_with_notifications_all_sent(mocker, sample_templ created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) create_notification(sample_template, job, 0) @@ -1197,7 +1194,7 @@ def test_process_incomplete_job_with_notifications_all_sent(mocker, sample_templ completed_job = Job.query.filter(Job.id == job.id).one() - assert completed_job.job_status == JOB_STATUS_FINISHED + assert completed_job.job_status == JobStatus.FINISHED assert ( mock_save_sms.call_count == 0 @@ -1217,7 +1214,7 @@ def test_process_incomplete_jobs_sms(mocker, sample_template): created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) create_notification(sample_template, job, 0) create_notification(sample_template, job, 1) @@ -1231,7 +1228,7 @@ def test_process_incomplete_jobs_sms(mocker, sample_template): created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) create_notification(sample_template, job2, 0) @@ -1248,9 +1245,9 @@ def test_process_incomplete_jobs_sms(mocker, sample_template): completed_job = Job.query.filter(Job.id == job.id).one() completed_job2 = Job.query.filter(Job.id == job2.id).one() - assert completed_job.job_status == JOB_STATUS_FINISHED + assert completed_job.job_status == JobStatus.FINISHED - assert completed_job2.job_status == JOB_STATUS_FINISHED + assert completed_job2.job_status == JobStatus.FINISHED assert ( mock_save_sms.call_count == 12 @@ -1270,7 +1267,7 @@ def test_process_incomplete_jobs_no_notifications_added(mocker, sample_template) created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) assert Notification.query.filter(Notification.job_id == job.id).count() == 0 @@ -1279,7 +1276,7 @@ def test_process_incomplete_jobs_no_notifications_added(mocker, sample_template) completed_job = Job.query.filter(Job.id == job.id).one() - assert completed_job.job_status == JOB_STATUS_FINISHED + assert completed_job.job_status == JobStatus.FINISHED assert mock_save_sms.call_count == 10 # There are 10 in the csv file @@ -1327,7 +1324,7 @@ def test_process_incomplete_job_email(mocker, sample_email_template): created_at=datetime.utcnow() - timedelta(hours=2), scheduled_for=datetime.utcnow() - timedelta(minutes=31), processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) create_notification(sample_email_template, job, 0) @@ -1339,7 +1336,7 @@ def test_process_incomplete_job_email(mocker, sample_email_template): completed_job = Job.query.filter(Job.id == job.id).one() - assert completed_job.job_status == JOB_STATUS_FINISHED + assert completed_job.job_status == JobStatus.FINISHED assert ( mock_email_saver.call_count == 8 @@ -1357,20 +1354,20 @@ def test_process_incomplete_jobs_sets_status_to_in_progress_and_resets_processin job1 = create_job( sample_template, processing_started=datetime.utcnow() - timedelta(minutes=30), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) job2 = create_job( sample_template, processing_started=datetime.utcnow() - timedelta(minutes=31), - job_status=JOB_STATUS_ERROR, + job_status=JobStatus.ERROR, ) process_incomplete_jobs([str(job1.id), str(job2.id)]) - assert job1.job_status == JOB_STATUS_IN_PROGRESS + assert job1.job_status == JobStatus.IN_PROGRESS assert job1.processing_started == datetime.utcnow() - assert job2.job_status == JOB_STATUS_IN_PROGRESS + assert job2.job_status == JobStatus.IN_PROGRESS assert job2.processing_started == datetime.utcnow() assert mock_process_incomplete_job.mock_calls == [ @@ -1403,7 +1400,7 @@ def test_save_api_email_or_sms(mocker, sample_service, notification_type): "client_reference": "our email", "reply_to_text": None, "document_download_count": 0, - "status": NOTIFICATION_CREATED, + "status": NotificationStatus.CREATED, "created_at": datetime.utcnow().strftime(DATETIME_FORMAT), } @@ -1455,7 +1452,7 @@ def test_save_api_email_dont_retry_if_notification_already_exists( "client_reference": "our email", "reply_to_text": "our.email@gov.uk", "document_download_count": 0, - "status": NOTIFICATION_CREATED, + "status": NotificationStatus.CREATED, "created_at": datetime.utcnow().strftime(DATETIME_FORMAT), } @@ -1590,7 +1587,7 @@ def test_save_api_tasks_use_cache( "client_reference": "our email", "reply_to_text": "our.email@gov.uk", "document_download_count": 0, - "status": NOTIFICATION_CREATED, + "status": NotificationStatus.CREATED, "created_at": datetime.utcnow().strftime(DATETIME_FORMAT), } ) diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index 3babf13e8..3afe283c2 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -782,11 +782,11 @@ def test_get_jobs_accepts_page_parameter(admin_request, sample_template): @pytest.mark.parametrize( "statuses_filter, expected_statuses", [ - ("", JobStatus.TYPES), + ("", JobStatus), ("pending", [JobStatus.PENDING]), ( "pending, in progress, finished, sending limits exceeded, scheduled, cancelled, ready to send, sent to dvla, error", # noqa - JobStatus.TYPES, + JobStatus, ), # bad statuses are accepted, just return no data ("foo", []), diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index d683895ee..98289dc62 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -8,12 +8,8 @@ from flask import current_app, json from app.dao import templates_dao from app.dao.service_sms_sender_dao import dao_update_service_sms_sender -from app.models import ( - NOTIFICATION_CREATED, - Notification, - NotificationType, - ServicePermissionType, -) +from app.enums import NotificationStatus, NotificationType, ServicePermissionType +from app.models import Notification from app.schema_validation import validate from app.v2.errors import RateLimitError from app.v2.notifications.notification_schemas import ( @@ -59,7 +55,7 @@ def test_post_sms_notification_returns_201( assert validate(resp_json, post_sms_response) == resp_json notifications = Notification.query.all() assert len(notifications) == 1 - assert notifications[0].status == NOTIFICATION_CREATED + assert notifications[0].status == NotificationStatus.CREATED notification_id = notifications[0].id assert notifications[0].document_download_count is None assert resp_json["id"] == str(notification_id) @@ -490,7 +486,7 @@ def test_post_email_notification_returns_201( resp_json = json.loads(response.get_data(as_text=True)) assert validate(resp_json, post_email_response) == resp_json notification = Notification.query.one() - assert notification.status == NOTIFICATION_CREATED + assert notification.status == NotificationStatus.CREATED assert resp_json["id"] == str(notification.id) assert resp_json["reference"] == reference assert notification.reference is None @@ -1042,7 +1038,7 @@ def test_post_notification_with_document_upload( ] notification = Notification.query.one() - assert notification.status == NOTIFICATION_CREATED + assert notification.status == NotificationStatus.CREATED assert notification.personalisation == { "first_link": "abababab-link", "second_link": "cdcdcdcd-link", From 95ee8b7c2ebc524809d484950bd79647b4b8ac27 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 17 Jan 2024 17:28:46 -0500 Subject: [PATCH 151/259] Cleaning things up, trying to get tests to work. Signed-off-by: Cliff Hill --- app/email_branding/email_branding_schema.py | 6 +++--- app/schemas.py | 4 ++-- migrations/versions/0172_deprioritise_examples.py | 2 -- migrations/versions/0219_default_email_branding.py | 4 ++-- tests/app/dao/test_service_guest_list_dao.py | 9 +++++---- tests/app/dao/test_users_dao.py | 5 +++-- tests/app/organization/test_invite_rest.py | 5 +++-- tests/app/user/test_rest.py | 7 +++---- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/email_branding/email_branding_schema.py b/app/email_branding/email_branding_schema.py index 99428c4bd..805835724 100644 --- a/app/email_branding/email_branding_schema.py +++ b/app/email_branding/email_branding_schema.py @@ -1,4 +1,4 @@ -from app.models import BRANDING_TYPES +from app.enums import BrandType post_create_email_branding_schema = { "$schema": "http://json-schema.org/draft-07/schema#", @@ -9,7 +9,7 @@ post_create_email_branding_schema = { "name": {"type": "string"}, "text": {"type": ["string", "null"]}, "logo": {"type": ["string", "null"]}, - "brand_type": {"enum": BRANDING_TYPES}, + "brand_type": {"enum": [e.value for e in BrandType]}, }, "required": ["name"], } @@ -23,7 +23,7 @@ post_update_email_branding_schema = { "name": {"type": ["string", "null"]}, "text": {"type": ["string", "null"]}, "logo": {"type": ["string", "null"]}, - "brand_type": {"enum": BRANDING_TYPES}, + "brand_type": {"enum": [e.value for e in BrandType]}, }, "required": [], } diff --git a/app/schemas.py b/app/schemas.py index 0161e4901..4e13e9aaa 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -13,7 +13,7 @@ from marshmallow import ( validates, validates_schema, ) -from marshmallow_sqlalchemy import field_for +from marshmallow_sqlalchemy import auto_field, field_for from notifications_utils.recipients import ( InvalidEmailError, InvalidPhoneError, @@ -464,7 +464,7 @@ class JobSchema(BaseSchema): processing_started = FlexibleDateTime() processing_finished = FlexibleDateTime() - job_status = field_for(models.JobStatus, "name", required=False) + job_status = auto_field() scheduled_for = FlexibleDateTime() service_name = fields.Nested( diff --git a/migrations/versions/0172_deprioritise_examples.py b/migrations/versions/0172_deprioritise_examples.py index f5c3f291d..bb1452311 100644 --- a/migrations/versions/0172_deprioritise_examples.py +++ b/migrations/versions/0172_deprioritise_examples.py @@ -8,8 +8,6 @@ Create Date: 2018-02-28 17:09:56.619803 import sqlalchemy as sa from alembic import op -from app.models import NORMAL - revision = "0172_deprioritise_examples" down_revision = "0171_add_org_invite_template" diff --git a/migrations/versions/0219_default_email_branding.py b/migrations/versions/0219_default_email_branding.py index 458f9b4df..2e66a149f 100644 --- a/migrations/versions/0219_default_email_branding.py +++ b/migrations/versions/0219_default_email_branding.py @@ -6,7 +6,7 @@ Create Date: 2018-08-24 13:36:49.346156 from alembic import op from sqlalchemy import text -from app.models import BRANDING_ORG +from app.enums import BrandType revision = "0219_default_email_branding" down_revision = "0216_remove_colours" @@ -14,7 +14,7 @@ down_revision = "0216_remove_colours" def upgrade(): conn = op.get_bind() - input_params = {"branding_org": BRANDING_ORG} + input_params = {"branding_org": BrandType.ORG.value} conn.execute( text( """ diff --git a/tests/app/dao/test_service_guest_list_dao.py b/tests/app/dao/test_service_guest_list_dao.py index 3fdd88227..870c78bd8 100644 --- a/tests/app/dao/test_service_guest_list_dao.py +++ b/tests/app/dao/test_service_guest_list_dao.py @@ -5,7 +5,8 @@ from app.dao.service_guest_list_dao import ( dao_fetch_service_guest_list, dao_remove_service_guest_list, ) -from app.models import GuestListRecipientType, ServiceGuestList +from app.enums import RecipientType +from app.models import ServiceGuestList from tests.app.db import create_service @@ -21,7 +22,7 @@ def test_fetch_service_guest_list_ignores_other_service(sample_service_guest_lis def test_add_and_commit_guest_list_contacts_saves_data(sample_service): guest_list = ServiceGuestList.from_string( - sample_service.id, GuestListRecipientType.EMAIL, "foo@example.com" + sample_service.id, RecipientType.EMAIL, "foo@example.com" ) dao_add_and_commit_guest_list_contacts([guest_list]) @@ -37,10 +38,10 @@ def test_remove_service_guest_list_only_removes_for_my_service(notify_db_session dao_add_and_commit_guest_list_contacts( [ ServiceGuestList.from_string( - service_1.id, GuestListRecipientType.EMAIL, "service1@example.com" + service_1.id, RecipientType.EMAIL, "service1@example.com" ), ServiceGuestList.from_string( - service_2.id, GuestListRecipientType.EMAIL, "service2@example.com" + service_2.id, RecipientType.EMAIL, "service2@example.com" ), ] ) diff --git a/tests/app/dao/test_users_dao.py b/tests/app/dao/test_users_dao.py index 53c82e52d..7b10cf4d5 100644 --- a/tests/app/dao/test_users_dao.py +++ b/tests/app/dao/test_users_dao.py @@ -24,8 +24,9 @@ from app.dao.users_dao import ( update_user_password, user_can_be_archived, ) +from app.enums import AuthType from app.errors import InvalidRequest -from app.models import EMAIL_AUTH_TYPE, User, VerifyCode +from app.models import User, VerifyCode from tests.app.db import ( create_permissions, create_service, @@ -229,7 +230,7 @@ def test_dao_archive_user(sample_user, sample_organization, fake_uuid): assert sample_user.get_permissions() == {} assert sample_user.services == [] assert sample_user.organizations == [] - assert sample_user.auth_type == EMAIL_AUTH_TYPE + assert sample_user.auth_type == AuthType.EMAIL assert sample_user.email_address == "_archived_2018-07-07_notify@digital.fake.gov" assert sample_user.mobile_number is None assert sample_user.current_session_id == uuid.UUID( diff --git a/tests/app/organization/test_invite_rest.py b/tests/app/organization/test_invite_rest.py index 097fa92e4..fc5cf045d 100644 --- a/tests/app/organization/test_invite_rest.py +++ b/tests/app/organization/test_invite_rest.py @@ -5,7 +5,8 @@ from flask import current_app, json from freezegun import freeze_time from notifications_utils.url_safe_token import generate_token -from app.models import INVITE_PENDING, Notification +from app.enums import InvitedUserStatus +from app.models import Notification from tests import create_admin_authorization_header from tests.app.db import create_invited_org_user @@ -56,7 +57,7 @@ def test_create_invited_org_user( assert json_resp["data"]["organization"] == str(sample_organization.id) assert json_resp["data"]["email_address"] == email_address assert json_resp["data"]["invited_by"] == str(sample_user.id) - assert json_resp["data"]["status"] == INVITE_PENDING + assert json_resp["data"]["status"] == InvitedUserStatus.PENDING assert json_resp["data"]["id"] notification = Notification.query.first() diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index 2e06b2b9b..7be13a45c 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -7,7 +7,6 @@ import pytest from flask import current_app from freezegun import freeze_time -from app.dao.permissions_dao import default_service_permissions from app.dao.service_user_dao import dao_get_service_user, dao_update_service_user from app.enums import AuthType, PermissionType from app.models import Notification, Permission, User @@ -28,7 +27,7 @@ def test_get_user_list(admin_request, sample_service): # it may have the notify user in the DB still :weary: assert len(json_resp["data"]) >= 1 sample_user = sample_service.users[0] - expected_permissions = default_service_permissions + expected_permissions = PermissionType.defaults() fetched = next(x for x in json_resp["data"] if x["id"] == str(sample_user.id)) assert sample_user.name == fetched["name"] @@ -56,7 +55,7 @@ def test_get_user(admin_request, sample_service, sample_organization): sample_user.organizations = [sample_organization] json_resp = admin_request.get("user.get_user", user_id=sample_user.id) - expected_permissions = default_service_permissions + expected_permissions = PermissionType.defaults() fetched = json_resp["data"] assert fetched["id"] == str(sample_user.id) @@ -376,7 +375,7 @@ def test_get_user_by_email(admin_request, sample_service): json_resp = admin_request.get("user.get_by_email", email=sample_user.email_address) - expected_permissions = default_service_permissions + expected_permissions = PermissionType.defaults() fetched = json_resp["data"] assert str(sample_user.id) == fetched["id"] From d7e8228cff9a1e2c6474d80bdb7e30a2bf13f543 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 09:40:27 -0500 Subject: [PATCH 152/259] Making the migration. Signed-off-by: Cliff Hill --- app/models.py | 185 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 152 insertions(+), 33 deletions(-) diff --git a/app/models.py b/app/models.py index 241f1ffd0..78307cf85 100644 --- a/app/models.py +++ b/app/models.py @@ -52,6 +52,11 @@ def filter_null_value_fields(obj): return dict(filter(lambda x: x[1] is not None, obj.items())) +def enum_values(enum_type): + """Helper function used to persist enum values to the database rather than names.""" + return [i.value for i in enum_type] + + class HistoryModel: @classmethod def from_original(cls, original): @@ -106,7 +111,11 @@ class User(db.Model): platform_admin = db.Column(db.Boolean, nullable=False, default=False) current_session_id = db.Column(UUID(as_uuid=True), nullable=True) auth_type = db.Column( - db.Enum(AuthType, name="auth_type"), + db.Enum( + AuthType, + name="auth_type", + values_callable=enum_values, + ), index=True, nullable=False, default=AuthType.SMS, @@ -280,7 +289,11 @@ class EmailBranding(db.Model): name = db.Column(db.String(255), unique=True, nullable=False) text = db.Column(db.String(255), nullable=True) brand_type = db.Column( - db.Enum(BrandType, name="brand_type"), + db.Enum( + BrandType, + name="brand_type", + values_callable=enum_values, + ), index=True, nullable=False, default=BrandType.ORG, @@ -361,7 +374,11 @@ class Organization(db.Model): ) agreement_signed_version = db.Column(db.Float, nullable=True) organization_type = db.Column( - db.Enum(OrganizationType, name="organization_type"), + db.Enum( + OrganizationType, + name="organization_type", + values_callable=enum_values, + ), unique=False, nullable=True, ) @@ -498,8 +515,11 @@ class Service(db.Model, Versioned): created_by = db.relationship("User", foreign_keys=[created_by_id]) prefix_sms = db.Column(db.Boolean, nullable=False, default=True) organization_type = db.Column( - db.String(255), - db.ForeignKey("organization_types.name"), + db.Enum( + OrganizationType, + name="organization_type", + values_callable=enum_values, + ), unique=False, nullable=True, ) @@ -772,7 +792,11 @@ class ServicePermission(db.Model): nullable=False, ) permission = db.Column( - db.Enum(ServicePermissionType, name="service_permission_type"), + db.Enum( + ServicePermissionType, + name="service_permission_type", + values_callable=enum_values, + ), index=True, primary_key=True, nullable=False, @@ -806,7 +830,11 @@ class ServiceGuestList(db.Model): ) service = db.relationship("Service", backref="guest_list") recipient_type = db.Column( - db.Enum(RecipientType, name="recipient_type"), + db.Enum( + RecipientType, + name="recipient_type", + values_callable=enum_values, + ), nullable=False, ) recipient = db.Column(db.String(255), nullable=False) @@ -895,7 +923,11 @@ class ServiceCallbackApi(db.Model, Versioned): service = db.relationship("Service", backref="service_callback_api") url = db.Column(db.String(), nullable=False) callback_type = db.Column( - db.Enum(CallbackType, name="callback_type"), + db.Enum( + CallbackType, + name="callback_type", + values_callable=enum_values, + ), nullable=True, ) _bearer_token = db.Column("bearer_token", db.String(), nullable=False) @@ -955,8 +987,11 @@ class ApiKey(db.Model, Versioned): ) service = db.relationship("Service", backref="api_keys") key_type = db.Column( - db.String(255), - db.ForeignKey("key_types.name"), + db.Enum( + KeyType, + name="key_type", + values_callable=enum_values, + ), index=True, nullable=False, ) @@ -1090,7 +1125,11 @@ class TemplateBase(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = db.Column(db.String(255), nullable=False) template_type = db.Column( - db.Enum(TemplateType, name="template_type"), + db.Enum( + TemplateType, + name="template_type", + values_callable=enum_values, + ), nullable=False, ) created_at = db.Column( @@ -1123,7 +1162,11 @@ class TemplateBase(db.Model): @declared_attr def process_type(cls): return db.Column( - db.Enum(TemplateProcessType, name="template_process_type"), + db.Enum( + TemplateProcessType, + name="template_process_type", + values_callable=enum_values, + ), index=True, nullable=False, default=TemplateProcessType.NORMAL, @@ -1292,7 +1335,11 @@ class ProviderDetails(db.Model): identifier = db.Column(db.String, nullable=False) priority = db.Column(db.Integer, nullable=False) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), + db.Enum( + NotificationType, + name="notification_type", + values_callable=enum_values, + ), nullable=False, ) active = db.Column(db.Boolean, default=False, nullable=False) @@ -1320,7 +1367,11 @@ class ProviderDetailsHistory(db.Model, HistoryModel): identifier = db.Column(db.String, nullable=False) priority = db.Column(db.Integer, nullable=False) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), + db.Enum( + NotificationType, + name="notification_type", + values_callable=enum_values, + ), nullable=False, ) active = db.Column(db.Boolean, nullable=False) @@ -1384,7 +1435,11 @@ class Job(db.Model): ) scheduled_for = db.Column(db.DateTime, index=True, unique=False, nullable=True) job_status = db.Column( - db.Enum(JobStatus, name="job_status"), + db.Enum( + JobStatus, + name="job_status", + values_callable=enum_values, + ), index=True, nullable=False, default="pending", @@ -1402,7 +1457,11 @@ class VerifyCode(db.Model): user = db.relationship("User", backref=db.backref("verify_codes", lazy="dynamic")) _code = db.Column(db.String, nullable=False) code_type = db.Column( - db.Enum(CodeType, name="code_type"), + db.Enum( + CodeType, + name="code_type", + values_callable=enum_values, + ), index=False, unique=False, nullable=False, @@ -1451,7 +1510,13 @@ class NotificationAllTimeView(db.Model): api_key_id = db.Column(UUID(as_uuid=True)) key_type = db.Column(db.String) billable_units = db.Column(db.Integer) - notification_type = db.Column(db.Enum(NotificationType, name="notification_type")) + notification_type = db.Column( + db.Enum( + NotificationType, + name="notification_type", + values_callable=enum_values, + ) + ) created_at = db.Column(db.DateTime) sent_at = db.Column(db.DateTime) sent_by = db.Column(db.String) @@ -1496,13 +1561,21 @@ class Notification(db.Model): ) api_key = db.relationship("ApiKey") key_type = db.Column( - db.Enum(KeyType, name="key_type"), + db.Enum( + KeyType, + name="key_type", + values_callable=enum_values, + ), unique=False, nullable=False, ) billable_units = db.Column(db.Integer, nullable=False, default=0) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), + db.Enum( + NotificationType, + name="notification_type", + values_callable=enum_values, + ), nullable=False, ) created_at = db.Column(db.DateTime, index=True, unique=False, nullable=False) @@ -1516,7 +1589,11 @@ class Notification(db.Model): onupdate=datetime.datetime.utcnow, ) status = db.Column( - db.Enum(NotificationStatus, name="notify_status_type"), + db.Enum( + NotificationStatus, + name="notify_status_type", + values_callable=enum_values, + ), nullable=True, default="created", key="status", # http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Column @@ -1779,14 +1856,21 @@ class NotificationHistory(db.Model, HistoryModel): ) api_key = db.relationship("ApiKey") key_type = db.Column( - db.String, - db.ForeignKey("key_types.name"), + db.Enum( + KeyType, + name="key_type", + values_callable=enum_values, + ), unique=False, nullable=False, ) billable_units = db.Column(db.Integer, nullable=False, default=0) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), + db.Enum( + NotificationType, + name="notification_type", + values_callable=enum_values, + ), nullable=False, ) created_at = db.Column(db.DateTime, unique=False, nullable=False) @@ -1801,8 +1885,11 @@ class NotificationHistory(db.Model, HistoryModel): ) status = db.Column( "notification_status", - db.Text, - db.ForeignKey("notification_status_types.name"), + db.Enum( + NotificationStatus, + nsmr="notification_status_type", + values_callable=enum_values, + ), nullable=True, default="created", key="status", # http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Column @@ -1870,13 +1957,21 @@ class InvitedUser(db.Model): default=datetime.datetime.utcnow, ) status = db.Column( - db.Enum(InvitedUserStatus, name="invited_user_status"), + db.Enum( + InvitedUserStatus, + name="invited_user_status", + values_callable=enum_values, + ), nullable=False, default=InvitedUserStatus.PENDING, ) permissions = db.Column(db.String, nullable=False) auth_type = db.Column( - db.Enum(AuthType, name="auth_type"), + db.Enum( + AuthType, + name="auth_type", + values_callable=enum_values, + ), index=True, nullable=False, default=AuthType.SMS, @@ -1913,7 +2008,11 @@ class InvitedOrganizationUser(db.Model): ) status = db.Column( - db.Enum(InvitedUserStatus, name="invited_users_status"), + db.Enum( + InvitedUserStatus, + name="invited_users_status", + values_callable=enum_values, + ), nullable=False, default=InvitedUserStatus.PENDING, ) @@ -1950,7 +2049,11 @@ class Permission(db.Model): ) user = db.relationship("User") permission = db.Column( - db.Enum(PermissionType, name="permission_type"), + db.Enum( + PermissionType, + name="permission_type", + values_callable=enum_values, + ), index=False, unique=False, nullable=False, @@ -1995,7 +2098,11 @@ class Rate(db.Model): valid_from = db.Column(db.DateTime, nullable=False) rate = db.Column(db.Float(asdecimal=False), nullable=False) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), + db.Enum( + NotificationType, + name="notification_type", + values_callable=enum_values, + ), index=True, nullable=False, ) @@ -2254,7 +2361,11 @@ class ServiceDataRetention(db.Model): ), ) notification_type = db.Column( - db.Enum(NotificationType, name="notification_type"), + db.Enum( + NotificationType, + name="notification_type", + values_callable=enum_values, + ), nullable=False, ) days_of_retention = db.Column(db.Integer, nullable=False) @@ -2343,14 +2454,22 @@ class Agreement(db.Model): unique=False, ) type = db.Column( - db.Enum(AgreementType, name="agreement_type"), + db.Enum( + AgreementType, + name="agreement_type", + values_callable=enum_values, + ), index=False, unique=False, nullable=False, ) partner_name = db.Column(db.String(255), nullable=False, unique=True, index=True) status = db.Column( - db.Enum(AgreementStatus, name="agreement_status"), + db.Enum( + AgreementStatus, + name="agreement_status", + values_callable=enum_values, + ), index=False, unique=False, nullable=False, From f4c8c3e74328bf6ae0fd48b2a26c3e8b28850eef Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 09:41:45 -0500 Subject: [PATCH 153/259] Making the migration, part 2. Signed-off-by: Cliff Hill --- .../versions/0410_enums_for_everything.py | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 migrations/versions/0410_enums_for_everything.py diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py new file mode 100644 index 000000000..f8469c9f8 --- /dev/null +++ b/migrations/versions/0410_enums_for_everything.py @@ -0,0 +1,437 @@ +""" + +Revision ID: 0410_enums_for_everything +Revises: 0409_fix_service_name +Create Date: 2024-01-18 09:37:39.485458 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +revision = '0410_enums_for_everything' +down_revision = '0409_fix_service_name' + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('notifications_all_time_view', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('job_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('job_row_number', sa.Integer(), nullable=True), + sa.Column('service_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('template_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('template_version', sa.Integer(), nullable=True), + sa.Column('api_key_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('key_type', sa.String(), nullable=True), + sa.Column('billable_units', sa.Integer(), nullable=True), + sa.Column('notification_type', sa.Enum('sms', 'email', 'letter', name='notification_type'), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('sent_at', sa.DateTime(), nullable=True), + sa.Column('sent_by', sa.String(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('notification_status', sa.Text(), nullable=True), + sa.Column('reference', sa.String(), nullable=True), + sa.Column('client_reference', sa.String(), nullable=True), + sa.Column('international', sa.Boolean(), nullable=True), + sa.Column('phone_prefix', sa.String(), nullable=True), + sa.Column('rate_multiplier', sa.Numeric(asdecimal=False), nullable=True), + sa.Column('created_by_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('document_download_count', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id'), + info={'managed_by_alembic': False} + ) + op.drop_table('notification_status_types') + op.drop_table('service_permission_types') + op.drop_table('key_types') + op.drop_table('service_callback_type') + op.drop_table('template_process_type') + op.drop_table('branding_type') + op.drop_table('invite_status_type') + op.drop_table('organization_types') + op.drop_table('job_status') + op.drop_table('auth_type') + op.alter_column('agreements', 'type', + existing_type=postgresql.ENUM('MOU', 'IAA', name='agreement_types'), + type_=sa.Enum('MOU', 'IAA', name='agreement_type'), + existing_nullable=False) + op.alter_column('agreements', 'status', + existing_type=postgresql.ENUM('active', 'expired', name='agreement_statuses'), + type_=sa.Enum('active', 'expired', name='agreement_status'), + existing_nullable=False) + op.alter_column('agreements', 'start_time', + existing_type=postgresql.TIMESTAMP(), + nullable=True) + op.alter_column('agreements', 'end_time', + existing_type=postgresql.TIMESTAMP(), + nullable=True) + op.alter_column('agreements', 'budget_amount', + existing_type=postgresql.DOUBLE_PRECISION(precision=53), + nullable=True) + op.alter_column('agreements', 'organization_id', + existing_type=postgresql.UUID(), + nullable=True) + op.create_index(op.f('ix_agreements_partner_name'), 'agreements', ['partner_name'], unique=True) + op.create_index(op.f('ix_agreements_url'), 'agreements', ['url'], unique=True) + op.create_foreign_key(None, 'agreements', 'organization', ['organization_id'], ['id']) + op.alter_column('api_keys', 'key_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('normal', 'team', 'test', name='key_type'), + existing_nullable=False) + op.drop_constraint('api_keys_key_type_fkey', 'api_keys', type_='foreignkey') + op.alter_column('api_keys_history', 'key_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('normal', 'team', 'test', name='key_type'), + existing_nullable=False) + op.alter_column('email_branding', 'brand_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('govuk', 'org', 'both', 'org_banner', name='brand_type'), + existing_nullable=False) + op.drop_constraint('email_branding_brand_type_fkey', 'email_branding', type_='foreignkey') + op.create_index(op.f('ix_inbound_sms_history_created_at'), 'inbound_sms_history', ['created_at'], unique=False) + op.alter_column('invited_organization_users', 'status', + existing_type=sa.VARCHAR(), + type_=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status'), + existing_nullable=False) + op.drop_constraint('invited_organisation_users_status_fkey', 'invited_organization_users', type_='foreignkey') + op.alter_column('invited_users', 'status', + existing_type=postgresql.ENUM('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status_types'), + type_=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_user_status'), + existing_nullable=False) + op.alter_column('invited_users', 'auth_type', + existing_type=sa.VARCHAR(), + type_=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying")) + op.drop_constraint('invited_users_auth_type_fkey', 'invited_users', type_='foreignkey') + op.alter_column('jobs', 'job_status', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('pending', 'in progress', 'finished', 'sending limits exceeded', 'scheduled', 'cancelled', 'ready to send', 'sent to dvla', 'error', name='job_status'), + existing_nullable=False) + op.drop_constraint('jobs_job_status_fkey', 'jobs', type_='foreignkey') + op.alter_column('notification_history', 'key_type', + existing_type=sa.VARCHAR(), + type_=sa.Enum('normal', 'team', 'test', name='key_type'), + existing_nullable=False) + op.alter_column('notification_history', 'notification_status', + existing_type=sa.TEXT(), + type_=sa.Enum('cancelled', 'created', 'sending', 'sent', 'delivered', 'pending', 'failed', 'technical-failure', 'temporary-failure', 'permanent-failure', 'pending-virus-check', 'validation-failed', 'virus-scan-failed', name='notificationstatus'), + existing_nullable=True) + op.drop_constraint('fk_notification_history_notification_status', 'notification_history', type_='foreignkey') + op.drop_constraint('notification_history_key_type_fkey', 'notification_history', type_='foreignkey') + op.drop_column('notification_history', 'carrier') + op.add_column('notifications', sa.Column('status', sa.Enum('cancelled', 'created', 'sending', 'sent', 'delivered', 'pending', 'failed', 'technical-failure', 'temporary-failure', 'permanent-failure', 'pending-virus-check', 'validation-failed', 'virus-scan-failed', name='notify_status_type'), nullable=True)) + op.alter_column('notifications', 'key_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('normal', 'team', 'test', name='key_type'), + existing_nullable=False) + op.alter_column('notifications', 'international', + existing_type=sa.BOOLEAN(), + nullable=False) + op.drop_index('ix_notifications_notification_type_composite', table_name='notifications') + op.create_index('ix_notifications_notification_type_composite', 'notifications', ['notification_type', 'status', 'created_at'], unique=False) + op.drop_index('ix_notifications_service_id_composite', table_name='notifications') + op.create_index('ix_notifications_service_id_composite', 'notifications', ['service_id', 'notification_type', 'status', 'created_at'], unique=False) + op.drop_constraint('notifications_key_type_fkey', 'notifications', type_='foreignkey') + op.drop_constraint('fk_notifications_notification_status', 'notifications', type_='foreignkey') + op.drop_column('notifications', 'queue_name') + op.drop_column('notifications', 'notification_status') + op.alter_column('organization', 'organization_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('federal', 'state', 'other', name='organization_type'), + existing_nullable=True) + op.drop_constraint('organisation_organisation_type_fkey', 'organization', type_='foreignkey') + op.alter_column('permissions', 'permission', + existing_type=postgresql.ENUM('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'send_letters', 'manage_api_keys', 'platform_admin', 'view_activity', 'create_broadcasts', 'approve_broadcasts', 'cancel_broadcasts', 'reject_broadcasts', name='permission_types'), + type_=sa.Enum('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'manage_api_keys', 'platform_admin', 'view_activity', name='permission_type'), + existing_nullable=False) + op.alter_column('rates', 'rate', + existing_type=sa.NUMERIC(), + type_=sa.Float(), + existing_nullable=False) + op.alter_column('service_callback_api', 'callback_type', + existing_type=sa.VARCHAR(), + type_=sa.Enum('delivery_status', 'complaint', name='callback_type'), + existing_nullable=True) + op.drop_constraint('service_callback_api_type_fk', 'service_callback_api', type_='foreignkey') + op.alter_column('service_callback_api_history', 'callback_type', + existing_type=sa.VARCHAR(), + type_=sa.Enum('delivery_status', 'complaint', name='callback_type'), + existing_nullable=True) + op.alter_column('service_permissions', 'permission', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('email', 'sms', 'international_sms', 'inbound_sms', 'schedule_notifications', 'email_auth', 'upload_document', 'edit_folder_permissions', name='service_permission_type'), + existing_nullable=False) + op.drop_constraint('service_permissions_permission_fkey', 'service_permissions', type_='foreignkey') + op.alter_column('service_sms_senders', 'sms_sender', + existing_type=sa.VARCHAR(length=255), + type_=sa.String(length=11), + existing_nullable=False) + op.alter_column('services', 'total_message_limit', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + nullable=False) + op.alter_column('services', 'organization_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('federal', 'state', 'other', name='organization_type'), + existing_nullable=True) + op.drop_index('ix_services_organisation_id', table_name='services') + op.create_index(op.f('ix_services_organization_id'), 'services', ['organization_id'], unique=False) + op.drop_constraint('services_organisation_type_fkey', 'services', type_='foreignkey') + op.alter_column('services_history', 'total_message_limit', + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + nullable=False) + op.alter_column('services_history', 'prefix_sms', + existing_type=sa.BOOLEAN(), + nullable=False) + op.alter_column('services_history', 'organization_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('federal', 'state', 'other', name='organization_type'), + existing_nullable=True) + op.drop_index('ix_services_history_organisation_id', table_name='services_history') + op.create_index(op.f('ix_services_history_organization_id'), 'services_history', ['organization_id'], unique=False) + op.alter_column('templates', 'process_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('normal', 'priority', name='template_process_type'), + existing_nullable=False) + op.drop_constraint('templates_process_type_fkey', 'templates', type_='foreignkey') + op.alter_column('templates_history', 'process_type', + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum('normal', 'priority', name='template_process_type'), + existing_nullable=False) + op.drop_constraint('templates_history_process_type_fkey', 'templates_history', type_='foreignkey') + op.drop_constraint('uix_user_to_organisation', 'user_to_organization', type_='unique') + op.create_unique_constraint('uix_user_to_organization', 'user_to_organization', ['user_id', 'organization_id']) + op.alter_column('user_to_service', 'user_id', + existing_type=postgresql.UUID(), + nullable=False) + op.alter_column('user_to_service', 'service_id', + existing_type=postgresql.UUID(), + nullable=False) + op.alter_column('users', 'auth_type', + existing_type=sa.VARCHAR(), + type_=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying")) + op.drop_constraint('users_auth_type_fkey', 'users', type_='foreignkey') + op.alter_column('verify_codes', 'code_type', + existing_type=postgresql.ENUM('email', 'sms', name='verify_code_types'), + type_=sa.Enum('email', 'sms', name='code_type'), + existing_nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('verify_codes', 'code_type', + existing_type=sa.Enum('email', 'sms', name='code_type'), + type_=postgresql.ENUM('email', 'sms', name='verify_code_types'), + existing_nullable=False) + op.create_foreign_key('users_auth_type_fkey', 'users', 'auth_type', ['auth_type'], ['name']) + op.alter_column('users', 'auth_type', + existing_type=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), + type_=sa.VARCHAR(), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying")) + op.alter_column('user_to_service', 'service_id', + existing_type=postgresql.UUID(), + nullable=True) + op.alter_column('user_to_service', 'user_id', + existing_type=postgresql.UUID(), + nullable=True) + op.drop_constraint('uix_user_to_organization', 'user_to_organization', type_='unique') + op.create_unique_constraint('uix_user_to_organisation', 'user_to_organization', ['user_id', 'organization_id']) + op.create_foreign_key('templates_history_process_type_fkey', 'templates_history', 'template_process_type', ['process_type'], ['name']) + op.alter_column('templates_history', 'process_type', + existing_type=sa.Enum('normal', 'priority', name='template_process_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.create_foreign_key('templates_process_type_fkey', 'templates', 'template_process_type', ['process_type'], ['name']) + op.alter_column('templates', 'process_type', + existing_type=sa.Enum('normal', 'priority', name='template_process_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.drop_index(op.f('ix_services_history_organization_id'), table_name='services_history') + op.create_index('ix_services_history_organisation_id', 'services_history', ['organization_id'], unique=False) + op.alter_column('services_history', 'organization_type', + existing_type=sa.Enum('federal', 'state', 'other', name='organization_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=True) + op.alter_column('services_history', 'prefix_sms', + existing_type=sa.BOOLEAN(), + nullable=True) + op.alter_column('services_history', 'total_message_limit', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + nullable=True) + op.create_foreign_key('services_organisation_type_fkey', 'services', 'organization_types', ['organization_type'], ['name']) + op.drop_index(op.f('ix_services_organization_id'), table_name='services') + op.create_index('ix_services_organisation_id', 'services', ['organization_id'], unique=False) + op.alter_column('services', 'organization_type', + existing_type=sa.Enum('federal', 'state', 'other', name='organization_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=True) + op.alter_column('services', 'total_message_limit', + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + nullable=True) + op.alter_column('service_sms_senders', 'sms_sender', + existing_type=sa.String(length=11), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.create_foreign_key('service_permissions_permission_fkey', 'service_permissions', 'service_permission_types', ['permission'], ['name']) + op.alter_column('service_permissions', 'permission', + existing_type=sa.Enum('email', 'sms', 'international_sms', 'inbound_sms', 'schedule_notifications', 'email_auth', 'upload_document', 'edit_folder_permissions', name='service_permission_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.alter_column('service_callback_api_history', 'callback_type', + existing_type=sa.Enum('delivery_status', 'complaint', name='callback_type'), + type_=sa.VARCHAR(), + existing_nullable=True) + op.create_foreign_key('service_callback_api_type_fk', 'service_callback_api', 'service_callback_type', ['callback_type'], ['name']) + op.alter_column('service_callback_api', 'callback_type', + existing_type=sa.Enum('delivery_status', 'complaint', name='callback_type'), + type_=sa.VARCHAR(), + existing_nullable=True) + op.alter_column('rates', 'rate', + existing_type=sa.Float(), + type_=sa.NUMERIC(), + existing_nullable=False) + op.alter_column('permissions', 'permission', + existing_type=sa.Enum('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'manage_api_keys', 'platform_admin', 'view_activity', name='permission_type'), + type_=postgresql.ENUM('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'send_letters', 'manage_api_keys', 'platform_admin', 'view_activity', 'create_broadcasts', 'approve_broadcasts', 'cancel_broadcasts', 'reject_broadcasts', name='permission_types'), + existing_nullable=False) + op.create_foreign_key('organisation_organisation_type_fkey', 'organization', 'organization_types', ['organization_type'], ['name']) + op.alter_column('organization', 'organization_type', + existing_type=sa.Enum('federal', 'state', 'other', name='organization_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=True) + op.add_column('notifications', sa.Column('notification_status', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('notifications', sa.Column('queue_name', sa.TEXT(), autoincrement=False, nullable=True)) + op.create_foreign_key('fk_notifications_notification_status', 'notifications', 'notification_status_types', ['notification_status'], ['name']) + op.create_foreign_key('notifications_key_type_fkey', 'notifications', 'key_types', ['key_type'], ['name']) + op.drop_index('ix_notifications_service_id_composite', table_name='notifications') + op.create_index('ix_notifications_service_id_composite', 'notifications', ['service_id', 'notification_type', 'notification_status', 'created_at'], unique=False) + op.drop_index('ix_notifications_notification_type_composite', table_name='notifications') + op.create_index('ix_notifications_notification_type_composite', 'notifications', ['notification_type', 'notification_status', 'created_at'], unique=False) + op.alter_column('notifications', 'international', + existing_type=sa.BOOLEAN(), + nullable=True) + op.alter_column('notifications', 'key_type', + existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.drop_column('notifications', 'status') + op.add_column('notification_history', sa.Column('carrier', sa.TEXT(), autoincrement=False, nullable=True)) + op.create_foreign_key('notification_history_key_type_fkey', 'notification_history', 'key_types', ['key_type'], ['name']) + op.create_foreign_key('fk_notification_history_notification_status', 'notification_history', 'notification_status_types', ['notification_status'], ['name']) + op.alter_column('notification_history', 'notification_status', + existing_type=sa.Enum('cancelled', 'created', 'sending', 'sent', 'delivered', 'pending', 'failed', 'technical-failure', 'temporary-failure', 'permanent-failure', 'pending-virus-check', 'validation-failed', 'virus-scan-failed', name='notificationstatus'), + type_=sa.TEXT(), + existing_nullable=True) + op.alter_column('notification_history', 'key_type', + existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), + type_=sa.VARCHAR(), + existing_nullable=False) + op.create_foreign_key('jobs_job_status_fkey', 'jobs', 'job_status', ['job_status'], ['name']) + op.alter_column('jobs', 'job_status', + existing_type=sa.Enum('pending', 'in progress', 'finished', 'sending limits exceeded', 'scheduled', 'cancelled', 'ready to send', 'sent to dvla', 'error', name='job_status'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.create_foreign_key('invited_users_auth_type_fkey', 'invited_users', 'auth_type', ['auth_type'], ['name']) + op.alter_column('invited_users', 'auth_type', + existing_type=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), + type_=sa.VARCHAR(), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying")) + op.alter_column('invited_users', 'status', + existing_type=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_user_status'), + type_=postgresql.ENUM('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status_types'), + existing_nullable=False) + op.create_foreign_key('invited_organisation_users_status_fkey', 'invited_organization_users', 'invite_status_type', ['status'], ['name']) + op.alter_column('invited_organization_users', 'status', + existing_type=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status'), + type_=sa.VARCHAR(), + existing_nullable=False) + op.drop_index(op.f('ix_inbound_sms_history_created_at'), table_name='inbound_sms_history') + op.create_foreign_key('email_branding_brand_type_fkey', 'email_branding', 'branding_type', ['brand_type'], ['name']) + op.alter_column('email_branding', 'brand_type', + existing_type=sa.Enum('govuk', 'org', 'both', 'org_banner', name='brand_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.alter_column('api_keys_history', 'key_type', + existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.create_foreign_key('api_keys_key_type_fkey', 'api_keys', 'key_types', ['key_type'], ['name']) + op.alter_column('api_keys', 'key_type', + existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), + type_=sa.VARCHAR(length=255), + existing_nullable=False) + op.drop_constraint(None, 'agreements', type_='foreignkey') + op.drop_index(op.f('ix_agreements_url'), table_name='agreements') + op.drop_index(op.f('ix_agreements_partner_name'), table_name='agreements') + op.alter_column('agreements', 'organization_id', + existing_type=postgresql.UUID(), + nullable=False) + op.alter_column('agreements', 'budget_amount', + existing_type=postgresql.DOUBLE_PRECISION(precision=53), + nullable=False) + op.alter_column('agreements', 'end_time', + existing_type=postgresql.TIMESTAMP(), + nullable=False) + op.alter_column('agreements', 'start_time', + existing_type=postgresql.TIMESTAMP(), + nullable=False) + op.alter_column('agreements', 'status', + existing_type=sa.Enum('active', 'expired', name='agreement_status'), + type_=postgresql.ENUM('active', 'expired', name='agreement_statuses'), + existing_nullable=False) + op.alter_column('agreements', 'type', + existing_type=sa.Enum('MOU', 'IAA', name='agreement_type'), + type_=postgresql.ENUM('MOU', 'IAA', name='agreement_types'), + existing_nullable=False) + op.create_table('auth_type', + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='auth_type_pkey') + ) + op.create_table('job_status', + sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='job_status_pkey') + ) + op.create_table('organization_types', + sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('annual_free_sms_fragment_limit', sa.BIGINT(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='organisation_types_pkey') + ) + op.create_table('invite_status_type', + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='invite_status_type_pkey') + ) + op.create_table('branding_type', + sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='branding_type_pkey') + ) + op.create_table('template_process_type', + sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='template_process_type_pkey') + ) + op.create_table('service_callback_type', + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='service_callback_type_pkey') + ) + op.create_table('key_types', + sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='key_types_pkey') + ) + op.create_table('service_permission_types', + sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='service_permission_types_pkey') + ) + op.create_table('notification_status_types', + sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('name', name='notification_status_types_pkey') + ) + op.drop_table('notifications_all_time_view') + # ### end Alembic commands ### From 655a57c0a4e926befca2017b138d9b802e0bdc0b Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 09:46:43 -0500 Subject: [PATCH 154/259] Adding some documentation to the helper function. Signed-off-by: Cliff Hill --- app/models.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index 78307cf85..97043908b 100644 --- a/app/models.py +++ b/app/models.py @@ -53,7 +53,20 @@ def filter_null_value_fields(obj): def enum_values(enum_type): - """Helper function used to persist enum values to the database rather than names.""" + """ + Helper function used to persist enum values to the database rather than names. + + See Also: + https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.Enum + + Notes: + In order to persist the values and not the names, the Enum.values_callable + parameter may be used. The value of this parameter is a user-supplied callable, + which is intended to be used with a PEP-435-compliant enumerated class and + returns a list of string values to be persisted. For a simple enumeration that + uses string values, a callable such as lambda x: [e.value for e in x] is + sufficient. + """ return [i.value for i in enum_type] From 22c4866d213d57c2ecc6e9a7288829c91ccf03c6 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 10:13:37 -0500 Subject: [PATCH 155/259] MOre cleanup of helper function. Signed-off-by: Cliff Hill --- app/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models.py b/app/models.py index 97043908b..751b63eab 100644 --- a/app/models.py +++ b/app/models.py @@ -1,4 +1,5 @@ import datetime +from enum import Enum import itertools import uuid @@ -52,7 +53,7 @@ def filter_null_value_fields(obj): return dict(filter(lambda x: x[1] is not None, obj.items())) -def enum_values(enum_type): +def enum_values(enum: Enum) -> list[str]: """ Helper function used to persist enum values to the database rather than names. @@ -67,7 +68,7 @@ def enum_values(enum_type): uses string values, a callable such as lambda x: [e.value for e in x] is sufficient. """ - return [i.value for i in enum_type] + return [i.value for i in enum] # type: ignore[attr-defined] class HistoryModel: From a89d0252fe5c79924fa7f869fb92672ee8e93390 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 10:24:34 -0500 Subject: [PATCH 156/259] Removing migration file, will rebuild soon. Signed-off-by: Cliff Hill --- .../versions/0410_enums_for_everything.py | 437 ------------------ 1 file changed, 437 deletions(-) delete mode 100644 migrations/versions/0410_enums_for_everything.py diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py deleted file mode 100644 index f8469c9f8..000000000 --- a/migrations/versions/0410_enums_for_everything.py +++ /dev/null @@ -1,437 +0,0 @@ -""" - -Revision ID: 0410_enums_for_everything -Revises: 0409_fix_service_name -Create Date: 2024-01-18 09:37:39.485458 - -""" -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -revision = '0410_enums_for_everything' -down_revision = '0409_fix_service_name' - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('notifications_all_time_view', - sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), - sa.Column('job_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('job_row_number', sa.Integer(), nullable=True), - sa.Column('service_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('template_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('template_version', sa.Integer(), nullable=True), - sa.Column('api_key_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('key_type', sa.String(), nullable=True), - sa.Column('billable_units', sa.Integer(), nullable=True), - sa.Column('notification_type', sa.Enum('sms', 'email', 'letter', name='notification_type'), nullable=True), - sa.Column('created_at', sa.DateTime(), nullable=True), - sa.Column('sent_at', sa.DateTime(), nullable=True), - sa.Column('sent_by', sa.String(), nullable=True), - sa.Column('updated_at', sa.DateTime(), nullable=True), - sa.Column('notification_status', sa.Text(), nullable=True), - sa.Column('reference', sa.String(), nullable=True), - sa.Column('client_reference', sa.String(), nullable=True), - sa.Column('international', sa.Boolean(), nullable=True), - sa.Column('phone_prefix', sa.String(), nullable=True), - sa.Column('rate_multiplier', sa.Numeric(asdecimal=False), nullable=True), - sa.Column('created_by_id', postgresql.UUID(as_uuid=True), nullable=True), - sa.Column('document_download_count', sa.Integer(), nullable=True), - sa.PrimaryKeyConstraint('id'), - info={'managed_by_alembic': False} - ) - op.drop_table('notification_status_types') - op.drop_table('service_permission_types') - op.drop_table('key_types') - op.drop_table('service_callback_type') - op.drop_table('template_process_type') - op.drop_table('branding_type') - op.drop_table('invite_status_type') - op.drop_table('organization_types') - op.drop_table('job_status') - op.drop_table('auth_type') - op.alter_column('agreements', 'type', - existing_type=postgresql.ENUM('MOU', 'IAA', name='agreement_types'), - type_=sa.Enum('MOU', 'IAA', name='agreement_type'), - existing_nullable=False) - op.alter_column('agreements', 'status', - existing_type=postgresql.ENUM('active', 'expired', name='agreement_statuses'), - type_=sa.Enum('active', 'expired', name='agreement_status'), - existing_nullable=False) - op.alter_column('agreements', 'start_time', - existing_type=postgresql.TIMESTAMP(), - nullable=True) - op.alter_column('agreements', 'end_time', - existing_type=postgresql.TIMESTAMP(), - nullable=True) - op.alter_column('agreements', 'budget_amount', - existing_type=postgresql.DOUBLE_PRECISION(precision=53), - nullable=True) - op.alter_column('agreements', 'organization_id', - existing_type=postgresql.UUID(), - nullable=True) - op.create_index(op.f('ix_agreements_partner_name'), 'agreements', ['partner_name'], unique=True) - op.create_index(op.f('ix_agreements_url'), 'agreements', ['url'], unique=True) - op.create_foreign_key(None, 'agreements', 'organization', ['organization_id'], ['id']) - op.alter_column('api_keys', 'key_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('normal', 'team', 'test', name='key_type'), - existing_nullable=False) - op.drop_constraint('api_keys_key_type_fkey', 'api_keys', type_='foreignkey') - op.alter_column('api_keys_history', 'key_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('normal', 'team', 'test', name='key_type'), - existing_nullable=False) - op.alter_column('email_branding', 'brand_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('govuk', 'org', 'both', 'org_banner', name='brand_type'), - existing_nullable=False) - op.drop_constraint('email_branding_brand_type_fkey', 'email_branding', type_='foreignkey') - op.create_index(op.f('ix_inbound_sms_history_created_at'), 'inbound_sms_history', ['created_at'], unique=False) - op.alter_column('invited_organization_users', 'status', - existing_type=sa.VARCHAR(), - type_=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status'), - existing_nullable=False) - op.drop_constraint('invited_organisation_users_status_fkey', 'invited_organization_users', type_='foreignkey') - op.alter_column('invited_users', 'status', - existing_type=postgresql.ENUM('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status_types'), - type_=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_user_status'), - existing_nullable=False) - op.alter_column('invited_users', 'auth_type', - existing_type=sa.VARCHAR(), - type_=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), - existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying")) - op.drop_constraint('invited_users_auth_type_fkey', 'invited_users', type_='foreignkey') - op.alter_column('jobs', 'job_status', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('pending', 'in progress', 'finished', 'sending limits exceeded', 'scheduled', 'cancelled', 'ready to send', 'sent to dvla', 'error', name='job_status'), - existing_nullable=False) - op.drop_constraint('jobs_job_status_fkey', 'jobs', type_='foreignkey') - op.alter_column('notification_history', 'key_type', - existing_type=sa.VARCHAR(), - type_=sa.Enum('normal', 'team', 'test', name='key_type'), - existing_nullable=False) - op.alter_column('notification_history', 'notification_status', - existing_type=sa.TEXT(), - type_=sa.Enum('cancelled', 'created', 'sending', 'sent', 'delivered', 'pending', 'failed', 'technical-failure', 'temporary-failure', 'permanent-failure', 'pending-virus-check', 'validation-failed', 'virus-scan-failed', name='notificationstatus'), - existing_nullable=True) - op.drop_constraint('fk_notification_history_notification_status', 'notification_history', type_='foreignkey') - op.drop_constraint('notification_history_key_type_fkey', 'notification_history', type_='foreignkey') - op.drop_column('notification_history', 'carrier') - op.add_column('notifications', sa.Column('status', sa.Enum('cancelled', 'created', 'sending', 'sent', 'delivered', 'pending', 'failed', 'technical-failure', 'temporary-failure', 'permanent-failure', 'pending-virus-check', 'validation-failed', 'virus-scan-failed', name='notify_status_type'), nullable=True)) - op.alter_column('notifications', 'key_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('normal', 'team', 'test', name='key_type'), - existing_nullable=False) - op.alter_column('notifications', 'international', - existing_type=sa.BOOLEAN(), - nullable=False) - op.drop_index('ix_notifications_notification_type_composite', table_name='notifications') - op.create_index('ix_notifications_notification_type_composite', 'notifications', ['notification_type', 'status', 'created_at'], unique=False) - op.drop_index('ix_notifications_service_id_composite', table_name='notifications') - op.create_index('ix_notifications_service_id_composite', 'notifications', ['service_id', 'notification_type', 'status', 'created_at'], unique=False) - op.drop_constraint('notifications_key_type_fkey', 'notifications', type_='foreignkey') - op.drop_constraint('fk_notifications_notification_status', 'notifications', type_='foreignkey') - op.drop_column('notifications', 'queue_name') - op.drop_column('notifications', 'notification_status') - op.alter_column('organization', 'organization_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('federal', 'state', 'other', name='organization_type'), - existing_nullable=True) - op.drop_constraint('organisation_organisation_type_fkey', 'organization', type_='foreignkey') - op.alter_column('permissions', 'permission', - existing_type=postgresql.ENUM('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'send_letters', 'manage_api_keys', 'platform_admin', 'view_activity', 'create_broadcasts', 'approve_broadcasts', 'cancel_broadcasts', 'reject_broadcasts', name='permission_types'), - type_=sa.Enum('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'manage_api_keys', 'platform_admin', 'view_activity', name='permission_type'), - existing_nullable=False) - op.alter_column('rates', 'rate', - existing_type=sa.NUMERIC(), - type_=sa.Float(), - existing_nullable=False) - op.alter_column('service_callback_api', 'callback_type', - existing_type=sa.VARCHAR(), - type_=sa.Enum('delivery_status', 'complaint', name='callback_type'), - existing_nullable=True) - op.drop_constraint('service_callback_api_type_fk', 'service_callback_api', type_='foreignkey') - op.alter_column('service_callback_api_history', 'callback_type', - existing_type=sa.VARCHAR(), - type_=sa.Enum('delivery_status', 'complaint', name='callback_type'), - existing_nullable=True) - op.alter_column('service_permissions', 'permission', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('email', 'sms', 'international_sms', 'inbound_sms', 'schedule_notifications', 'email_auth', 'upload_document', 'edit_folder_permissions', name='service_permission_type'), - existing_nullable=False) - op.drop_constraint('service_permissions_permission_fkey', 'service_permissions', type_='foreignkey') - op.alter_column('service_sms_senders', 'sms_sender', - existing_type=sa.VARCHAR(length=255), - type_=sa.String(length=11), - existing_nullable=False) - op.alter_column('services', 'total_message_limit', - existing_type=sa.INTEGER(), - type_=sa.BigInteger(), - nullable=False) - op.alter_column('services', 'organization_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('federal', 'state', 'other', name='organization_type'), - existing_nullable=True) - op.drop_index('ix_services_organisation_id', table_name='services') - op.create_index(op.f('ix_services_organization_id'), 'services', ['organization_id'], unique=False) - op.drop_constraint('services_organisation_type_fkey', 'services', type_='foreignkey') - op.alter_column('services_history', 'total_message_limit', - existing_type=sa.INTEGER(), - type_=sa.BigInteger(), - nullable=False) - op.alter_column('services_history', 'prefix_sms', - existing_type=sa.BOOLEAN(), - nullable=False) - op.alter_column('services_history', 'organization_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('federal', 'state', 'other', name='organization_type'), - existing_nullable=True) - op.drop_index('ix_services_history_organisation_id', table_name='services_history') - op.create_index(op.f('ix_services_history_organization_id'), 'services_history', ['organization_id'], unique=False) - op.alter_column('templates', 'process_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('normal', 'priority', name='template_process_type'), - existing_nullable=False) - op.drop_constraint('templates_process_type_fkey', 'templates', type_='foreignkey') - op.alter_column('templates_history', 'process_type', - existing_type=sa.VARCHAR(length=255), - type_=sa.Enum('normal', 'priority', name='template_process_type'), - existing_nullable=False) - op.drop_constraint('templates_history_process_type_fkey', 'templates_history', type_='foreignkey') - op.drop_constraint('uix_user_to_organisation', 'user_to_organization', type_='unique') - op.create_unique_constraint('uix_user_to_organization', 'user_to_organization', ['user_id', 'organization_id']) - op.alter_column('user_to_service', 'user_id', - existing_type=postgresql.UUID(), - nullable=False) - op.alter_column('user_to_service', 'service_id', - existing_type=postgresql.UUID(), - nullable=False) - op.alter_column('users', 'auth_type', - existing_type=sa.VARCHAR(), - type_=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), - existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying")) - op.drop_constraint('users_auth_type_fkey', 'users', type_='foreignkey') - op.alter_column('verify_codes', 'code_type', - existing_type=postgresql.ENUM('email', 'sms', name='verify_code_types'), - type_=sa.Enum('email', 'sms', name='code_type'), - existing_nullable=False) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.alter_column('verify_codes', 'code_type', - existing_type=sa.Enum('email', 'sms', name='code_type'), - type_=postgresql.ENUM('email', 'sms', name='verify_code_types'), - existing_nullable=False) - op.create_foreign_key('users_auth_type_fkey', 'users', 'auth_type', ['auth_type'], ['name']) - op.alter_column('users', 'auth_type', - existing_type=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), - type_=sa.VARCHAR(), - existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying")) - op.alter_column('user_to_service', 'service_id', - existing_type=postgresql.UUID(), - nullable=True) - op.alter_column('user_to_service', 'user_id', - existing_type=postgresql.UUID(), - nullable=True) - op.drop_constraint('uix_user_to_organization', 'user_to_organization', type_='unique') - op.create_unique_constraint('uix_user_to_organisation', 'user_to_organization', ['user_id', 'organization_id']) - op.create_foreign_key('templates_history_process_type_fkey', 'templates_history', 'template_process_type', ['process_type'], ['name']) - op.alter_column('templates_history', 'process_type', - existing_type=sa.Enum('normal', 'priority', name='template_process_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.create_foreign_key('templates_process_type_fkey', 'templates', 'template_process_type', ['process_type'], ['name']) - op.alter_column('templates', 'process_type', - existing_type=sa.Enum('normal', 'priority', name='template_process_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.drop_index(op.f('ix_services_history_organization_id'), table_name='services_history') - op.create_index('ix_services_history_organisation_id', 'services_history', ['organization_id'], unique=False) - op.alter_column('services_history', 'organization_type', - existing_type=sa.Enum('federal', 'state', 'other', name='organization_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=True) - op.alter_column('services_history', 'prefix_sms', - existing_type=sa.BOOLEAN(), - nullable=True) - op.alter_column('services_history', 'total_message_limit', - existing_type=sa.BigInteger(), - type_=sa.INTEGER(), - nullable=True) - op.create_foreign_key('services_organisation_type_fkey', 'services', 'organization_types', ['organization_type'], ['name']) - op.drop_index(op.f('ix_services_organization_id'), table_name='services') - op.create_index('ix_services_organisation_id', 'services', ['organization_id'], unique=False) - op.alter_column('services', 'organization_type', - existing_type=sa.Enum('federal', 'state', 'other', name='organization_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=True) - op.alter_column('services', 'total_message_limit', - existing_type=sa.BigInteger(), - type_=sa.INTEGER(), - nullable=True) - op.alter_column('service_sms_senders', 'sms_sender', - existing_type=sa.String(length=11), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.create_foreign_key('service_permissions_permission_fkey', 'service_permissions', 'service_permission_types', ['permission'], ['name']) - op.alter_column('service_permissions', 'permission', - existing_type=sa.Enum('email', 'sms', 'international_sms', 'inbound_sms', 'schedule_notifications', 'email_auth', 'upload_document', 'edit_folder_permissions', name='service_permission_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.alter_column('service_callback_api_history', 'callback_type', - existing_type=sa.Enum('delivery_status', 'complaint', name='callback_type'), - type_=sa.VARCHAR(), - existing_nullable=True) - op.create_foreign_key('service_callback_api_type_fk', 'service_callback_api', 'service_callback_type', ['callback_type'], ['name']) - op.alter_column('service_callback_api', 'callback_type', - existing_type=sa.Enum('delivery_status', 'complaint', name='callback_type'), - type_=sa.VARCHAR(), - existing_nullable=True) - op.alter_column('rates', 'rate', - existing_type=sa.Float(), - type_=sa.NUMERIC(), - existing_nullable=False) - op.alter_column('permissions', 'permission', - existing_type=sa.Enum('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'manage_api_keys', 'platform_admin', 'view_activity', name='permission_type'), - type_=postgresql.ENUM('manage_users', 'manage_templates', 'manage_settings', 'send_texts', 'send_emails', 'send_letters', 'manage_api_keys', 'platform_admin', 'view_activity', 'create_broadcasts', 'approve_broadcasts', 'cancel_broadcasts', 'reject_broadcasts', name='permission_types'), - existing_nullable=False) - op.create_foreign_key('organisation_organisation_type_fkey', 'organization', 'organization_types', ['organization_type'], ['name']) - op.alter_column('organization', 'organization_type', - existing_type=sa.Enum('federal', 'state', 'other', name='organization_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=True) - op.add_column('notifications', sa.Column('notification_status', sa.TEXT(), autoincrement=False, nullable=True)) - op.add_column('notifications', sa.Column('queue_name', sa.TEXT(), autoincrement=False, nullable=True)) - op.create_foreign_key('fk_notifications_notification_status', 'notifications', 'notification_status_types', ['notification_status'], ['name']) - op.create_foreign_key('notifications_key_type_fkey', 'notifications', 'key_types', ['key_type'], ['name']) - op.drop_index('ix_notifications_service_id_composite', table_name='notifications') - op.create_index('ix_notifications_service_id_composite', 'notifications', ['service_id', 'notification_type', 'notification_status', 'created_at'], unique=False) - op.drop_index('ix_notifications_notification_type_composite', table_name='notifications') - op.create_index('ix_notifications_notification_type_composite', 'notifications', ['notification_type', 'notification_status', 'created_at'], unique=False) - op.alter_column('notifications', 'international', - existing_type=sa.BOOLEAN(), - nullable=True) - op.alter_column('notifications', 'key_type', - existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.drop_column('notifications', 'status') - op.add_column('notification_history', sa.Column('carrier', sa.TEXT(), autoincrement=False, nullable=True)) - op.create_foreign_key('notification_history_key_type_fkey', 'notification_history', 'key_types', ['key_type'], ['name']) - op.create_foreign_key('fk_notification_history_notification_status', 'notification_history', 'notification_status_types', ['notification_status'], ['name']) - op.alter_column('notification_history', 'notification_status', - existing_type=sa.Enum('cancelled', 'created', 'sending', 'sent', 'delivered', 'pending', 'failed', 'technical-failure', 'temporary-failure', 'permanent-failure', 'pending-virus-check', 'validation-failed', 'virus-scan-failed', name='notificationstatus'), - type_=sa.TEXT(), - existing_nullable=True) - op.alter_column('notification_history', 'key_type', - existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), - type_=sa.VARCHAR(), - existing_nullable=False) - op.create_foreign_key('jobs_job_status_fkey', 'jobs', 'job_status', ['job_status'], ['name']) - op.alter_column('jobs', 'job_status', - existing_type=sa.Enum('pending', 'in progress', 'finished', 'sending limits exceeded', 'scheduled', 'cancelled', 'ready to send', 'sent to dvla', 'error', name='job_status'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.create_foreign_key('invited_users_auth_type_fkey', 'invited_users', 'auth_type', ['auth_type'], ['name']) - op.alter_column('invited_users', 'auth_type', - existing_type=sa.Enum('sms_auth', 'email_auth', 'webauthn_auth', name='auth_type'), - type_=sa.VARCHAR(), - existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying")) - op.alter_column('invited_users', 'status', - existing_type=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_user_status'), - type_=postgresql.ENUM('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status_types'), - existing_nullable=False) - op.create_foreign_key('invited_organisation_users_status_fkey', 'invited_organization_users', 'invite_status_type', ['status'], ['name']) - op.alter_column('invited_organization_users', 'status', - existing_type=sa.Enum('pending', 'accepted', 'cancelled', 'expired', name='invited_users_status'), - type_=sa.VARCHAR(), - existing_nullable=False) - op.drop_index(op.f('ix_inbound_sms_history_created_at'), table_name='inbound_sms_history') - op.create_foreign_key('email_branding_brand_type_fkey', 'email_branding', 'branding_type', ['brand_type'], ['name']) - op.alter_column('email_branding', 'brand_type', - existing_type=sa.Enum('govuk', 'org', 'both', 'org_banner', name='brand_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.alter_column('api_keys_history', 'key_type', - existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.create_foreign_key('api_keys_key_type_fkey', 'api_keys', 'key_types', ['key_type'], ['name']) - op.alter_column('api_keys', 'key_type', - existing_type=sa.Enum('normal', 'team', 'test', name='key_type'), - type_=sa.VARCHAR(length=255), - existing_nullable=False) - op.drop_constraint(None, 'agreements', type_='foreignkey') - op.drop_index(op.f('ix_agreements_url'), table_name='agreements') - op.drop_index(op.f('ix_agreements_partner_name'), table_name='agreements') - op.alter_column('agreements', 'organization_id', - existing_type=postgresql.UUID(), - nullable=False) - op.alter_column('agreements', 'budget_amount', - existing_type=postgresql.DOUBLE_PRECISION(precision=53), - nullable=False) - op.alter_column('agreements', 'end_time', - existing_type=postgresql.TIMESTAMP(), - nullable=False) - op.alter_column('agreements', 'start_time', - existing_type=postgresql.TIMESTAMP(), - nullable=False) - op.alter_column('agreements', 'status', - existing_type=sa.Enum('active', 'expired', name='agreement_status'), - type_=postgresql.ENUM('active', 'expired', name='agreement_statuses'), - existing_nullable=False) - op.alter_column('agreements', 'type', - existing_type=sa.Enum('MOU', 'IAA', name='agreement_type'), - type_=postgresql.ENUM('MOU', 'IAA', name='agreement_types'), - existing_nullable=False) - op.create_table('auth_type', - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='auth_type_pkey') - ) - op.create_table('job_status', - sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='job_status_pkey') - ) - op.create_table('organization_types', - sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.Column('annual_free_sms_fragment_limit', sa.BIGINT(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='organisation_types_pkey') - ) - op.create_table('invite_status_type', - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='invite_status_type_pkey') - ) - op.create_table('branding_type', - sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='branding_type_pkey') - ) - op.create_table('template_process_type', - sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='template_process_type_pkey') - ) - op.create_table('service_callback_type', - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='service_callback_type_pkey') - ) - op.create_table('key_types', - sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='key_types_pkey') - ) - op.create_table('service_permission_types', - sa.Column('name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='service_permission_types_pkey') - ) - op.create_table('notification_status_types', - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('name', name='notification_status_types_pkey') - ) - op.drop_table('notifications_all_time_view') - # ### end Alembic commands ### From ecf7b71292cc8ac4558ad8575621da8aeceba9b0 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 11:31:02 -0500 Subject: [PATCH 157/259] Cleaning up models.py to have consistant enum definitions. Signed-off-by: Cliff Hill --- app/models.py | 372 ++++++++++++++++++-------------------------------- 1 file changed, 133 insertions(+), 239 deletions(-) diff --git a/app/models.py b/app/models.py index 751b63eab..cf48c14d5 100644 --- a/app/models.py +++ b/app/models.py @@ -70,6 +70,99 @@ def enum_values(enum: Enum) -> list[str]: """ return [i.value for i in enum] # type: ignore[attr-defined] +# This has the standard definition for all of the enum columns used throughout the models. +# This is used in the enum_column() function below. +_enum_column_types = { + AuthType: db.Enum( # the key should be the Enum class to use. + AuthType, # The Enum class to use. + name="auth_types", # The name of the type defined in PostgreSQL - should be plural + values_callable=enum_values, # Every entry in this dict should contain this, note above. + ), + BrandType: db.Enum( + BrandType, + name="brand_types", + values_callable=enum_values, + ), + OrganizationType: db.Enum( + OrganizationType, + name="organization_types", + values_callable=enum_values, + ), + ServicePermissionType: db.Enum( + ServicePermissionType, + name="service_permission_types", + values_callable=enum_values, + ), + RecipientType: db.Enum( + RecipientType, + name="recipient_types", + values_callable=enum_values, + ), + CallbackType: db.Enum( + CallbackType, + name="callback_types", + values_callable=enum_values, + ), + KeyType: db.Enum( + KeyType, + name="key_types", + values_callable=enum_values, + ), + TemplateType: db.Enum( + TemplateType, + name="template_types", + values_callable=enum_values, + ), + TemplateProcessType: db.Enum( + TemplateProcessType, + name="template_process_types", + values_callable=enum_values, + ), + NotificationType: db.Enum( + NotificationType, + name="notification_types", + values_callable=enum_values, + ), + JobStatus: db.Enum( + JobStatus, + name="job_statuses", + values_callable=enum_values, + ), + CodeType: db.Enum( + CodeType, + name="code_types", + values_callable=enum_values, + ), + NotificationStatus: db.Enum( + NotificationStatus, + name="notify_statuses", + values_callable=enum_values, + ), + InvitedUserStatus: db.Enum( + InvitedUserStatus, + name="invited_user_statuses", + values_callable=enum_values, + ), + PermissionType: db.Enum( + PermissionType, + name="permission_types", + values_callable=enum_values, + ), + AgreementType: db.Enum( + AgreementType, + name="agreement_types", + values_callable=enum_values, + ), + AgreementStatus: db.Enum( + AgreementStatus, + name="agreement_statuses", + values_callable=enum_values, + ), +} + + +def enum_column(enum_type, **kwargs): + return db.Column(_enum_column_types[enum_type], **kwargs) class HistoryModel: @classmethod @@ -124,16 +217,7 @@ class User(db.Model): state = db.Column(db.String, nullable=False, default="pending") platform_admin = db.Column(db.Boolean, nullable=False, default=False) current_session_id = db.Column(UUID(as_uuid=True), nullable=True) - auth_type = db.Column( - db.Enum( - AuthType, - name="auth_type", - values_callable=enum_values, - ), - index=True, - nullable=False, - default=AuthType.SMS, - ) + auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS) email_access_validated_at = db.Column( db.DateTime, index=False, @@ -302,12 +386,8 @@ class EmailBranding(db.Model): logo = db.Column(db.String(255), nullable=True) name = db.Column(db.String(255), unique=True, nullable=False) text = db.Column(db.String(255), nullable=True) - brand_type = db.Column( - db.Enum( - BrandType, - name="brand_type", - values_callable=enum_values, - ), + brand_type = enum_column( + BrandType, index=True, nullable=False, default=BrandType.ORG, @@ -387,15 +467,7 @@ class Organization(db.Model): db.String(255), nullable=True ) agreement_signed_version = db.Column(db.Float, nullable=True) - organization_type = db.Column( - db.Enum( - OrganizationType, - name="organization_type", - values_callable=enum_values, - ), - unique=False, - nullable=True, - ) + organization_type = enum_column(OrganizationType, unique=False, nullable=True) request_to_go_live_notes = db.Column(db.Text) domains = db.relationship("Domain") @@ -528,15 +600,7 @@ class Service(db.Model, Versioned): ) created_by = db.relationship("User", foreign_keys=[created_by_id]) prefix_sms = db.Column(db.Boolean, nullable=False, default=True) - organization_type = db.Column( - db.Enum( - OrganizationType, - name="organization_type", - values_callable=enum_values, - ), - unique=False, - nullable=True, - ) + organization_type = enum_column(OrganizationType, unique=False, nullable=True) rate_limit = db.Column(db.Integer, index=False, nullable=False, default=3000) contact_link = db.Column(db.String(255), nullable=True, unique=False) volume_sms = db.Column(db.Integer(), nullable=True, unique=False) @@ -805,12 +869,8 @@ class ServicePermission(db.Model): index=True, nullable=False, ) - permission = db.Column( - db.Enum( - ServicePermissionType, - name="service_permission_type", - values_callable=enum_values, - ), + permission = enum_column( + ServicePermissionType, index=True, primary_key=True, nullable=False, @@ -843,14 +903,7 @@ class ServiceGuestList(db.Model): nullable=False, ) service = db.relationship("Service", backref="guest_list") - recipient_type = db.Column( - db.Enum( - RecipientType, - name="recipient_type", - values_callable=enum_values, - ), - nullable=False, - ) + recipient_type = enum_column(RecipientType, nullable=False) recipient = db.Column(db.String(255), nullable=False) created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow) @@ -936,14 +989,7 @@ class ServiceCallbackApi(db.Model, Versioned): ) service = db.relationship("Service", backref="service_callback_api") url = db.Column(db.String(), nullable=False) - callback_type = db.Column( - db.Enum( - CallbackType, - name="callback_type", - values_callable=enum_values, - ), - nullable=True, - ) + callback_type = enum_column(CallbackType, nullable=True) _bearer_token = db.Column("bearer_token", db.String(), nullable=False) created_at = db.Column( db.DateTime, @@ -1000,15 +1046,7 @@ class ApiKey(db.Model, Versioned): nullable=False, ) service = db.relationship("Service", backref="api_keys") - key_type = db.Column( - db.Enum( - KeyType, - name="key_type", - values_callable=enum_values, - ), - index=True, - nullable=False, - ) + key_type = enum_column(KeyType, index=True, nullable=False) expiry_date = db.Column(db.DateTime) created_at = db.Column( db.DateTime, @@ -1138,14 +1176,7 @@ class TemplateBase(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = db.Column(db.String(255), nullable=False) - template_type = db.Column( - db.Enum( - TemplateType, - name="template_type", - values_callable=enum_values, - ), - nullable=False, - ) + template_type = enum_column(TemplateType, nullable=False) created_at = db.Column( db.DateTime, nullable=False, @@ -1175,12 +1206,8 @@ class TemplateBase(db.Model): @declared_attr def process_type(cls): - return db.Column( - db.Enum( - TemplateProcessType, - name="template_process_type", - values_callable=enum_values, - ), + return enum_column( + TemplateProcessType, index=True, nullable=False, default=TemplateProcessType.NORMAL, @@ -1348,14 +1375,7 @@ class ProviderDetails(db.Model): display_name = db.Column(db.String, nullable=False) identifier = db.Column(db.String, nullable=False) priority = db.Column(db.Integer, nullable=False) - notification_type = db.Column( - db.Enum( - NotificationType, - name="notification_type", - values_callable=enum_values, - ), - nullable=False, - ) + notification_type = enum_column(NotificationType, nullable=False) active = db.Column(db.Boolean, default=False, nullable=False) version = db.Column(db.Integer, default=1, nullable=False) updated_at = db.Column( @@ -1380,14 +1400,7 @@ class ProviderDetailsHistory(db.Model, HistoryModel): display_name = db.Column(db.String, nullable=False) identifier = db.Column(db.String, nullable=False) priority = db.Column(db.Integer, nullable=False) - notification_type = db.Column( - db.Enum( - NotificationType, - name="notification_type", - values_callable=enum_values, - ), - nullable=False, - ) + notification_type = enum_column(NotificationType, nullable=False) active = db.Column(db.Boolean, nullable=False) version = db.Column(db.Integer, primary_key=True, nullable=False) updated_at = db.Column( @@ -1448,15 +1461,11 @@ class Job(db.Model): UUID(as_uuid=True), db.ForeignKey("users.id"), index=True, nullable=True ) scheduled_for = db.Column(db.DateTime, index=True, unique=False, nullable=True) - job_status = db.Column( - db.Enum( - JobStatus, - name="job_status", - values_callable=enum_values, - ), + job_status = enum_column( + JobStatus, index=True, nullable=False, - default="pending", + default=JobStatus.PENDING, ) archived = db.Column(db.Boolean, nullable=False, default=False) @@ -1470,16 +1479,7 @@ class VerifyCode(db.Model): ) user = db.relationship("User", backref=db.backref("verify_codes", lazy="dynamic")) _code = db.Column(db.String, nullable=False) - code_type = db.Column( - db.Enum( - CodeType, - name="code_type", - values_callable=enum_values, - ), - index=False, - unique=False, - nullable=False, - ) + code_type = enum_column(CodeType, index=False, unique=False, nullable=False) expiry_datetime = db.Column(db.DateTime, nullable=False) code_used = db.Column(db.Boolean, default=False) created_at = db.Column( @@ -1524,13 +1524,7 @@ class NotificationAllTimeView(db.Model): api_key_id = db.Column(UUID(as_uuid=True)) key_type = db.Column(db.String) billable_units = db.Column(db.Integer) - notification_type = db.Column( - db.Enum( - NotificationType, - name="notification_type", - values_callable=enum_values, - ) - ) + notification_type = enum_column(NotificationType) created_at = db.Column(db.DateTime) sent_at = db.Column(db.DateTime) sent_by = db.Column(db.String) @@ -1574,24 +1568,9 @@ class Notification(db.Model): unique=False, ) api_key = db.relationship("ApiKey") - key_type = db.Column( - db.Enum( - KeyType, - name="key_type", - values_callable=enum_values, - ), - unique=False, - nullable=False, - ) + key_type = enum_column(KeyType, unique=False, nullable=False) billable_units = db.Column(db.Integer, nullable=False, default=0) - notification_type = db.Column( - db.Enum( - NotificationType, - name="notification_type", - values_callable=enum_values, - ), - nullable=False, - ) + notification_type = enum_column(NotificationType, nullable=False) created_at = db.Column(db.DateTime, index=True, unique=False, nullable=False) sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True) sent_by = db.Column(db.String, nullable=True) @@ -1602,15 +1581,10 @@ class Notification(db.Model): nullable=True, onupdate=datetime.datetime.utcnow, ) - status = db.Column( - db.Enum( - NotificationStatus, - name="notify_status_type", - values_callable=enum_values, - ), + status = enum_column( + NotificationStatus, nullable=True, - default="created", - key="status", # http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Column + default=NotificationStatus.CREATED, ) reference = db.Column(db.String, nullable=True, index=True) client_reference = db.Column(db.String, index=True, nullable=True) @@ -1869,24 +1843,9 @@ class NotificationHistory(db.Model, HistoryModel): unique=False, ) api_key = db.relationship("ApiKey") - key_type = db.Column( - db.Enum( - KeyType, - name="key_type", - values_callable=enum_values, - ), - unique=False, - nullable=False, - ) + key_type = enum_column(KeyType, unique=False, nullable=False) billable_units = db.Column(db.Integer, nullable=False, default=0) - notification_type = db.Column( - db.Enum( - NotificationType, - name="notification_type", - values_callable=enum_values, - ), - nullable=False, - ) + notification_type = enum_column(NotificationType, nullable=False) created_at = db.Column(db.DateTime, unique=False, nullable=False) sent_at = db.Column(db.DateTime, index=False, unique=False, nullable=True) sent_by = db.Column(db.String, nullable=True) @@ -1897,16 +1856,10 @@ class NotificationHistory(db.Model, HistoryModel): nullable=True, onupdate=datetime.datetime.utcnow, ) - status = db.Column( - "notification_status", - db.Enum( - NotificationStatus, - nsmr="notification_status_type", - values_callable=enum_values, - ), + status = enum_column( + NotificationStatus, nullable=True, - default="created", - key="status", # http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.Column + default=NotificationStatus.CREATED, ) reference = db.Column(db.String, nullable=True, index=True) client_reference = db.Column(db.String, nullable=True) @@ -1970,26 +1923,13 @@ class InvitedUser(db.Model): nullable=False, default=datetime.datetime.utcnow, ) - status = db.Column( - db.Enum( - InvitedUserStatus, - name="invited_user_status", - values_callable=enum_values, - ), + status = enum_column( + InvitedUserStatus, nullable=False, default=InvitedUserStatus.PENDING, ) permissions = db.Column(db.String, nullable=False) - auth_type = db.Column( - db.Enum( - AuthType, - name="auth_type", - values_callable=enum_values, - ), - index=True, - nullable=False, - default=AuthType.SMS, - ) + auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS) folder_permissions = db.Column(JSONB(none_as_null=True), nullable=False, default=[]) # would like to have used properties for this but haven't found a way to make them @@ -2021,12 +1961,8 @@ class InvitedOrganizationUser(db.Model): default=datetime.datetime.utcnow, ) - status = db.Column( - db.Enum( - InvitedUserStatus, - name="invited_users_status", - values_callable=enum_values, - ), + status = enum_column( + InvitedUserStatus, nullable=False, default=InvitedUserStatus.PENDING, ) @@ -2062,16 +1998,7 @@ class Permission(db.Model): nullable=False, ) user = db.relationship("User") - permission = db.Column( - db.Enum( - PermissionType, - name="permission_type", - values_callable=enum_values, - ), - index=False, - unique=False, - nullable=False, - ) + permission = enum_column(PermissionType, index=False, unique=False, nullable=False) created_at = db.Column( db.DateTime, index=False, @@ -2111,15 +2038,7 @@ class Rate(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) valid_from = db.Column(db.DateTime, nullable=False) rate = db.Column(db.Float(asdecimal=False), nullable=False) - notification_type = db.Column( - db.Enum( - NotificationType, - name="notification_type", - values_callable=enum_values, - ), - index=True, - nullable=False, - ) + notification_type = enum_column(NotificationType, index=True, nullable=False) def __str__(self): the_string = "{}".format(self.rate) @@ -2374,14 +2293,7 @@ class ServiceDataRetention(db.Model): collection_class=attribute_mapped_collection("notification_type"), ), ) - notification_type = db.Column( - db.Enum( - NotificationType, - name="notification_type", - values_callable=enum_values, - ), - nullable=False, - ) + notification_type = enum_column(NotificationType, nullable=False) days_of_retention = db.Column(db.Integer, nullable=False) created_at = db.Column( db.DateTime, @@ -2467,27 +2379,9 @@ class Agreement(db.Model): default=uuid.uuid4, unique=False, ) - type = db.Column( - db.Enum( - AgreementType, - name="agreement_type", - values_callable=enum_values, - ), - index=False, - unique=False, - nullable=False, - ) + type = enum_column(AgreementType, index=False, unique=False, nullable=False) partner_name = db.Column(db.String(255), nullable=False, unique=True, index=True) - status = db.Column( - db.Enum( - AgreementStatus, - name="agreement_status", - values_callable=enum_values, - ), - index=False, - unique=False, - nullable=False, - ) + status = enum_column(AgreementStatus, index=False, unique=False, nullable=False) start_time = db.Column(db.DateTime, nullable=True) end_time = db.Column(db.DateTime, nullable=True) url = db.Column(db.String(255), nullable=False, unique=True, index=True) From f6297a3c06fe5b8e0ee0118561703e8a3e9dff0f Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 18 Jan 2024 16:58:06 -0500 Subject: [PATCH 158/259] Making migration (better!) Signed-off-by: Cliff Hill --- app/models.py | 21 +- .../versions/0410_enums_for_everything.py | 954 ++++++++++++++++++ 2 files changed, 968 insertions(+), 7 deletions(-) create mode 100644 migrations/versions/0410_enums_for_everything.py diff --git a/app/models.py b/app/models.py index cf48c14d5..b3711931e 100644 --- a/app/models.py +++ b/app/models.py @@ -1,7 +1,7 @@ import datetime -from enum import Enum import itertools import uuid +from enum import Enum from flask import current_app, url_for from notifications_utils.clients.encryption.encryption_client import EncryptionError @@ -70,13 +70,14 @@ def enum_values(enum: Enum) -> list[str]: """ return [i.value for i in enum] # type: ignore[attr-defined] + # This has the standard definition for all of the enum columns used throughout the models. # This is used in the enum_column() function below. _enum_column_types = { - AuthType: db.Enum( # the key should be the Enum class to use. - AuthType, # The Enum class to use. - name="auth_types", # The name of the type defined in PostgreSQL - should be plural - values_callable=enum_values, # Every entry in this dict should contain this, note above. + AuthType: db.Enum( # the key should be the Enum class to use. + AuthType, # The Enum class to use. + name="auth_types", # The name of the type defined in PostgreSQL - should be plural + values_callable=enum_values, # Every entry in this dict should contain this, note above. ), BrandType: db.Enum( BrandType, @@ -161,8 +162,12 @@ _enum_column_types = { } -def enum_column(enum_type, **kwargs): - return db.Column(_enum_column_types[enum_type], **kwargs) +def enum_column(enum_type, name=None, **kwargs): + if name is None: + return db.Column(_enum_column_types[enum_type], **kwargs) + else: + return db.Column(name, _enum_column_types[enum_type], **kwargs) + class HistoryModel: @classmethod @@ -1583,6 +1588,7 @@ class Notification(db.Model): ) status = enum_column( NotificationStatus, + name="notification_status", nullable=True, default=NotificationStatus.CREATED, ) @@ -1858,6 +1864,7 @@ class NotificationHistory(db.Model, HistoryModel): ) status = enum_column( NotificationStatus, + name="notification_status", nullable=True, default=NotificationStatus.CREATED, ) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py new file mode 100644 index 000000000..3f0f4c493 --- /dev/null +++ b/migrations/versions/0410_enums_for_everything.py @@ -0,0 +1,954 @@ +""" + +Revision ID: 0410_enums_for_everything +Revises: 0409_fix_service_name +Create Date: 2024-01-18 12:34:32.857422 + +""" +from enum import Enum +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql + +from app.enums import AuthType, BrandType, InvitedUserStatus, JobStatus, KeyType, NotificationStatus + +revision = "0410_enums_for_everything" +down_revision = "0409_fix_service_name" + +_enum_params = { + KeyType: {"values": ["normal, team, test"], "name": "key_types"}, + BrandType: { + "values": ["govuk", "org", "both", "org_banner"], + "name": "brand_types", + }, + InvitedUserStatus: { + "values": ["pending", "accepted", "cancelled", "expired"], + "name": "invited_user_statuses", + }, + AuthType: { + "values": ["sms_auth", "email_auth", "webauthn_auth"], + "name": "auth_types", + }, + JobStatus: { + "values": [ + "pending", + "in progress", + "finished", + "sending limits exceeded", + "scheduled", + "cancelled", + "ready to send", + "sent to dvla", + "error", + ], + "name": "job_statuses", + }, + NotificationStatus: { + "values": [ + "cancelled", + "created", + "sending", + "sent", + "delivered", + "pending", + "failed", + "technical-failure", + "temporary-failure", + "permanent-failure", + "pending-virus-check", + "validation-failed", + "virus-scan-failed", + ], + "name": "notify_statuses", + }, +} + + +def enum_type(enum: Enum) -> sa.Enum: + return sa.Enum(*_enum_params[enum]["values"], name=_enum_params[enum]["name"]) + + +def upgrade(): + # Remove foreign key constraints for old "helper" tables. + op.drop_constraint("api_keys_key_type_fkey", "api_keys", type_="foreignkey") + op.drop_constraint( + "email_branding_brand_type_fkey", "email_branding", type_="foreignkey" + ) + op.drop_constraint( + "invited_organisation_users_status_fkey", + "invited_organization_users", + type_="foreignkey", + ) + op.drop_constraint( + "invited_users_auth_type_fkey", "invited_users", type_="foreignkey" + ) + op.drop_constraint("jobs_job_status_fkey", "jobs", type_="foreignkey") + op.drop_constraint( + "notification_history_key_type_fkey", "notification_history", type_="foreignkey" + ) + op.drop_constraint( + "fk_notification_history_notification_status", + "notification_history", + type_="foreignkey", + ) + op.drop_constraint( + "notifications_key_type_fkey", "notifications", type_="foreignkey" + ) + op.drop_constraint( + "fk_notifications_notification_status", "notifications", type_="foreignkey" + ) + op.drop_constraint( + "organisation_organisation_type_fkey", "organization", type_="foreignkey" + ) + op.drop_constraint( + "service_callback_api_type_fk", "service_callback_api", type_="foreignkey" + ) + op.drop_constraint( + "service_permissions_permission_fkey", "service_permissions", type_="foreignkey" + ) + op.drop_constraint( + "services_organisation_type_fkey", "services", type_="foreignkey" + ) + op.drop_constraint("templates_process_type_fkey", "templates", type_="foreignkey") + op.drop_constraint( + "templates_history_process_type_fkey", "templates_history", type_="foreignkey" + ) + op.drop_constraint( + "uix_user_to_organisation", "user_to_organization", type_="unique" + ) + op.drop_constraint("users_auth_type_fkey", "users", type_="foreignkey") + + # Drop composite indexes + op.drop_index("ix_notifications_service_id_composite", table_name="notifications") + op.drop_index( + "ix_notifications_notification_type_composite", table_name="notifications" + ) + op.drop_index("ix_services_organisation_id", table_name="services") + op.drop_index("ix_services_history_organisation_id", table_name="services_history") + + # drop old "helper" tables + op.drop_table("template_process_type") + op.drop_table("key_types") + op.drop_table("service_callback_type") + op.drop_table("auth_type") + op.drop_table("organization_types") + op.drop_table("invite_status_type") + op.drop_table("branding_type") + op.drop_table("notification_status_types") + op.drop_table("job_status") + op.drop_table("service_permission_types") + + # alter existing columns to use new enums + op.alter_column( + "api_keys", + "key_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(KeyType), + existing_nullable=False, + ) + op.alter_column( + "api_keys_history", + "key_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(KeyType), + existing_nullable=False, + ) + op.alter_column( + "email_branding", + "brand_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(BrandType), + existing_nullable=False, + ) + op.alter_column( + "invited_organization_users", + "status", + existing_type=sa.VARCHAR(), + type_=enum_type(InvitedUserStatus), + existing_nullable=False, + ) + op.alter_column( + "invited_users", + "status", + existing_type=postgresql.ENUM( + "pending", + "accepted", + "cancelled", + "expired", + name="invited_users_status_types", + ), + type_=enum_type(InvitedUserStatus), + existing_nullable=False, + ) + op.alter_column( + "invited_users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=enum_type(AuthType), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "jobs", + "job_status", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(JobStatus), + existing_nullable=False, + ) + op.alter_column( + 'notification_history', + 'notification_status', + existing_type=sa.TEXT(), + type_=enum_type(NotificationStatus), + existing_nullable=True, + ) + op.alter_column( + "notification_history", + "key_type", + existing_type=sa.VARCHAR(), + type_=enum_type(KeyType), + existing_nullable=False, + ) + op.alter_column( + "notification_history", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_nullable=False, + ) + op.alter_column( + 'notifications', + 'notification_status', + existing_type=sa.TEXT(), + type_=enum_type(NotificationStatus), + existing_nullable=True, + ) + op.alter_column( + "notifications", + "key_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(KeyType), + existing_nullable=False, + ) + op.alter_column( + "notifications", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_nullable=False, + ) + op.alter_column( + "notifications", "international", existing_type=sa.BOOLEAN(), nullable=False + ) + op.alter_column( + "organization", + "organization_type", + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum("federal", "state", "other", name="organization_types"), + existing_nullable=True, + ) + op.alter_column( + "provider_details", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_nullable=False, + ) + op.alter_column( + "provider_details_history", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_nullable=False, + ) + op.alter_column( + "rates", + "rate", + existing_type=sa.NUMERIC(), + type_=sa.Float(), + existing_nullable=False, + ) + op.alter_column( + "rates", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_nullable=False, + ) + op.alter_column( + "service_callback_api", + "callback_type", + existing_type=sa.VARCHAR(), + type_=sa.Enum("delivery_status", "complaint", name="callback_types"), + existing_nullable=True, + ) + op.alter_column( + "service_callback_api_history", + "callback_type", + existing_type=sa.VARCHAR(), + type_=sa.Enum("delivery_status", "complaint", name="callback_types"), + existing_nullable=True, + ) + op.alter_column( + "service_data_retention", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_nullable=False, + ) + op.alter_column( + "service_permissions", + "permission", + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum( + "email", + "sms", + "international_sms", + "inbound_sms", + "schedule_notifications", + "email_auth", + "upload_document", + "edit_folder_permissions", + name="service_permission_types", + ), + existing_nullable=False, + ) + op.alter_column( + "service_sms_senders", + "sms_sender", + existing_type=sa.VARCHAR(length=255), + type_=sa.String(length=11), + existing_nullable=False, + ) + op.alter_column( + "service_whitelist", + "recipient_type", + existing_type=postgresql.ENUM("mobile", "email", name="recipient_type"), + type_=sa.Enum("mobile", "email", name="recipient_types"), + existing_nullable=False, + ) + op.alter_column( + "services", + "total_message_limit", + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + nullable=False, + ) + op.alter_column( + "services", + "organization_type", + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum("federal", "state", "other", name="organization_types"), + existing_nullable=True, + ) + op.alter_column( + "services_history", + "total_message_limit", + existing_type=sa.INTEGER(), + type_=sa.BigInteger(), + nullable=False, + ) + op.alter_column( + "services_history", "prefix_sms", existing_type=sa.BOOLEAN(), nullable=False + ) + op.alter_column( + "services_history", + "organization_type", + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum("federal", "state", "other", name="organization_types"), + existing_nullable=True, + ) + op.alter_column( + "templates", + "template_type", + existing_type=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + type_=sa.Enum("sms", "email", "letter", name="template_types"), + existing_nullable=False, + ) + op.alter_column( + "templates", + "process_type", + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum("normal", "priority", name="template_process_types"), + existing_nullable=False, + ) + op.alter_column( + "templates_history", + "template_type", + existing_type=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + type_=sa.Enum("sms", "email", "letter", name="template_types"), + existing_nullable=False, + ) + op.alter_column( + "templates_history", + "process_type", + existing_type=sa.VARCHAR(length=255), + type_=sa.Enum("normal", "priority", name="template_process_types"), + existing_nullable=False, + ) + op.alter_column( + "user_to_service", "user_id", existing_type=postgresql.UUID(), nullable=False + ) + op.alter_column( + "user_to_service", "service_id", existing_type=postgresql.UUID(), nullable=False + ) + op.alter_column( + "users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=enum_type(AuthType), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "verify_codes", + "code_type", + existing_type=postgresql.ENUM("email", "sms", name="verify_code_types"), + type_=sa.Enum("email", "sms", name="code_types"), + existing_nullable=False, + ) + + # Recreate composite indexes + op.create_index( + "ix_notifications_service_id_composite", + "notifications", + ["service_id", "notification_type", "status", "created_at"], + unique=False, + ) + op.create_index( + "ix_notifications_notification_type_composite", + "notifications", + ["notification_type", "status", "created_at"], + unique=False, + ) + op.create_index( + op.f("ix_services_organization_id"), + "services", + ["organization_id"], + unique=False, + ) + op.create_index( + op.f("ix_services_history_organization_id"), + "services_history", + ["organization_id"], + unique=False, + ) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column( + "verify_codes", + "code_type", + existing_type=sa.Enum("email", "sms", name="code_types"), + type_=postgresql.ENUM("email", "sms", name="verify_code_types"), + existing_nullable=False, + ) + op.create_foreign_key( + "users_auth_type_fkey", "users", "auth_type", ["auth_type"], ["name"] + ) + op.alter_column( + "users", + "auth_type", + existing_type=enum_type(AuthType), + type_=sa.VARCHAR(), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "user_to_service", "service_id", existing_type=postgresql.UUID(), nullable=True + ) + op.alter_column( + "user_to_service", "user_id", existing_type=postgresql.UUID(), nullable=True + ) + op.drop_constraint( + "uix_user_to_organization", "user_to_organization", type_="unique" + ) + op.create_unique_constraint( + "uix_user_to_organisation", + "user_to_organization", + ["user_id", "organization_id"], + ) + op.create_foreign_key( + "templates_history_process_type_fkey", + "templates_history", + "template_process_type", + ["process_type"], + ["name"], + ) + op.alter_column( + "templates_history", + "process_type", + existing_type=sa.Enum("normal", "priority", name="template_process_types"), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "templates_history", + "template_type", + existing_type=sa.Enum("sms", "email", "letter", name="template_types"), + type_=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + existing_nullable=False, + ) + op.create_foreign_key( + "templates_process_type_fkey", + "templates", + "template_process_type", + ["process_type"], + ["name"], + ) + op.alter_column( + "templates", + "process_type", + existing_type=sa.Enum("normal", "priority", name="template_process_types"), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "templates", + "template_type", + existing_type=sa.Enum("sms", "email", "letter", name="template_types"), + type_=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + existing_nullable=False, + ) + op.drop_index( + op.f("ix_services_history_organization_id"), table_name="services_history" + ) + op.create_index( + "ix_services_history_organisation_id", + "services_history", + ["organization_id"], + unique=False, + ) + op.alter_column( + "services_history", + "organization_type", + existing_type=sa.Enum("federal", "state", "other", name="organization_types"), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "services_history", "prefix_sms", existing_type=sa.BOOLEAN(), nullable=True + ) + op.alter_column( + "services_history", + "total_message_limit", + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + nullable=True, + ) + op.create_foreign_key( + "services_organisation_type_fkey", + "services", + "organization_types", + ["organization_type"], + ["name"], + ) + op.drop_index(op.f("ix_services_organization_id"), table_name="services") + op.create_index( + "ix_services_organisation_id", "services", ["organization_id"], unique=False + ) + op.alter_column( + "services", + "organization_type", + existing_type=sa.Enum("federal", "state", "other", name="organization_types"), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "services", + "total_message_limit", + existing_type=sa.BigInteger(), + type_=sa.INTEGER(), + nullable=True, + ) + op.alter_column( + "service_whitelist", + "recipient_type", + existing_type=sa.Enum("mobile", "email", name="recipient_types"), + type_=postgresql.ENUM("mobile", "email", name="recipient_type"), + existing_nullable=False, + ) + op.alter_column( + "service_sms_senders", + "sms_sender", + existing_type=sa.String(length=11), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.create_foreign_key( + "service_permissions_permission_fkey", + "service_permissions", + "service_permission_types", + ["permission"], + ["name"], + ) + op.alter_column( + "service_permissions", + "permission", + existing_type=sa.Enum( + "email", + "sms", + "international_sms", + "inbound_sms", + "schedule_notifications", + "email_auth", + "upload_document", + "edit_folder_permissions", + name="service_permission_types", + ), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "service_data_retention", + "notification_type", + existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + ) + op.alter_column( + "service_callback_api_history", + "callback_type", + existing_type=sa.Enum("delivery_status", "complaint", name="callback_types"), + type_=sa.VARCHAR(), + existing_nullable=True, + ) + op.create_foreign_key( + "service_callback_api_type_fk", + "service_callback_api", + "service_callback_type", + ["callback_type"], + ["name"], + ) + op.alter_column( + "service_callback_api", + "callback_type", + existing_type=sa.Enum("delivery_status", "complaint", name="callback_types"), + type_=sa.VARCHAR(), + existing_nullable=True, + ) + op.alter_column( + "rates", + "notification_type", + existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + ) + op.alter_column( + "rates", + "rate", + existing_type=sa.Float(), + type_=sa.NUMERIC(), + existing_nullable=False, + ) + op.alter_column( + "provider_details_history", + "notification_type", + existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + ) + op.alter_column( + "provider_details", + "notification_type", + existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + ) + op.create_foreign_key( + "organisation_organisation_type_fkey", + "organization", + "organization_types", + ["organization_type"], + ["name"], + ) + op.alter_column( + "organization", + "organization_type", + existing_type=sa.Enum("federal", "state", "other", name="organization_types"), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + 'notifications', + 'notification_status', + existing_type=enum_type(NotificationStatus), + type_=sa.TEXT(), + existing_nullable=True + ) + op.add_column( + "notifications", + sa.Column("queue_name", sa.TEXT(), autoincrement=False, nullable=True), + ) + op.create_foreign_key( + "fk_notifications_notification_status", + "notifications", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notifications_key_type_fkey", + "notifications", + "key_types", + ["key_type"], + ["name"], + ) + op.drop_index("ix_notifications_service_id_composite", table_name="notifications") + op.create_index( + "ix_notifications_service_id_composite", + "notifications", + ["service_id", "notification_type", "notification_status", "created_at"], + unique=False, + ) + op.drop_index( + "ix_notifications_notification_type_composite", table_name="notifications" + ) + op.create_index( + "ix_notifications_notification_type_composite", + "notifications", + ["notification_type", "notification_status", "created_at"], + unique=False, + ) + op.alter_column( + "notifications", "international", existing_type=sa.BOOLEAN(), nullable=True + ) + op.alter_column( + "notifications", + "notification_type", + existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + ) + op.alter_column( + "notifications", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + 'notification_history', + 'notification_status', + existing_type=enum_type(NotificationStatus), + type_=sa.TEXT(), + existing_nullable=True + ) + + op.add_column( + "notification_history", + sa.Column("carrier", sa.TEXT(), autoincrement=False, nullable=True), + ) + op.create_foreign_key( + "fk_notification_history_notification_status", + "notification_history", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notification_history_key_type_fkey", + "notification_history", + "key_types", + ["key_type"], + ["name"], + ) + op.alter_column( + "notification_history", + "notification_type", + existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + ) + op.alter_column( + "notification_history", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(), + existing_nullable=False, + ) + op.create_foreign_key( + "jobs_job_status_fkey", "jobs", "job_status", ["job_status"], ["name"] + ) + op.alter_column( + "jobs", + "job_status", + existing_type=enum_type(JobStatus), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.create_foreign_key( + "invited_users_auth_type_fkey", + "invited_users", + "auth_type", + ["auth_type"], + ["name"], + ) + op.alter_column( + "invited_users", + "auth_type", + existing_type=enum_type(AuthType), + type_=sa.VARCHAR(), + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "invited_users", + "status", + existing_type=enum_type(InvitedUserStatus), + type_=postgresql.ENUM( + "pending", + "accepted", + "cancelled", + "expired", + name="invited_users_status_types", + ), + existing_nullable=False, + ) + op.create_foreign_key( + "invited_organisation_users_status_fkey", + "invited_organization_users", + "invite_status_type", + ["status"], + ["name"], + ) + op.alter_column( + "invited_organization_users", + "status", + existing_type=enum_type(InvitedUserStatus), + type_=sa.VARCHAR(), + existing_nullable=False, + ) + op.drop_index( + op.f("ix_inbound_sms_history_created_at"), table_name="inbound_sms_history" + ) + op.create_foreign_key( + "email_branding_brand_type_fkey", + "email_branding", + "branding_type", + ["brand_type"], + ["name"], + ) + op.alter_column( + "email_branding", + "brand_type", + existing_type=enum_type(BrandType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "api_keys_history", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.create_foreign_key( + "api_keys_key_type_fkey", "api_keys", "key_types", ["key_type"], ["name"] + ) + op.alter_column( + "api_keys", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.drop_constraint(None, "agreements", type_="foreignkey") + op.drop_index(op.f("ix_agreements_url"), table_name="agreements") + op.drop_index(op.f("ix_agreements_partner_name"), table_name="agreements") + op.alter_column( + "agreements", "organization_id", existing_type=postgresql.UUID(), nullable=False + ) + op.alter_column( + "agreements", + "budget_amount", + existing_type=postgresql.DOUBLE_PRECISION(precision=53), + nullable=False, + ) + op.alter_column( + "agreements", "end_time", existing_type=postgresql.TIMESTAMP(), nullable=False + ) + op.alter_column( + "agreements", "start_time", existing_type=postgresql.TIMESTAMP(), nullable=False + ) + op.create_table( + "service_permission_types", + sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="service_permission_types_pkey"), + ) + op.create_table( + "job_status", + sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="job_status_pkey"), + ) + op.create_table( + "notification_status_types", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="notification_status_types_pkey"), + ) + op.create_table( + "branding_type", + sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="branding_type_pkey"), + ) + op.create_table( + "invite_status_type", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="invite_status_type_pkey"), + ) + op.create_table( + "organization_types", + sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column( + "annual_free_sms_fragment_limit", + sa.BIGINT(), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("name", name="organisation_types_pkey"), + ) + op.create_table( + "auth_type", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="auth_type_pkey"), + ) + op.create_table( + "service_callback_type", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="service_callback_type_pkey"), + ) + op.create_table( + "key_types", + sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="key_types_pkey"), + ) + op.create_table( + "template_process_type", + sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="template_process_type_pkey"), + ) + op.drop_table("notifications_all_time_view") + # ### end Alembic commands ### From 8c50f393dcbdee1f109f8f125d735426603b8d4c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 19 Jan 2024 14:49:52 -0500 Subject: [PATCH 159/259] More stuff is being done to get the migration to work. Signed-off-by: Cliff Hill --- .../versions/0410_enums_for_everything.py | 146 +++++++++++------- 1 file changed, 89 insertions(+), 57 deletions(-) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index 3f0f4c493..b1ff29ce8 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -6,17 +6,38 @@ Create Date: 2024-01-18 12:34:32.857422 """ from enum import Enum +from typing import TypedDict import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql -from app.enums import AuthType, BrandType, InvitedUserStatus, JobStatus, KeyType, NotificationStatus +from app.enums import ( + AuthType, + BrandType, + CallbackType, + InvitedUserStatus, + JobStatus, + KeyType, + NotificationStatus, + NotificationType, + OrganizationType, + RecipientType, + ServicePermissionType, + TemplateProcessType, + TemplateType, +) revision = "0410_enums_for_everything" down_revision = "0409_fix_service_name" -_enum_params = { - KeyType: {"values": ["normal, team, test"], "name": "key_types"}, + +class EnumValues(TypedDict): + values: list[str] + name: str + + +_enum_params: dict[Enum, EnumValues] = { + KeyType: {"values": ["normal", "team", "test"], "name": "key_types"}, BrandType: { "values": ["govuk", "org", "both", "org_banner"], "name": "brand_types", @@ -61,6 +82,37 @@ _enum_params = { ], "name": "notify_statuses", }, + NotificationType: { + "values": ["sms", "email", "letter"], + "name": "notification_types", + }, + OrganizationType: { + "values": ["federal", "state", "other"], + "name": "organization_types", + }, + CallbackType: { + "values": ["delivery_status", "complaint"], + "name": "callback_types", + }, + ServicePermissionType: { + "values": [ + "email", + "sms", + "international_sms", + "inbound_sms", + "schedule_notifications", + "email_auth", + "upload_document", + "edit_folder_permissions", + ], + "name": "service_permission_types", + }, + RecipientType: {"values": ["mobile", "email"], "name": "recipient_types"}, + TemplateType: {"values": ["sms", "email", "letter"], "name": "template_types"}, + TemplateProcessType: { + "values": ["normal", "priority"], + "name": "template_process_types" + }, } @@ -215,7 +267,7 @@ def upgrade(): existing_type=postgresql.ENUM( "email", "sms", "letter", name="notification_type" ), - type_=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=enum_type(NotificationType), existing_nullable=False, ) op.alter_column( @@ -238,7 +290,7 @@ def upgrade(): existing_type=postgresql.ENUM( "email", "sms", "letter", name="notification_type" ), - type_=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=enum_type(NotificationType), existing_nullable=False, ) op.alter_column( @@ -248,7 +300,7 @@ def upgrade(): "organization", "organization_type", existing_type=sa.VARCHAR(length=255), - type_=sa.Enum("federal", "state", "other", name="organization_types"), + type_=enum_type(OrganizationType), existing_nullable=True, ) op.alter_column( @@ -257,7 +309,7 @@ def upgrade(): existing_type=postgresql.ENUM( "email", "sms", "letter", name="notification_type" ), - type_=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=enum_type(NotificationType), existing_nullable=False, ) op.alter_column( @@ -266,7 +318,7 @@ def upgrade(): existing_type=postgresql.ENUM( "email", "sms", "letter", name="notification_type" ), - type_=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=enum_type(NotificationType), existing_nullable=False, ) op.alter_column( @@ -282,21 +334,21 @@ def upgrade(): existing_type=postgresql.ENUM( "email", "sms", "letter", name="notification_type" ), - type_=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=enum_type(NotificationType), existing_nullable=False, ) op.alter_column( "service_callback_api", "callback_type", existing_type=sa.VARCHAR(), - type_=sa.Enum("delivery_status", "complaint", name="callback_types"), + type_=enum_type(CallbackType), existing_nullable=True, ) op.alter_column( "service_callback_api_history", "callback_type", existing_type=sa.VARCHAR(), - type_=sa.Enum("delivery_status", "complaint", name="callback_types"), + type_=enum_type(CallbackType), existing_nullable=True, ) op.alter_column( @@ -305,24 +357,14 @@ def upgrade(): existing_type=postgresql.ENUM( "email", "sms", "letter", name="notification_type" ), - type_=sa.Enum("sms", "email", "letter", name="notification_types"), + type_=enum_type(NotificationType), existing_nullable=False, ) op.alter_column( "service_permissions", "permission", existing_type=sa.VARCHAR(length=255), - type_=sa.Enum( - "email", - "sms", - "international_sms", - "inbound_sms", - "schedule_notifications", - "email_auth", - "upload_document", - "edit_folder_permissions", - name="service_permission_types", - ), + type_=enum_type(ServicePermissionType), existing_nullable=False, ) op.alter_column( @@ -336,7 +378,7 @@ def upgrade(): "service_whitelist", "recipient_type", existing_type=postgresql.ENUM("mobile", "email", name="recipient_type"), - type_=sa.Enum("mobile", "email", name="recipient_types"), + type_=enum_type(RecipientType), existing_nullable=False, ) op.alter_column( @@ -350,7 +392,7 @@ def upgrade(): "services", "organization_type", existing_type=sa.VARCHAR(length=255), - type_=sa.Enum("federal", "state", "other", name="organization_types"), + type_=enum_type(OrganizationType), existing_nullable=True, ) op.alter_column( @@ -367,7 +409,7 @@ def upgrade(): "services_history", "organization_type", existing_type=sa.VARCHAR(length=255), - type_=sa.Enum("federal", "state", "other", name="organization_types"), + type_=enum_type(OrganizationType), existing_nullable=True, ) op.alter_column( @@ -376,14 +418,14 @@ def upgrade(): existing_type=postgresql.ENUM( "sms", "email", "letter", "broadcast", name="template_type" ), - type_=sa.Enum("sms", "email", "letter", name="template_types"), + type_=enum_type(TemplateType), existing_nullable=False, ) op.alter_column( "templates", "process_type", existing_type=sa.VARCHAR(length=255), - type_=sa.Enum("normal", "priority", name="template_process_types"), + type_=enum_type(TemplateProcessType), existing_nullable=False, ) op.alter_column( @@ -392,14 +434,14 @@ def upgrade(): existing_type=postgresql.ENUM( "sms", "email", "letter", "broadcast", name="template_type" ), - type_=sa.Enum("sms", "email", "letter", name="template_types"), + type_=enum_type(TemplateType), existing_nullable=False, ) op.alter_column( "templates_history", "process_type", existing_type=sa.VARCHAR(length=255), - type_=sa.Enum("normal", "priority", name="template_process_types"), + type_=enum_type(TemplateProcessType), existing_nullable=False, ) op.alter_column( @@ -497,14 +539,14 @@ def downgrade(): op.alter_column( "templates_history", "process_type", - existing_type=sa.Enum("normal", "priority", name="template_process_types"), + existing_type=enum_type(TemplateProcessType), type_=sa.VARCHAR(length=255), existing_nullable=False, ) op.alter_column( "templates_history", "template_type", - existing_type=sa.Enum("sms", "email", "letter", name="template_types"), + existing_type=enum_type(TemplateType), type_=postgresql.ENUM( "sms", "email", "letter", "broadcast", name="template_type" ), @@ -520,14 +562,14 @@ def downgrade(): op.alter_column( "templates", "process_type", - existing_type=sa.Enum("normal", "priority", name="template_process_types"), + existing_type=enum_type(TemplateProcessType), type_=sa.VARCHAR(length=255), existing_nullable=False, ) op.alter_column( "templates", "template_type", - existing_type=sa.Enum("sms", "email", "letter", name="template_types"), + existing_type=enum_type(TemplateType), type_=postgresql.ENUM( "sms", "email", "letter", "broadcast", name="template_type" ), @@ -545,7 +587,7 @@ def downgrade(): op.alter_column( "services_history", "organization_type", - existing_type=sa.Enum("federal", "state", "other", name="organization_types"), + existing_type=enum_type(OrganizationType), type_=sa.VARCHAR(length=255), existing_nullable=True, ) @@ -573,7 +615,7 @@ def downgrade(): op.alter_column( "services", "organization_type", - existing_type=sa.Enum("federal", "state", "other", name="organization_types"), + existing_type=enum_type(OrganizationType), type_=sa.VARCHAR(length=255), existing_nullable=True, ) @@ -587,7 +629,7 @@ def downgrade(): op.alter_column( "service_whitelist", "recipient_type", - existing_type=sa.Enum("mobile", "email", name="recipient_types"), + existing_type=enum_type(RecipientType), type_=postgresql.ENUM("mobile", "email", name="recipient_type"), existing_nullable=False, ) @@ -608,31 +650,21 @@ def downgrade(): op.alter_column( "service_permissions", "permission", - existing_type=sa.Enum( - "email", - "sms", - "international_sms", - "inbound_sms", - "schedule_notifications", - "email_auth", - "upload_document", - "edit_folder_permissions", - name="service_permission_types", - ), + existing_type=enum_type(ServicePermissionType), type_=sa.VARCHAR(length=255), existing_nullable=False, ) op.alter_column( "service_data_retention", "notification_type", - existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) op.alter_column( "service_callback_api_history", "callback_type", - existing_type=sa.Enum("delivery_status", "complaint", name="callback_types"), + existing_type=enum_type(CallbackType), type_=sa.VARCHAR(), existing_nullable=True, ) @@ -646,14 +678,14 @@ def downgrade(): op.alter_column( "service_callback_api", "callback_type", - existing_type=sa.Enum("delivery_status", "complaint", name="callback_types"), + existing_type=enum_type(CallbackType), type_=sa.VARCHAR(), existing_nullable=True, ) op.alter_column( "rates", "notification_type", - existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) @@ -667,14 +699,14 @@ def downgrade(): op.alter_column( "provider_details_history", "notification_type", - existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) op.alter_column( "provider_details", "notification_type", - existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) @@ -688,7 +720,7 @@ def downgrade(): op.alter_column( "organization", "organization_type", - existing_type=sa.Enum("federal", "state", "other", name="organization_types"), + existing_type=enum_type(OrganizationType), type_=sa.VARCHAR(length=255), existing_nullable=True, ) @@ -739,7 +771,7 @@ def downgrade(): op.alter_column( "notifications", "notification_type", - existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) @@ -779,7 +811,7 @@ def downgrade(): op.alter_column( "notification_history", "notification_type", - existing_type=sa.Enum("sms", "email", "letter", name="notification_types"), + existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) From d7ad4edf8089d97c223a5de294519afe0097b637 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 19 Jan 2024 15:17:29 -0500 Subject: [PATCH 160/259] More cleanup of migration. Signed-off-by: Cliff Hill --- .../versions/0410_enums_for_everything.py | 410 ++++++------------ 1 file changed, 136 insertions(+), 274 deletions(-) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index b1ff29ce8..ce26675c2 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -7,6 +7,7 @@ Create Date: 2024-01-18 12:34:32.857422 """ from enum import Enum from typing import TypedDict + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql @@ -15,6 +16,7 @@ from app.enums import ( AuthType, BrandType, CallbackType, + CodeType, InvitedUserStatus, JobStatus, KeyType, @@ -111,8 +113,9 @@ _enum_params: dict[Enum, EnumValues] = { TemplateType: {"values": ["sms", "email", "letter"], "name": "template_types"}, TemplateProcessType: { "values": ["normal", "priority"], - "name": "template_process_types" + "name": "template_process_types", }, + CodeType: {"values": ["email", "sms"], "name": "code_types"}, } @@ -165,9 +168,6 @@ def upgrade(): op.drop_constraint( "templates_history_process_type_fkey", "templates_history", type_="foreignkey" ) - op.drop_constraint( - "uix_user_to_organisation", "user_to_organization", type_="unique" - ) op.drop_constraint("users_auth_type_fkey", "users", type_="foreignkey") # Drop composite indexes @@ -175,8 +175,6 @@ def upgrade(): op.drop_index( "ix_notifications_notification_type_composite", table_name="notifications" ) - op.drop_index("ix_services_organisation_id", table_name="services") - op.drop_index("ix_services_history_organisation_id", table_name="services_history") # drop old "helper" tables op.drop_table("template_process_type") @@ -248,8 +246,8 @@ def upgrade(): existing_nullable=False, ) op.alter_column( - 'notification_history', - 'notification_status', + "notification_history", + "notification_status", existing_type=sa.TEXT(), type_=enum_type(NotificationStatus), existing_nullable=True, @@ -271,8 +269,8 @@ def upgrade(): existing_nullable=False, ) op.alter_column( - 'notifications', - 'notification_status', + "notifications", + "notification_status", existing_type=sa.TEXT(), type_=enum_type(NotificationStatus), existing_nullable=True, @@ -321,13 +319,6 @@ def upgrade(): type_=enum_type(NotificationType), existing_nullable=False, ) - op.alter_column( - "rates", - "rate", - existing_type=sa.NUMERIC(), - type_=sa.Float(), - existing_nullable=False, - ) op.alter_column( "rates", "notification_type", @@ -367,13 +358,6 @@ def upgrade(): type_=enum_type(ServicePermissionType), existing_nullable=False, ) - op.alter_column( - "service_sms_senders", - "sms_sender", - existing_type=sa.VARCHAR(length=255), - type_=sa.String(length=11), - existing_nullable=False, - ) op.alter_column( "service_whitelist", "recipient_type", @@ -381,13 +365,6 @@ def upgrade(): type_=enum_type(RecipientType), existing_nullable=False, ) - op.alter_column( - "services", - "total_message_limit", - existing_type=sa.INTEGER(), - type_=sa.BigInteger(), - nullable=False, - ) op.alter_column( "services", "organization_type", @@ -395,16 +372,6 @@ def upgrade(): type_=enum_type(OrganizationType), existing_nullable=True, ) - op.alter_column( - "services_history", - "total_message_limit", - existing_type=sa.INTEGER(), - type_=sa.BigInteger(), - nullable=False, - ) - op.alter_column( - "services_history", "prefix_sms", existing_type=sa.BOOLEAN(), nullable=False - ) op.alter_column( "services_history", "organization_type", @@ -444,12 +411,6 @@ def upgrade(): type_=enum_type(TemplateProcessType), existing_nullable=False, ) - op.alter_column( - "user_to_service", "user_id", existing_type=postgresql.UUID(), nullable=False - ) - op.alter_column( - "user_to_service", "service_id", existing_type=postgresql.UUID(), nullable=False - ) op.alter_column( "users", "auth_type", @@ -462,7 +423,7 @@ def upgrade(): "verify_codes", "code_type", existing_type=postgresql.ENUM("email", "sms", name="verify_code_types"), - type_=sa.Enum("email", "sms", name="code_types"), + type_=enum_type(CodeType), existing_nullable=False, ) @@ -479,34 +440,121 @@ def upgrade(): ["notification_type", "status", "created_at"], unique=False, ) - op.create_index( - op.f("ix_services_organization_id"), - "services", - ["organization_id"], - unique=False, - ) - op.create_index( - op.f("ix_services_history_organization_id"), - "services_history", - ["organization_id"], - unique=False, - ) # ### end Alembic commands ### def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### + # Recreate foreign keys + op.create_foreign_key( + "users_auth_type_fkey", "users", "auth_type", ["auth_type"], ["name"] + ) + op.create_foreign_key( + "templates_history_process_type_fkey", + "templates_history", + "template_process_type", + ["process_type"], + ["name"], + ) + op.create_foreign_key( + "templates_process_type_fkey", + "templates", + "template_process_type", + ["process_type"], + ["name"], + ) + op.create_foreign_key( + "services_organisation_type_fkey", + "services", + "organization_types", + ["organization_type"], + ["name"], + ) + op.create_foreign_key( + "service_permissions_permission_fkey", + "service_permissions", + "service_permission_types", + ["permission"], + ["name"], + ) + op.create_foreign_key( + "service_callback_api_type_fk", + "service_callback_api", + "service_callback_type", + ["callback_type"], + ["name"], + ) + op.create_foreign_key( + "organisation_organisation_type_fkey", + "organization", + "organization_types", + ["organization_type"], + ["name"], + ) + op.create_foreign_key( + "fk_notifications_notification_status", + "notifications", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notifications_key_type_fkey", + "notifications", + "key_types", + ["key_type"], + ["name"], + ) + op.create_foreign_key( + "fk_notification_history_notification_status", + "notification_history", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notification_history_key_type_fkey", + "notification_history", + "key_types", + ["key_type"], + ["name"], + ) + op.create_foreign_key( + "jobs_job_status_fkey", "jobs", "job_status", ["job_status"], ["name"] + ) + op.create_foreign_key( + "invited_users_auth_type_fkey", + "invited_users", + "auth_type", + ["auth_type"], + ["name"], + ) + op.create_foreign_key( + "invited_organisation_users_status_fkey", + "invited_organization_users", + "invite_status_type", + ["status"], + ["name"], + ) + op.create_foreign_key( + "email_branding_brand_type_fkey", + "email_branding", + "branding_type", + ["brand_type"], + ["name"], + ) + op.create_foreign_key( + "api_keys_key_type_fkey", "api_keys", "key_types", ["key_type"], ["name"] + ) + + # Alter columns back op.alter_column( "verify_codes", "code_type", - existing_type=sa.Enum("email", "sms", name="code_types"), + existing_type=enum_type(CodeType), type_=postgresql.ENUM("email", "sms", name="verify_code_types"), existing_nullable=False, ) - op.create_foreign_key( - "users_auth_type_fkey", "users", "auth_type", ["auth_type"], ["name"] - ) op.alter_column( "users", "auth_type", @@ -515,27 +563,6 @@ def downgrade(): existing_nullable=False, existing_server_default=sa.text("'sms_auth'::character varying"), ) - op.alter_column( - "user_to_service", "service_id", existing_type=postgresql.UUID(), nullable=True - ) - op.alter_column( - "user_to_service", "user_id", existing_type=postgresql.UUID(), nullable=True - ) - op.drop_constraint( - "uix_user_to_organization", "user_to_organization", type_="unique" - ) - op.create_unique_constraint( - "uix_user_to_organisation", - "user_to_organization", - ["user_id", "organization_id"], - ) - op.create_foreign_key( - "templates_history_process_type_fkey", - "templates_history", - "template_process_type", - ["process_type"], - ["name"], - ) op.alter_column( "templates_history", "process_type", @@ -552,13 +579,6 @@ def downgrade(): ), existing_nullable=False, ) - op.create_foreign_key( - "templates_process_type_fkey", - "templates", - "template_process_type", - ["process_type"], - ["name"], - ) op.alter_column( "templates", "process_type", @@ -575,15 +595,6 @@ def downgrade(): ), existing_nullable=False, ) - op.drop_index( - op.f("ix_services_history_organization_id"), table_name="services_history" - ) - op.create_index( - "ix_services_history_organisation_id", - "services_history", - ["organization_id"], - unique=False, - ) op.alter_column( "services_history", "organization_type", @@ -591,27 +602,6 @@ def downgrade(): type_=sa.VARCHAR(length=255), existing_nullable=True, ) - op.alter_column( - "services_history", "prefix_sms", existing_type=sa.BOOLEAN(), nullable=True - ) - op.alter_column( - "services_history", - "total_message_limit", - existing_type=sa.BigInteger(), - type_=sa.INTEGER(), - nullable=True, - ) - op.create_foreign_key( - "services_organisation_type_fkey", - "services", - "organization_types", - ["organization_type"], - ["name"], - ) - op.drop_index(op.f("ix_services_organization_id"), table_name="services") - op.create_index( - "ix_services_organisation_id", "services", ["organization_id"], unique=False - ) op.alter_column( "services", "organization_type", @@ -619,13 +609,6 @@ def downgrade(): type_=sa.VARCHAR(length=255), existing_nullable=True, ) - op.alter_column( - "services", - "total_message_limit", - existing_type=sa.BigInteger(), - type_=sa.INTEGER(), - nullable=True, - ) op.alter_column( "service_whitelist", "recipient_type", @@ -633,20 +616,6 @@ def downgrade(): type_=postgresql.ENUM("mobile", "email", name="recipient_type"), existing_nullable=False, ) - op.alter_column( - "service_sms_senders", - "sms_sender", - existing_type=sa.String(length=11), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.create_foreign_key( - "service_permissions_permission_fkey", - "service_permissions", - "service_permission_types", - ["permission"], - ["name"], - ) op.alter_column( "service_permissions", "permission", @@ -668,13 +637,6 @@ def downgrade(): type_=sa.VARCHAR(), existing_nullable=True, ) - op.create_foreign_key( - "service_callback_api_type_fk", - "service_callback_api", - "service_callback_type", - ["callback_type"], - ["name"], - ) op.alter_column( "service_callback_api", "callback_type", @@ -689,13 +651,6 @@ def downgrade(): type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) - op.alter_column( - "rates", - "rate", - existing_type=sa.Float(), - type_=sa.NUMERIC(), - existing_nullable=False, - ) op.alter_column( "provider_details_history", "notification_type", @@ -710,13 +665,6 @@ def downgrade(): type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, ) - op.create_foreign_key( - "organisation_organisation_type_fkey", - "organization", - "organization_types", - ["organization_type"], - ["name"], - ) op.alter_column( "organization", "organization_type", @@ -725,48 +673,11 @@ def downgrade(): existing_nullable=True, ) op.alter_column( - 'notifications', - 'notification_status', + "notifications", + "notification_status", existing_type=enum_type(NotificationStatus), type_=sa.TEXT(), - existing_nullable=True - ) - op.add_column( - "notifications", - sa.Column("queue_name", sa.TEXT(), autoincrement=False, nullable=True), - ) - op.create_foreign_key( - "fk_notifications_notification_status", - "notifications", - "notification_status_types", - ["notification_status"], - ["name"], - ) - op.create_foreign_key( - "notifications_key_type_fkey", - "notifications", - "key_types", - ["key_type"], - ["name"], - ) - op.drop_index("ix_notifications_service_id_composite", table_name="notifications") - op.create_index( - "ix_notifications_service_id_composite", - "notifications", - ["service_id", "notification_type", "notification_status", "created_at"], - unique=False, - ) - op.drop_index( - "ix_notifications_notification_type_composite", table_name="notifications" - ) - op.create_index( - "ix_notifications_notification_type_composite", - "notifications", - ["notification_type", "notification_status", "created_at"], - unique=False, - ) - op.alter_column( - "notifications", "international", existing_type=sa.BOOLEAN(), nullable=True + existing_nullable=True, ) op.alter_column( "notifications", @@ -783,30 +694,11 @@ def downgrade(): existing_nullable=False, ) op.alter_column( - 'notification_history', - 'notification_status', + "notification_history", + "notification_status", existing_type=enum_type(NotificationStatus), type_=sa.TEXT(), - existing_nullable=True - ) - - op.add_column( - "notification_history", - sa.Column("carrier", sa.TEXT(), autoincrement=False, nullable=True), - ) - op.create_foreign_key( - "fk_notification_history_notification_status", - "notification_history", - "notification_status_types", - ["notification_status"], - ["name"], - ) - op.create_foreign_key( - "notification_history_key_type_fkey", - "notification_history", - "key_types", - ["key_type"], - ["name"], + existing_nullable=True, ) op.alter_column( "notification_history", @@ -822,9 +714,6 @@ def downgrade(): type_=sa.VARCHAR(), existing_nullable=False, ) - op.create_foreign_key( - "jobs_job_status_fkey", "jobs", "job_status", ["job_status"], ["name"] - ) op.alter_column( "jobs", "job_status", @@ -832,13 +721,6 @@ def downgrade(): type_=sa.VARCHAR(length=255), existing_nullable=False, ) - op.create_foreign_key( - "invited_users_auth_type_fkey", - "invited_users", - "auth_type", - ["auth_type"], - ["name"], - ) op.alter_column( "invited_users", "auth_type", @@ -860,13 +742,6 @@ def downgrade(): ), existing_nullable=False, ) - op.create_foreign_key( - "invited_organisation_users_status_fkey", - "invited_organization_users", - "invite_status_type", - ["status"], - ["name"], - ) op.alter_column( "invited_organization_users", "status", @@ -874,16 +749,6 @@ def downgrade(): type_=sa.VARCHAR(), existing_nullable=False, ) - op.drop_index( - op.f("ix_inbound_sms_history_created_at"), table_name="inbound_sms_history" - ) - op.create_foreign_key( - "email_branding_brand_type_fkey", - "email_branding", - "branding_type", - ["brand_type"], - ["name"], - ) op.alter_column( "email_branding", "brand_type", @@ -898,9 +763,6 @@ def downgrade(): type_=sa.VARCHAR(length=255), existing_nullable=False, ) - op.create_foreign_key( - "api_keys_key_type_fkey", "api_keys", "key_types", ["key_type"], ["name"] - ) op.alter_column( "api_keys", "key_type", @@ -908,24 +770,26 @@ def downgrade(): type_=sa.VARCHAR(length=255), existing_nullable=False, ) - op.drop_constraint(None, "agreements", type_="foreignkey") - op.drop_index(op.f("ix_agreements_url"), table_name="agreements") - op.drop_index(op.f("ix_agreements_partner_name"), table_name="agreements") - op.alter_column( - "agreements", "organization_id", existing_type=postgresql.UUID(), nullable=False + + # Composite Index stuff + op.drop_index("ix_notifications_service_id_composite", table_name="notifications") + op.create_index( + "ix_notifications_service_id_composite", + "notifications", + ["service_id", "notification_type", "notification_status", "created_at"], + unique=False, ) - op.alter_column( - "agreements", - "budget_amount", - existing_type=postgresql.DOUBLE_PRECISION(precision=53), - nullable=False, + op.drop_index( + "ix_notifications_notification_type_composite", table_name="notifications" ) - op.alter_column( - "agreements", "end_time", existing_type=postgresql.TIMESTAMP(), nullable=False - ) - op.alter_column( - "agreements", "start_time", existing_type=postgresql.TIMESTAMP(), nullable=False + op.create_index( + "ix_notifications_notification_type_composite", + "notifications", + ["notification_type", "notification_status", "created_at"], + unique=False, ) + + # Recreate helper tables op.create_table( "service_permission_types", sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), @@ -982,5 +846,3 @@ def downgrade(): sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="template_process_type_pkey"), ) - op.drop_table("notifications_all_time_view") - # ### end Alembic commands ### From cbf73ffcf2357f4321145a668927be2306f50622 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 19 Jan 2024 23:26:31 -0500 Subject: [PATCH 161/259] Migration file organized. Need a few more things. Signed-off-by: Cliff Hill --- .../versions/0410_enums_for_everything.py | 238 +++++++++--------- 1 file changed, 120 insertions(+), 118 deletions(-) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index ce26675c2..b67caef0f 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -445,106 +445,10 @@ def upgrade(): def downgrade(): - # Recreate foreign keys - op.create_foreign_key( - "users_auth_type_fkey", "users", "auth_type", ["auth_type"], ["name"] - ) - op.create_foreign_key( - "templates_history_process_type_fkey", - "templates_history", - "template_process_type", - ["process_type"], - ["name"], - ) - op.create_foreign_key( - "templates_process_type_fkey", - "templates", - "template_process_type", - ["process_type"], - ["name"], - ) - op.create_foreign_key( - "services_organisation_type_fkey", - "services", - "organization_types", - ["organization_type"], - ["name"], - ) - op.create_foreign_key( - "service_permissions_permission_fkey", - "service_permissions", - "service_permission_types", - ["permission"], - ["name"], - ) - op.create_foreign_key( - "service_callback_api_type_fk", - "service_callback_api", - "service_callback_type", - ["callback_type"], - ["name"], - ) - op.create_foreign_key( - "organisation_organisation_type_fkey", - "organization", - "organization_types", - ["organization_type"], - ["name"], - ) - op.create_foreign_key( - "fk_notifications_notification_status", - "notifications", - "notification_status_types", - ["notification_status"], - ["name"], - ) - op.create_foreign_key( - "notifications_key_type_fkey", - "notifications", - "key_types", - ["key_type"], - ["name"], - ) - op.create_foreign_key( - "fk_notification_history_notification_status", - "notification_history", - "notification_status_types", - ["notification_status"], - ["name"], - ) - op.create_foreign_key( - "notification_history_key_type_fkey", - "notification_history", - "key_types", - ["key_type"], - ["name"], - ) - op.create_foreign_key( - "jobs_job_status_fkey", "jobs", "job_status", ["job_status"], ["name"] - ) - op.create_foreign_key( - "invited_users_auth_type_fkey", - "invited_users", - "auth_type", - ["auth_type"], - ["name"], - ) - op.create_foreign_key( - "invited_organisation_users_status_fkey", - "invited_organization_users", - "invite_status_type", - ["status"], - ["name"], - ) - op.create_foreign_key( - "email_branding_brand_type_fkey", - "email_branding", - "branding_type", - ["brand_type"], - ["name"], - ) - op.create_foreign_key( - "api_keys_key_type_fkey", "api_keys", "key_types", ["key_type"], ["name"] + # Dropping composite indexes + op.drop_index("ix_notifications_service_id_composite", table_name="notifications") + op.drop_index( + "ix_notifications_notification_type_composite", table_name="notifications" ) # Alter columns back @@ -771,24 +675,6 @@ def downgrade(): existing_nullable=False, ) - # Composite Index stuff - op.drop_index("ix_notifications_service_id_composite", table_name="notifications") - op.create_index( - "ix_notifications_service_id_composite", - "notifications", - ["service_id", "notification_type", "notification_status", "created_at"], - unique=False, - ) - op.drop_index( - "ix_notifications_notification_type_composite", table_name="notifications" - ) - op.create_index( - "ix_notifications_notification_type_composite", - "notifications", - ["notification_type", "notification_status", "created_at"], - unique=False, - ) - # Recreate helper tables op.create_table( "service_permission_types", @@ -846,3 +732,119 @@ def downgrade(): sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="template_process_type_pkey"), ) + + # Creating composite indexes + op.create_index( + "ix_notifications_service_id_composite", + "notifications", + ["service_id", "notification_type", "notification_status", "created_at"], + unique=False, + ) + op.create_index( + "ix_notifications_notification_type_composite", + "notifications", + ["notification_type", "notification_status", "created_at"], + unique=False, + ) + + # Recreate foreign keys + op.create_foreign_key( + "users_auth_type_fkey", "users", "auth_type", ["auth_type"], ["name"] + ) + op.create_foreign_key( + "templates_history_process_type_fkey", + "templates_history", + "template_process_type", + ["process_type"], + ["name"], + ) + op.create_foreign_key( + "templates_process_type_fkey", + "templates", + "template_process_type", + ["process_type"], + ["name"], + ) + op.create_foreign_key( + "services_organisation_type_fkey", + "services", + "organization_types", + ["organization_type"], + ["name"], + ) + op.create_foreign_key( + "service_permissions_permission_fkey", + "service_permissions", + "service_permission_types", + ["permission"], + ["name"], + ) + op.create_foreign_key( + "service_callback_api_type_fk", + "service_callback_api", + "service_callback_type", + ["callback_type"], + ["name"], + ) + op.create_foreign_key( + "organisation_organisation_type_fkey", + "organization", + "organization_types", + ["organization_type"], + ["name"], + ) + op.create_foreign_key( + "fk_notifications_notification_status", + "notifications", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notifications_key_type_fkey", + "notifications", + "key_types", + ["key_type"], + ["name"], + ) + op.create_foreign_key( + "fk_notification_history_notification_status", + "notification_history", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notification_history_key_type_fkey", + "notification_history", + "key_types", + ["key_type"], + ["name"], + ) + op.create_foreign_key( + "jobs_job_status_fkey", "jobs", "job_status", ["job_status"], ["name"] + ) + op.create_foreign_key( + "invited_users_auth_type_fkey", + "invited_users", + "auth_type", + ["auth_type"], + ["name"], + ) + op.create_foreign_key( + "invited_organisation_users_status_fkey", + "invited_organization_users", + "invite_status_type", + ["status"], + ["name"], + ) + op.create_foreign_key( + "email_branding_brand_type_fkey", + "email_branding", + "branding_type", + ["brand_type"], + ["name"], + ) + op.create_foreign_key( + "api_keys_key_type_fkey", "api_keys", "key_types", ["key_type"], ["name"] + ) From 0fbe67b04818b7c18e711dbd1572d57d373779de Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 23 Jan 2024 10:08:45 -0500 Subject: [PATCH 162/259] Migration is set up, need to test. Signed-off-by: Cliff Hill --- app/enums.py | 2 - .../versions/0410_enums_for_everything.py | 125 ++++++++++++++++-- 2 files changed, 115 insertions(+), 12 deletions(-) diff --git a/app/enums.py b/app/enums.py index 860132129..74d6a2100 100644 --- a/app/enums.py +++ b/app/enums.py @@ -188,8 +188,6 @@ class InvitedUserStatus(Enum): class BrandType(Enum): - # TODO: Should EmailBranding.branding_type be changed to use this? - GOVUK = "govuk" # Deprecated outside migrations ORG = "org" BOTH = "both" ORG_BANNER = "org_banner" diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index b67caef0f..6334819ed 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -676,32 +676,99 @@ def downgrade(): ) # Recreate helper tables - op.create_table( + service_permission_types = op.create_table( "service_permission_types", sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="service_permission_types_pkey"), ) - op.create_table( + op.bulk_insert( + service_permission_types, + [ + {"name": "letter"}, + {"name": "international_sms"}, + {"name": "sms"}, + {"name": "email"}, + {"name": "inbound_sms"}, + {"name": "schedule_notifications"}, + {"name": "email_auth"}, + {"name": "letters_as_pdf"}, + {"name": "upload_document"}, + {"name": "edit_folder_permissions"}, + {"name": "upload_letters"}, + {"name": "international_letters"}, + {"name": "broadcast}"}, + ], + ) + job_status = op.create_table( "job_status", sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="job_status_pkey"), ) - op.create_table( + op.bulk_insert( + job_status, + [ + {"name": "pending"}, + {"name": "in progress"}, + {"name": "finished"}, + {"name": "sending limits exceeded"}, + {"name": "scheduled"}, + {"name": "cancelled"}, + {"name": "ready to send"}, + {"name": "sent to dvla"}, + {"name": "error"}, + ], + ) + notification_status_types = op.create_table( "notification_status_types", sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="notification_status_types_pkey"), ) - op.create_table( + op.bulk_insert( + notification_status_types, + [ + {"name": "created"}, + {"name": "pending"}, + {"name": "temporary-failure"}, + {"name": "delivered"}, + {"name": "sent"}, + {"name": "sending"}, + {"name": "failed"}, + {"name": "permanent-failure"}, + {"name": "technical-failure"}, + {"name": "pending-virus-check"}, + {"name": "virus-scan-failed"}, + {"name": "cancelled"}, + {"name": "returned-letter"}, + {"name": "validation-failed"}, + ], + ) + branding_type = op.create_table( "branding_type", sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="branding_type_pkey"), ) - op.create_table( + op.bulk_insert( + branding_type, + [ + {"name": "org"}, + {"name": "both"}, + {"name": "org_banner"}, + ], + ) + invite_status_type = op.create_table( "invite_status_type", sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="invite_status_type_pkey"), ) - op.create_table( + op.bulk_insert( + invite_status_type, + [ + {"name": "pending"}, + {"name": "accepted"}, + {"name": "cancelled"}, + ], + ) + organization_types = op.create_table( "organization_types", sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.Column( @@ -712,26 +779,64 @@ def downgrade(): ), sa.PrimaryKeyConstraint("name", name="organisation_types_pkey"), ) - op.create_table( + op.bulk_insert( + organization_types, + [ + {"name": "other", "annual_free_sms_fragment_limit": 25000}, + {"name": "state", "annual_free_sms_fragment_limit": 250000}, + {"name": "federal", "annual_free_sms_fragment_limit": 250000}, + ], + ) + auth_type = op.create_table( "auth_type", sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="auth_type_pkey"), ) - op.create_table( + op.bulk_insert( + auth_type, + [ + {"name": "email_auth"}, + {"name": "sms_auth"}, + {"name": "webauthn_auth"}, + ], + ) + service_callback_type = op.create_table( "service_callback_type", sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="service_callback_type_pkey"), ) - op.create_table( + op.bulk_insert( + service_callback_type, + [ + {"name": "delivery_status"}, + {"name": "complaint"}, + ], + ) + key_types = op.create_table( "key_types", sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="key_types_pkey"), ) - op.create_table( + op.bulk_insert( + key_types, + [ + {"name": "normal"}, + {"name": "team"}, + {"name": "test"}, + ], + ) + template_process_type = op.create_table( "template_process_type", sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), sa.PrimaryKeyConstraint("name", name="template_process_type_pkey"), ) + op.bulk_insert( + template_process_type, + [ + {"name": "normal"}, + {"name": "priority"}, + ], + ) # Creating composite indexes op.create_index( From 6a8f7ffeb35e734d9e2d02b8daeb22f13d08108b Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 25 Jan 2024 09:55:10 -0500 Subject: [PATCH 163/259] More migration fun. Signed-off-by: Cliff Hill --- app/models.py | 141 ++++-------------- .../versions/0410_enums_for_everything.py | 63 +++++++- 2 files changed, 89 insertions(+), 115 deletions(-) diff --git a/app/models.py b/app/models.py index b3711931e..1244f617c 100644 --- a/app/models.py +++ b/app/models.py @@ -14,7 +14,7 @@ from notifications_utils.recipients import ( ) from notifications_utils.template import PlainTextEmailTemplate, SMSMessageTemplate from sqlalchemy import CheckConstraint, Index, UniqueConstraint -from sqlalchemy.dialects.postgresql import JSON, JSONB, UUID +from sqlalchemy.dialects.postgresql import JSON, JSONB, UUID, ENUM from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import validates @@ -53,120 +53,35 @@ def filter_null_value_fields(obj): return dict(filter(lambda x: x[1] is not None, obj.items())) -def enum_values(enum: Enum) -> list[str]: - """ - Helper function used to persist enum values to the database rather than names. - - See Also: - https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.Enum - - Notes: - In order to persist the values and not the names, the Enum.values_callable - parameter may be used. The value of this parameter is a user-supplied callable, - which is intended to be used with a PEP-435-compliant enumerated class and - returns a list of string values to be persisted. For a simple enumeration that - uses string values, a callable such as lambda x: [e.value for e in x] is - sufficient. - """ - return [i.value for i in enum] # type: ignore[attr-defined] - - -# This has the standard definition for all of the enum columns used throughout the models. -# This is used in the enum_column() function below. -_enum_column_types = { - AuthType: db.Enum( # the key should be the Enum class to use. - AuthType, # The Enum class to use. - name="auth_types", # The name of the type defined in PostgreSQL - should be plural - values_callable=enum_values, # Every entry in this dict should contain this, note above. - ), - BrandType: db.Enum( - BrandType, - name="brand_types", - values_callable=enum_values, - ), - OrganizationType: db.Enum( - OrganizationType, - name="organization_types", - values_callable=enum_values, - ), - ServicePermissionType: db.Enum( - ServicePermissionType, - name="service_permission_types", - values_callable=enum_values, - ), - RecipientType: db.Enum( - RecipientType, - name="recipient_types", - values_callable=enum_values, - ), - CallbackType: db.Enum( - CallbackType, - name="callback_types", - values_callable=enum_values, - ), - KeyType: db.Enum( - KeyType, - name="key_types", - values_callable=enum_values, - ), - TemplateType: db.Enum( - TemplateType, - name="template_types", - values_callable=enum_values, - ), - TemplateProcessType: db.Enum( - TemplateProcessType, - name="template_process_types", - values_callable=enum_values, - ), - NotificationType: db.Enum( - NotificationType, - name="notification_types", - values_callable=enum_values, - ), - JobStatus: db.Enum( - JobStatus, - name="job_statuses", - values_callable=enum_values, - ), - CodeType: db.Enum( - CodeType, - name="code_types", - values_callable=enum_values, - ), - NotificationStatus: db.Enum( - NotificationStatus, - name="notify_statuses", - values_callable=enum_values, - ), - InvitedUserStatus: db.Enum( - InvitedUserStatus, - name="invited_user_statuses", - values_callable=enum_values, - ), - PermissionType: db.Enum( - PermissionType, - name="permission_types", - values_callable=enum_values, - ), - AgreementType: db.Enum( - AgreementType, - name="agreement_types", - values_callable=enum_values, - ), - AgreementStatus: db.Enum( - AgreementStatus, - name="agreement_statuses", - values_callable=enum_values, - ), +_enum_column_names = { + AuthType: "auth_types", + BrandType: "brand_types", + OrganizationType: "organization_types", + ServicePermissionType: "service_permission_types", + RecipientType: "recipient_types", + CallbackType: "callback_types", + KeyType: "key_types", + TemplateType: "template_types", + TemplateProcessType: "template_process_types", + NotificationType: "notification_types", + JobStatus: "job_statuses", + CodeType: "code_types", + NotificationStatus: "notify_statuses", + InvitedUserStatus: "invited_user_statuses", + PermissionType: "permission_types", + AgreementType: "agreement_types", + AgreementStatus: "agreement_statuses", } -def enum_column(enum_type, name=None, **kwargs): - if name is None: - return db.Column(_enum_column_types[enum_type], **kwargs) - else: - return db.Column(name, _enum_column_types[enum_type], **kwargs) +def enum_column(enum_type, **kwargs): + return db.Column( + db.Enum( + *[i.value for i in enum_type], + name=_enum_column_names[enum_type] + ), + **kwargs, + ) class HistoryModel: @@ -1591,6 +1506,7 @@ class Notification(db.Model): name="notification_status", nullable=True, default=NotificationStatus.CREATED, + key="status", ) reference = db.Column(db.String, nullable=True, index=True) client_reference = db.Column(db.String, index=True, nullable=True) @@ -1867,6 +1783,7 @@ class NotificationHistory(db.Model, HistoryModel): name="notification_status", nullable=True, default=NotificationStatus.CREATED, + key="status", ) reference = db.Column(db.String, nullable=True, index=True) client_reference = db.Column(db.String, nullable=True) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index 6334819ed..06381484b 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -118,6 +118,19 @@ _enum_params: dict[Enum, EnumValues] = { CodeType: {"values": ["email", "sms"], "name": "code_types"}, } +def enum_create(values: list[str], name: str) -> None: + enum_db_type = postgresql.ENUM(*values, name=name) + enum_db_type.create(op.get_bind()) + + +def enum_drop(values: list[str], name: str) -> None: + enum_db_type = postgresql.ENUM(*values, name=name) + enum_db_type.drop(op.get_bind()) + + +def enum_using(column_name: str, enum: Enum) -> str: + return f"{column_name}::{_enum_params[enum]['name']}" + def enum_type(enum: Enum) -> sa.Enum: return sa.Enum(*_enum_params[enum]["values"], name=_enum_params[enum]["name"]) @@ -188,6 +201,9 @@ def upgrade(): op.drop_table("job_status") op.drop_table("service_permission_types") + for enum_data in _enum_params.values(): + enum_create(**enum_data) + # alter existing columns to use new enums op.alter_column( "api_keys", @@ -195,6 +211,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(KeyType), existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), ) op.alter_column( "api_keys_history", @@ -202,6 +219,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(KeyType), existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), ) op.alter_column( "email_branding", @@ -209,6 +227,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(BrandType), existing_nullable=False, + postgresql_using=enum_using("brand_type", BrandType), ) op.alter_column( "invited_organization_users", @@ -216,6 +235,7 @@ def upgrade(): existing_type=sa.VARCHAR(), type_=enum_type(InvitedUserStatus), existing_nullable=False, + postgresql_using=enum_using("status", InvitedUserStatus), ) op.alter_column( "invited_users", @@ -229,6 +249,7 @@ def upgrade(): ), type_=enum_type(InvitedUserStatus), existing_nullable=False, + postgresql_using=enum_using("status", InvitedUserStatus), ) op.alter_column( "invited_users", @@ -237,6 +258,7 @@ def upgrade(): type_=enum_type(AuthType), existing_nullable=False, existing_server_default=sa.text("'sms_auth'::character varying"), + postgresql_using=enum_using("auth_type", AuthType), ) op.alter_column( "jobs", @@ -244,6 +266,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(JobStatus), existing_nullable=False, + postgresql_using=enum_using("job_status", JobStatus), ) op.alter_column( "notification_history", @@ -251,6 +274,7 @@ def upgrade(): existing_type=sa.TEXT(), type_=enum_type(NotificationStatus), existing_nullable=True, + postgresql_using=enum_using("notification_status", NotificationStatus), ) op.alter_column( "notification_history", @@ -258,6 +282,7 @@ def upgrade(): existing_type=sa.VARCHAR(), type_=enum_type(KeyType), existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), ) op.alter_column( "notification_history", @@ -267,6 +292,7 @@ def upgrade(): ), type_=enum_type(NotificationType), existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), ) op.alter_column( "notifications", @@ -274,6 +300,7 @@ def upgrade(): existing_type=sa.TEXT(), type_=enum_type(NotificationStatus), existing_nullable=True, + postgresql_using=enum_using("notification_status", NotificationStatus), ) op.alter_column( "notifications", @@ -281,6 +308,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(KeyType), existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), ) op.alter_column( "notifications", @@ -290,9 +318,7 @@ def upgrade(): ), type_=enum_type(NotificationType), existing_nullable=False, - ) - op.alter_column( - "notifications", "international", existing_type=sa.BOOLEAN(), nullable=False + postgresql_using=enum_using("notification_type", NotificationType), ) op.alter_column( "organization", @@ -300,6 +326,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(OrganizationType), existing_nullable=True, + postgresql_using=enum_using("organization_type", OrganizationType), ) op.alter_column( "provider_details", @@ -309,6 +336,7 @@ def upgrade(): ), type_=enum_type(NotificationType), existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), ) op.alter_column( "provider_details_history", @@ -318,6 +346,7 @@ def upgrade(): ), type_=enum_type(NotificationType), existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), ) op.alter_column( "rates", @@ -327,6 +356,7 @@ def upgrade(): ), type_=enum_type(NotificationType), existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), ) op.alter_column( "service_callback_api", @@ -334,6 +364,7 @@ def upgrade(): existing_type=sa.VARCHAR(), type_=enum_type(CallbackType), existing_nullable=True, + postgresql_using=enum_using("callback_type", CallbackType), ) op.alter_column( "service_callback_api_history", @@ -341,6 +372,7 @@ def upgrade(): existing_type=sa.VARCHAR(), type_=enum_type(CallbackType), existing_nullable=True, + postgresql_using=enum_using("callback_type", CallbackType), ) op.alter_column( "service_data_retention", @@ -350,6 +382,7 @@ def upgrade(): ), type_=enum_type(NotificationType), existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), ) op.alter_column( "service_permissions", @@ -357,6 +390,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(ServicePermissionType), existing_nullable=False, + postgresql_using=enum_using("permission", ServicePermissionType), ) op.alter_column( "service_whitelist", @@ -364,6 +398,7 @@ def upgrade(): existing_type=postgresql.ENUM("mobile", "email", name="recipient_type"), type_=enum_type(RecipientType), existing_nullable=False, + postgresql_using=enum_using("recipient_type", RecipientType), ) op.alter_column( "services", @@ -371,6 +406,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(OrganizationType), existing_nullable=True, + postgresql_using=enum_using("organization_type", OrganizationType), ) op.alter_column( "services_history", @@ -378,6 +414,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(OrganizationType), existing_nullable=True, + postgresql_using=enum_using("organization_type", OrganizationType), ) op.alter_column( "templates", @@ -387,6 +424,7 @@ def upgrade(): ), type_=enum_type(TemplateType), existing_nullable=False, + postgresql_using=enum_using("template_type", TemplateType), ) op.alter_column( "templates", @@ -394,6 +432,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(TemplateProcessType), existing_nullable=False, + postgresql_using=enum_using("process_type", TemplateProcessType), ) op.alter_column( "templates_history", @@ -403,6 +442,7 @@ def upgrade(): ), type_=enum_type(TemplateType), existing_nullable=False, + postgresql_using=enum_using("template_type", TemplateType), ) op.alter_column( "templates_history", @@ -410,6 +450,7 @@ def upgrade(): existing_type=sa.VARCHAR(length=255), type_=enum_type(TemplateProcessType), existing_nullable=False, + postgresql_using=enum_using("process_type", TemplateProcessType), ) op.alter_column( "users", @@ -418,6 +459,7 @@ def upgrade(): type_=enum_type(AuthType), existing_nullable=False, existing_server_default=sa.text("'sms_auth'::character varying"), + postgresql_using=enum_using("auth_type", AuthType), ) op.alter_column( "verify_codes", @@ -425,6 +467,7 @@ def upgrade(): existing_type=postgresql.ENUM("email", "sms", name="verify_code_types"), type_=enum_type(CodeType), existing_nullable=False, + postgresql_using=enum_using("code_type", CodeType), ) # Recreate composite indexes @@ -458,6 +501,7 @@ def downgrade(): existing_type=enum_type(CodeType), type_=postgresql.ENUM("email", "sms", name="verify_code_types"), existing_nullable=False, + postgresql_using="code_type::verify_code_types", ) op.alter_column( "users", @@ -482,6 +526,7 @@ def downgrade(): "sms", "email", "letter", "broadcast", name="template_type" ), existing_nullable=False, + postgresql_using="template_type::template_type", ) op.alter_column( "templates", @@ -498,6 +543,7 @@ def downgrade(): "sms", "email", "letter", "broadcast", name="template_type" ), existing_nullable=False, + postgresql_using="template_type::template_type", ) op.alter_column( "services_history", @@ -519,6 +565,7 @@ def downgrade(): existing_type=enum_type(RecipientType), type_=postgresql.ENUM("mobile", "email", name="recipient_type"), existing_nullable=False, + postgresql_using="recipient_type::recipient_type", ) op.alter_column( "service_permissions", @@ -533,6 +580,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, + postgresql_using="notification_type::notification_type", ) op.alter_column( "service_callback_api_history", @@ -554,6 +602,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, + postgresql_using="notification_type::notification_type", ) op.alter_column( "provider_details_history", @@ -561,6 +610,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, + postgresql_using="notification_type::notification_type", ) op.alter_column( "provider_details", @@ -568,6 +618,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, + postgresql_using="notification_type::notification_type", ) op.alter_column( "organization", @@ -589,6 +640,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, + postgresql_using="notification_type::notification_type", ) op.alter_column( "notifications", @@ -610,6 +662,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, + postgresql_using="notification_type::notification_type", ) op.alter_column( "notification_history", @@ -645,6 +698,7 @@ def downgrade(): name="invited_users_status_types", ), existing_nullable=False, + postgresql_using="status::invited_user_status_types", ) op.alter_column( "invited_organization_users", @@ -675,6 +729,9 @@ def downgrade(): existing_nullable=False, ) + for enum_data in _enum_params.values(): + enum_drop(**enum_data) + # Recreate helper tables service_permission_types = op.create_table( "service_permission_types", From ced386b0c5734774bccb93db57d3a79d81572a8f Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 25 Jan 2024 14:25:00 -0500 Subject: [PATCH 164/259] Another thing needs an enum. Signed-off-by: Cliff Hill --- app/models.py | 11 ++++-- .../versions/0410_enums_for_everything.py | 36 ++++++++++++++----- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/app/models.py b/app/models.py index 1244f617c..d920cd651 100644 --- a/app/models.py +++ b/app/models.py @@ -78,7 +78,8 @@ def enum_column(enum_type, **kwargs): return db.Column( db.Enum( *[i.value for i in enum_type], - name=_enum_column_names[enum_type] + name=_enum_column_names[enum_type], + values_callable=(lambda x: [i.value for i in x]), ), **kwargs, ) @@ -1449,7 +1450,13 @@ class NotificationAllTimeView(db.Model): sent_at = db.Column(db.DateTime) sent_by = db.Column(db.String) updated_at = db.Column(db.DateTime) - status = db.Column("notification_status", db.Text) + status = enum_column( + NotificationStatus, + name="notification_status", + nullable=True, + default=NotificationStatus.CREATED, + key="status", + ) reference = db.Column(db.String) client_reference = db.Column(db.String) international = db.Column(db.Boolean) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index 06381484b..884ffbc65 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -118,6 +118,7 @@ _enum_params: dict[Enum, EnumValues] = { CodeType: {"values": ["email", "sms"], "name": "code_types"}, } + def enum_create(values: list[str], name: str) -> None: enum_db_type = postgresql.ENUM(*values, name=name) enum_db_type.create(op.get_bind()) @@ -129,11 +130,15 @@ def enum_drop(values: list[str], name: str) -> None: def enum_using(column_name: str, enum: Enum) -> str: - return f"{column_name}::{_enum_params[enum]['name']}" + return f"{column_name}::text::{_enum_params[enum]['name']}" def enum_type(enum: Enum) -> sa.Enum: - return sa.Enum(*_enum_params[enum]["values"], name=_enum_params[enum]["name"]) + return sa.Enum( + *_enum_params[enum]["values"], + name=_enum_params[enum]["name"], + values_callable=(lambda x: [e.value for e in x]), + ) def upgrade(): @@ -251,14 +256,22 @@ def upgrade(): existing_nullable=False, postgresql_using=enum_using("status", InvitedUserStatus), ) + op.alter_column( + "invited_users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=sa.VARCHAR(), + existing_nullable=False, + server_default=None, + existing_server_default=sa.text("'sms_auth'::character varying"), + ) op.alter_column( "invited_users", "auth_type", existing_type=sa.VARCHAR(), type_=enum_type(AuthType), existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying"), - postgresql_using=enum_using("auth_type", AuthType), + postgresql_using="auth_type::auth_types", ) op.alter_column( "jobs", @@ -456,10 +469,17 @@ def upgrade(): "users", "auth_type", existing_type=sa.VARCHAR(), - type_=enum_type(AuthType), + type_=sa.VARCHAR(), + server_default=None, existing_nullable=False, existing_server_default=sa.text("'sms_auth'::character varying"), - postgresql_using=enum_using("auth_type", AuthType), + ) + op.alter_column( + "users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=enum_type(AuthType), + existing_nullable=False, ) op.alter_column( "verify_codes", @@ -509,7 +529,7 @@ def downgrade(): existing_type=enum_type(AuthType), type_=sa.VARCHAR(), existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying"), + server_default=sa.text("'sms_auth'::character varying"), ) op.alter_column( "templates_history", @@ -684,7 +704,7 @@ def downgrade(): existing_type=enum_type(AuthType), type_=sa.VARCHAR(), existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying"), + server_default=sa.text("'sms_auth'::character varying"), ) op.alter_column( "invited_users", From 5d02f2240839d4f68361bb3017890fa4fb182d12 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 25 Jan 2024 14:45:37 -0500 Subject: [PATCH 165/259] Migration worked without errors now. Signed-off-by: Cliff Hill --- app/models.py | 3 +- .../versions/0410_enums_for_everything.py | 1808 +++++++++-------- 2 files changed, 929 insertions(+), 882 deletions(-) diff --git a/app/models.py b/app/models.py index d920cd651..e921ae97b 100644 --- a/app/models.py +++ b/app/models.py @@ -1,7 +1,6 @@ import datetime import itertools import uuid -from enum import Enum from flask import current_app, url_for from notifications_utils.clients.encryption.encryption_client import EncryptionError @@ -14,7 +13,7 @@ from notifications_utils.recipients import ( ) from notifications_utils.template import PlainTextEmailTemplate, SMSMessageTemplate from sqlalchemy import CheckConstraint, Index, UniqueConstraint -from sqlalchemy.dialects.postgresql import JSON, JSONB, UUID, ENUM +from sqlalchemy.dialects.postgresql import JSON, JSONB, UUID from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.orm import validates diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index 884ffbc65..b65c66974 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -5,8 +5,10 @@ Revises: 0409_fix_service_name Create Date: 2024-01-18 12:34:32.857422 """ +from contextlib import contextmanager from enum import Enum -from typing import TypedDict +from re import I +from typing import Iterator, TypedDict import sqlalchemy as sa from alembic import op @@ -141,892 +143,938 @@ def enum_type(enum: Enum) -> sa.Enum: ) +@contextmanager +def view_handler() -> Iterator[None]: + op.execute("DROP VIEW notifications_all_time_view") + + yield + + op.execute( + """ + CREATE VIEW notifications_all_time_view AS + ( + SELECT + id, + job_id, + job_row_number, + service_id, + template_id, + template_version, + api_key_id, + key_type, + billable_units, + notification_type, + created_at, + sent_at, + sent_by, + updated_at, + notification_status, + reference, + client_reference, + international, + phone_prefix, + rate_multiplier, + created_by_id, + document_download_count + FROM notifications + ) UNION + ( + SELECT + id, + job_id, + job_row_number, + service_id, + template_id, + template_version, + api_key_id, + key_type, + billable_units, + notification_type, + created_at, + sent_at, + sent_by, + updated_at, + notification_status, + reference, + client_reference, + international, + phone_prefix, + rate_multiplier, + created_by_id, + document_download_count + FROM notification_history + ) + """ + ) + + def upgrade(): - # Remove foreign key constraints for old "helper" tables. - op.drop_constraint("api_keys_key_type_fkey", "api_keys", type_="foreignkey") - op.drop_constraint( - "email_branding_brand_type_fkey", "email_branding", type_="foreignkey" - ) - op.drop_constraint( - "invited_organisation_users_status_fkey", - "invited_organization_users", - type_="foreignkey", - ) - op.drop_constraint( - "invited_users_auth_type_fkey", "invited_users", type_="foreignkey" - ) - op.drop_constraint("jobs_job_status_fkey", "jobs", type_="foreignkey") - op.drop_constraint( - "notification_history_key_type_fkey", "notification_history", type_="foreignkey" - ) - op.drop_constraint( - "fk_notification_history_notification_status", - "notification_history", - type_="foreignkey", - ) - op.drop_constraint( - "notifications_key_type_fkey", "notifications", type_="foreignkey" - ) - op.drop_constraint( - "fk_notifications_notification_status", "notifications", type_="foreignkey" - ) - op.drop_constraint( - "organisation_organisation_type_fkey", "organization", type_="foreignkey" - ) - op.drop_constraint( - "service_callback_api_type_fk", "service_callback_api", type_="foreignkey" - ) - op.drop_constraint( - "service_permissions_permission_fkey", "service_permissions", type_="foreignkey" - ) - op.drop_constraint( - "services_organisation_type_fkey", "services", type_="foreignkey" - ) - op.drop_constraint("templates_process_type_fkey", "templates", type_="foreignkey") - op.drop_constraint( - "templates_history_process_type_fkey", "templates_history", type_="foreignkey" - ) - op.drop_constraint("users_auth_type_fkey", "users", type_="foreignkey") + with view_handler(): + # Remove foreign key constraints for old "helper" tables. + op.drop_constraint("api_keys_key_type_fkey", "api_keys", type_="foreignkey") + op.drop_constraint( + "email_branding_brand_type_fkey", "email_branding", type_="foreignkey" + ) + op.drop_constraint( + "invited_organisation_users_status_fkey", + "invited_organization_users", + type_="foreignkey", + ) + op.drop_constraint( + "invited_users_auth_type_fkey", "invited_users", type_="foreignkey" + ) + op.drop_constraint("jobs_job_status_fkey", "jobs", type_="foreignkey") + op.drop_constraint( + "notification_history_key_type_fkey", + "notification_history", + type_="foreignkey", + ) + op.drop_constraint( + "fk_notification_history_notification_status", + "notification_history", + type_="foreignkey", + ) + op.drop_constraint( + "notifications_key_type_fkey", "notifications", type_="foreignkey" + ) + op.drop_constraint( + "fk_notifications_notification_status", "notifications", type_="foreignkey" + ) + op.drop_constraint( + "organisation_organisation_type_fkey", "organization", type_="foreignkey" + ) + op.drop_constraint( + "service_callback_api_type_fk", "service_callback_api", type_="foreignkey" + ) + op.drop_constraint( + "service_permissions_permission_fkey", + "service_permissions", + type_="foreignkey", + ) + op.drop_constraint( + "services_organisation_type_fkey", "services", type_="foreignkey" + ) + op.drop_constraint( + "templates_process_type_fkey", "templates", type_="foreignkey" + ) + op.drop_constraint( + "templates_history_process_type_fkey", + "templates_history", + type_="foreignkey", + ) + op.drop_constraint("users_auth_type_fkey", "users", type_="foreignkey") - # Drop composite indexes - op.drop_index("ix_notifications_service_id_composite", table_name="notifications") - op.drop_index( - "ix_notifications_notification_type_composite", table_name="notifications" - ) + # drop old "helper" tables + op.drop_table("template_process_type") + op.drop_table("key_types") + op.drop_table("service_callback_type") + op.drop_table("auth_type") + op.drop_table("organization_types") + op.drop_table("invite_status_type") + op.drop_table("branding_type") + op.drop_table("notification_status_types") + op.drop_table("job_status") + op.drop_table("service_permission_types") - # drop old "helper" tables - op.drop_table("template_process_type") - op.drop_table("key_types") - op.drop_table("service_callback_type") - op.drop_table("auth_type") - op.drop_table("organization_types") - op.drop_table("invite_status_type") - op.drop_table("branding_type") - op.drop_table("notification_status_types") - op.drop_table("job_status") - op.drop_table("service_permission_types") + for enum_data in _enum_params.values(): + enum_create(**enum_data) - for enum_data in _enum_params.values(): - enum_create(**enum_data) - - # alter existing columns to use new enums - op.alter_column( - "api_keys", - "key_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(KeyType), - existing_nullable=False, - postgresql_using=enum_using("key_type", KeyType), - ) - op.alter_column( - "api_keys_history", - "key_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(KeyType), - existing_nullable=False, - postgresql_using=enum_using("key_type", KeyType), - ) - op.alter_column( - "email_branding", - "brand_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(BrandType), - existing_nullable=False, - postgresql_using=enum_using("brand_type", BrandType), - ) - op.alter_column( - "invited_organization_users", - "status", - existing_type=sa.VARCHAR(), - type_=enum_type(InvitedUserStatus), - existing_nullable=False, - postgresql_using=enum_using("status", InvitedUserStatus), - ) - op.alter_column( - "invited_users", - "status", - existing_type=postgresql.ENUM( - "pending", - "accepted", - "cancelled", - "expired", - name="invited_users_status_types", - ), - type_=enum_type(InvitedUserStatus), - existing_nullable=False, - postgresql_using=enum_using("status", InvitedUserStatus), - ) - op.alter_column( - "invited_users", - "auth_type", - existing_type=sa.VARCHAR(), - type_=sa.VARCHAR(), - existing_nullable=False, - server_default=None, - existing_server_default=sa.text("'sms_auth'::character varying"), - ) - op.alter_column( - "invited_users", - "auth_type", - existing_type=sa.VARCHAR(), - type_=enum_type(AuthType), - existing_nullable=False, - postgresql_using="auth_type::auth_types", - ) - op.alter_column( - "jobs", - "job_status", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(JobStatus), - existing_nullable=False, - postgresql_using=enum_using("job_status", JobStatus), - ) - op.alter_column( - "notification_history", - "notification_status", - existing_type=sa.TEXT(), - type_=enum_type(NotificationStatus), - existing_nullable=True, - postgresql_using=enum_using("notification_status", NotificationStatus), - ) - op.alter_column( - "notification_history", - "key_type", - existing_type=sa.VARCHAR(), - type_=enum_type(KeyType), - existing_nullable=False, - postgresql_using=enum_using("key_type", KeyType), - ) - op.alter_column( - "notification_history", - "notification_type", - existing_type=postgresql.ENUM( - "email", "sms", "letter", name="notification_type" - ), - type_=enum_type(NotificationType), - existing_nullable=False, - postgresql_using=enum_using("notification_type", NotificationType), - ) - op.alter_column( - "notifications", - "notification_status", - existing_type=sa.TEXT(), - type_=enum_type(NotificationStatus), - existing_nullable=True, - postgresql_using=enum_using("notification_status", NotificationStatus), - ) - op.alter_column( - "notifications", - "key_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(KeyType), - existing_nullable=False, - postgresql_using=enum_using("key_type", KeyType), - ) - op.alter_column( - "notifications", - "notification_type", - existing_type=postgresql.ENUM( - "email", "sms", "letter", name="notification_type" - ), - type_=enum_type(NotificationType), - existing_nullable=False, - postgresql_using=enum_using("notification_type", NotificationType), - ) - op.alter_column( - "organization", - "organization_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(OrganizationType), - existing_nullable=True, - postgresql_using=enum_using("organization_type", OrganizationType), - ) - op.alter_column( - "provider_details", - "notification_type", - existing_type=postgresql.ENUM( - "email", "sms", "letter", name="notification_type" - ), - type_=enum_type(NotificationType), - existing_nullable=False, - postgresql_using=enum_using("notification_type", NotificationType), - ) - op.alter_column( - "provider_details_history", - "notification_type", - existing_type=postgresql.ENUM( - "email", "sms", "letter", name="notification_type" - ), - type_=enum_type(NotificationType), - existing_nullable=False, - postgresql_using=enum_using("notification_type", NotificationType), - ) - op.alter_column( - "rates", - "notification_type", - existing_type=postgresql.ENUM( - "email", "sms", "letter", name="notification_type" - ), - type_=enum_type(NotificationType), - existing_nullable=False, - postgresql_using=enum_using("notification_type", NotificationType), - ) - op.alter_column( - "service_callback_api", - "callback_type", - existing_type=sa.VARCHAR(), - type_=enum_type(CallbackType), - existing_nullable=True, - postgresql_using=enum_using("callback_type", CallbackType), - ) - op.alter_column( - "service_callback_api_history", - "callback_type", - existing_type=sa.VARCHAR(), - type_=enum_type(CallbackType), - existing_nullable=True, - postgresql_using=enum_using("callback_type", CallbackType), - ) - op.alter_column( - "service_data_retention", - "notification_type", - existing_type=postgresql.ENUM( - "email", "sms", "letter", name="notification_type" - ), - type_=enum_type(NotificationType), - existing_nullable=False, - postgresql_using=enum_using("notification_type", NotificationType), - ) - op.alter_column( - "service_permissions", - "permission", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(ServicePermissionType), - existing_nullable=False, - postgresql_using=enum_using("permission", ServicePermissionType), - ) - op.alter_column( - "service_whitelist", - "recipient_type", - existing_type=postgresql.ENUM("mobile", "email", name="recipient_type"), - type_=enum_type(RecipientType), - existing_nullable=False, - postgresql_using=enum_using("recipient_type", RecipientType), - ) - op.alter_column( - "services", - "organization_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(OrganizationType), - existing_nullable=True, - postgresql_using=enum_using("organization_type", OrganizationType), - ) - op.alter_column( - "services_history", - "organization_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(OrganizationType), - existing_nullable=True, - postgresql_using=enum_using("organization_type", OrganizationType), - ) - op.alter_column( - "templates", - "template_type", - existing_type=postgresql.ENUM( - "sms", "email", "letter", "broadcast", name="template_type" - ), - type_=enum_type(TemplateType), - existing_nullable=False, - postgresql_using=enum_using("template_type", TemplateType), - ) - op.alter_column( - "templates", - "process_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(TemplateProcessType), - existing_nullable=False, - postgresql_using=enum_using("process_type", TemplateProcessType), - ) - op.alter_column( - "templates_history", - "template_type", - existing_type=postgresql.ENUM( - "sms", "email", "letter", "broadcast", name="template_type" - ), - type_=enum_type(TemplateType), - existing_nullable=False, - postgresql_using=enum_using("template_type", TemplateType), - ) - op.alter_column( - "templates_history", - "process_type", - existing_type=sa.VARCHAR(length=255), - type_=enum_type(TemplateProcessType), - existing_nullable=False, - postgresql_using=enum_using("process_type", TemplateProcessType), - ) - op.alter_column( - "users", - "auth_type", - existing_type=sa.VARCHAR(), - type_=sa.VARCHAR(), - server_default=None, - existing_nullable=False, - existing_server_default=sa.text("'sms_auth'::character varying"), - ) - op.alter_column( - "users", - "auth_type", - existing_type=sa.VARCHAR(), - type_=enum_type(AuthType), - existing_nullable=False, - ) - op.alter_column( - "verify_codes", - "code_type", - existing_type=postgresql.ENUM("email", "sms", name="verify_code_types"), - type_=enum_type(CodeType), - existing_nullable=False, - postgresql_using=enum_using("code_type", CodeType), - ) - - # Recreate composite indexes - op.create_index( - "ix_notifications_service_id_composite", - "notifications", - ["service_id", "notification_type", "status", "created_at"], - unique=False, - ) - op.create_index( - "ix_notifications_notification_type_composite", - "notifications", - ["notification_type", "status", "created_at"], - unique=False, - ) - - # ### end Alembic commands ### + # alter existing columns to use new enums + op.alter_column( + "api_keys", + "key_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(KeyType), + existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), + ) + op.alter_column( + "api_keys_history", + "key_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(KeyType), + existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), + ) + op.alter_column( + "email_branding", + "brand_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(BrandType), + existing_nullable=False, + postgresql_using=enum_using("brand_type", BrandType), + ) + op.alter_column( + "invited_organization_users", + "status", + existing_type=sa.VARCHAR(), + type_=enum_type(InvitedUserStatus), + existing_nullable=False, + postgresql_using=enum_using("status", InvitedUserStatus), + ) + op.alter_column( + "invited_users", + "status", + existing_type=postgresql.ENUM( + "pending", + "accepted", + "cancelled", + "expired", + name="invited_users_status_types", + ), + type_=enum_type(InvitedUserStatus), + existing_nullable=False, + postgresql_using=enum_using("status", InvitedUserStatus), + ) + op.alter_column( + "invited_users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=sa.VARCHAR(), + existing_nullable=False, + server_default=None, + existing_server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "invited_users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=enum_type(AuthType), + existing_nullable=False, + postgresql_using="auth_type::auth_types", + ) + op.alter_column( + "jobs", + "job_status", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(JobStatus), + existing_nullable=False, + postgresql_using=enum_using("job_status", JobStatus), + ) + op.alter_column( + "notification_history", + "notification_status", + existing_type=sa.TEXT(), + type_=enum_type(NotificationStatus), + existing_nullable=True, + postgresql_using=enum_using("notification_status", NotificationStatus), + ) + op.alter_column( + "notification_history", + "key_type", + existing_type=sa.VARCHAR(), + type_=enum_type(KeyType), + existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), + ) + op.alter_column( + "notification_history", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=enum_type(NotificationType), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) + op.alter_column( + "notifications", + "notification_status", + existing_type=sa.TEXT(), + type_=enum_type(NotificationStatus), + existing_nullable=True, + postgresql_using=enum_using("notification_status", NotificationStatus), + ) + op.alter_column( + "notifications", + "key_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(KeyType), + existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), + ) + op.alter_column( + "notifications", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=enum_type(NotificationType), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) + op.alter_column( + "organization", + "organization_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(OrganizationType), + existing_nullable=True, + postgresql_using=enum_using("organization_type", OrganizationType), + ) + op.alter_column( + "provider_details", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=enum_type(NotificationType), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) + op.alter_column( + "provider_details_history", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=enum_type(NotificationType), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) + op.alter_column( + "rates", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=enum_type(NotificationType), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) + op.alter_column( + "service_callback_api", + "callback_type", + existing_type=sa.VARCHAR(), + type_=enum_type(CallbackType), + existing_nullable=True, + postgresql_using=enum_using("callback_type", CallbackType), + ) + op.alter_column( + "service_callback_api_history", + "callback_type", + existing_type=sa.VARCHAR(), + type_=enum_type(CallbackType), + existing_nullable=True, + postgresql_using=enum_using("callback_type", CallbackType), + ) + op.alter_column( + "service_data_retention", + "notification_type", + existing_type=postgresql.ENUM( + "email", "sms", "letter", name="notification_type" + ), + type_=enum_type(NotificationType), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) + op.alter_column( + "service_permissions", + "permission", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(ServicePermissionType), + existing_nullable=False, + postgresql_using=enum_using("permission", ServicePermissionType), + ) + op.alter_column( + "service_whitelist", + "recipient_type", + existing_type=postgresql.ENUM("mobile", "email", name="recipient_type"), + type_=enum_type(RecipientType), + existing_nullable=False, + postgresql_using=enum_using("recipient_type", RecipientType), + ) + op.alter_column( + "services", + "organization_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(OrganizationType), + existing_nullable=True, + postgresql_using=enum_using("organization_type", OrganizationType), + ) + op.alter_column( + "services_history", + "organization_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(OrganizationType), + existing_nullable=True, + postgresql_using=enum_using("organization_type", OrganizationType), + ) + op.alter_column( + "templates", + "template_type", + existing_type=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + type_=enum_type(TemplateType), + existing_nullable=False, + postgresql_using=enum_using("template_type", TemplateType), + ) + op.alter_column( + "templates", + "process_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(TemplateProcessType), + existing_nullable=False, + postgresql_using=enum_using("process_type", TemplateProcessType), + ) + op.alter_column( + "templates_history", + "template_type", + existing_type=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + type_=enum_type(TemplateType), + existing_nullable=False, + postgresql_using=enum_using("template_type", TemplateType), + ) + op.alter_column( + "templates_history", + "process_type", + existing_type=sa.VARCHAR(length=255), + type_=enum_type(TemplateProcessType), + existing_nullable=False, + postgresql_using=enum_using("process_type", TemplateProcessType), + ) + op.alter_column( + "users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=sa.VARCHAR(), + server_default=None, + existing_nullable=False, + existing_server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "users", + "auth_type", + existing_type=sa.VARCHAR(), + type_=enum_type(AuthType), + existing_nullable=False, + postgresql_using="auth_type::auth_types", + ) + op.alter_column( + "verify_codes", + "code_type", + existing_type=postgresql.ENUM("email", "sms", name="verify_code_types"), + type_=enum_type(CodeType), + existing_nullable=False, + postgresql_using=enum_using("code_type", CodeType), + ) def downgrade(): - # Dropping composite indexes - op.drop_index("ix_notifications_service_id_composite", table_name="notifications") - op.drop_index( - "ix_notifications_notification_type_composite", table_name="notifications" - ) + with view_handler(): + # Alter columns back + op.alter_column( + "verify_codes", + "code_type", + existing_type=enum_type(CodeType), + type_=postgresql.ENUM("email", "sms", name="verify_code_types"), + existing_nullable=False, + postgresql_using="code_type::verify_code_types", + ) + op.alter_column( + "users", + "auth_type", + existing_type=enum_type(AuthType), + type_=sa.VARCHAR(), + existing_nullable=False, + server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "templates_history", + "process_type", + existing_type=enum_type(TemplateProcessType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "templates_history", + "template_type", + existing_type=enum_type(TemplateType), + type_=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + existing_nullable=False, + postgresql_using="template_type::template_type", + ) + op.alter_column( + "templates", + "process_type", + existing_type=enum_type(TemplateProcessType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "templates", + "template_type", + existing_type=enum_type(TemplateType), + type_=postgresql.ENUM( + "sms", "email", "letter", "broadcast", name="template_type" + ), + existing_nullable=False, + postgresql_using="template_type::template_type", + ) + op.alter_column( + "services_history", + "organization_type", + existing_type=enum_type(OrganizationType), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "services", + "organization_type", + existing_type=enum_type(OrganizationType), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "service_whitelist", + "recipient_type", + existing_type=enum_type(RecipientType), + type_=postgresql.ENUM("mobile", "email", name="recipient_type"), + existing_nullable=False, + postgresql_using="recipient_type::recipient_type", + ) + op.alter_column( + "service_permissions", + "permission", + existing_type=enum_type(ServicePermissionType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "service_data_retention", + "notification_type", + existing_type=enum_type(NotificationType), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + postgresql_using="notification_type::notification_type", + ) + op.alter_column( + "service_callback_api_history", + "callback_type", + existing_type=enum_type(CallbackType), + type_=sa.VARCHAR(), + existing_nullable=True, + ) + op.alter_column( + "service_callback_api", + "callback_type", + existing_type=enum_type(CallbackType), + type_=sa.VARCHAR(), + existing_nullable=True, + ) + op.alter_column( + "rates", + "notification_type", + existing_type=enum_type(NotificationType), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + postgresql_using="notification_type::notification_type", + ) + op.alter_column( + "provider_details_history", + "notification_type", + existing_type=enum_type(NotificationType), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + postgresql_using="notification_type::notification_type", + ) + op.alter_column( + "provider_details", + "notification_type", + existing_type=enum_type(NotificationType), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + postgresql_using="notification_type::notification_type", + ) + op.alter_column( + "organization", + "organization_type", + existing_type=enum_type(OrganizationType), + type_=sa.VARCHAR(length=255), + existing_nullable=True, + ) + op.alter_column( + "notifications", + "notification_status", + existing_type=enum_type(NotificationStatus), + type_=sa.TEXT(), + existing_nullable=True, + ) + op.alter_column( + "notifications", + "notification_type", + existing_type=enum_type(NotificationType), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + postgresql_using="notification_type::notification_type", + ) + op.alter_column( + "notifications", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "notification_history", + "notification_status", + existing_type=enum_type(NotificationStatus), + type_=sa.TEXT(), + existing_nullable=True, + ) + op.alter_column( + "notification_history", + "notification_type", + existing_type=enum_type(NotificationType), + type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), + existing_nullable=False, + postgresql_using="notification_type::notification_type", + ) + op.alter_column( + "notification_history", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(), + existing_nullable=False, + ) + op.alter_column( + "jobs", + "job_status", + existing_type=enum_type(JobStatus), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "invited_users", + "auth_type", + existing_type=enum_type(AuthType), + type_=sa.VARCHAR(), + existing_nullable=False, + server_default=sa.text("'sms_auth'::character varying"), + ) + op.alter_column( + "invited_users", + "status", + existing_type=enum_type(InvitedUserStatus), + type_=postgresql.ENUM( + "pending", + "accepted", + "cancelled", + "expired", + name="invited_users_status_types", + ), + existing_nullable=False, + postgresql_using="status::invited_user_status_types", + ) + op.alter_column( + "invited_organization_users", + "status", + existing_type=enum_type(InvitedUserStatus), + type_=sa.VARCHAR(), + existing_nullable=False, + ) + op.alter_column( + "email_branding", + "brand_type", + existing_type=enum_type(BrandType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "api_keys_history", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) + op.alter_column( + "api_keys", + "key_type", + existing_type=enum_type(KeyType), + type_=sa.VARCHAR(length=255), + existing_nullable=False, + ) - # Alter columns back - op.alter_column( - "verify_codes", - "code_type", - existing_type=enum_type(CodeType), - type_=postgresql.ENUM("email", "sms", name="verify_code_types"), - existing_nullable=False, - postgresql_using="code_type::verify_code_types", - ) - op.alter_column( - "users", - "auth_type", - existing_type=enum_type(AuthType), - type_=sa.VARCHAR(), - existing_nullable=False, - server_default=sa.text("'sms_auth'::character varying"), - ) - op.alter_column( - "templates_history", - "process_type", - existing_type=enum_type(TemplateProcessType), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.alter_column( - "templates_history", - "template_type", - existing_type=enum_type(TemplateType), - type_=postgresql.ENUM( - "sms", "email", "letter", "broadcast", name="template_type" - ), - existing_nullable=False, - postgresql_using="template_type::template_type", - ) - op.alter_column( - "templates", - "process_type", - existing_type=enum_type(TemplateProcessType), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.alter_column( - "templates", - "template_type", - existing_type=enum_type(TemplateType), - type_=postgresql.ENUM( - "sms", "email", "letter", "broadcast", name="template_type" - ), - existing_nullable=False, - postgresql_using="template_type::template_type", - ) - op.alter_column( - "services_history", - "organization_type", - existing_type=enum_type(OrganizationType), - type_=sa.VARCHAR(length=255), - existing_nullable=True, - ) - op.alter_column( - "services", - "organization_type", - existing_type=enum_type(OrganizationType), - type_=sa.VARCHAR(length=255), - existing_nullable=True, - ) - op.alter_column( - "service_whitelist", - "recipient_type", - existing_type=enum_type(RecipientType), - type_=postgresql.ENUM("mobile", "email", name="recipient_type"), - existing_nullable=False, - postgresql_using="recipient_type::recipient_type", - ) - op.alter_column( - "service_permissions", - "permission", - existing_type=enum_type(ServicePermissionType), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.alter_column( - "service_data_retention", - "notification_type", - existing_type=enum_type(NotificationType), - type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), - existing_nullable=False, - postgresql_using="notification_type::notification_type", - ) - op.alter_column( - "service_callback_api_history", - "callback_type", - existing_type=enum_type(CallbackType), - type_=sa.VARCHAR(), - existing_nullable=True, - ) - op.alter_column( - "service_callback_api", - "callback_type", - existing_type=enum_type(CallbackType), - type_=sa.VARCHAR(), - existing_nullable=True, - ) - op.alter_column( - "rates", - "notification_type", - existing_type=enum_type(NotificationType), - type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), - existing_nullable=False, - postgresql_using="notification_type::notification_type", - ) - op.alter_column( - "provider_details_history", - "notification_type", - existing_type=enum_type(NotificationType), - type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), - existing_nullable=False, - postgresql_using="notification_type::notification_type", - ) - op.alter_column( - "provider_details", - "notification_type", - existing_type=enum_type(NotificationType), - type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), - existing_nullable=False, - postgresql_using="notification_type::notification_type", - ) - op.alter_column( - "organization", - "organization_type", - existing_type=enum_type(OrganizationType), - type_=sa.VARCHAR(length=255), - existing_nullable=True, - ) - op.alter_column( - "notifications", - "notification_status", - existing_type=enum_type(NotificationStatus), - type_=sa.TEXT(), - existing_nullable=True, - ) - op.alter_column( - "notifications", - "notification_type", - existing_type=enum_type(NotificationType), - type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), - existing_nullable=False, - postgresql_using="notification_type::notification_type", - ) - op.alter_column( - "notifications", - "key_type", - existing_type=enum_type(KeyType), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.alter_column( - "notification_history", - "notification_status", - existing_type=enum_type(NotificationStatus), - type_=sa.TEXT(), - existing_nullable=True, - ) - op.alter_column( - "notification_history", - "notification_type", - existing_type=enum_type(NotificationType), - type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), - existing_nullable=False, - postgresql_using="notification_type::notification_type", - ) - op.alter_column( - "notification_history", - "key_type", - existing_type=enum_type(KeyType), - type_=sa.VARCHAR(), - existing_nullable=False, - ) - op.alter_column( - "jobs", - "job_status", - existing_type=enum_type(JobStatus), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.alter_column( - "invited_users", - "auth_type", - existing_type=enum_type(AuthType), - type_=sa.VARCHAR(), - existing_nullable=False, - server_default=sa.text("'sms_auth'::character varying"), - ) - op.alter_column( - "invited_users", - "status", - existing_type=enum_type(InvitedUserStatus), - type_=postgresql.ENUM( - "pending", - "accepted", - "cancelled", - "expired", - name="invited_users_status_types", - ), - existing_nullable=False, - postgresql_using="status::invited_user_status_types", - ) - op.alter_column( - "invited_organization_users", - "status", - existing_type=enum_type(InvitedUserStatus), - type_=sa.VARCHAR(), - existing_nullable=False, - ) - op.alter_column( - "email_branding", - "brand_type", - existing_type=enum_type(BrandType), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.alter_column( - "api_keys_history", - "key_type", - existing_type=enum_type(KeyType), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) - op.alter_column( - "api_keys", - "key_type", - existing_type=enum_type(KeyType), - type_=sa.VARCHAR(length=255), - existing_nullable=False, - ) + for enum_data in _enum_params.values(): + enum_drop(**enum_data) - for enum_data in _enum_params.values(): - enum_drop(**enum_data) + # Recreate helper tables + service_permission_types = op.create_table( + "service_permission_types", + sa.Column( + "name", sa.VARCHAR(length=255), autoincrement=False, nullable=False + ), + sa.PrimaryKeyConstraint("name", name="service_permission_types_pkey"), + ) + op.bulk_insert( + service_permission_types, + [ + {"name": "letter"}, + {"name": "international_sms"}, + {"name": "sms"}, + {"name": "email"}, + {"name": "inbound_sms"}, + {"name": "schedule_notifications"}, + {"name": "email_auth"}, + {"name": "letters_as_pdf"}, + {"name": "upload_document"}, + {"name": "edit_folder_permissions"}, + {"name": "upload_letters"}, + {"name": "international_letters"}, + {"name": "broadcast}"}, + ], + ) + job_status = op.create_table( + "job_status", + sa.Column( + "name", sa.VARCHAR(length=255), autoincrement=False, nullable=False + ), + sa.PrimaryKeyConstraint("name", name="job_status_pkey"), + ) + op.bulk_insert( + job_status, + [ + {"name": "pending"}, + {"name": "in progress"}, + {"name": "finished"}, + {"name": "sending limits exceeded"}, + {"name": "scheduled"}, + {"name": "cancelled"}, + {"name": "ready to send"}, + {"name": "sent to dvla"}, + {"name": "error"}, + ], + ) + notification_status_types = op.create_table( + "notification_status_types", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="notification_status_types_pkey"), + ) + op.bulk_insert( + notification_status_types, + [ + {"name": "created"}, + {"name": "pending"}, + {"name": "temporary-failure"}, + {"name": "delivered"}, + {"name": "sent"}, + {"name": "sending"}, + {"name": "failed"}, + {"name": "permanent-failure"}, + {"name": "technical-failure"}, + {"name": "pending-virus-check"}, + {"name": "virus-scan-failed"}, + {"name": "cancelled"}, + {"name": "returned-letter"}, + {"name": "validation-failed"}, + ], + ) + branding_type = op.create_table( + "branding_type", + sa.Column( + "name", sa.VARCHAR(length=255), autoincrement=False, nullable=False + ), + sa.PrimaryKeyConstraint("name", name="branding_type_pkey"), + ) + op.bulk_insert( + branding_type, + [ + {"name": "org"}, + {"name": "both"}, + {"name": "org_banner"}, + ], + ) + invite_status_type = op.create_table( + "invite_status_type", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="invite_status_type_pkey"), + ) + op.bulk_insert( + invite_status_type, + [ + {"name": "pending"}, + {"name": "accepted"}, + {"name": "cancelled"}, + ], + ) + organization_types = op.create_table( + "organization_types", + sa.Column( + "name", sa.VARCHAR(length=255), autoincrement=False, nullable=False + ), + sa.Column( + "annual_free_sms_fragment_limit", + sa.BIGINT(), + autoincrement=False, + nullable=False, + ), + sa.PrimaryKeyConstraint("name", name="organisation_types_pkey"), + ) + op.bulk_insert( + organization_types, + [ + {"name": "other", "annual_free_sms_fragment_limit": 25000}, + {"name": "state", "annual_free_sms_fragment_limit": 250000}, + {"name": "federal", "annual_free_sms_fragment_limit": 250000}, + ], + ) + auth_type = op.create_table( + "auth_type", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="auth_type_pkey"), + ) + op.bulk_insert( + auth_type, + [ + {"name": "email_auth"}, + {"name": "sms_auth"}, + {"name": "webauthn_auth"}, + ], + ) + service_callback_type = op.create_table( + "service_callback_type", + sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint("name", name="service_callback_type_pkey"), + ) + op.bulk_insert( + service_callback_type, + [ + {"name": "delivery_status"}, + {"name": "complaint"}, + ], + ) + key_types = op.create_table( + "key_types", + sa.Column( + "name", sa.VARCHAR(length=255), autoincrement=False, nullable=False + ), + sa.PrimaryKeyConstraint("name", name="key_types_pkey"), + ) + op.bulk_insert( + key_types, + [ + {"name": "normal"}, + {"name": "team"}, + {"name": "test"}, + ], + ) + template_process_type = op.create_table( + "template_process_type", + sa.Column( + "name", sa.VARCHAR(length=255), autoincrement=False, nullable=False + ), + sa.PrimaryKeyConstraint("name", name="template_process_type_pkey"), + ) + op.bulk_insert( + template_process_type, + [ + {"name": "normal"}, + {"name": "priority"}, + ], + ) - # Recreate helper tables - service_permission_types = op.create_table( - "service_permission_types", - sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="service_permission_types_pkey"), - ) - op.bulk_insert( - service_permission_types, - [ - {"name": "letter"}, - {"name": "international_sms"}, - {"name": "sms"}, - {"name": "email"}, - {"name": "inbound_sms"}, - {"name": "schedule_notifications"}, - {"name": "email_auth"}, - {"name": "letters_as_pdf"}, - {"name": "upload_document"}, - {"name": "edit_folder_permissions"}, - {"name": "upload_letters"}, - {"name": "international_letters"}, - {"name": "broadcast}"}, - ], - ) - job_status = op.create_table( - "job_status", - sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="job_status_pkey"), - ) - op.bulk_insert( - job_status, - [ - {"name": "pending"}, - {"name": "in progress"}, - {"name": "finished"}, - {"name": "sending limits exceeded"}, - {"name": "scheduled"}, - {"name": "cancelled"}, - {"name": "ready to send"}, - {"name": "sent to dvla"}, - {"name": "error"}, - ], - ) - notification_status_types = op.create_table( - "notification_status_types", - sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="notification_status_types_pkey"), - ) - op.bulk_insert( - notification_status_types, - [ - {"name": "created"}, - {"name": "pending"}, - {"name": "temporary-failure"}, - {"name": "delivered"}, - {"name": "sent"}, - {"name": "sending"}, - {"name": "failed"}, - {"name": "permanent-failure"}, - {"name": "technical-failure"}, - {"name": "pending-virus-check"}, - {"name": "virus-scan-failed"}, - {"name": "cancelled"}, - {"name": "returned-letter"}, - {"name": "validation-failed"}, - ], - ) - branding_type = op.create_table( - "branding_type", - sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="branding_type_pkey"), - ) - op.bulk_insert( - branding_type, - [ - {"name": "org"}, - {"name": "both"}, - {"name": "org_banner"}, - ], - ) - invite_status_type = op.create_table( - "invite_status_type", - sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="invite_status_type_pkey"), - ) - op.bulk_insert( - invite_status_type, - [ - {"name": "pending"}, - {"name": "accepted"}, - {"name": "cancelled"}, - ], - ) - organization_types = op.create_table( - "organization_types", - sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.Column( - "annual_free_sms_fragment_limit", - sa.BIGINT(), - autoincrement=False, - nullable=False, - ), - sa.PrimaryKeyConstraint("name", name="organisation_types_pkey"), - ) - op.bulk_insert( - organization_types, - [ - {"name": "other", "annual_free_sms_fragment_limit": 25000}, - {"name": "state", "annual_free_sms_fragment_limit": 250000}, - {"name": "federal", "annual_free_sms_fragment_limit": 250000}, - ], - ) - auth_type = op.create_table( - "auth_type", - sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="auth_type_pkey"), - ) - op.bulk_insert( - auth_type, - [ - {"name": "email_auth"}, - {"name": "sms_auth"}, - {"name": "webauthn_auth"}, - ], - ) - service_callback_type = op.create_table( - "service_callback_type", - sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="service_callback_type_pkey"), - ) - op.bulk_insert( - service_callback_type, - [ - {"name": "delivery_status"}, - {"name": "complaint"}, - ], - ) - key_types = op.create_table( - "key_types", - sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="key_types_pkey"), - ) - op.bulk_insert( - key_types, - [ - {"name": "normal"}, - {"name": "team"}, - {"name": "test"}, - ], - ) - template_process_type = op.create_table( - "template_process_type", - sa.Column("name", sa.VARCHAR(length=255), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint("name", name="template_process_type_pkey"), - ) - op.bulk_insert( - template_process_type, - [ - {"name": "normal"}, - {"name": "priority"}, - ], - ) - - # Creating composite indexes - op.create_index( - "ix_notifications_service_id_composite", - "notifications", - ["service_id", "notification_type", "notification_status", "created_at"], - unique=False, - ) - op.create_index( - "ix_notifications_notification_type_composite", - "notifications", - ["notification_type", "notification_status", "created_at"], - unique=False, - ) - - # Recreate foreign keys - op.create_foreign_key( - "users_auth_type_fkey", "users", "auth_type", ["auth_type"], ["name"] - ) - op.create_foreign_key( - "templates_history_process_type_fkey", - "templates_history", - "template_process_type", - ["process_type"], - ["name"], - ) - op.create_foreign_key( - "templates_process_type_fkey", - "templates", - "template_process_type", - ["process_type"], - ["name"], - ) - op.create_foreign_key( - "services_organisation_type_fkey", - "services", - "organization_types", - ["organization_type"], - ["name"], - ) - op.create_foreign_key( - "service_permissions_permission_fkey", - "service_permissions", - "service_permission_types", - ["permission"], - ["name"], - ) - op.create_foreign_key( - "service_callback_api_type_fk", - "service_callback_api", - "service_callback_type", - ["callback_type"], - ["name"], - ) - op.create_foreign_key( - "organisation_organisation_type_fkey", - "organization", - "organization_types", - ["organization_type"], - ["name"], - ) - op.create_foreign_key( - "fk_notifications_notification_status", - "notifications", - "notification_status_types", - ["notification_status"], - ["name"], - ) - op.create_foreign_key( - "notifications_key_type_fkey", - "notifications", - "key_types", - ["key_type"], - ["name"], - ) - op.create_foreign_key( - "fk_notification_history_notification_status", - "notification_history", - "notification_status_types", - ["notification_status"], - ["name"], - ) - op.create_foreign_key( - "notification_history_key_type_fkey", - "notification_history", - "key_types", - ["key_type"], - ["name"], - ) - op.create_foreign_key( - "jobs_job_status_fkey", "jobs", "job_status", ["job_status"], ["name"] - ) - op.create_foreign_key( - "invited_users_auth_type_fkey", - "invited_users", - "auth_type", - ["auth_type"], - ["name"], - ) - op.create_foreign_key( - "invited_organisation_users_status_fkey", - "invited_organization_users", - "invite_status_type", - ["status"], - ["name"], - ) - op.create_foreign_key( - "email_branding_brand_type_fkey", - "email_branding", - "branding_type", - ["brand_type"], - ["name"], - ) - op.create_foreign_key( - "api_keys_key_type_fkey", "api_keys", "key_types", ["key_type"], ["name"] - ) + # Recreate foreign keys + op.create_foreign_key( + "users_auth_type_fkey", "users", "auth_type", ["auth_type"], ["name"] + ) + op.create_foreign_key( + "templates_history_process_type_fkey", + "templates_history", + "template_process_type", + ["process_type"], + ["name"], + ) + op.create_foreign_key( + "templates_process_type_fkey", + "templates", + "template_process_type", + ["process_type"], + ["name"], + ) + op.create_foreign_key( + "services_organisation_type_fkey", + "services", + "organization_types", + ["organization_type"], + ["name"], + ) + op.create_foreign_key( + "service_permissions_permission_fkey", + "service_permissions", + "service_permission_types", + ["permission"], + ["name"], + ) + op.create_foreign_key( + "service_callback_api_type_fk", + "service_callback_api", + "service_callback_type", + ["callback_type"], + ["name"], + ) + op.create_foreign_key( + "organisation_organisation_type_fkey", + "organization", + "organization_types", + ["organization_type"], + ["name"], + ) + op.create_foreign_key( + "fk_notifications_notification_status", + "notifications", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notifications_key_type_fkey", + "notifications", + "key_types", + ["key_type"], + ["name"], + ) + op.create_foreign_key( + "fk_notification_history_notification_status", + "notification_history", + "notification_status_types", + ["notification_status"], + ["name"], + ) + op.create_foreign_key( + "notification_history_key_type_fkey", + "notification_history", + "key_types", + ["key_type"], + ["name"], + ) + op.create_foreign_key( + "jobs_job_status_fkey", "jobs", "job_status", ["job_status"], ["name"] + ) + op.create_foreign_key( + "invited_users_auth_type_fkey", + "invited_users", + "auth_type", + ["auth_type"], + ["name"], + ) + op.create_foreign_key( + "invited_organisation_users_status_fkey", + "invited_organization_users", + "invite_status_type", + ["status"], + ["name"], + ) + op.create_foreign_key( + "email_branding_brand_type_fkey", + "email_branding", + "branding_type", + ["brand_type"], + ["name"], + ) + op.create_foreign_key( + "api_keys_key_type_fkey", "api_keys", "key_types", ["key_type"], ["name"] + ) From b3223f84cc8dc7be50440f72350e04dbaa80e3e0 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 25 Jan 2024 15:50:21 -0500 Subject: [PATCH 166/259] Downgrade works now too. Signed-off-by: Cliff Hill --- .../versions/0410_enums_for_everything.py | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index b65c66974..33ffec277 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -561,9 +561,29 @@ def upgrade(): postgresql_using=enum_using("code_type", CodeType), ) + # Drop old enum types. + enum_drop( + values=["pending", "accepted", "cancelled", "expired"], + name="invited_users_status_types", + ) + enum_drop(values=["email", "sms", "letter"], name="notification_type") + enum_drop(values=["mobile", "email"], name="recipient_type") + enum_drop(values=["sms", "email", "letter", "broadcast"], name="template_type") + enum_drop(values=["email", "sms"], name="verify_code_types") + def downgrade(): with view_handler(): + # Create old enum types. + enum_create( + values=["pending", "accepted", "cancelled", "expired"], + name="invited_users_status_types", + ) + enum_create(values=["email", "sms", "letter"], name="notification_type") + enum_create(values=["mobile", "email"], name="recipient_type") + enum_create(values=["sms", "email", "letter", "broadcast"], name="template_type") + enum_create(values=["email", "sms"], name="verify_code_types") + # Alter columns back op.alter_column( "verify_codes", @@ -571,7 +591,7 @@ def downgrade(): existing_type=enum_type(CodeType), type_=postgresql.ENUM("email", "sms", name="verify_code_types"), existing_nullable=False, - postgresql_using="code_type::verify_code_types", + postgresql_using="code_type::text::verify_code_types", ) op.alter_column( "users", @@ -596,7 +616,7 @@ def downgrade(): "sms", "email", "letter", "broadcast", name="template_type" ), existing_nullable=False, - postgresql_using="template_type::template_type", + postgresql_using="template_type::text::template_type", ) op.alter_column( "templates", @@ -613,7 +633,7 @@ def downgrade(): "sms", "email", "letter", "broadcast", name="template_type" ), existing_nullable=False, - postgresql_using="template_type::template_type", + postgresql_using="template_type::text::template_type", ) op.alter_column( "services_history", @@ -635,7 +655,7 @@ def downgrade(): existing_type=enum_type(RecipientType), type_=postgresql.ENUM("mobile", "email", name="recipient_type"), existing_nullable=False, - postgresql_using="recipient_type::recipient_type", + postgresql_using="recipient_type::text::recipient_type", ) op.alter_column( "service_permissions", @@ -650,7 +670,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, - postgresql_using="notification_type::notification_type", + postgresql_using="notification_type::text::notification_type", ) op.alter_column( "service_callback_api_history", @@ -672,7 +692,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, - postgresql_using="notification_type::notification_type", + postgresql_using="notification_type::text::notification_type", ) op.alter_column( "provider_details_history", @@ -680,7 +700,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, - postgresql_using="notification_type::notification_type", + postgresql_using="notification_type::text::notification_type", ) op.alter_column( "provider_details", @@ -688,7 +708,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, - postgresql_using="notification_type::notification_type", + postgresql_using="notification_type::text::notification_type", ) op.alter_column( "organization", @@ -710,7 +730,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, - postgresql_using="notification_type::notification_type", + postgresql_using="notification_type::text::notification_type", ) op.alter_column( "notifications", @@ -732,7 +752,7 @@ def downgrade(): existing_type=enum_type(NotificationType), type_=postgresql.ENUM("email", "sms", "letter", name="notification_type"), existing_nullable=False, - postgresql_using="notification_type::notification_type", + postgresql_using="notification_type::text::notification_type", ) op.alter_column( "notification_history", @@ -754,7 +774,7 @@ def downgrade(): existing_type=enum_type(AuthType), type_=sa.VARCHAR(), existing_nullable=False, - server_default=sa.text("'sms_auth'::character varying"), + server_default=sa.text("'sms_auth'::text::character varying"), ) op.alter_column( "invited_users", @@ -768,7 +788,7 @@ def downgrade(): name="invited_users_status_types", ), existing_nullable=False, - postgresql_using="status::invited_user_status_types", + postgresql_using="status::text::invited_users_status_types", ) op.alter_column( "invited_organization_users", From 8907edc194c267926580c6b719a779c5b2fb44b7 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 25 Jan 2024 16:16:04 -0500 Subject: [PATCH 167/259] Got tests to use OrganizationType everywhere now. Signed-off-by: Cliff Hill --- .../versions/0410_enums_for_everything.py | 4 ++- tests/app/dao/test_annual_billing_dao.py | 3 ++- tests/app/dao/test_organization_dao.py | 27 ++++++++++--------- tests/app/dao/test_services_dao.py | 22 +++++++-------- tests/app/db.py | 4 +-- tests/app/organization/test_rest.py | 27 ++++++++++--------- tests/app/service/test_rest.py | 6 ++--- tests/app/test_commands.py | 8 +++--- 8 files changed, 53 insertions(+), 48 deletions(-) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index 33ffec277..d385bfe69 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -581,7 +581,9 @@ def downgrade(): ) enum_create(values=["email", "sms", "letter"], name="notification_type") enum_create(values=["mobile", "email"], name="recipient_type") - enum_create(values=["sms", "email", "letter", "broadcast"], name="template_type") + enum_create( + values=["sms", "email", "letter", "broadcast"], name="template_type" + ) enum_create(values=["email", "sms"], name="verify_code_types") # Alter columns back diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index 383e42734..182d94efc 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -8,6 +8,7 @@ from app.dao.annual_billing_dao import ( set_default_free_allowance_for_service, ) from app.dao.date_util import get_current_calendar_year_start_year +from app.enums import OrganizationType from app.models import AnnualBilling from tests.app.db import create_annual_billing, create_service @@ -114,7 +115,7 @@ def test_set_default_free_allowance_for_service_updates_existing_year(sample_ser assert annual_billing[0].service_id == sample_service.id assert annual_billing[0].free_sms_fragment_limit == 150000 - sample_service.organization_type = "federal" + sample_service.organization_type = OrganizationType.FEDERAL set_default_free_allowance_for_service(service=sample_service, year_start=None) annual_billing = AnnualBilling.query.all() diff --git a/tests/app/dao/test_organization_dao.py b/tests/app/dao/test_organization_dao.py index dc958cbe1..497b4a74a 100644 --- a/tests/app/dao/test_organization_dao.py +++ b/tests/app/dao/test_organization_dao.py @@ -16,6 +16,7 @@ from app.dao.organization_dao import ( dao_get_users_for_organization, dao_update_organization, ) +from app.enums import OrganizationType from app.models import Organization, Service from tests.app.db import ( create_domain, @@ -62,7 +63,7 @@ def test_update_organization(notify_db_session): data = { "name": "new name", - "organization_type": "state", + "organization_type": OrganizationType.STATE, "agreement_signed": True, "agreement_signed_at": datetime.datetime.utcnow(), "agreement_signed_by_id": user.id, @@ -138,8 +139,8 @@ def test_update_organization_does_not_update_the_service_if_certain_attributes_n ): email_branding = create_email_branding() - sample_service.organization_type = "state" - sample_organization.organization_type = "federal" + sample_service.organization_type = OrganizationType.STATE + sample_organization.organization_type = OrganizationType.FEDERAL sample_organization.email_branding = email_branding sample_organization.services.append(sample_service) @@ -151,8 +152,8 @@ def test_update_organization_does_not_update_the_service_if_certain_attributes_n assert sample_organization.name == "updated org name" - assert sample_organization.organization_type == "federal" - assert sample_service.organization_type == "state" + assert sample_organization.organization_type == OrganizationType.FEDERAL + assert sample_service.organization_type == OrganizationType.STATE assert sample_organization.email_branding == email_branding assert sample_service.email_branding is None @@ -162,22 +163,22 @@ def test_update_organization_updates_the_service_org_type_if_org_type_is_provide sample_service, sample_organization, ): - sample_service.organization_type = "state" - sample_organization.organization_type = "state" + sample_service.organization_type = OrganizationType.STATE + sample_organization.organization_type = OrganizationType.STATE sample_organization.services.append(sample_service) db.session.commit() - dao_update_organization(sample_organization.id, organization_type="federal") + dao_update_organization(sample_organization.id, organization_type=OrganizationType.FEDERAL) - assert sample_organization.organization_type == "federal" - assert sample_service.organization_type == "federal" + assert sample_organization.organization_type == OrganizationType.FEDERAL + assert sample_service.organization_type == OrganizationType.FEDERAL assert ( Service.get_history_model() .query.filter_by(id=sample_service.id, version=2) .one() .organization_type - == "federal" + == OrganizationType.FEDERAL ) @@ -217,8 +218,8 @@ def test_update_organization_does_not_override_service_branding( def test_add_service_to_organization(sample_service, sample_organization): assert sample_organization.services == [] - sample_service.organization_type = "federal" - sample_organization.organization_type = "state" + sample_service.organization_type = OrganizationType.FEDERAL + sample_organization.organization_type = OrganizationType.STATE dao_add_service_to_organization(sample_service, sample_organization.id) diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index cf59d5287..44be3c50e 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -40,7 +40,7 @@ from app.dao.services_dao import ( get_services_by_partial_name, ) from app.dao.users_dao import create_user_code, save_model_user -from app.enums import KeyType +from app.enums import KeyType, OrganizationType from app.models import ( ApiKey, InvitedUser, @@ -85,7 +85,7 @@ def test_create_service(notify_db_session): email_from="email_from", message_limit=1000, restricted=False, - organization_type="federal", + organization_type=OrganizationType.FEDERAL, created_by=user, ) dao_create_service(service, user) @@ -97,7 +97,7 @@ def test_create_service(notify_db_session): assert service_db.prefix_sms is True assert service.active is True assert user in service_db.users - assert service_db.organization_type == "federal" + assert service_db.organization_type == OrganizationType.FEDERAL assert not service.organization_id @@ -105,7 +105,7 @@ def test_create_service_with_organization(notify_db_session): user = create_user(email="local.authority@local-authority.gov.uk") organization = create_organization( name="Some local authority", - organization_type="state", + organization_type=OrganizationType.STATE, domains=["local-authority.gov.uk"], ) assert Service.query.count() == 0 @@ -114,7 +114,7 @@ def test_create_service_with_organization(notify_db_session): email_from="email_from", message_limit=1000, restricted=False, - organization_type="federal", + organization_type=OrganizationType.FEDERAL, created_by=user, ) dao_create_service(service, user) @@ -127,7 +127,7 @@ def test_create_service_with_organization(notify_db_session): assert service_db.prefix_sms is True assert service.active is True assert user in service_db.users - assert service_db.organization_type == "state" + assert service_db.organization_type == OrganizationType.STATE assert service.organization_id == organization.id assert service.organization == organization @@ -136,7 +136,7 @@ def test_fetch_service_by_id_with_api_keys(notify_db_session): user = create_user(email="local.authority@local-authority.gov.uk") organization = create_organization( name="Some local authority", - organization_type="state", + organization_type=OrganizationType.STATE, domains=["local-authority.gov.uk"], ) assert Service.query.count() == 0 @@ -145,7 +145,7 @@ def test_fetch_service_by_id_with_api_keys(notify_db_session): email_from="email_from", message_limit=1000, restricted=False, - organization_type="federal", + organization_type=OrganizationType.FEDERAL, created_by=user, ) dao_create_service(service, user) @@ -158,7 +158,7 @@ def test_fetch_service_by_id_with_api_keys(notify_db_session): assert service_db.prefix_sms is True assert service.active is True assert user in service_db.users - assert service_db.organization_type == "state" + assert service_db.organization_type == OrganizationType.STATE assert service.organization_id == organization.id assert service.organization == organization @@ -491,7 +491,7 @@ def test_get_all_user_services_should_return_empty_list_if_no_services_for_user( @freeze_time("2019-04-23T10:00:00") def test_dao_fetch_live_services_data(sample_user): - org = create_organization(organization_type="federal") + org = create_organization(organization_type=OrganizationType.FEDERAL) service = create_service(go_live_user=sample_user, go_live_at="2014-04-20T10:00:00") sms_template = create_template(service=service) service_2 = create_service( @@ -530,7 +530,7 @@ def test_dao_fetch_live_services_data(sample_user): "service_id": mock.ANY, "service_name": "Sample service", "organization_name": "test_org_1", - "organization_type": "federal", + "organization_type": OrganizationType.FEDERAL, "consent_to_research": None, "contact_name": "Test User", "contact_email": "notify@digital.fake.gov", diff --git a/tests/app/db.py b/tests/app/db.py index 7f18de861..0306ac299 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -26,7 +26,7 @@ from app.dao.service_sms_sender_dao import ( from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template, dao_update_template from app.dao.users_dao import save_model_user -from app.enums import KeyType, RecipientType, ServicePermissionType, TemplateType +from app.enums import KeyType, OrganizationType, RecipientType, ServicePermissionType, TemplateType from app.models import ( AnnualBilling, ApiKey, @@ -107,7 +107,7 @@ def create_service( prefix_sms=True, message_limit=1000, total_message_limit=250000, - organization_type="federal", + organization_type=OrganizationType.FEDERAL, check_if_service_exists=False, go_live_user=None, go_live_at=None, diff --git a/tests/app/organization/test_rest.py b/tests/app/organization/test_rest.py index 25f25ec43..7c0548db9 100644 --- a/tests/app/organization/test_rest.py +++ b/tests/app/organization/test_rest.py @@ -11,6 +11,7 @@ from app.dao.organization_dao import ( dao_add_user_to_organization, ) from app.dao.services_dao import dao_archive_service +from app.enums import OrganizationType from app.models import AnnualBilling, Organization from tests.app.db import ( create_annual_billing, @@ -25,7 +26,7 @@ from tests.app.db import ( def test_get_all_organizations(admin_request, notify_db_session): - create_organization(name="inactive org", active=False, organization_type="federal") + create_organization(name="inactive org", active=False, organization_type=OrganizationType.FEDERAL) create_organization(name="active org", domains=["example.com"]) response = admin_request.get("organization.get_organizations", _expected_status=200) @@ -52,7 +53,7 @@ def test_get_all_organizations(admin_request, notify_db_session): assert response[1]["active"] is False assert response[1]["count_of_live_services"] == 0 assert response[1]["domains"] == [] - assert response[1]["organization_type"] == "federal" + assert response[1]["organization_type"] == OrganizationType.FEDERAL def test_get_organization_by_id(admin_request, notify_db_session): @@ -163,7 +164,7 @@ def test_post_create_organization(admin_request, notify_db_session): data = { "name": "test organization", "active": True, - "organization_type": "state", + "organization_type": OrganizationType.STATE, } response = admin_request.post( @@ -213,7 +214,7 @@ def test_post_create_organization_existing_name_raises_400( data = { "name": sample_organization.name, "active": True, - "organization_type": "federal", + "organization_type": OrganizationType.FEDERAL, } response = admin_request.post( @@ -233,7 +234,7 @@ def test_post_create_organization_works(admin_request, sample_organization): data = { "name": "org 2", "active": True, - "organization_type": "federal", + "organization_type": OrganizationType.FEDERAL, } admin_request.post( @@ -251,7 +252,7 @@ def test_post_create_organization_works(admin_request, sample_organization): ( { "active": False, - "organization_type": "federal", + "organization_type": OrganizationType.FEDERAL, }, "name is a required property", ), @@ -295,7 +296,7 @@ def test_post_update_organization_updates_fields( data = { "name": "new organization name", "active": False, - "organization_type": "federal", + "organization_type": OrganizationType.FEDERAL, } admin_request.post( @@ -312,7 +313,7 @@ def test_post_update_organization_updates_fields( assert organization[0].name == data["name"] assert organization[0].active == data["active"] assert organization[0].domains == [] - assert organization[0].organization_type == "federal" + assert organization[0].organization_type == OrganizationType.FEDERAL @pytest.mark.parametrize( @@ -568,7 +569,7 @@ def test_post_update_organization_set_mou_emails_signed_by( def test_post_link_service_to_organization(admin_request, sample_service): data = {"service_id": str(sample_service.id)} - organization = create_organization(organization_type="federal") + organization = create_organization(organization_type=OrganizationType.FEDERAL) admin_request.post( "organization.link_service_to_organization", @@ -585,7 +586,7 @@ def test_post_link_service_to_organization_inserts_annual_billing( admin_request, sample_service ): data = {"service_id": str(sample_service.id)} - organization = create_organization(organization_type="federal") + organization = create_organization(organization_type=OrganizationType.FEDERAL) assert len(organization.services) == 0 assert len(AnnualBilling.query.all()) == 0 admin_request.post( @@ -610,7 +611,7 @@ def test_post_link_service_to_organization_rollback_service_if_annual_billing_up data = {"service_id": str(sample_service.id)} assert not sample_service.organization_type - organization = create_organization(organization_type="federal") + organization = create_organization(organization_type=OrganizationType.FEDERAL) assert len(organization.services) == 0 assert len(AnnualBilling.query.all()) == 0 with pytest.raises(expected_exception=SQLAlchemyError): @@ -641,7 +642,7 @@ def test_post_link_service_to_another_org( assert len(sample_organization.services) == 1 assert not sample_service.organization_type - new_org = create_organization(organization_type="federal") + new_org = create_organization(organization_type=OrganizationType.FEDERAL) admin_request.post( "organization.link_service_to_organization", _data=data, @@ -650,7 +651,7 @@ def test_post_link_service_to_another_org( ) assert not sample_organization.services assert len(new_org.services) == 1 - assert sample_service.organization_type == "federal" + assert sample_service.organization_type == OrganizationType.FEDERAL annual_billing = AnnualBilling.query.all() assert len(annual_billing) == 1 assert annual_billing[0].free_sms_fragment_limit == 150000 diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index c7cf7e87d..6957edfb1 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -14,7 +14,7 @@ from app.dao.service_user_dao import dao_get_service_user from app.dao.services_dao import dao_add_user_to_service, dao_remove_user_from_service from app.dao.templates_dao import dao_redact_template from app.dao.users_dao import save_model_user -from app.enums import KeyType, NotificationType, ServicePermissionType, TemplateType +from app.enums import KeyType, NotificationType, OrganizationType, ServicePermissionType, TemplateType from app.models import ( AnnualBilling, EmailBranding, @@ -684,7 +684,7 @@ def test_update_service(client, notify_db_session, sample_service): "email_from": "updated.service.name", "created_by": str(sample_service.created_by.id), "email_branding": str(brand.id), - "organization_type": "federal", + "organization_type": OrganizationType.FEDERAL, } auth_header = create_admin_authorization_header() @@ -699,7 +699,7 @@ def test_update_service(client, notify_db_session, sample_service): assert result["data"]["name"] == "updated service name" assert result["data"]["email_from"] == "updated.service.name" assert result["data"]["email_branding"] == str(brand.id) - assert result["data"]["organization_type"] == "federal" + assert result["data"]["organization_type"] == OrganizationType.FEDERAL def test_cant_update_service_org_type_to_random_value(client, sample_service): diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index 858039ee0..1032d6c69 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -20,7 +20,7 @@ from app.commands import ( ) from app.dao.inbound_numbers_dao import dao_get_available_inbound_numbers from app.dao.users_dao import get_user_by_email -from app.enums import KeyType, NotificationStatus, NotificationType +from app.enums import KeyType, NotificationStatus, NotificationType, OrganizationType from app.models import ( AnnualBilling, Job, @@ -251,7 +251,7 @@ def test_insert_inbound_numbers_from_file(notify_db_session, notify_api, tmpdir) @pytest.mark.parametrize( - "organization_type, expected_allowance", [("federal", 40000), ("state", 40000)] + "organization_type, expected_allowance", [(OrganizationType.FEDERAL, 40000), (OrganizationType.STATE, 40000)] ) def test_populate_annual_billing_with_defaults( notify_db_session, notify_api, organization_type, expected_allowance @@ -274,7 +274,7 @@ def test_populate_annual_billing_with_defaults( @pytest.mark.parametrize( - "organization_type, expected_allowance", [("federal", 40000), ("state", 40000)] + "organization_type, expected_allowance", [(OrganizationType.FEDERAL, 40000), (OrganizationType.STATE, 40000)] ) def test_populate_annual_billing_with_the_previous_years_allowance( notify_db_session, notify_api, organization_type, expected_allowance @@ -328,7 +328,7 @@ def test_fix_billable_units(notify_db_session, notify_api, sample_template): def test_populate_annual_billing_with_defaults_sets_free_allowance_to_zero_if_previous_year_is_zero( notify_db_session, notify_api ): - service = create_service(organization_type="federal") + service = create_service(organization_type=OrganizationType.FEDERAL) create_annual_billing( service_id=service.id, free_sms_fragment_limit=0, financial_year_start=2021 ) From e64e5005615aa3d61cc82e58e04b35a1a836cabb Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Mon, 29 Jan 2024 16:04:22 -0500 Subject: [PATCH 168/259] Debugging things is fun. Signed-off-by: Cliff Hill --- app/commands.py | 6 +++--- app/models.py | 4 ++-- app/user/rest.py | 2 +- app/webauthn/rest.py | 5 +++-- tests/app/dao/test_organization_dao.py | 4 +++- tests/app/db.py | 8 +++++++- tests/app/organization/test_rest.py | 4 +++- tests/app/service/test_rest.py | 8 +++++++- tests/app/test_commands.py | 14 ++++++++------ 9 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/commands.py b/app/commands.py index 8f5a8b15d..e36657812 100644 --- a/app/commands.py +++ b/app/commands.py @@ -49,7 +49,7 @@ from app.dao.users_dao import ( delete_user_verify_codes, get_user_by_email, ) -from app.enums import KeyType, NotificationStatus, NotificationType +from app.enums import AuthType, KeyType, NotificationStatus, NotificationType from app.models import ( AnnualBilling, Domain, @@ -238,7 +238,7 @@ def rebuild_ft_billing_for_day(service_id, day): "-a", "--auth_type", required=False, - help="The authentication type for the user, sms_auth or email_auth. Defaults to sms_auth if not provided", + help="The authentication type for the user, AuthType.SMS or AuthType.EMAIL. Defaults to AuthType.SMS if not provided", ) @click.option( "-p", "--permissions", required=True, help="Comma separated list of permissions." @@ -703,7 +703,7 @@ def validate_mobile(ctx, param, value): # noqa hide_input=True, confirmation_prompt=True, ) -@click.option("-a", "--auth_type", default="sms_auth") +@click.option("-a", "--auth_type", default=AuthType.SMS) @click.option("-s", "--state", default="active") @click.option("-d", "--admin", default=False, type=bool) def create_test_user(name, email, mobile_number, password, auth_type, state, admin): diff --git a/app/models.py b/app/models.py index e921ae97b..4f228ed17 100644 --- a/app/models.py +++ b/app/models.py @@ -155,7 +155,7 @@ class User(db.Model): # either email auth or a mobile number must be provided CheckConstraint( - "auth_type in ('email_auth', 'webauthn_auth') or mobile_number is not null" + "auth_type in (AuthType.EMAIL, AuthType.WEBAUTHN) or mobile_number is not null" ) services = db.relationship("Service", secondary="user_to_service", backref="users") @@ -182,7 +182,7 @@ class User(db.Model): if self.platform_admin: return True - if self.auth_type == "webauthn_auth": + if self.auth_type == AuthType.WEBAUTHN: return True return any( diff --git a/app/user/rest.py b/app/user/rest.py index 8ea746e4b..098d23de8 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -73,7 +73,7 @@ def handle_integrity_error(exc): return ( jsonify( result="error", - message="Mobile number must be set if auth_type is set to sms_auth", + message="Mobile number must be set if auth_type is set to AuthType.SMS", ), 400, ) diff --git a/app/webauthn/rest.py b/app/webauthn/rest.py index 97b7ab2ff..1dba333e7 100644 --- a/app/webauthn/rest.py +++ b/app/webauthn/rest.py @@ -62,9 +62,10 @@ def delete_webauthn_credential(user_id, webauthn_credential_id): user = get_user_by_id(user_id) if len(user.webauthn_credentials) == 1: - # TODO: Only raise an error if user has auth type webauthn_auth + # TODO: Only raise an error if user has auth type AuthType.WEBAUTHN raise InvalidRequest( - "Cannot delete last remaining webauthn credential for user", status_code=400 + "Cannot delete last remaining webauthn credential for user", + status_code=400, ) dao_delete_webauthn_credential(webauthn_credential) diff --git a/tests/app/dao/test_organization_dao.py b/tests/app/dao/test_organization_dao.py index 497b4a74a..a03b5fa8a 100644 --- a/tests/app/dao/test_organization_dao.py +++ b/tests/app/dao/test_organization_dao.py @@ -169,7 +169,9 @@ def test_update_organization_updates_the_service_org_type_if_org_type_is_provide sample_organization.services.append(sample_service) db.session.commit() - dao_update_organization(sample_organization.id, organization_type=OrganizationType.FEDERAL) + dao_update_organization( + sample_organization.id, organization_type=OrganizationType.FEDERAL + ) assert sample_organization.organization_type == OrganizationType.FEDERAL assert sample_service.organization_type == OrganizationType.FEDERAL diff --git a/tests/app/db.py b/tests/app/db.py index 0306ac299..06faf39c6 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -26,7 +26,13 @@ from app.dao.service_sms_sender_dao import ( from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template, dao_update_template from app.dao.users_dao import save_model_user -from app.enums import KeyType, OrganizationType, RecipientType, ServicePermissionType, TemplateType +from app.enums import ( + KeyType, + OrganizationType, + RecipientType, + ServicePermissionType, + TemplateType, +) from app.models import ( AnnualBilling, ApiKey, diff --git a/tests/app/organization/test_rest.py b/tests/app/organization/test_rest.py index 7c0548db9..74bf7ac71 100644 --- a/tests/app/organization/test_rest.py +++ b/tests/app/organization/test_rest.py @@ -26,7 +26,9 @@ from tests.app.db import ( def test_get_all_organizations(admin_request, notify_db_session): - create_organization(name="inactive org", active=False, organization_type=OrganizationType.FEDERAL) + create_organization( + name="inactive org", active=False, organization_type=OrganizationType.FEDERAL + ) create_organization(name="active org", domains=["example.com"]) response = admin_request.get("organization.get_organizations", _expected_status=200) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 6957edfb1..51de304dc 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -14,7 +14,13 @@ from app.dao.service_user_dao import dao_get_service_user from app.dao.services_dao import dao_add_user_to_service, dao_remove_user_from_service from app.dao.templates_dao import dao_redact_template from app.dao.users_dao import save_model_user -from app.enums import KeyType, NotificationType, OrganizationType, ServicePermissionType, TemplateType +from app.enums import ( + KeyType, + NotificationType, + OrganizationType, + ServicePermissionType, + TemplateType, +) from app.models import ( AnnualBilling, EmailBranding, diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index 1032d6c69..0d6fe2dfb 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -20,7 +20,7 @@ from app.commands import ( ) from app.dao.inbound_numbers_dao import dao_get_available_inbound_numbers from app.dao.users_dao import get_user_by_email -from app.enums import KeyType, NotificationStatus, NotificationType, OrganizationType +from app.enums import KeyType, NotificationStatus, NotificationType, OrganizationType, TemplateType from app.models import ( AnnualBilling, Job, @@ -91,7 +91,7 @@ def test_purge_functional_test_data_bad_mobile(notify_db_session, notify_api): def test_update_jobs_archived_flag(notify_db_session, notify_api): service = create_service() - sms_template = create_template(service=service, template_type="sms") + sms_template = create_template(service=service, template_type=TemplateType.SMS) create_job(sms_template) right_now = datetime.datetime.utcnow() @@ -251,13 +251,14 @@ def test_insert_inbound_numbers_from_file(notify_db_session, notify_api, tmpdir) @pytest.mark.parametrize( - "organization_type, expected_allowance", [(OrganizationType.FEDERAL, 40000), (OrganizationType.STATE, 40000)] + "organization_type, expected_allowance", + [(OrganizationType.FEDERAL, 40000), (OrganizationType.STATE, 40000)], ) def test_populate_annual_billing_with_defaults( notify_db_session, notify_api, organization_type, expected_allowance ): service = create_service( - service_name=organization_type, organization_type=organization_type + service_name=organization_type.value, organization_type=organization_type ) notify_api.test_cli_runner().invoke( @@ -274,13 +275,14 @@ def test_populate_annual_billing_with_defaults( @pytest.mark.parametrize( - "organization_type, expected_allowance", [(OrganizationType.FEDERAL, 40000), (OrganizationType.STATE, 40000)] + "organization_type, expected_allowance", + [(OrganizationType.FEDERAL, 40000), (OrganizationType.STATE, 40000)], ) def test_populate_annual_billing_with_the_previous_years_allowance( notify_db_session, notify_api, organization_type, expected_allowance ): service = create_service( - service_name=organization_type, organization_type=organization_type + service_name=organization_type.value, organization_type=organization_type ) notify_api.test_cli_runner().invoke( From 77bc5fbfb1c40bb73b2fda39db3db876dd510969 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Mon, 29 Jan 2024 16:09:27 -0500 Subject: [PATCH 169/259] Trying to get the Auth Types to work right. Signed-off-by: Cliff Hill --- tests/app/test_commands.py | 2 +- tests/app/test_model.py | 3 ++- tests/app/user/test_rest.py | 10 +++++----- tests/app/user/test_rest_verify.py | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index 0d6fe2dfb..2f5a3fb4b 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -229,7 +229,7 @@ def test_create_test_user_command(notify_db_session, notify_api): # that user should be the one we added user = User.query.filter_by(name="Fake Personson").first() assert user.email_address == "somebody@fake.gov" - assert user.auth_type == "sms_auth" + assert user.auth_type == AuthType.SMS assert user.state == "active" diff --git a/tests/app/test_model.py b/tests/app/test_model.py index fec227dc1..549d95c9a 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -8,6 +8,7 @@ from app import encryption from app.enums import ( AgreementStatus, AgreementType, + AuthType, NotificationStatus, RecipientType, TemplateType, @@ -328,7 +329,7 @@ def test_user_can_use_webauthn_if_platform_admin(sample_user, is_platform_admin) @pytest.mark.parametrize( ("auth_type", "can_use_webauthn"), - [("email_auth", False), ("sms_auth", False), ("webauthn_auth", True)], + [(AuthType.EMAIL, False), (AuthType.SMS, False), (AuthType.WEBAUTHN, True)], ) def test_user_can_use_webauthn_if_they_login_with_it( sample_user, auth_type, can_use_webauthn diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index 7be13a45c..3f4f22d67 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -210,7 +210,7 @@ def test_cannot_create_user_with_sms_auth_and_no_mobile( assert ( json_resp["message"] - == "Mobile number must be set if auth_type is set to sms_auth" + == "Mobile number must be set if auth_type is set to AuthType.SMS" ) @@ -883,15 +883,15 @@ def test_activate_user_fails_if_already_active(admin_request, sample_user): def test_update_user_auth_type(admin_request, sample_user): - assert sample_user.auth_type == "sms_auth" + assert sample_user.auth_type == AuthType.SMS resp = admin_request.post( "user.update_user_attribute", user_id=sample_user.id, - _data={"auth_type": "email_auth"}, + _data={"auth_type": AuthType.EMAIL}, ) assert resp["data"]["id"] == str(sample_user.id) - assert resp["data"]["auth_type"] == "email_auth" + assert resp["data"]["auth_type"] == AuthType.EMAIL def test_can_set_email_auth_and_remove_mobile_at_same_time(admin_request, sample_user): @@ -922,7 +922,7 @@ def test_cannot_remove_mobile_if_sms_auth(admin_request, sample_user): assert ( json_resp["message"] - == "Mobile number must be set if auth_type is set to sms_auth" + == "Mobile number must be set if auth_type is set to AuthType.SMS" ) diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index bb0e06ec3..01c28bb25 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -425,7 +425,7 @@ def test_reset_failed_login_count_returns_404_when_user_does_not_exist(client): assert resp.status_code == 404 -# we send sms_auth users and webauthn_auth users email code to validate their email access +# we send AuthType.SMS users and AuthType.WEBAUTHN users email code to validate their email access @pytest.mark.parametrize("auth_type", AuthType) @pytest.mark.parametrize( "data, expected_auth_url", @@ -514,7 +514,7 @@ def test_send_email_code_returns_404_for_bad_input_data(admin_request): @freeze_time("2016-01-01T12:00:00") -# we send sms_auth and webauthn_auth users email code to validate their email access +# we send iAuthType.SMS and AuthType.WEBAUTHN users email code to validate their email access @pytest.mark.parametrize("auth_type", AuthType) def test_user_verify_email_code(admin_request, sample_user, auth_type): sample_user.logged_in_at = datetime.utcnow() - timedelta(days=1) From 0790e5f64d9c4910e147c1f475d64c9a7e24dd08 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 30 Jan 2024 09:21:27 -0500 Subject: [PATCH 170/259] TRying to fix defaults. Signed-off-by: Cliff Hill --- app/dao/users_dao.py | 5 ++++- app/models.py | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/dao/users_dao.py b/app/dao/users_dao.py index 44098b1a6..f50b70c45 100644 --- a/app/dao/users_dao.py +++ b/app/dao/users_dao.py @@ -31,7 +31,10 @@ def save_user_attribute(usr, update_dict=None): def save_model_user( - user, update_dict=None, password=None, validated_email_access=False + user, + update_dict=None, + password=None, + validated_email_access=False, ): if password: user.password = password diff --git a/app/models.py b/app/models.py index 4f228ed17..8144bc873 100644 --- a/app/models.py +++ b/app/models.py @@ -137,7 +137,7 @@ class User(db.Model): state = db.Column(db.String, nullable=False, default="pending") platform_admin = db.Column(db.Boolean, nullable=False, default=False) current_session_id = db.Column(UUID(as_uuid=True), nullable=True) - auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS) + auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS.value) email_access_validated_at = db.Column( db.DateTime, index=False, @@ -310,7 +310,7 @@ class EmailBranding(db.Model): BrandType, index=True, nullable=False, - default=BrandType.ORG, + default=BrandType.ORG.value, ) def serialize(self): @@ -1130,7 +1130,7 @@ class TemplateBase(db.Model): TemplateProcessType, index=True, nullable=False, - default=TemplateProcessType.NORMAL, + default=TemplateProcessType.NORMAL.value, ) redact_personalisation = association_proxy( @@ -1385,7 +1385,7 @@ class Job(db.Model): JobStatus, index=True, nullable=False, - default=JobStatus.PENDING, + default=JobStatus.PENDING.value, ) archived = db.Column(db.Boolean, nullable=False, default=False) @@ -1453,7 +1453,7 @@ class NotificationAllTimeView(db.Model): NotificationStatus, name="notification_status", nullable=True, - default=NotificationStatus.CREATED, + default=NotificationStatus.CREATED.value, key="status", ) reference = db.Column(db.String) @@ -1511,7 +1511,7 @@ class Notification(db.Model): NotificationStatus, name="notification_status", nullable=True, - default=NotificationStatus.CREATED, + default=NotificationStatus.CREATED.value, key="status", ) reference = db.Column(db.String, nullable=True, index=True) @@ -1788,7 +1788,7 @@ class NotificationHistory(db.Model, HistoryModel): NotificationStatus, name="notification_status", nullable=True, - default=NotificationStatus.CREATED, + default=NotificationStatus.CREATED.value, key="status", ) reference = db.Column(db.String, nullable=True, index=True) @@ -1856,11 +1856,11 @@ class InvitedUser(db.Model): status = enum_column( InvitedUserStatus, nullable=False, - default=InvitedUserStatus.PENDING, + default=InvitedUserStatus.PENDING.value, ) permissions = db.Column(db.String, nullable=False) - auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS) - folder_permissions = db.Column(JSONB(none_as_null=True), nullable=False, default=[]) + auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS.value) + folder_permissions = db.Column(JSONB(none_as_null=True), nullable=False, default=list) # would like to have used properties for this but haven't found a way to make them # play nice with marshmallow yet @@ -1894,7 +1894,7 @@ class InvitedOrganizationUser(db.Model): status = enum_column( InvitedUserStatus, nullable=False, - default=InvitedUserStatus.PENDING, + default=InvitedUserStatus.PENDING.value, ) def serialize(self): From 116078524ef726ba9076718dcc31b2b823836ec6 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 30 Jan 2024 10:18:53 -0500 Subject: [PATCH 171/259] Switching to using StrEnum, which is in an external lib until 3.11. Signed-off-by: Cliff Hill --- app/commands.py | 3 +- app/enums.py | 59 +++++++++++++++++++------------------- app/models.py | 24 ++++++++-------- poetry.lock | 16 +++++++++++ pyproject.toml | 1 + tests/app/test_commands.py | 9 +++++- 6 files changed, 68 insertions(+), 44 deletions(-) diff --git a/app/commands.py b/app/commands.py index e36657812..6f195a84b 100644 --- a/app/commands.py +++ b/app/commands.py @@ -238,7 +238,8 @@ def rebuild_ft_billing_for_day(service_id, day): "-a", "--auth_type", required=False, - help="The authentication type for the user, AuthType.SMS or AuthType.EMAIL. Defaults to AuthType.SMS if not provided", + help="The authentication type for the user, AuthType.SMS or AuthType.EMAIL. " + "Defaults to AuthType.SMS if not provided", ) @click.option( "-p", "--permissions", required=True, help="Comma separated list of permissions." diff --git a/app/enums.py b/app/enums.py index 74d6a2100..3dbb594f0 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,42 +1,42 @@ -from enum import Enum +from strenum import StrEnum -class TemplateType(Enum): +class TemplateType(StrEnum): SMS = "sms" EMAIL = "email" LETTER = "letter" -class NotificationType(Enum): +class NotificationType(StrEnum): SMS = "sms" EMAIL = "email" LETTER = "letter" -class TemplateProcessType(Enum): +class TemplateProcessType(StrEnum): # TODO: Should Template.process_type be changed to use this? NORMAL = "normal" PRIORITY = "priority" -class AuthType(Enum): +class AuthType(StrEnum): SMS = "sms_auth" EMAIL = "email_auth" WEBAUTHN = "webauthn_auth" -class CallbackType(Enum): +class CallbackType(StrEnum): DELIVERY_STATUS = "delivery_status" COMPLAINT = "complaint" -class OrganizationType(Enum): +class OrganizationType(StrEnum): FEDERAL = "federal" STATE = "state" OTHER = "other" -class NotificationStatus(Enum): +class NotificationStatus(StrEnum): CANCELLED = "cancelled" CREATED = "created" SENDING = "sending" @@ -52,7 +52,7 @@ class NotificationStatus(Enum): VIRUS_SCAN_FAILED = "virus-scan-failed" @classmethod - def failed_types(cls) -> tuple["NotificationStatus", ...]: + def failed_types(cls) -> tuple[str, ...]: return ( cls.TECHNICAL_FAILURE, cls.TEMPORARY_FAILURE, @@ -62,7 +62,7 @@ class NotificationStatus(Enum): ) @classmethod - def completed_types(cls) -> tuple["NotificationStatus", ...]: + def completed_types(cls) -> tuple[str, ...]: return ( cls.SENT, cls.DELIVERED, @@ -74,11 +74,11 @@ class NotificationStatus(Enum): ) @classmethod - def success_types(cls) -> tuple["NotificationStatus", ...]: + def success_types(cls) -> tuple[str, ...]: return (cls.SENT, cls.DELIVERED) @classmethod - def billable_types(cls) -> tuple["NotificationStatus", ...]: + def billable_types(cls) -> tuple[str, ...]: return ( cls.SENDING, cls.SENT, @@ -90,7 +90,7 @@ class NotificationStatus(Enum): ) @classmethod - def billable_sms_types(cls) -> tuple["NotificationStatus", ...]: + def billable_sms_types(cls) -> tuple[str, ...]: return ( cls.SENDING, cls.SENT, # internationally @@ -101,7 +101,7 @@ class NotificationStatus(Enum): ) @classmethod - def sent_email_types(cls) -> tuple["NotificationStatus", ...]: + def sent_email_types(cls) -> tuple[str, ...]: return ( cls.SENDING, cls.DELIVERED, @@ -110,11 +110,11 @@ class NotificationStatus(Enum): ) @classmethod - def non_billable_types(cls) -> tuple["NotificationStatus", ...]: - return tuple({i for i in cls} - set(cls.billable_types())) + def non_billable_types(cls) -> tuple[str, ...]: + return tuple(set(cls) - set(cls.billable_types())) -class PermissionType(Enum): +class PermissionType(StrEnum): MANAGE_USERS = "manage_users" MANAGE_TEMPLATES = "manage_templates" MANAGE_SETTINGS = "manage_settings" @@ -125,7 +125,7 @@ class PermissionType(Enum): VIEW_ACTIVITY = "view_activity" @classmethod - def defaults(cls) -> tuple["PermissionType", ...]: + def defaults(cls) -> tuple[str, ...]: return ( cls.MANAGE_USERS, cls.MANAGE_TEMPLATES, @@ -137,7 +137,7 @@ class PermissionType(Enum): ) -class ServicePermissionType(Enum): +class ServicePermissionType(StrEnum): EMAIL = "email" SMS = "sms" INTERNATIONAL_SMS = "international_sms" @@ -147,9 +147,8 @@ class ServicePermissionType(Enum): UPLOAD_DOCUMENT = "upload_document" EDIT_FOLDER_PERMISSIONS = "edit_folder_permissions" - @property - def defaults(self) -> tuple["ServicePermissionType", ...]: - cls = type(self) + @classmethod + def defaults(cls) -> tuple[str, ...]: return ( cls.SMS, cls.EMAIL, @@ -157,18 +156,18 @@ class ServicePermissionType(Enum): ) -class RecipientType(Enum): +class RecipientType(StrEnum): MOBILE = "mobile" EMAIL = "email" -class KeyType(Enum): +class KeyType(StrEnum): NORMAL = "normal" TEAM = "team" TEST = "test" -class JobStatus(Enum): +class JobStatus(StrEnum): PENDING = "pending" IN_PROGRESS = "in progress" FINISHED = "finished" @@ -180,29 +179,29 @@ class JobStatus(Enum): ERROR = "error" -class InvitedUserStatus(Enum): +class InvitedUserStatus(StrEnum): PENDING = "pending" ACCEPTED = "accepted" CANCELLED = "cancelled" EXPIRED = "expired" -class BrandType(Enum): +class BrandType(StrEnum): ORG = "org" BOTH = "both" ORG_BANNER = "org_banner" -class CodeType(Enum): +class CodeType(StrEnum): EMAIL = "email" SMS = "sms" -class AgreementType(Enum): +class AgreementType(StrEnum): MOU = "MOU" IAA = "IAA" -class AgreementStatus(Enum): +class AgreementStatus(StrEnum): ACTIVE = "active" EXPIRED = "expired" diff --git a/app/models.py b/app/models.py index 8144bc873..6f7dec296 100644 --- a/app/models.py +++ b/app/models.py @@ -76,9 +76,9 @@ _enum_column_names = { def enum_column(enum_type, **kwargs): return db.Column( db.Enum( - *[i.value for i in enum_type], + *[i for i in enum_type], name=_enum_column_names[enum_type], - values_callable=(lambda x: [i.value for i in x]), + values_callable=(lambda x: [i for i in x]), ), **kwargs, ) @@ -137,7 +137,7 @@ class User(db.Model): state = db.Column(db.String, nullable=False, default="pending") platform_admin = db.Column(db.Boolean, nullable=False, default=False) current_session_id = db.Column(UUID(as_uuid=True), nullable=True) - auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS.value) + auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS) email_access_validated_at = db.Column( db.DateTime, index=False, @@ -310,7 +310,7 @@ class EmailBranding(db.Model): BrandType, index=True, nullable=False, - default=BrandType.ORG.value, + default=BrandType.ORG, ) def serialize(self): @@ -1130,7 +1130,7 @@ class TemplateBase(db.Model): TemplateProcessType, index=True, nullable=False, - default=TemplateProcessType.NORMAL.value, + default=TemplateProcessType.NORMAL, ) redact_personalisation = association_proxy( @@ -1385,7 +1385,7 @@ class Job(db.Model): JobStatus, index=True, nullable=False, - default=JobStatus.PENDING.value, + default=JobStatus.PENDING, ) archived = db.Column(db.Boolean, nullable=False, default=False) @@ -1453,7 +1453,7 @@ class NotificationAllTimeView(db.Model): NotificationStatus, name="notification_status", nullable=True, - default=NotificationStatus.CREATED.value, + default=NotificationStatus.CREATED, key="status", ) reference = db.Column(db.String) @@ -1511,7 +1511,7 @@ class Notification(db.Model): NotificationStatus, name="notification_status", nullable=True, - default=NotificationStatus.CREATED.value, + default=NotificationStatus.CREATED, key="status", ) reference = db.Column(db.String, nullable=True, index=True) @@ -1788,7 +1788,7 @@ class NotificationHistory(db.Model, HistoryModel): NotificationStatus, name="notification_status", nullable=True, - default=NotificationStatus.CREATED.value, + default=NotificationStatus.CREATED, key="status", ) reference = db.Column(db.String, nullable=True, index=True) @@ -1856,10 +1856,10 @@ class InvitedUser(db.Model): status = enum_column( InvitedUserStatus, nullable=False, - default=InvitedUserStatus.PENDING.value, + default=InvitedUserStatus.PENDING, ) permissions = db.Column(db.String, nullable=False) - auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS.value) + auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS) folder_permissions = db.Column(JSONB(none_as_null=True), nullable=False, default=list) # would like to have used properties for this but haven't found a way to make them @@ -1894,7 +1894,7 @@ class InvitedOrganizationUser(db.Model): status = enum_column( InvitedUserStatus, nullable=False, - default=InvitedUserStatus.PENDING.value, + default=InvitedUserStatus.PENDING, ) def serialize(self): diff --git a/poetry.lock b/poetry.lock index 6a1c8af62..be7abff41 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4231,6 +4231,22 @@ files = [ [package.dependencies] pbr = ">=2.0.0,<2.1.0 || >2.1.0" +[[package]] +name = "strenum" +version = "0.4.15" +description = "An Enum that inherits from str." +optional = false +python-versions = "*" +files = [ + {file = "StrEnum-0.4.15-py3-none-any.whl", hash = "sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659"}, + {file = "StrEnum-0.4.15.tar.gz", hash = "sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff"}, +] + +[package.extras] +docs = ["myst-parser[linkify]", "sphinx", "sphinx-rtd-theme"] +release = ["twine"] +test = ["pylint", "pytest", "pytest-black", "pytest-cov", "pytest-pylint"] + [[package]] name = "toml" version = "0.10.2" diff --git a/pyproject.toml b/pyproject.toml index 205d5a8db..970059f5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ pyjwt = "==2.8.0" python-dotenv = "==1.0.0" sqlalchemy = "==1.4.40" werkzeug = "^3.0.1" +strenum = "^0.4.15" [tool.poetry.group.dev.dependencies] diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index 2f5a3fb4b..586faaee7 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -20,7 +20,14 @@ from app.commands import ( ) from app.dao.inbound_numbers_dao import dao_get_available_inbound_numbers from app.dao.users_dao import get_user_by_email -from app.enums import KeyType, NotificationStatus, NotificationType, OrganizationType, TemplateType +from app.enums import ( + AuthType, + KeyType, + NotificationStatus, + NotificationType, + OrganizationType, + TemplateType, +) from app.models import ( AnnualBilling, Job, From fe690424670349b0d4a49ea06bca274a56a81e46 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 30 Jan 2024 10:19:35 -0500 Subject: [PATCH 172/259] Adding note. Signed-off-by: Cliff Hill --- app/enums.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/enums.py b/app/enums.py index 3dbb594f0..ced5fb0b9 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,4 +1,4 @@ -from strenum import StrEnum +from strenum import StrEnum # In 3.11 this is in the enum library. class TemplateType(StrEnum): From b1a53c76eea8b8a4d25e9d1f0cb3d3c9cc1ca8b8 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 30 Jan 2024 16:10:28 -0500 Subject: [PATCH 173/259] More cleanup. Signed-off-by: Cliff Hill --- app/enums.py | 3 ++- app/models.py | 4 ++- .../dao/test_fact_notification_status_dao.py | 26 +++++++++---------- tests/app/db.py | 26 +++++++++++-------- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/app/enums.py b/app/enums.py index ced5fb0b9..31d05c13c 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,4 +1,5 @@ -from strenum import StrEnum # In 3.11 this is in the enum library. +from strenum import StrEnum # In 3.11 this is in the enum library. We will not need + # this external library any more. class TemplateType(StrEnum): diff --git a/app/models.py b/app/models.py index 6f7dec296..788714ef0 100644 --- a/app/models.py +++ b/app/models.py @@ -1860,7 +1860,9 @@ class InvitedUser(db.Model): ) permissions = db.Column(db.String, nullable=False) auth_type = enum_column(AuthType, index=True, nullable=False, default=AuthType.SMS) - folder_permissions = db.Column(JSONB(none_as_null=True), nullable=False, default=list) + folder_permissions = db.Column( + JSONB(none_as_null=True), nullable=False, default=list + ) # would like to have used properties for this but haven't found a way to make them # play nice with marshmallow yet diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index 79d3eba9d..68ab57808 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -159,27 +159,27 @@ def test_fetch_notification_status_for_service_for_today_and_7_previous_days( service=service_1, template_type=TemplateType.EMAIL ) - create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) - create_ft_notification_status(date(2018, 10, 25), "sms", service_1, count=8) + create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=10,) + create_ft_notification_status(date(2018, 10, 25), NotificationType.SMS, service_1, count=8,) create_ft_notification_status( - date(2018, 10, 29), "sms", service_1, notification_status="created" + date(2018, 10, 29), NotificationType.SMS, service_1, notification_status=NotificationStatus.CREATED, ) - create_ft_notification_status(date(2018, 10, 29), "email", service_1, count=3) + create_ft_notification_status(date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3,) create_notification(sms_template, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification(sms_template_2, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status="delivered" + sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status="delivered" + email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status=NotificationStatus.DELIVERED, ) # too early, shouldn't be included create_notification( service_1.templates[0], created_at=datetime(2018, 10, 30, 12, 0, 0), - status="delivered", + status=NotificationStatus.DELIVERED, ) results = sorted( @@ -191,16 +191,16 @@ def test_fetch_notification_status_for_service_for_today_and_7_previous_days( assert len(results) == 3 - assert results[0].notification_type == "email" - assert results[0].status == "delivered" + assert results[0].notification_type == NotificationType.EMAIL + assert results[0].status == NotificationStatus.DELIVERED assert results[0].count == 4 - assert results[1].notification_type == "sms" - assert results[1].status == "created" + assert results[1].notification_type == NotificationType.SMS + assert results[1].status == NotificationStatus.CREATED assert results[1].count == 3 - assert results[2].notification_type == "sms" - assert results[2].status == "delivered" + assert results[2].notification_type == NotificationType.SMS + assert results[2].status == NotificationStatus.DELIVERED assert results[2].count == 19 diff --git a/tests/app/db.py b/tests/app/db.py index 06faf39c6..553b5c562 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -27,10 +27,14 @@ from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template, dao_update_template from app.dao.users_dao import save_model_user from app.enums import ( + JobStatus, KeyType, + NotificationStatus, + NotificationType, OrganizationType, RecipientType, ServicePermissionType, + TemplateProcessType, TemplateType, ) from app.models import ( @@ -204,7 +208,7 @@ def create_template( hidden=False, archived=False, folder=None, - process_type="normal", + process_type=TemplateProcessType.NORMAL, contact_block_id=None, ): data = { @@ -235,7 +239,7 @@ def create_notification( job=None, job_row_number=None, to_field=None, - status="created", + status=NotificationStatus.CREATED, reference=None, created_at=None, sent_at=None, @@ -326,7 +330,7 @@ def create_notification_history( template=None, job=None, job_row_number=None, - status="created", + status=NotificationStatus.CREATED, reference=None, created_at=None, sent_at=None, @@ -390,7 +394,7 @@ def create_job( template, notification_count=1, created_at=None, - job_status="pending", + job_status=JobStatus.PENDING, scheduled_for=None, processing_started=None, processing_finished=None, @@ -681,12 +685,12 @@ def create_ft_billing( def create_ft_notification_status( local_date, - notification_type="sms", + notification_type=NotificationType.SMS, service=None, template=None, job=None, - key_type="normal", - notification_status="delivered", + key_type=KeyType.NORMAL, + notification_status=NotificationStatus.DELIVERED, count=1, ): if job: @@ -842,7 +846,7 @@ def ses_notification_callback(): def create_service_data_retention( - service, notification_type="sms", days_of_retention=3 + service, notification_type=NotificationType.SMS, days_of_retention=3 ): data_retention = insert_service_data_retention( service_id=service.id, @@ -925,7 +929,7 @@ def set_up_usage_data(start_date): # service with emails only: service_with_emails = create_service(service_name="b - emails") - email_template = create_template(service=service_with_emails, template_type="email") + email_template = create_template(service=service_with_emails, template_type=TemplateType.EMAIL) org_2 = create_organization( name="Org for {}".format(service_with_emails.name), ) @@ -951,7 +955,7 @@ def set_up_usage_data(start_date): billing_reference="sms billing reference", ) sms_template = create_template( - service=service_with_sms_without_org, template_type="sms" + service=service_with_sms_without_org, template_type=TemplateType.SMS ) create_annual_billing( service_id=service_with_sms_without_org.id, @@ -971,7 +975,7 @@ def set_up_usage_data(start_date): service_name="e - sms within allowance" ) sms_template_2 = create_template( - service=service_with_sms_within_allowance, template_type="sms" + service=service_with_sms_within_allowance, template_type=TemplateType.SMS ) create_annual_billing( service_id=service_with_sms_within_allowance.id, From 544bf35c0633dbda5a7137f2525d0ba57c9ce7d1 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 31 Jan 2024 08:03:17 -0500 Subject: [PATCH 174/259] Adding another set of columns to the migration. Signed-off-by: Cliff Hill --- app/models.py | 6 +-- .../versions/0410_enums_for_everything.py | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/app/models.py b/app/models.py index 788714ef0..6eb81e482 100644 --- a/app/models.py +++ b/app/models.py @@ -2138,9 +2138,9 @@ class FactNotificationStatus(db.Model): nullable=False, ) job_id = db.Column(UUID(as_uuid=True), primary_key=True, index=True, nullable=False) - notification_type = db.Column(db.Text, primary_key=True, nullable=False) - key_type = db.Column(db.Text, primary_key=True, nullable=False) - notification_status = db.Column(db.Text, primary_key=True, nullable=False) + notification_type = enum_column(NotificationType, primary_key=True, nullable=False) + key_type = enum_column(KeyType, primary_key=True, nullable=False) + notification_status = enum_column(NotificationStatus, primary_key=True, nullable=False) notification_count = db.Column(db.Integer(), nullable=False) created_at = db.Column( db.DateTime, diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index d385bfe69..1ea6fc4d7 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -560,6 +560,30 @@ def upgrade(): existing_nullable=False, postgresql_using=enum_using("code_type", CodeType), ) + op.alter_column( + "ft_notification_status", + "notification_type", + existing_type=sa.TEXT(), + type_=enum_type(NotificationType), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) + op.alter_column( + "ft_notification_status", + "key_type", + existing_type=sa.TEXT(), + type_=enum_type(KeyType), + existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), + ) + op.alter_column( + "ft_notification_status", + "notification_type", + existing_type=sa.TEXT(), + type_=enum_type(NotificationStatus), + existing_nullable=False, + postgresql_using=enum_using("notification_status", NotificationStatus), + ) # Drop old enum types. enum_drop( @@ -587,6 +611,30 @@ def downgrade(): enum_create(values=["email", "sms"], name="verify_code_types") # Alter columns back + op.alter_column( + "ft_notification_status", + "notification_type", + existing_type=enum_type(NotificationStatus), + type_=sa.TEXT(), + existing_nullable=False, + postgresql_using=enum_using("notification_status", NotificationStatus), + ) + op.alter_column( + "ft_notification_status", + "key_type", + erxisting_type=enum_type(KeyType), + type_=sa.TEXT(), + existing_nullable=False, + postgresql_using=enum_using("key_type", KeyType), + ) + op.alter_column( + "ft_notification_status", + "notification_type", + existing_type=enum_type(NotificationType), + type_=sa.TEXT(), + existing_nullable=False, + postgresql_using=enum_using("notification_type", NotificationType), + ) op.alter_column( "verify_codes", "code_type", From 689ff10b7110d6f4e63e8c7abfe200b51015bc9b Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Feb 2024 11:40:25 -0500 Subject: [PATCH 175/259] Latest and greatest. Signed-off-by: Cliff Hill --- app/dao/fact_notification_status_dao.py | 10 +- app/enums.py | 5 +- app/models.py | 11 +- .../versions/0410_enums_for_everything.py | 4 +- .../dao/test_fact_notification_status_dao.py | 101 +++++++++++------- tests/app/db.py | 4 +- 6 files changed, 84 insertions(+), 51 deletions(-) diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index ee2e7c626..27e881191 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -146,7 +146,7 @@ def fetch_notification_status_for_service_for_today_and_7_previous_days( stats_for_today = ( db.session.query( - Notification.notification_type.cast(db.Text), + Notification.notification_type, Notification.status, *([Notification.template_id] if by_template else []), func.count().label("count"), @@ -212,14 +212,14 @@ def fetch_notification_status_totals_for_all_services(start_date, end_date): if start_date <= datetime.utcnow().date() <= end_date: stats_for_today = ( db.session.query( - Notification.notification_type.cast(db.Text).label("notification_type"), + Notification.notification_type.label("notification_type"), Notification.status, Notification.key_type, func.count().label("count"), ) .filter(Notification.created_at >= today) .group_by( - Notification.notification_type.cast(db.Text), + Notification.notification_type, Notification.status, Notification.key_type, ) @@ -297,7 +297,7 @@ def fetch_stats_for_all_services_by_date_range( today = get_midnight_in_utc(datetime.utcnow()) subquery = ( db.session.query( - Notification.notification_type.cast(db.Text).label("notification_type"), + Notification.notification_type.label("notification_type"), Notification.status.label("status"), Notification.service_id.label("service_id"), func.count(Notification.id).label("count"), @@ -450,7 +450,7 @@ def fetch_monthly_template_usage_for_service(start_date, end_date, service_id): def get_total_notifications_for_date_range(start_date, end_date): query = ( db.session.query( - FactNotificationStatus.local_date.cast(db.Text).label("local_date"), + FactNotificationStatus.local_date.label("local_date"), func.sum( case( [ diff --git a/app/enums.py b/app/enums.py index 31d05c13c..e2c0966ed 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,5 +1,6 @@ -from strenum import StrEnum # In 3.11 this is in the enum library. We will not need - # this external library any more. +from strenum import StrEnum # In 3.11 this is in the enum library. We will not need + +# this external library any more. class TemplateType(StrEnum): diff --git a/app/models.py b/app/models.py index 6eb81e482..e5a436df5 100644 --- a/app/models.py +++ b/app/models.py @@ -1973,10 +1973,7 @@ class Rate(db.Model): notification_type = enum_column(NotificationType, index=True, nullable=False) def __str__(self): - the_string = "{}".format(self.rate) - the_string += " {}".format(self.notification_type) - the_string += " {}".format(self.valid_from) - return the_string + return f"{self.rate} {self.notification_type} {self.valid_from}" class InboundSms(db.Model): @@ -2140,7 +2137,11 @@ class FactNotificationStatus(db.Model): job_id = db.Column(UUID(as_uuid=True), primary_key=True, index=True, nullable=False) notification_type = enum_column(NotificationType, primary_key=True, nullable=False) key_type = enum_column(KeyType, primary_key=True, nullable=False) - notification_status = enum_column(NotificationStatus, primary_key=True, nullable=False) + notification_status = enum_column( + NotificationStatus, + primary_key=True, + nullable=False, + ) notification_count = db.Column(db.Integer(), nullable=False) created_at = db.Column( db.DateTime, diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index 1ea6fc4d7..879e3c772 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -578,7 +578,7 @@ def upgrade(): ) op.alter_column( "ft_notification_status", - "notification_type", + "notification_status", existing_type=sa.TEXT(), type_=enum_type(NotificationStatus), existing_nullable=False, @@ -613,7 +613,7 @@ def downgrade(): # Alter columns back op.alter_column( "ft_notification_status", - "notification_type", + "notification_status", existing_type=enum_type(NotificationStatus), type_=sa.TEXT(), existing_nullable=False, diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index 68ab57808..cd9c5bedd 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -17,8 +17,8 @@ from app.dao.fact_notification_status_dao import ( get_total_notifications_for_date_range, update_fact_notification_status, ) -from app.enums import KeyType, NotificationStatus -from app.models import FactNotificationStatus, NotificationType, TemplateType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType +from app.models import FactNotificationStatus from tests.app.db import ( create_ft_notification_status, create_job, @@ -32,24 +32,31 @@ def test_fetch_notification_status_for_service_by_month(notify_db_session): service_1 = create_service(service_name="service_1") service_2 = create_service(service_name="service_2") - create_ft_notification_status(date(2018, 1, 1), "sms", service_1, count=4) - create_ft_notification_status(date(2018, 1, 2), "sms", service_1, count=10) create_ft_notification_status( - date(2018, 1, 2), "sms", service_1, notification_status="created" + date(2018, 1, 1), NotificationType.SMS, service_1, count=4 ) - create_ft_notification_status(date(2018, 1, 3), "email", service_1) + create_ft_notification_status( + date(2018, 1, 2), NotificationType.SMS, service_1, count=10 + ) + create_ft_notification_status( + date(2018, 1, 2), + NotificationType.SMS, + service_1, + notification_status=NotificationStatus.CREATED, + ) + create_ft_notification_status(date(2018, 1, 3), NotificationType.EMAIL, service_1) - create_ft_notification_status(date(2018, 2, 2), "sms", service_1) + create_ft_notification_status(date(2018, 2, 2), NotificationType.SMS, service_1) # not included - too early - create_ft_notification_status(date(2017, 12, 31), "sms", service_1) + create_ft_notification_status(date(2017, 12, 31), NotificationType.SMS, service_1) # not included - too late - create_ft_notification_status(date(2017, 3, 1), "sms", service_1) + create_ft_notification_status(date(2017, 3, 1), NotificationType.SMS, service_1) # not included - wrong service - create_ft_notification_status(date(2018, 1, 3), "sms", service_2) + create_ft_notification_status(date(2018, 1, 3), NotificationType.SMS, service_2) # not included - test keys create_ft_notification_status( - date(2018, 1, 3), "sms", service_1, key_type=KeyType.TEST + date(2018, 1, 3), NotificationType.SMS, service_1, key_type=KeyType.TEST ) results = sorted( @@ -62,23 +69,23 @@ def test_fetch_notification_status_for_service_by_month(notify_db_session): assert len(results) == 4 assert results[0].month.date() == date(2018, 1, 1) - assert results[0].notification_type == "email" - assert results[0].notification_status == "delivered" + assert results[0].notification_type == NotificationType.EMAIL + assert results[0].notification_status == NotificationStatus.DELIVERED assert results[0].count == 1 assert results[1].month.date() == date(2018, 1, 1) - assert results[1].notification_type == "sms" - assert results[1].notification_status == "created" + assert results[1].notification_type == NotificationType.SMS + assert results[1].notification_status == NotificationStatus.CREATED assert results[1].count == 1 assert results[2].month.date() == date(2018, 1, 1) - assert results[2].notification_type == "sms" - assert results[2].notification_status == "delivered" + assert results[2].notification_type == NotificationType.SMS + assert results[2].notification_status == NotificationStatus.DELIVERED assert results[2].count == 14 assert results[3].month.date() == date(2018, 2, 1) - assert results[3].notification_type == "sms" - assert results[3].notification_status == "delivered" + assert results[3].notification_type == NotificationType.SMS + assert results[3].notification_status == NotificationStatus.DELIVERED assert results[3].count == 1 @@ -109,7 +116,7 @@ def test_fetch_notification_status_for_service_for_day(notify_db_session): create_notification( service_1.templates[0], created_at=datetime(2018, 6, 1, 12, 0, 0), - status="delivered", + status=NotificationStatus.DELIVERED, ) # test key @@ -138,13 +145,13 @@ def test_fetch_notification_status_for_service_for_day(notify_db_session): assert len(results) == 2 assert results[0].month == datetime(2018, 6, 1, 0, 0) - assert results[0].notification_type == "sms" - assert results[0].notification_status == "created" + assert results[0].notification_type == NotificationType.SMS + assert results[0].notification_status == NotificationStatus.CREATED assert results[0].count == 3 assert results[1].month == datetime(2018, 6, 1, 0, 0) - assert results[1].notification_type == "sms" - assert results[1].notification_status == "delivered" + assert results[1].notification_type == NotificationType.SMS + assert results[1].notification_status == NotificationStatus.DELIVERED assert results[1].count == 1 @@ -159,20 +166,42 @@ def test_fetch_notification_status_for_service_for_today_and_7_previous_days( service=service_1, template_type=TemplateType.EMAIL ) - create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=10,) - create_ft_notification_status(date(2018, 10, 25), NotificationType.SMS, service_1, count=8,) create_ft_notification_status( - date(2018, 10, 29), NotificationType.SMS, service_1, notification_status=NotificationStatus.CREATED, + date(2018, 10, 29), + NotificationType.SMS, + service_1, + count=10, + ) + create_ft_notification_status( + date(2018, 10, 25), + NotificationType.SMS, + service_1, + count=8, + ) + create_ft_notification_status( + date(2018, 10, 29), + NotificationType.SMS, + service_1, + notification_status=NotificationStatus.CREATED, + ) + create_ft_notification_status( + date(2018, 10, 29), + NotificationType.EMAIL, + service_1, + count=3, ) - create_ft_notification_status(date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3,) create_notification(sms_template, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification(sms_template_2, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, + sms_template, + created_at=datetime(2018, 10, 31, 12, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status=NotificationStatus.DELIVERED, + email_template, + created_at=datetime(2018, 10, 31, 13, 0, 0), + status=NotificationStatus.DELIVERED, ) # too early, shouldn't be included @@ -299,16 +328,16 @@ def test_fetch_notification_status_totals_for_all_services( assert len(results) == 3 - assert results[0].notification_type == "email" - assert results[0].status == "delivered" + assert results[0].notification_type == NotificationType.EMAIL + assert results[0].status == NotificationStatus.DELIVERED assert results[0].count == expected_email - assert results[1].notification_type == "sms" - assert results[1].status == "created" + assert results[1].notification_type == NotificationType.SMS + assert results[1].status == NotificationStatus.CREATED assert results[1].count == expected_created_sms - assert results[2].notification_type == "sms" - assert results[2].status == "delivered" + assert results[2].notification_type == NotificationType.SMS + assert results[2].status == NotificationStatus.DELIVERED assert results[2].count == expected_sms diff --git a/tests/app/db.py b/tests/app/db.py index 553b5c562..5fdc70b3c 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -929,7 +929,9 @@ def set_up_usage_data(start_date): # service with emails only: service_with_emails = create_service(service_name="b - emails") - email_template = create_template(service=service_with_emails, template_type=TemplateType.EMAIL) + email_template = create_template( + service=service_with_emails, template_type=TemplateType.EMAIL + ) org_2 = create_organization( name="Org for {}".format(service_with_emails.name), ) From c6136bb4aa884463e768c9c773bee376159fe6a5 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 2 Feb 2024 17:48:57 -0500 Subject: [PATCH 176/259] Minor fixes. Signed-off-by: Cliff Hill --- app/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models.py b/app/models.py index e5a436df5..b4b2b36fb 100644 --- a/app/models.py +++ b/app/models.py @@ -76,9 +76,9 @@ _enum_column_names = { def enum_column(enum_type, **kwargs): return db.Column( db.Enum( - *[i for i in enum_type], + enum_type, name=_enum_column_names[enum_type], - values_callable=(lambda x: [i for i in x]), + values_callable=(lambda x: [i.value for i in x]), ), **kwargs, ) From 671772966403781886367c4e511f7b993f66bb66 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 6 Feb 2024 15:09:49 -0500 Subject: [PATCH 177/259] Casting columns to TEXT type seems to resolve some bugs with UNION queries. Signed-off-by: Cliff Hill --- app/dao/fact_notification_status_dao.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index 27e881191..0473bdc4c 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -130,8 +130,10 @@ def fetch_notification_status_for_service_for_today_and_7_previous_days( start_date = midnight_n_days_ago(limit_days) now = datetime.utcnow() stats_for_7_days = db.session.query( - FactNotificationStatus.notification_type.label("notification_type"), - FactNotificationStatus.notification_status.label("status"), + FactNotificationStatus.notification_type.cast(db.Text).label( + "notification_type" + ), + FactNotificationStatus.notification_status.cast(db.Text).label("status"), *( [FactNotificationStatus.template_id.label("template_id")] if by_template @@ -146,8 +148,8 @@ def fetch_notification_status_for_service_for_today_and_7_previous_days( stats_for_today = ( db.session.query( - Notification.notification_type, - Notification.status, + Notification.notification_type.cast(db.Text), + Notification.status.cast(db.Text), *([Notification.template_id] if by_template else []), func.count().label("count"), ) @@ -193,9 +195,9 @@ def fetch_notification_status_for_service_for_today_and_7_previous_days( def fetch_notification_status_totals_for_all_services(start_date, end_date): stats = ( db.session.query( - FactNotificationStatus.notification_type.label("notification_type"), - FactNotificationStatus.notification_status.label("status"), - FactNotificationStatus.key_type.label("key_type"), + FactNotificationStatus.notification_type.cast(db.Text).label("notification_type"), + FactNotificationStatus.notification_status.cast(db.Text).label("status"), + FactNotificationStatus.key_type.cast(db.Text).label("key_type"), func.sum(FactNotificationStatus.notification_count).label("count"), ) .filter( @@ -212,9 +214,9 @@ def fetch_notification_status_totals_for_all_services(start_date, end_date): if start_date <= datetime.utcnow().date() <= end_date: stats_for_today = ( db.session.query( - Notification.notification_type.label("notification_type"), - Notification.status, - Notification.key_type, + Notification.notification_type.cast(db.Text).label("notification_type"), + Notification.status.cast(db.Text), + Notification.key_type.cast(db.Text), func.count().label("count"), ) .filter(Notification.created_at >= today) From 2655dc60cd49bea49fd8e1a061292accc27964a2 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 6 Feb 2024 16:01:08 -0500 Subject: [PATCH 178/259] Stuff was done. Signed-off-by: Cliff Hill --- app/dao/fact_notification_status_dao.py | 14 +++++++++----- tests/app/dao/test_fact_notification_status_dao.py | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/dao/fact_notification_status_dao.py b/app/dao/fact_notification_status_dao.py index 0473bdc4c..286beebc2 100644 --- a/app/dao/fact_notification_status_dao.py +++ b/app/dao/fact_notification_status_dao.py @@ -195,7 +195,9 @@ def fetch_notification_status_for_service_for_today_and_7_previous_days( def fetch_notification_status_totals_for_all_services(start_date, end_date): stats = ( db.session.query( - FactNotificationStatus.notification_type.cast(db.Text).label("notification_type"), + FactNotificationStatus.notification_type.cast(db.Text).label( + "notification_type" + ), FactNotificationStatus.notification_status.cast(db.Text).label("status"), FactNotificationStatus.key_type.cast(db.Text).label("key_type"), func.sum(FactNotificationStatus.notification_count).label("count"), @@ -270,8 +272,10 @@ def fetch_stats_for_all_services_by_date_range( Service.restricted.label("restricted"), Service.active.label("active"), Service.created_at.label("created_at"), - FactNotificationStatus.notification_type.label("notification_type"), - FactNotificationStatus.notification_status.label("status"), + FactNotificationStatus.notification_type.cast(db.Text).label( + "notification_type" + ), + FactNotificationStatus.notification_status.cast(db.Text).label("status"), func.sum(FactNotificationStatus.notification_count).label("count"), ) .filter( @@ -321,8 +325,8 @@ def fetch_stats_for_all_services_by_date_range( Service.restricted.label("restricted"), Service.active.label("active"), Service.created_at.label("created_at"), - subquery.c.notification_type.label("notification_type"), - subquery.c.status.label("status"), + subquery.c.notification_type.cast(db.Text).label("notification_type"), + subquery.c.status.cast(db.Text).label("status"), subquery.c.count.label("count"), ).outerjoin(subquery, subquery.c.service_id == Service.id) diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index cd9c5bedd..e1606433e 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -857,7 +857,7 @@ def test_get_total_notifications_for_date_range(sample_service): ) assert len(results) == 1 - assert results[0] == ("2021-03-01", 15, 20) + assert results[0] == (date.fromisoformat("2021-03-01"), 15, 20) @pytest.mark.skip(reason="Need a better way to test variable DST date") From 2a6784629efbf705277aedbb195c05e986b37fe5 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 7 Feb 2024 12:55:15 -0500 Subject: [PATCH 179/259] Fixed more stuff. Signed-off-by: Cliff Hill --- app/schemas.py | 16 ++++++++-------- tests/app/service/test_rest.py | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/schemas.py b/app/schemas.py index 4e13e9aaa..b975e3c2d 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -24,7 +24,7 @@ from notifications_utils.recipients import ( from app import ma, models from app.dao.permissions_dao import permission_dao -from app.enums import TemplateType +from app.enums import ServicePermissionType, TemplateType from app.models import ServicePermission from app.utils import DATETIME_FORMAT_NO_TIMEZONE, get_template_instance @@ -153,7 +153,7 @@ class UserSchema(BaseSchema): if value is not None: validate_phone_number(value, international=True) except InvalidPhoneError as error: - raise ValidationError("Invalid phone number: {}".format(error)) + raise ValidationError(f"Invalid phone number: {error}") class UserUpdateAttributeSchema(BaseSchema): @@ -193,13 +193,13 @@ class UserUpdateAttributeSchema(BaseSchema): if value is not None: validate_phone_number(value, international=True) except InvalidPhoneError as error: - raise ValidationError("Invalid phone number: {}".format(error)) + raise ValidationError(f"Invalid phone number: {error}") @validates_schema(pass_original=True) def check_unknown_fields(self, data, original_data, **kwargs): for key in original_data: if key not in self.fields: - raise ValidationError("Unknown field name {}".format(key)) + raise ValidationError(f"Unknown field name {key}") class UserUpdatePasswordSchema(BaseSchema): @@ -210,7 +210,7 @@ class UserUpdatePasswordSchema(BaseSchema): def check_unknown_fields(self, data, original_data, **kwargs): for key in original_data: if key not in self.fields: - raise ValidationError("Unknown field name {}".format(key)) + raise ValidationError(f"Unknown field name {key}") class ProviderDetailsSchema(BaseSchema): @@ -285,12 +285,12 @@ class ServiceSchema(BaseSchema, UUIDsAsStringsMixin): def validate_permissions(self, value): permissions = [v.permission for v in value] for p in permissions: - if p not in models.SERVICE_PERMISSION_TYPES: - raise ValidationError("Invalid Service Permission: '{}'".format(p)) + if p not in {e for e in ServicePermissionType}: + raise ValidationError(f"Invalid Service Permission: '{p}'") if len(set(permissions)) != len(permissions): duplicates = list(set([x for x in permissions if permissions.count(x) > 1])) - raise ValidationError("Duplicate Service Permission: {}".format(duplicates)) + raise ValidationError(f"Duplicate Service Permission: {duplicates}") @pre_load() def format_for_data_model(self, in_data, **kwargs): diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 51de304dc..ddd5e6c5e 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -768,7 +768,7 @@ def test_update_service_flags(client, sample_service): json_resp = resp.json assert resp.status_code == 200 assert json_resp["data"]["name"] == sample_service.name - data = {"permissions": {ServicePermissionType.INTERNATIONAL_SMS}} + data = {"permissions": [ServicePermissionType.INTERNATIONAL_SMS]} auth_header = create_admin_authorization_header() @@ -855,7 +855,7 @@ def test_update_service_flags_with_service_without_default_service_permissions( ): auth_header = create_admin_authorization_header() data = { - "permissions": {ServicePermissionType.INTERNATIONAL_SMS}, + "permissions": [ServicePermissionType.INTERNATIONAL_SMS], } resp = client.post( @@ -888,7 +888,7 @@ def test_update_service_flags_will_remove_service_permissions( p.permission for p in service.permissions } - data = {"permissions": {ServicePermissionType.SMS, ServicePermissionType.EMAIL}} + data = {"permissions": [ServicePermissionType.SMS, ServicePermissionType.EMAIL]} resp = client.post( "/service/{}".format(service.id), @@ -912,7 +912,7 @@ def test_update_permissions_will_override_permission_flags( ): auth_header = create_admin_authorization_header() - data = {"permissions": {ServicePermissionType.INTERNATIONAL_SMS}} + data = {"permissions": [ServicePermissionType.INTERNATIONAL_SMS]} resp = client.post( "/service/{}".format(service_with_no_permissions.id), @@ -922,9 +922,9 @@ def test_update_permissions_will_override_permission_flags( result = resp.json assert resp.status_code == 200 - assert set(result["data"]["permissions"]) == { + assert set(result["data"]["permissions"]) == [ ServicePermissionType.INTERNATIONAL_SMS - } + ] def test_update_service_permissions_will_add_service_permissions( @@ -932,7 +932,7 @@ def test_update_service_permissions_will_add_service_permissions( ): auth_header = create_admin_authorization_header() - data = {"permissions": {ServicePermissionType.EMAIL, ServicePermissionType.SMS}} + data = {"permissions": [ServicePermissionType.EMAIL, ServicePermissionType.SMS]} resp = client.post( "/service/{}".format(sample_service.id), @@ -986,11 +986,11 @@ def test_update_permissions_with_an_invalid_permission_will_raise_error( invalid_permission = "invalid_permission" data = { - "permissions": { + "permissions": [ ServicePermissionType.EMAIL, ServicePermissionType.SMS, invalid_permission, - } + ] } resp = client.post( @@ -1014,11 +1014,11 @@ def test_update_permissions_with_duplicate_permissions_will_raise_error( auth_header = create_admin_authorization_header() data = { - "permissions": { + "permissions": [ ServicePermissionType.EMAIL, ServicePermissionType.SMS, ServicePermissionType.SMS, - } + ] } resp = client.post( From 1d580df0eac93b379c2951151e6cf1f14d9ea6e2 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 7 Feb 2024 17:25:59 -0500 Subject: [PATCH 180/259] More fixes, only 47 tests left to get right. Signed-off-by: Cliff Hill --- tests/app/service/test_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index ddd5e6c5e..b6cc6fd04 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1212,7 +1212,7 @@ def test_default_permissions_are_added_for_user_service( ] from app.dao.permissions_dao import default_service_permissions - assert sorted(default_service_permissions) == sorted(service_permissions) + assert sorted(ServicePermissionType.defaults()) == sorted(service_permissions) def test_add_existing_user_to_another_service_with_all_permissions( From f43ad683293866925dcbadc336c8e6433d2db570 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 8 Feb 2024 09:26:37 -0500 Subject: [PATCH 181/259] More fixes for everyone. Signed-off-by: Cliff Hill --- .../test_notification_schemas.py | 24 ++++++++----------- tests/app/v2/templates/test_get_templates.py | 14 +++++------ .../v2/templates/test_templates_schemas.py | 2 +- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/tests/app/v2/notifications/test_notification_schemas.py b/tests/app/v2/notifications/test_notification_schemas.py index 24bcec93e..0c6595c20 100644 --- a/tests/app/v2/notifications/test_notification_schemas.py +++ b/tests/app/v2/notifications/test_notification_schemas.py @@ -58,9 +58,7 @@ def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_stat errors = json.loads(str(e.value)).get("errors") assert len(errors) == len(invalid_statuses) for index, value in enumerate(invalid_statuses): - assert errors[index]["message"] == "status {} {}".format( - value, partial_error_status - ) + assert errors[index]["message"] == f"status {value} {partial_error_status}" @pytest.mark.parametrize( @@ -71,13 +69,13 @@ def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_stat # multiple invalid template_types (["orange", "avocado", "banana"], []), # one bad template_type and one good template_type - (["orange"], ["sms"]), + (["orange"], [TemplateType.SMS]), ], ) def test_get_notifications_request_invalid_template_types( invalid_template_types, valid_template_types ): - partial_error_template_type = "is not one of [sms, email]" + partial_error_template_type = "is not one of [sms, email, letter]" with pytest.raises(ValidationError) as e: validate( @@ -88,8 +86,8 @@ def test_get_notifications_request_invalid_template_types( errors = json.loads(str(e.value)).get("errors") assert len(errors) == len(invalid_template_types) for index, value in enumerate(invalid_template_types): - assert errors[index]["message"] == "template_type {} {}".format( - value, partial_error_template_type + assert errors[index]["message"] == ( + f"template_type {value} {partial_error_template_type}" ) @@ -97,8 +95,8 @@ def test_get_notifications_request_invalid_statuses_and_template_types(): with pytest.raises(ValidationError) as e: validate( { - "status": ["created", "elephant", "giraffe"], - "template_type": ["sms", "orange", "avocado"], + "status": [NotificationStatus.CREATED, "elephant", "giraffe"], + "template_type": [TemplateType.SMS, "orange", "avocado"], }, get_notifications_request, ) @@ -110,17 +108,15 @@ def test_get_notifications_request_invalid_statuses_and_template_types(): error_messages = [error["message"] for error in errors] for invalid_status in ["elephant", "giraffe"]: assert ( - "status {} is not one of [cancelled, created, sending, sent, delivered, " + f"status {invalid_status} is not one of [cancelled, created, sending, sent, delivered, " "pending, failed, technical-failure, temporary-failure, permanent-failure, " - "pending-virus-check, validation-failed, virus-scan-failed]".format( - invalid_status - ) + "pending-virus-check, validation-failed, virus-scan-failed]" in error_messages ) for invalid_template_type in ["orange", "avocado"]: assert ( - "template_type {} is not one of [sms, email]".format(invalid_template_type) + f"template_type {invalid_template_type} is not one of [sms, email, letter]" in error_messages ) diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index 016dde637..328633050 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -13,7 +13,7 @@ def test_get_all_templates_returns_200(client, sample_service): create_template( sample_service, template_type=tmp_type, - subject="subject_{}".format(name) if tmp_type == TemplateType.EMAIL else "", + subject=f"subject_{name}" if tmp_type == TemplateType.EMAIL else "", template_name=name, ) for name, tmp_type in product(("A", "B", "C"), TemplateType) @@ -47,8 +47,8 @@ def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tm create_template( sample_service, template_type=tmp_type, - template_name="Template {}".format(i), - subject="subject_{}".format(i) if tmp_type == TemplateType.EMAIL else "", + template_name=f"Template {i}", + subject=f"subject_{i}" if tmp_type == TemplateType.EMAIL else "", ) for i in range(3) ] @@ -56,7 +56,7 @@ def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tm auth_header = create_service_authorization_header(service_id=sample_service.id) response = client.get( - path="/v2/templates?type={}".format(tmp_type), + path=f"/v2/templates?type={tmp_type}", headers=[("Content-Type", "application/json"), auth_header], ) @@ -92,7 +92,7 @@ def test_get_correct_num_templates_for_valid_type_returns_200( auth_header = create_service_authorization_header(service_id=sample_service.id) response = client.get( - path="/v2/templates?type={}".format(tmp_type), + path=f"/v2/templates?type={tmp_type}", headers=[("Content-Type", "application/json"), auth_header], ) @@ -109,7 +109,7 @@ def test_get_all_templates_for_invalid_type_returns_400(client, sample_service): invalid_type = "coconut" response = client.get( - path="/v2/templates?type={}".format(invalid_type), + path=f"/v2/templates?type={invalid_type}", headers=[("Content-Type", "application/json"), auth_header], ) @@ -122,7 +122,7 @@ def test_get_all_templates_for_invalid_type_returns_400(client, sample_service): "status_code": 400, "errors": [ { - "message": "type coconut is not one of [sms, email]", + "message": "type coconut is not one of [sms, email, letter]", "error": "ValidationError", } ], diff --git a/tests/app/v2/templates/test_templates_schemas.py b/tests/app/v2/templates/test_templates_schemas.py index dd2681e38..f133b5760 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -278,7 +278,7 @@ def test_get_all_template_request_schema_against_invalid_args_is_invalid(templat assert errors["status_code"] == 400 assert len(errors["errors"]) == 1 - assert errors["errors"][0]["message"] == "type unknown is not one of [sms, email]" + assert errors["errors"][0]["message"] == "type unknown is not one of [sms, email, letter]" @pytest.mark.parametrize("response", valid_json_get_all_response) From b5c2a8e2400330114716beaed914bbfacaef8d4c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 8 Feb 2024 13:14:19 -0500 Subject: [PATCH 182/259] Fixin' bugs. Signed-off-by: Cliff Hill --- app/enums.py | 7 ++++--- app/models.py | 4 +++- tests/app/v2/templates/test_get_templates.py | 6 +++--- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/enums.py b/app/enums.py index e2c0966ed..a09325fb7 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,6 +1,7 @@ -from strenum import StrEnum # In 3.11 this is in the enum library. We will not need - -# this external library any more. +from strenum import StrEnum # type: ignore [import-not-found] +# In 3.11 this is in the enum library. We will not need this external library any more. +# The line will simply change from importing from strenum to importing from enum. +# And the strenum library can then be removed from poetry. class TemplateType(StrEnum): diff --git a/app/models.py b/app/models.py index b4b2b36fb..7d8cb9e53 100644 --- a/app/models.py +++ b/app/models.py @@ -1164,8 +1164,10 @@ class TemplateBase(db.Model): def _as_utils_template(self): if self.template_type == TemplateType.EMAIL: return PlainTextEmailTemplate(self.__dict__) - if self.template_type == TemplateType.SMS: + elif self.template_type == TemplateType.SMS: return SMSMessageTemplate(self.__dict__) + else: + raise ValueError(f"{self.template_type} is an invalid template type.") def _as_utils_template_with_personalisation(self, values): template = self._as_utils_template() diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index 328633050..396d663fd 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -16,7 +16,7 @@ def test_get_all_templates_returns_200(client, sample_service): subject=f"subject_{name}" if tmp_type == TemplateType.EMAIL else "", template_name=name, ) - for name, tmp_type in product(("A", "B", "C"), TemplateType) + for name, tmp_type in (("A", TemplateType.SMS), ("B", TemplateType.EMAIL)) ] auth_header = create_service_authorization_header(service_id=sample_service.id) @@ -41,7 +41,7 @@ def test_get_all_templates_returns_200(client, sample_service): assert template["subject"] == templates[index].subject -@pytest.mark.parametrize("tmp_type", TemplateType) +@pytest.mark.parametrize("tmp_type", (TemplateType.SMS, TemplateType.EMAIL)) def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tmp_type): templates = [ create_template( @@ -75,7 +75,7 @@ def test_get_all_templates_for_valid_type_returns_200(client, sample_service, tm assert template["subject"] == templates[index].subject -@pytest.mark.parametrize("tmp_type", TemplateType) +@pytest.mark.parametrize("tmp_type", (TemplateType.SMS, TemplateType.EMAIL)) def test_get_correct_num_templates_for_valid_type_returns_200( client, sample_service, tmp_type ): From cd081ef163c9c1243009017e43639709a01785c5 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 8 Feb 2024 17:15:03 -0500 Subject: [PATCH 183/259] Down to 11 errors left to fix for tests. Signed-off-by: Cliff Hill --- app/enums.py | 1 + app/service/statistics.py | 5 ++-- tests/app/service/test_rest.py | 4 ++- tests/app/service/test_statistics_rest.py | 4 ++- tests/app/user/test_rest.py | 6 +---- .../notifications/test_get_notifications.py | 26 +++++++++++++------ tests/app/v2/template/test_post_template.py | 4 +-- .../v2/templates/test_templates_schemas.py | 5 +++- 8 files changed, 35 insertions(+), 20 deletions(-) diff --git a/app/enums.py b/app/enums.py index a09325fb7..a5090463f 100644 --- a/app/enums.py +++ b/app/enums.py @@ -1,4 +1,5 @@ from strenum import StrEnum # type: ignore [import-not-found] + # In 3.11 this is in the enum library. We will not need this external library any more. # The line will simply change from importing from strenum to importing from enum. # And the strenum library can then be removed from poetry. diff --git a/app/service/statistics.py b/app/service/statistics.py index ef538b45f..e781854bd 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -78,7 +78,7 @@ def format_monthly_template_notification_stats(year, rows): def create_zeroed_stats_dicts(): return { template_type: {status: 0 for status in ("requested", "delivered", "failed")} - for template_type in TemplateType + for template_type in (TemplateType.SMS, TemplateType.EMAIL) } @@ -103,7 +103,8 @@ def create_empty_monthly_notification_status_stats_dict(year): # nested dicts - data[month][template type][status] = count return { start.strftime("%Y-%m"): { - template_type: defaultdict(int) for template_type in TemplateType + template_type: defaultdict(int) + for template_type in (TemplateType.SMS, TemplateType.EMAIL) } for start in utc_month_starts } diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index b6cc6fd04..189cc2f4b 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1212,7 +1212,9 @@ def test_default_permissions_are_added_for_user_service( ] from app.dao.permissions_dao import default_service_permissions - assert sorted(ServicePermissionType.defaults()) == sorted(service_permissions) + assert sorted(ServicePermissionType.defaults()) == sorted( + service_permissions + ) def test_add_existing_user_to_another_service_with_all_permissions( diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index f0d8eda4a..5e88702a6 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -108,7 +108,9 @@ def test_get_template_usage_by_month_returns_two_templates( def test_get_service_notification_statistics( admin_request, sample_service, sample_template, today_only, stats ): - create_ft_notification_status(date(2000, 1, 1), "sms", sample_service, count=1) + create_ft_notification_status( + date(2000, 1, 1), NotificationType.SMS, sample_service, count=1 + ) with freeze_time("2000-01-02T12:00:00"): create_notification(sample_template, status="created") resp = admin_request.get( diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index 3f4f22d67..c24b950d8 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -457,11 +457,7 @@ def test_set_user_permissions(admin_request, sample_user, sample_service): "user.set_permissions", user_id=str(sample_user.id), service_id=str(sample_service.id), - _data={ - "permissions": [ - {"permission": PermissionType.PermissionType.MANAGE_SETTINGS} - ] - }, + _data={"permissions": [{"permission": PermissionType.MANAGE_SETTINGS}]}, _expected_status=204, ) diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index 82b589e4c..a68ed3ac7 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -1,6 +1,7 @@ import pytest from flask import json, url_for +from app.enums import NotificationType, TemplateType from app.utils import DATETIME_FORMAT from tests import create_service_authorization_header from tests.app.db import create_notification, create_template @@ -239,7 +240,7 @@ def test_get_notification_by_id_invalid_id(client, sample_notification, id): } -@pytest.mark.parametrize("template_type", ["sms", "email"]) +@pytest.mark.parametrize("template_type", [TemplateType.SMS, TemplateType.EMAIL]) def test_get_notification_doesnt_have_delivery_estimate_for_non_letters( client, sample_service, template_type ): @@ -290,7 +291,7 @@ def test_get_all_notifications_except_job_notifications_returns_200( "version": 1, } assert json_response["notifications"][0]["phone_number"] == "1" - assert json_response["notifications"][0]["type"] == "sms" + assert json_response["notifications"][0]["type"] == NotificationType.SMS assert not json_response["notifications"][0]["scheduled_for"] @@ -348,8 +349,12 @@ def test_get_all_notifications_no_notifications_if_no_notifications( def test_get_all_notifications_filter_by_template_type(client, sample_service): - email_template = create_template(service=sample_service, template_type="email") - sms_template = create_template(service=sample_service, template_type="sms") + email_template = create_template( + service=sample_service, template_type=TemplateType.EMAIL + ) + sms_template = create_template( + service=sample_service, template_type=TemplateType.SMS + ) notification = create_notification( template=email_template, to_field="don.draper@scdp.biz" @@ -382,7 +387,7 @@ def test_get_all_notifications_filter_by_template_type(client, sample_service): "version": 1, } assert json_response["notifications"][0]["email_address"] == "1" - assert json_response["notifications"][0]["type"] == "email" + assert json_response["notifications"][0]["type"] == NotificationType.EMAIL def test_get_all_notifications_filter_by_template_type_invalid_template_type( @@ -405,7 +410,7 @@ def test_get_all_notifications_filter_by_template_type_invalid_template_type( assert len(json_response["errors"]) == 1 assert ( json_response["errors"][0]["message"] - == "template_type orange is not one of [sms, email]" + == "template_type orange is not one of [sms, email, letter]" ) @@ -625,7 +630,9 @@ def test_get_all_notifications_filter_multiple_query_parameters( # wrong status create_notification(template=sample_email_template) - wrong_template = create_template(sample_email_template.service, template_type="sms") + wrong_template = create_template( + sample_email_template.service, template_type=TemplateType.SMS + ) # wrong template create_notification(template=wrong_template, status="sending") @@ -681,7 +688,10 @@ def test_get_all_notifications_renames_letter_statuses( assert response.status_code == 200 for noti in json_response["notifications"]: - if noti["type"] == "sms" or noti["type"] == "email": + if ( + noti["type"] == NotificationType.SMS + or noti["type"] == NotificationType.EMAIL + ): assert noti["status"] == "created" else: pytest.fail() diff --git a/tests/app/v2/template/test_post_template.py b/tests/app/v2/template/test_post_template.py index 57c9bb868..00d3aabfe 100644 --- a/tests/app/v2/template/test_post_template.py +++ b/tests/app/v2/template/test_post_template.py @@ -59,7 +59,7 @@ valid_post = [ ] -@pytest.mark.parametrize("tmp_type", TemplateType) +@pytest.mark.parametrize("tmp_type", (TemplateType.SMS, TemplateType.EMAIL)) @pytest.mark.parametrize( "subject,content,post_data,expected_subject,expected_content,expected_html", valid_post, @@ -125,7 +125,7 @@ def test_email_templates_not_rendered_into_content(client, sample_service): assert resp_json["body"] == template.content -@pytest.mark.parametrize("tmp_type", TemplateType) +@pytest.mark.parametrize("tmp_type", (TemplateType.SMS, TemplateType.EMAIL)) def test_invalid_post_template_returns_400(client, sample_service, tmp_type): template = create_template( sample_service, diff --git a/tests/app/v2/templates/test_templates_schemas.py b/tests/app/v2/templates/test_templates_schemas.py index f133b5760..08236d8b1 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -278,7 +278,10 @@ def test_get_all_template_request_schema_against_invalid_args_is_invalid(templat assert errors["status_code"] == 400 assert len(errors["errors"]) == 1 - assert errors["errors"][0]["message"] == "type unknown is not one of [sms, email, letter]" + assert ( + errors["errors"][0]["message"] + == "type unknown is not one of [sms, email, letter]" + ) @pytest.mark.parametrize("response", valid_json_get_all_response) From dcf788536523aef80a313afe6609a197d15549b9 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 9 Feb 2024 11:42:53 -0500 Subject: [PATCH 184/259] Only 6 fails left to go. Signed-off-by: Cliff Hill --- app/service/statistics.py | 2 +- tests/app/service/test_rest.py | 8 ++++---- tests/app/service/test_service_data_retention_rest.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/service/statistics.py b/app/service/statistics.py index e781854bd..bf230f164 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -40,7 +40,7 @@ def format_admin_stats(statistics): def create_stats_dict(): stats_dict = {} - for template in TemplateType: + for template in (TemplateType.SMS, TemplateType.EMAIL): stats_dict[template] = {} for status in ("total", "test-key"): diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 189cc2f4b..d344e6aae 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -18,6 +18,7 @@ from app.enums import ( KeyType, NotificationType, OrganizationType, + PermissionType, ServicePermissionType, TemplateType, ) @@ -922,9 +923,9 @@ def test_update_permissions_will_override_permission_flags( result = resp.json assert resp.status_code == 200 - assert set(result["data"]["permissions"]) == [ + assert set(result["data"]["permissions"]) == { ServicePermissionType.INTERNATIONAL_SMS - ] + } def test_update_service_permissions_will_add_service_permissions( @@ -1210,9 +1211,8 @@ def test_default_permissions_are_added_for_user_service( service_permissions = json_resp["data"]["permissions"][ str(sample_service.id) ] - from app.dao.permissions_dao import default_service_permissions - assert sorted(ServicePermissionType.defaults()) == sorted( + assert sorted(i.value for i in PermissionType.defaults()) == sorted( service_permissions ) diff --git a/tests/app/service/test_service_data_retention_rest.py b/tests/app/service/test_service_data_retention_rest.py index f651c253a..4defec5c0 100644 --- a/tests/app/service/test_service_data_retention_rest.py +++ b/tests/app/service/test_service_data_retention_rest.py @@ -23,8 +23,8 @@ def test_get_service_data_retention(client, sample_service): assert response.status_code == 200 json_response = json.loads(response.get_data(as_text=True)) assert len(json_response) == 2 - assert json_response[0] == email_data_retention.serialize() - assert json_response[1] == sms_data_retention.serialize() + assert json_response[0] == sms_data_retention.serialize() + assert json_response[1] == email_data_retention.serialize() def test_get_service_data_retention_returns_empty_list(client, sample_service): From 9cc3f1c1ffda31adde9706a632fb77ba07fd53ee Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 9 Feb 2024 12:24:09 -0500 Subject: [PATCH 185/259] Cleanup. Signed-off-by: Cliff Hill --- tests/app/billing/test_rest.py | 15 ++-- tests/app/celery/test_nightly_tasks.py | 29 ++++---- tests/app/celery/test_reporting_tasks.py | 90 ++++++++++++------------ tests/app/celery/test_scheduled_tasks.py | 34 ++++----- tests/app/conftest.py | 11 +-- tests/app/db.py | 2 +- tests/app/test_commands.py | 2 +- tests/app/test_model.py | 21 +++--- 8 files changed, 104 insertions(+), 100 deletions(-) diff --git a/tests/app/billing/test_rest.py b/tests/app/billing/test_rest.py index dd2781f90..5d503b019 100644 --- a/tests/app/billing/test_rest.py +++ b/tests/app/billing/test_rest.py @@ -6,6 +6,7 @@ from freezegun import freeze_time from app.billing.rest import update_free_sms_fragment_limit_data from app.dao.annual_billing_dao import dao_get_free_sms_fragment_limit_for_year from app.dao.date_util import get_current_calendar_year_start_year +from app.enums import NotificationType, TemplateType from tests.app.db import ( create_annual_billing, create_ft_billing, @@ -153,7 +154,7 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se service_id=service.id, free_sms_fragment_limit=1, financial_year_start=2016 ) - sms_template = create_template(service=service, template_type="sms") + sms_template = create_template(service=service, template_type=TemplateType.SMS) email_template = create_template(service=service, template_type="email") for dt in (date(2016, 1, 28), date(2016, 8, 10), date(2016, 12, 26)): @@ -173,10 +174,10 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se email_rows = [row for row in json_response if row["notification_type"] == "email"] assert len(email_rows) == 0 - sms_row = next(x for x in json_response if x["notification_type"] == "sms") + sms_row = next(x for x in json_response if x["notification_type"] == NotificationType.SMS) assert sms_row["month"] == "January" - assert sms_row["notification_type"] == "sms" + assert sms_row["notification_type"] == NotificationType.SMS assert sms_row["chargeable_units"] == 1 assert sms_row["notifications_sent"] == 1 assert sms_row["rate"] == 0.0162 @@ -216,8 +217,8 @@ def test_get_yearly_billing_usage_summary_from_ft_billing( service_id=service.id, free_sms_fragment_limit=1, financial_year_start=2016 ) - sms_template = create_template(service=service, template_type="sms") - email_template = create_template(service=service, template_type="email") + sms_template = create_template(service=service, template_type=TemplateType.SMS) + email_template = create_template(service=service, template_type=TemplateType.EMAIL) for dt in (date(2016, 1, 28), date(2016, 8, 10), date(2016, 12, 26)): create_ft_billing(local_date=dt, template=sms_template, rate=0.0162) @@ -233,7 +234,7 @@ def test_get_yearly_billing_usage_summary_from_ft_billing( assert len(json_response) == 2 - assert json_response[0]["notification_type"] == "email" + assert json_response[0]["notification_type"] == NotificationType.EMAIL assert json_response[0]["chargeable_units"] == 0 assert json_response[0]["notifications_sent"] == 3 assert json_response[0]["rate"] == 0 @@ -241,7 +242,7 @@ def test_get_yearly_billing_usage_summary_from_ft_billing( assert json_response[0]["free_allowance_used"] == 0 assert json_response[0]["charged_units"] == 0 - assert json_response[1]["notification_type"] == "sms" + assert json_response[1]["notification_type"] == NotificationType.SMS assert json_response[1]["chargeable_units"] == 3 assert json_response[1]["notifications_sent"] == 3 assert json_response[1]["rate"] == 0.0162 diff --git a/tests/app/celery/test_nightly_tasks.py b/tests/app/celery/test_nightly_tasks.py index 83d74660c..f4eddc58b 100644 --- a/tests/app/celery/test_nightly_tasks.py +++ b/tests/app/celery/test_nightly_tasks.py @@ -17,7 +17,8 @@ from app.celery.nightly_tasks import ( save_daily_notification_processing_time, timeout_notifications, ) -from app.models import FactProcessingTime, Job, NotificationType +from app.enums import NotificationType, TemplateType +from app.models import FactProcessingTime, Job from tests.app.db import ( create_job, create_notification, @@ -142,7 +143,7 @@ def test_delete_sms_notifications_older_than_retention_calls_child_task( "app.celery.nightly_tasks._delete_notifications_older_than_retention_by_type" ) delete_sms_notifications_older_than_retention() - mocked.assert_called_once_with("sms") + mocked.assert_called_once_with(NotificationType.SMS) def test_delete_email_notifications_older_than_retentions_calls_child_task( @@ -276,21 +277,21 @@ def test_delete_notifications_task_calls_task_for_services_with_data_retention_o email_service = create_service(service_name="b") letter_service = create_service(service_name="c") - create_service_data_retention(sms_service, notification_type="sms") - create_service_data_retention(email_service, notification_type="email") - create_service_data_retention(letter_service, notification_type="letter") + create_service_data_retention(sms_service, notification_type=NotificationType.SMS) + create_service_data_retention(email_service, notification_type=NotificationType.EMAIL) + create_service_data_retention(letter_service, notification_type=NotificationType.LETTER) mock_subtask = mocker.patch( "app.celery.nightly_tasks.delete_notifications_for_service_and_type" ) - _delete_notifications_older_than_retention_by_type("sms") + _delete_notifications_older_than_retention_by_type(NotificationType.SMS) mock_subtask.apply_async.assert_called_once_with( queue="reporting-tasks", kwargs={ "service_id": sms_service.id, - "notification_type": "sms", + "notification_type": NotificationType.SMS, # three days of retention, its morn of 5th, so we want to keep all messages from 4th, 3rd and 2nd. "datetime_to_delete_before": date(2021, 6, 2), }, @@ -310,7 +311,7 @@ def test_delete_notifications_task_calls_task_for_services_with_data_retention_b "app.celery.nightly_tasks.delete_notifications_for_service_and_type" ) - _delete_notifications_older_than_retention_by_type("sms") + _delete_notifications_older_than_retention_by_type(NotificationType.SMS) assert mock_subtask.apply_async.call_count == 2 mock_subtask.apply_async.assert_has_calls( @@ -320,7 +321,7 @@ def test_delete_notifications_task_calls_task_for_services_with_data_retention_b queue=ANY, kwargs={ "service_id": service_14_days.id, - "notification_type": "sms", + "notification_type": NotificationType.SMS, "datetime_to_delete_before": date(2021, 3, 21), }, ), @@ -328,7 +329,7 @@ def test_delete_notifications_task_calls_task_for_services_with_data_retention_b queue=ANY, kwargs={ "service_id": service_3_days.id, - "notification_type": "sms", + "notification_type": NotificationType.SMS, "datetime_to_delete_before": date(2021, 4, 1), }, ), @@ -347,7 +348,7 @@ def test_delete_notifications_task_calls_task_for_services_that_have_sent_notifi create_template(service_will_delete_1) create_template(service_will_delete_2) nothing_to_delete_sms_template = create_template( - service_nothing_to_delete, template_type="sms" + service_nothing_to_delete, template_type=TemplateType.SMS ) nothing_to_delete_email_template = create_template( service_nothing_to_delete, template_type="email" @@ -377,7 +378,7 @@ def test_delete_notifications_task_calls_task_for_services_that_have_sent_notifi "app.celery.nightly_tasks.delete_notifications_for_service_and_type" ) - _delete_notifications_older_than_retention_by_type("sms") + _delete_notifications_older_than_retention_by_type(NotificationType.SMS) assert mock_subtask.apply_async.call_count == 2 mock_subtask.apply_async.assert_has_calls( @@ -387,7 +388,7 @@ def test_delete_notifications_task_calls_task_for_services_that_have_sent_notifi queue=ANY, kwargs={ "service_id": service_will_delete_1.id, - "notification_type": "sms", + "notification_type": NotificationType.SMS, "datetime_to_delete_before": date(2021, 3, 26), }, ), @@ -395,7 +396,7 @@ def test_delete_notifications_task_calls_task_for_services_that_have_sent_notifi queue=ANY, kwargs={ "service_id": service_will_delete_2.id, - "notification_type": "sms", + "notification_type": NotificationType.SMS, "datetime_to_delete_before": date(2021, 3, 26), }, ), diff --git a/tests/app/celery/test_reporting_tasks.py b/tests/app/celery/test_reporting_tasks.py index 7045ce1a7..cec90c473 100644 --- a/tests/app/celery/test_reporting_tasks.py +++ b/tests/app/celery/test_reporting_tasks.py @@ -13,7 +13,7 @@ from app.celery.reporting_tasks import ( ) from app.config import QueueNames from app.dao.fact_billing_dao import get_rate -from app.enums import KeyType, NotificationType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType from app.models import FactBilling, FactNotificationStatus, Notification from tests.app.db import ( create_notification, @@ -122,13 +122,13 @@ def test_create_nightly_billing_for_day_checks_history( create_notification( created_at=yesterday, template=sample_template, - status="sending", + status=NotificationStatus.SENDING, ) create_notification_history( created_at=yesterday, template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, ) records = FactBilling.query.all() @@ -164,7 +164,7 @@ def test_create_nightly_billing_for_day_sms_rate_multiplier( create_notification( created_at=yesterday, template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by="sns", international=False, rate_multiplier=1.0, @@ -173,7 +173,7 @@ def test_create_nightly_billing_for_day_sms_rate_multiplier( create_notification( created_at=yesterday, template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by="sns", international=False, rate_multiplier=second_rate, @@ -204,7 +204,7 @@ def test_create_nightly_billing_for_day_different_templates( create_notification( created_at=yesterday, template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by="sns", international=False, rate_multiplier=1.0, @@ -213,7 +213,7 @@ def test_create_nightly_billing_for_day_different_templates( create_notification( created_at=yesterday, template=sample_email_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by="sns", international=False, rate_multiplier=0, @@ -248,7 +248,7 @@ def test_create_nightly_billing_for_day_same_sent_by( create_notification( created_at=yesterday, template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by="sns", international=False, rate_multiplier=1.0, @@ -257,7 +257,7 @@ def test_create_nightly_billing_for_day_same_sent_by( create_notification( created_at=yesterday, template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by="sns", international=False, rate_multiplier=1.0, @@ -288,7 +288,7 @@ def test_create_nightly_billing_for_day_null_sent_by_sms( create_notification( created_at=yesterday, template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by=None, international=False, rate_multiplier=1.0, @@ -334,7 +334,7 @@ def test_create_nightly_billing_for_day_use_BST( create_notification( created_at=datetime(2018, 3, 26, 4, 1), template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, rate_multiplier=1.0, billable_units=1, ) @@ -342,7 +342,7 @@ def test_create_nightly_billing_for_day_use_BST( create_notification( created_at=datetime(2018, 3, 25, 23, 59), template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, rate_multiplier=1.0, billable_units=2, ) @@ -351,7 +351,7 @@ def test_create_nightly_billing_for_day_use_BST( create_notification( created_at=datetime(2018, 3, 24, 23, 59), template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, rate_multiplier=1.0, billable_units=4, ) @@ -376,7 +376,7 @@ def test_create_nightly_billing_for_day_update_when_record_exists( create_notification( created_at=datetime.now() - timedelta(days=1), template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by=None, international=False, rate_multiplier=1.0, @@ -397,7 +397,7 @@ def test_create_nightly_billing_for_day_update_when_record_exists( create_notification( created_at=datetime.now() - timedelta(days=1), template=sample_template, - status="delivered", + status=NotificationStatus.DELIVERED, sent_by=None, international=False, rate_multiplier=1.0, @@ -415,25 +415,25 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio first_service = create_service(service_name="First Service") first_template = create_template(service=first_service) second_service = create_service(service_name="second Service") - second_template = create_template(service=second_service, template_type="email") + second_template = create_template(service=second_service, template_type=TemplateType.EMAIL,) process_day = datetime.utcnow().date() - timedelta(days=5) with freeze_time(datetime.combine(process_day, time.max)): - create_notification(template=first_template, status="delivered") - create_notification(template=second_template, status="failed") + create_notification(template=first_template, status=NotificationStatus.DELIVERED,) + create_notification(template=second_template, status=NotificationStatus.FAILED) # team API key notifications are included create_notification( - template=second_template, status="sending", key_type=KeyType.TEAM + template=second_template, status=NotificationStatus.SENDING, key_type=KeyType.TEAM, ) # test notifications are ignored create_notification( - template=second_template, status="sending", key_type=KeyType.TEST + template=second_template, status=NotificationStatus.SENDING, key_type=KeyType.TEST, ) # historical notifications are included - create_notification_history(template=second_template, status="delivered") + create_notification_history(template=second_template, status=NotificationStatus.DELIVERED,) # these created notifications from a different day get ignored with freeze_time( @@ -445,10 +445,10 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert len(FactNotificationStatus.query.all()) == 0 create_nightly_notification_status_for_service_and_day( - str(process_day), first_service.id, "sms" + str(process_day), first_service.id, NotificationType.SMS, ) create_nightly_notification_status_for_service_and_day( - str(process_day), second_service.id, "email" + str(process_day), second_service.id, NotificationType.EMAIL, ) new_fact_data = FactNotificationStatus.query.order_by( @@ -461,16 +461,16 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio email_delivered_row = new_fact_data[0] assert email_delivered_row.template_id == second_template.id assert email_delivered_row.service_id == second_service.id - assert email_delivered_row.notification_type == "email" - assert email_delivered_row.notification_status == "delivered" + assert email_delivered_row.notification_type == NotificationType.EMAIL + assert email_delivered_row.notification_status == NotificationStatus.DELIVERED assert email_delivered_row.notification_count == 1 assert email_delivered_row.key_type == KeyType.NORMAL email_sending_row = new_fact_data[1] assert email_sending_row.template_id == second_template.id assert email_sending_row.service_id == second_service.id - assert email_sending_row.notification_type == "email" - assert email_sending_row.notification_status == "failed" + assert email_sending_row.notification_type == NotificationType.EMAIL + assert email_sending_row.notification_status == NotificationStatus.FAILED assert email_sending_row.notification_count == 1 assert email_sending_row.key_type == KeyType.NORMAL @@ -479,16 +479,16 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert email_failure_row.template_id == second_template.id assert email_failure_row.service_id == second_service.id assert email_failure_row.job_id == UUID("00000000-0000-0000-0000-000000000000") - assert email_failure_row.notification_type == "email" - assert email_failure_row.notification_status == "sending" + assert email_failure_row.notification_type == NotificationType.EMAIL + assert email_failure_row.notification_status == NotificationStatus.SENDING assert email_failure_row.notification_count == 1 assert email_failure_row.key_type == KeyType.TEAM sms_delivered_row = new_fact_data[3] assert sms_delivered_row.template_id == first_template.id assert sms_delivered_row.service_id == first_service.id - assert sms_delivered_row.notification_type == "sms" - assert sms_delivered_row.notification_status == "delivered" + assert sms_delivered_row.notification_type == NotificationType.SMS + assert sms_delivered_row.notification_status == NotificationStatus.DELIVERED assert sms_delivered_row.notification_count == 1 assert sms_delivered_row.key_type == KeyType.NORMAL @@ -501,22 +501,22 @@ def test_create_nightly_notification_status_for_service_and_day_overwrites_old_d process_day = datetime.utcnow().date() # first run: one notification, expect one row (just one status) - notification = create_notification(template=first_template, status="sending") + notification = create_notification(template=first_template, status=NotificationStatus.SENDING) create_nightly_notification_status_for_service_and_day( - str(process_day), first_service.id, "sms" + str(process_day), first_service.id, NotificationType.SMS, ) new_fact_data = FactNotificationStatus.query.all() assert len(new_fact_data) == 1 assert new_fact_data[0].notification_count == 1 - assert new_fact_data[0].notification_status == "sending" + assert new_fact_data[0].notification_status == NotificationStatus.SENDING # second run: status changed, still expect one row (one status) - notification.status = "delivered" - create_notification(template=first_template, status="created") + notification.status = NotificationStatus.DELIVERED + create_notification(template=first_template, status=NotificationStatus.CREATED) create_nightly_notification_status_for_service_and_day( - str(process_day), first_service.id, "sms" + str(process_day), first_service.id, NotificationType.SMS, ) updated_fact_data = FactNotificationStatus.query.order_by( @@ -525,9 +525,9 @@ def test_create_nightly_notification_status_for_service_and_day_overwrites_old_d assert len(updated_fact_data) == 2 assert updated_fact_data[0].notification_count == 1 - assert updated_fact_data[0].notification_status == "created" + assert updated_fact_data[0].notification_status == NotificationStatus.CREATED assert updated_fact_data[1].notification_count == 1 - assert updated_fact_data[1].notification_status == "delivered" + assert updated_fact_data[1].notification_status == NotificationStatus.DELIVERED # the job runs at 04:30am EST time. @@ -536,22 +536,22 @@ def test_create_nightly_notification_status_for_service_and_day_respects_bst( sample_template, ): create_notification( - sample_template, status="delivered", created_at=datetime(2019, 4, 2, 5, 0) + sample_template, status=NotificationStatus.DELIVERED, created_at=datetime(2019, 4, 2, 5, 0), ) # too new create_notification( - sample_template, status="created", created_at=datetime(2019, 4, 2, 5, 59) + sample_template, status=NotificationStatus.CREATED, created_at=datetime(2019, 4, 2, 5, 59), ) create_notification( - sample_template, status="created", created_at=datetime(2019, 4, 1, 4, 0) + sample_template, status=NotificationStatus.CREATED, created_at=datetime(2019, 4, 1, 4, 0), ) create_notification( - sample_template, status="delivered", created_at=datetime(2019, 3, 21, 17, 59) + sample_template, status=NotificationStatus.DELIVERED, created_at=datetime(2019, 3, 21, 17, 59), ) # too old create_nightly_notification_status_for_service_and_day( - "2019-04-01", sample_template.service_id, "sms" + "2019-04-01", sample_template.service_id, NotificationType.SMS, ) noti_status = FactNotificationStatus.query.order_by( @@ -560,4 +560,4 @@ def test_create_nightly_notification_status_for_service_and_day_respects_bst( assert len(noti_status) == 1 assert noti_status[0].local_date == date(2019, 4, 1) - assert noti_status[0].notification_status == "created" + assert noti_status[0].notification_status == NotificationStatus.CREATED diff --git a/tests/app/celery/test_scheduled_tasks.py b/tests/app/celery/test_scheduled_tasks.py index e140c58cf..0472e9fdb 100644 --- a/tests/app/celery/test_scheduled_tasks.py +++ b/tests/app/celery/test_scheduled_tasks.py @@ -19,7 +19,7 @@ from app.celery.scheduled_tasks import ( ) from app.config import QueueNames, Test from app.dao.jobs_dao import dao_get_job_by_id -from app.enums import JobStatus +from app.enums import JobStatus, NotificationStatus, TemplateType from tests.app import load_example_csv from tests.app.db import create_job, create_notification, create_template @@ -101,13 +101,13 @@ def test_should_update_scheduled_jobs_and_put_on_queue(mocker, sample_template): one_minute_in_the_past = datetime.utcnow() - timedelta(minutes=1) job = create_job( - sample_template, job_status="scheduled", scheduled_for=one_minute_in_the_past + sample_template, job_status=JobStatus.SCHEDULED, scheduled_for=one_minute_in_the_past ) run_scheduled_jobs() updated_job = dao_get_job_by_id(job.id) - assert updated_job.job_status == "pending" + assert updated_job.job_status == JobStatus.PENDING mocked.assert_called_with([str(job.id)], queue="job-tasks") @@ -118,22 +118,22 @@ def test_should_update_all_scheduled_jobs_and_put_on_queue(sample_template, mock ten_minutes_in_the_past = datetime.utcnow() - timedelta(minutes=10) twenty_minutes_in_the_past = datetime.utcnow() - timedelta(minutes=20) job_1 = create_job( - sample_template, job_status="scheduled", scheduled_for=one_minute_in_the_past + sample_template, job_status=JobStatus.SCHEDULED, scheduled_for=one_minute_in_the_past, ) job_2 = create_job( - sample_template, job_status="scheduled", scheduled_for=ten_minutes_in_the_past + sample_template, job_status=JobStatus.SCHEDULED, scheduled_for=ten_minutes_in_the_past, ) job_3 = create_job( sample_template, - job_status="scheduled", + job_status=JobStatus.SCHEDULED, scheduled_for=twenty_minutes_in_the_past, ) run_scheduled_jobs() - assert dao_get_job_by_id(job_1.id).job_status == "pending" - assert dao_get_job_by_id(job_2.id).job_status == "pending" - assert dao_get_job_by_id(job_2.id).job_status == "pending" + assert dao_get_job_by_id(job_1.id).job_status == JobStatus.PENDING + assert dao_get_job_by_id(job_2.id).job_status == JobStatus.PENDING + assert dao_get_job_by_id(job_2.id).job_status == JobStatus.PENDING mocked.assert_has_calls( [ @@ -299,36 +299,36 @@ def test_replay_created_notifications(notify_db_session, sample_service, mocker) "app.celery.provider_tasks.deliver_sms.apply_async" ) - sms_template = create_template(service=sample_service, template_type="sms") - email_template = create_template(service=sample_service, template_type="email") + sms_template = create_template(service=sample_service, template_type=TemplateType.SMS) + email_template = create_template(service=sample_service, template_type=TemplateType.EMAIL) older_than = (60 * 60) + (60 * 15) # 1 hour 15 minutes # notifications expected to be resent old_sms = create_notification( template=sms_template, created_at=datetime.utcnow() - timedelta(seconds=older_than), - status="created", + status=NotificationStatus.CREATED, ) old_email = create_notification( template=email_template, created_at=datetime.utcnow() - timedelta(seconds=older_than), - status="created", + status=NotificationStatus.CREATED, ) # notifications that are not to be resent create_notification( template=sms_template, created_at=datetime.utcnow() - timedelta(seconds=older_than), - status="sending", + status=NotificationStatus.SENDING, ) create_notification( template=email_template, created_at=datetime.utcnow() - timedelta(seconds=older_than), - status="delivered", + status=NotificationStatus.DELIVERED, ) create_notification( - template=sms_template, created_at=datetime.utcnow(), status="created" + template=sms_template, created_at=datetime.utcnow(), status=NotificationStatus.CREATED, ) create_notification( - template=email_template, created_at=datetime.utcnow(), status="created" + template=email_template, created_at=datetime.utcnow(), status=NotificationStatus.CREATED, ) replay_created_notifications() diff --git a/tests/app/conftest.py b/tests/app/conftest.py index a47bf23b1..2f550c1d4 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -1,4 +1,5 @@ import json +from types import CodeType import uuid from datetime import datetime, timedelta @@ -170,7 +171,7 @@ def service_factory(sample_user): create_template( service, template_name="Template Name", - template_type="sms", + template_type=TemplateType.SMS, ) return service @@ -203,7 +204,7 @@ def create_code(notify_db_session, code_type): @pytest.fixture(scope="function") def sample_sms_code(notify_db_session): - code, txt_code = create_code(notify_db_session, code_type="sms") + code, txt_code = create_code(notify_db_session, code_type=CodeType.SMS) code.txt_code = txt_code return code @@ -252,7 +253,7 @@ def sample_template(sample_user): data = { "name": "Template Name", - "template_type": "sms", + "template_type": TemplateType.SMS, "content": "This is a template:\nwith a newline", "service": service, "created_by": sample_user, @@ -621,7 +622,7 @@ def sms_code_template(notify_service): user=notify_service.users[0], template_config_name="SMS_CODE_TEMPLATE_ID", content="((verify_code))", - template_type="sms", + template_type=TemplateType.SMS, ) @@ -723,7 +724,7 @@ def team_member_mobile_edit_template(notify_service): user=notify_service.users[0], template_config_name="TEAM_MEMBER_EDIT_MOBILE_TEMPLATE_ID", content="Your mobile number was changed by ((servicemanagername)).", - template_type="sms", + template_type=TemplateType.SMS, ) diff --git a/tests/app/db.py b/tests/app/db.py index 5fdc70b3c..9de87ba48 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -899,7 +899,7 @@ def set_up_usage_data(start_date): billing_reference="service billing reference", ) sms_template_1 = create_template( - service=service_1_sms_and_letter, template_type="sms" + service=service_1_sms_and_letter, template_type=TemplateType.SMS ) create_annual_billing( service_id=service_1_sms_and_letter.id, diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index 586faaee7..b147f3a0f 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -358,7 +358,7 @@ def test_update_template(notify_db_session, email_2fa_code_template): _update_template( "299726d2-dba6-42b8-8209-30e1d66ea164", "Example text message template!", - "sms", + TemplateType.SMS, [ "Hi, I’m trying out Notify.gov! Today is ((day of week)) and my favorite color is ((color))." ], diff --git a/tests/app/test_model.py b/tests/app/test_model.py index 549d95c9a..461056a26 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -10,6 +10,7 @@ from app.enums import ( AgreementType, AuthType, NotificationStatus, + NotificationType, RecipientType, TemplateType, ) @@ -119,8 +120,8 @@ def test_status_conversion(initial_statuses, expected_statuses): @pytest.mark.parametrize( "template_type, recipient", [ - ("sms", "+12028675309"), - ("email", "foo@bar.com"), + (TemplateType.SMS, "+12028675309"), + (TemplateType.EMAIL, "foo@bar.com"), ], ) def test_notification_for_csv_returns_correct_type( @@ -147,17 +148,17 @@ def test_notification_for_csv_returns_correct_job_row_number(sample_job): @pytest.mark.parametrize( "template_type, status, expected_status", [ - ("email", "failed", "Failed"), - ("email", "technical-failure", "Technical failure"), - ("email", "temporary-failure", "Inbox not accepting messages right now"), - ("email", "permanent-failure", "Email address doesn’t exist"), + (TemplateType.EMAIL, "failed", "Failed"), + (TemplateType.EMAIL, "technical-failure", "Technical failure"), + (TemplateType.EMAIL, "temporary-failure", "Inbox not accepting messages right now",), + (TemplateType.EMAIL, "permanent-failure", "Email address doesn’t exist"), ( - "sms", + TemplateType.SMS, "temporary-failure", "Unable to find carrier response -- still looking", ), - ("sms", "permanent-failure", "Unable to find carrier response."), - ("sms", "sent", "Sent internationally"), + (TemplateType.SMS, "permanent-failure", "Unable to find carrier response."), + (TemplateType.SMS, "sent", "Sent internationally"), ], ) def test_notification_for_csv_returns_formatted_status( @@ -419,7 +420,7 @@ def test_notification_history_from_original(sample_notification): def test_rate_str(): - rate = create_rate("2023-01-01 00:00:00", 1.5, "sms") + rate = create_rate("2023-01-01 00:00:00", 1.5, NotificationType.SMS) assert rate.__str__() == "1.5 sms 2023-01-01 00:00:00" From 262bd8943a7de0f6920d99eac3e85b90da368515 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 9 Feb 2024 13:46:44 -0500 Subject: [PATCH 186/259] More cleanup. Signed-off-by: Cliff Hill --- .../app/celery/test_service_callback_tasks.py | 13 +- tests/app/celery/test_tasks.py | 62 +++--- tests/app/dao/test_fact_billing_dao.py | 182 +++++++++--------- 3 files changed, 129 insertions(+), 128 deletions(-) diff --git a/tests/app/celery/test_service_callback_tasks.py b/tests/app/celery/test_service_callback_tasks.py index ff41a2eb5..5dd7f18ca 100644 --- a/tests/app/celery/test_service_callback_tasks.py +++ b/tests/app/celery/test_service_callback_tasks.py @@ -10,6 +10,7 @@ from app.celery.service_callback_tasks import ( send_complaint_to_service, send_delivery_status_to_service, ) +from app.enums import CallbackType, NotificationStatus, NotificationType from app.utils import DATETIME_FORMAT from tests.app.db import ( create_complaint, @@ -20,7 +21,7 @@ from tests.app.db import ( ) -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS]) def test_send_delivery_status_to_service_post_https_request_to_service_with_encrypted_data( notify_db_session, notification_type ): @@ -32,7 +33,7 @@ def test_send_delivery_status_to_service_post_https_request_to_service_with_encr created_at=datestr, updated_at=datestr, sent_at=datestr, - status="sent", + status=NotificationStatus.SENT, ) encrypted_status_update = _set_up_data_for_status_update(callback_api, notification) with requests_mock.Mocker() as request_mock: @@ -102,7 +103,7 @@ def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted ] == "Bearer {}".format(callback_api.bearer_token) -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS],) @pytest.mark.parametrize("status_code", [429, 500, 503]) def test__send_data_to_service_callback_api_retries_if_request_returns_error_code_with_encrypted_data( notify_db_session, mocker, notification_type, status_code @@ -114,7 +115,7 @@ def test__send_data_to_service_callback_api_retries_if_request_returns_error_cod created_at=datestr, updated_at=datestr, sent_at=datestr, - status="sent", + status=NotificationStatus.SENT, ) encrypted_data = _set_up_data_for_status_update(callback_api, notification) mocked = mocker.patch( @@ -130,7 +131,7 @@ def test__send_data_to_service_callback_api_retries_if_request_returns_error_cod assert mocked.call_args[1]["queue"] == "service-callbacks-retry" -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS],) def test__send_data_to_service_callback_api_does_not_retry_if_request_returns_404_with_encrypted_data( notify_db_session, mocker, notification_type ): @@ -159,7 +160,7 @@ def test__send_data_to_service_callback_api_does_not_retry_if_request_returns_40 def test_send_delivery_status_to_service_succeeds_if_sent_at_is_none( notify_db_session, mocker ): - callback_api, template = _set_up_test_data("email", "delivery_status") + callback_api, template = _set_up_test_data(NotificationType.EMAIL, CallbackType.DELIVERY_STATUS,) datestr = datetime(2017, 6, 20) notification = create_notification( template=template, diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index c615ee586..bda1837a3 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -194,7 +194,7 @@ def test_should_not_create_save_task_for_empty_file(sample_job, mocker): service_id=str(sample_job.service.id), job_id=str(sample_job.id) ) job = jobs_dao.dao_get_job_by_id(sample_job.id) - assert job.job_status == "finished" + assert job.job_status == JobStatus.FINISHED assert tasks.save_sms.apply_async.called is False @@ -420,13 +420,13 @@ def test_should_send_template_to_correct_sms_task_and_persist( persisted_notification.template_version == sample_template_with_placeholders.version ) - assert persisted_notification.status == "created" + assert persisted_notification.status == NotificationStatus.CREATED assert persisted_notification.created_at <= datetime.utcnow() assert not persisted_notification.sent_at assert not persisted_notification.sent_by assert not persisted_notification.job_id assert persisted_notification.personalisation == {"name": "Jo"} - assert persisted_notification.notification_type == "sms" + assert persisted_notification.notification_type == NotificationType.SMS mocked_deliver_sms.assert_called_once_with( [str(persisted_notification.id)], queue="send-sms-tasks" ) @@ -456,13 +456,13 @@ def test_should_save_sms_if_restricted_service_and_valid_number( assert persisted_notification.to == "1" assert persisted_notification.template_id == template.id assert persisted_notification.template_version == template.version - assert persisted_notification.status == "created" + assert persisted_notification.status == NotificationStatus.CREATED assert persisted_notification.created_at <= datetime.utcnow() assert not persisted_notification.sent_at assert not persisted_notification.sent_by assert not persisted_notification.job_id assert not persisted_notification.personalisation - assert persisted_notification.notification_type == "sms" + assert persisted_notification.notification_type == NotificationType.SMS provider_tasks.deliver_sms.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue="send-sms-tasks" ) @@ -475,7 +475,7 @@ def test_save_email_should_save_default_email_reply_to_text_on_notification( create_reply_to_email( service=service, email_address="reply_to@digital.fake.gov", is_default=True ) - template = create_template(service=service, template_type="email", subject="Hello") + template = create_template(service=service, template_type=TemplateType.EMAIL, subject="Hello",) notification = _notification_json(template, to="test@example.com") mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") @@ -536,7 +536,7 @@ def test_should_not_save_email_if_restricted_service_and_invalid_email_address( ): user = create_user() service = create_service(user=user, restricted=True) - template = create_template(service=service, template_type="email", subject="Hello") + template = create_template(service=service, template_type=TemplateType.EMAIL, subject="Hello",) notification = _notification_json(template, to="test@example.com") notification_id = uuid.uuid4() @@ -551,7 +551,7 @@ def test_should_not_save_email_if_restricted_service_and_invalid_email_address( def test_should_save_sms_template_to_and_persist_with_job_id(sample_job, mocker): notification = _notification_json( - sample_job.template, to="+447234123123", job_id=sample_job.id, row_number=2 + sample_job.template, to="+447234123123", job_id=sample_job.id, row_number=2, ) mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") @@ -566,14 +566,14 @@ def test_should_save_sms_template_to_and_persist_with_job_id(sample_job, mocker) assert persisted_notification.to == "1" assert persisted_notification.job_id == sample_job.id assert persisted_notification.template_id == sample_job.template.id - assert persisted_notification.status == "created" + assert persisted_notification.status == NotificationStatus.CREATED assert not persisted_notification.sent_at assert persisted_notification.created_at >= now assert not persisted_notification.sent_by assert persisted_notification.job_row_number == 2 assert persisted_notification.api_key_id is None assert persisted_notification.key_type == KeyType.NORMAL - assert persisted_notification.notification_type == "sms" + assert persisted_notification.notification_type == NotificationType.SMS provider_tasks.deliver_sms.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue="send-sms-tasks" @@ -638,13 +638,13 @@ def test_should_use_email_template_and_persist( ) assert persisted_notification.created_at >= now assert not persisted_notification.sent_at - assert persisted_notification.status == "created" + assert persisted_notification.status == NotificationStatus.CREATED assert not persisted_notification.sent_by assert persisted_notification.job_row_number == 1 assert persisted_notification.personalisation == {"name": "Jo"} assert persisted_notification.api_key_id is None assert persisted_notification.key_type == KeyType.NORMAL - assert persisted_notification.notification_type == "email" + assert persisted_notification.notification_type == NotificationType.EMAIL provider_tasks.deliver_email.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue="send-email-tasks" @@ -680,9 +680,9 @@ def test_save_email_should_use_template_version_from_job_not_latest( assert persisted_notification.template_version == version_on_notification assert persisted_notification.created_at >= now assert not persisted_notification.sent_at - assert persisted_notification.status == "created" + assert persisted_notification.status == NotificationStatus.CREATED assert not persisted_notification.sent_by - assert persisted_notification.notification_type == "email" + assert persisted_notification.notification_type == NotificationType.EMAIL provider_tasks.deliver_email.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue="send-email-tasks" ) @@ -708,12 +708,12 @@ def test_should_use_email_template_subject_placeholders( assert ( persisted_notification.template_id == sample_email_template_with_placeholders.id ) - assert persisted_notification.status == "created" + assert persisted_notification.status == NotificationStatus.CREATED assert persisted_notification.created_at >= now assert not persisted_notification.sent_by assert persisted_notification.personalisation == {"name": "Jo"} assert not persisted_notification.reference - assert persisted_notification.notification_type == "email" + assert persisted_notification.notification_type == NotificationType.EMAIL provider_tasks.deliver_email.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue="send-email-tasks" ) @@ -726,11 +726,11 @@ def test_save_email_uses_the_reply_to_text_when_provided(sample_email_template, service = sample_email_template.service notification_id = uuid.uuid4() service_email_reply_to_dao.add_reply_to_email_address_for_service( - service.id, "default@example.com", True + service.id, "default@example.com", True, ) other_email_reply_to = ( service_email_reply_to_dao.add_reply_to_email_address_for_service( - service.id, "other@example.com", False + service.id, "other@example.com", False, ) ) @@ -741,7 +741,7 @@ def test_save_email_uses_the_reply_to_text_when_provided(sample_email_template, sender_id=other_email_reply_to.id, ) persisted_notification = Notification.query.one() - assert persisted_notification.notification_type == "email" + assert persisted_notification.notification_type == NotificationType.EMAIL assert persisted_notification.reply_to_text == "other@example.com" @@ -754,7 +754,7 @@ def test_save_email_uses_the_default_reply_to_text_if_sender_id_is_none( service = sample_email_template.service notification_id = uuid.uuid4() service_email_reply_to_dao.add_reply_to_email_address_for_service( - service.id, "default@example.com", True + service.id, "default@example.com", True, ) save_email( @@ -764,7 +764,7 @@ def test_save_email_uses_the_default_reply_to_text_if_sender_id_is_none( sender_id=None, ) persisted_notification = Notification.query.one() - assert persisted_notification.notification_type == "email" + assert persisted_notification.notification_type == NotificationType.EMAIL assert persisted_notification.reply_to_text == "default@example.com" @@ -787,11 +787,11 @@ def test_should_use_email_template_and_persist_without_personalisation( assert persisted_notification.template_id == sample_email_template.id assert persisted_notification.created_at >= now assert not persisted_notification.sent_at - assert persisted_notification.status == "created" + assert persisted_notification.status == NotificationStatus.CREATED assert not persisted_notification.sent_by assert not persisted_notification.personalisation assert not persisted_notification.reference - assert persisted_notification.notification_type == "email" + assert persisted_notification.notification_type == NotificationType.EMAIL provider_tasks.deliver_email.apply_async.assert_called_once_with( [str(persisted_notification.id)], queue="send-email-tasks" ) @@ -925,7 +925,7 @@ def test_save_sms_uses_non_default_sms_sender_reply_to_text_if_provided( service = create_service_with_defined_sms_sender(sms_sender_value="2028675309") template = create_template(service=service) new_sender = service_sms_sender_dao.dao_add_sms_sender_for_service( - service.id, "new-sender", False + service.id, "new-sender", False, ) notification = _notification_json(template, to="202-867-5301") @@ -952,7 +952,7 @@ def test_should_cancel_job_if_service_is_inactive(sample_service, sample_job, mo process_job(sample_job.id) job = jobs_dao.dao_get_job_by_id(sample_job.id) - assert job.job_status == "cancelled" + assert job.job_status == JobStatus.CANCELLED s3.get_job_from_s3.assert_not_called() tasks.process_row.assert_not_called() @@ -1377,7 +1377,7 @@ def test_process_incomplete_jobs_sets_status_to_in_progress_and_resets_processin @freeze_time("2020-03-25 14:30") -@pytest.mark.parametrize("notification_type", ["sms", "email"]) +@pytest.mark.parametrize("notification_type", [NotificationType.SMS, NotificationType.EMAIL],) def test_save_api_email_or_sms(mocker, sample_service, notification_type): template = ( create_template(sample_service) @@ -1427,7 +1427,7 @@ def test_save_api_email_or_sms(mocker, sample_service, notification_type): @freeze_time("2020-03-25 14:30") -@pytest.mark.parametrize("notification_type", ["sms", "email"]) +@pytest.mark.parametrize("notification_type", [NotificationType.SMS, NotificationType.EMAIL]) def test_save_api_email_dont_retry_if_notification_already_exists( sample_service, mocker, notification_type ): @@ -1492,13 +1492,13 @@ def test_save_api_email_dont_retry_if_notification_already_exists( save_email, "app.celery.provider_tasks.deliver_email.apply_async", "test@example.com", - {"template_type": "email", "subject": "Hello"}, + {"template_type": TemplateType.EMAIL, "subject": "Hello"}, ), ( save_sms, "app.celery.provider_tasks.deliver_sms.apply_async", "202-867-5309", - {"template_type": "sms"}, + {"template_type": TemplateType.SMS}, ), ), ) @@ -1549,8 +1549,8 @@ def test_save_tasks_use_cached_service_and_template( @pytest.mark.parametrize( "notification_type, task_function, expected_queue, recipient", ( - ("sms", save_api_sms, QueueNames.SEND_SMS, "+447700900855"), - ("email", save_api_email, QueueNames.SEND_EMAIL, "jane.citizen@example.com"), + (NotificationType.SMS, save_api_sms, QueueNames.SEND_SMS, "+447700900855",), + (NotificationType.EMAIL, save_api_email, QueueNames.SEND_EMAIL, "jane.citizen@example.com",), ), ) def test_save_api_tasks_use_cache( diff --git a/tests/app/dao/test_fact_billing_dao.py b/tests/app/dao/test_fact_billing_dao.py index 90bfb6d69..41a3dd485 100644 --- a/tests/app/dao/test_fact_billing_dao.py +++ b/tests/app/dao/test_fact_billing_dao.py @@ -21,8 +21,8 @@ from app.dao.fact_billing_dao import ( query_organization_sms_usage_for_year, ) from app.dao.organization_dao import dao_add_service_to_organization -from app.enums import NotificationStatus -from app.models import FactBilling +from app.enums import NotificationStatus, NotificationType, TemplateType +from app.models import FactBilling, Notification from tests.app.db import ( create_annual_billing, create_ft_billing, @@ -39,8 +39,8 @@ from tests.app.db import ( def set_up_yearly_data(): service = create_service() - sms_template = create_template(service=service, template_type="sms") - email_template = create_template(service=service, template_type="email") + sms_template = create_template(service=service, template_type=TemplateType.SMS) + email_template = create_template(service=service, template_type=TemplateType.EMAIL) # use different rates for adjacent financial years to make sure the query # doesn't accidentally bleed over into them @@ -68,7 +68,7 @@ def set_up_yearly_data(): def set_up_yearly_data_variable_rates(): service = create_service() - sms_template = create_template(service=service, template_type="sms") + sms_template = create_template(service=service, template_type=TemplateType.SMS) create_ft_billing(local_date="2018-05-16", template=sms_template, rate=0.162) create_ft_billing( @@ -93,9 +93,9 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_key_type( notify_db_session, ): service = create_service() - template = create_template(service=service, template_type="email") + template = create_template(service=service, template_type=TemplateType.EMAIL) for key_type in ["normal", "test", "team"]: - create_notification(template=template, status="delivered", key_type=key_type) + create_notification(template=template, status=NotificationStatus.DELIVERED, key_type=key_type,) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -103,13 +103,13 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_key_type( assert results[0].notifications_sent == 2 -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS]) def test_fetch_billing_data_for_day_only_calls_query_for_permission_type( notify_db_session, notification_type ): service = create_service(service_permissions=[notification_type]) - email_template = create_template(service=service, template_type="email") - sms_template = create_template(service=service, template_type="sms") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) + sms_template = create_template(service=service, template_type=TemplateType.SMS) create_notification(template=email_template, status="delivered") create_notification(template=sms_template, status="delivered") today = datetime.utcnow() @@ -119,18 +119,18 @@ def test_fetch_billing_data_for_day_only_calls_query_for_permission_type( assert len(results) == 1 -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS],) def test_fetch_billing_data_for_day_only_calls_query_for_all_channels( notify_db_session, notification_type ): service = create_service(service_permissions=[notification_type]) - email_template = create_template(service=service, template_type="email") - sms_template = create_template(service=service, template_type="sms") - create_notification(template=email_template, status="delivered") - create_notification(template=sms_template, status="delivered") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) + sms_template = create_template(service=service, template_type=TemplateType.SMS) + create_notification(template=email_template, status=NotificationStatus.DELIVERED) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED) today = datetime.utcnow() results = fetch_billing_data_for_day( - process_day=today.date(), check_permissions=False + process_day=today.date(), check_permissions=False, ) assert len(results) == 2 @@ -141,21 +141,21 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_date( ): process_day = datetime(2018, 4, 1, 13, 30, 0) service = create_service() - template = create_template(service=service, template_type="email") - create_notification(template=template, status="delivered", created_at=process_day) + template = create_template(service=service, template_type=TemplateType.EMAIL) + create_notification(template=template, status=NotificationStatus.DELIVERED, created_at=process_day,) create_notification( template=template, - status="delivered", + status=NotificationStatus.DELIVERED, created_at=datetime(2018, 4, 1, 4, 23, 23), ) create_notification( template=template, - status="delivered", + status=NotificationStatus.DELIVERED, created_at=datetime(2018, 4, 1, 0, 23, 23), ) create_notification( - template=template, status="sending", created_at=process_day + timedelta(days=1) + template=template, status=NotificationStatus.SENDING, created_at=process_day + timedelta(days=1,) ) day_under_test = process_day @@ -168,10 +168,10 @@ def test_fetch_billing_data_for_day_is_grouped_by_template_and_notification_type notify_db_session, ): service = create_service() - email_template = create_template(service=service, template_type="email") - sms_template = create_template(service=service, template_type="sms") - create_notification(template=email_template, status="delivered") - create_notification(template=sms_template, status="delivered") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) + sms_template = create_template(service=service, template_type=TemplateType.SMS) + create_notification(template=email_template, status=NotificationStatus.DELIVERED) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -185,8 +185,8 @@ def test_fetch_billing_data_for_day_is_grouped_by_service(notify_db_session): service_2 = create_service(service_name="Service 2") email_template = create_template(service=service_1) sms_template = create_template(service=service_2) - create_notification(template=email_template, status="delivered") - create_notification(template=sms_template, status="delivered") + create_notification(template=email_template, status=NotificationStatus.DELIVERED) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -198,8 +198,8 @@ def test_fetch_billing_data_for_day_is_grouped_by_service(notify_db_session): def test_fetch_billing_data_for_day_is_grouped_by_provider(notify_db_session): service = create_service() template = create_template(service=service) - create_notification(template=template, status="delivered", sent_by="sns") - create_notification(template=template, status="delivered", sent_by="sns") + create_notification(template=template, status=NotificationStatus.DELIVERED, sent_by="sns",) + create_notification(template=template, status=NotificationStatus.DELIVERED, sent_by="sns",) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -211,8 +211,8 @@ def test_fetch_billing_data_for_day_is_grouped_by_provider(notify_db_session): def test_fetch_billing_data_for_day_is_grouped_by_rate_mulitplier(notify_db_session): service = create_service() template = create_template(service=service) - create_notification(template=template, status="delivered", rate_multiplier=1) - create_notification(template=template, status="delivered", rate_multiplier=2) + create_notification(template=template, status=NotificationStatus.DELIVERED, rate_multiplier=1,) + create_notification(template=template, status=NotificationStatus.DELIVERED, rate_multiplier=2,) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -224,8 +224,8 @@ def test_fetch_billing_data_for_day_is_grouped_by_rate_mulitplier(notify_db_sess def test_fetch_billing_data_for_day_is_grouped_by_international(notify_db_session): service = create_service() sms_template = create_template(service=service) - create_notification(template=sms_template, status="delivered", international=True) - create_notification(template=sms_template, status="delivered", international=False) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED, international=True,) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED, international=False,) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -235,13 +235,13 @@ def test_fetch_billing_data_for_day_is_grouped_by_international(notify_db_sessio def test_fetch_billing_data_for_day_is_grouped_by_notification_type(notify_db_session): service = create_service() - sms_template = create_template(service=service, template_type="sms") - email_template = create_template(service=service, template_type="email") - create_notification(template=sms_template, status="delivered") - create_notification(template=sms_template, status="delivered") - create_notification(template=sms_template, status="delivered") - create_notification(template=email_template, status="delivered") - create_notification(template=email_template, status="delivered") + sms_template = create_template(service=service, template_type=TemplateType.SMS) + email_template = create_template(service=service, template_type=TemplateType.EMAIL) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED) + create_notification(template=email_template, status=NotificationStatus.DELIVERED) + create_notification(template=email_template, status=NotificationStatus.DELIVERED) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -259,26 +259,26 @@ def test_fetch_billing_data_for_day_returns_empty_list(notify_db_session): def test_fetch_billing_data_for_day_uses_correct_table(notify_db_session): service = create_service() create_service_data_retention( - service, notification_type="email", days_of_retention=3 + service, notification_type=NotificationType.EMAIL, days_of_retention=3 ) - sms_template = create_template(service=service, template_type="sms") - email_template = create_template(service=service, template_type="email") + sms_template = create_template(service=service, template_type=TemplateType.SMS) + email_template = create_template(service=service, template_type=TemplateType.EMAIL) five_days_ago = datetime.utcnow() - timedelta(days=5) create_notification( - template=sms_template, status="delivered", created_at=five_days_ago + template=sms_template, status=NotificationStatus.DELIVERED, created_at=five_days_ago, ) create_notification_history( - template=email_template, status="delivered", created_at=five_days_ago + template=email_template, status=NotificationStatus.DELIVERED, created_at=five_days_ago, ) results = fetch_billing_data_for_day( process_day=five_days_ago.date(), service_id=service.id ) assert len(results) == 2 - assert results[0].notification_type == "sms" + assert results[0].notification_type == NotificationType.SMS assert results[0].notifications_sent == 1 - assert results[1].notification_type == "email" + assert results[1].notification_type == NotificationType.EMAIL assert results[1].notifications_sent == 1 @@ -287,8 +287,8 @@ def test_fetch_billing_data_for_day_returns_list_for_given_service(notify_db_ses service_2 = create_service(service_name="Service 2") template = create_template(service=service) template_2 = create_template(service=service_2) - create_notification(template=template, status="delivered") - create_notification(template=template_2, status="delivered") + create_notification(template=template, status=NotificationStatus.DELIVERED) + create_notification(template=template_2, status=NotificationStatus.DELIVERED) today = datetime.utcnow() results = fetch_billing_data_for_day( @@ -300,8 +300,8 @@ def test_fetch_billing_data_for_day_returns_list_for_given_service(notify_db_ses def test_fetch_billing_data_for_day_bills_correctly_for_status(notify_db_session): service = create_service() - sms_template = create_template(service=service, template_type="sms") - email_template = create_template(service=service, template_type="email") + sms_template = create_template(service=service, template_type=TemplateType.SMS) + email_template = create_template(service=service, template_type=TemplateType.EMAIL) for status in NotificationStatus: create_notification(template=sms_template, status=status) create_notification(template=email_template, status=status) @@ -310,17 +310,17 @@ def test_fetch_billing_data_for_day_bills_correctly_for_status(notify_db_session process_day=today.date(), service_id=service.id ) - sms_results = [x for x in results if x.notification_type == "sms"] - email_results = [x for x in results if x.notification_type == "email"] + sms_results = [x for x in results if x.notification_type == NotificationType.SMS] + email_results = [x for x in results if x.notification_type == NotificationType.EMAIL] # we expect as many rows as we check for notification types assert 6 == sms_results[0].notifications_sent assert 4 == email_results[0].notifications_sent def test_get_rates_for_billing(notify_db_session): - create_rate(start_date=datetime.utcnow(), value=12, notification_type="email") - create_rate(start_date=datetime.utcnow(), value=22, notification_type="sms") - create_rate(start_date=datetime.utcnow(), value=33, notification_type="email") + create_rate(start_date=datetime.utcnow(), value=12, notification_type=NotificationType.EMAIL) + create_rate(start_date=datetime.utcnow(), value=22, notification_type=NotificationType.SMS) + create_rate(start_date=datetime.utcnow(), value=33, notification_type=NotificationType.EMAIL) rates = get_rates_for_billing() assert len(rates) == 3 @@ -329,17 +329,17 @@ def test_get_rates_for_billing(notify_db_session): @freeze_time("2017-06-01 12:00") def test_get_rate(notify_db_session): create_rate( - start_date=datetime(2017, 5, 30, 23, 0), value=1.2, notification_type="email" + start_date=datetime(2017, 5, 30, 23, 0), value=1.2, notification_type=NotificationType.EMAIL, ) create_rate( - start_date=datetime(2017, 5, 30, 23, 0), value=2.2, notification_type="sms" + start_date=datetime(2017, 5, 30, 23, 0), value=2.2, notification_type=NotificationType.SMS, ) create_rate( - start_date=datetime(2017, 5, 30, 23, 0), value=3.3, notification_type="email" + start_date=datetime(2017, 5, 30, 23, 0), value=3.3, notification_type=NotificationType.EMAIL, ) rates = get_rates_for_billing() - rate = get_rate(rates, notification_type="sms", date=date(2017, 6, 1)) + rate = get_rate(rates, notification_type=NotificationType.SMS, date=date(2017, 6, 1)) assert rate == 2.2 @@ -351,14 +351,14 @@ def test_get_rate_chooses_right_rate_depending_on_date( notify_db_session, date, expected_rate ): create_rate( - start_date=datetime(2016, 1, 1, 0, 0), value=1.2, notification_type="sms" + start_date=datetime(2016, 1, 1, 0, 0), value=1.2, notification_type=NotificationType.SMS, ) create_rate( - start_date=datetime(2018, 9, 30, 23, 0), value=2.2, notification_type="sms" + start_date=datetime(2018, 9, 30, 23, 0), value=2.2, notification_type=NotificationType.SMS, ) rates = get_rates_for_billing() - rate = get_rate(rates, "sms", date) + rate = get_rate(rates, NotificationType.SMS, date) assert rate == expected_rate @@ -373,7 +373,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session): print(f"RESULTS {results}") assert str(results[0].month) == "2016-01-01" - assert results[0].notification_type == "email" + assert results[0].notification_type == NotificationType.EMAIL assert results[0].notifications_sent == 2 assert results[0].chargeable_units == 0 assert results[0].rate == Decimal("0") @@ -382,7 +382,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session): assert results[0].charged_units == 0 assert str(results[1].month) == "2016-01-01" - assert results[1].notification_type == "sms" + assert results[1].notification_type == NotificationType.SMS assert results[1].notifications_sent == 2 assert results[1].chargeable_units == 2 assert results[1].rate == Decimal("0.162") @@ -397,7 +397,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session): def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session): service = set_up_yearly_data_variable_rates() create_annual_billing( - service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018 + service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018, ) results = fetch_monthly_billing_for_year(service.id, 2018) @@ -405,7 +405,7 @@ def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session): assert len(results) == 2 assert str(results[0].month) == "2018-05-01" - assert results[0].notification_type == "sms" + assert results[0].notification_type == NotificationType.SMS assert results[0].notifications_sent == 1 assert results[0].chargeable_units == 4 assert results[0].rate == Decimal("0.015") @@ -415,7 +415,7 @@ def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session): assert results[0].charged_units == 3 assert str(results[1].month) == "2018-05-01" - assert results[1].notification_type == "sms" + assert results[1].notification_type == NotificationType.SMS assert results[1].notifications_sent == 2 assert results[1].chargeable_units == 5 assert results[1].rate == Decimal("0.162") @@ -428,12 +428,12 @@ def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session): @freeze_time("2018-08-01 13:30:00") def test_fetch_monthly_billing_for_year_adds_data_for_today(notify_db_session): service = create_service() - template = create_template(service=service, template_type="sms") + template = create_template(service=service, template_type=TemplateType.SMS) create_rate( start_date=datetime.utcnow() - timedelta(days=1), value=0.158, - notification_type="sms", + notification_type=NotificationType.SMS, ) create_annual_billing( service_id=service.id, free_sms_fragment_limit=1000, financial_year_start=2018 @@ -442,7 +442,7 @@ def test_fetch_monthly_billing_for_year_adds_data_for_today(notify_db_session): for i in range(1, 32): create_ft_billing(local_date="2018-07-{}".format(i), template=template) - create_notification(template=template, status="delivered") + create_notification(template=template, status=NotificationStatus.DELIVERED) assert db.session.query(FactBilling.local_date).count() == 31 results = fetch_monthly_billing_for_year(service_id=service.id, year=2018) @@ -459,7 +459,7 @@ def test_fetch_billing_totals_for_year(notify_db_session): results = fetch_billing_totals_for_year(service_id=service.id, year=2016) assert len(results) == 2 - assert results[0].notification_type == "email" + assert results[0].notification_type == NotificationType.EMAIL assert results[0].notifications_sent == 4 assert results[0].chargeable_units == 0 assert results[0].rate == Decimal("0") @@ -467,7 +467,7 @@ def test_fetch_billing_totals_for_year(notify_db_session): assert results[0].free_allowance_used == 0 assert results[0].charged_units == 0 - assert results[1].notification_type == "sms" + assert results[1].notification_type == NotificationType.SMS assert results[1].notifications_sent == 4 assert results[1].chargeable_units == 4 assert results[1].rate == Decimal("0.162") @@ -488,7 +488,7 @@ def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_ses result = next( result for result in fetch_billing_totals_for_year(service_id=service.id, year=2016) - if result.notification_type == "sms" + if result.notification_type == NotificationType.SMS ) assert result.chargeable_units == 4 @@ -501,13 +501,13 @@ def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_ses def test_fetch_billing_totals_for_year_variable_rates(notify_db_session): service = set_up_yearly_data_variable_rates() create_annual_billing( - service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018 + service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018, ) results = fetch_billing_totals_for_year(service_id=service.id, year=2018) assert len(results) == 2 - assert results[0].notification_type == "sms" + assert results[0].notification_type == NotificationType.SMS assert results[0].notifications_sent == 1 assert results[0].chargeable_units == 4 assert results[0].rate == Decimal("0.015") @@ -516,7 +516,7 @@ def test_fetch_billing_totals_for_year_variable_rates(notify_db_session): assert results[0].free_allowance_used == 1 assert results[0].charged_units == 3 - assert results[1].notification_type == "sms" + assert results[1].notification_type == NotificationType.SMS assert results[1].notifications_sent == 2 assert results[1].chargeable_units == 5 assert results[1].rate == Decimal("0.162") @@ -529,9 +529,9 @@ def test_fetch_billing_totals_for_year_variable_rates(notify_db_session): def test_delete_billing_data(notify_db_session): service_1 = create_service(service_name="1") service_2 = create_service(service_name="2") - sms_template = create_template(service_1, "sms") - email_template = create_template(service_1, "email") - other_service_template = create_template(service_2, "sms") + sms_template = create_template(service_1, TemplateType.SMS) + email_template = create_template(service_1, TemplateType.EMAIL) + other_service_template = create_template(service_2, TemplateType.SMS) existing_rows_to_delete = [ # noqa create_ft_billing("2018-01-01", sms_template, billable_unit=1), @@ -558,13 +558,13 @@ def test_fetch_sms_free_allowance_remainder_until_date_with_two_services( org = create_organization(name="Org for {}".format(service.name)) dao_add_service_to_organization(service=service, organization_id=org.id) create_annual_billing( - service_id=service.id, free_sms_fragment_limit=10, financial_year_start=2016 + service_id=service.id, free_sms_fragment_limit=10, financial_year_start=2016, ) create_ft_billing( - template=template, local_date=datetime(2016, 4, 20), billable_unit=2, rate=0.11 + template=template, local_date=datetime(2016, 4, 20), billable_unit=2, rate=0.11, ) create_ft_billing( - template=template, local_date=datetime(2016, 5, 20), billable_unit=3, rate=0.11 + template=template, local_date=datetime(2016, 5, 20), billable_unit=3, rate=0.11, ) service_2 = create_service(service_name="used free allowance") @@ -693,7 +693,7 @@ def test_fetch_sms_billing_for_all_services_with_remainder(notify_db_session): ) service_4 = create_service(service_name="d - email only") - email_template = create_template(service=service_4, template_type="email") + email_template = create_template(service=service_4, template_type=TemplateType.EMAIL) org_4 = create_organization(name="Org for {}".format(service_4.name)) dao_add_service_to_organization(service=service_4, organization_id=org_4.id) create_annual_billing( @@ -827,13 +827,13 @@ def test_fetch_usage_year_for_organization(notify_db_session): financial_year_start=2019, ) dao_add_service_to_organization( - service=service_with_emails_for_org, organization_id=fixtures["org_1"].id + service=service_with_emails_for_org, organization_id=fixtures["org_1"].id, ) template = create_template( - service=service_with_emails_for_org, template_type="email" + service=service_with_emails_for_org, template_type=TemplateType.EMAIL, ) create_ft_billing( - local_date=datetime(2019, 5, 1), template=template, notifications_sent=1100 + local_date=datetime(2019, 5, 1), template=template, notifications_sent=1100, ) results = fetch_usage_year_for_organization(fixtures["org_1"].id, 2019) @@ -877,7 +877,7 @@ def test_fetch_usage_year_for_organization_populates_ft_billing_for_today( create_rate( start_date=datetime.utcnow() - timedelta(days=1), value=0.65, - notification_type="sms", + notification_type=NotificationType.SMS, ) new_org = create_organization(name="New organization") service = create_service() @@ -892,7 +892,7 @@ def test_fetch_usage_year_for_organization_populates_ft_billing_for_today( assert FactBilling.query.count() == 0 - create_notification(template=template, status="delivered") + create_notification(template=template, status=NotificationStatus.DELIVERED) results = fetch_usage_year_for_organization( organization_id=new_org.id, year=current_year @@ -1039,8 +1039,8 @@ def test_fetch_usage_year_for_organization_only_returns_data_for_live_services( live_service = create_service(restricted=False) sms_template = create_template(service=live_service) trial_service = create_service(restricted=True, service_name="trial_service") - email_template = create_template(service=trial_service, template_type="email") - trial_sms_template = create_template(service=trial_service, template_type="sms") + email_template = create_template(service=trial_service, template_type=TemplateType.EMAIL) + trial_sms_template = create_template(service=trial_service, template_type=TemplateType.SMS) dao_add_service_to_organization(service=live_service, organization_id=org.id) dao_add_service_to_organization(service=trial_service, organization_id=org.id) create_ft_billing( From f27964812c29dfe4e61db0f387976143bb051e83 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 9 Feb 2024 15:03:16 -0500 Subject: [PATCH 187/259] More cleanup. Signed-off-by: Cliff Hill --- .../dao/test_fact_notification_status_dao.py | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index e1606433e..be750cb83 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -255,46 +255,46 @@ def test_fetch_notification_status_by_template_for_service_for_today_and_7_previ # create unused email template create_template(service=service_1, template_type=TemplateType.EMAIL) - create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) - create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=11) - create_ft_notification_status(date(2018, 10, 25), "sms", service_1, count=8) + create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=10,) + create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=11,) + create_ft_notification_status(date(2018, 10, 25), NotificationType.SMS, service_1, count=8,) create_ft_notification_status( - date(2018, 10, 29), "sms", service_1, notification_status="created" + date(2018, 10, 29), NotificationType.SMS, service_1, notification_status=NotificationStatus.CREATED, ) - create_ft_notification_status(date(2018, 10, 29), "email", service_1, count=3) + create_ft_notification_status(date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3,) create_notification(sms_template, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status="delivered" + sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, ) create_notification( - sms_template_2, created_at=datetime(2018, 10, 31, 12, 0, 0), status="delivered" + sms_template_2, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status="delivered" + email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status=NotificationStatus.DELIVERED, ) # too early, shouldn't be included create_notification( service_1.templates[0], created_at=datetime(2018, 10, 30, 12, 0, 0), - status="delivered", + status=NotificationStatus.DELIVERED, ) results = fetch_notification_status_for_service_for_today_and_7_previous_days( - service_1.id, by_template=True + service_1.id, by_template=True, ) assert [ - ("email Template Name", False, mock.ANY, "email", "delivered", 1), - ("email Template Name", False, mock.ANY, "email", "delivered", 3), - ("sms Template 1", False, mock.ANY, "sms", "created", 1), - ("sms Template Name", False, mock.ANY, "sms", "created", 1), - ("sms Template 1", False, mock.ANY, "sms", "delivered", 1), - ("sms Template 2", False, mock.ANY, "sms", "delivered", 1), - ("sms Template Name", False, mock.ANY, "sms", "delivered", 8), - ("sms Template Name", False, mock.ANY, "sms", "delivered", 10), - ("sms Template Name", False, mock.ANY, "sms", "delivered", 11), + ("email Template Name", False, mock.ANY, NotificationType.EMAIL, NotificationStatus.DELIVERED, 1,), + ("email Template Name", False, mock.ANY, NotificationType.EMAIL, NotificationStatus.DELIVERED, 3,), + ("sms Template 1", False, mock.ANY, NotificationType.SMS, NotificationStatus.CREATED, 1,), + ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.CREATED, 1,), + ("sms Template 1", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 1,), + ("sms Template 2", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 1,), + ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 8,), + ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 10,), + ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 11,), ] == sorted( results, key=lambda x: (x.notification_type, x.status, x.template_name, x.count) ) @@ -352,19 +352,19 @@ def test_fetch_notification_status_totals_for_all_services_works_in_est( ) create_notification( - sms_template, created_at=datetime(2018, 4, 20, 12, 0, 0), status="delivered" + sms_template, created_at=datetime(2018, 4, 20, 12, 0, 0), status=NotificationStatus.DELIVERED, ) create_notification( - sms_template, created_at=datetime(2018, 4, 21, 11, 0, 0), status="created" + sms_template, created_at=datetime(2018, 4, 21, 11, 0, 0), status=NotificationStatus.CREATED, ) create_notification( - sms_template, created_at=datetime(2018, 4, 21, 12, 0, 0), status="delivered" + sms_template, created_at=datetime(2018, 4, 21, 12, 0, 0), status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 4, 21, 13, 0, 0), status="delivered" + email_template, created_at=datetime(2018, 4, 21, 13, 0, 0), status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 4, 21, 14, 0, 0), status="delivered" + email_template, created_at=datetime(2018, 4, 21, 14, 0, 0), status=NotificationStatus.DELIVERED, ) results = sorted( @@ -376,16 +376,16 @@ def test_fetch_notification_status_totals_for_all_services_works_in_est( assert len(results) == 3 - assert results[0].notification_type == "email" - assert results[0].status == "delivered" + assert results[0].notification_type == NotificationType.EMAIL + assert results[0].status == NotificationStatus.DELIVERED assert results[0].count == 2 - assert results[1].notification_type == "sms" - assert results[1].status == "created" + assert results[1].notification_type == NotificationType.SMS + assert results[1].status == NotificationStatus.CREATED assert results[1].count == 1 - assert results[2].notification_type == "sms" - assert results[2].status == "delivered" + assert results[2].notification_type == NotificationType.SMS + assert results[2].status == NotificationStatus.DELIVERED assert results[2].count == 1 @@ -396,24 +396,24 @@ def set_up_data(): email_template = create_template( service=service_1, template_type=TemplateType.EMAIL ) - create_ft_notification_status(date(2018, 10, 24), "sms", service_1, count=8) - create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) + create_ft_notification_status(date(2018, 10, 24), NotificationType.SMS, service_1, count=8,) + create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=10,) create_ft_notification_status( - date(2018, 10, 29), "sms", service_1, notification_status="created" + date(2018, 10, 29), NotificationType.SMS, service_1, notification_status=NotificationStatus.CREATED, ) - create_ft_notification_status(date(2018, 10, 29), "email", service_1, count=3) + create_ft_notification_status(date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3) create_notification( service_1.templates[0], created_at=datetime(2018, 10, 30, 12, 0, 0), - status="delivered", + status=NotificationStatus.DELIVERED, ) create_notification(sms_template, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status="delivered" + sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status="delivered" + email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status=NotificationStatus.DELIVERED, ) return service_1, service_2 @@ -423,16 +423,16 @@ def test_fetch_notification_statuses_for_job(sample_template): j2 = create_job(sample_template) create_ft_notification_status( - date(2018, 10, 1), job=j1, notification_status="created", count=1 + date(2018, 10, 1), job=j1, notification_status=NotificationStatus.CREATED, count=1, ) create_ft_notification_status( - date(2018, 10, 1), job=j1, notification_status="delivered", count=2 + date(2018, 10, 1), job=j1, notification_status=NotificationStatus.DELIVERED, count=2, ) create_ft_notification_status( - date(2018, 10, 2), job=j1, notification_status="created", count=4 + date(2018, 10, 2), job=j1, notification_status=NotificationStatus.CREATED, count=4, ) create_ft_notification_status( - date(2018, 10, 1), job=j2, notification_status="created", count=8 + date(2018, 10, 1), job=j2, notification_status=NotificationStatus.CREATED, count=8, ) assert {x.status: x.count for x in fetch_notification_statuses_for_job(j1.id)} == { @@ -450,18 +450,18 @@ def test_fetch_stats_for_all_services_by_date_range(notify_db_session): assert len(results) == 4 assert results[0].service_id == service_1.id - assert results[0].notification_type == "email" - assert results[0].status == "delivered" + assert results[0].notification_type == NotificationType.EMAIL + assert results[0].status == NotificationStatus.DELIVERED assert results[0].count == 4 assert results[1].service_id == service_1.id - assert results[1].notification_type == "sms" - assert results[1].status == "created" + assert results[1].notification_type == NotificationType.SMS + assert results[1].status == NotificationStatus.CREATED assert results[1].count == 2 assert results[2].service_id == service_1.id - assert results[2].notification_type == "sms" - assert results[2].status == "delivered" + assert results[2].notification_type == NotificationType.SMS + assert results[2].status == NotificationStatus.DELIVERED assert results[2].count == 11 assert results[3].service_id == service_2.id @@ -473,10 +473,10 @@ def test_fetch_stats_for_all_services_by_date_range(notify_db_session): @freeze_time("2018-03-30 14:00") def test_fetch_monthly_template_usage_for_service(sample_service): template_one = create_template( - service=sample_service, template_type="sms", template_name="a" + service=sample_service, template_type=TemplateType.SMS, template_name="a", ) template_two = create_template( - service=sample_service, template_type="email", template_name="b" + service=sample_service, template_type=TemplateType.EMAIL, template_name="b", ) create_ft_notification_status( @@ -548,10 +548,10 @@ def test_fetch_monthly_template_usage_for_service_does_join_to_notifications_if_ sample_service, ): template_one = create_template( - service=sample_service, template_type="sms", template_name="a" + service=sample_service, template_type=TemplateType.SMS, template_name="a", ) template_two = create_template( - service=sample_service, template_type="email", template_name="b" + service=sample_service, template_type=TmplateType.EMAIL, template_name="b", ) create_ft_notification_status( local_date=date(2018, 2, 1), @@ -600,11 +600,11 @@ def test_fetch_monthly_template_usage_for_service_does_not_include_cancelled_sta local_date=date(2018, 3, 1), service=sample_template.service, template=sample_template, - notification_status="cancelled", + notification_status=NotificationStatus.CANCELLED, count=15, ) create_notification( - template=sample_template, created_at=datetime.utcnow(), status="cancelled" + template=sample_template, created_at=datetime.utcnow(), status=NotificationStatus.CANCELLED, ) results = fetch_monthly_template_usage_for_service( datetime(2018, 1, 1), datetime(2018, 3, 31), sample_template.service_id @@ -621,18 +621,18 @@ def test_fetch_monthly_template_usage_for_service_does_not_include_test_notifica local_date=date(2018, 3, 1), service=sample_template.service, template=sample_template, - notification_status="delivered", - key_type="test", + notification_status=NotificationStatus.DELIVERED, + key_type=KeyType.TEST, count=15, ) create_notification( template=sample_template, created_at=datetime.utcnow(), - status="delivered", - key_type="test", + status=NotificationStatus.DELIVERED, + key_type=KeyType.TEST, ) results = fetch_monthly_template_usage_for_service( - datetime(2018, 1, 1), datetime(2018, 3, 31), sample_template.service_id + datetime(2018, 1, 1), datetime(2018, 3, 31), sample_template.service_id, ) assert len(results) == 0 @@ -651,69 +651,69 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): create_ft_notification_status( date(2019, 4, 30), - notification_type="sms", + notification_type=NotificationType.SMS, service=service_one, notification_status=NotificationStatus.DELIVERED, ) create_ft_notification_status( date(2019, 3, 1), - notification_type="email", + notification_type=NotificationType.EMAIL, service=service_one, notification_status=NotificationStatus.SENDING, count=4, ) create_ft_notification_status( date(2019, 3, 1), - notification_type="email", + notification_type=NotificationType.EMAIL, service=service_one, notification_status=NotificationStatus.PENDING, count=1, ) create_ft_notification_status( date(2019, 3, 2), - notification_type="email", + notification_type=NotificationType.EMAIL, service=service_one, notification_status=NotificationStatus.TECHNICAL_FAILURE, count=2, ) create_ft_notification_status( date(2019, 3, 7), - notification_type="email", + notification_type=NotificationType.EMAIL, service=service_one, notification_status=NotificationStatus.FAILED, count=1, ) create_ft_notification_status( date(2019, 3, 10), - notification_type="sms", + notification_type=NotificationType.SMS, service=service_two, notification_status=NotificationStatus.PERMANENT_FAILURE, count=1, ) create_ft_notification_status( date(2019, 3, 10), - notification_type="sms", + notification_type=NotificationType.SMS, service=service_two, notification_status=NotificationStatus.PERMANENT_FAILURE, count=1, ) create_ft_notification_status( date(2019, 3, 13), - notification_type="sms", + notification_type=NotificationType.SMS, service=service_one, notification_status=NotificationStatus.SENT, count=1, ) create_ft_notification_status( date(2019, 4, 1), - notification_type="sms", + notification_type=NotificationType.SMS, service=service_two, notification_status=NotificationStatus.TEMPORARY_FAILURE, count=10, ) create_ft_notification_status( date(2019, 3, 31), - notification_type="sms", + notification_type=NotificationType.SMS, service=service_one, notification_status=NotificationStatus.DELIVERED, ) @@ -729,7 +729,7 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): date(2019, 3, 1), service_two.id, "service two", - "sms", + NotificationType.SMS, 0, 0, 0, @@ -741,7 +741,7 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): date(2019, 3, 1), service_one.id, "service one", - "email", + NotificationType.EMAIL, 5, 0, 3, @@ -753,7 +753,7 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): date(2019, 3, 1), service_one.id, "service one", - "sms", + NotificationType.SMS, 0, 1, 0, @@ -765,7 +765,7 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): date(2019, 4, 1), service_two.id, "service two", - "sms", + NotificationType.SMS, 0, 0, 0, @@ -777,7 +777,7 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): date(2019, 4, 1), service_one.id, "service one", - "sms", + NotificationType.SMS, 0, 1, 0, @@ -821,10 +821,10 @@ def test_fetch_monthly_notification_statuses_per_service_for_rows_that_should_be def test_get_total_notifications_for_date_range(sample_service): template_sms = create_template( - service=sample_service, template_type="sms", template_name="a" + service=sample_service, template_type=TemplateType.SMS, template_name="a", ) template_email = create_template( - service=sample_service, template_type="email", template_name="b" + service=sample_service, template_type=TemplateType.EMAIL, template_name="b", ) create_ft_notification_status( local_date=date(2021, 2, 28), From acecf9792dde73eb9dcf5f37bf3cfad4f0684686 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 9 Feb 2024 15:23:24 -0500 Subject: [PATCH 188/259] Even more cleanup of tests. Signed-off-by: Cliff Hill --- tests/app/dao/test_inbound_sms_dao.py | 11 ++++--- tests/app/dao/test_provider_details_dao.py | 25 ++++++++------- .../dao/test_service_data_retention_dao.py | 31 ++++++++++--------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/tests/app/dao/test_inbound_sms_dao.py b/tests/app/dao/test_inbound_sms_dao.py index 7645f2726..a53180965 100644 --- a/tests/app/dao/test_inbound_sms_dao.py +++ b/tests/app/dao/test_inbound_sms_dao.py @@ -12,6 +12,7 @@ from app.dao.inbound_sms_dao import ( dao_get_paginated_most_recent_inbound_sms_by_user_number_for_service, delete_inbound_sms_older_than_retention, ) +from app.enums import NotificationType from app.models import InboundSmsHistory from tests.app.db import ( create_inbound_sms, @@ -65,10 +66,10 @@ def test_get_all_inbound_sms_filters_on_service(notify_db_session): def test_get_all_inbound_sms_filters_on_time(sample_service, notify_db_session): create_inbound_sms( - sample_service, created_at=datetime(2017, 8, 6, 23, 59) + sample_service, created_at=datetime(2017, 8, 6, 23, 59), ) # sunday evening sms_two = create_inbound_sms( - sample_service, created_at=datetime(2017, 8, 7, 0, 0) + sample_service, created_at=datetime(2017, 8, 7, 0, 0), ) # monday (7th) morning with freeze_time("2017-08-14 12:00"): @@ -110,13 +111,13 @@ def test_should_delete_inbound_sms_according_to_data_retention(notify_db_session services = [short_retention_service, no_retention_service, long_retention_service] create_service_data_retention( - long_retention_service, notification_type="sms", days_of_retention=30 + long_retention_service, notification_type=NotificationType.SMS, days_of_retention=30, ) create_service_data_retention( - short_retention_service, notification_type="sms", days_of_retention=3 + short_retention_service, notification_type=NotificationType.SMS, days_of_retention=3, ) create_service_data_retention( - short_retention_service, notification_type="email", days_of_retention=4 + short_retention_service, notification_type=NotificationType.EMAIL, days_of_retention=4, ) dates = [ diff --git a/tests/app/dao/test_provider_details_dao.py b/tests/app/dao/test_provider_details_dao.py index 14e80d873..aa81ed0c0 100644 --- a/tests/app/dao/test_provider_details_dao.py +++ b/tests/app/dao/test_provider_details_dao.py @@ -14,6 +14,7 @@ from app.dao.provider_details_dao import ( get_provider_details_by_identifier, get_provider_details_by_notification_type, ) +from app.enums import NotificationType, TemplateType from app.models import ProviderDetails, ProviderDetailsHistory from tests.app.db import create_ft_billing, create_service, create_template from tests.conftest import set_config @@ -39,37 +40,37 @@ def set_primary_sms_provider(identifier): def test_can_get_sms_non_international_providers(notify_db_session): - sms_providers = get_provider_details_by_notification_type("sms") + sms_providers = get_provider_details_by_notification_type(NotificationType.SMS) assert len(sms_providers) > 0 - assert all("sms" == prov.notification_type for prov in sms_providers) + assert all(NotificationType.SMS == prov.notification_type for prov in sms_providers) def test_can_get_sms_international_providers(notify_db_session): - sms_providers = get_provider_details_by_notification_type("sms", True) + sms_providers = get_provider_details_by_notification_type(NotificationType.SMS, True) assert len(sms_providers) == 1 - assert all("sms" == prov.notification_type for prov in sms_providers) + assert all(NotificationType.SMS == prov.notification_type for prov in sms_providers) assert all(prov.supports_international for prov in sms_providers) def test_can_get_sms_providers_in_order_of_priority(notify_db_session): - providers = get_provider_details_by_notification_type("sms", False) + providers = get_provider_details_by_notification_type(NotificationType.SMS, False) priorities = [provider.priority for provider in providers] assert priorities == sorted(priorities) def test_can_get_email_providers_in_order_of_priority(notify_db_session): - providers = get_provider_details_by_notification_type("email") + providers = get_provider_details_by_notification_type(NotificationType.EMAIL) assert providers[0].identifier == "ses" def test_can_get_email_providers(notify_db_session): - assert len(get_provider_details_by_notification_type("email")) == 1 + assert len(get_provider_details_by_notification_type(NotificationType.EMAIL)) == 1 types = [ provider.notification_type - for provider in get_provider_details_by_notification_type("email") + for provider in get_provider_details_by_notification_type(NotificationType.EMAIL) ] - assert all("email" == notification_type for notification_type in types) + assert all(NotificationType.EMAIL == notification_type for notification_type in types) def test_should_not_error_if_any_provider_in_code_not_in_database( @@ -222,8 +223,8 @@ def test_get_sms_providers_for_update_returns_nothing_if_recent_updates( def test_dao_get_provider_stats(notify_db_session): service_1 = create_service(service_name="1") service_2 = create_service(service_name="2") - sms_template_1 = create_template(service_1, "sms") - sms_template_2 = create_template(service_2, "sms") + sms_template_1 = create_template(service_1, TemplateType.SMS) + sms_template_2 = create_template(service_2, TemplateType.SMS) create_ft_billing("2017-06-05", sms_template_2, provider="sns", billable_unit=4) create_ft_billing("2018-06-03", sms_template_2, provider="sns", billable_unit=4) @@ -241,7 +242,7 @@ def test_dao_get_provider_stats(notify_db_session): assert ses.current_month_billable_sms == 0 assert sns.display_name == "AWS SNS" - assert sns.notification_type == "sms" + assert sns.notification_type == NotificationType.SMS assert sns.supports_international is True assert sns.active is True assert sns.current_month_billable_sms == 5 diff --git a/tests/app/dao/test_service_data_retention_dao.py b/tests/app/dao/test_service_data_retention_dao.py index 3296a9147..9ddc6263e 100644 --- a/tests/app/dao/test_service_data_retention_dao.py +++ b/tests/app/dao/test_service_data_retention_dao.py @@ -11,13 +11,14 @@ from app.dao.service_data_retention_dao import ( insert_service_data_retention, update_service_data_retention, ) +from app.enums import NotificationType from app.models import ServiceDataRetention from tests.app.db import create_service, create_service_data_retention def test_fetch_service_data_retention(sample_service): - email_data_retention = insert_service_data_retention(sample_service.id, "email", 3) - sms_data_retention = insert_service_data_retention(sample_service.id, "sms", 5) + email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) + sms_data_retention = insert_service_data_retention(sample_service.id, NotificationType.SMS, 5,) list_of_data_retention = fetch_service_data_retention(sample_service.id) @@ -28,8 +29,8 @@ def test_fetch_service_data_retention(sample_service): def test_fetch_service_data_retention_only_returns_row_for_service(sample_service): another_service = create_service(service_name="Another service") - email_data_retention = insert_service_data_retention(sample_service.id, "email", 3) - insert_service_data_retention(another_service.id, "sms", 5) + email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) + insert_service_data_retention(another_service.id, NotificationType.SMS, 5) list_of_data_retention = fetch_service_data_retention(sample_service.id) assert len(list_of_data_retention) == 1 @@ -45,7 +46,7 @@ def test_fetch_service_data_retention_returns_empty_list_when_no_rows_for_servic def test_fetch_service_data_retention_by_id(sample_service): email_data_retention = insert_service_data_retention(sample_service.id, "email", 3) - insert_service_data_retention(sample_service.id, "sms", 13) + insert_service_data_retention(sample_service.id, NotificationType.SMS, 13) result = fetch_service_data_retention_by_id( sample_service.id, email_data_retention.id ) @@ -61,7 +62,7 @@ def test_fetch_service_data_retention_by_id_returns_none_if_id_not_for_service( sample_service, ): another_service = create_service(service_name="Another service") - email_data_retention = insert_service_data_retention(sample_service.id, "email", 3) + email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) result = fetch_service_data_retention_by_id( another_service.id, email_data_retention.id ) @@ -70,30 +71,30 @@ def test_fetch_service_data_retention_by_id_returns_none_if_id_not_for_service( def test_insert_service_data_retention(sample_service): insert_service_data_retention( - service_id=sample_service.id, notification_type="email", days_of_retention=3 + service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=3, ) results = ServiceDataRetention.query.all() assert len(results) == 1 assert results[0].service_id == sample_service.id - assert results[0].notification_type == "email" + assert results[0].notification_type == NotificationType.EMAIL assert results[0].days_of_retention == 3 assert results[0].created_at.date() == datetime.utcnow().date() def test_insert_service_data_retention_throws_unique_constraint(sample_service): insert_service_data_retention( - service_id=sample_service.id, notification_type="email", days_of_retention=3 + service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=3, ) with pytest.raises(expected_exception=IntegrityError): insert_service_data_retention( - service_id=sample_service.id, notification_type="email", days_of_retention=5 + service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=5, ) def test_update_service_data_retention(sample_service): data_retention = insert_service_data_retention( - service_id=sample_service.id, notification_type="sms", days_of_retention=3 + service_id=sample_service.id, notification_type=NotificationType.SMS, days_of_retention=3 ) updated_count = update_service_data_retention( service_data_retention_id=data_retention.id, @@ -105,7 +106,7 @@ def test_update_service_data_retention(sample_service): assert len(results) == 1 assert results[0].id == data_retention.id assert results[0].service_id == sample_service.id - assert results[0].notification_type == "sms" + assert results[0].notification_type == NotificationType.SMS assert results[0].days_of_retention == 5 assert results[0].created_at.date() == datetime.utcnow().date() assert results[0].updated_at.date() == datetime.utcnow().date() @@ -127,7 +128,7 @@ def test_update_service_data_retention_does_not_update_row_if_data_retention_is_ sample_service, ): data_retention = insert_service_data_retention( - service_id=sample_service.id, notification_type="email", days_of_retention=3 + service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=3, ) updated_count = update_service_data_retention( service_data_retention_id=data_retention.id, @@ -138,7 +139,7 @@ def test_update_service_data_retention_does_not_update_row_if_data_retention_is_ @pytest.mark.parametrize( - "notification_type, alternate", [("sms", "email"), ("email", "sms")] + "notification_type, alternate", [(NotificationType.SMS, NotificationType.EMAIL), (NotificationType.EMAIL, NotificationType.SMS),], ) def test_fetch_service_data_retention_by_notification_type( sample_service, notification_type, alternate @@ -157,5 +158,5 @@ def test_fetch_service_data_retention_by_notification_type_returns_none_when_no_ sample_service, ): assert not fetch_service_data_retention_by_notification_type( - sample_service.id, "email" + sample_service.id, NotificationType.EMAIL, ) From 63aa12e4efb036ed6a856e8f469de6522aef8e89 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 13 Feb 2024 11:04:02 -0500 Subject: [PATCH 189/259] More cleanup. Signed-off-by: Cliff Hill --- tests/app/billing/test_rest.py | 4 +- tests/app/celery/test_nightly_tasks.py | 8 +- tests/app/celery/test_reporting_tasks.py | 63 +++-- tests/app/celery/test_scheduled_tasks.py | 28 ++- .../app/celery/test_service_callback_tasks.py | 19 +- tests/app/celery/test_tasks.py | 56 ++++- tests/app/conftest.py | 2 +- tests/app/dao/test_fact_billing_dao.py | 161 +++++++++--- .../dao/test_fact_notification_status_dao.py | 232 ++++++++++++++---- tests/app/dao/test_inbound_sms_dao.py | 18 +- tests/app/dao/test_provider_details_dao.py | 12 +- .../dao/test_service_data_retention_dao.py | 53 +++- tests/app/dao/test_services_dao.py | 201 ++++++++++----- tests/app/test_model.py | 6 +- tests/app/v2/templates/test_get_templates.py | 2 - 15 files changed, 662 insertions(+), 203 deletions(-) diff --git a/tests/app/billing/test_rest.py b/tests/app/billing/test_rest.py index 5d503b019..e1dfc33d3 100644 --- a/tests/app/billing/test_rest.py +++ b/tests/app/billing/test_rest.py @@ -174,7 +174,9 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se email_rows = [row for row in json_response if row["notification_type"] == "email"] assert len(email_rows) == 0 - sms_row = next(x for x in json_response if x["notification_type"] == NotificationType.SMS) + sms_row = next( + x for x in json_response if x["notification_type"] == NotificationType.SMS + ) assert sms_row["month"] == "January" assert sms_row["notification_type"] == NotificationType.SMS diff --git a/tests/app/celery/test_nightly_tasks.py b/tests/app/celery/test_nightly_tasks.py index f4eddc58b..0e98d0f48 100644 --- a/tests/app/celery/test_nightly_tasks.py +++ b/tests/app/celery/test_nightly_tasks.py @@ -278,8 +278,12 @@ def test_delete_notifications_task_calls_task_for_services_with_data_retention_o letter_service = create_service(service_name="c") create_service_data_retention(sms_service, notification_type=NotificationType.SMS) - create_service_data_retention(email_service, notification_type=NotificationType.EMAIL) - create_service_data_retention(letter_service, notification_type=NotificationType.LETTER) + create_service_data_retention( + email_service, notification_type=NotificationType.EMAIL + ) + create_service_data_retention( + letter_service, notification_type=NotificationType.LETTER + ) mock_subtask = mocker.patch( "app.celery.nightly_tasks.delete_notifications_for_service_and_type" diff --git a/tests/app/celery/test_reporting_tasks.py b/tests/app/celery/test_reporting_tasks.py index cec90c473..296d1a1c0 100644 --- a/tests/app/celery/test_reporting_tasks.py +++ b/tests/app/celery/test_reporting_tasks.py @@ -415,25 +415,38 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio first_service = create_service(service_name="First Service") first_template = create_template(service=first_service) second_service = create_service(service_name="second Service") - second_template = create_template(service=second_service, template_type=TemplateType.EMAIL,) + second_template = create_template( + service=second_service, + template_type=TemplateType.EMAIL, + ) process_day = datetime.utcnow().date() - timedelta(days=5) with freeze_time(datetime.combine(process_day, time.max)): - create_notification(template=first_template, status=NotificationStatus.DELIVERED,) + create_notification( + template=first_template, + status=NotificationStatus.DELIVERED, + ) create_notification(template=second_template, status=NotificationStatus.FAILED) # team API key notifications are included create_notification( - template=second_template, status=NotificationStatus.SENDING, key_type=KeyType.TEAM, + template=second_template, + status=NotificationStatus.SENDING, + key_type=KeyType.TEAM, ) # test notifications are ignored create_notification( - template=second_template, status=NotificationStatus.SENDING, key_type=KeyType.TEST, + template=second_template, + status=NotificationStatus.SENDING, + key_type=KeyType.TEST, ) # historical notifications are included - create_notification_history(template=second_template, status=NotificationStatus.DELIVERED,) + create_notification_history( + template=second_template, + status=NotificationStatus.DELIVERED, + ) # these created notifications from a different day get ignored with freeze_time( @@ -445,10 +458,14 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert len(FactNotificationStatus.query.all()) == 0 create_nightly_notification_status_for_service_and_day( - str(process_day), first_service.id, NotificationType.SMS, + str(process_day), + first_service.id, + NotificationType.SMS, ) create_nightly_notification_status_for_service_and_day( - str(process_day), second_service.id, NotificationType.EMAIL, + str(process_day), + second_service.id, + NotificationType.EMAIL, ) new_fact_data = FactNotificationStatus.query.order_by( @@ -501,9 +518,13 @@ def test_create_nightly_notification_status_for_service_and_day_overwrites_old_d process_day = datetime.utcnow().date() # first run: one notification, expect one row (just one status) - notification = create_notification(template=first_template, status=NotificationStatus.SENDING) + notification = create_notification( + template=first_template, status=NotificationStatus.SENDING + ) create_nightly_notification_status_for_service_and_day( - str(process_day), first_service.id, NotificationType.SMS, + str(process_day), + first_service.id, + NotificationType.SMS, ) new_fact_data = FactNotificationStatus.query.all() @@ -516,7 +537,9 @@ def test_create_nightly_notification_status_for_service_and_day_overwrites_old_d notification.status = NotificationStatus.DELIVERED create_notification(template=first_template, status=NotificationStatus.CREATED) create_nightly_notification_status_for_service_and_day( - str(process_day), first_service.id, NotificationType.SMS, + str(process_day), + first_service.id, + NotificationType.SMS, ) updated_fact_data = FactNotificationStatus.query.order_by( @@ -536,22 +559,32 @@ def test_create_nightly_notification_status_for_service_and_day_respects_bst( sample_template, ): create_notification( - sample_template, status=NotificationStatus.DELIVERED, created_at=datetime(2019, 4, 2, 5, 0), + sample_template, + status=NotificationStatus.DELIVERED, + created_at=datetime(2019, 4, 2, 5, 0), ) # too new create_notification( - sample_template, status=NotificationStatus.CREATED, created_at=datetime(2019, 4, 2, 5, 59), + sample_template, + status=NotificationStatus.CREATED, + created_at=datetime(2019, 4, 2, 5, 59), ) create_notification( - sample_template, status=NotificationStatus.CREATED, created_at=datetime(2019, 4, 1, 4, 0), + sample_template, + status=NotificationStatus.CREATED, + created_at=datetime(2019, 4, 1, 4, 0), ) create_notification( - sample_template, status=NotificationStatus.DELIVERED, created_at=datetime(2019, 3, 21, 17, 59), + sample_template, + status=NotificationStatus.DELIVERED, + created_at=datetime(2019, 3, 21, 17, 59), ) # too old create_nightly_notification_status_for_service_and_day( - "2019-04-01", sample_template.service_id, NotificationType.SMS, + "2019-04-01", + sample_template.service_id, + NotificationType.SMS, ) noti_status = FactNotificationStatus.query.order_by( diff --git a/tests/app/celery/test_scheduled_tasks.py b/tests/app/celery/test_scheduled_tasks.py index 0472e9fdb..94b586a3a 100644 --- a/tests/app/celery/test_scheduled_tasks.py +++ b/tests/app/celery/test_scheduled_tasks.py @@ -101,7 +101,9 @@ def test_should_update_scheduled_jobs_and_put_on_queue(mocker, sample_template): one_minute_in_the_past = datetime.utcnow() - timedelta(minutes=1) job = create_job( - sample_template, job_status=JobStatus.SCHEDULED, scheduled_for=one_minute_in_the_past + sample_template, + job_status=JobStatus.SCHEDULED, + scheduled_for=one_minute_in_the_past, ) run_scheduled_jobs() @@ -118,10 +120,14 @@ def test_should_update_all_scheduled_jobs_and_put_on_queue(sample_template, mock ten_minutes_in_the_past = datetime.utcnow() - timedelta(minutes=10) twenty_minutes_in_the_past = datetime.utcnow() - timedelta(minutes=20) job_1 = create_job( - sample_template, job_status=JobStatus.SCHEDULED, scheduled_for=one_minute_in_the_past, + sample_template, + job_status=JobStatus.SCHEDULED, + scheduled_for=one_minute_in_the_past, ) job_2 = create_job( - sample_template, job_status=JobStatus.SCHEDULED, scheduled_for=ten_minutes_in_the_past, + sample_template, + job_status=JobStatus.SCHEDULED, + scheduled_for=ten_minutes_in_the_past, ) job_3 = create_job( sample_template, @@ -299,8 +305,12 @@ def test_replay_created_notifications(notify_db_session, sample_service, mocker) "app.celery.provider_tasks.deliver_sms.apply_async" ) - sms_template = create_template(service=sample_service, template_type=TemplateType.SMS) - email_template = create_template(service=sample_service, template_type=TemplateType.EMAIL) + sms_template = create_template( + service=sample_service, template_type=TemplateType.SMS + ) + email_template = create_template( + service=sample_service, template_type=TemplateType.EMAIL + ) older_than = (60 * 60) + (60 * 15) # 1 hour 15 minutes # notifications expected to be resent old_sms = create_notification( @@ -325,10 +335,14 @@ def test_replay_created_notifications(notify_db_session, sample_service, mocker) status=NotificationStatus.DELIVERED, ) create_notification( - template=sms_template, created_at=datetime.utcnow(), status=NotificationStatus.CREATED, + template=sms_template, + created_at=datetime.utcnow(), + status=NotificationStatus.CREATED, ) create_notification( - template=email_template, created_at=datetime.utcnow(), status=NotificationStatus.CREATED, + template=email_template, + created_at=datetime.utcnow(), + status=NotificationStatus.CREATED, ) replay_created_notifications() diff --git a/tests/app/celery/test_service_callback_tasks.py b/tests/app/celery/test_service_callback_tasks.py index 5dd7f18ca..af1e8abd4 100644 --- a/tests/app/celery/test_service_callback_tasks.py +++ b/tests/app/celery/test_service_callback_tasks.py @@ -21,7 +21,9 @@ from tests.app.db import ( ) -@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS]) +@pytest.mark.parametrize( + "notification_type", [NotificationType.EMAIL, NotificationType.SMS] +) def test_send_delivery_status_to_service_post_https_request_to_service_with_encrypted_data( notify_db_session, notification_type ): @@ -103,7 +105,10 @@ def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted ] == "Bearer {}".format(callback_api.bearer_token) -@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS],) +@pytest.mark.parametrize( + "notification_type", + [NotificationType.EMAIL, NotificationType.SMS], +) @pytest.mark.parametrize("status_code", [429, 500, 503]) def test__send_data_to_service_callback_api_retries_if_request_returns_error_code_with_encrypted_data( notify_db_session, mocker, notification_type, status_code @@ -131,7 +136,10 @@ def test__send_data_to_service_callback_api_retries_if_request_returns_error_cod assert mocked.call_args[1]["queue"] == "service-callbacks-retry" -@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS],) +@pytest.mark.parametrize( + "notification_type", + [NotificationType.EMAIL, NotificationType.SMS], +) def test__send_data_to_service_callback_api_does_not_retry_if_request_returns_404_with_encrypted_data( notify_db_session, mocker, notification_type ): @@ -160,7 +168,10 @@ def test__send_data_to_service_callback_api_does_not_retry_if_request_returns_40 def test_send_delivery_status_to_service_succeeds_if_sent_at_is_none( notify_db_session, mocker ): - callback_api, template = _set_up_test_data(NotificationType.EMAIL, CallbackType.DELIVERY_STATUS,) + callback_api, template = _set_up_test_data( + NotificationType.EMAIL, + CallbackType.DELIVERY_STATUS, + ) datestr = datetime(2017, 6, 20) notification = create_notification( template=template, diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index bda1837a3..7d61e28ad 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -475,7 +475,11 @@ def test_save_email_should_save_default_email_reply_to_text_on_notification( create_reply_to_email( service=service, email_address="reply_to@digital.fake.gov", is_default=True ) - template = create_template(service=service, template_type=TemplateType.EMAIL, subject="Hello",) + template = create_template( + service=service, + template_type=TemplateType.EMAIL, + subject="Hello", + ) notification = _notification_json(template, to="test@example.com") mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") @@ -536,7 +540,11 @@ def test_should_not_save_email_if_restricted_service_and_invalid_email_address( ): user = create_user() service = create_service(user=user, restricted=True) - template = create_template(service=service, template_type=TemplateType.EMAIL, subject="Hello",) + template = create_template( + service=service, + template_type=TemplateType.EMAIL, + subject="Hello", + ) notification = _notification_json(template, to="test@example.com") notification_id = uuid.uuid4() @@ -551,7 +559,10 @@ def test_should_not_save_email_if_restricted_service_and_invalid_email_address( def test_should_save_sms_template_to_and_persist_with_job_id(sample_job, mocker): notification = _notification_json( - sample_job.template, to="+447234123123", job_id=sample_job.id, row_number=2, + sample_job.template, + to="+447234123123", + job_id=sample_job.id, + row_number=2, ) mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") @@ -726,11 +737,15 @@ def test_save_email_uses_the_reply_to_text_when_provided(sample_email_template, service = sample_email_template.service notification_id = uuid.uuid4() service_email_reply_to_dao.add_reply_to_email_address_for_service( - service.id, "default@example.com", True, + service.id, + "default@example.com", + True, ) other_email_reply_to = ( service_email_reply_to_dao.add_reply_to_email_address_for_service( - service.id, "other@example.com", False, + service.id, + "other@example.com", + False, ) ) @@ -754,7 +769,9 @@ def test_save_email_uses_the_default_reply_to_text_if_sender_id_is_none( service = sample_email_template.service notification_id = uuid.uuid4() service_email_reply_to_dao.add_reply_to_email_address_for_service( - service.id, "default@example.com", True, + service.id, + "default@example.com", + True, ) save_email( @@ -925,7 +942,9 @@ def test_save_sms_uses_non_default_sms_sender_reply_to_text_if_provided( service = create_service_with_defined_sms_sender(sms_sender_value="2028675309") template = create_template(service=service) new_sender = service_sms_sender_dao.dao_add_sms_sender_for_service( - service.id, "new-sender", False, + service.id, + "new-sender", + False, ) notification = _notification_json(template, to="202-867-5301") @@ -1377,7 +1396,10 @@ def test_process_incomplete_jobs_sets_status_to_in_progress_and_resets_processin @freeze_time("2020-03-25 14:30") -@pytest.mark.parametrize("notification_type", [NotificationType.SMS, NotificationType.EMAIL],) +@pytest.mark.parametrize( + "notification_type", + [NotificationType.SMS, NotificationType.EMAIL], +) def test_save_api_email_or_sms(mocker, sample_service, notification_type): template = ( create_template(sample_service) @@ -1427,7 +1449,9 @@ def test_save_api_email_or_sms(mocker, sample_service, notification_type): @freeze_time("2020-03-25 14:30") -@pytest.mark.parametrize("notification_type", [NotificationType.SMS, NotificationType.EMAIL]) +@pytest.mark.parametrize( + "notification_type", [NotificationType.SMS, NotificationType.EMAIL] +) def test_save_api_email_dont_retry_if_notification_already_exists( sample_service, mocker, notification_type ): @@ -1549,8 +1573,18 @@ def test_save_tasks_use_cached_service_and_template( @pytest.mark.parametrize( "notification_type, task_function, expected_queue, recipient", ( - (NotificationType.SMS, save_api_sms, QueueNames.SEND_SMS, "+447700900855",), - (NotificationType.EMAIL, save_api_email, QueueNames.SEND_EMAIL, "jane.citizen@example.com",), + ( + NotificationType.SMS, + save_api_sms, + QueueNames.SEND_SMS, + "+447700900855", + ), + ( + NotificationType.EMAIL, + save_api_email, + QueueNames.SEND_EMAIL, + "jane.citizen@example.com", + ), ), ) def test_save_api_tasks_use_cache( diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 2f550c1d4..44e93b98b 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -1,7 +1,7 @@ import json -from types import CodeType import uuid from datetime import datetime, timedelta +from types import CodeType import pytest import pytz diff --git a/tests/app/dao/test_fact_billing_dao.py b/tests/app/dao/test_fact_billing_dao.py index 41a3dd485..85d40c3e6 100644 --- a/tests/app/dao/test_fact_billing_dao.py +++ b/tests/app/dao/test_fact_billing_dao.py @@ -22,7 +22,7 @@ from app.dao.fact_billing_dao import ( ) from app.dao.organization_dao import dao_add_service_to_organization from app.enums import NotificationStatus, NotificationType, TemplateType -from app.models import FactBilling, Notification +from app.models import FactBilling from tests.app.db import ( create_annual_billing, create_ft_billing, @@ -95,7 +95,11 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_key_type( service = create_service() template = create_template(service=service, template_type=TemplateType.EMAIL) for key_type in ["normal", "test", "team"]: - create_notification(template=template, status=NotificationStatus.DELIVERED, key_type=key_type,) + create_notification( + template=template, + status=NotificationStatus.DELIVERED, + key_type=key_type, + ) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -103,7 +107,9 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_key_type( assert results[0].notifications_sent == 2 -@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS]) +@pytest.mark.parametrize( + "notification_type", [NotificationType.EMAIL, NotificationType.SMS] +) def test_fetch_billing_data_for_day_only_calls_query_for_permission_type( notify_db_session, notification_type ): @@ -119,7 +125,10 @@ def test_fetch_billing_data_for_day_only_calls_query_for_permission_type( assert len(results) == 1 -@pytest.mark.parametrize("notification_type", [NotificationType.EMAIL, NotificationType.SMS],) +@pytest.mark.parametrize( + "notification_type", + [NotificationType.EMAIL, NotificationType.SMS], +) def test_fetch_billing_data_for_day_only_calls_query_for_all_channels( notify_db_session, notification_type ): @@ -130,7 +139,8 @@ def test_fetch_billing_data_for_day_only_calls_query_for_all_channels( create_notification(template=sms_template, status=NotificationStatus.DELIVERED) today = datetime.utcnow() results = fetch_billing_data_for_day( - process_day=today.date(), check_permissions=False, + process_day=today.date(), + check_permissions=False, ) assert len(results) == 2 @@ -142,7 +152,11 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_date( process_day = datetime(2018, 4, 1, 13, 30, 0) service = create_service() template = create_template(service=service, template_type=TemplateType.EMAIL) - create_notification(template=template, status=NotificationStatus.DELIVERED, created_at=process_day,) + create_notification( + template=template, + status=NotificationStatus.DELIVERED, + created_at=process_day, + ) create_notification( template=template, status=NotificationStatus.DELIVERED, @@ -155,7 +169,12 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_date( created_at=datetime(2018, 4, 1, 0, 23, 23), ) create_notification( - template=template, status=NotificationStatus.SENDING, created_at=process_day + timedelta(days=1,) + template=template, + status=NotificationStatus.SENDING, + created_at=process_day + + timedelta( + days=1, + ), ) day_under_test = process_day @@ -198,8 +217,16 @@ def test_fetch_billing_data_for_day_is_grouped_by_service(notify_db_session): def test_fetch_billing_data_for_day_is_grouped_by_provider(notify_db_session): service = create_service() template = create_template(service=service) - create_notification(template=template, status=NotificationStatus.DELIVERED, sent_by="sns",) - create_notification(template=template, status=NotificationStatus.DELIVERED, sent_by="sns",) + create_notification( + template=template, + status=NotificationStatus.DELIVERED, + sent_by="sns", + ) + create_notification( + template=template, + status=NotificationStatus.DELIVERED, + sent_by="sns", + ) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -211,8 +238,16 @@ def test_fetch_billing_data_for_day_is_grouped_by_provider(notify_db_session): def test_fetch_billing_data_for_day_is_grouped_by_rate_mulitplier(notify_db_session): service = create_service() template = create_template(service=service) - create_notification(template=template, status=NotificationStatus.DELIVERED, rate_multiplier=1,) - create_notification(template=template, status=NotificationStatus.DELIVERED, rate_multiplier=2,) + create_notification( + template=template, + status=NotificationStatus.DELIVERED, + rate_multiplier=1, + ) + create_notification( + template=template, + status=NotificationStatus.DELIVERED, + rate_multiplier=2, + ) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -224,8 +259,16 @@ def test_fetch_billing_data_for_day_is_grouped_by_rate_mulitplier(notify_db_sess def test_fetch_billing_data_for_day_is_grouped_by_international(notify_db_session): service = create_service() sms_template = create_template(service=service) - create_notification(template=sms_template, status=NotificationStatus.DELIVERED, international=True,) - create_notification(template=sms_template, status=NotificationStatus.DELIVERED, international=False,) + create_notification( + template=sms_template, + status=NotificationStatus.DELIVERED, + international=True, + ) + create_notification( + template=sms_template, + status=NotificationStatus.DELIVERED, + international=False, + ) today = datetime.utcnow() results = fetch_billing_data_for_day(today.date()) @@ -266,10 +309,14 @@ def test_fetch_billing_data_for_day_uses_correct_table(notify_db_session): five_days_ago = datetime.utcnow() - timedelta(days=5) create_notification( - template=sms_template, status=NotificationStatus.DELIVERED, created_at=five_days_ago, + template=sms_template, + status=NotificationStatus.DELIVERED, + created_at=five_days_ago, ) create_notification_history( - template=email_template, status=NotificationStatus.DELIVERED, created_at=five_days_ago, + template=email_template, + status=NotificationStatus.DELIVERED, + created_at=five_days_ago, ) results = fetch_billing_data_for_day( @@ -311,16 +358,24 @@ def test_fetch_billing_data_for_day_bills_correctly_for_status(notify_db_session ) sms_results = [x for x in results if x.notification_type == NotificationType.SMS] - email_results = [x for x in results if x.notification_type == NotificationType.EMAIL] + email_results = [ + x for x in results if x.notification_type == NotificationType.EMAIL + ] # we expect as many rows as we check for notification types assert 6 == sms_results[0].notifications_sent assert 4 == email_results[0].notifications_sent def test_get_rates_for_billing(notify_db_session): - create_rate(start_date=datetime.utcnow(), value=12, notification_type=NotificationType.EMAIL) - create_rate(start_date=datetime.utcnow(), value=22, notification_type=NotificationType.SMS) - create_rate(start_date=datetime.utcnow(), value=33, notification_type=NotificationType.EMAIL) + create_rate( + start_date=datetime.utcnow(), value=12, notification_type=NotificationType.EMAIL + ) + create_rate( + start_date=datetime.utcnow(), value=22, notification_type=NotificationType.SMS + ) + create_rate( + start_date=datetime.utcnow(), value=33, notification_type=NotificationType.EMAIL + ) rates = get_rates_for_billing() assert len(rates) == 3 @@ -329,17 +384,25 @@ def test_get_rates_for_billing(notify_db_session): @freeze_time("2017-06-01 12:00") def test_get_rate(notify_db_session): create_rate( - start_date=datetime(2017, 5, 30, 23, 0), value=1.2, notification_type=NotificationType.EMAIL, + start_date=datetime(2017, 5, 30, 23, 0), + value=1.2, + notification_type=NotificationType.EMAIL, ) create_rate( - start_date=datetime(2017, 5, 30, 23, 0), value=2.2, notification_type=NotificationType.SMS, + start_date=datetime(2017, 5, 30, 23, 0), + value=2.2, + notification_type=NotificationType.SMS, ) create_rate( - start_date=datetime(2017, 5, 30, 23, 0), value=3.3, notification_type=NotificationType.EMAIL, + start_date=datetime(2017, 5, 30, 23, 0), + value=3.3, + notification_type=NotificationType.EMAIL, ) rates = get_rates_for_billing() - rate = get_rate(rates, notification_type=NotificationType.SMS, date=date(2017, 6, 1)) + rate = get_rate( + rates, notification_type=NotificationType.SMS, date=date(2017, 6, 1) + ) assert rate == 2.2 @@ -351,10 +414,14 @@ def test_get_rate_chooses_right_rate_depending_on_date( notify_db_session, date, expected_rate ): create_rate( - start_date=datetime(2016, 1, 1, 0, 0), value=1.2, notification_type=NotificationType.SMS, + start_date=datetime(2016, 1, 1, 0, 0), + value=1.2, + notification_type=NotificationType.SMS, ) create_rate( - start_date=datetime(2018, 9, 30, 23, 0), value=2.2, notification_type=NotificationType.SMS, + start_date=datetime(2018, 9, 30, 23, 0), + value=2.2, + notification_type=NotificationType.SMS, ) rates = get_rates_for_billing() @@ -397,7 +464,9 @@ def test_fetch_monthly_billing_for_year(notify_db_session): def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session): service = set_up_yearly_data_variable_rates() create_annual_billing( - service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018, + service_id=service.id, + free_sms_fragment_limit=6, + financial_year_start=2018, ) results = fetch_monthly_billing_for_year(service.id, 2018) @@ -501,7 +570,9 @@ def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_ses def test_fetch_billing_totals_for_year_variable_rates(notify_db_session): service = set_up_yearly_data_variable_rates() create_annual_billing( - service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018, + service_id=service.id, + free_sms_fragment_limit=6, + financial_year_start=2018, ) results = fetch_billing_totals_for_year(service_id=service.id, year=2018) @@ -558,13 +629,21 @@ def test_fetch_sms_free_allowance_remainder_until_date_with_two_services( org = create_organization(name="Org for {}".format(service.name)) dao_add_service_to_organization(service=service, organization_id=org.id) create_annual_billing( - service_id=service.id, free_sms_fragment_limit=10, financial_year_start=2016, + service_id=service.id, + free_sms_fragment_limit=10, + financial_year_start=2016, ) create_ft_billing( - template=template, local_date=datetime(2016, 4, 20), billable_unit=2, rate=0.11, + template=template, + local_date=datetime(2016, 4, 20), + billable_unit=2, + rate=0.11, ) create_ft_billing( - template=template, local_date=datetime(2016, 5, 20), billable_unit=3, rate=0.11, + template=template, + local_date=datetime(2016, 5, 20), + billable_unit=3, + rate=0.11, ) service_2 = create_service(service_name="used free allowance") @@ -693,7 +772,9 @@ def test_fetch_sms_billing_for_all_services_with_remainder(notify_db_session): ) service_4 = create_service(service_name="d - email only") - email_template = create_template(service=service_4, template_type=TemplateType.EMAIL) + email_template = create_template( + service=service_4, template_type=TemplateType.EMAIL + ) org_4 = create_organization(name="Org for {}".format(service_4.name)) dao_add_service_to_organization(service=service_4, organization_id=org_4.id) create_annual_billing( @@ -827,13 +908,17 @@ def test_fetch_usage_year_for_organization(notify_db_session): financial_year_start=2019, ) dao_add_service_to_organization( - service=service_with_emails_for_org, organization_id=fixtures["org_1"].id, + service=service_with_emails_for_org, + organization_id=fixtures["org_1"].id, ) template = create_template( - service=service_with_emails_for_org, template_type=TemplateType.EMAIL, + service=service_with_emails_for_org, + template_type=TemplateType.EMAIL, ) create_ft_billing( - local_date=datetime(2019, 5, 1), template=template, notifications_sent=1100, + local_date=datetime(2019, 5, 1), + template=template, + notifications_sent=1100, ) results = fetch_usage_year_for_organization(fixtures["org_1"].id, 2019) @@ -1039,8 +1124,12 @@ def test_fetch_usage_year_for_organization_only_returns_data_for_live_services( live_service = create_service(restricted=False) sms_template = create_template(service=live_service) trial_service = create_service(restricted=True, service_name="trial_service") - email_template = create_template(service=trial_service, template_type=TemplateType.EMAIL) - trial_sms_template = create_template(service=trial_service, template_type=TemplateType.SMS) + email_template = create_template( + service=trial_service, template_type=TemplateType.EMAIL + ) + trial_sms_template = create_template( + service=trial_service, template_type=TemplateType.SMS + ) dao_add_service_to_organization(service=live_service, organization_id=org.id) dao_add_service_to_organization(service=trial_service, organization_id=org.id) create_ft_billing( diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index be750cb83..99181e811 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -255,23 +255,52 @@ def test_fetch_notification_status_by_template_for_service_for_today_and_7_previ # create unused email template create_template(service=service_1, template_type=TemplateType.EMAIL) - create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=10,) - create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=11,) - create_ft_notification_status(date(2018, 10, 25), NotificationType.SMS, service_1, count=8,) create_ft_notification_status( - date(2018, 10, 29), NotificationType.SMS, service_1, notification_status=NotificationStatus.CREATED, + date(2018, 10, 29), + NotificationType.SMS, + service_1, + count=10, + ) + create_ft_notification_status( + date(2018, 10, 29), + NotificationType.SMS, + service_1, + count=11, + ) + create_ft_notification_status( + date(2018, 10, 25), + NotificationType.SMS, + service_1, + count=8, + ) + create_ft_notification_status( + date(2018, 10, 29), + NotificationType.SMS, + service_1, + notification_status=NotificationStatus.CREATED, + ) + create_ft_notification_status( + date(2018, 10, 29), + NotificationType.EMAIL, + service_1, + count=3, ) - create_ft_notification_status(date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3,) create_notification(sms_template, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, + sms_template, + created_at=datetime(2018, 10, 31, 12, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - sms_template_2, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, + sms_template_2, + created_at=datetime(2018, 10, 31, 12, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status=NotificationStatus.DELIVERED, + email_template, + created_at=datetime(2018, 10, 31, 13, 0, 0), + status=NotificationStatus.DELIVERED, ) # too early, shouldn't be included @@ -282,19 +311,83 @@ def test_fetch_notification_status_by_template_for_service_for_today_and_7_previ ) results = fetch_notification_status_for_service_for_today_and_7_previous_days( - service_1.id, by_template=True, + service_1.id, + by_template=True, ) assert [ - ("email Template Name", False, mock.ANY, NotificationType.EMAIL, NotificationStatus.DELIVERED, 1,), - ("email Template Name", False, mock.ANY, NotificationType.EMAIL, NotificationStatus.DELIVERED, 3,), - ("sms Template 1", False, mock.ANY, NotificationType.SMS, NotificationStatus.CREATED, 1,), - ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.CREATED, 1,), - ("sms Template 1", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 1,), - ("sms Template 2", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 1,), - ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 8,), - ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 10,), - ("sms Template Name", False, mock.ANY, NotificationType.SMS, NotificationStatus.DELIVERED, 11,), + ( + "email Template Name", + False, + mock.ANY, + NotificationType.EMAIL, + NotificationStatus.DELIVERED, + 1, + ), + ( + "email Template Name", + False, + mock.ANY, + NotificationType.EMAIL, + NotificationStatus.DELIVERED, + 3, + ), + ( + "sms Template 1", + False, + mock.ANY, + NotificationType.SMS, + NotificationStatus.CREATED, + 1, + ), + ( + "sms Template Name", + False, + mock.ANY, + NotificationType.SMS, + NotificationStatus.CREATED, + 1, + ), + ( + "sms Template 1", + False, + mock.ANY, + NotificationType.SMS, + NotificationStatus.DELIVERED, + 1, + ), + ( + "sms Template 2", + False, + mock.ANY, + NotificationType.SMS, + NotificationStatus.DELIVERED, + 1, + ), + ( + "sms Template Name", + False, + mock.ANY, + NotificationType.SMS, + NotificationStatus.DELIVERED, + 8, + ), + ( + "sms Template Name", + False, + mock.ANY, + NotificationType.SMS, + NotificationStatus.DELIVERED, + 10, + ), + ( + "sms Template Name", + False, + mock.ANY, + NotificationType.SMS, + NotificationStatus.DELIVERED, + 11, + ), ] == sorted( results, key=lambda x: (x.notification_type, x.status, x.template_name, x.count) ) @@ -352,19 +445,29 @@ def test_fetch_notification_status_totals_for_all_services_works_in_est( ) create_notification( - sms_template, created_at=datetime(2018, 4, 20, 12, 0, 0), status=NotificationStatus.DELIVERED, + sms_template, + created_at=datetime(2018, 4, 20, 12, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - sms_template, created_at=datetime(2018, 4, 21, 11, 0, 0), status=NotificationStatus.CREATED, + sms_template, + created_at=datetime(2018, 4, 21, 11, 0, 0), + status=NotificationStatus.CREATED, ) create_notification( - sms_template, created_at=datetime(2018, 4, 21, 12, 0, 0), status=NotificationStatus.DELIVERED, + sms_template, + created_at=datetime(2018, 4, 21, 12, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 4, 21, 13, 0, 0), status=NotificationStatus.DELIVERED, + email_template, + created_at=datetime(2018, 4, 21, 13, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 4, 21, 14, 0, 0), status=NotificationStatus.DELIVERED, + email_template, + created_at=datetime(2018, 4, 21, 14, 0, 0), + status=NotificationStatus.DELIVERED, ) results = sorted( @@ -396,12 +499,27 @@ def set_up_data(): email_template = create_template( service=service_1, template_type=TemplateType.EMAIL ) - create_ft_notification_status(date(2018, 10, 24), NotificationType.SMS, service_1, count=8,) - create_ft_notification_status(date(2018, 10, 29), NotificationType.SMS, service_1, count=10,) create_ft_notification_status( - date(2018, 10, 29), NotificationType.SMS, service_1, notification_status=NotificationStatus.CREATED, + date(2018, 10, 24), + NotificationType.SMS, + service_1, + count=8, + ) + create_ft_notification_status( + date(2018, 10, 29), + NotificationType.SMS, + service_1, + count=10, + ) + create_ft_notification_status( + date(2018, 10, 29), + NotificationType.SMS, + service_1, + notification_status=NotificationStatus.CREATED, + ) + create_ft_notification_status( + date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3 ) - create_ft_notification_status(date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3) create_notification( service_1.templates[0], @@ -410,10 +528,14 @@ def set_up_data(): ) create_notification(sms_template, created_at=datetime(2018, 10, 31, 11, 0, 0)) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status=NotificationStatus.DELIVERED, + sms_template, + created_at=datetime(2018, 10, 31, 12, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status=NotificationStatus.DELIVERED, + email_template, + created_at=datetime(2018, 10, 31, 13, 0, 0), + status=NotificationStatus.DELIVERED, ) return service_1, service_2 @@ -423,16 +545,28 @@ def test_fetch_notification_statuses_for_job(sample_template): j2 = create_job(sample_template) create_ft_notification_status( - date(2018, 10, 1), job=j1, notification_status=NotificationStatus.CREATED, count=1, + date(2018, 10, 1), + job=j1, + notification_status=NotificationStatus.CREATED, + count=1, ) create_ft_notification_status( - date(2018, 10, 1), job=j1, notification_status=NotificationStatus.DELIVERED, count=2, + date(2018, 10, 1), + job=j1, + notification_status=NotificationStatus.DELIVERED, + count=2, ) create_ft_notification_status( - date(2018, 10, 2), job=j1, notification_status=NotificationStatus.CREATED, count=4, + date(2018, 10, 2), + job=j1, + notification_status=NotificationStatus.CREATED, + count=4, ) create_ft_notification_status( - date(2018, 10, 1), job=j2, notification_status=NotificationStatus.CREATED, count=8, + date(2018, 10, 1), + job=j2, + notification_status=NotificationStatus.CREATED, + count=8, ) assert {x.status: x.count for x in fetch_notification_statuses_for_job(j1.id)} == { @@ -473,10 +607,14 @@ def test_fetch_stats_for_all_services_by_date_range(notify_db_session): @freeze_time("2018-03-30 14:00") def test_fetch_monthly_template_usage_for_service(sample_service): template_one = create_template( - service=sample_service, template_type=TemplateType.SMS, template_name="a", + service=sample_service, + template_type=TemplateType.SMS, + template_name="a", ) template_two = create_template( - service=sample_service, template_type=TemplateType.EMAIL, template_name="b", + service=sample_service, + template_type=TemplateType.EMAIL, + template_name="b", ) create_ft_notification_status( @@ -548,10 +686,14 @@ def test_fetch_monthly_template_usage_for_service_does_join_to_notifications_if_ sample_service, ): template_one = create_template( - service=sample_service, template_type=TemplateType.SMS, template_name="a", + service=sample_service, + template_type=TemplateType.SMS, + template_name="a", ) template_two = create_template( - service=sample_service, template_type=TmplateType.EMAIL, template_name="b", + service=sample_service, + template_type=TemplateType.EMAIL, + template_name="b", ) create_ft_notification_status( local_date=date(2018, 2, 1), @@ -604,7 +746,9 @@ def test_fetch_monthly_template_usage_for_service_does_not_include_cancelled_sta count=15, ) create_notification( - template=sample_template, created_at=datetime.utcnow(), status=NotificationStatus.CANCELLED, + template=sample_template, + created_at=datetime.utcnow(), + status=NotificationStatus.CANCELLED, ) results = fetch_monthly_template_usage_for_service( datetime(2018, 1, 1), datetime(2018, 3, 31), sample_template.service_id @@ -632,7 +776,9 @@ def test_fetch_monthly_template_usage_for_service_does_not_include_test_notifica key_type=KeyType.TEST, ) results = fetch_monthly_template_usage_for_service( - datetime(2018, 1, 1), datetime(2018, 3, 31), sample_template.service_id, + datetime(2018, 1, 1), + datetime(2018, 3, 31), + sample_template.service_id, ) assert len(results) == 0 @@ -821,10 +967,14 @@ def test_fetch_monthly_notification_statuses_per_service_for_rows_that_should_be def test_get_total_notifications_for_date_range(sample_service): template_sms = create_template( - service=sample_service, template_type=TemplateType.SMS, template_name="a", + service=sample_service, + template_type=TemplateType.SMS, + template_name="a", ) template_email = create_template( - service=sample_service, template_type=TemplateType.EMAIL, template_name="b", + service=sample_service, + template_type=TemplateType.EMAIL, + template_name="b", ) create_ft_notification_status( local_date=date(2021, 2, 28), diff --git a/tests/app/dao/test_inbound_sms_dao.py b/tests/app/dao/test_inbound_sms_dao.py index a53180965..9f3d6738d 100644 --- a/tests/app/dao/test_inbound_sms_dao.py +++ b/tests/app/dao/test_inbound_sms_dao.py @@ -66,10 +66,12 @@ def test_get_all_inbound_sms_filters_on_service(notify_db_session): def test_get_all_inbound_sms_filters_on_time(sample_service, notify_db_session): create_inbound_sms( - sample_service, created_at=datetime(2017, 8, 6, 23, 59), + sample_service, + created_at=datetime(2017, 8, 6, 23, 59), ) # sunday evening sms_two = create_inbound_sms( - sample_service, created_at=datetime(2017, 8, 7, 0, 0), + sample_service, + created_at=datetime(2017, 8, 7, 0, 0), ) # monday (7th) morning with freeze_time("2017-08-14 12:00"): @@ -111,13 +113,19 @@ def test_should_delete_inbound_sms_according_to_data_retention(notify_db_session services = [short_retention_service, no_retention_service, long_retention_service] create_service_data_retention( - long_retention_service, notification_type=NotificationType.SMS, days_of_retention=30, + long_retention_service, + notification_type=NotificationType.SMS, + days_of_retention=30, ) create_service_data_retention( - short_retention_service, notification_type=NotificationType.SMS, days_of_retention=3, + short_retention_service, + notification_type=NotificationType.SMS, + days_of_retention=3, ) create_service_data_retention( - short_retention_service, notification_type=NotificationType.EMAIL, days_of_retention=4, + short_retention_service, + notification_type=NotificationType.EMAIL, + days_of_retention=4, ) dates = [ diff --git a/tests/app/dao/test_provider_details_dao.py b/tests/app/dao/test_provider_details_dao.py index aa81ed0c0..8af524fa6 100644 --- a/tests/app/dao/test_provider_details_dao.py +++ b/tests/app/dao/test_provider_details_dao.py @@ -46,7 +46,9 @@ def test_can_get_sms_non_international_providers(notify_db_session): def test_can_get_sms_international_providers(notify_db_session): - sms_providers = get_provider_details_by_notification_type(NotificationType.SMS, True) + sms_providers = get_provider_details_by_notification_type( + NotificationType.SMS, True + ) assert len(sms_providers) == 1 assert all(NotificationType.SMS == prov.notification_type for prov in sms_providers) assert all(prov.supports_international for prov in sms_providers) @@ -68,9 +70,13 @@ def test_can_get_email_providers(notify_db_session): assert len(get_provider_details_by_notification_type(NotificationType.EMAIL)) == 1 types = [ provider.notification_type - for provider in get_provider_details_by_notification_type(NotificationType.EMAIL) + for provider in get_provider_details_by_notification_type( + NotificationType.EMAIL + ) ] - assert all(NotificationType.EMAIL == notification_type for notification_type in types) + assert all( + NotificationType.EMAIL == notification_type for notification_type in types + ) def test_should_not_error_if_any_provider_in_code_not_in_database( diff --git a/tests/app/dao/test_service_data_retention_dao.py b/tests/app/dao/test_service_data_retention_dao.py index 9ddc6263e..f800bd464 100644 --- a/tests/app/dao/test_service_data_retention_dao.py +++ b/tests/app/dao/test_service_data_retention_dao.py @@ -17,8 +17,16 @@ from tests.app.db import create_service, create_service_data_retention def test_fetch_service_data_retention(sample_service): - email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) - sms_data_retention = insert_service_data_retention(sample_service.id, NotificationType.SMS, 5,) + email_data_retention = insert_service_data_retention( + sample_service.id, + NotificationType.EMAIL, + 3, + ) + sms_data_retention = insert_service_data_retention( + sample_service.id, + NotificationType.SMS, + 5, + ) list_of_data_retention = fetch_service_data_retention(sample_service.id) @@ -29,7 +37,11 @@ def test_fetch_service_data_retention(sample_service): def test_fetch_service_data_retention_only_returns_row_for_service(sample_service): another_service = create_service(service_name="Another service") - email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) + email_data_retention = insert_service_data_retention( + sample_service.id, + NotificationType.EMAIL, + 3, + ) insert_service_data_retention(another_service.id, NotificationType.SMS, 5) list_of_data_retention = fetch_service_data_retention(sample_service.id) @@ -62,7 +74,11 @@ def test_fetch_service_data_retention_by_id_returns_none_if_id_not_for_service( sample_service, ): another_service = create_service(service_name="Another service") - email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) + email_data_retention = insert_service_data_retention( + sample_service.id, + NotificationType.EMAIL, + 3, + ) result = fetch_service_data_retention_by_id( another_service.id, email_data_retention.id ) @@ -71,7 +87,9 @@ def test_fetch_service_data_retention_by_id_returns_none_if_id_not_for_service( def test_insert_service_data_retention(sample_service): insert_service_data_retention( - service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=3, + service_id=sample_service.id, + notification_type=NotificationType.EMAIL, + days_of_retention=3, ) results = ServiceDataRetention.query.all() @@ -84,17 +102,23 @@ def test_insert_service_data_retention(sample_service): def test_insert_service_data_retention_throws_unique_constraint(sample_service): insert_service_data_retention( - service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=3, + service_id=sample_service.id, + notification_type=NotificationType.EMAIL, + days_of_retention=3, ) with pytest.raises(expected_exception=IntegrityError): insert_service_data_retention( - service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=5, + service_id=sample_service.id, + notification_type=NotificationType.EMAIL, + days_of_retention=5, ) def test_update_service_data_retention(sample_service): data_retention = insert_service_data_retention( - service_id=sample_service.id, notification_type=NotificationType.SMS, days_of_retention=3 + service_id=sample_service.id, + notification_type=NotificationType.SMS, + days_of_retention=3, ) updated_count = update_service_data_retention( service_data_retention_id=data_retention.id, @@ -128,7 +152,9 @@ def test_update_service_data_retention_does_not_update_row_if_data_retention_is_ sample_service, ): data_retention = insert_service_data_retention( - service_id=sample_service.id, notification_type=NotificationType.EMAIL, days_of_retention=3, + service_id=sample_service.id, + notification_type=NotificationType.EMAIL, + days_of_retention=3, ) updated_count = update_service_data_retention( service_data_retention_id=data_retention.id, @@ -139,7 +165,11 @@ def test_update_service_data_retention_does_not_update_row_if_data_retention_is_ @pytest.mark.parametrize( - "notification_type, alternate", [(NotificationType.SMS, NotificationType.EMAIL), (NotificationType.EMAIL, NotificationType.SMS),], + "notification_type, alternate", + [ + (NotificationType.SMS, NotificationType.EMAIL), + (NotificationType.EMAIL, NotificationType.SMS), + ], ) def test_fetch_service_data_retention_by_notification_type( sample_service, notification_type, alternate @@ -158,5 +188,6 @@ def test_fetch_service_data_retention_by_notification_type_returns_none_when_no_ sample_service, ): assert not fetch_service_data_retention_by_notification_type( - sample_service.id, NotificationType.EMAIL, + sample_service.id, + NotificationType.EMAIL, ) diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 44be3c50e..990315260 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -40,7 +40,15 @@ from app.dao.services_dao import ( get_services_by_partial_name, ) from app.dao.users_dao import create_user_code, save_model_user -from app.enums import KeyType, OrganizationType +from app.enums import ( + CodeType, + KeyType, + NotificationStatus, + NotificationType, + OrganizationType, + ServicePermissionType, + TemplateType, +) from app.models import ( ApiKey, InvitedUser, @@ -51,7 +59,6 @@ from app.models import ( Permission, Service, ServicePermission, - ServicePermissionType, ServiceUser, Template, TemplateHistory, @@ -766,7 +773,9 @@ def test_update_service_permission_creates_a_history_record_with_current_data( ), ) - permission = [p for p in service.permissions if p.permission == "sms"][0] + permission = [ + p for p in service.permissions if p.permission == ServicePermissionType.SMS + ][0] service.permissions.remove(permission) dao_update_service(service) @@ -824,8 +833,8 @@ def test_delete_service_and_associated_objects(notify_db_session): service = create_service( user=user, service_permissions=None, organization=organization ) - create_user_code(user=user, code="somecode", code_type="email") - create_user_code(user=user, code="somecode", code_type="sms") + create_user_code(user=user, code="somecode", code_type=CodeType.EMAIL) + create_user_code(user=user, code="somecode", code_type=CodeType.SMS) template = create_template(service=service) api_key = create_api_key(service=service) create_notification(template=template, api_key=api_key) @@ -953,27 +962,29 @@ def test_fetch_stats_ignores_historical_notification_data(sample_template): def test_dao_fetch_todays_stats_for_service(notify_db_session): service = create_service() sms_template = create_template(service=service) - email_template = create_template(service=service, template_type="email") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) # two created email, one failed email, and one created sms - create_notification(template=email_template, status="created") - create_notification(template=email_template, status="created") - create_notification(template=email_template, status="technical-failure") - create_notification(template=sms_template, status="created") + create_notification(template=email_template, status=NotificationStatus.CREATED) + create_notification(template=email_template, status=NotificationStatus.CREATED) + create_notification( + template=email_template, status=NotificationStatus.TECHNICAL_FAILURE + ) + create_notification(template=sms_template, status=NotificationStatus.CREATED) stats = dao_fetch_todays_stats_for_service(service.id) stats = sorted(stats, key=lambda x: (x.notification_type, x.status)) assert len(stats) == 3 - assert stats[0].notification_type == "email" - assert stats[0].status == "created" + assert stats[0].notification_type == NotificationType.EMAIL + assert stats[0].status == NotificationStatus.CREATED assert stats[0].count == 2 - assert stats[1].notification_type == "email" - assert stats[1].status == "technical-failure" + assert stats[1].notification_type == NotificationType.EMAIL + assert stats[1].status == NotificationStatus.TECHNICAL_FAILURE assert stats[1].count == 1 - assert stats[2].notification_type == "sms" - assert stats[2].status == "created" + assert stats[2].notification_type == NotificationType.SMS + assert stats[2].status == NotificationStatus.CREATED assert stats[2].count == 1 @@ -998,8 +1009,8 @@ def test_dao_fetch_todays_stats_for_service_should_ignore_test_key(notify_db_ses stats = dao_fetch_todays_stats_for_service(service.id) assert len(stats) == 1 - assert stats[0].notification_type == "sms" - assert stats[0].status == "created" + assert stats[0].notification_type == NotificationType.SMS + assert stats[0].status == NotificationStatus.CREATED assert stats[0].count == 3 @@ -1008,15 +1019,27 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today(notify_db_sessio # two created email, one failed email, and one created sms with freeze_time("2001-01-02T04:59:00"): # just_before_midnight_yesterday - create_notification(template=template, to_field="1", status="delivered") + create_notification( + template=template, + to_field="1", + status=NotificationStatus.DELIVERED, + ) with freeze_time("2001-01-02T05:01:00"): # just_after_midnight_today - create_notification(template=template, to_field="2", status="failed") + create_notification( + template=template, + to_field="2", + status=NotificationStatus.FAILED, + ) with freeze_time("2001-01-02T12:00:00"): # right_now - create_notification(template=template, to_field="3", status="created") + create_notification( + template=template, + to_field="3", + status=NotificationStatus.CREATED, + ) stats = dao_fetch_todays_stats_for_service(template.service_id) @@ -1033,14 +1056,26 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today_when_clocks_spri template = create_template(service=create_service()) with freeze_time("2021-03-27T23:59:59"): # just before midnight yesterday in UTC -- not included - create_notification(template=template, to_field="1", status="permanent-failure") + create_notification( + template=template, + to_field="1", + status=NotificationStatus.PERMANENT_FAILURE, + ) with freeze_time("2021-03-28T00:01:00"): # just after midnight yesterday in UTC -- included - create_notification(template=template, to_field="2", status="failed") + create_notification( + template=template, + to_field="2", + status=NotificationStatus.FAILED, + ) with freeze_time("2021-03-28T12:00:00"): # we have entered BST at this point but had not for the previous two notifications --included # collect stats for this timestamp - create_notification(template=template, to_field="3", status="created") + create_notification( + template=template, + to_field="3", + status=NotificationStatus.CREATED, + ) stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} @@ -1057,14 +1092,20 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today_during_bst( template = create_template(service=create_service()) with freeze_time("2021-03-28T23:59:59"): # just before midnight BST -- not included - create_notification(template=template, to_field="1", status="permanent-failure") + create_notification( + template=template, to_field="1", status=NotificationStatus.PERMANENT_FAILURE + ) with freeze_time("2021-03-29T04:00:01"): # just after midnight BST -- included - create_notification(template=template, to_field="2", status="failed") + create_notification( + template=template, to_field="2", status=NotificationStatus.FAILED + ) with freeze_time("2021-03-29T12:00:00"): # well after midnight BST -- included # collect stats for this timestamp - create_notification(template=template, to_field="3", status="created") + create_notification( + template=template, to_field="3", status=NotificationStatus.CREATED + ) stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} @@ -1080,15 +1121,21 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today_when_clocks_fall template = create_template(service=create_service()) with freeze_time("2021-10-30T22:59:59"): # just before midnight BST -- not included - create_notification(template=template, to_field="1", status="permanent-failure") + create_notification( + template=template, to_field="1", status=NotificationStatus.PERMANENT_FAILURE + ) with freeze_time("2021-10-31T23:00:01"): # just after midnight BST -- included - create_notification(template=template, to_field="2", status="failed") + create_notification( + template=template, to_field="2", status=NotificationStatus.FAILED + ) # clocks go back to UTC on 31 October at 2am with freeze_time("2021-10-31T12:00:00"): # well after midnight -- included # collect stats for this timestamp - create_notification(template=template, to_field="3", status="created") + create_notification( + template=template, to_field="3", status=NotificationStatus.CREATED + ) stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} @@ -1102,15 +1149,21 @@ def test_dao_fetch_todays_stats_for_service_only_includes_during_utc(notify_db_s template = create_template(service=create_service()) with freeze_time("2021-10-30T12:59:59"): # just before midnight UTC -- not included - create_notification(template=template, to_field="1", status="permanent-failure") + create_notification( + template=template, to_field="1", status=NotificationStatus.PERMANENT_FAILURE + ) with freeze_time("2021-10-31T05:00:01"): # just after midnight UTC -- included - create_notification(template=template, to_field="2", status="failed") + create_notification( + template=template, to_field="2", status=NotificationStatus.FAILED + ) # clocks go back to UTC on 31 October at 2am with freeze_time("2021-10-31T12:00:00"): # well after midnight -- included # collect stats for this timestamp - create_notification(template=template, to_field="3", status="created") + create_notification( + template=template, to_field="3", status=NotificationStatus.CREATED + ) stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} @@ -1126,10 +1179,14 @@ def test_dao_fetch_todays_stats_for_all_services_includes_all_services( # two services, each with an email and sms notification service1 = create_service(service_name="service 1", email_from="service.1") service2 = create_service(service_name="service 2", email_from="service.2") - template_email_one = create_template(service=service1, template_type="email") - template_sms_one = create_template(service=service1, template_type="sms") - template_email_two = create_template(service=service2, template_type="email") - template_sms_two = create_template(service=service2, template_type="sms") + template_email_one = create_template( + service=service1, template_type=TemplateType.EMAIL + ) + template_sms_one = create_template(service=service1, template_type=TemplateType.SMS) + template_email_two = create_template( + service=service2, template_type=TemplateType.EMAIL + ) + template_sms_two = create_template(service=service2, template_type=TemplateType.SMS) create_notification(template=template_email_one) create_notification(template=template_sms_one) create_notification(template=template_email_two) @@ -1146,11 +1203,15 @@ def test_dao_fetch_todays_stats_for_all_services_only_includes_today(notify_db_s template = create_template(service=create_service()) with freeze_time("2001-01-01T23:59:00"): # just_before_midnight_yesterday - create_notification(template=template, to_field="1", status="delivered") + create_notification( + template=template, to_field="1", status=NotificationStatus.DELIVERED + ) with freeze_time("2001-01-02T05:01:00"): # just_after_midnight_today - create_notification(template=template, to_field="2", status="failed") + create_notification( + template=template, to_field="2", status=NotificationStatus.FAILED + ) with freeze_time("2001-01-02T12:00:00"): stats = dao_fetch_todays_stats_for_all_services() @@ -1164,12 +1225,12 @@ def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db_sess service1 = create_service(service_name="service 1", email_from="service.1") service2 = create_service(service_name="service 2", email_from="service.2") template_sms = create_template(service=service1) - template_email = create_template(service=service1, template_type="email") + template_email = create_template(service=service1, template_type=TemplateType.EMAIL) template_two = create_template(service=service2) # service1: 2 sms with status "created" and one "failed", and one email create_notification(template=template_sms) create_notification(template=template_sms) - create_notification(template=template_sms, status="failed") + create_notification(template=template_sms, status=NotificationStatus.FAILED) create_notification(template=template_email) # service2: 1 sms "created" create_notification(template=template_two) @@ -1182,8 +1243,8 @@ def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db_sess service1.restricted, service1.active, service1.created_at, - "sms", - "created", + NotificationType.SMS, + NotificationStatus.CREATED, 2, ) in stats assert ( @@ -1192,8 +1253,8 @@ def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db_sess service1.restricted, service1.active, service1.created_at, - "sms", - "failed", + NotificationType.SMS, + NotificationStatus.FAILED, 1, ) in stats assert ( @@ -1202,8 +1263,8 @@ def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db_sess service1.restricted, service1.active, service1.created_at, - "email", - "created", + NotificationType.EMAIL, + NotificationStatus.CREATED, 1, ) in stats assert ( @@ -1212,8 +1273,8 @@ def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db_sess service2.restricted, service2.active, service2.created_at, - "sms", - "created", + NotificationType.SMS, + NotificationType.CREATED, 1, ) in stats @@ -1388,7 +1449,9 @@ def test_dao_find_services_sending_to_tv_numbers(notify_db_session, fake_uuid): template = create_template(service) for _ in range(0, 5): create_notification( - template, normalised_to=tv_number, status="permanent-failure" + template, + normalised_to=tv_number, + status=NotificationStatus.PERMANENT_FAILURE, ) service_6 = create_service( @@ -1398,27 +1461,35 @@ def test_dao_find_services_sending_to_tv_numbers(notify_db_session, fake_uuid): template_6 = create_template(service_6) for _ in range(0, 5): create_notification( - template_6, normalised_to=tv_number, status="permanent-failure" + template_6, + normalised_to=tv_number, + status=NotificationStatus.PERMANENT_FAILURE, ) service_2 = create_service(service_name="Service 2") # below threshold is excluded template_2 = create_template(service_2) - create_notification(template_2, normalised_to=tv_number, status="permanent-failure") + create_notification( + template_2, + normalised_to=tv_number, + status=NotificationStatus.PERMANENT_FAILURE, + ) for _ in range(0, 5): # test key type is excluded create_notification( template_2, normalised_to=tv_number, - status="permanent-failure", - key_type="test", + status=NotificationStatus.PERMANENT_FAILURE, + key_type=KeyType.TEST, ) for _ in range(0, 5): # normal numbers are not counted by the query - create_notification(template_2, normalised_to=normal_number, status="delivered") + create_notification( + template_2, normalised_to=normal_number, status=NotificationStatus.DELIVERED + ) create_notification( template_2, normalised_to=normal_number_resembling_tv_number, - status="delivered", + status=NotificationStatus.DELIVERED, ) start_date = datetime.utcnow() - timedelta(days=1) @@ -1442,27 +1513,31 @@ def test_dao_find_services_with_high_failure_rates(notify_db_session, fake_uuid) for service in services: template = create_template(service) for _ in range(0, 3): - create_notification(template, status="permanent-failure") - create_notification(template, status="delivered") - create_notification(template, status="sending") - create_notification(template, status="temporary-failure") + create_notification(template, status=NotificationStatus.PERMANENT_FAILURE) + create_notification(template, status=NotificationStatus.DELIVERED) + create_notification(template, status=NotificationStatus.SENDING) + create_notification(template, status=NotificationStatus.TEMPORART_FAILURE) service_6 = create_service(service_name="Service 6") with freeze_time("2019-11-30 15:00:00.000000"): template_6 = create_template(service_6) for _ in range(0, 4): create_notification( - template_6, status="permanent-failure" + template_6, + status=NotificationStatus.PERMANENT_FAILURE, ) # notifications too old are excluded service_2 = create_service(service_name="Service 2") template_2 = create_template(service_2) for _ in range(0, 4): create_notification( - template_2, status="permanent-failure", key_type="test" + template_2, + status=NotificationStatus.PERMANENT_FAILURE, + key_type=KeyType.TEST, ) # test key type is excluded create_notification( - template_2, status="permanent-failure" + template_2, + status=NotificationStatus.PERMANET_FAILURE, ) # below threshold is excluded start_date = datetime.utcnow() - timedelta(days=1) diff --git a/tests/app/test_model.py b/tests/app/test_model.py index 461056a26..67a83e661 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -150,7 +150,11 @@ def test_notification_for_csv_returns_correct_job_row_number(sample_job): [ (TemplateType.EMAIL, "failed", "Failed"), (TemplateType.EMAIL, "technical-failure", "Technical failure"), - (TemplateType.EMAIL, "temporary-failure", "Inbox not accepting messages right now",), + ( + TemplateType.EMAIL, + "temporary-failure", + "Inbox not accepting messages right now", + ), (TemplateType.EMAIL, "permanent-failure", "Email address doesn’t exist"), ( TemplateType.SMS, diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index 396d663fd..ebed4fbcf 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -1,5 +1,3 @@ -from itertools import product - import pytest from flask import json From 3624cd812bc613508495b610338bdd60841bdc91 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 13 Feb 2024 14:48:10 -0500 Subject: [PATCH 190/259] Stuff was done. Signed-off-by: Cliff Hill --- .../notification_dao/test_notification_dao.py | 515 +++++++++++++----- ...t_notification_dao_delete_notifications.py | 110 ++-- tests/app/dao/test_templates_dao.py | 26 +- tests/app/dao/test_uploads_dao.py | 15 +- tests/app/dao/test_users_dao.py | 3 +- tests/app/delivery/test_send_to_providers.py | 62 +-- tests/app/inbound_sms/test_rest.py | 3 +- 7 files changed, 490 insertions(+), 244 deletions(-) diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index b45940ac6..849adfb45 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -28,7 +28,13 @@ from app.dao.notifications_dao import ( update_notification_status_by_id, update_notification_status_by_reference, ) -from app.enums import JobStatus, KeyType, NotificationStatus, NotificationType +from app.enums import ( + JobStatus, + KeyType, + NotificationStatus, + NotificationType, + TemplateType, +) from app.models import Job, Notification, NotificationHistory from tests.app.db import ( create_ft_notification_status, @@ -43,18 +49,22 @@ from tests.app.db import ( def test_should_by_able_to_update_status_by_reference( sample_email_template, ses_provider ): - data = _notification_json(sample_email_template, status="sending") + data = _notification_json(sample_email_template, status=NotificationStatus.SENDING) notification = Notification(**data) dao_create_notification(notification) - assert Notification.query.get(notification.id).status == "sending" + assert Notification.query.get(notification.id).status == NotificationStatus.SENDING notification.reference = "reference" dao_update_notification(notification) - updated = update_notification_status_by_reference("reference", "delivered") - assert updated.status == "delivered" - assert Notification.query.get(notification.id).status == "delivered" + updated = update_notification_status_by_reference( + "reference", NotificationStatus.DELIVERED + ) + assert updated.status == NotificationStatus.DELIVERED + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) def test_should_by_able_to_update_status_by_id( @@ -62,33 +72,48 @@ def test_should_by_able_to_update_status_by_id( ): with freeze_time("2000-01-01 12:00:00"): data = _notification_json( - sample_template, job_id=sample_job.id, status="sending" + sample_template, + job_id=sample_job.id, + status=NotificationStatus.SENDING, ) notification = Notification(**data) dao_create_notification(notification) - assert notification.status == "sending" + assert notification.status == NotificationStatus.SENDING - assert Notification.query.get(notification.id).status == "sending" + assert Notification.query.get(notification.id).status == NotificationStatus.SENDING with freeze_time("2000-01-02 12:00:00"): - updated = update_notification_status_by_id(notification.id, "delivered") + updated = update_notification_status_by_id( + notification.id, + NotificationStatus.DELIVERED, + ) - assert updated.status == "delivered" + assert updated.status == NotificationStatus.DELIVERED assert updated.updated_at == datetime(2000, 1, 2, 12, 0, 0) - assert Notification.query.get(notification.id).status == "delivered" + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) assert notification.updated_at == datetime(2000, 1, 2, 12, 0, 0) - assert notification.status == "delivered" + assert notification.status == NotificationStatus.DELIVERED def test_should_not_update_status_by_id_if_not_sending_and_does_not_update_job( sample_job, ): notification = create_notification( - template=sample_job.template, status="delivered", job=sample_job + template=sample_job.template, + status=NotificationStatus.DELIVERED, + job=sample_job, + ) + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) + assert not update_notification_status_by_id( + notification.id, NotificationStatus.FAILED + ) + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED ) - assert Notification.query.get(notification.id).status == "delivered" - assert not update_notification_status_by_id(notification.id, "failed") - assert Notification.query.get(notification.id).status == "delivered" assert sample_job == Job.query.get(notification.job_id) @@ -97,30 +122,49 @@ def test_should_not_update_status_by_reference_if_not_sending_and_does_not_updat ): notification = create_notification( template=sample_job.template, - status="delivered", + status=NotificationStatus.DELIVERED, reference="reference", job=sample_job, ) - assert Notification.query.get(notification.id).status == "delivered" - assert not update_notification_status_by_reference("reference", "failed") - assert Notification.query.get(notification.id).status == "delivered" + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) + assert not update_notification_status_by_reference( + "reference", NotificationStatus.FAILED + ) + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) assert sample_job == Job.query.get(notification.job_id) def test_should_update_status_by_id_if_created(sample_template, sample_notification): - assert Notification.query.get(sample_notification.id).status == "created" - updated = update_notification_status_by_id(sample_notification.id, "failed") - assert Notification.query.get(sample_notification.id).status == "failed" - assert updated.status == "failed" + assert ( + Notification.query.get(sample_notification.id).status + == NotificationStatus.CREATED + ) + updated = update_notification_status_by_id( + sample_notification.id, + NotificationStatus.FAILED, + ) + assert ( + Notification.query.get(sample_notification.id).status + == NotificationStatus.FAILED + ) + assert updated.status == NotificationStatus.FAILED def test_should_update_status_by_id_and_set_sent_by(sample_template): - notification = create_notification(template=sample_template, status="sending") + notification = create_notification( + template=sample_template, status=NotificationStatus.SENDING + ) updated = update_notification_status_by_id( - notification.id, "delivered", sent_by="sns" + notification.id, + NotificationStatus.DELIVERED, + sent_by="sns", ) - assert updated.status == "delivered" + assert updated.status == NotificationStatus.DELIVERED assert updated.sent_by == "sns" @@ -131,7 +175,7 @@ def test_should_not_update_status_by_reference_if_from_country_with_no_delivery_ sample_template, status=NotificationStatus.SENT, reference="foo" ) - res = update_notification_status_by_reference("foo", "failed") + res = update_notification_status_by_reference("foo", NotificationStatus.FAILED) assert res is None assert notification.status == NotificationStatus.SENT @@ -147,7 +191,9 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_unknown_delivery phone_prefix="249", # sudan has no delivery receipts (or at least, that we know about) ) - res = update_notification_status_by_id(notification.id, "delivered") + res = update_notification_status_by_id( + notification.id, NotificationStatus.DELIVERED + ) assert res is None assert notification.status == NotificationStatus.SENT @@ -163,7 +209,10 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_carrier_delivery phone_prefix="1", # americans only have carrier delivery receipts ) - res = update_notification_status_by_id(notification.id, "delivered") + res = update_notification_status_by_id( + notification.id, + NotificationStatus.DELIVERED, + ) assert res is None assert notification.status == NotificationStatus.SENT @@ -179,7 +228,10 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_delivery_receipt phone_prefix="7", # russians have full delivery receipts ) - res = update_notification_status_by_id(notification.id, "delivered") + res = update_notification_status_by_id( + notification.id, + NotificationStatus.DELIVERED, + ) assert res == notification assert notification.status == NotificationStatus.DELIVERED @@ -187,11 +239,15 @@ def test_should_not_update_status_by_id_if_sent_to_country_with_delivery_receipt def test_should_not_update_status_by_reference_if_not_sending(sample_template): notification = create_notification( - template=sample_template, status="created", reference="reference" + template=sample_template, + status=NotificationStatus.CREATED, + reference="reference", ) - assert Notification.query.get(notification.id).status == "created" - updated = update_notification_status_by_reference("reference", "failed") - assert Notification.query.get(notification.id).status == "created" + assert Notification.query.get(notification.id).status == NotificationStatus.CREATED + updated = update_notification_status_by_reference( + "reference", NotificationStatus.FAILED + ) + assert Notification.query.get(notification.id).status == NotificationStatus.CREATED assert not updated @@ -199,72 +255,119 @@ def test_should_by_able_to_update_status_by_id_from_pending_to_delivered( sample_template, sample_job ): notification = create_notification( - template=sample_template, job=sample_job, status="sending" + template=sample_template, + job=sample_job, + status=NotificationStatus.SENDING, ) assert update_notification_status_by_id( - notification_id=notification.id, status="pending" + notification_id=notification.id, status=NotificationStatus.PENDING ) - assert Notification.query.get(notification.id).status == "pending" + assert Notification.query.get(notification.id).status == NotificationStatus.PENDING - assert update_notification_status_by_id(notification.id, "delivered") - assert Notification.query.get(notification.id).status == "delivered" + assert update_notification_status_by_id( + notification.id, + NotificationStatus.DELIVERED, + ) + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) def test_should_by_able_to_update_status_by_id_from_pending_to_temporary_failure( sample_template, sample_job ): notification = create_notification( - template=sample_template, job=sample_job, status="sending", sent_by="sns" + template=sample_template, + job=sample_job, + status=NotificationStatus.SENDING, + sent_by="sns", ) assert update_notification_status_by_id( - notification_id=notification.id, status="pending" + notification_id=notification.id, + status=NotificationStatus.PENDING, ) - assert Notification.query.get(notification.id).status == "pending" + assert Notification.query.get(notification.id).status == NotificationStatus.PENDING - assert update_notification_status_by_id(notification.id, status="permanent-failure") + assert update_notification_status_by_id( + notification.id, + status=NotificationStatus.PERMANENT_FAILURE, + ) - assert Notification.query.get(notification.id).status == "temporary-failure" + assert ( + Notification.query.get(notification.id).status + == NotificationStatus.TEMPORARY_FAILURE + ) def test_should_by_able_to_update_status_by_id_from_sending_to_permanent_failure( sample_template, sample_job ): - data = _notification_json(sample_template, job_id=sample_job.id, status="sending") + data = _notification_json( + sample_template, + job_id=sample_job.id, + status=NotificationStatus.SENDING, + ) notification = Notification(**data) dao_create_notification(notification) - assert Notification.query.get(notification.id).status == "sending" + assert Notification.query.get(notification.id).status == NotificationStatus.SENDING - assert update_notification_status_by_id(notification.id, status="permanent-failure") - assert Notification.query.get(notification.id).status == "permanent-failure" + assert update_notification_status_by_id( + notification.id, + status=NotificationStatus.PERMANENT_FAILURE, + ) + assert ( + Notification.query.get(notification.id).status + == NotificationStatus.PERMANENT_FAILURE + ) def test_should_not_update_status_once_notification_status_is_delivered( sample_email_template, ): - notification = create_notification(template=sample_email_template, status="sending") - assert Notification.query.get(notification.id).status == "sending" + notification = create_notification( + template=sample_email_template, + status=NotificationStatus.SENDING, + ) + assert Notification.query.get(notification.id).status == NotificationStatus.SENDING notification.reference = "reference" dao_update_notification(notification) - update_notification_status_by_reference("reference", "delivered") - assert Notification.query.get(notification.id).status == "delivered" + update_notification_status_by_reference( + "reference", + NotificationStatus.DELIVERED, + ) + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) - update_notification_status_by_reference("reference", "failed") - assert Notification.query.get(notification.id).status == "delivered" + update_notification_status_by_reference( + "reference", + NotificationStatus.FAILED, + ) + assert ( + Notification.query.get(notification.id).status == NotificationStatus.DELIVERED + ) def test_should_return_zero_count_if_no_notification_with_id(): - assert not update_notification_status_by_id(str(uuid.uuid4()), "delivered") + assert not update_notification_status_by_id( + str(uuid.uuid4()), + NotificationStatus.DELIVERED, + ) def test_should_return_zero_count_if_no_notification_with_reference(): - assert not update_notification_status_by_reference("something", "delivered") + assert not update_notification_status_by_reference( + "something", + NotificationStatus.DELIVERED, + ) def test_create_notification_creates_notification_with_personalisation( - sample_template_with_placeholders, sample_job + sample_template_with_placeholders, + sample_job, ): assert Notification.query.count() == 0 @@ -272,7 +375,7 @@ def test_create_notification_creates_notification_with_personalisation( template=sample_template_with_placeholders, job=sample_job, personalisation={"name": "Jo"}, - status="created", + status=NotificationStatus.CREATED, ) assert Notification.query.count() == 1 @@ -284,7 +387,7 @@ def test_create_notification_creates_notification_with_personalisation( assert data.template == notification_from_db.template assert data.template_version == notification_from_db.template_version assert data.created_at == notification_from_db.created_at - assert notification_from_db.status == "created" + assert notification_from_db.status == NotificationStatus.CREATED assert {"name": "Jo"} == notification_from_db.personalisation @@ -305,7 +408,7 @@ def test_save_notification_creates_sms(sample_template, sample_job): assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version assert data["created_at"] == notification_from_db.created_at - assert notification_from_db.status == "created" + assert notification_from_db.status == NotificationStatus.CREATED def test_save_notification_and_create_email(sample_email_template, sample_job): @@ -325,7 +428,7 @@ def test_save_notification_and_create_email(sample_email_template, sample_job): assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version assert data["created_at"] == notification_from_db.created_at - assert notification_from_db.status == "created" + assert notification_from_db.status == NotificationStatus.CREATED def test_save_notification(sample_email_template, sample_job): @@ -363,10 +466,10 @@ def test_update_notification_with_research_mode_service_does_not_create_or_updat assert Notification.query.count() == 1 assert NotificationHistory.query.count() == 0 - notification.status = "delivered" + notification.status = NotificationStatus.DELIVERED dao_update_notification(notification) - assert Notification.query.one().status == "delivered" + assert Notification.query.one().status == NotificationStatus.DELIVERED assert NotificationHistory.query.count() == 0 @@ -402,7 +505,7 @@ def test_save_notification_and_increment_job(sample_template, sample_job, sns_pr assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version assert data["created_at"] == notification_from_db.created_at - assert notification_from_db.status == "created" + assert notification_from_db.status == NotificationStatus.CREATED notification_2 = Notification(**data) dao_create_notification(notification_2) @@ -428,7 +531,7 @@ def test_save_notification_and_increment_correct_job(sample_template, sns_provid assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version assert data["created_at"] == notification_from_db.created_at - assert notification_from_db.status == "created" + assert notification_from_db.status == NotificationStatus.CREATED assert job_1.id != job_2.id @@ -447,13 +550,18 @@ def test_save_notification_with_no_job(sample_template, sns_provider): assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version assert data["created_at"] == notification_from_db.created_at - assert notification_from_db.status == "created" + assert notification_from_db.status == NotificationStatus.CREATED def test_get_notification_with_personalisation_by_id(sample_template): - notification = create_notification(template=sample_template, status="created") + notification = create_notification( + template=sample_template, + status=NotificationStatus.CREATED, + ) notification_from_db = get_notification_with_personalisation( - sample_template.service.id, notification.id, key_type=None + sample_template.service.id, + notification.id, + key_type=None, ) assert notification == notification_from_db @@ -507,7 +615,7 @@ def test_save_notification_no_job_id(sample_template): assert data["service"] == notification_from_db.service assert data["template_id"] == notification_from_db.template_id assert data["template_version"] == notification_from_db.template_version - assert notification_from_db.status == "created" + assert notification_from_db.status == NotificationStatus.CREATED assert data.get("job_id") is None @@ -572,11 +680,11 @@ def test_dao_get_notification_count_for_job_id_returns_zero_for_no_notifications def test_update_notification_sets_status(sample_notification): - assert sample_notification.status == "created" - sample_notification.status = "failed" + assert sample_notification.status == NotificationStatus.CREATED + sample_notification.status = NotificationStatus.FAILED dao_update_notification(sample_notification) notification_from_db = Notification.query.get(sample_notification.id) - assert notification_from_db.status == "failed" + assert notification_from_db.status == NotificationStatus.FAILED @freeze_time("2016-01-10") @@ -589,7 +697,9 @@ def test_should_limit_notifications_return_by_day_limit_plus_one(sample_template past_date = "2016-01-{0:02d} 12:00:00".format(i) with freeze_time(past_date): create_notification( - sample_template, created_at=datetime.utcnow(), status="failed" + sample_template, + created_at=datetime.utcnow(), + status=NotificationStatus.FAILED, ) all_notifications = Notification.query.all() @@ -680,32 +790,56 @@ def _notification_json(sample_template, job_id=None, id=None, status=None): def test_dao_timeout_notifications(sample_template): with freeze_time(datetime.utcnow() - timedelta(minutes=2)): - created = create_notification(sample_template, status="created") - sending = create_notification(sample_template, status="sending") - pending = create_notification(sample_template, status="pending") - delivered = create_notification(sample_template, status="delivered") + created = create_notification( + sample_template, + status=NotificationStatus.CREATED, + ) + sending = create_notification( + sample_template, + status=NotificationStatus.SENDING, + ) + pending = create_notification( + sample_template, + status=NotificationStatus.PENDING, + ) + delivered = create_notification( + sample_template, + status=NotificationStatus.DELIVERED, + ) temporary_failure_notifications = dao_timeout_notifications(datetime.utcnow()) assert len(temporary_failure_notifications) == 2 - assert Notification.query.get(created.id).status == "created" - assert Notification.query.get(sending.id).status == "temporary-failure" - assert Notification.query.get(pending.id).status == "temporary-failure" - assert Notification.query.get(delivered.id).status == "delivered" + assert Notification.query.get(created.id).status == NotificationStatus.CREATED + assert ( + Notification.query.get(sending.id).status + == NotificationStatus.TEMPORARY_FAILURE + ) + assert ( + Notification.query.get(pending.id).status + == NotificationStatus.TEMPORARY_FAILURE + ) + assert Notification.query.get(delivered.id).status == NotificationStatus.DELIVERED def test_dao_timeout_notifications_only_updates_for_older_notifications( sample_template, ): with freeze_time(datetime.utcnow() + timedelta(minutes=10)): - sending = create_notification(sample_template, status="sending") - pending = create_notification(sample_template, status="pending") + sending = create_notification( + sample_template, + status=NotificationStatus.SENDING, + ) + pending = create_notification( + sample_template, + status=NotificationStatus.PENDING, + ) temporary_failure_notifications = dao_timeout_notifications(datetime.utcnow()) assert len(temporary_failure_notifications) == 0 - assert Notification.query.get(sending.id).status == "sending" - assert Notification.query.get(pending.id).status == "pending" + assert Notification.query.get(sending.id).status == NotificationStatus.SENDING + assert Notification.query.get(pending.id).status == NotificationStatus.PENDING def test_should_return_notifications_excluding_jobs_by_default( @@ -821,7 +955,9 @@ def test_get_notifications_with_a_live_api_key_type( sample_job, sample_api_key, sample_team_api_key, sample_test_api_key ): create_notification( - template=sample_job.template, created_at=datetime.utcnow(), job=sample_job + template=sample_job.template, + created_at=datetime.utcnow(), + job=sample_job, ) create_notification( template=sample_job.template, @@ -885,13 +1021,18 @@ def test_get_notifications_with_a_test_api_key_type( # only those created with test API key, no jobs all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, key_type=KeyType.TEST + sample_job.service_id, + limit_days=1, + key_type=KeyType.TEST, ).items assert len(all_notifications) == 1 # only those created with test API key, no jobs, even when requested all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, include_jobs=True, key_type=KeyType.TEST + sample_job.service_id, + limit_days=1, + include_jobs=True, + key_type=KeyType.TEST, ).items assert len(all_notifications) == 1 @@ -900,7 +1041,9 @@ def test_get_notifications_with_a_team_api_key_type( sample_job, sample_api_key, sample_team_api_key, sample_test_api_key ): create_notification( - template=sample_job.template, created_at=datetime.utcnow(), job=sample_job + template=sample_job.template, + created_at=datetime.utcnow(), + job=sample_job, ) create_notification( template=sample_job.template, @@ -923,13 +1066,18 @@ def test_get_notifications_with_a_team_api_key_type( # only those created with team API key, no jobs all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, key_type=KeyType.TEAM + sample_job.service_id, + limit_days=1, + key_type=KeyType.TEAM, ).items assert len(all_notifications) == 1 # only those created with team API key, no jobs, even when requested all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, include_jobs=True, key_type=KeyType.TEAM + sample_job.service_id, + limit_days=1, + include_jobs=True, + key_type=KeyType.TEAM, ).items assert len(all_notifications) == 1 @@ -938,7 +1086,9 @@ def test_should_exclude_test_key_notifications_by_default( sample_job, sample_api_key, sample_team_api_key, sample_test_api_key ): create_notification( - template=sample_job.template, created_at=datetime.utcnow(), job=sample_job + template=sample_job.template, + created_at=datetime.utcnow(), + job=sample_job, ) create_notification( @@ -969,12 +1119,16 @@ def test_should_exclude_test_key_notifications_by_default( assert len(all_notifications) == 2 all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, include_jobs=True + sample_job.service_id, + limit_days=1, + include_jobs=True, ).items assert len(all_notifications) == 3 all_notifications = get_notifications_for_service( - sample_job.service_id, limit_days=1, key_type=KeyType.TEST + sample_job.service_id, + limit_days=1, + key_type=KeyType.TEST, ).items assert len(all_notifications) == 1 @@ -989,10 +1143,13 @@ def test_dao_get_notifications_by_recipient(sample_template): } notification1 = create_notification( - template=sample_template, **recipient_to_search_for + template=sample_template, + **recipient_to_search_for, ) create_notification( - template=sample_template, key_type=KeyType.TEST, **recipient_to_search_for + template=sample_template, + key_type=KeyType.TEST, + **recipient_to_search_for, ) create_notification( template=sample_template, @@ -1008,7 +1165,7 @@ def test_dao_get_notifications_by_recipient(sample_template): results = dao_get_notifications_by_recipient_or_reference( notification1.service_id, recipient_to_search_for["to_field"], - notification_type="sms", + notification_type=NotificationType.SMS, ) assert len(results.items) == 1 @@ -1029,7 +1186,7 @@ def test_dao_get_notifications_by_recipient_is_limited_to_50_results(sample_temp results = dao_get_notifications_by_recipient_or_reference( sample_template.service_id, "447700900855", - notification_type="sms", + notification_type=NotificationType.SMS, page_size=50, ) @@ -1049,7 +1206,9 @@ def test_dao_get_notifications_by_recipient_is_not_case_sensitive( normalised_to="jack@gmail.com", ) results = dao_get_notifications_by_recipient_or_reference( - notification.service_id, search_term, notification_type="email" + notification.service_id, + search_term, + notification_type=NotificationType.EMAIL, ) notification_ids = [notification.id for notification in results.items] @@ -1074,7 +1233,9 @@ def test_dao_get_notifications_by_recipient_matches_partial_emails( normalised_to="jacque@gmail.com", ) results = dao_get_notifications_by_recipient_or_reference( - notification_1.service_id, "ack", notification_type="email" + notification_1.service_id, + "ack", + notification_type=NotificationType.EMAIL, ) notification_ids = [notification.id for notification in results.items] @@ -1128,7 +1289,7 @@ def test_dao_get_notifications_by_recipient_escapes( dao_get_notifications_by_recipient_or_reference( sample_email_template.service_id, search_term, - notification_type="email", + notification_type=NotificationType.EMAIL, ).items ) == expected_result_count @@ -1181,7 +1342,7 @@ def test_dao_get_notifications_by_reference_escapes_special_character( dao_get_notifications_by_recipient_or_reference( sample_email_template.service_id, search_term, - notification_type="email", + notification_type=NotificationType.EMAIL, ).items ) == expected_result_count @@ -1219,7 +1380,9 @@ def test_dao_get_notifications_by_recipient_matches_partial_phone_numbers( normalised_to="+12026785000", ) results = dao_get_notifications_by_recipient_or_reference( - notification_1.service_id, search_term, notification_type="sms" + notification_1.service_id, + search_term, + notification_type=NotificationType.SMS, ) notification_ids = [notification.id for notification in results.items] @@ -1242,7 +1405,9 @@ def test_dao_get_notifications_by_recipient_accepts_invalid_phone_numbers_and_em normalised_to="test@example.com", ) results = dao_get_notifications_by_recipient_or_reference( - notification.service_id, to, notification_type="email" + notification.service_id, + to, + notification_type=NotificationType.EMAIL, ) assert len(results.items) == 0 @@ -1271,7 +1436,9 @@ def test_dao_get_notifications_by_recipient_ignores_spaces(sample_template): ) results = dao_get_notifications_by_recipient_or_reference( - notification1.service_id, "+447700900855", notification_type="sms" + notification1.service_id, + "+447700900855", + notification_type=NotificationType.SMS, ) notification_ids = [notification.id for notification in results.items] @@ -1299,9 +1466,11 @@ def test_dao_get_notifications_by_recipient_searches_across_notification_types( ): service = create_service() sms_template = create_template(service=service) - email_template = create_template(service=service, template_type="email") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) sms = create_notification( - template=sms_template, to_field="202-867-5309", normalised_to="+12028675309" + template=sms_template, + to_field="202-867-5309", + normalised_to="+12028675309", ) email = create_notification( template=email_template, @@ -1310,13 +1479,17 @@ def test_dao_get_notifications_by_recipient_searches_across_notification_types( ) results = dao_get_notifications_by_recipient_or_reference( - service.id, phone_search, notification_type="sms" + service.id, + phone_search, + notification_type=NotificationType.SMS, ) assert len(results.items) == 1 assert results.items[0].id == sms.id results = dao_get_notifications_by_recipient_or_reference( - service.id, email_search, notification_type="email" + service.id, + email_search, + notification_type=NotificationType.EMAIL, ) assert len(results.items) == 1 assert results.items[0].id == email.id @@ -1333,7 +1506,10 @@ def test_dao_get_notifications_by_recipient_searches_across_notification_types( def test_dao_get_notifications_by_reference(notify_db_session): service = create_service() sms_template = create_template(service=service) - email_template = create_template(service=service, template_type="email") + email_template = create_template( + service=service, + template_type=TemplateType.EMAIL, + ) sms = create_notification( template=sms_template, to_field="07711111111", @@ -1364,42 +1540,56 @@ def test_dao_get_notifications_by_reference(notify_db_session): assert results.items[0].id == email.id results = dao_get_notifications_by_recipient_or_reference( - service.id, "077", notification_type="sms" + service.id, + "077", + notification_type=NotificationType.SMS, ) assert len(results.items) == 1 assert results.items[0].id == sms.id results = dao_get_notifications_by_recipient_or_reference( - service.id, "77", notification_type="sms" + service.id, + "77", + notification_type=NotificationType.SMS, ) assert len(results.items) == 1 assert results.items[0].id == sms.id results = dao_get_notifications_by_recipient_or_reference( - service.id, "Aa", notification_type="sms" + service.id, + "Aa", + notification_type=NotificationType.SMS, ) assert len(results.items) == 1 assert results.items[0].id == sms.id results = dao_get_notifications_by_recipient_or_reference( - service.id, "bB", notification_type="sms" + service.id, + "bB", + notification_type=NotificationType.SMS, ) assert len(results.items) == 0 results = dao_get_notifications_by_recipient_or_reference( - service.id, "77", notification_type="email" + service.id, + "77", + notification_type=NotificationType.EMAIL, ) assert len(results.items) == 1 assert results.items[0].id == email.id results = dao_get_notifications_by_recipient_or_reference( - service.id, "Bb", notification_type="email" + service.id, + "Bb", + notification_type=NotificationType.EMAIL, ) assert len(results.items) == 1 assert results.items[0].id == email.id results = dao_get_notifications_by_recipient_or_reference( - service.id, "aA", notification_type="email" + service.id, + "aA", + notification_type=NotificationType.EMAIL, ) assert len(results.items) == 0 @@ -1412,20 +1602,20 @@ def test_dao_get_notifications_by_to_field_filters_status(sample_template): template=sample_template, to_field="+447700900855", normalised_to="447700900855", - status="delivered", + status=NotificationStatus.DELIVERED, ) create_notification( template=sample_template, to_field="+447700900855", normalised_to="447700900855", - status="temporary-failure", + status=NotificationStatus.TEMPORARY_FAILURE, ) notifications = dao_get_notifications_by_recipient_or_reference( notification.service_id, "+447700900855", - statuses=["delivered"], - notification_type="sms", + statuses=[NotificationStatus.DELIVERED], + notification_type=NotificationStatus.SMS, ) assert len(notifications.items) == 1 @@ -1440,20 +1630,20 @@ def test_dao_get_notifications_by_to_field_filters_multiple_statuses(sample_temp template=sample_template, to_field="+447700900855", normalised_to="447700900855", - status="delivered", + status=NotificationStatus.DELIVERED, ) notification2 = create_notification( template=sample_template, to_field="+447700900855", normalised_to="447700900855", - status="sending", + status=NotificationStatus.SENDING, ) notifications = dao_get_notifications_by_recipient_or_reference( notification1.service_id, "+447700900855", - statuses=["delivered", "sending"], - notification_type="sms", + statuses=[NotificationStatus.DELIVERED, NotificationStatus.SENDING], + notification_type=NotificationType.SMS, ) notification_ids = [notification.id for notification in notifications.items] @@ -1472,17 +1662,19 @@ def test_dao_get_notifications_by_to_field_returns_all_if_no_status_filter( template=sample_template, to_field="+447700900855", normalised_to="447700900855", - status="delivered", + status=NotificationStatus.DELIVERED, ) notification2 = create_notification( template=sample_template, to_field="+447700900855", normalised_to="447700900855", - status="temporary-failure", + status=NotificationStatus.TEMPORARY_FAILURE, ) notifications = dao_get_notifications_by_recipient_or_reference( - notification1.service_id, "+447700900855", notification_type="sms" + notification1.service_id, + "+447700900855", + notification_type=NotificationType.SMS, ) notification_ids = [notification.id for notification in notifications.items] @@ -1509,7 +1701,9 @@ def test_dao_get_notifications_by_to_field_orders_by_created_at_desc(sample_temp notification = notification(created_at=datetime.utcnow()) notifications = dao_get_notifications_by_recipient_or_reference( - sample_template.service_id, "+447700900855", notification_type="sms" + sample_template.service_id, + "+447700900855", + notification_type=NotificationType.SMS, ) assert len(notifications.items) == 2 @@ -1556,15 +1750,15 @@ def test_dao_update_notifications_by_reference_updated_notifications(sample_temp updated_count, updated_history_count = dao_update_notifications_by_reference( references=["ref1", "ref2"], - update_dict={"status": "delivered", "billable_units": 2}, + update_dict={"status": NotificationStatus.DELIVERED, "billable_units": 2}, ) assert updated_count == 2 updated_1 = Notification.query.get(notification_1.id) assert updated_1.billable_units == 2 - assert updated_1.status == "delivered" + assert updated_1.status == NotificationStatus.DELIVERED updated_2 = Notification.query.get(notification_2.id) assert updated_2.billable_units == 2 - assert updated_2.status == "delivered" + assert updated_2.status == NotificationStatus.DELIVERED assert updated_history_count == 0 @@ -1577,7 +1771,7 @@ def test_dao_update_notifications_by_reference_updates_history_some_notification updated_count, updated_history_count = dao_update_notifications_by_reference( references=["ref1", "ref2"], - update_dict={"status": "delivered", "billable_units": 2}, + update_dict={"status": NotificationStatus.DELIVERED, "billable_units": 2}, ) assert updated_count == 1 assert updated_history_count == 1 @@ -1591,7 +1785,7 @@ def test_dao_update_notifications_by_reference_updates_history_no_notifications_ updated_count, updated_history_count = dao_update_notifications_by_reference( references=["ref1", "ref2"], - update_dict={"status": "delivered", "billable_units": 2}, + update_dict={"status": NotificationStatus.DELIVERED, "billable_units": 2}, ) assert updated_count == 0 assert updated_history_count == 2 @@ -1601,7 +1795,8 @@ def test_dao_update_notifications_by_reference_returns_zero_when_no_notification notify_db_session, ): updated_count, updated_history_count = dao_update_notifications_by_reference( - references=["ref"], update_dict={"status": "delivered", "billable_units": 2} + references=["ref"], + update_dict={"status": NotificationStatus.DELIVERED, "billable_units": 2}, ) assert updated_count == 0 @@ -1617,13 +1812,19 @@ def test_dao_update_notifications_by_reference_updates_history_when_one_of_two_n notification2 = create_notification(template=sample_template, reference="ref2") updated_count, updated_history_count = dao_update_notifications_by_reference( - references=["ref1", "ref2"], update_dict={"status": "delivered"} + references=["ref1", "ref2"], + update_dict={"status": NotificationStatus.DELIVERED}, ) assert updated_count == 1 assert updated_history_count == 1 - assert Notification.query.get(notification2.id).status == "delivered" - assert NotificationHistory.query.get(notification1.id).status == "delivered" + assert ( + Notification.query.get(notification2.id).status == NotificationStatus.DELIVERED + ) + assert ( + NotificationHistory.query.get(notification1.id).status + == NotificationStatus.DELIVERED + ) def test_dao_get_notification_by_reference_with_one_match_returns_notification( @@ -1678,22 +1879,26 @@ def test_dao_get_notification_history_by_reference_with_no_matches_raises_error( dao_get_notification_history_by_reference("REF1") -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize( + "notification_type", [NotificationType.EMAIL, NotificationType.SMS] +) def test_notifications_not_yet_sent(sample_service, notification_type): older_than = 4 # number of seconds the notification can not be older than template = create_template(service=sample_service, template_type=notification_type) old_notification = create_notification( template=template, created_at=datetime.utcnow() - timedelta(seconds=older_than), - status="created", + status=NotificationStatus.CREATED, ) create_notification( template=template, created_at=datetime.utcnow() - timedelta(seconds=older_than), - status="sending", + status=NotificationStatus.SENDING, ) create_notification( - template=template, created_at=datetime.utcnow(), status="created" + template=template, + created_at=datetime.utcnow(), + status=NotificationStatus.CREATED, ) results = notifications_not_yet_sent(older_than, notification_type) @@ -1701,18 +1906,26 @@ def test_notifications_not_yet_sent(sample_service, notification_type): assert results[0] == old_notification -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize( + "notification_type", [NotificationType.EMAIL, NotificationType.SMS] +) def test_notifications_not_yet_sent_return_no_rows(sample_service, notification_type): older_than = 5 # number of seconds the notification can not be older than template = create_template(service=sample_service, template_type=notification_type) create_notification( - template=template, created_at=datetime.utcnow(), status="created" + template=template, + created_at=datetime.utcnow(), + status=NotificationStatus.CREATED, ) create_notification( - template=template, created_at=datetime.utcnow(), status="sending" + template=template, + created_at=datetime.utcnow(), + status=NotificationStatus.SENDING, ) create_notification( - template=template, created_at=datetime.utcnow(), status="delivered" + template=template, + created_at=datetime.utcnow(), + status=NotificationStatus.DELIVERED, ) results = notifications_not_yet_sent(older_than, notification_type) @@ -1750,7 +1963,8 @@ def test_get_service_ids_with_notifications_on_date_checks_ft_status( assert ( len( get_service_ids_with_notifications_on_date( - NotificationType.SMS, date(2022, 1, 1) + NotificationType.SMS, + date(2022, 1, 1), ) ) == 1 @@ -1758,7 +1972,8 @@ def test_get_service_ids_with_notifications_on_date_checks_ft_status( assert ( len( get_service_ids_with_notifications_on_date( - NotificationType.SMS, date(2022, 1, 2) + NotificationType.SMS, + date(2022, 1, 2), ) ) == 1 diff --git a/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py b/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py index 807996748..086f3c9e9 100644 --- a/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py +++ b/tests/app/dao/notification_dao/test_notification_dao_delete_notifications.py @@ -7,7 +7,7 @@ from app.dao.notifications_dao import ( insert_notification_history_delete_notifications, move_notifications_to_notification_history, ) -from app.enums import KeyType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType from app.models import Notification, NotificationHistory from tests.app.db import ( create_notification, @@ -23,23 +23,26 @@ def test_move_notifications_does_nothing_if_notification_history_row_already_exi notification = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=8), - status="temporary-failure", + status=NotificationStatus.TEMPORARY_FAILURE, ) create_notification_history( id=notification.id, template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=8), - status="delivered", + status=NotificationStatus.DELIVERED, ) move_notifications_to_notification_history( - "email", sample_email_template.service_id, datetime.utcnow(), 1 + NotificationType.EMAIL, + sample_email_template.service_id, + datetime.utcnow(), + 1, ) assert Notification.query.count() == 0 history = NotificationHistory.query.all() assert len(history) == 1 - assert history[0].status == "delivered" + assert history[0].status == NotificationStatus.DELIVERED def test_move_notifications_only_moves_notifications_older_than_provided_timestamp( @@ -59,7 +62,9 @@ def test_move_notifications_only_moves_notifications_older_than_provided_timesta old_notification_id = old_notification.id result = move_notifications_to_notification_history( - "sms", sample_template.service_id, delete_time + NotificationType.SMS, + sample_template.service_id, + delete_time, ) assert result == 1 @@ -78,12 +83,15 @@ def test_move_notifications_keeps_calling_until_no_more_to_delete_and_then_retur timestamp = datetime(2021, 1, 1) result = move_notifications_to_notification_history( - "sms", service_id, timestamp, qry_limit=5 + NotificationType.SMS, + service_id, + timestamp, + qry_limit=5, ) assert result == 11 mock_insert.asset_called_with( - notification_type="sms", + notification_type=NotificationType.SMS, service_id=service_id, timestamp_to_delete_backwards_from=timestamp, qry_limit=5, @@ -95,17 +103,19 @@ def test_move_notifications_only_moves_for_given_notification_type(sample_servic delete_time = datetime(2020, 6, 1, 12) one_second_before = delete_time - timedelta(seconds=1) - sms_template = create_template(sample_service, "sms") - email_template = create_template(sample_service, "email") + sms_template = create_template(sample_service, TemplateType.SMS) + email_template = create_template(sample_service, TemplateType.EMAIL) create_notification(sms_template, created_at=one_second_before) create_notification(email_template, created_at=one_second_before) result = move_notifications_to_notification_history( - "sms", sample_service.id, delete_time + NotificationType.SMS, + sample_service.id, + delete_time, ) assert result == 1 - assert {x.notification_type for x in Notification.query} == {"email"} - assert NotificationHistory.query.one().notification_type == "sms" + assert {x.notification_type for x in Notification.query} == {NotificationType.EMAIL} + assert NotificationHistory.query.one().notification_type == NotificationType.SMS def test_move_notifications_only_moves_for_given_service(notify_db_session): @@ -115,13 +125,17 @@ def test_move_notifications_only_moves_for_given_service(notify_db_session): service = create_service(service_name="service") other_service = create_service(service_name="other") - template = create_template(service, "sms") - other_template = create_template(other_service, "sms") + template = create_template(service, TemplateType.SMS) + other_template = create_template(other_service, TemplateType.SMS) create_notification(template, created_at=one_second_before) create_notification(other_template, created_at=one_second_before) - result = move_notifications_to_notification_history("sms", service.id, delete_time) + result = move_notifications_to_notification_history( + NotificationType.SMS, + service.id, + delete_time, + ) assert result == 1 assert NotificationHistory.query.one().service_id == service.id @@ -132,17 +146,25 @@ def test_move_notifications_just_deletes_test_key_notifications(sample_template) delete_time = datetime(2020, 6, 1, 12) one_second_before = delete_time - timedelta(seconds=1) create_notification( - template=sample_template, created_at=one_second_before, key_type=KeyType.NORMAL + template=sample_template, + created_at=one_second_before, + key_type=KeyType.NORMAL, ) create_notification( - template=sample_template, created_at=one_second_before, key_type=KeyType.TEAM + template=sample_template, + created_at=one_second_before, + key_type=KeyType.TEAM, ) create_notification( - template=sample_template, created_at=one_second_before, key_type=KeyType.TEST + template=sample_template, + created_at=one_second_before, + key_type=KeyType.TEST, ) result = move_notifications_to_notification_history( - "sms", sample_template.service_id, delete_time + NotificationType.SMS, + sample_template.service_id, + delete_time, ) assert result == 2 @@ -163,58 +185,58 @@ def test_insert_notification_history_delete_notifications(sample_email_template) n1 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, minutes=4), - status="delivered", + status=NotificationStatus.DELIVERED, ) n2 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, minutes=20), - status="permanent-failure", + status=NotificationStatus.PERMANENT_FAILURE, ) n3 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, minutes=30), - status="temporary-failure", + status=NotificationStatus.TEMPORARY_FAILURE, ) n4 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, minutes=59), - status="temporary-failure", + status=NotificationStatus.TEMPORARY_FAILURE, ) n5 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, hours=1), - status="sending", + status=NotificationStatus.SENDING, ) n6 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, minutes=61), - status="pending", + status=NotificationStatus.PENDING, ) n7 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, hours=1, seconds=1), - status="validation-failed", + status=NotificationStatus.VALIDATION_FAILED, ) n8 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(days=1, minutes=20), - status="created", + status=NotificationStatus.CREATED, ) # should NOT be deleted - wrong status n9 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(hours=1), - status="delivered", + status=NotificationStatus.DELIVERED, ) n10 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(hours=1), - status="technical-failure", + status=NotificationStatus.TECHNICAL_FAILURE, ) n11 = create_notification( template=sample_email_template, created_at=datetime.utcnow() - timedelta(hours=23, minutes=59), - status="created", + status=NotificationStatus.CREATED, ) ids_to_move = sorted([n1.id, n2.id, n3.id, n4.id, n5.id, n6.id, n7.id, n8.id]) @@ -239,17 +261,17 @@ def test_insert_notification_history_delete_notifications_more_notifications_tha create_notification( template=sample_template, created_at=datetime.utcnow() + timedelta(minutes=4), - status="delivered", + status=NotificationStatus.DELIVERED, ) create_notification( template=sample_template, created_at=datetime.utcnow() + timedelta(minutes=20), - status="permanent-failure", + status=NotificationStatus.PERMANENT_FAILURE, ) create_notification( template=sample_template, created_at=datetime.utcnow() + timedelta(minutes=30), - status="temporary-failure", + status=NotificationStatus.TEMPORARY_FAILURE, ) del_count = insert_notification_history_delete_notifications( @@ -272,14 +294,16 @@ def test_insert_notification_history_delete_notifications_only_insert_delete_for notification_to_move = create_notification( template=sample_email_template, created_at=datetime.utcnow() + timedelta(minutes=4), - status="delivered", + status=NotificationStatus.DELIVERED, ) another_service = create_service(service_name="Another service") - another_template = create_template(service=another_service, template_type="email") + another_template = create_template( + service=another_service, template_type=TemplateType.EMAIL + ) notification_to_stay = create_notification( template=another_template, created_at=datetime.utcnow() + timedelta(minutes=4), - status="delivered", + status=NotificationStatus.DELIVERED, ) del_count = insert_notification_history_delete_notifications( @@ -303,20 +327,20 @@ def test_insert_notification_history_delete_notifications_insert_for_key_type( create_notification( template=sample_template, created_at=datetime.utcnow() - timedelta(hours=4), - status="delivered", - key_type="normal", + status=NotificationStatus.DELIVERED, + key_type=KeyType.NORMAL, ) create_notification( template=sample_template, created_at=datetime.utcnow() - timedelta(hours=4), - status="delivered", - key_type="team", + status=NotificationStatus.DELIVERED, + key_type=KeyType.TEAM, ) with_test_key = create_notification( template=sample_template, created_at=datetime.utcnow() - timedelta(hours=4), - status="delivered", - key_type="test", + status=NotificationStatus.DELIVERED, + key_type=KeyType.TEST, ) del_count = insert_notification_history_delete_notifications( diff --git a/tests/app/dao/test_templates_dao.py b/tests/app/dao/test_templates_dao.py index c371baa1c..bfe0e59d1 100644 --- a/tests/app/dao/test_templates_dao.py +++ b/tests/app/dao/test_templates_dao.py @@ -12,6 +12,7 @@ from app.dao.templates_dao import ( dao_redact_template, dao_update_template, ) +from app.enums import TemplateProcessType, TemplateType from app.models import Template, TemplateHistory, TemplateRedacted from tests.app.db import create_template @@ -19,8 +20,8 @@ from tests.app.db import create_template @pytest.mark.parametrize( "template_type, subject", [ - ("sms", None), - ("email", "subject"), + (TemplateType.SMS, None), + (TemplateType.EMAIL, "subject"), ], ) def test_create_template(sample_service, sample_user, template_type, subject): @@ -43,7 +44,8 @@ def test_create_template(sample_service, sample_user, template_type, subject): == "Sample Template" ) assert ( - dao_get_all_templates_for_service(sample_service.id)[0].process_type == "normal" + dao_get_all_templates_for_service(sample_service.id)[0].process_type + == TemplateProcessType.NORMAL ) @@ -61,7 +63,7 @@ def test_create_template_creates_redact_entry(sample_service): def test_update_template(sample_service, sample_user): data = { "name": "Sample Template", - "template_type": "sms", + "template_type": TemplateType.SMS, "content": "Template content", "service": sample_service, "created_by": sample_user, @@ -101,19 +103,19 @@ def test_get_all_templates_for_service(service_factory): create_template( service=service_1, template_name="Sample Template 1", - template_type="sms", + template_type=TemplateType.SMS, content="Template content", ) create_template( service=service_1, template_name="Sample Template 2", - template_type="sms", + template_type=TemplateType.SMS, content="Template content", ) create_template( service=service_2, template_name="Sample Template 3", - template_type="sms", + template_type=TemplateType.SMS, content="Template content", ) @@ -125,19 +127,19 @@ def test_get_all_templates_for_service(service_factory): def test_get_all_templates_for_service_is_alphabetised(sample_service): create_template( template_name="Sample Template 1", - template_type="sms", + template_type=TemplateType.SMS, content="Template content", service=sample_service, ) template_2 = create_template( template_name="Sample Template 2", - template_type="sms", + template_type=TemplateType.SMS, content="Template content", service=sample_service, ) create_template( template_name="Sample Template 3", - template_type="sms", + template_type=TemplateType.SMS, content="Template content", service=sample_service, ) @@ -259,7 +261,7 @@ def test_create_template_creates_a_history_record_with_current_data( assert TemplateHistory.query.count() == 0 data = { "name": "Sample Template", - "template_type": "email", + "template_type": TemplateType.EMAIL, "subject": "subject", "content": "Template content", "service": sample_service, @@ -288,7 +290,7 @@ def test_update_template_creates_a_history_record_with_current_data( assert TemplateHistory.query.count() == 0 data = { "name": "Sample Template", - "template_type": "email", + "template_type": TemplateType.EMAIL, "subject": "subject", "content": "Template content", "service": sample_service, diff --git a/tests/app/dao/test_uploads_dao.py b/tests/app/dao/test_uploads_dao.py index c95b2f000..5a4fb33b8 100644 --- a/tests/app/dao/test_uploads_dao.py +++ b/tests/app/dao/test_uploads_dao.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from freezegun import freeze_time from app.dao.uploads_dao import dao_get_uploads_by_service_id -from app.enums import JobStatus, TemplateType +from app.enums import JobStatus, NotificationStatus, NotificationType, TemplateType from tests.app.db import ( create_job, create_notification, @@ -13,7 +13,12 @@ from tests.app.db import ( ) -def create_uploaded_letter(letter_template, service, status="created", created_at=None): +def create_uploaded_letter( + letter_template, + service, + status=NotificationStatus.CREATED, + created_at=None, +): return create_notification( template=letter_template, to_field="file-name", @@ -39,7 +44,9 @@ def create_uploaded_template(service): @freeze_time("2020-02-02 09:00") # GMT time def test_get_uploads_for_service(sample_template): - create_service_data_retention(sample_template.service, "sms", days_of_retention=9) + create_service_data_retention( + sample_template.service, NotificationType.SMS, days_of_retention=9 + ) job = create_job(sample_template, processing_started=datetime.utcnow()) other_service = create_service(service_name="other service") @@ -55,7 +62,7 @@ def test_get_uploads_for_service(sample_template): job.id, job.original_file_name, job.notification_count, - "sms", + TemplateType.SMS, 9, job.created_at, job.scheduled_for, diff --git a/tests/app/dao/test_users_dao.py b/tests/app/dao/test_users_dao.py index 7b10cf4d5..6e4529310 100644 --- a/tests/app/dao/test_users_dao.py +++ b/tests/app/dao/test_users_dao.py @@ -1,5 +1,6 @@ import uuid from datetime import datetime, timedelta +from types import CodeType import pytest from freezegun import freeze_time @@ -137,7 +138,7 @@ def test_should_not_delete_verification_codes_less_than_one_day_old(sample_user) def make_verify_code(user, age=None, expiry_age=None, code="12335", code_used=False): verify_code = VerifyCode( - code_type="sms", + code_type=CodeType.SMS, _code=code, created_at=datetime.utcnow() - (age or timedelta(hours=0)), expiry_datetime=datetime.utcnow() - (expiry_age or timedelta(0)), diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index 09dc0f39a..59d5395df 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -14,7 +14,7 @@ from app.dao import notifications_dao from app.dao.provider_details_dao import get_provider_details_by_identifier from app.delivery import send_to_providers from app.delivery.send_to_providers import get_html_email_options, get_logo_url -from app.enums import BrandType, KeyType +from app.enums import BrandType, KeyType, NotificationStatus from app.exceptions import NotificationTechnicalFailureException from app.models import EmailBranding, Notification from app.serialised_models import SerialisedService @@ -78,7 +78,7 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist( db_notification = create_notification( template=sample_sms_template_with_html, personalisation={"name": "Jo"}, - status="created", + status=NotificationStatus.CREATED, reply_to_text=sample_sms_template_with_html.service.get_default_sms_sender(), ) @@ -99,7 +99,7 @@ def test_should_send_personalised_template_to_correct_sms_provider_and_persist( notification = Notification.query.filter_by(id=db_notification.id).one() - assert notification.status == "sending" + assert notification.status == NotificationStatus.SENDING assert notification.sent_at <= datetime.utcnow() assert notification.sent_by == "sns" assert notification.billable_units == 1 @@ -137,7 +137,7 @@ def test_should_send_personalised_template_to_correct_email_provider_and_persist ) notification = Notification.query.filter_by(id=db_notification.id).one() - assert notification.status == "sending" + assert notification.status == NotificationStatus.SENDING assert notification.sent_at <= datetime.utcnow() assert notification.sent_by == "ses" assert notification.personalisation == {"name": "Jo"} @@ -175,7 +175,7 @@ def test_send_sms_should_use_template_version_from_notification_not_latest( db_notification = create_notification( template=sample_template, to_field="2028675309", - status="created", + status=NotificationStatus.CREATED, reply_to_text=sample_template.service.get_default_sms_sender(), normalised_to="2028675309", ) @@ -217,7 +217,7 @@ def test_send_sms_should_use_template_version_from_notification_not_latest( assert persisted_notification.template_id == expected_template_id assert persisted_notification.template_version == version_on_notification assert persisted_notification.template_version != t.version - assert persisted_notification.status == "sending" + assert persisted_notification.status == NotificationStatus.SENDING assert not persisted_notification.personalisation @@ -225,20 +225,20 @@ def test_should_have_sending_status_if_fake_callback_function_fails( sample_notification, mocker ): mocker.patch( - "app.delivery.send_to_providers.send_sms_response", side_effect=HTTPError + "app.delivery.send_to_providers.send_sms_response", side_effect=HTTPError, ) sample_notification.key_type = KeyType.TEST with pytest.raises(HTTPError): send_to_providers.send_sms_to_provider(sample_notification) - assert sample_notification.status == "sending" + assert sample_notification.status == NotificationStatus.SENDING assert sample_notification.sent_by == "sns" def test_should_not_send_to_provider_when_status_is_not_created( sample_template, mocker ): - notification = create_notification(template=sample_template, status="sending") + notification = create_notification(template=sample_template, status=NotificationStatus.SENDING,) mocker.patch("app.aws_sns_client.send_sms") response_mock = mocker.patch("app.delivery.send_to_providers.send_sms_response") @@ -307,7 +307,7 @@ def test_send_email_to_provider_should_not_send_to_provider_when_status_is_not_c mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") mock_redis.get.return_value = "test@example.com".encode("utf-8") - notification = create_notification(template=sample_email_template, status="sending") + notification = create_notification(template=sample_email_template, status=NotificationStatus.SENDING) mocker.patch("app.aws_ses_client.send_email") mocker.patch("app.delivery.send_to_providers.send_email_response") @@ -329,12 +329,10 @@ def test_send_email_should_use_service_reply_to_email( ) create_reply_to_email(service=sample_service, email_address="foo@bar.com") - send_to_providers.send_email_to_provider( - db_notification, - ) + send_to_providers.send_email_to_provider(db_notification) app.aws_ses_client.send_email.assert_called_once_with( - ANY, ANY, ANY, body=ANY, html_body=ANY, reply_to_address="foo@bar.com" + ANY, ANY, ANY, body=ANY, html_body=ANY, reply_to_address="foo@bar.com", ) @@ -393,7 +391,7 @@ def test_get_html_email_renderer_with_branding_details_and_render_govuk_banner_o def test_get_html_email_renderer_prepends_logo_path(notify_api): Service = namedtuple("Service", ["email_branding"]) EmailBranding = namedtuple( - "EmailBranding", ["brand_type", "colour", "name", "logo", "text"] + "EmailBranding", ["brand_type", "colour", "name", "logo", "text"], ) email_branding = EmailBranding( @@ -417,7 +415,7 @@ def test_get_html_email_renderer_prepends_logo_path(notify_api): def test_get_html_email_renderer_handles_email_branding_without_logo(notify_api): Service = namedtuple("Service", ["email_branding"]) EmailBranding = namedtuple( - "EmailBranding", ["brand_type", "colour", "name", "logo", "text"] + "EmailBranding", ["brand_type", "colour", "name", "logo", "text"], ) email_branding = EmailBranding( @@ -473,9 +471,9 @@ def test_get_logo_url_works_for_different_environments(base_url, expected_url): @pytest.mark.parametrize( "starting_status, expected_status", [ - ("delivered", "delivered"), - ("created", "sending"), - ("technical-failure", "technical-failure"), + (NotificationStatus.DELIVERED, NotificationStatus.DELIVERED), + (NotificationStatus.CREATED, NotificationStatus.SENDING), + (NotificationStatus.TECHNICAL_FAILURE, NotificationStatus.TECHNICAL_FAILURE), ], ) def test_update_notification_to_sending_does_not_update_status_from_a_final_status( @@ -498,19 +496,19 @@ def __update_notification(notification_to_update, research_mode, expected_status @pytest.mark.parametrize( "research_mode,key_type, billable_units, expected_status", [ - (True, KeyType.NORMAL, 0, "delivered"), - (False, KeyType.NORMAL, 1, "sending"), - (False, KeyType.TEST, 0, "sending"), - (True, KeyType.TEST, 0, "sending"), - (True, KeyType.TEAM, 0, "delivered"), - (False, KeyType.TEAM, 1, "sending"), + (True, KeyType.NORMAL, 0, NotificationStatus.DELIVERED), + (False, KeyType.NORMAL, 1, NotificationStatus.SENDING), + (False, KeyType.TEST, 0, NotificationStatus.SENDING), + (True, KeyType.TEST, 0, NotificationStatus.SENDING), + (True, KeyType.TEAM, 0, NotificationStatus.DELIVERED), + (False, KeyType.TEAM, 1, NotificationStatus.SENDING), ], ) def test_should_update_billable_units_and_status_according_to_research_mode_and_key_type( sample_template, mocker, research_mode, key_type, billable_units, expected_status ): notification = create_notification( - template=sample_template, billable_units=0, status="created", key_type=key_type + template=sample_template, billable_units=0, status=NotificationStatus.CREATED, key_type=key_type, ) mocker.patch("app.aws_sns_client.send_sms") mocker.patch( @@ -557,7 +555,7 @@ def test_should_send_sms_to_international_providers( template=sample_template, to_field="+6011-17224412", personalisation={"name": "Jo"}, - status="created", + status=NotificationStatus.CREATED, international=True, reply_to_text=sample_template.service.get_default_sms_sender(), normalised_to="601117224412", @@ -576,7 +574,7 @@ def test_should_send_sms_to_international_providers( international=True, ) - assert notification_international.status == "sending" + assert notification_international.status == NotificationStatus.SENDING assert notification_international.sent_by == "sns" @@ -625,15 +623,13 @@ def test_send_email_to_provider_uses_reply_to_from_notification( mocker.patch("app.aws_ses_client.send_email", return_value="reference") db_notification = create_notification( - template=sample_email_template, reply_to_text="test@test.com" + template=sample_email_template, reply_to_text="test@test.com", ) - send_to_providers.send_email_to_provider( - db_notification, - ) + send_to_providers.send_email_to_provider(db_notification) app.aws_ses_client.send_email.assert_called_once_with( - ANY, ANY, ANY, body=ANY, html_body=ANY, reply_to_address="test@test.com" + ANY, ANY, ANY, body=ANY, html_body=ANY, reply_to_address="test@test.com", ) diff --git a/tests/app/inbound_sms/test_rest.py b/tests/app/inbound_sms/test_rest.py index 74e78b13d..3601f4f01 100644 --- a/tests/app/inbound_sms/test_rest.py +++ b/tests/app/inbound_sms/test_rest.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta import pytest from freezegun import freeze_time +from app.enums import NotificationType from tests.app.db import ( create_inbound_sms, create_service, @@ -131,7 +132,7 @@ def test_post_to_get_most_recent_inbound_sms_for_service_limits_to_a_week( def test_post_to_get_inbound_sms_for_service_respects_data_retention( admin_request, sample_service, days_of_retention, too_old_date, returned_date ): - create_service_data_retention(sample_service, "sms", days_of_retention) + create_service_data_retention(sample_service, NotificationType.SMS, days_of_retention,) create_inbound_sms(sample_service, created_at=too_old_date) returned_inbound = create_inbound_sms(sample_service, created_at=returned_date) From 64adb091820d863c82702d9ee044962403400c6c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 13 Feb 2024 16:21:37 -0500 Subject: [PATCH 191/259] More stuff done. Signed-off-by: Cliff Hill --- tests/app/inbound_sms/test_rest.py | 14 +-- tests/app/job/test_rest.py | 148 ++++++++++++++--------------- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/tests/app/inbound_sms/test_rest.py b/tests/app/inbound_sms/test_rest.py index 3601f4f01..3da37ea96 100644 --- a/tests/app/inbound_sms/test_rest.py +++ b/tests/app/inbound_sms/test_rest.py @@ -206,10 +206,10 @@ def test_get_inbound_sms_by_id_with_invalid_service_id_returns_404( @pytest.mark.parametrize( - "page_given, expected_rows, has_next_link", [(True, 10, False), (False, 50, True)] + "page_given, expected_rows, has_next_link", [(True, 10, False), (False, 50, True)], ) def test_get_most_recent_inbound_sms_for_service( - admin_request, page_given, sample_service, expected_rows, has_next_link + admin_request, page_given, sample_service, expected_rows, has_next_link, ): for i in range(60): create_inbound_sms( @@ -229,13 +229,13 @@ def test_get_most_recent_inbound_sms_for_service( @freeze_time("Monday 10th April 2017 12:00") def test_get_most_recent_inbound_sms_for_service_respects_data_retention( - admin_request, sample_service + admin_request, sample_service, ): - create_service_data_retention(sample_service, "sms", 5) + create_service_data_retention(sample_service, NotificationType.SMS, 5) for i in range(10): created = datetime.utcnow() - timedelta(days=i) create_inbound_sms( - sample_service, user_number="44770090000{}".format(i), created_at=created + sample_service, user_number="44770090000{}".format(i), created_at=created, ) response = admin_request.get( @@ -258,7 +258,7 @@ def test_get_most_recent_inbound_sms_for_service_respects_data_retention( def test_get_most_recent_inbound_sms_for_service_respects_data_retention_if_older_than_a_week( admin_request, sample_service ): - create_service_data_retention(sample_service, "sms", 14) + create_service_data_retention(sample_service, NotificationType.SMS, 14) create_inbound_sms(sample_service, created_at=datetime(2017, 4, 1, 12, 0)) response = admin_request.get( @@ -274,7 +274,7 @@ def test_get_most_recent_inbound_sms_for_service_respects_data_retention_if_olde def test_get_inbound_sms_for_service_respects_data_retention( admin_request, sample_service ): - create_service_data_retention(sample_service, "sms", 5) + create_service_data_retention(sample_service, NotificationType.SMS, 5) for i in range(10): created = datetime.utcnow() - timedelta(days=i) create_inbound_sms( diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index 3afe283c2..3d862a54f 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -9,7 +9,7 @@ from freezegun import freeze_time import app.celery.tasks from app.dao.templates_dao import dao_update_template -from app.enums import JobStatus +from app.enums import JobStatus, KeyType, NotificationStatus, NotificationType, TemplateType from tests import create_admin_authorization_header from tests.app.db import ( create_ft_notification_status, @@ -60,7 +60,7 @@ def test_cancel_job(client, sample_scheduled_job): assert response.status_code == 200 resp_json = json.loads(response.get_data(as_text=True)) assert resp_json["data"]["id"] == job_id - assert resp_json["data"]["job_status"] == "cancelled" + assert resp_json["data"]["job_status"] == JobStatus.CANCELLED def test_cant_cancel_normal_job(client, sample_job, mocker): @@ -104,9 +104,9 @@ def test_create_unscheduled_job(client, sample_template, mocker, fake_uuid): assert resp_json["data"]["id"] == fake_uuid assert resp_json["data"]["statistics"] == [] - assert resp_json["data"]["job_status"] == "pending" + assert resp_json["data"]["job_status"] == JobStatus.PENDING assert not resp_json["data"]["scheduled_for"] - assert resp_json["data"]["job_status"] == "pending" + assert resp_json["data"]["job_status"] == JobStatus.PENDING assert resp_json["data"]["template"] == str(sample_template.id) assert resp_json["data"]["original_file_name"] == "thisisatest.csv" assert resp_json["data"]["notification_count"] == 1 @@ -138,7 +138,7 @@ def test_create_unscheduled_job_with_sender_id_in_metadata( assert response.status_code == 201 app.celery.tasks.process_job.apply_async.assert_called_once_with( - ([str(fake_uuid)]), {"sender_id": fake_uuid}, queue="job-tasks" + ([str(fake_uuid)]), {"sender_id": fake_uuid}, queue="job-tasks", ) @@ -176,7 +176,7 @@ def test_create_scheduled_job(client, sample_template, mocker, fake_uuid): resp_json["data"]["scheduled_for"] == datetime(2016, 1, 5, 11, 59, 0, tzinfo=pytz.UTC).isoformat() ) - assert resp_json["data"]["job_status"] == "scheduled" + assert resp_json["data"]["job_status"] == JobStatus.SCHEDULED assert resp_json["data"]["template"] == str(sample_template.id) assert resp_json["data"]["original_file_name"] == "thisisatest.csv" assert resp_json["data"]["notification_count"] == 1 @@ -479,19 +479,19 @@ def test_get_all_notifications_for_job_in_order_of_job_number( @pytest.mark.parametrize( "expected_notification_count, status_args", [ - (1, ["created"]), - (0, ["sending"]), - (1, ["created", "sending"]), - (0, ["sending", "delivered"]), + (1, [NotificationStatus.CREATED]), + (0, [NotificationStatus.SENDING]), + (1, [NotificationStatus.CREATED, NotificationStatus.SENDING]), + (0, [NotificationStatus.SENDING, NotificationStatus.DELIVERED]), ], ) def test_get_all_notifications_for_job_filtered_by_status( - admin_request, sample_job, expected_notification_count, status_args, mocker + admin_request, sample_job, expected_notification_count, status_args, mocker, ): mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") mock_s3.return_value = "15555555555" - create_notification(job=sample_job, to_field="1", status="created") + create_notification(job=sample_job, to_field="1", status=NotificationStatus.CREATED) resp = admin_request.get( "job.get_all_notifications_for_service_job", @@ -578,29 +578,29 @@ def test_get_job_by_id_should_return_summed_statistics(admin_request, sample_job job_id = str(sample_job.id) service_id = sample_job.service.id - create_notification(job=sample_job, status="created") - create_notification(job=sample_job, status="created") - create_notification(job=sample_job, status="created") - create_notification(job=sample_job, status="sending") - create_notification(job=sample_job, status="failed") - create_notification(job=sample_job, status="failed") - create_notification(job=sample_job, status="failed") - create_notification(job=sample_job, status="technical-failure") - create_notification(job=sample_job, status="temporary-failure") - create_notification(job=sample_job, status="temporary-failure") + create_notification(job=sample_job, status=NotificationStatus.CREATED) + create_notification(job=sample_job, status=NotificationStatus.CREATED) + create_notification(job=sample_job, status=NotificationStatus.CREATED) + create_notification(job=sample_job, status=NotificationStatus.SENDING) + create_notification(job=sample_job, status=NotificationStatus.FAILED) + create_notification(job=sample_job, status=NotificationStatus.FAILED) + create_notification(job=sample_job, status=NotificationStatus.FAILED) + create_notification(job=sample_job, status=NotificationStatus.TECHNICAL_FAILURE) + create_notification(job=sample_job, status=NotificationStatus.TEMPORARY_FAILURE) + create_notification(job=sample_job, status=NotificationStatus.TEMPORARY_FAILURE) resp_json = admin_request.get( - "job.get_job_by_service_and_job_id", service_id=service_id, job_id=job_id + "job.get_job_by_service_and_job_id", service_id=service_id, job_id=job_id, ) assert resp_json["data"]["id"] == job_id - assert {"status": "created", "count": 3} in resp_json["data"]["statistics"] - assert {"status": "sending", "count": 1} in resp_json["data"]["statistics"] - assert {"status": "failed", "count": 3} in resp_json["data"]["statistics"] - assert {"status": "technical-failure", "count": 1} in resp_json["data"][ + assert {"status": NotificationStatus.CREATED, "count": 3,} in resp_json["data"]["statistics"] + assert {"status": NotificationStatus.SENDING, "count": 1,} in resp_json["data"]["statistics"] + assert {"status": NotificationStatus.FAILED, "count": 3,} in resp_json["data"]["statistics"] + assert {"status": NotificationStatus.TECHNICAL_FAILURE, "count": 1,} in resp_json["data"][ "statistics" ] - assert {"status": "temporary-failure", "count": 2} in resp_json["data"][ + assert {"status": NotificationStatus.TEMPORARY_FAILURE, "count": 2,} in resp_json["data"][ "statistics" ] assert resp_json["data"]["created_by"]["name"] == "Test User" @@ -613,26 +613,26 @@ def test_get_job_by_id_with_stats_for_old_job_where_notifications_have_been_purg sample_template, notification_count=10, created_at=datetime.utcnow() - timedelta(days=9), - job_status="finished", + job_status=JobStatus.FINISHED, ) def __create_ft_status(job, status, count): create_ft_notification_status( local_date=job.created_at.date(), - notification_type="sms", + notification_type=NotificationType.SMS, service=job.service, job=job, template=job.template, - key_type="normal", + key_type=KeyType.NORMAL, notification_status=status, count=count, ) - __create_ft_status(old_job, "created", 3) - __create_ft_status(old_job, "sending", 1) - __create_ft_status(old_job, "failed", 3) - __create_ft_status(old_job, "technical-failure", 1) - __create_ft_status(old_job, "temporary-failure", 2) + __create_ft_status(old_job, NotificationStatus.CREATED, 3) + __create_ft_status(old_job, NotificationStatus.SENDING, 1) + __create_ft_status(old_job, NotificationStatus.FAILED, 3) + __create_ft_status(old_job, NotificationStatus.TECHNICAL_FAILURE, 1) + __create_ft_status(old_job, NotificationStatus.TEMPORARY_FAILURE, 2) resp_json = admin_request.get( "job.get_job_by_service_and_job_id", @@ -641,13 +641,13 @@ def test_get_job_by_id_with_stats_for_old_job_where_notifications_have_been_purg ) assert resp_json["data"]["id"] == str(old_job.id) - assert {"status": "created", "count": 3} in resp_json["data"]["statistics"] - assert {"status": "sending", "count": 1} in resp_json["data"]["statistics"] - assert {"status": "failed", "count": 3} in resp_json["data"]["statistics"] - assert {"status": "technical-failure", "count": 1} in resp_json["data"][ + assert {"status": NotificationStatus.CREATED, "count": 3,} in resp_json["data"]["statistics"] + assert {"status": NotificationStatus.SENDING, "count": 1,} in resp_json["data"]["statistics"] + assert {"status": NotificationStatus.FAILED, "count": 3,} in resp_json["data"]["statistics"] + assert {"status": NotificationStatus.TECHNICAL_FAILURE, "count": 1,} in resp_json["data"][ "statistics" ] - assert {"status": "temporary-failure", "count": 2} in resp_json["data"][ + assert {"status": NotificationStatus.TEMPORARY_FAILURE, "count": 2,} in resp_json["data"][ "statistics" ] assert resp_json["data"]["created_by"]["name"] == "Test User" @@ -669,7 +669,7 @@ def test_get_jobs(admin_request, sample_template): "name": "Test User", }, "id": ANY, - "job_status": "pending", + "job_status": JobStatus.PENDING, "notification_count": 1, "original_file_name": "some.csv", "processing_finished": None, @@ -680,7 +680,7 @@ def test_get_jobs(admin_request, sample_template): "statistics": [], "template": str(sample_template.id), "template_name": sample_template.name, - "template_type": "sms", + "template_type": TemplateType.SMS, "template_version": 1, "updated_at": None, } @@ -710,12 +710,12 @@ def test_get_jobs_should_return_statistics(admin_request, sample_template): earlier = datetime.utcnow() - timedelta(days=1) job_1 = create_job(sample_template, processing_started=earlier) job_2 = create_job(sample_template, processing_started=now) - create_notification(job=job_1, status="created") - create_notification(job=job_1, status="created") - create_notification(job=job_1, status="created") - create_notification(job=job_2, status="sending") - create_notification(job=job_2, status="sending") - create_notification(job=job_2, status="sending") + create_notification(job=job_1, status=NotificationStatus.CREATED) + create_notification(job=job_1, status=NotificationStatus.CREATED) + create_notification(job=job_1, status=NotificationStatus.CREATED) + create_notification(job=job_2, status=NotificationStatus.SENDING) + create_notification(job=job_2, status=NotificationStatus.SENDING) + create_notification(job=job_2, status=NotificationStatus.SENDING) resp_json = admin_request.get( "job.get_jobs_by_service", service_id=sample_template.service_id @@ -723,13 +723,13 @@ def test_get_jobs_should_return_statistics(admin_request, sample_template): assert len(resp_json["data"]) == 2 assert resp_json["data"][0]["id"] == str(job_2.id) - assert {"status": "sending", "count": 3} in resp_json["data"][0]["statistics"] + assert {"status": NotificationStatus.SENDING, "count": 3,} in resp_json["data"][0]["statistics"] assert resp_json["data"][1]["id"] == str(job_1.id) - assert {"status": "created", "count": 3} in resp_json["data"][1]["statistics"] + assert {"status": NotificationStatus.CREATED, "count": 3,} in resp_json["data"][1]["statistics"] def test_get_jobs_should_return_no_stats_if_no_rows_in_notifications( - admin_request, sample_template + admin_request, sample_template, ): now = datetime.utcnow() earlier = datetime.utcnow() - timedelta(days=1) @@ -795,15 +795,15 @@ def test_get_jobs_accepts_page_parameter(admin_request, sample_template): def test_get_jobs_can_filter_on_statuses( admin_request, sample_template, statuses_filter, expected_statuses ): - create_job(sample_template, job_status="pending") - create_job(sample_template, job_status="in progress") - create_job(sample_template, job_status="finished") - create_job(sample_template, job_status="sending limits exceeded") - create_job(sample_template, job_status="scheduled") - create_job(sample_template, job_status="cancelled") - create_job(sample_template, job_status="ready to send") - create_job(sample_template, job_status="sent to dvla") - create_job(sample_template, job_status="error") + create_job(sample_template, job_status=JobStatus.PENDING) + create_job(sample_template, job_status=JobStatus.IN_PROGRESS) + create_job(sample_template, job_status=JobStatus.FINISHED) + create_job(sample_template, job_status=JobStatus.SENDING_LIMITS_EXCEEDED) + create_job(sample_template, job_status=JobStatus.SCHEDULED) + create_job(sample_template, job_status=JobStatus.CANCELLED) + create_job(sample_template, job_status=JobStatus.READY_TO_SEND) + create_job(sample_template, job_status=JobStatus.SENT_TO_DVLA) + create_job(sample_template, job_status=JobStatus.ERROR) resp_json = admin_request.get( "job.get_jobs_by_service", @@ -876,32 +876,32 @@ def test_get_jobs_should_retrieve_from_ft_notification_status_for_old_jobs( # some notifications created more than three days ago, some created after the midnight cutoff create_ft_notification_status( - date(2017, 6, 6), job=job_1, notification_status="delivered", count=2 + date(2017, 6, 6), job=job_1, notification_status=NotificationStatus.DELIVERED, count=2, ) create_ft_notification_status( - date(2017, 6, 7), job=job_1, notification_status="delivered", count=4 + date(2017, 6, 7), job=job_1, notification_status=NotificationStatus.DELIVERED, count=4, ) # job2's new enough create_notification( - job=job_2, status="created", created_at=not_quite_three_days_ago + job=job_2, status=NotificationStatus.CREATED, created_at=not_quite_three_days_ago, ) # this isn't picked up because the job is too new create_ft_notification_status( - date(2017, 6, 7), job=job_2, notification_status="delivered", count=8 + date(2017, 6, 7), job=job_2, notification_status=NotificationStatus.DELIVERED, count=8, ) # this isn't picked up - while the job is old, it started in last 3 days so we look at notification table instead create_ft_notification_status( - date(2017, 6, 7), job=job_3, notification_status="delivered", count=16 + date(2017, 6, 7), job=job_3, notification_status=NotificationStatus.DELIVERED, count=16, ) # this isn't picked up because we're using the ft status table for job_1 as it's old create_notification( - job=job_1, status="created", created_at=not_quite_three_days_ago + job=job_1, status=NotificationStatus.CREATED, created_at=not_quite_three_days_ago, ) resp_json = admin_request.get( - "job.get_jobs_by_service", service_id=sample_template.service_id + "job.get_jobs_by_service", service_id=sample_template.service_id, ) assert resp_json["data"][0]["id"] == str(job_3.id) @@ -935,26 +935,26 @@ def test_get_scheduled_job_stats(admin_request): # Shouldn’t be counted – wrong status create_job( - service_1_template, job_status="finished", scheduled_for="2017-07-17 07:00" + service_1_template, job_status=JobStatus.FINISHED, scheduled_for="2017-07-17 07:00", ) create_job( - service_1_template, job_status="in progress", scheduled_for="2017-07-17 08:00" + service_1_template, job_status=JobStatus.IN_PROGRESS, scheduled_for="2017-07-17 08:00", ) # Should be counted – service 1 create_job( - service_1_template, job_status="scheduled", scheduled_for="2017-07-17 09:00" + service_1_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 09:00", ) create_job( - service_1_template, job_status="scheduled", scheduled_for="2017-07-17 10:00" + service_1_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 10:00", ) create_job( - service_1_template, job_status="scheduled", scheduled_for="2017-07-17 11:00" + service_1_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 11:00", ) # Should be counted – service 2 create_job( - service_2_template, job_status="scheduled", scheduled_for="2017-07-17 11:00" + service_2_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 11:00", ) assert admin_request.get( From cf1db4dbf4246732ba76e45ed5adf6faaf4a20a1 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 13 Feb 2024 16:22:26 -0500 Subject: [PATCH 192/259] Cleaning house a bit. Signed-off-by: Cliff Hill --- tests/app/delivery/test_send_to_providers.py | 40 ++++- tests/app/inbound_sms/test_rest.py | 22 ++- tests/app/job/test_rest.py | 172 +++++++++++++++---- 3 files changed, 182 insertions(+), 52 deletions(-) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index 59d5395df..c6cb9a5c4 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -225,7 +225,8 @@ def test_should_have_sending_status_if_fake_callback_function_fails( sample_notification, mocker ): mocker.patch( - "app.delivery.send_to_providers.send_sms_response", side_effect=HTTPError, + "app.delivery.send_to_providers.send_sms_response", + side_effect=HTTPError, ) sample_notification.key_type = KeyType.TEST @@ -238,7 +239,10 @@ def test_should_have_sending_status_if_fake_callback_function_fails( def test_should_not_send_to_provider_when_status_is_not_created( sample_template, mocker ): - notification = create_notification(template=sample_template, status=NotificationStatus.SENDING,) + notification = create_notification( + template=sample_template, + status=NotificationStatus.SENDING, + ) mocker.patch("app.aws_sns_client.send_sms") response_mock = mocker.patch("app.delivery.send_to_providers.send_sms_response") @@ -307,7 +311,9 @@ def test_send_email_to_provider_should_not_send_to_provider_when_status_is_not_c mock_redis = mocker.patch("app.delivery.send_to_providers.redis_store") mock_redis.get.return_value = "test@example.com".encode("utf-8") - notification = create_notification(template=sample_email_template, status=NotificationStatus.SENDING) + notification = create_notification( + template=sample_email_template, status=NotificationStatus.SENDING + ) mocker.patch("app.aws_ses_client.send_email") mocker.patch("app.delivery.send_to_providers.send_email_response") @@ -332,7 +338,12 @@ def test_send_email_should_use_service_reply_to_email( send_to_providers.send_email_to_provider(db_notification) app.aws_ses_client.send_email.assert_called_once_with( - ANY, ANY, ANY, body=ANY, html_body=ANY, reply_to_address="foo@bar.com", + ANY, + ANY, + ANY, + body=ANY, + html_body=ANY, + reply_to_address="foo@bar.com", ) @@ -391,7 +402,8 @@ def test_get_html_email_renderer_with_branding_details_and_render_govuk_banner_o def test_get_html_email_renderer_prepends_logo_path(notify_api): Service = namedtuple("Service", ["email_branding"]) EmailBranding = namedtuple( - "EmailBranding", ["brand_type", "colour", "name", "logo", "text"], + "EmailBranding", + ["brand_type", "colour", "name", "logo", "text"], ) email_branding = EmailBranding( @@ -415,7 +427,8 @@ def test_get_html_email_renderer_prepends_logo_path(notify_api): def test_get_html_email_renderer_handles_email_branding_without_logo(notify_api): Service = namedtuple("Service", ["email_branding"]) EmailBranding = namedtuple( - "EmailBranding", ["brand_type", "colour", "name", "logo", "text"], + "EmailBranding", + ["brand_type", "colour", "name", "logo", "text"], ) email_branding = EmailBranding( @@ -508,7 +521,10 @@ def test_should_update_billable_units_and_status_according_to_research_mode_and_ sample_template, mocker, research_mode, key_type, billable_units, expected_status ): notification = create_notification( - template=sample_template, billable_units=0, status=NotificationStatus.CREATED, key_type=key_type, + template=sample_template, + billable_units=0, + status=NotificationStatus.CREATED, + key_type=key_type, ) mocker.patch("app.aws_sns_client.send_sms") mocker.patch( @@ -623,13 +639,19 @@ def test_send_email_to_provider_uses_reply_to_from_notification( mocker.patch("app.aws_ses_client.send_email", return_value="reference") db_notification = create_notification( - template=sample_email_template, reply_to_text="test@test.com", + template=sample_email_template, + reply_to_text="test@test.com", ) send_to_providers.send_email_to_provider(db_notification) app.aws_ses_client.send_email.assert_called_once_with( - ANY, ANY, ANY, body=ANY, html_body=ANY, reply_to_address="test@test.com", + ANY, + ANY, + ANY, + body=ANY, + html_body=ANY, + reply_to_address="test@test.com", ) diff --git a/tests/app/inbound_sms/test_rest.py b/tests/app/inbound_sms/test_rest.py index 3da37ea96..fd45c0253 100644 --- a/tests/app/inbound_sms/test_rest.py +++ b/tests/app/inbound_sms/test_rest.py @@ -132,7 +132,11 @@ def test_post_to_get_most_recent_inbound_sms_for_service_limits_to_a_week( def test_post_to_get_inbound_sms_for_service_respects_data_retention( admin_request, sample_service, days_of_retention, too_old_date, returned_date ): - create_service_data_retention(sample_service, NotificationType.SMS, days_of_retention,) + create_service_data_retention( + sample_service, + NotificationType.SMS, + days_of_retention, + ) create_inbound_sms(sample_service, created_at=too_old_date) returned_inbound = create_inbound_sms(sample_service, created_at=returned_date) @@ -206,10 +210,15 @@ def test_get_inbound_sms_by_id_with_invalid_service_id_returns_404( @pytest.mark.parametrize( - "page_given, expected_rows, has_next_link", [(True, 10, False), (False, 50, True)], + "page_given, expected_rows, has_next_link", + [(True, 10, False), (False, 50, True)], ) def test_get_most_recent_inbound_sms_for_service( - admin_request, page_given, sample_service, expected_rows, has_next_link, + admin_request, + page_given, + sample_service, + expected_rows, + has_next_link, ): for i in range(60): create_inbound_sms( @@ -229,13 +238,16 @@ def test_get_most_recent_inbound_sms_for_service( @freeze_time("Monday 10th April 2017 12:00") def test_get_most_recent_inbound_sms_for_service_respects_data_retention( - admin_request, sample_service, + admin_request, + sample_service, ): create_service_data_retention(sample_service, NotificationType.SMS, 5) for i in range(10): created = datetime.utcnow() - timedelta(days=i) create_inbound_sms( - sample_service, user_number="44770090000{}".format(i), created_at=created, + sample_service, + user_number="44770090000{}".format(i), + created_at=created, ) response = admin_request.get( diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index 3d862a54f..1f289bfe8 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -9,7 +9,13 @@ from freezegun import freeze_time import app.celery.tasks from app.dao.templates_dao import dao_update_template -from app.enums import JobStatus, KeyType, NotificationStatus, NotificationType, TemplateType +from app.enums import ( + JobStatus, + KeyType, + NotificationStatus, + NotificationType, + TemplateType, +) from tests import create_admin_authorization_header from tests.app.db import ( create_ft_notification_status, @@ -138,7 +144,9 @@ def test_create_unscheduled_job_with_sender_id_in_metadata( assert response.status_code == 201 app.celery.tasks.process_job.apply_async.assert_called_once_with( - ([str(fake_uuid)]), {"sender_id": fake_uuid}, queue="job-tasks", + ([str(fake_uuid)]), + {"sender_id": fake_uuid}, + queue="job-tasks", ) @@ -486,7 +494,11 @@ def test_get_all_notifications_for_job_in_order_of_job_number( ], ) def test_get_all_notifications_for_job_filtered_by_status( - admin_request, sample_job, expected_notification_count, status_args, mocker, + admin_request, + sample_job, + expected_notification_count, + status_args, + mocker, ): mock_s3 = mocker.patch("app.job.rest.get_phone_number_from_s3") mock_s3.return_value = "15555555555" @@ -590,19 +602,42 @@ def test_get_job_by_id_should_return_summed_statistics(admin_request, sample_job create_notification(job=sample_job, status=NotificationStatus.TEMPORARY_FAILURE) resp_json = admin_request.get( - "job.get_job_by_service_and_job_id", service_id=service_id, job_id=job_id, + "job.get_job_by_service_and_job_id", + service_id=service_id, + job_id=job_id, ) assert resp_json["data"]["id"] == job_id - assert {"status": NotificationStatus.CREATED, "count": 3,} in resp_json["data"]["statistics"] - assert {"status": NotificationStatus.SENDING, "count": 1,} in resp_json["data"]["statistics"] - assert {"status": NotificationStatus.FAILED, "count": 3,} in resp_json["data"]["statistics"] - assert {"status": NotificationStatus.TECHNICAL_FAILURE, "count": 1,} in resp_json["data"][ - "statistics" - ] - assert {"status": NotificationStatus.TEMPORARY_FAILURE, "count": 2,} in resp_json["data"][ - "statistics" - ] + assert { + "status": NotificationStatus.CREATED, + "count": 3, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.SENDING, + "count": 1, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.FAILED, + "count": 3, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.TECHNICAL_FAILURE, + "count": 1, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.TEMPORARY_FAILURE, + "count": 2, + } in resp_json[ + "data" + ]["statistics"] assert resp_json["data"]["created_by"]["name"] == "Test User" @@ -641,15 +676,36 @@ def test_get_job_by_id_with_stats_for_old_job_where_notifications_have_been_purg ) assert resp_json["data"]["id"] == str(old_job.id) - assert {"status": NotificationStatus.CREATED, "count": 3,} in resp_json["data"]["statistics"] - assert {"status": NotificationStatus.SENDING, "count": 1,} in resp_json["data"]["statistics"] - assert {"status": NotificationStatus.FAILED, "count": 3,} in resp_json["data"]["statistics"] - assert {"status": NotificationStatus.TECHNICAL_FAILURE, "count": 1,} in resp_json["data"][ - "statistics" - ] - assert {"status": NotificationStatus.TEMPORARY_FAILURE, "count": 2,} in resp_json["data"][ - "statistics" - ] + assert { + "status": NotificationStatus.CREATED, + "count": 3, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.SENDING, + "count": 1, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.FAILED, + "count": 3, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.TECHNICAL_FAILURE, + "count": 1, + } in resp_json[ + "data" + ]["statistics"] + assert { + "status": NotificationStatus.TEMPORARY_FAILURE, + "count": 2, + } in resp_json[ + "data" + ]["statistics"] assert resp_json["data"]["created_by"]["name"] == "Test User" @@ -723,13 +779,24 @@ def test_get_jobs_should_return_statistics(admin_request, sample_template): assert len(resp_json["data"]) == 2 assert resp_json["data"][0]["id"] == str(job_2.id) - assert {"status": NotificationStatus.SENDING, "count": 3,} in resp_json["data"][0]["statistics"] + assert { + "status": NotificationStatus.SENDING, + "count": 3, + } in resp_json["data"][ + 0 + ]["statistics"] assert resp_json["data"][1]["id"] == str(job_1.id) - assert {"status": NotificationStatus.CREATED, "count": 3,} in resp_json["data"][1]["statistics"] + assert { + "status": NotificationStatus.CREATED, + "count": 3, + } in resp_json["data"][ + 1 + ]["statistics"] def test_get_jobs_should_return_no_stats_if_no_rows_in_notifications( - admin_request, sample_template, + admin_request, + sample_template, ): now = datetime.utcnow() earlier = datetime.utcnow() - timedelta(days=1) @@ -876,32 +943,49 @@ def test_get_jobs_should_retrieve_from_ft_notification_status_for_old_jobs( # some notifications created more than three days ago, some created after the midnight cutoff create_ft_notification_status( - date(2017, 6, 6), job=job_1, notification_status=NotificationStatus.DELIVERED, count=2, + date(2017, 6, 6), + job=job_1, + notification_status=NotificationStatus.DELIVERED, + count=2, ) create_ft_notification_status( - date(2017, 6, 7), job=job_1, notification_status=NotificationStatus.DELIVERED, count=4, + date(2017, 6, 7), + job=job_1, + notification_status=NotificationStatus.DELIVERED, + count=4, ) # job2's new enough create_notification( - job=job_2, status=NotificationStatus.CREATED, created_at=not_quite_three_days_ago, + job=job_2, + status=NotificationStatus.CREATED, + created_at=not_quite_three_days_ago, ) # this isn't picked up because the job is too new create_ft_notification_status( - date(2017, 6, 7), job=job_2, notification_status=NotificationStatus.DELIVERED, count=8, + date(2017, 6, 7), + job=job_2, + notification_status=NotificationStatus.DELIVERED, + count=8, ) # this isn't picked up - while the job is old, it started in last 3 days so we look at notification table instead create_ft_notification_status( - date(2017, 6, 7), job=job_3, notification_status=NotificationStatus.DELIVERED, count=16, + date(2017, 6, 7), + job=job_3, + notification_status=NotificationStatus.DELIVERED, + count=16, ) # this isn't picked up because we're using the ft status table for job_1 as it's old create_notification( - job=job_1, status=NotificationStatus.CREATED, created_at=not_quite_three_days_ago, + job=job_1, + status=NotificationStatus.CREATED, + created_at=not_quite_three_days_ago, ) resp_json = admin_request.get( - "job.get_jobs_by_service", service_id=sample_template.service_id, + "job.get_jobs_by_service", + service_id=sample_template.service_id, ) assert resp_json["data"][0]["id"] == str(job_3.id) @@ -935,26 +1019,38 @@ def test_get_scheduled_job_stats(admin_request): # Shouldn’t be counted – wrong status create_job( - service_1_template, job_status=JobStatus.FINISHED, scheduled_for="2017-07-17 07:00", + service_1_template, + job_status=JobStatus.FINISHED, + scheduled_for="2017-07-17 07:00", ) create_job( - service_1_template, job_status=JobStatus.IN_PROGRESS, scheduled_for="2017-07-17 08:00", + service_1_template, + job_status=JobStatus.IN_PROGRESS, + scheduled_for="2017-07-17 08:00", ) # Should be counted – service 1 create_job( - service_1_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 09:00", + service_1_template, + job_status=JobStatus.SCHEDULED, + scheduled_for="2017-07-17 09:00", ) create_job( - service_1_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 10:00", + service_1_template, + job_status=JobStatus.SCHEDULED, + scheduled_for="2017-07-17 10:00", ) create_job( - service_1_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 11:00", + service_1_template, + job_status=JobStatus.SCHEDULED, + scheduled_for="2017-07-17 11:00", ) # Should be counted – service 2 create_job( - service_2_template, job_status=JobStatus.SCHEDULED, scheduled_for="2017-07-17 11:00", + service_2_template, + job_status=JobStatus.SCHEDULED, + scheduled_for="2017-07-17 11:00", ) assert admin_request.get( From 287dcd6a46069ddd8e48158c03f26a93a5252eac Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 16 Feb 2024 15:49:46 -0500 Subject: [PATCH 193/259] More cleanup happening. Signed-off-by: Cliff Hill --- .../test_process_notification.py | 71 ++++--- tests/app/notifications/test_rest.py | 44 ++-- tests/app/notifications/test_validators.py | 103 +++++---- tests/app/performance_dashboard/test_rest.py | 9 +- tests/app/platform_stats/test_rest.py | 22 +- tests/app/service/test_rest.py | 195 ++++++++++-------- 6 files changed, 261 insertions(+), 183 deletions(-) diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index 71f8cb419..591ddfaad 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -11,7 +11,7 @@ from notifications_utils.recipients import ( ) from sqlalchemy.exc import SQLAlchemyError -from app.enums import ServicePermissionType, TemplateType +from app.enums import KeyType, NotificationType, ServicePermissionType, TemplateType from app.models import Notification, NotificationHistory from app.notifications.process_notifications import ( create_content_for_notification, @@ -79,7 +79,7 @@ def test_persist_notification_creates_and_save_to_db( recipient="+447111111111", service=sample_template.service, personalisation={}, - notification_type="sms", + notification_type=NotificationType.SMS, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, job_id=sample_job.id, @@ -123,7 +123,7 @@ def test_persist_notification_throws_exception_when_missing_template(sample_api_ recipient="+447111111111", service=sample_api_key.service, personalisation=None, - notification_type="sms", + notification_type=NotificationType.SMS, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, ) @@ -143,7 +143,7 @@ def test_persist_notification_with_optionals(sample_job, sample_api_key): recipient="+12028675309", service=sample_job.service, personalisation=None, - notification_type="sms", + notification_type=NotificationType.SMS, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, created_at=created_at, @@ -181,7 +181,7 @@ def test_persist_notification_cache_is_not_incremented_on_failure_to_create_noti recipient="+447111111111", service=sample_api_key.service, personalisation=None, - notification_type="sms", + notification_type=NotificationType.SMS, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, ) @@ -191,20 +191,38 @@ def test_persist_notification_cache_is_not_incremented_on_failure_to_create_noti @pytest.mark.parametrize( ("requested_queue, notification_type, key_type, expected_queue, expected_task"), [ - (None, "sms", "normal", "send-sms-tasks", "provider_tasks.deliver_sms"), - (None, "email", "normal", "send-email-tasks", "provider_tasks.deliver_email"), - (None, "sms", "team", "send-sms-tasks", "provider_tasks.deliver_sms"), + ( + None, + NotificationType.SMS, + KeyType.NORMAL, + "send-sms-tasks", + "provider_tasks.deliver_sms", + ), + ( + None, + NotificationType.EMAIL, + KeyType.NORMAL, + "send-email-tasks", + "provider_tasks.deliver_email", + ), + ( + None, + NotificationType.SMS, + KeyType.TEAM, + "send-sms-tasks", + "provider_tasks.deliver_sms", + ), ( "notify-internal-tasks", - "sms", - "normal", + NotificationType.SMS, + KeyType.NORMAL, "notify-internal-tasks", "provider_tasks.deliver_sms", ), ( "notify-internal-tasks", - "email", - "normal", + NotificationType.EMAIL, + KeyType.NORMAL, "notify-internal-tasks", "provider_tasks.deliver_email", ), @@ -245,7 +263,8 @@ def test_send_notification_to_queue_throws_exception_deletes_notification( with pytest.raises(Boto3Error): send_notification_to_queue(sample_notification, False) mocked.assert_called_once_with( - [(str(sample_notification.id))], queue="send-sms-tasks" + [(str(sample_notification.id))], + queue="send-sms-tasks", ) assert Notification.query.count() == 0 @@ -255,13 +274,13 @@ def test_send_notification_to_queue_throws_exception_deletes_notification( @pytest.mark.parametrize( "to_address, notification_type, expected", [ - ("+14254147755", "sms", True), - ("+14254147167", "sms", True), - ("simulate-delivered@notifications.service.gov.uk", "email", True), - ("simulate-delivered-2@notifications.service.gov.uk", "email", True), - ("simulate-delivered-3@notifications.service.gov.uk", "email", True), - ("2028675309", "sms", False), - ("valid_email@test.com", "email", False), + ("+14254147755", NotificationType.SMS, True), + ("+14254147167", NotificationType.SMS, True), + ("simulate-delivered@notifications.service.gov.uk", NotificationType.EMAIL, True), + ("simulate-delivered-2@notifications.service.gov.uk", NotificationType.EMAIL, True), + ("simulate-delivered-3@notifications.service.gov.uk", NotificationType.EMAIL, True), + ("2028675309", NotificationType.SMS, False), + ("valid_email@test.com", NotificationType.EMAIL, False), ], ) def test_simulated_recipient(notify_api, to_address, notification_type, expected): @@ -277,7 +296,7 @@ def test_simulated_recipient(notify_api, to_address, notification_type, expected """ formatted_address = None - if notification_type == "email": + if notification_type == NotificationType.EMAIL: formatted_address = validate_and_format_email_address(to_address) else: formatted_address = validate_and_format_phone_number(to_address) @@ -311,7 +330,7 @@ def test_persist_notification_with_international_info_stores_correct_info( recipient=recipient, service=sample_job.service, personalisation=None, - notification_type="sms", + notification_type=NotificationType.SMS, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, job_id=sample_job.id, @@ -334,7 +353,7 @@ def test_persist_notification_with_international_info_does_not_store_for_email( recipient="foo@bar.com", service=sample_job.service, personalisation=None, - notification_type="email", + notification_type=NotificationType.EMAIL, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, job_id=sample_job.id, @@ -368,7 +387,7 @@ def test_persist_sms_notification_stores_normalised_number( recipient=recipient, service=sample_job.service, personalisation=None, - notification_type="sms", + notification_type=NotificationType.SMS, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, job_id=sample_job.id, @@ -392,7 +411,7 @@ def test_persist_email_notification_stores_normalised_email( recipient=recipient, service=sample_job.service, personalisation=None, - notification_type="email", + notification_type=NotificationType.EMAIL, api_key_id=sample_api_key.id, key_type=sample_api_key.key_type, job_id=sample_job.id, @@ -415,7 +434,7 @@ def test_persist_notification_with_billable_units_stores_correct_info(mocker): personalisation=None, notification_type=template.template_type, api_key_id=None, - key_type="normal", + key_type=KeyType.NORMAL, billable_units=3, ) persisted_notification = Notification.query.all()[0] diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index b349c193f..0e09cd5fb 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -8,19 +8,19 @@ from notifications_python_client.authentication import create_jwt_token from app.dao.api_key_dao import save_model_api_key from app.dao.notifications_dao import dao_update_notification from app.dao.templates_dao import dao_update_template -from app.enums import KeyType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType from app.models import ApiKey from tests import create_service_authorization_header from tests.app.db import create_api_key, create_notification -@pytest.mark.parametrize("type", ("email", "sms")) +@pytest.mark.parametrize("type", (NotificationType.EMAIL, NotificationType.SMS)) def test_get_notification_by_id( client, sample_notification, sample_email_notification, type ): - if type == "email": + if type == NotificationType.EMAIL: notification_to_get = sample_email_notification - if type == "sms": + elif type == NotificationType.SMS: notification_to_get = sample_notification auth_header = create_service_authorization_header( @@ -46,13 +46,13 @@ def test_get_notification_by_id( @pytest.mark.parametrize("id", ["1234-badly-formatted-id-7890", "0"]) -@pytest.mark.parametrize("type", ("email", "sms")) +@pytest.mark.parametrize("type", (NotificationType.EMAIL, NotificationType.SMS)) def test_get_notification_by_invalid_id( client, sample_notification, sample_email_notification, id, type ): - if type == "email": + if type == NotificationType.EMAIL: notification_to_get = sample_email_notification - if type == "sms": + elif type == NotificationType.SMS: notification_to_get = sample_notification auth_header = create_service_authorization_header( service_id=notification_to_get.service_id @@ -420,7 +420,10 @@ def test_filter_by_template_type(client, sample_template, sample_email_template) notifications = json.loads(response.get_data(as_text=True)) assert len(notifications["notifications"]) == 1 - assert notifications["notifications"][0]["template"]["template_type"] == "sms" + assert ( + notifications["notifications"][0]["template"]["template_type"] + == TemplateType.SMS + ) assert response.status_code == 200 @@ -441,13 +444,13 @@ def test_filter_by_multiple_template_types( assert response.status_code == 200 notifications = json.loads(response.get_data(as_text=True)) assert len(notifications["notifications"]) == 2 - assert {"sms", "email"} == set( + assert {TemplateType.SMS, TemplateType.EMAIL} == { x["template"]["template_type"] for x in notifications["notifications"] - ) + } def test_filter_by_status(client, sample_email_template): - create_notification(sample_email_template, status="delivered") + create_notification(sample_email_template, status=NotificationStatus.DELIVERED) create_notification(sample_email_template) auth_header = create_service_authorization_header( @@ -458,13 +461,13 @@ def test_filter_by_status(client, sample_email_template): notifications = json.loads(response.get_data(as_text=True)) assert len(notifications["notifications"]) == 1 - assert notifications["notifications"][0]["status"] == "delivered" + assert notifications["notifications"][0]["status"] == NotificationStatus.DELIVERED assert response.status_code == 200 def test_filter_by_multiple_statuses(client, sample_email_template): - create_notification(sample_email_template, status="delivered") - create_notification(sample_email_template, status="sending") + create_notification(sample_email_template, status=NotificationStatus.DELIVERED) + create_notification(sample_email_template, status=NotificationStatus.SENDING) auth_header = create_service_authorization_header( service_id=sample_email_template.service_id @@ -477,9 +480,9 @@ def test_filter_by_multiple_statuses(client, sample_email_template): assert response.status_code == 200 notifications = json.loads(response.get_data(as_text=True)) assert len(notifications["notifications"]) == 2 - assert {"delivered", "sending"} == set( + assert {NotificationStatus.DELIVERED, NotificationStatus.SENDING} == { x["status"] for x in notifications["notifications"] - ) + } def test_filter_by_status_and_template_type( @@ -487,7 +490,7 @@ def test_filter_by_status_and_template_type( ): create_notification(sample_template) create_notification(sample_email_template) - create_notification(sample_email_template, status="delivered") + create_notification(sample_email_template, status=NotificationStatus.DELIVERED) auth_header = create_service_authorization_header( service_id=sample_email_template.service_id @@ -500,8 +503,11 @@ def test_filter_by_status_and_template_type( notifications = json.loads(response.get_data(as_text=True)) assert response.status_code == 200 assert len(notifications["notifications"]) == 1 - assert notifications["notifications"][0]["template"]["template_type"] == "email" - assert notifications["notifications"][0]["status"] == "delivered" + assert ( + notifications["notifications"][0]["template"]["template_type"] + == TemplateType.EMAIL + ) + assert notifications["notifications"][0]["status"] == NotificationStatus.DELIVERED def test_get_notification_by_id_returns_merged_template_content( diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index bc49f5ecb..25d4695bf 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -54,7 +54,7 @@ def enable_redis(notify_api): yield -@pytest.mark.parametrize("key_type", ["team", "normal"]) +@pytest.mark.parametrize("key_type", [KeyType.TEAM, KeyType.NORMAL]) def test_check_service_over_total_message_limit_fails( key_type, mocker, notify_db_session ): @@ -71,7 +71,7 @@ def test_check_service_over_total_message_limit_fails( assert e.value.fields == [] -@pytest.mark.parametrize("key_type", ["team", "normal"]) +@pytest.mark.parametrize("key_type", [KeyType.TEAM, KeyType.NORMAL]) def test_check_application_over_retention_limit_fails( key_type, mocker, notify_db_session ): @@ -142,7 +142,7 @@ def test_check_template_is_active_fails(sample_template): assert e.value.fields == [{"template": "Template has been deleted"}] -@pytest.mark.parametrize("key_type", ["test", "normal"]) +@pytest.mark.parametrize("key_type", [KeyType.TEST, KeyType.NORMAL]) def test_service_can_send_to_recipient_passes(key_type, notify_db_session): trial_mode_service = create_service(service_name="trial mode", restricted=True) serialised_service = SerialisedService.from_id(trial_mode_service.id) @@ -175,7 +175,11 @@ def test_service_can_send_to_recipient_passes_with_non_normalized_number( serialised_service = SerialisedService.from_id(sample_service.id) assert ( - service_can_send_to_recipient(recipient_number, "team", serialised_service) + service_can_send_to_recipient( + recipient_number, + KeyType.TEAM, + serialised_service, + ) is None ) @@ -194,12 +198,12 @@ def test_service_can_send_to_recipient_passes_with_non_normalized_email( serialised_service = SerialisedService.from_id(sample_service.id) assert ( - service_can_send_to_recipient(recipient_email, "team", serialised_service) + service_can_send_to_recipient(recipient_email, KeyType.TEAM, serialised_service) is None ) -@pytest.mark.parametrize("key_type", ["test", "normal"]) +@pytest.mark.parametrize("key_type", [KeyType.TEST, KeyType.NORMAL]) def test_service_can_send_to_recipient_passes_for_live_service_non_team_member( key_type, sample_service ): @@ -222,12 +226,19 @@ def test_service_can_send_to_recipient_passes_for_guest_list_recipient_passes( create_service_guest_list(sample_service, email_address="some_other_email@test.com") assert ( service_can_send_to_recipient( - "some_other_email@test.com", "team", sample_service + "some_other_email@test.com", KeyType.TEAM, sample_service ) is None ) create_service_guest_list(sample_service, mobile_number="2028675309") - assert service_can_send_to_recipient("2028675309", "team", sample_service) is None + assert ( + service_can_send_to_recipient( + "2028675309", + KeyType.TEAM, + sample_service, + ) + is None + ) @pytest.mark.parametrize( @@ -246,7 +257,7 @@ def test_service_can_send_to_recipient_fails_when_ignoring_guest_list( with pytest.raises(BadRequestError) as exec_info: service_can_send_to_recipient( next(iter(recipient.values())), - "team", + KeyType.TEAM, sample_service, allow_guest_list_recipients=False, ) @@ -262,9 +273,9 @@ def test_service_can_send_to_recipient_fails_when_ignoring_guest_list( @pytest.mark.parametrize( "key_type, error_message", [ - ("team", "Can’t send to this recipient using a team-only API key"), + (KeyType.TEAM, "Can’t send to this recipient using a team-only API key"), ( - "normal", + KeyType.NORMAL, "Can’t send to this recipient when service is in trial mode – see https://www.notifications.service.gov.uk/trial-mode", # noqa ), ], @@ -287,7 +298,7 @@ def test_service_can_send_to_recipient_fails_when_mobile_number_is_not_on_team( sample_service, ): with pytest.raises(BadRequestError) as e: - service_can_send_to_recipient("0758964221", "team", sample_service) + service_can_send_to_recipient("0758964221", KeyType.TEAM, sample_service) assert e.value.status_code == 400 assert e.value.message == "Can’t send to this recipient using a team-only API key" assert e.value.fields == [] @@ -295,7 +306,7 @@ def test_service_can_send_to_recipient_fails_when_mobile_number_is_not_on_team( @pytest.mark.parametrize("char_count", [612, 0, 494, 200, 918]) @pytest.mark.parametrize("show_prefix", [True, False]) -@pytest.mark.parametrize("template_type", ["sms", "email"]) +@pytest.mark.parametrize("template_type", [TemplateType.SMS, TemplateType.EMAIL]) def test_check_is_message_too_long_passes( notify_db_session, show_prefix, char_count, template_type ): @@ -316,7 +327,7 @@ def test_check_is_message_too_long_fails(notify_db_session, show_prefix, char_co with pytest.raises(BadRequestError) as e: service = create_service(prefix_sms=show_prefix) t = create_template( - service=service, content="a" * char_count, template_type="sms" + service=service, content="a" * char_count, template_type=TemplateType.SMS ) template = templates_dao.dao_get_template_by_id_and_service_id( template_id=t.id, service_id=service.id @@ -340,7 +351,7 @@ def test_check_is_message_too_long_passes_for_long_email(sample_service): t = create_template( service=sample_service, content="a" * email_character_count, - template_type="email", + template_type=TemplateType.EMAIL, ) template = templates_dao.dao_get_template_by_id_and_service_id( template_id=t.id, service_id=t.service_id @@ -392,15 +403,15 @@ def test_check_notification_content_is_not_empty_fails( def test_validate_template(sample_service): - template = create_template(sample_service, template_type="email") - validate_template(template.id, {}, sample_service, "email") + template = create_template(sample_service, template_type=TemplateType.EMAIL) + validate_template(template.id, {}, sample_service, NotificationType.EMAIL) @pytest.mark.parametrize("check_char_count", [True, False]) def test_validate_template_calls_all_validators( mocker, fake_uuid, sample_service, check_char_count ): - template = create_template(sample_service, template_type="email") + template = create_template(sample_service, template_type=TemplateType.EMAIL) mock_check_type = mocker.patch( "app.notifications.validators.check_template_is_for_notification_type" ) @@ -418,10 +429,14 @@ def test_validate_template_calls_all_validators( "app.notifications.validators.check_is_message_too_long" ) template, template_with_content = validate_template( - template.id, {}, sample_service, "email", check_char_count=check_char_count + template.id, + {}, + sample_service, + NotificationType.EMAIL, + check_char_count=check_char_count, ) - mock_check_type.assert_called_once_with("email", "email") + mock_check_type.assert_called_once_with(NotificationType.EMAIL, TemplateType.EMAIL) mock_check_if_active.assert_called_once_with(template) mock_create_conent.assert_called_once_with(template, {}) mock_check_not_empty.assert_called_once_with("content") @@ -434,7 +449,7 @@ def test_validate_template_calls_all_validators( def test_validate_template_calls_all_validators_exception_message_too_long( mocker, fake_uuid, sample_service ): - template = create_template(sample_service, template_type="email") + template = create_template(sample_service, template_type=TemplateType.EMAIL) mock_check_type = mocker.patch( "app.notifications.validators.check_template_is_for_notification_type" ) @@ -452,7 +467,11 @@ def test_validate_template_calls_all_validators_exception_message_too_long( "app.notifications.validators.check_is_message_too_long" ) template, template_with_content = validate_template( - template.id, {}, sample_service, "email", check_char_count=False + template.id, + {}, + sample_service, + NotificationType.EMAIL, + check_char_count=False, ) mock_check_type.assert_called_once_with("email", "email") @@ -462,20 +481,15 @@ def test_validate_template_calls_all_validators_exception_message_too_long( assert not mock_check_message_is_too_long.called -@pytest.mark.parametrize("key_type", ["team", "live", "test"]) +@pytest.mark.parametrize("key_type", [KeyType.TEAM, KeyType.NORMAL, KeyType.TEST]) def test_check_service_over_api_rate_limit_when_exceed_rate_limit_request_fails_raises_error( key_type, sample_service, mocker ): with freeze_time("2016-01-01 12:00:00.000000"): - if key_type == "live": - api_key_type = "normal" - else: - api_key_type = key_type - mocker.patch("app.redis_store.exceeded_rate_limit", return_value=True) sample_service.restricted = True - api_key = create_api_key(sample_service, key_type=api_key_type) + api_key = create_api_key(sample_service, key_type=key_type) serialised_service = SerialisedService.from_id(sample_service.id) serialised_api_key = SerialisedAPIKeyCollection.from_service_id( serialised_service.id @@ -490,17 +504,16 @@ def test_check_service_over_api_rate_limit_when_exceed_rate_limit_request_fails_ 60, ) assert e.value.status_code == 429 - assert ( - e.value.message - == "Exceeded rate limit for key type {} of {} requests per {} seconds".format( - key_type.upper(), sample_service.rate_limit, 60 - ) + assert e.value.message == ( + f"Exceeded rate limit for key type {key_type.upper()} of " + f"{sample_service.rate_limit} requests per {60} seconds" ) assert e.value.fields == [] def test_check_service_over_api_rate_limit_when_rate_limit_has_not_exceeded_limit_succeeds( - sample_service, mocker + sample_service, + mocker, ): with freeze_time("2016-01-01 12:00:00.000000"): mocker.patch("app.redis_store.exceeded_rate_limit", return_value=False) @@ -551,7 +564,7 @@ def test_check_rate_limiting_validates_api_rate_limit_and_daily_limit( mock_rate_limit.assert_called_once_with(service, api_key) -@pytest.mark.parametrize("key_type", ["test", "normal"]) +@pytest.mark.parametrize("key_type", [KeyType.TEST, KeyType.NORMAL]) def test_validate_and_format_recipient_fails_when_international_number_and_service_does_not_allow_int_sms( key_type, notify_db_session, @@ -590,7 +603,10 @@ def test_validate_and_format_recipient_fails_when_no_recipient(): assert e.value.message == "Recipient can't be empty" -@pytest.mark.parametrize("notification_type", ["sms", "email"]) +@pytest.mark.parametrize( + "notification_type", + [NotificationType.SMS, NotificationType.EMAIL], +) def test_check_service_email_reply_to_id_where_reply_to_id_is_none(notification_type): assert check_service_email_reply_to_id(None, None, notification_type) is None @@ -638,7 +654,10 @@ def test_check_service_email_reply_to_id_where_reply_to_id_is_not_found( ) -@pytest.mark.parametrize("notification_type", ["sms", "email"]) +@pytest.mark.parametrize( + "notification_type", + [NotificationType.SMS, NotificationType.EMAIL], +) def test_check_service_sms_sender_id_where_sms_sender_id_is_none(notification_type): assert check_service_sms_sender_id(None, None, notification_type) is None @@ -684,7 +703,10 @@ def test_check_service_sms_sender_id_where_sms_sender_is_not_found( ) -@pytest.mark.parametrize("notification_type", ["sms", "email"]) +@pytest.mark.parametrize( + "notification_type", + [NotificationType.SMS, NotificationType.EMAIL], +) def test_check_reply_to_with_empty_reply_to(sample_service, notification_type): assert check_reply_to(sample_service.id, None, notification_type) is None @@ -778,6 +800,7 @@ def test_check_service_over_total_message_limit(mocker, sample_service): get_redis_mock = mocker.patch("app.notifications.validators.redis_store.get") get_redis_mock.return_value = None service_stats = check_service_over_total_message_limit( - KeyType.NORMAL, sample_service + KeyType.NORMAL, + sample_service, ) assert service_stats == 0 diff --git a/tests/app/performance_dashboard/test_rest.py b/tests/app/performance_dashboard/test_rest.py index 79614a3e9..39757668b 100644 --- a/tests/app/performance_dashboard/test_rest.py +++ b/tests/app/performance_dashboard/test_rest.py @@ -1,5 +1,6 @@ from datetime import date +from app.enums import TemplateType from tests.app.db import ( create_ft_notification_status, create_process_time, @@ -9,10 +10,14 @@ from tests.app.db import ( def test_performance_dashboard(sample_service, admin_request): template_sms = create_template( - service=sample_service, template_type="sms", template_name="a" + service=sample_service, + template_type=TemplateType.SMS, + template_name="a", ) template_email = create_template( - service=sample_service, template_type="email", template_name="b" + service=sample_service, + template_type=TemplateType.EMAIL, + template_name="b", ) create_ft_notification_status( local_date=date(2021, 2, 28), diff --git a/tests/app/platform_stats/test_rest.py b/tests/app/platform_stats/test_rest.py index 88ed93564..ffd6bd8cf 100644 --- a/tests/app/platform_stats/test_rest.py +++ b/tests/app/platform_stats/test_rest.py @@ -3,7 +3,7 @@ from datetime import date, datetime import pytest from freezegun import freeze_time -from app.enums import TemplateType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType from app.errors import InvalidRequest from app.platform_stats.rest import validate_date_range_is_within_a_financial_year from tests.app.db import ( @@ -64,17 +64,27 @@ def test_get_platform_stats_with_real_query(admin_request, notify_db_session): service=service_1, template_type=TemplateType.EMAIL, ) - create_ft_notification_status(date(2018, 10, 29), "sms", service_1, count=10) - create_ft_notification_status(date(2018, 10, 29), "email", service_1, count=3) + create_ft_notification_status( + date(2018, 10, 29), NotificationType.SMS, service_1, count=10 + ) + create_ft_notification_status( + date(2018, 10, 29), NotificationType.EMAIL, service_1, count=3 + ) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 11, 0, 0), key_type="test" + sms_template, + created_at=datetime(2018, 10, 31, 11, 0, 0), + key_type=KeyType.TEST, ) create_notification( - sms_template, created_at=datetime(2018, 10, 31, 12, 0, 0), status="delivered" + sms_template, + created_at=datetime(2018, 10, 31, 12, 0, 0), + status=NotificationStatus.DELIVERED, ) create_notification( - email_template, created_at=datetime(2018, 10, 31, 13, 0, 0), status="delivered" + email_template, + created_at=datetime(2018, 10, 31, 13, 0, 0), + status=NotificationStatus.DELIVERED, ) response = admin_request.get( diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d344e6aae..a825c5acb 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -16,6 +16,7 @@ from app.dao.templates_dao import dao_redact_template from app.dao.users_dao import save_model_user from app.enums import ( KeyType, + NotificationStatus, NotificationType, OrganizationType, PermissionType, @@ -136,9 +137,10 @@ def test_find_services_by_name_finds_services(notify_db_session, admin_request, "app.service.rest.get_services_by_partial_name", return_value=[service_1, service_2], ) - response = admin_request.get("service.find_services_by_name", service_name="ABC")[ - "data" - ] + response = admin_request.get( + "service.find_services_by_name", + service_name="ABC", + )["data"] mock_get_services_by_partial_name.assert_called_once_with("ABC") assert len(response) == 2 @@ -172,11 +174,13 @@ def test_get_live_services_data(sample_user, admin_request): service = create_service(go_live_user=sample_user, go_live_at=datetime(2018, 1, 1)) service_2 = create_service( - service_name="second", go_live_at=datetime(2019, 1, 1), go_live_user=sample_user + service_name="second", + go_live_at=datetime(2019, 1, 1), + go_live_user=sample_user, ) sms_template = create_template(service=service) - email_template = create_template(service=service, template_type="email") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) dao_add_service_to_organization(service=service, organization_id=org.id) create_ft_billing(local_date="2019-04-20", template=sms_template) create_ft_billing(local_date="2019-04-20", template=email_template) @@ -269,7 +273,9 @@ def test_get_service_by_id_returns_organization_type( admin_request, sample_service, detailed ): json_resp = admin_request.get( - "service.get_service_by_id", service_id=sample_service.id, detailed=detailed + "service.get_service_by_id", + service_id=sample_service.id, + detailed=detailed, ) assert json_resp["data"]["organization_type"] is None @@ -297,7 +303,8 @@ def test_get_service_by_id_has_default_service_permissions( admin_request, sample_service ): json_resp = admin_request.get( - "service.get_service_by_id", service_id=sample_service.id + "service.get_service_by_id", + service_id=sample_service.id, ) assert set(json_resp["data"]["permissions"]) == { @@ -309,7 +316,9 @@ def test_get_service_by_id_has_default_service_permissions( def test_get_service_by_id_should_404_if_no_service(admin_request, notify_db_session): json_resp = admin_request.get( - "service.get_service_by_id", service_id=uuid.uuid4(), _expected_status=404 + "service.get_service_by_id", + service_id=uuid.uuid4(), + _expected_status=404, ) assert json_resp["result"] == "error" @@ -381,7 +390,9 @@ def test_create_service( } json_resp = admin_request.post( - "service.create_service", _data=data, _expected_status=201 + "service.create_service", + _data=data, + _expected_status=201, ) assert json_resp["data"]["id"] @@ -452,7 +463,9 @@ def test_create_service_with_domain_sets_organization( } json_resp = admin_request.post( - "service.create_service", _data=data, _expected_status=201 + "service.create_service", + _data=data, + _expected_status=201, ) if expected_org: @@ -679,7 +692,9 @@ def test_create_service_should_throw_duplicate_key_constraint_for_existing_email def test_update_service(client, notify_db_session, sample_service): brand = EmailBranding( - colour="#000000", logo="justice-league.png", name="Justice League" + colour="#000000", + logo="justice-league.png", + name="Justice League", ) notify_db_session.add(brand) notify_db_session.commit() @@ -731,7 +746,9 @@ def test_update_service_remove_email_branding( admin_request, notify_db_session, sample_service ): brand = EmailBranding( - colour="#000000", logo="justice-league.png", name="Justice League" + colour="#000000", + logo="justice-league.png", + name="Justice League", ) sample_service.email_branding = brand notify_db_session.commit() @@ -748,7 +765,9 @@ def test_update_service_change_email_branding( admin_request, notify_db_session, sample_service ): brand1 = EmailBranding( - colour="#000000", logo="justice-league.png", name="Justice League" + colour="#000000", + logo="justice-league.png", + name="Justice League", ) brand2 = EmailBranding(colour="#111111", logo="avengers.png", name="Avengers") notify_db_session.add_all([brand1, brand2]) @@ -1251,13 +1270,13 @@ def test_add_existing_user_to_another_service_with_all_permissions( data = { "permissions": [ - {"permission": "send_emails"}, - {"permission": "send_texts"}, - {"permission": "manage_users"}, - {"permission": "manage_settings"}, - {"permission": "manage_api_keys"}, - {"permission": "manage_templates"}, - {"permission": "view_activity"}, + {"permission": PermissionType.SEND_EMAILS}, + {"permission": PermissionType.SEND_TEXTS}, + {"permission": PermissionType.MANAGE_USERS}, + {"permission": PermissionType.MANAGE_SETTINGS}, + {"permission": PermissionType.MANAGE_API_KEYS}, + {"permission": PermissionType.MANAGE_TEMPLATES}, + {"permission": PermissionType.VIEW_ACTIVITY}, ], "folder_permissions": [], } @@ -1320,8 +1339,8 @@ def test_add_existing_user_to_another_service_with_send_permissions( data = { "permissions": [ - {"permission": "send_emails"}, - {"permission": "send_texts"}, + {"permission": PermissionType.SEND_EMAILS}, + {"permission": PermissionType.SEND_TEXTS}, ], "folder_permissions": [], } @@ -1367,9 +1386,9 @@ def test_add_existing_user_to_another_service_with_manage_permissions( data = { "permissions": [ - {"permission": "manage_users"}, - {"permission": "manage_settings"}, - {"permission": "manage_templates"}, + {"permission": PermissionType.MANAGE_USERS}, + {"permission": PermissionType.MANAGE_SETTINGS}, + {"permission": PermissionType.MANAGE_TEMPLATES}, ] } @@ -1395,9 +1414,9 @@ def test_add_existing_user_to_another_service_with_manage_permissions( permissions = json_resp["data"]["permissions"][str(sample_service.id)] expected_permissions = [ - "manage_users", - "manage_settings", - "manage_templates", + PermissionType.MANAGE_USERS, + PermissionType.MANAGE_SETTINGS, + PermissionType.MANAGE_TEMPLATES, ] assert sorted(expected_permissions) == sorted(permissions) @@ -1420,7 +1439,7 @@ def test_add_existing_user_to_another_service_with_folder_permissions( folder_2 = create_template_folder(sample_service) data = { - "permissions": [{"permission": "manage_api_keys"}], + "permissions": [{"permission": PermissionType.MANAGE_API_KEYS}], "folder_permissions": [str(folder_1.id), str(folder_2.id)], } @@ -1457,7 +1476,7 @@ def test_add_existing_user_to_another_service_with_manage_api_keys( ) save_model_user(user_to_add, validated_email_access=True) - data = {"permissions": [{"permission": "manage_api_keys"}]} + data = {"permissions": [{"permission": PermissionType.MANAGE_API_KEYS}]} auth_header = create_admin_authorization_header() @@ -1748,7 +1767,7 @@ def test_get_all_notifications_for_service_filters_notifications_when_using_post auth_header = create_admin_authorization_header() data = { "page": 1, - "template_type": ["sms"], + "template_type": [TemplateType.SMS], "status": ["created", "sending"], "to": "0855", } @@ -2059,7 +2078,9 @@ def test_set_sms_prefixing_for_service_cant_be_none( def test_get_detailed_service( sample_template, client, sample_service, today_only, stats ): - create_ft_notification_status(date(2000, 1, 1), "sms", sample_service, count=1) + create_ft_notification_status( + date(2000, 1, 1), NotificationType.SMS, sample_service, count=1 + ) with freeze_time("2000-01-02T12:00:00"): create_notification(template=sample_template, status="created") resp = client.get( @@ -2259,21 +2280,23 @@ def test_get_detailed_services_for_date_range( create_ft_notification_status( local_date=(datetime.utcnow() - timedelta(days=3)).date(), service=sample_template.service, - notification_type="sms", + notification_type=NotificationType.SMS, ) create_ft_notification_status( local_date=(datetime.utcnow() - timedelta(days=2)).date(), service=sample_template.service, - notification_type="sms", + notification_type=NotificationType.SMS, ) create_ft_notification_status( local_date=(datetime.utcnow() - timedelta(days=1)).date(), service=sample_template.service, - notification_type="sms", + notification_type=NotificationType.SMS, ) create_notification( - template=sample_template, created_at=datetime.utcnow(), status="delivered" + template=sample_template, + created_at=datetime.utcnow(), + status=NotificationStatus.DELIVERED, ) start_date = (datetime.utcnow() - timedelta(days=start_date_delta)).date() @@ -2337,9 +2360,8 @@ def test_search_for_notification_by_to_field_return_empty_list_if_there_is_no_ma create_notification(sample_email_template, to_field="jack@gmail.com") response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - notification1.service_id, "+447700900800", "sms" - ), + f"/service/{notification1.service_id}/notifications?" + f"to={+447700900800}&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2368,9 +2390,8 @@ def test_search_for_notification_by_to_field_return_multiple_matches( ) response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - notification1.service_id, "+447700900855", "sms" - ), + f"/service/{notification1.service_id}/notifications?" + f"to={+447700900855}&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2397,9 +2418,8 @@ def test_search_for_notification_by_to_field_returns_next_link_if_more_than_50( ) response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - sample_template.service_id, "+447700900855", "sms" - ), + f"/service/{sample_template.service_id}/notifications?" + f"to={+447700900855}&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) assert response.status_code == 200 @@ -2422,9 +2442,8 @@ def test_search_for_notification_by_to_field_returns_no_next_link_if_50_or_less( ) response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - sample_template.service_id, "+447700900855", "sms" - ), + f"/service/{sample_template.service_id}/notifications?" + f"to={+447700900855}&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) assert response.status_code == 200 @@ -2447,7 +2466,7 @@ def test_update_service_calls_send_notification_as_service_becomes_live( auth_header = create_admin_authorization_header() resp = client.post( - "service/{}".format(restricted_service.id), + f"service/{restricted_service.id}", data=json.dumps(data), headers=[auth_header], content_type="application/json", @@ -2494,7 +2513,7 @@ def test_update_service_does_not_call_send_notification_when_restricted_not_chan auth_header = create_admin_authorization_header() resp = client.post( - "service/{}".format(sample_service.id), + f"service/{sample_service.id}", data=json.dumps(data), headers=[auth_header], content_type="application/json", @@ -2522,9 +2541,8 @@ def test_search_for_notification_by_to_field_filters_by_status(client, sample_te ) response = client.get( - "/service/{}/notifications?to={}&status={}&template_type={}".format( - notification1.service_id, "+447700900855", "delivered", "sms" - ), + f"/service/{notification1.service_id}/notifications?to={+447700900855}" + f"&status={NotificationStatus.DELIVERED}&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2555,9 +2573,9 @@ def test_search_for_notification_by_to_field_filters_by_statuses( ) response = client.get( - "/service/{}/notifications?to={}&status={}&status={}&template_type={}".format( - notification1.service_id, "+447700900855", "delivered", "sending", "sms" - ), + f"/service/{notification1.service_id}/notifications?to={+447700900855}" + f"&status={NotificationStatus.DELIVERED}&status={NotificationStatus.SENDING}" + f"&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2583,9 +2601,8 @@ def test_search_for_notification_by_to_field_returns_content( ) response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - sample_template_with_placeholders.service_id, "+447700900855", "sms" - ), + f"/service/{sample_template_with_placeholders.service_id}/notifications?" + f"to={+447700900855}&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2689,9 +2706,8 @@ def test_search_for_notification_by_to_field_returns_personlisation( ) response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - sample_template_with_placeholders.service_id, "+447700900855", "sms" - ), + f"/service/{sample_template_with_placeholders.service_id}/notifications?" + f"to={+447700900855}&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2709,7 +2725,9 @@ def test_search_for_notification_by_to_field_returns_notifications_by_type( client, sample_template, sample_email_template ): sms_notification = create_notification( - sample_template, to_field="+447700900855", normalised_to="447700900855" + sample_template, + to_field="+447700900855", + normalised_to="447700900855", ) create_notification( sample_email_template, @@ -2718,9 +2736,8 @@ def test_search_for_notification_by_to_field_returns_notifications_by_type( ) response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - sms_notification.service_id, "0770", "sms" - ), + f"/service/{sms_notification.service_id}/notifications?to={'0770'}" + f"&template_type={TemplateType.SMS}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2734,7 +2751,7 @@ def test_get_email_reply_to_addresses_when_there_are_no_reply_to_email_addresses client, sample_service ): response = client.get( - "/service/{}/email-reply-to".format(sample_service.id), + f"/service/{sample_service.id}/email-reply-to", headers=[create_admin_authorization_header()], ) @@ -2747,7 +2764,7 @@ def test_get_email_reply_to_addresses_with_one_email_address(client, notify_db_s create_reply_to_email(service, "test@mail.com") response = client.get( - "/service/{}/email-reply-to".format(service.id), + f"/service/{service.id}/email-reply-to", headers=[create_admin_authorization_header()], ) json_response = json.loads(response.get_data(as_text=True)) @@ -2768,7 +2785,7 @@ def test_get_email_reply_to_addresses_with_multiple_email_addresses( reply_to_b = create_reply_to_email(service, "test_b@mail.com", False) response = client.get( - "/service/{}/email-reply-to".format(service.id), + f"/service/{service.id}/email-reply-to", headers=[create_admin_authorization_header()], ) json_response = json.loads(response.get_data(as_text=True)) @@ -3065,7 +3082,7 @@ def test_add_service_sms_sender_when_it_is_an_inbound_number_updates_the_only_ex "inbound_number_id": str(inbound_number.id), } response = client.post( - "/service/{}/sms-sender".format(service.id), + f"/service/{service.id}/sms-sender", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3096,7 +3113,7 @@ def test_add_service_sms_sender_when_it_is_an_inbound_number_inserts_new_sms_sen "inbound_number_id": str(inbound_number.id), } response = client.post( - "/service/{}/sms-sender".format(service.id), + f"/service/{service.id}/sms-sender", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3122,7 +3139,7 @@ def test_add_service_sms_sender_switches_default(client, notify_db_session): "is_default": True, } response = client.post( - "/service/{}/sms-sender".format(service.id), + f"/service/{service.id}/sms-sender", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3141,7 +3158,7 @@ def test_add_service_sms_sender_switches_default(client, notify_db_session): def test_add_service_sms_sender_return_404_when_service_does_not_exist(client): data = {"sms_sender": "12345", "is_default": False} response = client.post( - "/service/{}/sms-sender".format(uuid.uuid4()), + f"/service/{uuid.uuid4()}/sms-sender", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3164,7 +3181,7 @@ def test_update_service_sms_sender(client, notify_db_session): "is_default": False, } response = client.post( - "/service/{}/sms-sender/{}".format(service.id, service_sms_sender.id), + f"/service/{service.id}/sms-sender/{service_sms_sender.id}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3188,7 +3205,7 @@ def test_update_service_sms_sender_switches_default(client, notify_db_session): "is_default": True, } response = client.post( - "/service/{}/sms-sender/{}".format(service.id, service_sms_sender.id), + f"/service/{service.id}/sms-sender/{service_sms_sender.id}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3221,7 +3238,7 @@ def test_update_service_sms_sender_does_not_allow_sender_update_for_inbound_numb "inbound_number_id": str(inbound_number.id), } response = client.post( - "/service/{}/sms-sender/{}".format(service.id, service_sms_sender.id), + f"/service/{service.id}/sms-sender/{service_sms_sender.id}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3234,7 +3251,7 @@ def test_update_service_sms_sender_does_not_allow_sender_update_for_inbound_numb def test_update_service_sms_sender_return_404_when_service_does_not_exist(client): data = {"sms_sender": "12345", "is_default": False} response = client.post( - "/service/{}/sms-sender/{}".format(uuid.uuid4(), uuid.uuid4()), + f"/service/{uuid.uuid4()}/sms-sender/{uuid.uuid4()}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -3288,9 +3305,7 @@ def test_get_service_sms_sender_by_id(client, notify_db_session): service=create_service(), sms_sender="1235", is_default=False ) response = client.get( - "/service/{}/sms-sender/{}".format( - service_sms_sender.service_id, service_sms_sender.id - ), + f"/service/{service_sms_sender.service_id}/sms-sender/{service_sms_sender.id}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -3307,7 +3322,7 @@ def test_get_service_sms_sender_by_id_returns_404_when_service_does_not_exist( service=create_service(), sms_sender="1235", is_default=False ) response = client.get( - "/service/{}/sms-sender/{}".format(uuid.uuid4(), service_sms_sender.id), + f"/service/{uuid.uuid4()}/sms-sender/{service_sms_sender.id}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -3321,7 +3336,7 @@ def test_get_service_sms_sender_by_id_returns_404_when_sms_sender_does_not_exist ): service = create_service() response = client.get( - "/service/{}/sms-sender/{}".format(service.id, uuid.uuid4()), + f"/service/{service.id}/sms-sender/{uuid.uuid4()}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -3335,7 +3350,7 @@ def test_get_service_sms_senders_for_service(client, notify_db_session): service=create_service(), sms_sender="second", is_default=False ) response = client.get( - "/service/{}/sms-sender".format(service_sms_sender.service_id), + f"/service/{service_sms_sender.service_id}/sms-sender", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -3354,7 +3369,7 @@ def test_get_service_sms_senders_for_service_returns_empty_list_when_service_doe client, ): response = client.get( - "/service/{}/sms-sender".format(uuid.uuid4()), + f"/service/{uuid.uuid4()}/sms-sender", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -3386,15 +3401,15 @@ def test_get_organization_for_service_id_return_empty_dict_if_service_not_in_org def test_get_monthly_notification_data_by_service(sample_service, admin_request): create_ft_notification_status( date(2019, 4, 17), - notification_type="sms", + notification_type=NotificationType.SMS, service=sample_service, - notification_status="delivered", + notification_status=NotificationStatus.DELIVERED, ) create_ft_notification_status( date(2019, 3, 5), - notification_type="email", + notification_type=NotificationType.EMAIL, service=sample_service, - notification_status="sending", + notification_status=NotificationStatus.SENDING, count=4, ) response = admin_request.get( @@ -3408,7 +3423,7 @@ def test_get_monthly_notification_data_by_service(sample_service, admin_request) "2019-03-01", str(sample_service.id), "Sample service", - "email", + NotificationType.EMAIL, 4, 0, 0, @@ -3420,7 +3435,7 @@ def test_get_monthly_notification_data_by_service(sample_service, admin_request) "2019-04-01", str(sample_service.id), "Sample service", - "sms", + NotificationType.SMS, 0, 1, 0, From 8e8749a40e1518b7f433bf027ba6a9c2d86a101a Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 16 Feb 2024 16:30:17 -0500 Subject: [PATCH 194/259] More fixes. Signed-off-by: Cliff Hill --- .../test_service_data_retention_rest.py | 40 ++-- tests/app/service/test_statistics.py | 217 +++++++++++++----- tests/app/service/test_statistics_rest.py | 32 ++- 3 files changed, 197 insertions(+), 92 deletions(-) diff --git a/tests/app/service/test_service_data_retention_rest.py b/tests/app/service/test_service_data_retention_rest.py index 4defec5c0..035b11b42 100644 --- a/tests/app/service/test_service_data_retention_rest.py +++ b/tests/app/service/test_service_data_retention_rest.py @@ -1,6 +1,7 @@ import json import uuid +from app.enums import NotificationType from app.models import ServiceDataRetention from tests import create_admin_authorization_header from tests.app.db import create_service_data_retention @@ -13,7 +14,7 @@ def test_get_service_data_retention(client, sample_service): ) response = client.get( - "/service/{}/data-retention".format(str(sample_service.id)), + f"/service/{sample_service.id!s}/data-retention", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -29,7 +30,7 @@ def test_get_service_data_retention(client, sample_service): def test_get_service_data_retention_returns_empty_list(client, sample_service): response = client.get( - "/service/{}/data-retention".format(str(sample_service.id)), + f"/service/{sample_service.id!s}/data-retention", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -42,9 +43,8 @@ def test_get_service_data_retention_returns_empty_list(client, sample_service): def test_get_data_retention_for_service_notification_type(client, sample_service): data_retention = create_service_data_retention(service=sample_service) response = client.get( - "/service/{}/data-retention/notification-type/{}".format( - sample_service.id, "sms" - ), + f"/service/{sample_service.id}/data-retention/" + f"notification-type/{NotificationType.SMS}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -57,15 +57,17 @@ def test_get_data_retention_for_service_notification_type(client, sample_service def test_get_service_data_retention_by_id(client, sample_service): sms_data_retention = create_service_data_retention(service=sample_service) create_service_data_retention( - service=sample_service, notification_type="email", days_of_retention=10 + service=sample_service, + notification_type=NotificationType.EMAIL, + days_of_retention=10, ) create_service_data_retention( - service=sample_service, notification_type="letter", days_of_retention=30 + service=sample_service, + notification_type=NotificationType.LETTER, + days_of_retention=30, ) response = client.get( - "/service/{}/data-retention/{}".format( - str(sample_service.id), sms_data_retention.id - ), + f"/service/{sample_service.id!s}/data-retention/{sms_data_retention.id}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -79,7 +81,7 @@ def test_get_service_data_retention_by_id_returns_none_when_no_data_retention_ex client, sample_service ): response = client.get( - "/service/{}/data-retention/{}".format(str(sample_service.id), uuid.uuid4()), + f"/service/{sample_service.id!s}/data-retention/{uuid.uuid4()}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -90,9 +92,9 @@ def test_get_service_data_retention_by_id_returns_none_when_no_data_retention_ex def test_create_service_data_retention(client, sample_service): - data = {"notification_type": "sms", "days_of_retention": 3} + data = {"notification_type": NotificationType.SMS, "days_of_retention": 3} response = client.post( - "/service/{}/data-retention".format(str(sample_service.id)), + f"/service/{sample_service.id!s}/data-retention", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -113,7 +115,7 @@ def test_create_service_data_retention_returns_400_when_notification_type_is_inv ): data = {"notification_type": "unknown", "days_of_retention": 3} response = client.post( - "/service/{}/data-retention".format(str(uuid.uuid4())), + f"/service/{uuid.uuid4()!s}/data-retention", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -133,9 +135,9 @@ def test_create_service_data_retention_returns_400_when_data_retention_for_notif client, sample_service ): create_service_data_retention(service=sample_service) - data = {"notification_type": "sms", "days_of_retention": 3} + data = {"notification_type": NotificationType.SMS, "days_of_retention": 3} response = client.post( - "/service/{}/data-retention".format(str(uuid.uuid4())), + f"/service/{uuid.uuid4()!s}/data-retention", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -156,7 +158,7 @@ def test_modify_service_data_retention(client, sample_service): data_retention = create_service_data_retention(service=sample_service) data = {"days_of_retention": 3} response = client.post( - "/service/{}/data-retention/{}".format(sample_service.id, data_retention.id), + f"/service/{sample_service.id}/data-retention/{data_retention.id}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -172,7 +174,7 @@ def test_modify_service_data_retention_returns_400_when_data_retention_does_not_ ): data = {"days_of_retention": 3} response = client.post( - "/service/{}/data-retention/{}".format(sample_service.id, uuid.uuid4()), + f"/service/{sample_service.id}/data-retention/{uuid.uuid4()}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -186,7 +188,7 @@ def test_modify_service_data_retention_returns_400_when_data_retention_does_not_ def test_modify_service_data_retention_returns_400_when_data_is_invalid(client): data = {"bad_key": 3} response = client.post( - "/service/{}/data-retention/{}".format(uuid.uuid4(), uuid.uuid4()), + f"/service/{uuid.uuid4()}/data-retention/{uuid.uuid4()}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), diff --git a/tests/app/service/test_statistics.py b/tests/app/service/test_statistics.py index 8d7566a1f..0ccad3501 100644 --- a/tests/app/service/test_statistics.py +++ b/tests/app/service/test_statistics.py @@ -5,6 +5,7 @@ from unittest.mock import Mock import pytest from freezegun import freeze_time +from app.enums import KeyType, NotificationStatus, NotificationType from app.service.statistics import ( add_monthly_notification_status_stats, create_empty_monthly_notification_status_stats_dict, @@ -26,39 +27,51 @@ NewStatsRow = collections.namedtuple( { "empty": ([], [0, 0, 0], [0, 0, 0]), "always_increment_requested": ( - [StatsRow("email", "delivered", 1), StatsRow("email", "failed", 1)], + [ + StatsRow(NotificationType.EMAIL, NotificationStatus.DELIVERED, 1), + StatsRow(NotificationType.EMAIL, NotificationStatus.FAILED, 1), + ], [2, 1, 1], [0, 0, 0], ), "dont_mix_template_types": ( [ - StatsRow("email", "delivered", 1), - StatsRow("sms", "delivered", 1), + StatsRow(NotificationType.EMAIL, NotificationStatus.DELIVERED, 1), + StatsRow(NotificationType.SMS, NotificationStatus.DELIVERED, 1), ], [1, 1, 0], [1, 1, 0], ), "convert_fail_statuses_to_failed": ( [ - StatsRow("email", "failed", 1), - StatsRow("email", "technical-failure", 1), - StatsRow("email", "temporary-failure", 1), - StatsRow("email", "permanent-failure", 1), + StatsRow(NotificationType.EMAIL, NotificationStatus.FAILED, 1), + StatsRow( + NotificationType.EMAIL, NotificationStatus.TECHNICAL_FAILURE, 1 + ), + StatsRow( + NotificationType.EMAIL, NotificationStatus.TEMPORARY_FAILURE, 1 + ), + StatsRow( + NotificationType.EMAIL, NotificationStatus.PERMANENT_FAILURE, 1 + ), ], [4, 0, 4], [0, 0, 0], ), "convert_sent_to_delivered": ( [ - StatsRow("sms", "sending", 1), - StatsRow("sms", "delivered", 1), - StatsRow("sms", "sent", 1), + StatsRow(NotificationType.SMS, NotificationStatus.SENDING, 1), + StatsRow(NotificationType.SMS, NotificationStatus.DELIVERED, 1), + StatsRow(NotificationType.SMS, NotificationStatus.SENT, 1), ], [0, 0, 0], [3, 2, 0], ), "handles_none_rows": ( - [StatsRow("sms", "sending", 1), StatsRow(None, None, None)], + [ + StatsRow(NotificationType.SMS, NotificationStatus.SENDING, 1), + StatsRow(None, None, None), + ], [0, 0, 0], [1, 0, 0], ), @@ -67,44 +80,66 @@ NewStatsRow = collections.namedtuple( def test_format_statistics(stats, email_counts, sms_counts): ret = format_statistics(stats) - assert ret["email"] == { + assert ret[NotificationType.EMAIL] == { status: count - for status, count in zip(["requested", "delivered", "failed"], email_counts) + for status, count in zip( + [ + NotificationStatus.REQUESTED, + NotificationStatus.DELIVERED, + NotificationStatus.FAILED, + ], + email_counts, + ) } - assert ret["sms"] == { + assert ret[NotificationType.SMS] == { status: count - for status, count in zip(["requested", "delivered", "failed"], sms_counts) + for status, count in zip( + [ + NotificationStatus.REQUESTED, + NotificationStatus.DELIVERED, + NotificationStatus.FAILED, + ], + sms_counts, + ) } def test_create_zeroed_stats_dicts(): assert create_zeroed_stats_dicts() == { - "sms": {"requested": 0, "delivered": 0, "failed": 0}, - "email": {"requested": 0, "delivered": 0, "failed": 0}, + NotificationType.SMS: { + NotificationStatus.REQUESTED: 0, + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + }, + NotificationType.EMAIL: { + NotificationStatus.REQUESTED: 0, + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + }, } def test_create_stats_dict(): assert create_stats_dict() == { - "sms": { + NotificationType.SMS: { "total": 0, "test-key": 0, "failures": { - "technical-failure": 0, - "permanent-failure": 0, - "temporary-failure": 0, - "virus-scan-failed": 0, + NotificationStatus.TECHNICAL_FAILURE: 0, + NotificationStatus.PERMANENT_FAILURE: 0, + NotificationStatus.TEMPORARY_FAILURE: 0, + NotificationStatus.VIRUS_SCAN_FAILED: 0, }, }, - "email": { + NotificationType.EMAIL: { "total": 0, "test-key": 0, "failures": { - "technical-failure": 0, - "permanent-failure": 0, - "temporary-failure": 0, - "virus-scan-failed": 0, + NotificationStatus.TECHNICAL_FAILURE: 0, + NotificationStatus.PERMANENT_FAILURE: 0, + NotificationStatus.TEMPORARY_FAILURE: 0, + NotificationStatus.VIRUS_SCAN_FAILED: 0, }, }, } @@ -112,38 +147,89 @@ def test_create_stats_dict(): def test_format_admin_stats_only_includes_test_key_notifications_in_test_key_section(): rows = [ - NewStatsRow("email", "technical-failure", "test", 3), - NewStatsRow("sms", "permanent-failure", "test", 4), + NewStatsRow( + NotificationType.EMAIL, + NotificationStatus.TECHNICAL_FAILURE, + KeyType.TEST, + 3, + ), + NewStatsRow( + NotificationType.SMS, NotificationType.PERMANENT_FAILURE, KeyType.TEST, 4 + ), ] stats_dict = format_admin_stats(rows) - assert stats_dict["email"]["total"] == 0 - assert stats_dict["email"]["failures"]["technical-failure"] == 0 - assert stats_dict["email"]["test-key"] == 3 + assert stats_dict[NotificationType.EMAIL]["total"] == 0 + assert ( + stats_dict[NotificationType.EMAIL]["failures"][ + NotificationStatus.TECHNICAL_FAILURE + ] + == 0 + ) + assert stats_dict[NotificationType.EMAIL]["test-key"] == 3 - assert stats_dict["sms"]["total"] == 0 - assert stats_dict["sms"]["failures"]["permanent-failure"] == 0 - assert stats_dict["sms"]["test-key"] == 4 + assert stats_dict[NotificationType.SMS]["total"] == 0 + assert ( + stats_dict[NotificationType.SMS]["failures"][ + NotificationStatus.PERMANENT_FAILURE + ] + == 0 + ) + assert stats_dict[NotificationType.SMS]["test-key"] == 4 def test_format_admin_stats_counts_non_test_key_notifications_correctly(): rows = [ - NewStatsRow("email", "technical-failure", "normal", 1), - NewStatsRow("email", "created", "team", 3), - NewStatsRow("sms", "temporary-failure", "normal", 6), - NewStatsRow("sms", "sent", "normal", 2), + NewStatsRow( + NotificationType.EMAIL, + NotificationStatus.TECHNICAL_FAILURE, + KeyType.NORMAL, + 1, + ), + NewStatsRow( + NotificationType.EMAIL, + NotificationStatus.CREATED, + KeyType.TEAM, + 3, + ), + NewStatsRow( + NotificationType.SMS, + NotificationStatus.TEMPORARY_FAILURE, + KeyType.NORMAL, + 6, + ), + NewStatsRow( + NotificationType.SMS, + NotificationStatus.SENT, + KeyType.NORMAL, + 2, + ), ] stats_dict = format_admin_stats(rows) - assert stats_dict["email"]["total"] == 4 - assert stats_dict["email"]["failures"]["technical-failure"] == 1 + assert stats_dict[NotificationType.EMAIL]["total"] == 4 + assert ( + stats_dict[NotificationType.EMAIL]["failures"][ + NotificationStatus.TECHNICAL_FAILURE + ] + == 1 + ) - assert stats_dict["sms"]["total"] == 8 - assert stats_dict["sms"]["failures"]["permanent-failure"] == 0 + assert stats_dict[NotificationType.SMS]["total"] == 8 + assert ( + stats_dict[NotificationType.SMS]["failures"][ + NotificationStatus.PERMANENT_FAILURE + ] + == 0 + ) def _stats(requested, delivered, failed): - return {"requested": requested, "delivered": delivered, "failed": failed} + return { + NotificationStatus.REQUESTED: requested, + NotificationStatus.DELIVERED: delivered, + NotificationStatus.FAILED: failed, + } @pytest.mark.parametrize( @@ -174,7 +260,7 @@ def test_create_empty_monthly_notification_status_stats_dict(year, expected_year output = create_empty_monthly_notification_status_stats_dict(year) assert sorted(output.keys()) == expected_years for v in output.values(): - assert v == {"sms": {}, "email": {}} + assert v == {NotificationType.SMS: {}, NotificationType.EMAIL: {}} @freeze_time("2018-06-01 04:59:59") @@ -182,26 +268,26 @@ def test_add_monthly_notification_status_stats(): row_data = [ { "month": datetime(2018, 4, 1), - "notification_type": "sms", - "notification_status": "sending", + "notification_type": NotificationType.SMS, + "notification_status": NotificationStatus.SENDING, "count": 1, }, { "month": datetime(2018, 4, 1), - "notification_type": "sms", - "notification_status": "delivered", + "notification_type": NotificationType.SMS, + "notification_status": NotificationStatus.DELIVERED, "count": 2, }, { "month": datetime(2018, 4, 1), - "notification_type": "email", - "notification_status": "sending", + "notification_type": NotificationType.EMAIL, + "notification_status": NotificationStatus.SENDING, "count": 4, }, { "month": datetime(2018, 5, 1), - "notification_type": "sms", - "notification_status": "sending", + "notification_type": NotificationType.SMS, + "notification_status": NotificationStatus.SENDING, "count": 8, }, ] @@ -214,18 +300,27 @@ def test_add_monthly_notification_status_stats(): data = create_empty_monthly_notification_status_stats_dict(2018) # this data won't be affected - data["2018-05"]["email"]["sending"] = 32 + data["2018-05"][NotificationType.EMAIL][NotificationStatus.SENDING] = 32 # this data will get combined with the 8 from row_data - data["2018-05"]["sms"]["sending"] = 16 + data["2018-05"][NotificationType.SMS][NotificationStatus.SENDING] = 16 add_monthly_notification_status_stats(data, rows) # first 3 months are empty assert data == { - "2018-01": {"sms": {}, "email": {}}, - "2018-02": {"sms": {}, "email": {}}, - "2018-03": {"sms": {}, "email": {}}, - "2018-04": {"sms": {"sending": 1, "delivered": 2}, "email": {"sending": 4}}, - "2018-05": {"sms": {"sending": 24}, "email": {"sending": 32}}, - "2018-06": {"sms": {}, "email": {}}, + "2018-01": {NotificationType.SMS: {}, NotificationType.EMAIL: {}}, + "2018-02": {NotificationType.SMS: {}, NotificationType.EMAIL: {}}, + "2018-03": {NotificationType.SMS: {}, NotificationType.EMAIL: {}}, + "2018-04": { + NotificationType.SMS: { + NotificationStatus.SENDING: 1, + NotificationStatus.DELIVERED: 2, + }, + NotificationType.EMAIL: {NotificationStatus.SENDING: 4}, + }, + "2018-05": { + NotificationType.SMS: {NotificationStatus.SENDING: 24}, + NotificationType.EMAIL: {NotificationStatus.SENDING: 32}, + }, + "2018-06": {NotificationType.SMS: {}, NotificationType.EMAIL: {}}, } diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index 5e88702a6..3a43aa3b6 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -4,7 +4,7 @@ from datetime import date, datetime import pytest from freezegun import freeze_time -from app.enums import KeyType, NotificationType, TemplateType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType from tests.app.db import ( create_ft_notification_status, create_notification, @@ -112,7 +112,7 @@ def test_get_service_notification_statistics( date(2000, 1, 1), NotificationType.SMS, sample_service, count=1 ) with freeze_time("2000-01-02T12:00:00"): - create_notification(sample_template, status="created") + create_notification(sample_template, status=NotificationStatus.CREATED) resp = admin_request.get( "service.get_service_notification_statistics", service_id=sample_template.service_id, @@ -120,10 +120,10 @@ def test_get_service_notification_statistics( ) assert set(resp["data"].keys()) == { - NotificationType.SMS.value, - NotificationType.EMAIL.value, + NotificationType.SMS, + NotificationType.EMAIL, } - assert resp["data"][NotificationType.SMS.value] == stats + assert resp["data"][NotificationType.SMS] == stats def test_get_service_notification_statistics_with_unknown_service(admin_request): @@ -132,8 +132,8 @@ def test_get_service_notification_statistics_with_unknown_service(admin_request) ) assert resp["data"] == { - NotificationType.SMS.value: {"requested": 0, "delivered": 0, "failed": 0}, - NotificationType.EMAIL.value: {"requested": 0, "delivered": 0, "failed": 0}, + NotificationType.SMS: {"requested": 0, "delivered": 0, "failed": 0}, + NotificationType.EMAIL: {"requested": 0, "delivered": 0, "failed": 0}, } @@ -238,25 +238,33 @@ def test_get_monthly_notification_stats_combines_todays_data_and_historic_stats( admin_request, sample_template ): create_ft_notification_status( - datetime(2016, 5, 1, 12), template=sample_template, count=1 + datetime(2016, 5, 1, 12), + template=sample_template, + count=1, ) create_ft_notification_status( datetime(2016, 6, 1, 12), template=sample_template, - notification_status="created", + notification_status=NotificationStatus.CREATED, count=2, ) # noqa create_notification( - sample_template, created_at=datetime(2016, 6, 5, 12), status="created" + sample_template, + created_at=datetime(2016, 6, 5, 12), + status=NotificationStatus.CREATED, ) create_notification( - sample_template, created_at=datetime(2016, 6, 5, 12), status="delivered" + sample_template, + created_at=datetime(2016, 6, 5, 12), + status=NotificationStatus.DELIVERED, ) # this doesn't get returned in the stats because it is old - it should be in ft_notification_status by now create_notification( - sample_template, created_at=datetime(2016, 6, 4, 12), status="sending" + sample_template, + created_at=datetime(2016, 6, 4, 12), + status=NotificationStatus.SENDING, ) response = admin_request.get( From f0e737472a63686455c2a4d1ee58bc44c44837f4 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 20 Feb 2024 14:40:40 -0500 Subject: [PATCH 195/259] Done some stuff. Signed-off-by: Cliff Hill --- .../test_send_notification.py | 69 ++++++++++------- .../test_send_one_off_notification.py | 8 +- tests/app/template/test_rest.py | 74 ++++++++----------- tests/app/template_statistics/test_rest.py | 7 +- 4 files changed, 87 insertions(+), 71 deletions(-) diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index 74db646a0..e940dfe54 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -99,7 +99,7 @@ def test_send_notification_invalid_template_id( with notify_api.test_request_context(): with notify_api.test_client() as client: mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type) + f"app.celery.provider_tasks.deliver_{template_type}.apply_async" ) data = {"to": to, "template": fake_uuid} @@ -108,7 +108,7 @@ def test_send_notification_invalid_template_id( ) response = client.post( - path="/notifications/{}".format(template_type), + path=f"/notifications/{template_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -653,7 +653,7 @@ def test_should_send_sms_to_anyone_with_test_key( data=json.dumps(data), headers=[ ("Content-Type", "application/json"), - ("Authorization", "Bearer {}".format(auth_header)), + ("Authorization", f"Bearer {auth_header}"), ], ) app.celery.provider_tasks.deliver_sms.apply_async.assert_called_once_with( @@ -691,7 +691,7 @@ def test_should_send_email_to_anyone_with_test_key( data=json.dumps(data), headers=[ ("Content-Type", "application/json"), - ("Authorization", "Bearer {}".format(auth_header)), + ("Authorization", f"Bearer {auth_header}"), ], ) @@ -756,7 +756,7 @@ def test_should_persist_notification( queue_name, ): mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type) + f"app.celery.provider_tasks.deliver_{template_type}.apply_async" ) mocker.patch( "app.notifications.process_notifications.uuid.uuid4", return_value=fake_uuid @@ -843,11 +843,11 @@ def test_should_delete_notification_and_return_error_if_redis_fails( with pytest.raises(expected_exception=Exception) as e: client.post( - path="/notifications/{}".format(template_type), + path=f"/notifications/{template_type}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), - ("Authorization", "Bearer {}".format(auth_header)), + ("Authorization", f"Bearer {auth_header}"), ], ) assert str(e.value) == "failed to talk to redis" @@ -926,7 +926,7 @@ def test_should_not_send_notification_to_non_guest_list_recipient_in_trial_mode( service.message_limit = 2 apply_async = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) + f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" ) template = create_template(service, template_type=notification_type) assert sample_service_guest_list.service_id == service.id @@ -946,7 +946,7 @@ def test_should_not_send_notification_to_non_guest_list_recipient_in_trial_mode( data=json.dumps(data), headers=[ ("Content-Type", "application/json"), - ("Authorization", "Bearer {}".format(auth_header)), + ("Authorization", f"Bearer {auth_header}"), ], ) @@ -989,7 +989,7 @@ def test_should_send_notification_to_guest_list_recipient( sample_service.restricted = service_restricted apply_async = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) + f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" ) template = create_template(sample_service, template_type=notification_type) if notification_type == NotificationType.SMS: @@ -1016,7 +1016,7 @@ def test_should_send_notification_to_guest_list_recipient( data=json.dumps(data), headers=[ ("Content-Type", "application/json"), - ("Authorization", "Bearer {}".format(auth_header)), + ("Authorization", f"Bearer {auth_header}"), ], ) @@ -1119,7 +1119,14 @@ def test_create_template_raises_invalid_request_when_content_too_large( @pytest.mark.parametrize( - "notification_type, send_to", [("sms", "2028675309"), ("email", "sample@email.com")] + "notification_type,send_to", + [ + (NotificationType.SMS, "2028675309"), + ( + NotificationType.EMAIL, + "sample@email.com", + ), + ], ) def test_send_notification_uses_priority_queue_when_template_is_marked_as_priority( client, @@ -1132,7 +1139,7 @@ def test_send_notification_uses_priority_queue_when_template_is_marked_as_priori sample_service, template_type=notification_type, process_type="priority" ) mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) + f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" ) data = {"to": send_to, "template": str(sample.id)} @@ -1140,7 +1147,7 @@ def test_send_notification_uses_priority_queue_when_template_is_marked_as_priori auth_header = create_service_authorization_header(service_id=sample.service_id) response = client.post( - path="/notifications/{}".format(notification_type), + path=f"/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1153,7 +1160,14 @@ def test_send_notification_uses_priority_queue_when_template_is_marked_as_priori @pytest.mark.parametrize( - "notification_type, send_to", [("sms", "2028675309"), ("email", "sample@email.com")] + "notification_type, send_to", + [ + (NotificationType.SMS, "2028675309"), + ( + NotificationType.EMAIL, + "sample@email.com", + ), + ], ) def test_returns_a_429_limit_exceeded_if_rate_limit_exceeded( client, sample_service, mocker, notification_type, send_to @@ -1172,7 +1186,7 @@ def test_returns_a_429_limit_exceeded_if_rate_limit_exceeded( auth_header = create_service_authorization_header(service_id=sample.service_id) response = client.post( - path="/notifications/{}".format(notification_type), + path=f"/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1181,9 +1195,9 @@ def test_returns_a_429_limit_exceeded_if_rate_limit_exceeded( result = json.loads(response.data)["result"] assert response.status_code == 429 assert result == "error" - assert ( - message - == "Exceeded rate limit for key type TYPE of LIMIT requests per INTERVAL seconds" + assert message == ( + "Exceeded rate limit for key type TYPE of LIMIT " + "requests per INTERVAL seconds" ) assert not persist_mock.called @@ -1332,7 +1346,7 @@ def test_should_throw_exception_if_notification_type_is_invalid( ): auth_header = create_service_authorization_header(service_id=sample_service.id) response = client.post( - path="/notifications/{}".format(notification_type), + path=f"/notifications/{notification_type}", data={}, headers=[("Content-Type", "application/json"), auth_header], ) @@ -1341,14 +1355,19 @@ def test_should_throw_exception_if_notification_type_is_invalid( @pytest.mark.parametrize( - "notification_type, recipient", [("sms", "2028675309"), ("email", "test@gov.uk")] + "notification_type, recipient", + [ + (NotificationType.SMS, "2028675309"), + ( + NotificationType.EMAIL, + "test@gov.uk", + ), + ], ) def test_post_notification_should_set_reply_to_text( client, sample_service, mocker, notification_type, recipient ): - mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) - ) + mocker.patch(f"app.celery.provider_tasks.deliver_{notification_type}.apply_async") template = create_template(sample_service, template_type=notification_type) expected_reply_to = current_app.config["FROM_NUMBER"] if notification_type == NotificationType.EMAIL: @@ -1359,7 +1378,7 @@ def test_post_notification_should_set_reply_to_text( data = {"to": recipient, "template": str(template.id)} response = client.post( - "/notifications/{}".format(notification_type), + f"/notifications/{notification_type}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), diff --git a/tests/app/service/send_notification/test_send_one_off_notification.py b/tests/app/service/send_notification/test_send_one_off_notification.py index 9e342369d..231b42be0 100644 --- a/tests/app/service/send_notification/test_send_one_off_notification.py +++ b/tests/app/service/send_notification/test_send_one_off_notification.py @@ -11,6 +11,7 @@ from app.enums import ( KeyType, NotificationType, RecipientType, + ServicePermissionType, TemplateProcessType, TemplateType, ) @@ -100,7 +101,12 @@ def test_send_one_off_notification_calls_persist_correctly_for_sms( def test_send_one_off_notification_calls_persist_correctly_for_international_sms( persist_mock, celery_mock, notify_db_session ): - service = create_service(service_permissions=["sms", "international_sms"]) + service = create_service( + service_permissions=[ + ServicePermissionType.SMS, + ServicePermissionType.INTERNATIONAL_SMS, + ], + ) template = create_template( service=service, template_type=TemplateType.SMS, diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index 311d0e4d7..7691d8966 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -9,7 +9,7 @@ from freezegun import freeze_time from notifications_utils import SMS_CHAR_COUNT_LIMIT from app.dao.templates_dao import dao_get_template_by_id, dao_redact_template -from app.enums import ServicePermissionType, TemplateType +from app.enums import ServicePermissionType, TemplateProcessType, TemplateType from app.models import Template, TemplateHistory from tests import create_admin_authorization_header from tests.app.db import create_service, create_template, create_template_folder @@ -39,7 +39,7 @@ def test_should_create_a_new_template_for_a_service( auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(service.id), + f"/service/{service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -51,7 +51,7 @@ def test_should_create_a_new_template_for_a_service( assert json_resp["data"]["service"] == str(service.id) assert json_resp["data"]["id"] assert json_resp["data"]["version"] == 1 - assert json_resp["data"]["process_type"] == "normal" + assert json_resp["data"]["process_type"] == TemplateProcessType.NORMAL assert json_resp["data"]["created_by"] == str(sample_user.id) if subject: assert json_resp["data"]["subject"] == "subject" @@ -71,7 +71,7 @@ def test_create_a_new_template_for_a_service_adds_folder_relationship( data = { "name": "my template", - "template_type": "sms", + "template_type": TemplateType.SMS, "content": "template content", "service": str(sample_service.id), "created_by": str(sample_service.users[0].id), @@ -81,7 +81,7 @@ def test_create_a_new_template_for_a_service_adds_folder_relationship( auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(sample_service.id), + f"/service/{sample_service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -98,7 +98,7 @@ def test_create_template_should_return_400_if_folder_is_for_a_different_service( data = { "name": "my template", - "template_type": "sms", + "template_type": TemplateType.SMS, "content": "template content", "service": str(sample_service.id), "created_by": str(sample_service.users[0].id), @@ -108,7 +108,7 @@ def test_create_template_should_return_400_if_folder_is_for_a_different_service( auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(sample_service.id), + f"/service/{sample_service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -124,7 +124,7 @@ def test_create_template_should_return_400_if_folder_does_not_exist( ): data = { "name": "my template", - "template_type": "sms", + "template_type": TemplateType.SMS, "content": "template content", "service": str(sample_service.id), "created_by": str(sample_service.users[0].id), @@ -134,7 +134,7 @@ def test_create_template_should_return_400_if_folder_does_not_exist( auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(sample_service.id), + f"/service/{sample_service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -159,7 +159,7 @@ def test_should_raise_error_if_service_does_not_exist_on_create( auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(fake_uuid), + f"/service/{fake_uuid}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -204,7 +204,7 @@ def test_should_raise_error_on_create_if_no_permission( auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(service.id), + f"/service/{service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -245,9 +245,7 @@ def test_should_be_error_on_update_if_no_permission( auth_header = create_admin_authorization_header() update_response = client.post( - "/service/{}/template/{}".format( - template_without_permission.service_id, template_without_permission.id - ), + f"/service/{template_without_permission.service_id}/template/{template_without_permission.id}", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -270,7 +268,7 @@ def test_should_error_if_created_by_missing(client, sample_user, sample_service) auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(service_id), + f"/service/{service_id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -286,7 +284,7 @@ def test_should_be_error_if_service_does_not_exist_on_update(client, fake_uuid): auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template/{}".format(fake_uuid, fake_uuid), + f"/service/{fake_uuid}/template/{fake_uuid}", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -322,7 +320,7 @@ def test_must_have_a_subject_on_an_email_template( def test_update_should_update_a_template(client, sample_user): service = create_service() - template = create_template(service, template_type="sms") + template = create_template(service, template_type=TemplateType.SMS) assert template.created_by == service.created_by assert template.created_by != sample_user @@ -335,7 +333,7 @@ def test_update_should_update_a_template(client, sample_user): auth_header = create_admin_authorization_header() update_response = client.post( - "/service/{}/template/{}".format(service.id, template.id), + f"/service/{service.id}/template/{template.id}", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -373,9 +371,7 @@ def test_should_be_able_to_archive_template(client, sample_template): auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/template/{}".format( - sample_template.service.id, sample_template.id - ), + f"/service/{sample_template.service.id}/template/{sample_template.id}", headers=[("Content-Type", "application/json"), auth_header], data=json_data, ) @@ -431,14 +427,14 @@ def test_should_be_able_to_get_all_templates_for_a_service( data_2 = json.dumps(data) auth_header = create_admin_authorization_header() client.post( - "/service/{}/template".format(sample_service.id), + f"/service/{sample_service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data_1, ) auth_header = create_admin_authorization_header() client.post( - "/service/{}/template".format(sample_service.id), + f"/service/{sample_service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data_2, ) @@ -446,7 +442,7 @@ def test_should_be_able_to_get_all_templates_for_a_service( auth_header = create_admin_authorization_header() response = client.get( - "/service/{}/template".format(sample_service.id), headers=[auth_header] + f"/service/{sample_service.id}/template", headers=[auth_header] ) assert response.status_code == 200 @@ -467,10 +463,12 @@ def test_should_get_only_templates_for_that_service(admin_request, notify_db_ses id_3 = create_template(service_2).id json_resp_1 = admin_request.get( - "template.get_all_templates_for_service", service_id=service_1.id + "template.get_all_templates_for_service", + service_id=service_1.id, ) json_resp_2 = admin_request.get( - "template.get_all_templates_for_service", service_id=service_2.id + "template.get_all_templates_for_service", + service_id=service_2.id, ) assert {template["id"] for template in json_resp_1["data"]} == { @@ -580,7 +578,7 @@ def test_should_get_a_single_template( ) response = client.get( - "/service/{}/template/{}".format(sample_service.id, template.id), + f"/service/{sample_service.id}/template/{template.id}", headers=[create_admin_authorization_header()], ) @@ -667,7 +665,7 @@ def test_should_return_empty_array_if_no_templates_for_service(client, sample_se auth_header = create_admin_authorization_header() response = client.get( - "/service/{}/template".format(sample_service.id), headers=[auth_header] + f"/service/{sample_service.id}/template", headers=[auth_header] ) assert response.status_code == 200 @@ -681,7 +679,7 @@ def test_should_return_404_if_no_templates_for_service_with_id( auth_header = create_admin_authorization_header() response = client.get( - "/service/{}/template/{}".format(sample_service.id, fake_uuid), + f"/service/{sample_service.id}/template/{fake_uuid}", headers=[auth_header], ) @@ -715,7 +713,7 @@ def test_create_400_for_over_limit_content( auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/template".format(sample_service.id), + f"/service/{sample_service.id}/template", headers=[("Content-Type", "application/json"), auth_header], data=data, ) @@ -740,9 +738,7 @@ def test_update_400_for_over_limit_content( ) auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/template/{}".format( - sample_template.service.id, sample_template.id - ), + f"/service/{sample_template.service.id}/template/{sample_template.id}", headers=[("Content-Type", "application/json"), auth_header], data=json_data, ) @@ -766,9 +762,7 @@ def test_should_return_all_template_versions_for_service_and_template_id( auth_header = create_admin_authorization_header() resp = client.get( - "/service/{}/template/{}/versions".format( - sample_template.service_id, sample_template.id - ), + f"/service/{sample_template.service_id}/template/{sample_template.id}/versions", headers=[("Content-Type", "application/json"), auth_header], ) assert resp.status_code == 200 @@ -792,9 +786,7 @@ def test_update_does_not_create_new_version_when_there_is_no_change( "content": sample_template.content, } resp = client.post( - "/service/{}/template/{}".format( - sample_template.service_id, sample_template.id - ), + f"/service/{sample_template.service_id}/template/{sample_template.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -808,9 +800,7 @@ def test_update_set_process_type_on_template(client, sample_template): auth_header = create_admin_authorization_header() data = {"process_type": "priority"} resp = client.post( - "/service/{}/template/{}".format( - sample_template.service_id, sample_template.id - ), + f"/service/{sample_template.service_id}/template/{sample_template.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) diff --git a/tests/app/template_statistics/test_rest.py b/tests/app/template_statistics/test_rest.py index 9148839be..be6b368ab 100644 --- a/tests/app/template_statistics/test_rest.py +++ b/tests/app/template_statistics/test_rest.py @@ -5,6 +5,7 @@ from unittest.mock import Mock import pytest from freezegun import freeze_time +from app.enums import NotificationStatus, TemplateType from app.utils import DATETIME_FORMAT from tests.app.db import create_ft_notification_status, create_notification @@ -48,7 +49,7 @@ def test_get_template_statistics_for_service_by_day_returns_template_info( assert json_resp["data"][0]["count"] == 1 assert json_resp["data"][0]["template_id"] == str(sample_notification.template_id) assert json_resp["data"][0]["template_name"] == "sms Template Name" - assert json_resp["data"][0]["template_type"] == "sms" + assert json_resp["data"][0]["template_type"] == TemplateType.SMS @pytest.mark.parametrize("var_name", ["limit_days", "whole_days"]) @@ -77,7 +78,7 @@ def test_get_template_statistics_for_service_by_day_goes_to_db( count=3, template_name=sample_template.name, notification_type=sample_template.template_type, - status="created", + status=NotificationStatus.CREATED, ) ], ) @@ -93,7 +94,7 @@ def test_get_template_statistics_for_service_by_day_goes_to_db( "count": 3, "template_name": sample_template.name, "template_type": sample_template.template_type, - "status": "created", + "status": NotificationStatus.CREATED, } ] # dao only called for 2nd, since redis returned values for first call From a26cc54ed9041b8951d41c082a88f7272ecfa473 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 20 Feb 2024 14:41:21 -0500 Subject: [PATCH 196/259] Another changed string format. Signed-off-by: Cliff Hill --- tests/app/service/send_notification/test_send_notification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index e940dfe54..2e173eba2 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -942,7 +942,7 @@ def test_should_not_send_notification_to_non_guest_list_recipient_in_trial_mode( ) response = client.post( - path="/notifications/{}".format(notification_type), + path=f"/notifications/{notification_type}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), From 12658fe66743f47db978d15441838d1e2b92e10c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 20 Feb 2024 14:45:53 -0500 Subject: [PATCH 197/259] Even more format strings changed. Signed-off-by: Cliff Hill --- .../test_send_notification.py | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index 2e173eba2..6b5441efb 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -35,7 +35,7 @@ def test_create_notification_should_reject_if_missing_required_fields( with notify_api.test_request_context(): with notify_api.test_client() as client: mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type) + f"app.celery.provider_tasks.deliver_{template_type}.apply_async" ) data = {} auth_header = create_service_authorization_header( @@ -43,7 +43,7 @@ def test_create_notification_should_reject_if_missing_required_fields( ) response = client.post( - path="/notifications/{}".format(template_type), + path=f"/notifications/{template_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -286,7 +286,7 @@ def test_should_not_send_notification_if_restricted_and_not_a_service_user( with notify_api.test_request_context(): with notify_api.test_client() as client: mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type) + f"app.celery.provider_tasks.deliver_{template_type}.apply_async" ) template = ( sample_template @@ -302,7 +302,7 @@ def test_should_not_send_notification_if_restricted_and_not_a_service_user( ) response = client.post( - path="/notifications/{}".format(template_type), + path=f"/notifications/{template_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -326,7 +326,7 @@ def test_should_send_notification_if_restricted_and_a_service_user( with notify_api.test_request_context(): with notify_api.test_client() as client: mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type) + f"app.celery.provider_tasks.deliver_{template_type}.apply_async" ) template = ( @@ -348,7 +348,7 @@ def test_should_send_notification_if_restricted_and_a_service_user( ) response = client.post( - path="/notifications/{}".format(template_type), + path=f"/notifications/{template_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -364,7 +364,7 @@ def test_should_not_allow_template_from_another_service( with notify_api.test_request_context(): with notify_api.test_client() as client: mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type) + f"app.celery.provider_tasks.deliver_{template_type}.apply_async" ) service_1 = service_factory.get( "service 1", user=sample_user, email_from="service.1" @@ -386,7 +386,7 @@ def test_should_not_allow_template_from_another_service( auth_header = create_service_authorization_header(service_id=service_1.id) response = client.post( - path="/notifications/{}".format(template_type), + path=f"/notifications/{template_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -729,7 +729,7 @@ def test_should_send_sms_if_team_api_key_and_a_service_user( data=json.dumps(data), headers=[ ("Content-Type", "application/json"), - ("Authorization", "Bearer {}".format(auth_header)), + ("Authorization", f"Bearer {auth_header}"), ], ) @@ -783,11 +783,11 @@ def test_should_persist_notification( ) response = client.post( - path="/notifications/{}".format(template_type), + path=f"/notifications/{template_type}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), - ("Authorization", "Bearer {}".format(auth_header)), + ("Authorization", f"Bearer {auth_header}"), ], ) @@ -814,7 +814,7 @@ def test_should_delete_notification_and_return_error_if_redis_fails( queue_name, ): mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(template_type), + f"app.celery.provider_tasks.deliver_{template_type}.apply_async", side_effect=Exception("failed to talk to redis"), ) mocker.patch( @@ -1012,7 +1012,7 @@ def test_should_send_notification_to_guest_list_recipient( ) response = client.post( - path="/notifications/{}".format(notification_type), + path=f"/notifications/{notification_type}", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), @@ -1042,7 +1042,7 @@ def test_should_error_if_notification_type_does_not_match_template_type( data = {"to": to, "template": template.id} auth_header = create_service_authorization_header(service_id=template.service_id) response = client.post( - "/notifications/{}".format(notification_type), + f"/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1051,9 +1051,7 @@ def test_should_error_if_notification_type_does_not_match_template_type( json_resp = json.loads(response.get_data(as_text=True)) assert json_resp["result"] == "error" assert ( - "{0} template is not suitable for {1} notification".format( - template_type, notification_type - ) + f"{template_type} template is not suitable for {notification_type} notification" in json_resp["message"] ) @@ -1111,9 +1109,7 @@ def test_create_template_raises_invalid_request_when_content_too_large( pytest.fail("do not expect an InvalidRequest") assert e.message == { "content": [ - "Content has a character count greater than the limit of {}".format( - SMS_CHAR_COUNT_LIMIT - ) + f"Content has a character count greater than the limit of {SMS_CHAR_COUNT_LIMIT}" ] } From 74ac09ae55a90220da999c7798718deadfb4c6ac Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 20 Feb 2024 17:00:54 -0500 Subject: [PATCH 198/259] Even more cleanup. Signed-off-by: Cliff Hill --- tests/app/user/test_rest.py | 6 +- tests/app/user/test_rest_verify.py | 36 ++- .../notifications/test_post_notifications.py | 221 +++++++++++------- 3 files changed, 162 insertions(+), 101 deletions(-) diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index c24b950d8..801a18f38 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -8,7 +8,7 @@ from flask import current_app from freezegun import freeze_time from app.dao.service_user_dao import dao_get_service_user, dao_update_service_user -from app.enums import AuthType, PermissionType +from app.enums import AuthType, NotificationType, PermissionType from app.models import Notification, Permission, User from tests.app.db import ( create_organization, @@ -262,7 +262,7 @@ def test_post_user_attribute(admin_request, sample_user, user_attribute, user_va dict( api_key_id=None, key_type="normal", - notification_type="email", + notification_type=NotificationType.EMAIL, personalisation={ "name": "Test User", "servicemanagername": "Service Manago", @@ -281,7 +281,7 @@ def test_post_user_attribute(admin_request, sample_user, user_attribute, user_va dict( api_key_id=None, key_type="normal", - notification_type="sms", + notification_type=NotificationType.SMS, personalisation={ "name": "Test User", "servicemanagername": "Service Manago", diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index 01c28bb25..8bff45391 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -209,7 +209,11 @@ def test_send_user_sms_code(client, sample_user, sms_code_template, mocker): mocker.patch("app.celery.provider_tasks.deliver_sms.apply_async") resp = client.post( - url_for("user.send_user_2fa_code", code_type="sms", user_id=sample_user.id), + url_for( + "user.send_user_2fa_code", + code_type=CodeType.SMS, + user_id=sample_user.id, + ), data=json.dumps({}), headers=[("Content-Type", "application/json"), auth_header], ) @@ -247,7 +251,11 @@ def test_send_user_code_for_sms_with_optional_to_field( auth_header = create_admin_authorization_header() resp = client.post( - url_for("user.send_user_2fa_code", code_type="sms", user_id=sample_user.id), + url_for( + "user.send_user_2fa_code", + code_type=CodeType.SMS, + user_id=sample_user.id, + ), data=json.dumps({"to": to_number}), headers=[("Content-Type", "application/json"), auth_header], ) @@ -265,7 +273,7 @@ def test_send_sms_code_returns_404_for_bad_input_data(client): uuid_ = uuid.uuid4() auth_header = create_admin_authorization_header() resp = client.post( - url_for("user.send_user_2fa_code", code_type="sms", user_id=uuid_), + url_for("user.send_user_2fa_code", code_type=CodeType.SMS, user_id=uuid_), data=json.dumps({}), headers=[("Content-Type", "application/json"), auth_header], ) @@ -278,7 +286,7 @@ def test_send_sms_code_returns_204_when_too_many_codes_already_created( ): for _ in range(5): verify_code = VerifyCode( - code_type="sms", + code_type=CodeType.SMS, _code=12345, created_at=datetime.utcnow() - timedelta(minutes=10), expiry_datetime=datetime.utcnow() + timedelta(minutes=40), @@ -289,7 +297,11 @@ def test_send_sms_code_returns_204_when_too_many_codes_already_created( assert VerifyCode.query.count() == 5 auth_header = create_admin_authorization_header() resp = client.post( - url_for("user.send_user_2fa_code", code_type="sms", user_id=sample_user.id), + url_for( + "user.send_user_2fa_code", + code_type=CodeType.SMS, + user_id=sample_user.id, + ), data=json.dumps({}), headers=[("Content-Type", "application/json"), auth_header], ) @@ -463,7 +475,7 @@ def test_send_user_email_code( admin_request.post( "user.send_user_2fa_code", - code_type="email", + code_type=CodeType.EMAIL, user_id=sample_user.id, _data=data, _expected_status=204, @@ -493,7 +505,7 @@ def test_send_user_email_code_with_urlencoded_next_param( data = {"to": None, "next": "/services"} admin_request.post( "user.send_user_2fa_code", - code_type="email", + code_type=CodeType.EMAIL, user_id=sample_user.id, _data=data, _expected_status=204, @@ -505,7 +517,7 @@ def test_send_user_email_code_with_urlencoded_next_param( def test_send_email_code_returns_404_for_bad_input_data(admin_request): resp = admin_request.post( "user.send_user_2fa_code", - code_type="email", + code_type=CodeType.EMAIL, user_id=uuid.uuid4(), _data={}, _expected_status=404, @@ -523,7 +535,7 @@ def test_user_verify_email_code(admin_request, sample_user, auth_type): magic_code = str(uuid.uuid4()) verify_code = create_user_code(sample_user, magic_code, CodeType.EMAIL) - data = {"code_type": "email", "code": magic_code} + data = {"code_type": CodeType.EMAIL, "code": magic_code} admin_request.post( "user.verify_user_code", @@ -575,7 +587,11 @@ def test_send_user_2fa_code_sends_from_number_for_international_numbers( mocker.patch("app.user.rest.send_notification_to_queue") resp = client.post( - url_for("user.send_user_2fa_code", code_type="sms", user_id=sample_user.id), + url_for( + "user.send_user_2fa_code", + code_type=CodeType.SMS, + user_id=sample_user.id, + ), data=json.dumps({}), headers=[("Content-Type", "application/json"), auth_header], ) diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index 98289dc62..4a90c5372 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -8,7 +8,13 @@ from flask import current_app, json from app.dao import templates_dao from app.dao.service_sms_sender_dao import dao_update_service_sms_sender -from app.enums import NotificationStatus, NotificationType, ServicePermissionType +from app.enums import ( + KeyType, + NotificationStatus, + NotificationType, + ServicePermissionType, + TemplateType, +) from app.models import Notification from app.schema_validation import validate from app.v2.errors import RateLimitError @@ -64,16 +70,13 @@ def test_post_sms_notification_returns_201( "body" ] == sample_template_with_placeholders.content.replace("(( Name))", "Jo") assert resp_json["content"]["from_number"] == current_app.config["FROM_NUMBER"] - assert "v2/notifications/{}".format(notification_id) in resp_json["uri"] + assert f"v2/notifications/{notification_id}" in resp_json["uri"] assert resp_json["template"]["id"] == str(sample_template_with_placeholders.id) assert resp_json["template"]["version"] == sample_template_with_placeholders.version assert ( - "services/{}/templates/{}".format( - sample_template_with_placeholders.service_id, - sample_template_with_placeholders.id, - ) - in resp_json["template"]["uri"] - ) + f"services/{sample_template_with_placeholders.service_id}/templates/" + f"{sample_template_with_placeholders.service_id}" + ) in resp_json["template"]["uri"] assert not resp_json["scheduled_for"] assert mocked.called @@ -369,8 +372,8 @@ def test_should_return_template_if_found_in_redis(mocker, client, sample_templat @pytest.mark.parametrize( "notification_type, key_send_to, send_to", [ - ("sms", "phone_number", "+447700900855"), - ("email", "email_address", "sample@email.com"), + (NotificationType.SMS, "phone_number", "+447700900855"), + (NotificationType.EMAIL, "email_address", "sample@email.com"), ], ) def test_post_notification_returns_400_and_missing_template( @@ -380,7 +383,7 @@ def test_post_notification_returns_400_and_missing_template( auth_header = create_service_authorization_header(service_id=sample_service.id) response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -398,8 +401,8 @@ def test_post_notification_returns_400_and_missing_template( @pytest.mark.parametrize( "notification_type, key_send_to, send_to", [ - ("sms", "phone_number", "+447700900855"), - ("email", "email_address", "sample@email.com"), + (NotificationType.SMS, "phone_number", "+447700900855"), + (NotificationType.EMAIL, "email_address", "sample@email.com"), ], ) def test_post_notification_returns_401_and_well_formed_auth_error( @@ -408,7 +411,7 @@ def test_post_notification_returns_401_and_well_formed_auth_error( data = {key_send_to: send_to, "template_id": str(sample_template.id)} response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json")], ) @@ -428,8 +431,8 @@ def test_post_notification_returns_401_and_well_formed_auth_error( @pytest.mark.parametrize( "notification_type, key_send_to, send_to", [ - ("sms", "phone_number", "+447700900855"), - ("email", "email_address", "sample@email.com"), + (NotificationType.SMS, "phone_number", "+447700900855"), + (NotificationType.EMAIL, "email_address", "sample@email.com"), ], ) def test_notification_returns_400_and_for_schema_problems( @@ -441,7 +444,7 @@ def test_notification_returns_400_and_for_schema_problems( ) response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -498,11 +501,11 @@ def test_post_email_notification_returns_201( assert resp_json["content"][ "subject" ] == sample_email_template_with_placeholders.subject.replace("((name))", "Bob") - assert resp_json["content"]["from_email"] == "{}@{}".format( - sample_email_template_with_placeholders.service.email_from, - current_app.config["NOTIFY_EMAIL_DOMAIN"], + assert resp_json["content"]["from_email"] == ( + f"{sample_email_template_with_placeholders.service.email_from}@" + f"{current_app.config['NOTIFY_EMAIL_DOMAIN']}" ) - assert "v2/notifications/{}".format(notification.id) in resp_json["uri"] + assert f"v2/notifications/{notification.id}" in resp_json["uri"] assert resp_json["template"]["id"] == str( sample_email_template_with_placeholders.id ) @@ -511,12 +514,9 @@ def test_post_email_notification_returns_201( == sample_email_template_with_placeholders.version ) assert ( - "services/{}/templates/{}".format( - str(sample_email_template_with_placeholders.service_id), - str(sample_email_template_with_placeholders.id), - ) - in resp_json["template"]["uri"] - ) + f"services/{sample_email_template_with_placeholders.service_id}/templates/" + f"{sample_email_template_with_placeholders.id}" + ) in resp_json["template"]["uri"] assert not resp_json["scheduled_for"] assert mocked.called @@ -527,8 +527,8 @@ def test_post_email_notification_returns_201( ("simulate-delivered@notifications.service.gov.uk", NotificationType.EMAIL), ("simulate-delivered-2@notifications.service.gov.uk", NotificationType.EMAIL), ("simulate-delivered-3@notifications.service.gov.uk", NotificationType.EMAIL), - ("+14254147167", "sms"), - ("+14254147755", "sms"), + ("+14254147167", NotificationType.SMS), + ("+14254147755", NotificationType.SMS), ], ) def test_should_not_persist_or_send_notification_if_simulated_recipient( @@ -538,7 +538,7 @@ def test_should_not_persist_or_send_notification_if_simulated_recipient( "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) ) - if notification_type == "sms": + if notification_type == NotificationType.SMS: data = {"phone_number": recipient, "template_id": str(sample_template.id)} else: data = { @@ -551,7 +551,7 @@ def test_should_not_persist_or_send_notification_if_simulated_recipient( ) response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -565,22 +565,25 @@ def test_should_not_persist_or_send_notification_if_simulated_recipient( @pytest.mark.parametrize( "notification_type, key_send_to, send_to", [ - ("sms", "phone_number", "2028675309"), - ("email", "email_address", "sample@email.com"), + (NotificationType.SMS, "phone_number", "2028675309"), + (NotificationType.EMAIL, "email_address", "sample@email.com"), ], ) def test_send_notification_uses_priority_queue_when_template_is_marked_as_priority( - client, sample_service, mocker, notification_type, key_send_to, send_to + client, + sample_service, + mocker, + notification_type, + key_send_to, + send_to, ): - mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) - ) + mocker.patch(f"app.celery.provider_tasks.deliver_{notification_type}.apply_async") sample = create_template( service=sample_service, template_type=notification_type, process_type="priority" ) mocked = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) + f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" ) data = {key_send_to: send_to, "template_id": str(sample.id)} @@ -588,7 +591,7 @@ def test_send_notification_uses_priority_queue_when_template_is_marked_as_priori auth_header = create_service_authorization_header(service_id=sample.service_id) response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -602,8 +605,8 @@ def test_send_notification_uses_priority_queue_when_template_is_marked_as_priori @pytest.mark.parametrize( "notification_type, key_send_to, send_to", [ - ("sms", "phone_number", "2028675309"), - ("email", "email_address", "sample@email.com"), + (NotificationType.SMS, "phone_number", "2028675309"), + (NotificationType.EMAIL, "email_address", "sample@email.com"), ], ) def test_returns_a_429_limit_exceeded_if_rate_limit_exceeded( @@ -626,7 +629,7 @@ def test_returns_a_429_limit_exceeded_if_rate_limit_exceeded( auth_header = create_service_authorization_header(service_id=sample.service_id) response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -698,19 +701,29 @@ def test_post_sms_notification_with_archived_reply_to_id_returns_400( assert response.status_code == 400 resp_json = json.loads(response.get_data(as_text=True)) assert ( - "sms_sender_id {} does not exist in database for service id {}".format( - archived_sender.id, sample_template.service_id - ) - in resp_json["errors"][0]["message"] - ) + f"sms_sender_id {archived_sender.id} does not exist in database for " + f"service id {sample_template.service_id}" + ) in resp_json["errors"][0]["message"] assert "BadRequestError" in resp_json["errors"][0]["error"] @pytest.mark.parametrize( "recipient,label,permission_type, notification_type,expected_error", [ - ("2028675309", "phone_number", "email", "sms", "text messages"), - ("someone@test.com", "email_address", "sms", "email", "emails"), + ( + "2028675309", + "phone_number", + ServicePermissionType.EMAIL, + NotificationType.SMS, + "text messages", + ), + ( + "someone@test.com", + "email_address", + ServicePermissionType.SMS, + NotificationType.EMAIL, + "emails", + ), ], ) def test_post_sms_notification_returns_400_if_not_allowed_to_send_notification( @@ -732,9 +745,7 @@ def test_post_sms_notification_returns_400_if_not_allowed_to_send_notification( ) response = client.post( - path="/v2/notifications/{}".format( - sample_template_without_permission.template_type - ), + path=f"/v2/notifications/{sample_template_without_permission.template_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -747,7 +758,7 @@ def test_post_sms_notification_returns_400_if_not_allowed_to_send_notification( assert error_json["errors"] == [ { "error": "BadRequestError", - "message": "Service is not allowed to send {}".format(expected_error), + "message": f"Service is not allowed to send {expected_error}", } ] @@ -764,14 +775,15 @@ def test_post_sms_notification_returns_400_if_number_not_in_guest_list( ], ) template = create_template(service=service) - create_api_key(service=service, key_type="team") + create_api_key(service=service, key_type=KeyType.TEAM) data = { "phone_number": "+327700900855", "template_id": template.id, } auth_header = create_service_authorization_header( - service_id=service.id, key_type="team" + service_id=service.id, + key_type=KeyType.TEAM, ) response = client.post( @@ -855,7 +867,9 @@ def test_post_notification_raises_bad_request_if_not_valid_notification_type( assert "The requested URL was not found on the server." in error_json["message"] -@pytest.mark.parametrize("notification_type", ["sms", "email"]) +@pytest.mark.parametrize( + "notification_type", [NotificationType.SMS, NotificationType.EMAIL] +) def test_post_notification_with_wrong_type_of_sender( client, sample_template, sample_email_template, notification_type, fake_uuid ): @@ -878,14 +892,14 @@ def test_post_notification_with_wrong_type_of_sender( auth_header = create_service_authorization_header(service_id=template.service_id) response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) assert response.status_code == 400 resp_json = json.loads(response.get_data(as_text=True)) assert ( - "Additional properties are not allowed ({} was unexpected)".format(form_label) + f"Additional properties are not allowed ({form_label} was unexpected)" in resp_json["errors"][0]["message"] ) assert "ValidationError" in resp_json["errors"][0]["error"] @@ -942,11 +956,9 @@ def test_post_email_notification_with_invalid_reply_to_id_returns_400( assert response.status_code == 400 resp_json = json.loads(response.get_data(as_text=True)) assert ( - "email_reply_to_id {} does not exist in database for service id {}".format( - fake_uuid, sample_email_template.service_id - ) - in resp_json["errors"][0]["message"] - ) + f"email_reply_to_id {fake_uuid} does not exist in database for service id " + f"{sample_email_template.service_id}" + ) in resp_json["errors"][0]["message"] assert "BadRequestError" in resp_json["errors"][0]["error"] @@ -976,11 +988,9 @@ def test_post_email_notification_with_archived_reply_to_id_returns_400( assert response.status_code == 400 resp_json = json.loads(response.get_data(as_text=True)) assert ( - "email_reply_to_id {} does not exist in database for service id {}".format( - archived_reply_to.id, sample_email_template.service_id - ) - in resp_json["errors"][0]["message"] - ) + f"email_reply_to_id {archived_reply_to.id} does not exist in database for " + f"service id {sample_email_template.service_id}" + ) in resp_json["errors"][0]["message"] assert "BadRequestError" in resp_json["errors"][0]["error"] @@ -1000,7 +1010,7 @@ def test_post_notification_with_document_upload( service.contact_link = "contact.me@gov.uk" template = create_template( service=service, - template_type="email", + template_type=TemplateType.EMAIL, content="Document 1: ((first_link)). Document 2: ((second_link))", ) @@ -1057,7 +1067,9 @@ def test_post_notification_with_document_upload_simulated( service = create_service(service_permissions=[ServicePermissionType.EMAIL]) service.contact_link = "contact.me@gov.uk" template = create_template( - service=service, template_type="email", content="Document: ((document))" + service=service, + template_type=TemplateType.EMAIL, + content="Document: ((document))", ) mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") @@ -1093,7 +1105,9 @@ def test_post_notification_without_document_upload_permission( ): service = create_service(service_permissions=[ServicePermissionType.EMAIL]) template = create_template( - service=service, template_type="email", content="Document: ((document))" + service=service, + template_type=TemplateType.EMAIL, + content="Document: ((document))", ) mocker.patch("app.celery.provider_tasks.deliver_email.apply_async") @@ -1135,21 +1149,24 @@ def test_post_notification_returns_400_when_get_json_throws_exception( @pytest.mark.parametrize( "notification_type, content_type", [ - ("email", "application/json"), - ("email", "application/text"), - ("sms", "application/json"), - ("sms", "application/text"), + (NotificationType.EMAIL, "application/json"), + (NotificationType.EMAIL, "application/text"), + (NotificationType.SMS, "application/json"), + (NotificationType.SMS, "application/text"), ], ) def test_post_notification_when_payload_is_invalid_json_returns_400( - client, sample_service, notification_type, content_type + client, + sample_service, + notification_type, + content_type, ): auth_header = create_service_authorization_header(service_id=sample_service.id) payload_not_json = { "template_id": "dont-convert-to-json", } response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=payload_not_json, headers=[("Content-Type", content_type), auth_header], ) @@ -1160,14 +1177,18 @@ def test_post_notification_when_payload_is_invalid_json_returns_400( assert error_msg == "Invalid JSON supplied in POST data" -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize( + "notification_type", + [ + NotificationType.EMAIL, + NotificationType.SMS, + ], +) def test_post_notification_returns_201_when_content_type_is_missing_but_payload_is_valid_json( client, sample_service, notification_type, mocker ): template = create_template(service=sample_service, template_type=notification_type) - mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) - ) + mocker.patch(f"app.celery.provider_tasks.deliver_{notification_type}.apply_async") auth_header = create_service_authorization_header(service_id=sample_service.id) valid_json = { @@ -1178,33 +1199,45 @@ def test_post_notification_returns_201_when_content_type_is_missing_but_payload_ else: valid_json.update({"phone_number": "+447700900855"}) response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(valid_json), headers=[auth_header], ) assert response.status_code == 201 -@pytest.mark.parametrize("notification_type", ["email", "sms"]) +@pytest.mark.parametrize( + "notification_type", + [ + NotificationType.EMAIL, + NotificationType.SMS, + ], +) def test_post_email_notification_when_data_is_empty_returns_400( client, sample_service, notification_type ): auth_header = create_service_authorization_header(service_id=sample_service.id) data = None response = client.post( - path="/v2/notifications/{}".format(notification_type), + path=f"/v2/notifications/{notification_type}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) error_msg = json.loads(response.get_data(as_text=True))["errors"][0]["message"] assert response.status_code == 400 - if notification_type == "sms": + if notification_type == NotificationType.SMS: assert error_msg == "phone_number is a required property" else: assert error_msg == "email_address is a required property" -@pytest.mark.parametrize("notification_type", ("email", "sms")) +@pytest.mark.parametrize( + "notification_type", + ( + NotificationType.EMAIL, + NotificationType.SMS, + ), +) def test_post_notifications_saves_email_or_sms_to_queue( client, notify_db_session, mocker, notification_type ): @@ -1266,7 +1299,13 @@ def test_post_notifications_saves_email_or_sms_to_queue( botocore.parsers.ResponseParserError("exceeded max HTTP body length"), ], ) -@pytest.mark.parametrize("notification_type", ("email", "sms")) +@pytest.mark.parametrize( + "notification_type", + ( + NotificationType.EMAIL, + NotificationType.SMS, + ), +) def test_post_notifications_saves_email_or_sms_normally_if_saving_to_queue_fails( client, notify_db_session, mocker, notification_type, exception ): @@ -1324,7 +1363,13 @@ def test_post_notifications_saves_email_or_sms_normally_if_saving_to_queue_fails assert Notification.query.count() == 1 -@pytest.mark.parametrize("notification_type", ("email", "sms")) +@pytest.mark.parametrize( + "notification_type", + ( + NotificationType.EMAIL, + NotificationType.SMS, + ), +) def test_post_notifications_doesnt_use_save_queue_for_test_notifications( client, notify_db_session, mocker, notification_type ): From 4429e482218f152ad20848d73ba4372018bf2e61 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 20 Feb 2024 17:05:34 -0500 Subject: [PATCH 199/259] More changes are done. Signed-off-by: Cliff Hill --- app/models.py | 38 +++++++++---------- .../total_sent_notifications.py | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/models.py b/app/models.py index 7d8cb9e53..07806365f 100644 --- a/app/models.py +++ b/app/models.py @@ -1652,26 +1652,26 @@ class Notification(db.Model): @property def formatted_status(self): return { - "email": { - "failed": "Failed", - "technical-failure": "Technical failure", - "temporary-failure": "Inbox not accepting messages right now", - "permanent-failure": "Email address doesn’t exist", - "delivered": "Delivered", - "sending": "Sending", - "created": "Sending", - "sent": "Delivered", + NotificationType.EMAIL: { + NotificationStatus.FAILED: "Failed", + NotificationStatus.TECHNICAL_FAILURE: "Technical failure", + NotificationStatus.TEMPORARY_FAILURE: "Inbox not accepting messages right now", + NotificationStatus.PERMANENT_FAILURE: "Email address doesn’t exist", + NotificationStatus.DELIVERED: "Delivered", + NotificationStatus.SENDING: "Sending", + NotificationStatus.CREATED: "Sending", + NotificationStatus.SENT: "Delivered", }, - "sms": { - "failed": "Failed", - "technical-failure": "Technical failure", - "temporary-failure": "Unable to find carrier response -- still looking", - "permanent-failure": "Unable to find carrier response.", - "delivered": "Delivered", - "pending": "Pending", - "sending": "Sending", - "created": "Sending", - "sent": "Sent internationally", + NotificationType.SMS: { + NotificationStatus.FAILED: "Failed", + NotificationStatus.TECHNICAL_FAILURE: "Technical failure", + NotificationStatus.TEMPORARY_FAILURE: "Unable to find carrier response -- still looking", + NotificationStatus.PERMANENT_FAILURE: "Unable to find carrier response.", + NotificationStatus.DELIVERED: "Delivered", + NotificationStatus.PENDING: "Pending", + NotificationStatus.SENDING: "Sending", + NotificationStatus.CREATED: "Sending", + NotificationStatus.SENT: "Sent internationally", }, }[self.template.template_type].get(self.status, self.status) diff --git a/app/performance_platform/total_sent_notifications.py b/app/performance_platform/total_sent_notifications.py index 3d8806b7b..b8d043b19 100644 --- a/app/performance_platform/total_sent_notifications.py +++ b/app/performance_platform/total_sent_notifications.py @@ -26,6 +26,6 @@ def get_total_sent_notifications_for_day(day): sms_count = get_total_sent_notifications_for_day_and_type(day, NotificationType.SMS) return { - "email": email_count, - "sms": sms_count, + NotificationType.EMAIL: email_count, + NotificationType.SMS: sms_count, } From 9fde1f30ea1461c1dfe298f557715dafd2ba5ad4 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 08:36:42 -0500 Subject: [PATCH 200/259] "sms" entries cleaned up. Signed-off-by: Cliff Hill --- tests/app/delivery/test_send_to_providers.py | 10 +- tests/app/platform_stats/test_rest.py | 20 ++-- tests/app/service/test_statistics_rest.py | 101 +++++++++++++------ 3 files changed, 87 insertions(+), 44 deletions(-) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index c6cb9a5c4..d260f47e6 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -14,7 +14,7 @@ from app.dao import notifications_dao from app.dao.provider_details_dao import get_provider_details_by_identifier from app.delivery import send_to_providers from app.delivery.send_to_providers import get_html_email_options, get_logo_url -from app.enums import BrandType, KeyType, NotificationStatus +from app.enums import BrandType, KeyType, NotificationStatus, NotificationType from app.exceptions import NotificationTechnicalFailureException from app.models import EmailBranding, Notification from app.serialised_models import SerialisedService @@ -53,7 +53,7 @@ def test_provider_to_use_should_only_return_sns_for_international( sns = get_provider_details_by_identifier("sns") sns.priority = international_provider_priority - ret = send_to_providers.provider_to_use("sms", international=True) + ret = send_to_providers.provider_to_use(NotificationType.SMS, international=True) assert ret.name == "sns" @@ -66,7 +66,7 @@ def test_provider_to_use_raises_if_no_active_providers( # flake8 doesn't like raises with a generic exception try: - send_to_providers.provider_to_use("sms") + send_to_providers.provider_to_use(NotificationType.SMS) assert 1 == 0 except Exception: assert 1 == 1 @@ -496,7 +496,9 @@ def test_update_notification_to_sending_does_not_update_status_from_a_final_stat notification = create_notification(template=template, status=starting_status) send_to_providers.update_notification_to_sending( notification, - notification_provider_clients.get_client_by_name_and_type("sns", "sms"), + notification_provider_clients.get_client_by_name_and_type( + "sns", NotificationType.SMS + ), ) assert notification.status == expected_status diff --git a/tests/app/platform_stats/test_rest.py b/tests/app/platform_stats/test_rest.py index ffd6bd8cf..2b4fada54 100644 --- a/tests/app/platform_stats/test_rest.py +++ b/tests/app/platform_stats/test_rest.py @@ -92,22 +92,22 @@ def test_get_platform_stats_with_real_query(admin_request, notify_db_session): start_date=date(2018, 10, 29), ) assert response == { - "email": { + NotificationType.EMAIL: { "failures": { - "virus-scan-failed": 0, - "temporary-failure": 0, - "permanent-failure": 0, - "technical-failure": 0, + NotificationStatus.VIRUS_SCAN_FAILED: 0, + NotificationStatus.TEMPORARY_FAILURE: 0, + NotificationStatus.PERMANENT_FAILURE: 0, + NotificationStatus.TECHNICAL_FAILURE: 0, }, "total": 4, "test-key": 0, }, - "sms": { + NotificationType.SMS: { "failures": { - "virus-scan-failed": 0, - "temporary-failure": 0, - "permanent-failure": 0, - "technical-failure": 0, + NotificationStatus.VIRUS_SCAN_FAILED: 0, + NotificationStatus.TEMPORARY_FAILURE: 0, + NotificationStatus.PERMANENT_FAILURE: 0, + NotificationStatus.TECHNICAL_FAILURE: 0, }, "total": 11, "test-key": 1, diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index 3a43aa3b6..6216f2ab2 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -18,7 +18,9 @@ def test_get_template_usage_by_month_returns_correct_data( admin_request, sample_template ): create_ft_notification_status( - local_date=date(2017, 4, 2), template=sample_template, count=3 + local_date=date(2017, 4, 2), + template=sample_template, + count=3, ) create_notification(sample_template, created_at=datetime.utcnow()) @@ -57,10 +59,14 @@ def test_get_template_usage_by_month_returns_two_templates( hidden=True, ) create_ft_notification_status( - local_date=datetime(2017, 4, 2), template=template_one, count=1 + local_date=datetime(2017, 4, 2), + template=template_one, + count=1, ) create_ft_notification_status( - local_date=datetime(2017, 4, 2), template=sample_template, count=3 + local_date=datetime(2017, 4, 2), + template=sample_template, + count=3, ) create_notification(sample_template, created_at=datetime.utcnow()) @@ -191,7 +197,7 @@ def test_get_monthly_notification_stats_returns_empty_stats_with_correct_dates( ] assert sorted(response["data"].keys()) == keys for val in response["data"].values(): - assert val == {"sms": {}, "email": {}} + assert val == {NotificationType.SMS: {}, NotificationType.EMAIL: {}} def test_get_monthly_notification_stats_returns_stats(admin_request, sample_service): @@ -205,7 +211,9 @@ def test_get_monthly_notification_stats_returns_stats(admin_request, sample_serv create_ft_notification_status(datetime(2016, 7, 1), template=sms_t1) create_ft_notification_status(datetime(2016, 7, 1), template=sms_t2) create_ft_notification_status( - datetime(2016, 7, 1), template=sms_t1, notification_status="created" + datetime(2016, 7, 1), + template=sms_t1, + notification_status=NotificationStatus.CREATED, ) create_ft_notification_status(datetime(2016, 7, 1), template=email_template) @@ -217,19 +225,19 @@ def test_get_monthly_notification_stats_returns_stats(admin_request, sample_serv assert len(response["data"]) == 12 assert response["data"]["2016-06"] == { - "sms": { + NotificationType.SMS: { # it combines the two days - "delivered": 2 + NotificationStatus.DELIVERED: 2 }, - "email": {}, + NotificationType.EMAIL: {}, } assert response["data"]["2016-07"] == { # it combines the two template types - "sms": { - "created": 1, - "delivered": 2, + NotificationType.SMS: { + NotificationStatus.CREATED: 1, + NotificationStatus.DELIVERED: 2, }, - "email": {"delivered": 1}, + NotificationType.EMAIL: {"delivered": 1}, } @@ -274,14 +282,17 @@ def test_get_monthly_notification_stats_combines_todays_data_and_historic_stats( ) assert len(response["data"]) == 6 # January to June - assert response["data"]["2016-05"] == {"sms": {"delivered": 1}, "email": {}} + assert response["data"]["2016-05"] == { + NotificationType.SMS: {NotificationStatus.DELIVERED: 1}, + NotificationType.EMAIL: {}, + } assert response["data"]["2016-06"] == { - "sms": { + NotificationType.SMS: { # combines the stats from the historic ft_notification_status and the current notifications - "created": 3, - "delivered": 1, + NotificationStatus.CREATED: 3, + NotificationStatus.DELIVERED: 1, }, - "email": {}, + NotificationType.EMAIL: {}, } @@ -289,13 +300,22 @@ def test_get_monthly_notification_stats_ignores_test_keys( admin_request, sample_service ): create_ft_notification_status( - datetime(2016, 6, 1), service=sample_service, key_type=KeyType.NORMAL, count=1 + datetime(2016, 6, 1), + service=sample_service, + key_type=KeyType.NORMAL, + count=1, ) create_ft_notification_status( - datetime(2016, 6, 1), service=sample_service, key_type=KeyType.TEAM, count=2 + datetime(2016, 6, 1), + service=sample_service, + key_type=KeyType.TEAM, + count=2, ) create_ft_notification_status( - datetime(2016, 6, 1), service=sample_service, key_type=KeyType.TEST, count=4 + datetime(2016, 6, 1), + service=sample_service, + key_type=KeyType.TEST, + count=4, ) response = admin_request.get( @@ -304,20 +324,28 @@ def test_get_monthly_notification_stats_ignores_test_keys( year=2016, ) - assert response["data"]["2016-06"]["sms"] == {"delivered": 3} + assert response["data"]["2016-06"][NotificationType.SMS] == { + NotificationStatus.DELIVERED: 3, + } def test_get_monthly_notification_stats_checks_dates(admin_request, sample_service): t = create_template(sample_service) # create_ft_notification_status(datetime(2016, 3, 31), template=t, notification_status='created') create_ft_notification_status( - datetime(2016, 4, 2), template=t, notification_status="sending" + datetime(2016, 4, 2), + template=t, + notification_status=NotificationStatus.SENDING, ) create_ft_notification_status( - datetime(2017, 3, 31), template=t, notification_status="delivered" + datetime(2017, 3, 31), + template=t, + notification_status=NotificationStatus.DELIVERED, ) create_ft_notification_status( - datetime(2017, 4, 11), template=t, notification_status="permanent-failure" + datetime(2017, 4, 11), + template=t, + notification_status=NotificationStatus.PERMANENT_FAILURE, ) response = admin_request.get( @@ -327,8 +355,12 @@ def test_get_monthly_notification_stats_checks_dates(admin_request, sample_servi ) assert "2016-04" in response["data"] assert "2017-04" not in response["data"] - assert response["data"]["2016-04"]["sms"] == {"sending": 1} - assert response["data"]["2016-04"]["sms"] == {"sending": 1} + assert response["data"]["2016-04"][NotificationType.SMS] == { + NotificationStatus.SENDING: 1, + } + assert response["data"]["2016-04"][NotificationType.SMS] == { + NotificationStatus.SENDING: 1, + } def test_get_monthly_notification_stats_only_gets_for_one_service( @@ -339,14 +371,23 @@ def test_get_monthly_notification_stats_only_gets_for_one_service( templates = [create_template(services[0]), create_template(services[1])] create_ft_notification_status( - datetime(2016, 6, 1), template=templates[0], notification_status="created" + datetime(2016, 6, 1), + template=templates[0], + notification_status=NotificationStatus.CREATED, ) create_ft_notification_status( - datetime(2016, 6, 1), template=templates[1], notification_status="delivered" + datetime(2016, 6, 1), + template=templates[1], + notification_status=NotificationStatus.DELIVERED, ) response = admin_request.get( - "service.get_monthly_notification_stats", service_id=services[0].id, year=2016 + "service.get_monthly_notification_stats", + service_id=services[0].id, + year=2016, ) - assert response["data"]["2016-06"] == {"sms": {"created": 1}, "email": {}} + assert response["data"]["2016-06"] == { + NotificationType.SMS: {NotificationStatus.CREATED: 1}, + NotificationType.EMAIL: {}, + } From 6f5f5fb864736022476e831ce54d8c8ec0116109 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 10:24:18 -0500 Subject: [PATCH 201/259] Cleaning up "email" from code. Signed-off-by: Cliff Hill --- tests/app/billing/test_rest.py | 8 +- tests/app/celery/test_nightly_tasks.py | 10 +- .../app/celery/test_service_callback_tasks.py | 12 +- tests/app/celery/test_tasks.py | 16 +- tests/app/conftest.py | 35 ++-- tests/app/dao/test_complaint_dao.py | 9 +- .../dao/test_service_data_retention_dao.py | 2 +- tests/app/dao/test_services_dao.py | 6 +- tests/app/db.py | 24 +-- tests/app/notifications/test_validators.py | 34 ++-- tests/app/service/test_rest.py | 187 +++++++++--------- tests/app/test_model.py | 6 +- 12 files changed, 177 insertions(+), 172 deletions(-) diff --git a/tests/app/billing/test_rest.py b/tests/app/billing/test_rest.py index e1dfc33d3..5c82cabab 100644 --- a/tests/app/billing/test_rest.py +++ b/tests/app/billing/test_rest.py @@ -155,7 +155,7 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se ) sms_template = create_template(service=service, template_type=TemplateType.SMS) - email_template = create_template(service=service, template_type="email") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) for dt in (date(2016, 1, 28), date(2016, 8, 10), date(2016, 12, 26)): create_ft_billing(local_date=dt, template=sms_template, rate=0.0162) @@ -171,7 +171,11 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se assert len(json_response) == 3 # 3 billed months for SMS - email_rows = [row for row in json_response if row["notification_type"] == "email"] + email_rows = [ + row + for row in json_response + if row["notification_type"] == NotificationType.EMAIL + ] assert len(email_rows) == 0 sms_row = next( diff --git a/tests/app/celery/test_nightly_tasks.py b/tests/app/celery/test_nightly_tasks.py index 0e98d0f48..0c496fb2c 100644 --- a/tests/app/celery/test_nightly_tasks.py +++ b/tests/app/celery/test_nightly_tasks.py @@ -104,10 +104,10 @@ def test_will_remove_csv_files_for_jobs_older_than_retention_period( days_of_retention=30, ) sms_template_service_1 = create_template(service=service_1) - email_template_service_1 = create_template(service=service_1, template_type="email") + email_template_service_1 = create_template(service=service_1, template_type=TemplateType.EMAIL,) sms_template_service_2 = create_template(service=service_2) - email_template_service_2 = create_template(service=service_2, template_type="email") + email_template_service_2 = create_template(service=service_2, template_type=TemplateType.EMAIL,) four_days_ago = datetime.utcnow() - timedelta(days=4) eight_days_ago = datetime.utcnow() - timedelta(days=8) @@ -153,7 +153,7 @@ def test_delete_email_notifications_older_than_retentions_calls_child_task( "app.celery.nightly_tasks._delete_notifications_older_than_retention_by_type" ) delete_email_notifications_older_than_retention() - mocked_notifications.assert_called_once_with("email") + mocked_notifications.assert_called_once_with(NotificationType.EMAIL) @freeze_time("2021-12-13T10:00") @@ -352,10 +352,10 @@ def test_delete_notifications_task_calls_task_for_services_that_have_sent_notifi create_template(service_will_delete_1) create_template(service_will_delete_2) nothing_to_delete_sms_template = create_template( - service_nothing_to_delete, template_type=TemplateType.SMS + service_nothing_to_delete, template_type=TemplateType.SMS, ) nothing_to_delete_email_template = create_template( - service_nothing_to_delete, template_type="email" + service_nothing_to_delete, template_type=TemplateType.EMAIL, ) # will be deleted as service has no custom retention, but past our default 7 days diff --git a/tests/app/celery/test_service_callback_tasks.py b/tests/app/celery/test_service_callback_tasks.py index af1e8abd4..a39241528 100644 --- a/tests/app/celery/test_service_callback_tasks.py +++ b/tests/app/celery/test_service_callback_tasks.py @@ -64,14 +64,14 @@ def test_send_delivery_status_to_service_post_https_request_to_service_with_encr assert request_mock.request_history[0].headers["Content-type"] == "application/json" assert request_mock.request_history[0].headers[ "Authorization" - ] == "Bearer {}".format(callback_api.bearer_token) + ] == f"Bearer {callback_api.bearer_token}" def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted_data( notify_db_session, ): with freeze_time("2001-01-01T12:00:00"): - callback_api, template = _set_up_test_data("email", "complaint") + callback_api, template = _set_up_test_data(NotificationType.EMAIL, CallbackType.COMPLAINT,) notification = create_notification(template=template) complaint = create_complaint( @@ -102,7 +102,7 @@ def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted ) assert request_mock.request_history[0].headers[ "Authorization" - ] == "Bearer {}".format(callback_api.bearer_token) + ] == f"Bearer {callback_api.bearer_token}" @pytest.mark.parametrize( @@ -113,7 +113,7 @@ def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted def test__send_data_to_service_callback_api_retries_if_request_returns_error_code_with_encrypted_data( notify_db_session, mocker, notification_type, status_code ): - callback_api, template = _set_up_test_data(notification_type, "delivery_status") + callback_api, template = _set_up_test_data(notification_type, CallbackType.DELIVERY_STATUS,) datestr = datetime(2017, 6, 20) notification = create_notification( template=template, @@ -143,7 +143,7 @@ def test__send_data_to_service_callback_api_retries_if_request_returns_error_cod def test__send_data_to_service_callback_api_does_not_retry_if_request_returns_404_with_encrypted_data( notify_db_session, mocker, notification_type ): - callback_api, template = _set_up_test_data(notification_type, "delivery_status") + callback_api, template = _set_up_test_data(notification_type, CallbackType.DELIVERY_STATUS,) datestr = datetime(2017, 6, 20) notification = create_notification( template=template, @@ -178,7 +178,7 @@ def test_send_delivery_status_to_service_succeeds_if_sent_at_is_none( created_at=datestr, updated_at=datestr, sent_at=None, - status="technical-failure", + status=NotificationStatus.TECHNICAL_FAILURE, ) encrypted_data = _set_up_data_for_status_update(callback_api, notification) mocked = mocker.patch( diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 7d61e28ad..a3ea1baf9 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -116,7 +116,7 @@ def test_should_process_sms_job(sample_job, mocker): queue="database-tasks", ) job = jobs_dao.dao_get_job_by_id(sample_job.id) - assert job.job_status == "finished" + assert job.job_status == JobStatus.FINISHED def test_should_process_sms_job_with_sender_id(sample_job, mocker, fake_uuid): @@ -138,7 +138,7 @@ def test_should_process_sms_job_with_sender_id(sample_job, mocker, fake_uuid): def test_should_not_process_job_if_already_pending(sample_template, mocker): - job = create_job(template=sample_template, job_status="scheduled") + job = create_job(template=sample_template, job_status=JobStatus.SCHEDULED) mocker.patch("app.celery.tasks.s3.get_job_and_metadata_from_s3") mocker.patch("app.celery.tasks.process_row") @@ -153,7 +153,7 @@ def test_should_process_job_if_send_limits_are_not_exceeded( notify_api, notify_db_session, mocker ): service = create_service(message_limit=10) - template = create_template(service=service, template_type="email") + template = create_template(service=service, template_type=TemplateType.EMAIL) job = create_job(template=template, notification_count=10) mocker.patch( @@ -169,7 +169,7 @@ def test_should_process_job_if_send_limits_are_not_exceeded( service_id=str(job.service.id), job_id=str(job.id) ) job = jobs_dao.dao_get_job_by_id(job.id) - assert job.job_status == "finished" + assert job.job_status == JobStatus.FINISHED tasks.save_email.apply_async.assert_called_with( ( str(job.service_id), @@ -238,7 +238,7 @@ def test_should_process_email_job(email_job_with_placeholders, mocker): queue="database-tasks", ) job = jobs_dao.dao_get_job_by_id(email_job_with_placeholders.id) - assert job.job_status == "finished" + assert job.job_status == JobStatus.FINISHED def test_should_process_email_job_with_sender_id( @@ -293,7 +293,7 @@ def test_should_process_all_sms_job(sample_job_with_placeholdered_template, mock } assert tasks.save_sms.apply_async.call_count == 10 job = jobs_dao.dao_get_job_by_id(sample_job_with_placeholdered_template.id) - assert job.job_status == "finished" + assert job.job_status == JobStatus.FINISHED # -------------- process_row tests -------------- # @@ -311,7 +311,7 @@ def test_process_row_sends_letter_task( ): mocker.patch("app.celery.tasks.create_uuid", return_value="noti_uuid") task_mock = mocker.patch( - "app.celery.tasks.{}.apply_async".format(expected_function) + f"app.celery.tasks.{expected_function}.apply_async" ) encrypt_mock = mocker.patch("app.celery.tasks.encryption.encrypt") template = Mock(id="template_id", template_type=template_type) @@ -1041,7 +1041,7 @@ def test_send_inbound_sms_to_service_post_https_request_to_service( assert request_mock.request_history[0].headers["Content-type"] == "application/json" assert request_mock.request_history[0].headers[ "Authorization" - ] == "Bearer {}".format(inbound_api.bearer_token) + ] == f"Bearer {inbound_api.bearer_token}" def test_send_inbound_sms_to_service_does_not_send_request_when_inbound_sms_does_not_exist( diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 44e93b98b..86386d731 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -19,6 +19,7 @@ from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template from app.dao.users_dao import create_secret_code, create_user_code from app.enums import ( + JobStatus, KeyType, NotificationStatus, RecipientType, @@ -160,7 +161,7 @@ def service_factory(sample_user): user=user, check_if_service_exists=True, ) - if template_type == "email": + if template_type == TemplateType.EMAIL: create_template( service, template_name="Template Name", @@ -381,7 +382,7 @@ def sample_job(notify_db_session): "notification_count": 1, "created_at": datetime.utcnow(), "created_by": service.created_by, - "job_status": "pending", + "job_status": JobStatus.PENDING, "scheduled_for": None, "processing_started": None, "archived": False, @@ -405,7 +406,7 @@ def sample_job_with_placeholdered_template( def sample_scheduled_job(sample_template_with_placeholders): return create_job( sample_template_with_placeholders, - job_status="scheduled", + job_status=JobStatus.SCHEDULED, scheduled_for=(datetime.utcnow() + timedelta(minutes=60)).isoformat(), ) @@ -420,7 +421,7 @@ def sample_notification_with_job(notify_db_session): job=job, job_row_number=None, to_field=None, - status="created", + status=NotificationStatus.CREATED, reference=None, created_at=None, sent_at=None, @@ -525,7 +526,7 @@ def sample_notification_history(notify_db_session, sample_template): service=sample_template.service, template_id=sample_template.id, template_version=sample_template.version, - status="created", + status=NotificationStatus.CREATED, created_at=created_at, notification_type=notification_type, key_type=KeyType.NORMAL, @@ -572,7 +573,7 @@ def sample_expired_user(notify_db_session): "permissions": "send_messages,manage_service,manage_api_keys", "folder_permissions": ["folder_1_id", "folder_2_id"], "created_at": datetime.utcnow() - timedelta(days=3), - "status": "expired", + "status": NotificationStatus.EXPIRED, } expired_user = InvitedUser(**data) save_invited_user(expired_user) @@ -639,7 +640,7 @@ def email_2fa_code_template(notify_service): "((url))" ), subject="Sign in to GOV.UK Notify", - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -650,7 +651,7 @@ def email_verification_template(notify_service): user=notify_service.users[0], template_config_name="NEW_USER_EMAIL_VERIFICATION_TEMPLATE_ID", content="((user_name)) use ((url)) to complete registration", - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -665,7 +666,7 @@ def invitation_email_template(notify_service): template_config_name="INVITATION_EMAIL_TEMPLATE_ID", content=content, subject="Invitation to ((service_name))", - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -677,7 +678,7 @@ def org_invite_email_template(notify_service): template_config_name="ORGANIZATION_INVITATION_EMAIL_TEMPLATE_ID", content="((user_name)) ((organization_name)) ((url))", subject="Invitation to ((organization_name))", - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -689,7 +690,7 @@ def password_reset_email_template(notify_service): template_config_name="PASSWORD_RESET_TEMPLATE_ID", content="((user_name)) you can reset password by clicking ((url))", subject="Reset your password", - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -701,7 +702,7 @@ def verify_reply_to_address_email_template(notify_service): template_config_name="REPLY_TO_EMAIL_ADDRESS_VERIFICATION_TEMPLATE_ID", content="Hi,This address has been provided as the reply-to email address so we are verifying if it's working", subject="Your GOV.UK Notify reply-to email address", - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -713,7 +714,7 @@ def team_member_email_edit_template(notify_service): template_config_name="TEAM_MEMBER_EDIT_EMAIL_TEMPLATE_ID", content="Hi ((name)) ((servicemanagername)) changed your email to ((email address))", subject="Your GOV.UK Notify email address has changed", - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -737,7 +738,7 @@ def already_registered_template(notify_service): user=notify_service.users[0], template_config_name="ALREADY_REGISTERED_EMAIL_TEMPLATE_ID", content=content, - template_type="email", + template_type=TemplateType.EMAIL, ) @@ -753,7 +754,7 @@ def change_email_confirmation_template(notify_service): user=notify_service.users[0], template_config_name="CHANGE_EMAIL_CONFIRMATION_TEMPLATE_ID", content=content, - template_type="email", + template_type=TemplateType.EMAIL, ) return template @@ -771,7 +772,7 @@ def mou_signed_templates(notify_service): notify_service, notify_service.users[0], config_name, - "email", + TemplateType.EMAIL, content="\n".join( next( x @@ -862,7 +863,7 @@ def sample_inbound_numbers(sample_service): inbound_numbers.append(create_inbound_number(number="1", provider="sns")) inbound_numbers.append( create_inbound_number( - number="2", provider="sns", active=False, service_id=service.id + number="2", provider="sns", active=False, service_id=service.id, ) ) return inbound_numbers diff --git a/tests/app/dao/test_complaint_dao.py b/tests/app/dao/test_complaint_dao.py index 4a293ffc5..2f6f3d866 100644 --- a/tests/app/dao/test_complaint_dao.py +++ b/tests/app/dao/test_complaint_dao.py @@ -7,6 +7,7 @@ from app.dao.complaint_dao import ( fetch_paginated_complaints, save_complaint, ) +from app.enums import TemplateType from app.models import Complaint from tests.app.db import ( create_complaint, @@ -72,8 +73,8 @@ def test_fetch_complaint_by_service_returns_empty_list(sample_service): def test_fetch_complaint_by_service_return_many(notify_db_session): service_1 = create_service(service_name="first") service_2 = create_service(service_name="second") - template_1 = create_template(service=service_1, template_type="email") - template_2 = create_template(service=service_2, template_type="email") + template_1 = create_template(service=service_1, template_type=TemplateType.EMAIL) + template_2 = create_template(service=service_2, template_type=TemplateType.EMAIL) notification_1 = create_notification(template=template_1) notification_2 = create_notification(template=template_2) notification_3 = create_notification(template=template_2) @@ -138,13 +139,13 @@ def test_fetch_count_of_complaints(sample_email_notification): ) count_of_complaints = fetch_count_of_complaints( - start_date=datetime(2018, 6, 7), end_date=datetime(2018, 6, 7) + start_date=datetime(2018, 6, 7), end_date=datetime(2018, 6, 7), ) assert count_of_complaints == 5 def test_fetch_count_of_complaints_returns_zero(notify_db_session): count_of_complaints = fetch_count_of_complaints( - start_date=datetime(2018, 6, 7), end_date=datetime(2018, 6, 7) + start_date=datetime(2018, 6, 7), end_date=datetime(2018, 6, 7), ) assert count_of_complaints == 0 diff --git a/tests/app/dao/test_service_data_retention_dao.py b/tests/app/dao/test_service_data_retention_dao.py index f800bd464..bb5765fe7 100644 --- a/tests/app/dao/test_service_data_retention_dao.py +++ b/tests/app/dao/test_service_data_retention_dao.py @@ -57,7 +57,7 @@ def test_fetch_service_data_retention_returns_empty_list_when_no_rows_for_servic def test_fetch_service_data_retention_by_id(sample_service): - email_data_retention = insert_service_data_retention(sample_service.id, "email", 3) + email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) insert_service_data_retention(sample_service.id, NotificationType.SMS, 13) result = fetch_service_data_retention_by_id( sample_service.id, email_data_retention.id diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 990315260..3aa50738e 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -511,7 +511,7 @@ def test_dao_fetch_live_services_data(sample_user): create_service(service_name="restricted", restricted=True) create_service(service_name="not_active", active=False) create_service(service_name="not_live", count_as_live=False) - email_template = create_template(service=service, template_type="email") + email_template = create_template(service=service, template_type=TemplateType.EMAIL) dao_add_service_to_organization(service=service, organization_id=org.id) # two sms billing records for 1st service within current financial year: create_ft_billing(local_date="2019-04-20", template=sms_template) @@ -1217,8 +1217,8 @@ def test_dao_fetch_todays_stats_for_all_services_only_includes_today(notify_db_s stats = dao_fetch_todays_stats_for_all_services() stats = {row.status: row.count for row in stats} - assert "delivered" not in stats - assert stats["failed"] == 1 + assert NotificationStatus.DELIVERED not in stats + assert stats[NotificationStatus.FAILED] == 1 def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db_session): diff --git a/tests/app/db.py b/tests/app/db.py index 9de87ba48..ec8098cf5 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -141,7 +141,7 @@ def create_service( created_by=user if user else create_user( - email="{}@digital.cabinet-office.gov.uk".format(uuid.uuid4()) + email=f"{uuid.uuid4()}@digital.cabinet-office.gov.uk" ), prefix_sms=prefix_sms, organization_type=organization_type, @@ -212,7 +212,7 @@ def create_template( contact_block_id=None, ): data = { - "name": template_name or "{} Template Name".format(template_type), + "name": template_name or f"{template_type} Template Name", "template_type": template_type, "content": content, "service": service, @@ -353,7 +353,7 @@ def create_notification_history( if created_at is None: created_at = datetime.utcnow() - if status != "created": + if status != NotificationStatus.CREATED: sent_at = sent_at or datetime.utcnow() updated_at = updated_at or datetime.utcnow() @@ -498,7 +498,7 @@ def create_service_callback_api( def create_email_branding( - id=None, colour="blue", logo="test_x2.png", name="test_org_1", text="DisplayName" + id=None, colour="blue", logo="test_x2.png", name="test_org_1", text="DisplayName", ): data = { "colour": colour, @@ -529,7 +529,7 @@ def create_rate(start_date, value, notification_type): def create_api_key(service, key_type=KeyType.NORMAL, key_name=None): id_ = uuid.uuid4() - name = key_name if key_name else "{} api key {}".format(key_type, id_) + name = key_name if key_name else f"{key_type} api key {id_}" api_key = ApiKey( service=service, @@ -644,7 +644,7 @@ def create_organization( def create_invited_org_user( - organization, invited_by, email_address="invite@example.com" + organization, invited_by, email_address="invite@example.com", ): invited_org_user = InvitedOrganizationUser( email_address=email_address, @@ -752,7 +752,7 @@ def create_complaint(service=None, notification=None, created_at=None): if not service: service = create_service() if not notification: - template = create_template(service=service, template_type="email") + template = create_template(service=service, template_type=TemplateType.EMAIL) notification = create_notification(template=template) complaint = Complaint( @@ -907,7 +907,7 @@ def set_up_usage_data(start_date): financial_year_start=year, ) org_1 = create_organization( - name="Org for {}".format(service_1_sms_and_letter.name), + name=f"Org for {service_1_sms_and_letter.name}", purchase_order_number="org1 purchase order number", billing_contact_names="org1 billing contact names", billing_contact_email_addresses="org1@billing.contact email@addresses.gov.uk", @@ -918,13 +918,13 @@ def set_up_usage_data(start_date): ) create_ft_billing( - local_date=one_week_earlier, template=sms_template_1, billable_unit=2, rate=0.11 + local_date=one_week_earlier, template=sms_template_1, billable_unit=2, rate=0.11, ) create_ft_billing( - local_date=start_date, template=sms_template_1, billable_unit=2, rate=0.11 + local_date=start_date, template=sms_template_1, billable_unit=2, rate=0.11, ) create_ft_billing( - local_date=two_days_later, template=sms_template_1, billable_unit=1, rate=0.11 + local_date=two_days_later, template=sms_template_1, billable_unit=1, rate=0.11, ) # service with emails only: @@ -933,7 +933,7 @@ def set_up_usage_data(start_date): service=service_with_emails, template_type=TemplateType.EMAIL ) org_2 = create_organization( - name="Org for {}".format(service_with_emails.name), + name=f"Org for {service_with_emails.name}", ) dao_add_service_to_organization( service=service_with_emails, organization_id=org_2.id diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index 25d4695bf..10ab1e031 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -119,8 +119,8 @@ def test_check_template_is_for_notification_type_fails_when_template_type_does_n notification_type=notification_type, template_type=template_type ) assert e.value.status_code == 400 - error_message = "{0} template is not suitable for {1} notification".format( - template_type, notification_type + error_message = ( + f"{template_type} template is not suitable for {notification_type} notification" ) assert e.value.message == error_message assert e.value.fields == [{"template": error_message}] @@ -474,7 +474,7 @@ def test_validate_template_calls_all_validators_exception_message_too_long( check_char_count=False, ) - mock_check_type.assert_called_once_with("email", "email") + mock_check_type.assert_called_once_with(NotificationType.EMAIL, TemplateType.EMAIL) mock_check_if_active.assert_called_once_with(template) mock_create_conent.assert_called_once_with(template, {}) mock_check_not_empty.assert_called_once_with("content") @@ -499,7 +499,7 @@ def test_check_service_over_api_rate_limit_when_exceed_rate_limit_request_fails_ check_service_over_api_rate_limit(serialised_service, serialised_api_key) assert app.redis_store.exceeded_rate_limit.called_with( - "{}-{}".format(str(sample_service.id), api_key.key_type), + f"{sample_service.id}-{api_key.key_type}", sample_service.rate_limit, 60, ) @@ -527,7 +527,7 @@ def test_check_service_over_api_rate_limit_when_rate_limit_has_not_exceeded_limi check_service_over_api_rate_limit(serialised_service, serialised_api_key) assert app.redis_store.exceeded_rate_limit.called_with( - "{}-{}".format(str(sample_service.id), api_key.key_type), 3000, 60 + f"{sample_service.id}-{api_key.key_type}", 3000, 60, ) @@ -583,7 +583,7 @@ def test_validate_and_format_recipient_fails_when_international_number_and_servi assert e.value.fields == [] -@pytest.mark.parametrize("key_type", ["test", "normal"]) +@pytest.mark.parametrize("key_type", [KeyType.TEST, KeyType.NORMAL]) def test_validate_and_format_recipient_succeeds_with_international_numbers_if_service_does_allow_int_sms( key_type, sample_service_full_permissions ): @@ -597,7 +597,7 @@ def test_validate_and_format_recipient_succeeds_with_international_numbers_if_se def test_validate_and_format_recipient_fails_when_no_recipient(): with pytest.raises(BadRequestError) as e: validate_and_format_recipient( - None, "key_type", "service", "NotificationType.SMS" + None, KeyType.NORMAL, "service", NotificationType.SMS, ) assert e.value.status_code == 400 assert e.value.message == "Recipient can't be empty" @@ -632,8 +632,9 @@ def test_check_service_email_reply_to_id_where_service_id_is_not_found( assert e.value.status_code == 400 assert ( e.value.message - == "email_reply_to_id {} does not exist in database for service id {}".format( - reply_to_address.id, fake_uuid + == ( + f"email_reply_to_id {reply_to_address.id} does not exist in database for i" + f"service id {fake_uuid}" ) ) @@ -648,8 +649,9 @@ def test_check_service_email_reply_to_id_where_reply_to_id_is_not_found( assert e.value.status_code == 400 assert ( e.value.message - == "email_reply_to_id {} does not exist in database for service id {}".format( - fake_uuid, sample_service.id + == ( + f"email_reply_to_id {fake_uuid} does not exist in database for service " + f"id {sample_service.id}" ) ) @@ -683,8 +685,9 @@ def test_check_service_sms_sender_id_where_service_id_is_not_found( assert e.value.status_code == 400 assert ( e.value.message - == "sms_sender_id {} does not exist in database for service id {}".format( - sms_sender.id, fake_uuid + == ( + f"sms_sender_id {sms_sender.id} does not exist in database for service " + f"id {fake_uuid}" ) ) @@ -697,8 +700,9 @@ def test_check_service_sms_sender_id_where_sms_sender_is_not_found( assert e.value.status_code == 400 assert ( e.value.message - == "sms_sender_id {} does not exist in database for service id {}".format( - fake_uuid, sample_service.id + == ( + f"sms_sender_id {fake_uuid} does not exist in database for service " + f"id {sample_service.id}" ) ) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index a825c5acb..9c5dd174c 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -151,7 +151,7 @@ def test_find_services_by_name_handles_no_results( mock_get_services_by_partial_name = mocker.patch( "app.service.rest.get_services_by_partial_name", return_value=[] ) - response = admin_request.get("service.find_services_by_name", service_name="ABC")[ + response = admin_request.get("service.find_services_by_name", service_name="ABC",)[ "data" ] mock_get_services_by_partial_name.assert_called_once_with("ABC") @@ -330,7 +330,7 @@ def test_get_service_by_id_and_user(client, sample_service, sample_user): create_reply_to_email(service=sample_service, email_address="new@service.com") auth_header = create_admin_authorization_header() resp = client.get( - "/service/{}?user_id={}".format(sample_service.id, sample_user.id), + f"/service/{sample_service.id}?user_id={sample_user.id}", headers=[auth_header], ) assert resp.status_code == 200 @@ -345,7 +345,7 @@ def test_get_service_by_id_should_404_if_no_service_for_user(notify_api, sample_ service_id = str(uuid.uuid4()) auth_header = create_admin_authorization_header() resp = client.get( - "/service/{}?user_id={}".format(service_id, sample_user.id), + f"/service/{service_id}?user_id={sample_user.id}", headers=[auth_header], ) assert resp.status_code == 404 @@ -448,7 +448,7 @@ def test_create_service_with_domain_sets_organization( create_domain("fake.gov", another_org.id) create_domain("cabinetoffice.gov.uk", another_org.id) - sample_user.email_address = "test@{}".format(domain) + sample_user.email_address = f"test@{domain}" data = { "name": "created service", @@ -657,7 +657,7 @@ def test_should_not_create_service_with_duplicate_name( json_resp = resp.json assert json_resp["result"] == "error" assert ( - "Duplicate service name '{}'".format(sample_service.name) + f"Duplicate service name '{sample_service.name}'" in json_resp["message"]["name"] ) @@ -685,7 +685,7 @@ def test_create_service_should_throw_duplicate_key_constraint_for_existing_email json_resp = resp.json assert json_resp["result"] == "error" assert ( - "Duplicate service name '{}'".format(service_name) + f"Duplicate service name '{service_name}'" in json_resp["message"]["name"] ) @@ -712,7 +712,7 @@ def test_update_service(client, notify_db_session, sample_service): auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -735,7 +735,7 @@ def test_cant_update_service_org_type_to_random_value(client, sample_service): auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -784,7 +784,7 @@ def test_update_service_change_email_branding( def test_update_service_flags(client, sample_service): auth_header = create_admin_authorization_header() - resp = client.get("/service/{}".format(sample_service.id), headers=[auth_header]) + resp = client.get(f"/service/{sample_service.id}", headers=[auth_header]) json_resp = resp.json assert resp.status_code == 200 assert json_resp["data"]["name"] == sample_service.name @@ -793,7 +793,7 @@ def test_update_service_flags(client, sample_service): auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -879,7 +879,7 @@ def test_update_service_flags_with_service_without_default_service_permissions( } resp = client.post( - "/service/{}".format(service_with_no_permissions.id), + f"/service/{service_with_no_permissions.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -911,7 +911,7 @@ def test_update_service_flags_will_remove_service_permissions( data = {"permissions": [ServicePermissionType.SMS, ServicePermissionType.EMAIL]} resp = client.post( - "/service/{}".format(service.id), + f"/service/{service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -935,7 +935,7 @@ def test_update_permissions_will_override_permission_flags( data = {"permissions": [ServicePermissionType.INTERNATIONAL_SMS]} resp = client.post( - "/service/{}".format(service_with_no_permissions.id), + f"/service/{service_with_no_permissions.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -955,7 +955,7 @@ def test_update_service_permissions_will_add_service_permissions( data = {"permissions": [ServicePermissionType.EMAIL, ServicePermissionType.SMS]} resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -986,7 +986,7 @@ def test_add_service_permission_will_add_permission( data = {"permissions": [permission_to_add]} resp = client.post( - "/service/{}".format(service_with_no_permissions.id), + f"/service/{service_with_no_permissions.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1014,7 +1014,7 @@ def test_update_permissions_with_an_invalid_permission_will_raise_error( } resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1023,7 +1023,7 @@ def test_update_permissions_with_an_invalid_permission_will_raise_error( assert resp.status_code == 400 assert result["result"] == "error" assert ( - "Invalid Service Permission: '{}'".format(invalid_permission) + f"Invalid Service Permission: '{invalid_permission}'" in result["message"]["permissions"] ) @@ -1042,7 +1042,7 @@ def test_update_permissions_with_duplicate_permissions_will_raise_error( } resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1070,7 +1070,7 @@ def test_should_not_update_service_with_duplicate_name( auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1078,7 +1078,7 @@ def test_should_not_update_service_with_duplicate_name( json_resp = resp.json assert json_resp["result"] == "error" assert ( - "Duplicate service name '{}'".format(service_name) + f"Duplicate service name '{service_name}'" in json_resp["message"]["name"] ) @@ -1102,7 +1102,7 @@ def test_should_not_update_service_with_duplicate_email_from( auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1110,9 +1110,9 @@ def test_should_not_update_service_with_duplicate_email_from( json_resp = resp.json assert json_resp["result"] == "error" assert ( - "Duplicate service name '{}'".format(service_name) + f"Duplicate service name '{service_name}'" in json_resp["message"]["name"] - or "Duplicate service name '{}'".format(email_from) + or f"Duplicate service name '{email_from}'" in json_resp["message"]["name"] ) @@ -1127,7 +1127,7 @@ def test_update_service_should_404_if_id_is_invalid(notify_api): auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}".format(missing_service_id), + f"/service/{missing_service_id}", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -1141,7 +1141,7 @@ def test_get_users_by_service(notify_api, sample_service): auth_header = create_admin_authorization_header() resp = client.get( - "/service/{}/users".format(sample_service.id), + f"/service/{sample_service.id}/users", headers=[("Content-Type", "application/json"), auth_header], ) @@ -1162,7 +1162,7 @@ def test_get_users_for_service_returns_empty_list_if_no_users_associated_with_se auth_header = create_admin_authorization_header() response = client.get( - "/service/{}/users".format(sample_service.id), + f"/service/{sample_service.id}/users", headers=[("Content-Type", "application/json"), auth_header], ) result = json.loads(response.get_data(as_text=True)) @@ -1179,7 +1179,7 @@ def test_get_users_for_service_returns_404_when_service_does_not_exist( auth_header = create_admin_authorization_header() response = client.get( - "/service/{}/users".format(service_id), + f"/service/{service_id}/users", headers=[("Content-Type", "application/json"), auth_header], ) assert response.status_code == 404 @@ -1215,15 +1215,13 @@ def test_default_permissions_are_added_for_user_service( auth_header_fetch = create_admin_authorization_header() resp = client.get( - "/service/{}?user_id={}".format( - json_resp["data"]["id"], sample_user.id - ), + f"/service/{json_resp['data']['id']}?user_id={sample_user.id}", headers=[auth_header_fetch], ) assert resp.status_code == 200 header = create_admin_authorization_header() response = client.get( - url_for("user.get_user", user_id=sample_user.id), headers=[header] + url_for("user.get_user", user_id=sample_user.id), headers=[header], ) assert response.status_code == 200 json_resp = json.loads(response.get_data(as_text=True)) @@ -1246,7 +1244,7 @@ def test_add_existing_user_to_another_service_with_all_permissions( auth_header = create_admin_authorization_header() resp = client.get( - "/service/{}/users".format(sample_service.id), + f"/service/{sample_service.id}/users", headers=[("Content-Type", "application/json"), auth_header], ) @@ -1284,7 +1282,7 @@ def test_add_existing_user_to_another_service_with_all_permissions( auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(sample_service.id, user_to_add.id), + f"/service/{sample_service.id}/users/{user_to_add.id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) @@ -1295,7 +1293,7 @@ def test_add_existing_user_to_another_service_with_all_permissions( auth_header = create_admin_authorization_header() resp = client.get( - "/service/{}".format(sample_service.id), + f"/service/{sample_service.id}", headers=[("Content-Type", "application/json"), auth_header], ) assert resp.status_code == 200 @@ -1312,13 +1310,13 @@ def test_add_existing_user_to_another_service_with_all_permissions( json_resp = resp.json permissions = json_resp["data"]["permissions"][str(sample_service.id)] expected_permissions = [ - "send_texts", - "send_emails", - "manage_users", - "manage_settings", - "manage_templates", - "manage_api_keys", - "view_activity", + PermissionType.SEND_TEXTS, + PermissionType.SEND_EMAILS, + PermissionType.MANAGE_USERS, + PermissionType.MANAGE_SETTINGS, + PermissionType.MANAGE_TEMPLATES, + PermissionType.MANAGE_API_KEYS, + PermissionType.VIEW_ACTIVITY, ] assert sorted(expected_permissions) == sorted(permissions) @@ -1348,7 +1346,7 @@ def test_add_existing_user_to_another_service_with_send_permissions( auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(sample_service.id, user_to_add.id), + f"/service/{sample_service.id}/users/{user_to_add.id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) @@ -1395,7 +1393,7 @@ def test_add_existing_user_to_another_service_with_manage_permissions( auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(sample_service.id, user_to_add.id), + f"/service/{sample_service.id}/users/{user_to_add.id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) @@ -1446,7 +1444,7 @@ def test_add_existing_user_to_another_service_with_folder_permissions( auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(sample_service.id, user_to_add.id), + f"/service/{sample_service.id}/users/{user_to_add.id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) @@ -1481,7 +1479,7 @@ def test_add_existing_user_to_another_service_with_manage_api_keys( auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(sample_service.id, user_to_add.id), + f"/service/{sample_service.id}/users/{user_to_add.id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) @@ -1519,12 +1517,12 @@ def test_add_existing_user_to_non_existing_service_returns404( incorrect_id = uuid.uuid4() data = { - "permissions": ["send_messages", "manage_service", "manage_api_keys"] + "permissions": [PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, PermissionType.MANAGE_USERS, PermissionType.MANAGE_SETTINGS, PermissionType.MANAGE_TEMPLATES, PermissionType.MANAGE_API_KEYS,] } auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(incorrect_id, user_to_add.id), + f"/service/{incorrect_id}/users/{user_to_add.id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) @@ -1545,19 +1543,20 @@ def test_add_existing_user_of_service_to_service_returns400( existing_user_id = sample_service.users[0].id data = { - "permissions": ["send_messages", "manage_service", "manage_api_keys"] + "permissions": [PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, PermissionType.MANAGE_USERS, PermissionType.MANAGE_SETTINGS, PermissionType.MANAGE_TEMPLATES, PermissionType.MANAGE_API_KEYS,] } auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(sample_service.id, existing_user_id), + f"/service/{sample_service.id}/users/{existing_user_id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) result = resp.json - expected_message = "User id: {} already part of service id: {}".format( - existing_user_id, sample_service.id + expected_message = ( + f"User id: {existing_user_id} already part of service " + f"id: {sample_service.id}" ) assert resp.status_code == 400 @@ -1573,12 +1572,12 @@ def test_add_unknown_user_to_service_returns404( incorrect_id = 9876 data = { - "permissions": ["send_messages", "manage_service", "manage_api_keys"] + "permissions": [PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, PermissionType.MANAGE_USERS, PermissionType.MANAGE_SETTINGS, PermissionType.MANAGE_TEMPLATES, PermissionType.MANAGE_API_KEYS,] } auth_header = create_admin_authorization_header() resp = client.post( - "/service/{}/users/{}".format(sample_service.id, incorrect_id), + f"/service/{sample_service.id}/users/{incorrect_id}", headers=[("Content-Type", "application/json"), auth_header], data=json.dumps(data), ) @@ -1660,7 +1659,7 @@ def test_get_service_and_api_key_history(notify_api, sample_service, sample_api_ with notify_api.test_client() as client: auth_header = create_admin_authorization_header() response = client.get( - path="/service/{}/history".format(sample_service.id), + path=f"/service/{sample_service.id}/history", headers=[auth_header], ) assert response.status_code == 200 @@ -1691,7 +1690,7 @@ def test_get_all_notifications_for_service_in_order(client, notify_db_session): auth_header = create_admin_authorization_header() response = client.get( - path="/service/{}/notifications".format(service_1.id), headers=[auth_header] + path=f"/service/{service_1.id}/notifications", headers=[auth_header] ) resp = json.loads(response.get_data(as_text=True)) @@ -1790,9 +1789,7 @@ def test_get_all_notifications_for_service_formatted_for_csv(client, sample_temp auth_header = create_admin_authorization_header() response = client.get( - path="/service/{}/notifications?format_for_csv=True".format( - sample_template.service_id - ), + path=f"/service/{sample_template.service_id}/notifications?format_for_csv=True", headers=[auth_header], ) @@ -1809,7 +1806,7 @@ def test_get_all_notifications_for_service_formatted_for_csv(client, sample_temp def test_get_notification_for_service_without_uuid(client, notify_db_session): service_1 = create_service(service_name="1", email_from="1") response = client.get( - path="/service/{}/notifications/{}".format(service_1.id, "foo"), + path=f"/service/{service_1.id}/notifications/{'foo'}", headers=[create_admin_authorization_header()], ) assert response.status_code == 404 @@ -1832,7 +1829,7 @@ def test_get_notification_for_service(client, notify_db_session): for notification in service_1_notifications: response = client.get( - path="/service/{}/notifications/{}".format(service_1.id, notification.id), + path=f"/service/{service_1.id}/notifications/{notification.id}", headers=[create_admin_authorization_header()], ) resp = json.loads(response.get_data(as_text=True)) @@ -1840,7 +1837,7 @@ def test_get_notification_for_service(client, notify_db_session): assert response.status_code == 200 service_2_response = client.get( - path="/service/{}/notifications/{}".format(service_2.id, notification.id), + path=f"/service/{service_2.id}/notifications/{notification.id}", headers=[create_admin_authorization_header()], ) assert service_2_response.status_code == 404 @@ -1909,8 +1906,9 @@ def test_get_all_notifications_for_service_including_ones_made_by_jobs( auth_header = create_admin_authorization_header() response = client.get( - path="/service/{}/notifications?include_from_test_key={}".format( - sample_service.id, include_from_test_key + path=( + f"/service/{sample_service.id}/notifications?include_from_test_key=" + f"{include_from_test_key}" ), headers=[auth_header], ) @@ -2084,9 +2082,7 @@ def test_get_detailed_service( with freeze_time("2000-01-02T12:00:00"): create_notification(template=sample_template, status="created") resp = client.get( - "/service/{}?detailed=True&today_only={}".format( - sample_service.id, today_only - ), + f"/service/{sample_service.id}?detailed=True&today_only={today_only}", headers=[create_admin_authorization_header()], ) @@ -2197,10 +2193,10 @@ def test_get_detailed_services_groups_by_service(notify_db_session): service_1_template = create_template(service_1) service_2_template = create_template(service_2) - create_notification(service_1_template, status="created") - create_notification(service_2_template, status="created") - create_notification(service_1_template, status="delivered") - create_notification(service_1_template, status="created") + create_notification(service_1_template, status=NotificationStatus.CREATED) + create_notification(service_2_template, status=NotificationStatus.CREATED) + create_notification(service_1_template, status=NotificationStatus.DELIVERED) + create_notification(service_1_template, status=NotificationStatus.CREATED) data = get_detailed_services( start_date=datetime.utcnow().date(), end_date=datetime.utcnow().date() @@ -2210,13 +2206,13 @@ def test_get_detailed_services_groups_by_service(notify_db_session): assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS.value: {"delivered": 1, "failed": 0, "requested": 3}, + NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, + NotificationType.SMS.value: {NotificationStatus.DELIVERED: 1, NotificationStatus.FAILED: 0, "requested": 3,}, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 1}, + NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, + NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 1,}, } @@ -2239,13 +2235,13 @@ def test_get_detailed_services_includes_services_with_no_notifications( assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 1}, + NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, + NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 1,}, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, + NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, } @@ -2265,8 +2261,8 @@ def test_get_detailed_services_only_includes_todays_notifications(sample_templat assert len(data) == 1 assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 3}, + NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, + NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 3,}, } @@ -2311,13 +2307,13 @@ def test_get_detailed_services_for_date_range( assert len(data) == 1 assert data[0]["statistics"][NotificationType.EMAIL.value] == { - "delivered": 0, - "failed": 0, + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, "requested": 0, } assert data[0]["statistics"][NotificationType.SMS.value] == { - "delivered": 2, - "failed": 0, + NotificationStatus.DELIVERED: 2, + NotificationStatus.FAILED: 0, "requested": 2, } @@ -2338,9 +2334,8 @@ def test_search_for_notification_by_to_field( ) response = client.get( - "/service/{}/notifications?to={}&template_type={}".format( - notification1.service_id, "jack@gmail.com", "email" - ), + f"/service/{notification1.service_id}/notifications?to={'jack@gmail.com'}" + f"&template_type={TemplateType.EMAIL}", headers=[create_admin_authorization_header()], ) notifications = json.loads(response.get_data(as_text=True))["notifications"] @@ -2377,16 +2372,16 @@ def test_search_for_notification_by_to_field_return_multiple_matches( client, sample_template, sample_email_template ): notification1 = create_notification( - sample_template, to_field="+447700900855", normalised_to="447700900855" + sample_template, to_field="+447700900855", normalised_to="447700900855", ) notification2 = create_notification( - sample_template, to_field=" +44 77009 00855 ", normalised_to="447700900855" + sample_template, to_field=" +44 77009 00855 ", normalised_to="447700900855", ) notification3 = create_notification( - sample_template, to_field="+44770 0900 855", normalised_to="447700900855" + sample_template, to_field="+44770 0900 855", normalised_to="447700900855", ) notification4 = create_notification( - sample_email_template, to_field="jack@gmail.com", normalised_to="jack@gmail.com" + sample_email_template, to_field="jack@gmail.com", normalised_to="jack@gmail.com", ) response = client.get( @@ -2414,7 +2409,7 @@ def test_search_for_notification_by_to_field_returns_next_link_if_more_than_50( ): for _ in range(51): create_notification( - sample_template, to_field="+447700900855", normalised_to="447700900855" + sample_template, to_field="+447700900855", normalised_to="447700900855", ) response = client.get( @@ -2438,7 +2433,7 @@ def test_search_for_notification_by_to_field_returns_no_next_link_if_50_or_less( ): for _ in range(50): create_notification( - sample_template, to_field="+447700900855", normalised_to="447700900855" + sample_template, to_field="+447700900855", normalised_to="447700900855", ) response = client.get( @@ -2492,7 +2487,7 @@ def test_update_service_does_not_call_send_notification_for_live_service( auth_header = create_admin_authorization_header() resp = client.post( - "service/{}".format(sample_service.id), + f"service/{sample_service.id}", data=json.dumps(data), headers=[auth_header], content_type="application/json", @@ -3035,7 +3030,7 @@ def test_get_email_reply_to_address(client, notify_db_session): reply_to = create_reply_to_email(service, "test_a@mail.com") response = client.get( - "/service/{}/email-reply-to/{}".format(service.id, reply_to.id), + f"/service/{service.id}/email-reply-to/{reply_to.id}", headers=[ ("Content-Type", "application/json"), create_admin_authorization_header(), @@ -3053,7 +3048,7 @@ def test_add_service_sms_sender_can_add_multiple_senders(client, notify_db_sessi "is_default": False, } response = client.post( - "/service/{}/sms-sender".format(service.id), + f"/service/{service.id}/sms-sender", data=json.dumps(data), headers=[ ("Content-Type", "application/json"), diff --git a/tests/app/test_model.py b/tests/app/test_model.py index 67a83e661..b094bb824 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -42,7 +42,7 @@ from tests.app.db import ( @pytest.mark.parametrize("mobile_number", ["+447700900855", "+12348675309"]) def test_should_build_service_guest_list_from_mobile_number(mobile_number): service_guest_list = ServiceGuestList.from_string( - "service_id", RecipientType.MOBILE, mobile_number + "service_id", RecipientType.MOBILE, mobile_number, ) assert service_guest_list.recipient == mobile_number @@ -51,7 +51,7 @@ def test_should_build_service_guest_list_from_mobile_number(mobile_number): @pytest.mark.parametrize("email_address", ["test@example.com"]) def test_should_build_service_guest_list_from_email_address(email_address): service_guest_list = ServiceGuestList.from_string( - "service_id", RecipientType.EMAIL, email_address + "service_id", RecipientType.EMAIL, email_address, ) assert service_guest_list.recipient == email_address @@ -220,7 +220,7 @@ def test_notification_subject_is_none_for_sms(sample_service): assert notification.subject is None -@pytest.mark.parametrize("template_type", ["email"]) +@pytest.mark.parametrize("template_type", [TemplateType.EMAIL]) def test_notification_subject_fills_in_placeholders(sample_service, template_type): template = create_template( service=sample_service, template_type=template_type, subject="((name))" From 15eeac6367bb22d61c55cadf13dd80151606b707 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 10:24:45 -0500 Subject: [PATCH 202/259] Reformatting. Signed-off-by: Cliff Hill --- tests/app/celery/test_nightly_tasks.py | 16 ++- .../app/celery/test_service_callback_tasks.py | 29 +++-- tests/app/celery/test_tasks.py | 11 +- tests/app/conftest.py | 5 +- tests/app/dao/test_complaint_dao.py | 6 +- .../dao/test_service_data_retention_dao.py | 6 +- tests/app/db.py | 29 +++-- tests/app/notifications/test_validators.py | 45 +++---- tests/app/service/test_rest.py | 121 ++++++++++++++---- tests/app/test_model.py | 8 +- 10 files changed, 194 insertions(+), 82 deletions(-) diff --git a/tests/app/celery/test_nightly_tasks.py b/tests/app/celery/test_nightly_tasks.py index 0c496fb2c..e6d3be737 100644 --- a/tests/app/celery/test_nightly_tasks.py +++ b/tests/app/celery/test_nightly_tasks.py @@ -104,10 +104,16 @@ def test_will_remove_csv_files_for_jobs_older_than_retention_period( days_of_retention=30, ) sms_template_service_1 = create_template(service=service_1) - email_template_service_1 = create_template(service=service_1, template_type=TemplateType.EMAIL,) + email_template_service_1 = create_template( + service=service_1, + template_type=TemplateType.EMAIL, + ) sms_template_service_2 = create_template(service=service_2) - email_template_service_2 = create_template(service=service_2, template_type=TemplateType.EMAIL,) + email_template_service_2 = create_template( + service=service_2, + template_type=TemplateType.EMAIL, + ) four_days_ago = datetime.utcnow() - timedelta(days=4) eight_days_ago = datetime.utcnow() - timedelta(days=8) @@ -352,10 +358,12 @@ def test_delete_notifications_task_calls_task_for_services_that_have_sent_notifi create_template(service_will_delete_1) create_template(service_will_delete_2) nothing_to_delete_sms_template = create_template( - service_nothing_to_delete, template_type=TemplateType.SMS, + service_nothing_to_delete, + template_type=TemplateType.SMS, ) nothing_to_delete_email_template = create_template( - service_nothing_to_delete, template_type=TemplateType.EMAIL, + service_nothing_to_delete, + template_type=TemplateType.EMAIL, ) # will be deleted as service has no custom retention, but past our default 7 days diff --git a/tests/app/celery/test_service_callback_tasks.py b/tests/app/celery/test_service_callback_tasks.py index a39241528..f1e4db462 100644 --- a/tests/app/celery/test_service_callback_tasks.py +++ b/tests/app/celery/test_service_callback_tasks.py @@ -62,16 +62,20 @@ def test_send_delivery_status_to_service_post_https_request_to_service_with_encr assert request_mock.request_history[0].method == "POST" assert request_mock.request_history[0].text == json.dumps(mock_data) assert request_mock.request_history[0].headers["Content-type"] == "application/json" - assert request_mock.request_history[0].headers[ - "Authorization" - ] == f"Bearer {callback_api.bearer_token}" + assert ( + request_mock.request_history[0].headers["Authorization"] + == f"Bearer {callback_api.bearer_token}" + ) def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted_data( notify_db_session, ): with freeze_time("2001-01-01T12:00:00"): - callback_api, template = _set_up_test_data(NotificationType.EMAIL, CallbackType.COMPLAINT,) + callback_api, template = _set_up_test_data( + NotificationType.EMAIL, + CallbackType.COMPLAINT, + ) notification = create_notification(template=template) complaint = create_complaint( @@ -100,9 +104,10 @@ def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted request_mock.request_history[0].headers["Content-type"] == "application/json" ) - assert request_mock.request_history[0].headers[ - "Authorization" - ] == f"Bearer {callback_api.bearer_token}" + assert ( + request_mock.request_history[0].headers["Authorization"] + == f"Bearer {callback_api.bearer_token}" + ) @pytest.mark.parametrize( @@ -113,7 +118,10 @@ def test_send_complaint_to_service_posts_https_request_to_service_with_encrypted def test__send_data_to_service_callback_api_retries_if_request_returns_error_code_with_encrypted_data( notify_db_session, mocker, notification_type, status_code ): - callback_api, template = _set_up_test_data(notification_type, CallbackType.DELIVERY_STATUS,) + callback_api, template = _set_up_test_data( + notification_type, + CallbackType.DELIVERY_STATUS, + ) datestr = datetime(2017, 6, 20) notification = create_notification( template=template, @@ -143,7 +151,10 @@ def test__send_data_to_service_callback_api_retries_if_request_returns_error_cod def test__send_data_to_service_callback_api_does_not_retry_if_request_returns_404_with_encrypted_data( notify_db_session, mocker, notification_type ): - callback_api, template = _set_up_test_data(notification_type, CallbackType.DELIVERY_STATUS,) + callback_api, template = _set_up_test_data( + notification_type, + CallbackType.DELIVERY_STATUS, + ) datestr = datetime(2017, 6, 20) notification = create_notification( template=template, diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index a3ea1baf9..6dbaa588d 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -310,9 +310,7 @@ def test_process_row_sends_letter_task( template_type, expected_function, expected_queue, mocker ): mocker.patch("app.celery.tasks.create_uuid", return_value="noti_uuid") - task_mock = mocker.patch( - f"app.celery.tasks.{expected_function}.apply_async" - ) + task_mock = mocker.patch(f"app.celery.tasks.{expected_function}.apply_async") encrypt_mock = mocker.patch("app.celery.tasks.encryption.encrypt") template = Mock(id="template_id", template_type=template_type) job = Mock(id="job_id", template_version="temp_vers") @@ -1039,9 +1037,10 @@ def test_send_inbound_sms_to_service_post_https_request_to_service( assert request_mock.request_history[0].method == "POST" assert request_mock.request_history[0].text == json.dumps(data) assert request_mock.request_history[0].headers["Content-type"] == "application/json" - assert request_mock.request_history[0].headers[ - "Authorization" - ] == f"Bearer {inbound_api.bearer_token}" + assert ( + request_mock.request_history[0].headers["Authorization"] + == f"Bearer {inbound_api.bearer_token}" + ) def test_send_inbound_sms_to_service_does_not_send_request_when_inbound_sms_does_not_exist( diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 86386d731..d2c1427b6 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -863,7 +863,10 @@ def sample_inbound_numbers(sample_service): inbound_numbers.append(create_inbound_number(number="1", provider="sns")) inbound_numbers.append( create_inbound_number( - number="2", provider="sns", active=False, service_id=service.id, + number="2", + provider="sns", + active=False, + service_id=service.id, ) ) return inbound_numbers diff --git a/tests/app/dao/test_complaint_dao.py b/tests/app/dao/test_complaint_dao.py index 2f6f3d866..2c1125790 100644 --- a/tests/app/dao/test_complaint_dao.py +++ b/tests/app/dao/test_complaint_dao.py @@ -139,13 +139,15 @@ def test_fetch_count_of_complaints(sample_email_notification): ) count_of_complaints = fetch_count_of_complaints( - start_date=datetime(2018, 6, 7), end_date=datetime(2018, 6, 7), + start_date=datetime(2018, 6, 7), + end_date=datetime(2018, 6, 7), ) assert count_of_complaints == 5 def test_fetch_count_of_complaints_returns_zero(notify_db_session): count_of_complaints = fetch_count_of_complaints( - start_date=datetime(2018, 6, 7), end_date=datetime(2018, 6, 7), + start_date=datetime(2018, 6, 7), + end_date=datetime(2018, 6, 7), ) assert count_of_complaints == 0 diff --git a/tests/app/dao/test_service_data_retention_dao.py b/tests/app/dao/test_service_data_retention_dao.py index bb5765fe7..529281b61 100644 --- a/tests/app/dao/test_service_data_retention_dao.py +++ b/tests/app/dao/test_service_data_retention_dao.py @@ -57,7 +57,11 @@ def test_fetch_service_data_retention_returns_empty_list_when_no_rows_for_servic def test_fetch_service_data_retention_by_id(sample_service): - email_data_retention = insert_service_data_retention(sample_service.id, NotificationType.EMAIL, 3,) + email_data_retention = insert_service_data_retention( + sample_service.id, + NotificationType.EMAIL, + 3, + ) insert_service_data_retention(sample_service.id, NotificationType.SMS, 13) result = fetch_service_data_retention_by_id( sample_service.id, email_data_retention.id diff --git a/tests/app/db.py b/tests/app/db.py index ec8098cf5..ef5206e75 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -140,9 +140,7 @@ def create_service( else service_name.lower().replace(" ", "."), created_by=user if user - else create_user( - email=f"{uuid.uuid4()}@digital.cabinet-office.gov.uk" - ), + else create_user(email=f"{uuid.uuid4()}@digital.cabinet-office.gov.uk"), prefix_sms=prefix_sms, organization_type=organization_type, organization=organization, @@ -498,7 +496,11 @@ def create_service_callback_api( def create_email_branding( - id=None, colour="blue", logo="test_x2.png", name="test_org_1", text="DisplayName", + id=None, + colour="blue", + logo="test_x2.png", + name="test_org_1", + text="DisplayName", ): data = { "colour": colour, @@ -644,7 +646,9 @@ def create_organization( def create_invited_org_user( - organization, invited_by, email_address="invite@example.com", + organization, + invited_by, + email_address="invite@example.com", ): invited_org_user = InvitedOrganizationUser( email_address=email_address, @@ -918,13 +922,22 @@ def set_up_usage_data(start_date): ) create_ft_billing( - local_date=one_week_earlier, template=sms_template_1, billable_unit=2, rate=0.11, + local_date=one_week_earlier, + template=sms_template_1, + billable_unit=2, + rate=0.11, ) create_ft_billing( - local_date=start_date, template=sms_template_1, billable_unit=2, rate=0.11, + local_date=start_date, + template=sms_template_1, + billable_unit=2, + rate=0.11, ) create_ft_billing( - local_date=two_days_later, template=sms_template_1, billable_unit=1, rate=0.11, + local_date=two_days_later, + template=sms_template_1, + billable_unit=1, + rate=0.11, ) # service with emails only: diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index 10ab1e031..56c5e8e33 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -527,7 +527,9 @@ def test_check_service_over_api_rate_limit_when_rate_limit_has_not_exceeded_limi check_service_over_api_rate_limit(serialised_service, serialised_api_key) assert app.redis_store.exceeded_rate_limit.called_with( - f"{sample_service.id}-{api_key.key_type}", 3000, 60, + f"{sample_service.id}-{api_key.key_type}", + 3000, + 60, ) @@ -597,7 +599,10 @@ def test_validate_and_format_recipient_succeeds_with_international_numbers_if_se def test_validate_and_format_recipient_fails_when_no_recipient(): with pytest.raises(BadRequestError) as e: validate_and_format_recipient( - None, KeyType.NORMAL, "service", NotificationType.SMS, + None, + KeyType.NORMAL, + "service", + NotificationType.SMS, ) assert e.value.status_code == 400 assert e.value.message == "Recipient can't be empty" @@ -630,12 +635,9 @@ def test_check_service_email_reply_to_id_where_service_id_is_not_found( fake_uuid, reply_to_address.id, NotificationType.EMAIL ) assert e.value.status_code == 400 - assert ( - e.value.message - == ( - f"email_reply_to_id {reply_to_address.id} does not exist in database for i" - f"service id {fake_uuid}" - ) + assert e.value.message == ( + f"email_reply_to_id {reply_to_address.id} does not exist in database for i" + f"service id {fake_uuid}" ) @@ -647,12 +649,9 @@ def test_check_service_email_reply_to_id_where_reply_to_id_is_not_found( sample_service.id, fake_uuid, NotificationType.EMAIL ) assert e.value.status_code == 400 - assert ( - e.value.message - == ( - f"email_reply_to_id {fake_uuid} does not exist in database for service " - f"id {sample_service.id}" - ) + assert e.value.message == ( + f"email_reply_to_id {fake_uuid} does not exist in database for service " + f"id {sample_service.id}" ) @@ -683,12 +682,9 @@ def test_check_service_sms_sender_id_where_service_id_is_not_found( with pytest.raises(BadRequestError) as e: check_service_sms_sender_id(fake_uuid, sms_sender.id, NotificationType.SMS) assert e.value.status_code == 400 - assert ( - e.value.message - == ( - f"sms_sender_id {sms_sender.id} does not exist in database for service " - f"id {fake_uuid}" - ) + assert e.value.message == ( + f"sms_sender_id {sms_sender.id} does not exist in database for service " + f"id {fake_uuid}" ) @@ -698,12 +694,9 @@ def test_check_service_sms_sender_id_where_sms_sender_is_not_found( with pytest.raises(BadRequestError) as e: check_service_sms_sender_id(sample_service.id, fake_uuid, NotificationType.SMS) assert e.value.status_code == 400 - assert ( - e.value.message - == ( - f"sms_sender_id {fake_uuid} does not exist in database for service " - f"id {sample_service.id}" - ) + assert e.value.message == ( + f"sms_sender_id {fake_uuid} does not exist in database for service " + f"id {sample_service.id}" ) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 9c5dd174c..94cf5d8d6 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -151,9 +151,10 @@ def test_find_services_by_name_handles_no_results( mock_get_services_by_partial_name = mocker.patch( "app.service.rest.get_services_by_partial_name", return_value=[] ) - response = admin_request.get("service.find_services_by_name", service_name="ABC",)[ - "data" - ] + response = admin_request.get( + "service.find_services_by_name", + service_name="ABC", + )["data"] mock_get_services_by_partial_name.assert_called_once_with("ABC") assert len(response) == 0 @@ -1221,7 +1222,8 @@ def test_default_permissions_are_added_for_user_service( assert resp.status_code == 200 header = create_admin_authorization_header() response = client.get( - url_for("user.get_user", user_id=sample_user.id), headers=[header], + url_for("user.get_user", user_id=sample_user.id), + headers=[header], ) assert response.status_code == 200 json_resp = json.loads(response.get_data(as_text=True)) @@ -1517,7 +1519,14 @@ def test_add_existing_user_to_non_existing_service_returns404( incorrect_id = uuid.uuid4() data = { - "permissions": [PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, PermissionType.MANAGE_USERS, PermissionType.MANAGE_SETTINGS, PermissionType.MANAGE_TEMPLATES, PermissionType.MANAGE_API_KEYS,] + "permissions": [ + PermissionType.SEND_EMAILS, + PermissionType.SEND_TEXTS, + PermissionType.MANAGE_USERS, + PermissionType.MANAGE_SETTINGS, + PermissionType.MANAGE_TEMPLATES, + PermissionType.MANAGE_API_KEYS, + ] } auth_header = create_admin_authorization_header() @@ -1543,7 +1552,14 @@ def test_add_existing_user_of_service_to_service_returns400( existing_user_id = sample_service.users[0].id data = { - "permissions": [PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, PermissionType.MANAGE_USERS, PermissionType.MANAGE_SETTINGS, PermissionType.MANAGE_TEMPLATES, PermissionType.MANAGE_API_KEYS,] + "permissions": [ + PermissionType.SEND_EMAILS, + PermissionType.SEND_TEXTS, + PermissionType.MANAGE_USERS, + PermissionType.MANAGE_SETTINGS, + PermissionType.MANAGE_TEMPLATES, + PermissionType.MANAGE_API_KEYS, + ] } auth_header = create_admin_authorization_header() @@ -1572,7 +1588,14 @@ def test_add_unknown_user_to_service_returns404( incorrect_id = 9876 data = { - "permissions": [PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, PermissionType.MANAGE_USERS, PermissionType.MANAGE_SETTINGS, PermissionType.MANAGE_TEMPLATES, PermissionType.MANAGE_API_KEYS,] + "permissions": [ + PermissionType.SEND_EMAILS, + PermissionType.SEND_TEXTS, + PermissionType.MANAGE_USERS, + PermissionType.MANAGE_SETTINGS, + PermissionType.MANAGE_TEMPLATES, + PermissionType.MANAGE_API_KEYS, + ] } auth_header = create_admin_authorization_header() @@ -2206,13 +2229,29 @@ def test_get_detailed_services_groups_by_service(notify_db_session): assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, - NotificationType.SMS.value: {NotificationStatus.DELIVERED: 1, NotificationStatus.FAILED: 0, "requested": 3,}, + NotificationType.EMAIL.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 0, + }, + NotificationType.SMS.value: { + NotificationStatus.DELIVERED: 1, + NotificationStatus.FAILED: 0, + "requested": 3, + }, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, - NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 1,}, + NotificationType.EMAIL.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 0, + }, + NotificationType.SMS.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 1, + }, } @@ -2235,13 +2274,29 @@ def test_get_detailed_services_includes_services_with_no_notifications( assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, - NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 1,}, + NotificationType.EMAIL.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 0, + }, + NotificationType.SMS.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 1, + }, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, - NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, + NotificationType.EMAIL.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 0, + }, + NotificationType.SMS.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 0, + }, } @@ -2261,8 +2316,16 @@ def test_get_detailed_services_only_includes_todays_notifications(sample_templat assert len(data) == 1 assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 0,}, - NotificationType.SMS.value: {NotificationStatus.DELIVERED: 0, NotificationStatus.FAILED: 0, "requested": 3,}, + NotificationType.EMAIL.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 0, + }, + NotificationType.SMS.value: { + NotificationStatus.DELIVERED: 0, + NotificationStatus.FAILED: 0, + "requested": 3, + }, } @@ -2372,16 +2435,24 @@ def test_search_for_notification_by_to_field_return_multiple_matches( client, sample_template, sample_email_template ): notification1 = create_notification( - sample_template, to_field="+447700900855", normalised_to="447700900855", + sample_template, + to_field="+447700900855", + normalised_to="447700900855", ) notification2 = create_notification( - sample_template, to_field=" +44 77009 00855 ", normalised_to="447700900855", + sample_template, + to_field=" +44 77009 00855 ", + normalised_to="447700900855", ) notification3 = create_notification( - sample_template, to_field="+44770 0900 855", normalised_to="447700900855", + sample_template, + to_field="+44770 0900 855", + normalised_to="447700900855", ) notification4 = create_notification( - sample_email_template, to_field="jack@gmail.com", normalised_to="jack@gmail.com", + sample_email_template, + to_field="jack@gmail.com", + normalised_to="jack@gmail.com", ) response = client.get( @@ -2409,7 +2480,9 @@ def test_search_for_notification_by_to_field_returns_next_link_if_more_than_50( ): for _ in range(51): create_notification( - sample_template, to_field="+447700900855", normalised_to="447700900855", + sample_template, + to_field="+447700900855", + normalised_to="447700900855", ) response = client.get( @@ -2433,7 +2506,9 @@ def test_search_for_notification_by_to_field_returns_no_next_link_if_50_or_less( ): for _ in range(50): create_notification( - sample_template, to_field="+447700900855", normalised_to="447700900855", + sample_template, + to_field="+447700900855", + normalised_to="447700900855", ) response = client.get( diff --git a/tests/app/test_model.py b/tests/app/test_model.py index b094bb824..95039b6a3 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -42,7 +42,9 @@ from tests.app.db import ( @pytest.mark.parametrize("mobile_number", ["+447700900855", "+12348675309"]) def test_should_build_service_guest_list_from_mobile_number(mobile_number): service_guest_list = ServiceGuestList.from_string( - "service_id", RecipientType.MOBILE, mobile_number, + "service_id", + RecipientType.MOBILE, + mobile_number, ) assert service_guest_list.recipient == mobile_number @@ -51,7 +53,9 @@ def test_should_build_service_guest_list_from_mobile_number(mobile_number): @pytest.mark.parametrize("email_address", ["test@example.com"]) def test_should_build_service_guest_list_from_email_address(email_address): service_guest_list = ServiceGuestList.from_string( - "service_id", RecipientType.EMAIL, email_address, + "service_id", + RecipientType.EMAIL, + email_address, ) assert service_guest_list.recipient == email_address From 43a8b6539fcc7e9c9b4c2f8a8ff5c5c9655fc009 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 11:27:38 -0500 Subject: [PATCH 203/259] More fixes, removing literal "created" from code. Signed-off-by: Cliff Hill --- app/delivery/send_to_providers.py | 22 +++++----- tests/app/celery/test_provider_tasks.py | 9 +++-- tests/app/conftest.py | 6 +-- .../dao/test_fact_notification_status_dao.py | 4 +- tests/app/dao/test_jobs_dao.py | 34 ++++++++++------ tests/app/dao/test_services_dao.py | 40 +++++++++---------- tests/app/db.py | 8 ++-- tests/app/job/test_rest.py | 8 +++- tests/app/notifications/test_rest.py | 4 +- .../test_service_data_retention_rest.py | 4 +- tests/app/user/test_rest.py | 20 +++++++--- .../notifications/test_post_notifications.py | 4 +- .../app/v2/template/test_template_schemas.py | 4 +- 13 files changed, 95 insertions(+), 72 deletions(-) diff --git a/app/delivery/send_to_providers.py b/app/delivery/send_to_providers.py index 95d122032..1700699db 100644 --- a/app/delivery/send_to_providers.py +++ b/app/delivery/send_to_providers.py @@ -27,7 +27,7 @@ def send_sms_to_provider(notification): technical_failure(notification=notification) return - if notification.status == "created": + if notification.status == NotificationStatus.CREATED: provider = provider_to_use(NotificationType.SMS, notification.international) if not provider: technical_failure(notification=notification) @@ -109,7 +109,7 @@ def send_email_to_provider(notification): if not service.active: technical_failure(notification=notification) return - if notification.status == "created": + if notification.status == NotificationStatus.CREATED: provider = provider_to_use(NotificationType.EMAIL, False) template_dict = SerialisedTemplate.from_id_and_service_id( template_id=notification.template_id, @@ -134,10 +134,9 @@ def send_email_to_provider(notification): update_notification_to_sending(notification, provider) send_email_response(notification.reference, recipient) else: - from_address = '"{}" <{}@{}>'.format( - service.name, - service.email_from, - current_app.config["NOTIFY_EMAIL_DOMAIN"], + from_address = ( + f'"{service.name}" <{service.email_from}@' + f'{current_app.config["NOTIFY_EMAIL_DOMAIN"]}>' ) reference = provider.send_email( @@ -175,10 +174,8 @@ def provider_to_use(notification_type, international=True): ] if not active_providers: - current_app.logger.error( - "{} failed as no active providers".format(notification_type) - ) - raise Exception("No active {} providers".format(notification_type)) + current_app.logger.error(f"{notification_type} failed as no active providers") + raise Exception(f"No active {notification_type} providers") # we only have sns chosen_provider = active_providers[0] @@ -240,7 +237,6 @@ def technical_failure(notification): notification.status = NotificationStatus.TECHNICAL_FAILURE dao_update_notification(notification) raise NotificationTechnicalFailureException( - "Send {} for notification id {} to provider is not allowed: service {} is inactive".format( - notification.notification_type, notification.id, notification.service_id - ) + f"Send {notification.notification_type} for notification id {notification.id} " + f"to provider is not allowed: service {notification.service_id} is inactive" ) diff --git a/tests/app/celery/test_provider_tasks.py b/tests/app/celery/test_provider_tasks.py index 532c19e3f..e9f27be3d 100644 --- a/tests/app/celery/test_provider_tasks.py +++ b/tests/app/celery/test_provider_tasks.py @@ -11,6 +11,7 @@ from app.clients.email.aws_ses import ( AwsSesClientThrottlingSendRateException, ) from app.clients.sms import SmsClientResponseException +from app.enums import NotificationStatus from app.exceptions import NotificationTechnicalFailureException @@ -55,7 +56,7 @@ def test_should_retry_and_log_warning_if_SmsClientResponseException_for_deliver_ ) mocker.patch("app.celery.provider_tasks.deliver_sms.retry") mock_logger_warning = mocker.patch("app.celery.tasks.current_app.logger.warning") - assert sample_notification.status == "created" + assert sample_notification.status == NotificationStatus.CREATED deliver_sms(sample_notification.id) @@ -75,7 +76,7 @@ def test_should_retry_and_log_exception_for_non_SmsClientResponseException_excep "app.celery.tasks.current_app.logger.exception" ) - assert sample_notification.status == "created" + assert sample_notification.status == NotificationStatus.CREATED deliver_sms(sample_notification.id) assert provider_tasks.deliver_sms.retry.called is True @@ -204,7 +205,7 @@ def test_should_retry_and_log_exception_for_deliver_email_task( deliver_email(sample_notification.id) assert provider_tasks.deliver_email.retry.called is True - assert sample_notification.status == "created" + assert sample_notification.status == NotificationStatus.CREATED assert mock_logger_exception.called @@ -232,6 +233,6 @@ def test_if_ses_send_rate_throttle_then_should_retry_and_log_warning( deliver_email(sample_notification.id) assert provider_tasks.deliver_email.retry.called is True - assert sample_notification.status == "created" + assert sample_notification.status == NotificationStatus.CREATED assert not mock_logger_exception.called assert mock_logger_warning.called diff --git a/tests/app/conftest.py b/tests/app/conftest.py index d2c1427b6..2846acfd3 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -71,7 +71,7 @@ def create_sample_notification( job=None, job_row_number=None, to_field=None, - status="created", + status=NotificationStatus.CREATED, provider_response=None, reference=None, created_at=None, @@ -456,7 +456,7 @@ def sample_notification(notify_db_session): "service": service, "template_id": template.id, "template_version": template.version, - "status": "created", + "status": NotificationStatus.CREATED, "reference": None, "created_at": created_at, "sent_at": None, @@ -499,7 +499,7 @@ def sample_email_notification(notify_db_session): "service": service, "template_id": template.id, "template_version": template.version, - "status": "created", + "status": NotificationStatus.CREATED, "reference": None, "created_at": created_at, "billable_units": 0, diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index 99181e811..24812f5b2 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -570,8 +570,8 @@ def test_fetch_notification_statuses_for_job(sample_template): ) assert {x.status: x.count for x in fetch_notification_statuses_for_job(j1.id)} == { - "created": 5, - "delivered": 2, + NotificationStatus.CREATED: 5, + NotificationStatus.DELIVERED: 2, } diff --git a/tests/app/dao/test_jobs_dao.py b/tests/app/dao/test_jobs_dao.py index f9eac45f6..c2de39181 100644 --- a/tests/app/dao/test_jobs_dao.py +++ b/tests/app/dao/test_jobs_dao.py @@ -18,7 +18,7 @@ from app.dao.jobs_dao import ( find_jobs_with_missing_rows, find_missing_row_for_job, ) -from app.enums import JobStatus +from app.enums import JobStatus, NotificationStatus from app.models import Job, NotificationType, TemplateType from tests.app.db import ( create_job, @@ -31,19 +31,29 @@ from tests.app.db import ( def test_should_count_of_statuses_for_notifications_associated_with_job( sample_template, sample_job ): - create_notification(sample_template, job=sample_job, status="created") - create_notification(sample_template, job=sample_job, status="created") - create_notification(sample_template, job=sample_job, status="created") - create_notification(sample_template, job=sample_job, status="sending") - create_notification(sample_template, job=sample_job, status="delivered") + create_notification( + sample_template, job=sample_job, status=NotificationStatus.CREATED + ) + create_notification( + sample_template, job=sample_job, status=NotificationStatus.CREATED + ) + create_notification( + sample_template, job=sample_job, status=NotificationStatus.CREATED + ) + create_notification( + sample_template, job=sample_job, status=NotificationStatus.SENDING + ) + create_notification( + sample_template, job=sample_job, status=NotificationStatus.DELIVERED + ) results = dao_get_notification_outcomes_for_job( sample_template.service_id, sample_job.id ) assert {row.status: row.count for row in results} == { - "created": 3, - "sending": 1, - "delivered": 1, + NotificationStatus.CREATED: 3, + NotificationStatus.SENDING: 1, + NotificationStatus.DELIVERED: 1, } @@ -60,13 +70,13 @@ def test_should_return_notifications_only_for_this_job(sample_template): job_1 = create_job(sample_template) job_2 = create_job(sample_template) - create_notification(sample_template, job=job_1, status="created") - create_notification(sample_template, job=job_2, status="sent") + create_notification(sample_template, job=job_1, status=NotificationStatus.CREATED) + create_notification(sample_template, job=job_2, status=NotificationStatus.SENT) results = dao_get_notification_outcomes_for_job( sample_template.service_id, job_1.id ) - assert {row.status: row.count for row in results} == {"created": 1} + assert {row.status: row.count for row in results} == {NotificationStatus.CREATED: 1} def test_should_return_notifications_only_for_this_service( diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 3aa50738e..da9aa0e10 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -1044,9 +1044,9 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today(notify_db_sessio stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} - assert stats["delivered"] == 1 - assert stats["failed"] == 1 - assert stats["created"] == 1 + assert stats[NotificationStatus.DELIVERED] == 1 + assert stats[NotificationStatus.FAILED] == 1 + assert stats[NotificationStatus.CREATED] == 1 @pytest.mark.skip(reason="Need a better way to test variable DST date") @@ -1079,11 +1079,11 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today_when_clocks_spri stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} - assert "delivered" not in stats - assert stats["failed"] == 1 - assert stats["created"] == 1 - assert not stats.get("permanent-failure") - assert not stats.get("temporary-failure") + assert NotificationStatus.DELIVERED not in stats + assert stats[NotificationStatus.FAILED] == 1 + assert stats[NotificationStatus.CREATED] == 1 + assert not stats.get(NotificationStatus.PERMANENT_FAILURE) + assert not stats.get(NotificationStatus.TEMPORARY_FAILURE) def test_dao_fetch_todays_stats_for_service_only_includes_today_during_bst( @@ -1109,10 +1109,10 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today_during_bst( stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} - assert "delivered" not in stats - assert stats["failed"] == 1 - assert stats["created"] == 1 - assert not stats.get("permanent-failure") + assert NotificationStatus.DELIVERED not in stats + assert stats[NotificationStatus.FAILED] == 1 + assert stats[NotificationStatus.CREATED] == 1 + assert not stats.get(NotificationStatus.PERMANENT_FAILURE) def test_dao_fetch_todays_stats_for_service_only_includes_today_when_clocks_fall_back( @@ -1139,10 +1139,10 @@ def test_dao_fetch_todays_stats_for_service_only_includes_today_when_clocks_fall stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} - assert "delivered" not in stats - assert stats["failed"] == 1 - assert stats["created"] == 1 - assert not stats.get("permanent-failure") + assert NotificationStatus.DELIVERED not in stats + assert stats[NotificationStatus.FAILED] == 1 + assert stats[NotificationStatus.CREATED] == 1 + assert not stats.get(NotificationStatus.PERMANENT_FAILURE) def test_dao_fetch_todays_stats_for_service_only_includes_during_utc(notify_db_session): @@ -1167,10 +1167,10 @@ def test_dao_fetch_todays_stats_for_service_only_includes_during_utc(notify_db_s stats = dao_fetch_todays_stats_for_service(template.service_id) stats = {row.status: row.count for row in stats} - assert "delivered" not in stats - assert stats["failed"] == 1 - assert stats["created"] == 1 - assert not stats.get("permanent-failure") + assert NotificationStatus.DELIVERED not in stats + assert stats[NotificationStatus.FAILED] == 1 + assert stats[NotificationStatus.CREATED] == 1 + assert not stats.get(NotificationStatus.PERMANENT_FAILURE) def test_dao_fetch_todays_stats_for_all_services_includes_all_services( diff --git a/tests/app/db.py b/tests/app/db.py index ef5206e75..632c3a8a6 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -272,10 +272,10 @@ def create_notification( ) if status not in ( - "created", - "validation-failed", - "virus-scan-failed", - "pending-virus-check", + NotificationStatus.CREATED, + NotificationStatus.VALIDATION_FAILED, + NotificationStatus.VIRUS_SCAN_FAILED, + NotificationStatus.PENDING_VIRUS_CHECK, ): sent_at = sent_at or datetime.utcnow() updated_at = updated_at or datetime.utcnow() diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index 1f289bfe8..b49717e05 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -991,9 +991,13 @@ def test_get_jobs_should_retrieve_from_ft_notification_status_for_old_jobs( assert resp_json["data"][0]["id"] == str(job_3.id) assert resp_json["data"][0]["statistics"] == [] assert resp_json["data"][1]["id"] == str(job_2.id) - assert resp_json["data"][1]["statistics"] == [{"status": "created", "count": 1}] + assert resp_json["data"][1]["statistics"] == [ + {"status": NotificationStatus.CREATED, "count": 1}, + ] assert resp_json["data"][2]["id"] == str(job_1.id) - assert resp_json["data"][2]["statistics"] == [{"status": "delivered", "count": 6}] + assert resp_json["data"][2]["statistics"] == [ + {"status": NotificationStatus.DELIVERED, "count": 6}, + ] @freeze_time("2017-07-17 07:17") diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index 0e09cd5fb..6e8142e7c 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -32,7 +32,7 @@ def test_get_notification_by_id( assert response.status_code == 200 notification = json.loads(response.get_data(as_text=True))["data"]["notification"] - assert notification["status"] == "created" + assert notification["status"] == NotificationStatus.CREATED assert notification["template"] == { "id": str(notification_to_get.template.id), "name": notification_to_get.template.name, @@ -152,7 +152,7 @@ def test_get_all_notifications(client, sample_notification): notifications = json.loads(response.get_data(as_text=True)) assert response.status_code == 200 - assert notifications["notifications"][0]["status"] == "created" + assert notifications["notifications"][0]["status"] == NotificationStatus.CREATED assert notifications["notifications"][0]["template"] == { "id": str(sample_notification.template.id), "name": sample_notification.template.name, diff --git a/tests/app/service/test_service_data_retention_rest.py b/tests/app/service/test_service_data_retention_rest.py index 035b11b42..a6b96fc91 100644 --- a/tests/app/service/test_service_data_retention_rest.py +++ b/tests/app/service/test_service_data_retention_rest.py @@ -10,7 +10,9 @@ from tests.app.db import create_service_data_retention def test_get_service_data_retention(client, sample_service): sms_data_retention = create_service_data_retention(service=sample_service) email_data_retention = create_service_data_retention( - service=sample_service, notification_type="email", days_of_retention=10 + service=sample_service, + notification_type=NotificationType.EMAIL, + days_of_retention=10, ) response = client.get( diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index 801a18f38..fdbd9171d 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -312,7 +312,9 @@ def test_post_user_attribute_with_updated_by( mock_persist_notification = mocker.patch("app.user.rest.persist_notification") mocker.patch("app.user.rest.send_notification_to_queue") json_resp = admin_request.post( - "user.update_user_attribute", user_id=sample_user.id, _data=update_dict + "user.update_user_attribute", + user_id=sample_user.id, + _data=update_dict, ) assert json_resp["data"][user_attribute] == user_value if arguments: @@ -329,7 +331,9 @@ def test_post_user_attribute_with_updated_by_sends_notification_to_international mocker.patch("app.user.rest.send_notification_to_queue") admin_request.post( - "user.update_user_attribute", user_id=sample_user.id, _data=update_dict + "user.update_user_attribute", + user_id=sample_user.id, + _data=update_dict, ) notification = Notification.query.first() @@ -343,7 +347,9 @@ def test_archive_user(mocker, admin_request, sample_user): archive_mock = mocker.patch("app.user.rest.dao_archive_user") admin_request.post( - "user.archive_user", user_id=sample_user.id, _expected_status=204 + "user.archive_user", + user_id=sample_user.id, + _expected_status=204, ) archive_mock.assert_called_once_with(sample_user) @@ -363,7 +369,9 @@ def test_archive_user_when_user_cannot_be_archived(mocker, admin_request, sample mocker.patch("app.dao.users_dao.user_can_be_archived", return_value=False) json_resp = admin_request.post( - "user.archive_user", user_id=sample_user.id, _expected_status=400 + "user.archive_user", + user_id=sample_user.id, + _expected_status=400, ) msg = "User can’t be removed from a service - check all services have another team member with manage_settings" @@ -390,7 +398,9 @@ def test_get_user_by_email(admin_request, sample_service): def test_get_user_by_email_not_found_returns_404(admin_request, sample_user): json_resp = admin_request.get( - "user.get_by_email", email="no_user@digital.fake.gov", _expected_status=404 + "user.get_by_email", + email="no_user@digital.fake.gov", + _expected_status=404, ) assert json_resp["result"] == "error" assert json_resp["message"] == "No result found" diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index 4a90c5372..b7d98fab6 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -535,7 +535,7 @@ def test_should_not_persist_or_send_notification_if_simulated_recipient( client, recipient, notification_type, sample_email_template, sample_template, mocker ): apply_async = mocker.patch( - "app.celery.provider_tasks.deliver_{}.apply_async".format(notification_type) + f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" ) if notification_type == NotificationType.SMS: @@ -1194,7 +1194,7 @@ def test_post_notification_returns_201_when_content_type_is_missing_but_payload_ valid_json = { "template_id": str(template.id), } - if notification_type == "email": + if notification_type == NotificationType.EMAIL: valid_json.update({"email_address": sample_service.users[0].email_address}) else: valid_json.update({"phone_number": "+447700900855"}) diff --git a/tests/app/v2/template/test_template_schemas.py b/tests/app/v2/template/test_template_schemas.py index eff8348f2..198ad9bb6 100644 --- a/tests/app/v2/template/test_template_schemas.py +++ b/tests/app/v2/template/test_template_schemas.py @@ -80,14 +80,14 @@ invalid_json_post_args = [ valid_json_post_response = { "id": str(uuid.uuid4()), - "type": "email", + "type": TemplateType.EMAIL, "version": 1, "body": "some body", } valid_json_post_response_with_optionals = { "id": str(uuid.uuid4()), - "type": "email", + "type": TemplateType.EMAIL, "version": 1, "body": "some body", "subject": "some subject", From afc1de61f61c8579fdff531c644ed56b4428d64d Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 12:35:18 -0500 Subject: [PATCH 204/259] Even more cleanup. Signed-off-by: Cliff Hill --- app/email_branding/email_branding_schema.py | 4 +- app/service/service_data_retention_schema.py | 4 +- app/service/statistics.py | 18 ++-- app/template/template_schemas.py | 10 +- app/v2/notifications/notification_schemas.py | 6 +- app/v2/template/template_schemas.py | 4 +- app/v2/templates/templates_schemas.py | 2 +- tests/app/notifications/test_rest.py | 12 ++- tests/app/service/test_rest.py | 94 +++++++++---------- tests/app/test_commands.py | 6 +- .../notifications/test_get_notifications.py | 17 ++-- .../test_notification_schemas.py | 2 +- 12 files changed, 94 insertions(+), 85 deletions(-) diff --git a/app/email_branding/email_branding_schema.py b/app/email_branding/email_branding_schema.py index 805835724..fa3b7f60e 100644 --- a/app/email_branding/email_branding_schema.py +++ b/app/email_branding/email_branding_schema.py @@ -9,7 +9,7 @@ post_create_email_branding_schema = { "name": {"type": "string"}, "text": {"type": ["string", "null"]}, "logo": {"type": ["string", "null"]}, - "brand_type": {"enum": [e.value for e in BrandType]}, + "brand_type": {"enum": list(BrandType)}, }, "required": ["name"], } @@ -23,7 +23,7 @@ post_update_email_branding_schema = { "name": {"type": ["string", "null"]}, "text": {"type": ["string", "null"]}, "logo": {"type": ["string", "null"]}, - "brand_type": {"enum": [e.value for e in BrandType]}, + "brand_type": {"enum": list(BrandType)}, }, "required": [], } diff --git a/app/service/service_data_retention_schema.py b/app/service/service_data_retention_schema.py index da732c284..2aeb7f92a 100644 --- a/app/service/service_data_retention_schema.py +++ b/app/service/service_data_retention_schema.py @@ -7,9 +7,7 @@ add_service_data_retention_request = { "type": "object", "properties": { "days_of_retention": {"type": "integer"}, - "notification_type": { - "enum": [NotificationType.SMS.value, NotificationType.EMAIL.value] - }, + "notification_type": {"enum": [NotificationType.SMS, NotificationType.EMAIL]}, }, "required": ["days_of_retention", "notification_type"], } diff --git a/app/service/statistics.py b/app/service/statistics.py index bf230f164..78359fd6c 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -68,7 +68,7 @@ def format_monthly_template_notification_stats(year, rows): stats[formatted_month][str(row.template_id)] = { "name": row.name, "type": row.template_type, - "counts": dict.fromkeys([e.value for e in NotificationStatus], 0), + "counts": dict.fromkeys(list(NotificationStatus), 0), } stats[formatted_month][str(row.template_id)]["counts"][row.status] += row.count @@ -83,17 +83,17 @@ def create_zeroed_stats_dicts(): def _update_statuses_from_row(update_dict, row): - if row.status != "cancelled": + if row.status != NotificationStatus.CANCELLED: update_dict["requested"] += row.count - if row.status in ("delivered", "sent"): + if row.status in (NotificationStatus.DELIVERED, NotificationStatus.SENT): update_dict["delivered"] += row.count elif row.status in ( - "failed", - "technical-failure", - "temporary-failure", - "permanent-failure", - "validation-failed", - "virus-scan-failed", + NotificationStatus.FAILED, + NotificationStatus.TECHNICAL_FAILURE, + NotificationStatus.TEMPORARY_FAILURE, + NotificationStatus.PERMANENT_FAILURE, + NotificationStatus.VALIDATION_FAILED, + NotificationStatus.VIRUS_SCAN_FAILED, ): update_dict["failed"] += row.count diff --git a/app/template/template_schemas.py b/app/template/template_schemas.py index d03a83b93..899572d37 100644 --- a/app/template/template_schemas.py +++ b/app/template/template_schemas.py @@ -8,15 +8,15 @@ post_create_template_schema = { "title": "payload for POST /service//template", "properties": { "name": {"type": "string"}, - "template_type": {"enum": [e.value for e in TemplateType]}, + "template_type": {"enum": list(TemplateType)}, "service": uuid, - "process_type": {"enum": [e.value for e in TemplateProcessType]}, + "process_type": {"enum": list(TemplateProcessType)}, "content": {"type": "string"}, "subject": {"type": "string"}, "created_by": uuid, "parent_folder_id": uuid, }, - "if": {"properties": {"template_type": {"enum": [TemplateType.EMAIL.value]}}}, + "if": {"properties": {"template_type": {"enum": [TemplateType.EMAIL]}}}, "then": {"required": ["subject"]}, "required": ["name", "template_type", "content", "service", "created_by"], } @@ -29,9 +29,9 @@ post_update_template_schema = { "properties": { "id": uuid, "name": {"type": "string"}, - "template_type": {"enum": [e.value for e in TemplateType]}, + "template_type": {"enum": list(TemplateType)}, "service": uuid, - "process_type": {"enum": [e.value for e in TemplateProcessType]}, + "process_type": {"enum": list(TemplateProcessType)}, "content": {"type": "string"}, "subject": {"type": "string"}, "reply_to": nullable_uuid, diff --git a/app/v2/notifications/notification_schemas.py b/app/v2/notifications/notification_schemas.py index 0d737407e..c66ecf6c2 100644 --- a/app/v2/notifications/notification_schemas.py +++ b/app/v2/notifications/notification_schemas.py @@ -41,7 +41,7 @@ get_notification_response = { "line_5": {"type": ["string", "null"]}, "line_6": {"type": ["string", "null"]}, "postcode": {"type": ["string", "null"]}, - "type": {"enum": [e.value for e in TemplateType]}, + "type": {"enum": list(TemplateType)}, "status": {"type": "string"}, "template": template, "body": {"type": "string"}, @@ -82,11 +82,11 @@ get_notifications_request = { "reference": {"type": "string"}, "status": { "type": "array", - "items": {"enum": [e.value for e in NotificationStatus]}, + "items": {"enum": list(NotificationStatus)}, }, "template_type": { "type": "array", - "items": {"enum": [e.value for e in TemplateType]}, + "items": {"enum": list(TemplateType)}, }, "include_jobs": {"enum": ["true", "True"]}, "older_than": uuid, diff --git a/app/v2/template/template_schemas.py b/app/v2/template/template_schemas.py index 8d0cf300d..7461f3fe4 100644 --- a/app/v2/template/template_schemas.py +++ b/app/v2/template/template_schemas.py @@ -17,7 +17,7 @@ get_template_by_id_response = { "title": "reponse v2/template", "properties": { "id": uuid, - "type": {"enum": [e.value for e in TemplateType]}, + "type": {"enum": list(TemplateType)}, "created_at": { "format": "date-time", "type": "string", @@ -62,7 +62,7 @@ post_template_preview_response = { "title": "reponse v2/template/{id}/preview", "properties": { "id": uuid, - "type": {"enum": [e.value for e in TemplateType]}, + "type": {"enum": list(TemplateType)}, "version": {"type": "integer"}, "body": {"type": "string"}, "subject": {"type": ["string", "null"]}, diff --git a/app/v2/templates/templates_schemas.py b/app/v2/templates/templates_schemas.py index 70aea7522..90cb6e01d 100644 --- a/app/v2/templates/templates_schemas.py +++ b/app/v2/templates/templates_schemas.py @@ -5,7 +5,7 @@ get_all_template_request = { "$schema": "http://json-schema.org/draft-07/schema#", "description": "request schema for parameters allowed when getting all templates", "type": "object", - "properties": {"type": {"enum": [e.value for e in TemplateType]}}, + "properties": {"type": {"enum": list(TemplateType)}}, "additionalProperties": False, } diff --git a/tests/app/notifications/test_rest.py b/tests/app/notifications/test_rest.py index 6e8142e7c..0b7f711ce 100644 --- a/tests/app/notifications/test_rest.py +++ b/tests/app/notifications/test_rest.py @@ -272,13 +272,19 @@ def test_only_normal_api_keys_can_return_job_notifications( key_type, ): normal_notification = create_notification( - template=sample_template, api_key=sample_api_key, key_type=KeyType.NORMAL + template=sample_template, + api_key=sample_api_key, + key_type=KeyType.NORMAL, ) team_notification = create_notification( - template=sample_template, api_key=sample_team_api_key, key_type=KeyType.TEAM + template=sample_template, + api_key=sample_team_api_key, + key_type=KeyType.TEAM, ) test_notification = create_notification( - template=sample_template, api_key=sample_test_api_key, key_type=KeyType.TEST + template=sample_template, + api_key=sample_test_api_key, + key_type=KeyType.TEST, ) notification_objs = { diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 94cf5d8d6..4a5d60941 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1231,9 +1231,7 @@ def test_default_permissions_are_added_for_user_service( str(sample_service.id) ] - assert sorted(i.value for i in PermissionType.defaults()) == sorted( - service_permissions - ) + assert sorted(PermissionType.defaults()) == sorted(service_permissions) def test_add_existing_user_to_another_service_with_all_permissions( @@ -1790,7 +1788,7 @@ def test_get_all_notifications_for_service_filters_notifications_when_using_post data = { "page": 1, "template_type": [TemplateType.SMS], - "status": ["created", "sending"], + "status": [NotificationStatus.CREATED, NotificationStatus.SENDING], "to": "0855", } @@ -2103,7 +2101,7 @@ def test_get_detailed_service( date(2000, 1, 1), NotificationType.SMS, sample_service, count=1 ) with freeze_time("2000-01-02T12:00:00"): - create_notification(template=sample_template, status="created") + create_notification(template=sample_template, status=NotificationStatus.CREATED) resp = client.get( f"/service/{sample_service.id}?detailed=True&today_only={today_only}", headers=[create_admin_authorization_header()], @@ -2114,10 +2112,10 @@ def test_get_detailed_service( assert service["id"] == str(sample_service.id) assert "statistics" in service.keys() assert set(service["statistics"].keys()) == { - NotificationType.SMS.value, - NotificationType.EMAIL.value, + NotificationType.SMS, + NotificationType.EMAIL, } - assert service["statistics"][NotificationType.SMS.value] == stats + assert service["statistics"][NotificationType.SMS] == stats def test_get_services_with_detailed_flag(client, sample_template): @@ -2136,8 +2134,8 @@ def test_get_services_with_detailed_flag(client, sample_template): assert data[0]["name"] == "Sample service" assert data[0]["id"] == str(notifications[0].service_id) assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 3}, + NotificationType.EMAIL: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS: {"delivered": 0, "failed": 0, "requested": 3}, } @@ -2159,8 +2157,8 @@ def test_get_services_with_detailed_flag_excluding_from_test_key( data = resp.json["data"] assert len(data) == 1 assert data[0]["statistics"] == { - NotificationType.EMAIL.value: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS.value: {"delivered": 0, "failed": 0, "requested": 2}, + NotificationType.EMAIL: {"delivered": 0, "failed": 0, "requested": 0}, + NotificationType.SMS: {"delivered": 0, "failed": 0, "requested": 2}, } @@ -2229,27 +2227,27 @@ def test_get_detailed_services_groups_by_service(notify_db_session): assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - NotificationType.EMAIL.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.EMAIL: { + "delivered": 0, + "failed": 0, "requested": 0, }, - NotificationType.SMS.value: { - NotificationStatus.DELIVERED: 1, - NotificationStatus.FAILED: 0, + NotificationType.SMS: { + "delivered": 1, + "failed": 0, "requested": 3, }, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - NotificationType.EMAIL.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.EMAIL: { + "delivered": 0, + "failed": 0, "requested": 0, }, - NotificationType.SMS.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.SMS: { + "delivered": 0, + "failed": 0, "requested": 1, }, } @@ -2274,27 +2272,27 @@ def test_get_detailed_services_includes_services_with_no_notifications( assert len(data) == 2 assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { - NotificationType.EMAIL.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.EMAIL: { + "delivered": 0, + "failed": 0, "requested": 0, }, - NotificationType.SMS.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.SMS: { + "delivered": 0, + "failed": 0, "requested": 1, }, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { - NotificationType.EMAIL.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.EMAIL: { + "delivered": 0, + "failed": 0, "requested": 0, }, - NotificationType.SMS.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.SMS: { + "delivered": 0, + "failed": 0, "requested": 0, }, } @@ -2316,14 +2314,14 @@ def test_get_detailed_services_only_includes_todays_notifications(sample_templat assert len(data) == 1 assert data[0]["statistics"] == { - NotificationType.EMAIL.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.EMAIL: { + "delivered": 0, + "failed": 0, "requested": 0, }, - NotificationType.SMS.value: { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + NotificationType.SMS: { + "delivered": 0, + "failed": 0, "requested": 3, }, } @@ -2369,14 +2367,14 @@ def test_get_detailed_services_for_date_range( ) assert len(data) == 1 - assert data[0]["statistics"][NotificationType.EMAIL.value] == { - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + assert data[0]["statistics"][NotificationType.EMAIL] == { + "delivered": 0, + "failed": 0, "requested": 0, } - assert data[0]["statistics"][NotificationType.SMS.value] == { - NotificationStatus.DELIVERED: 2, - NotificationStatus.FAILED: 0, + assert data[0]["statistics"][NotificationType.SMS] == { + "delivered": 2, + "failed": 0, "requested": 2, } diff --git a/tests/app/test_commands.py b/tests/app/test_commands.py index b147f3a0f..a96eae599 100644 --- a/tests/app/test_commands.py +++ b/tests/app/test_commands.py @@ -265,7 +265,8 @@ def test_populate_annual_billing_with_defaults( notify_db_session, notify_api, organization_type, expected_allowance ): service = create_service( - service_name=organization_type.value, organization_type=organization_type + service_name=organization_type, + organization_type=organization_type, ) notify_api.test_cli_runner().invoke( @@ -289,7 +290,8 @@ def test_populate_annual_billing_with_the_previous_years_allowance( notify_db_session, notify_api, organization_type, expected_allowance ): service = create_service( - service_name=organization_type.value, organization_type=organization_type + service_name=organization_type, + organization_type=organization_type, ) notify_api.test_cli_runner().invoke( diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index a68ed3ac7..79fba95be 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -1,7 +1,7 @@ import pytest from flask import json, url_for -from app.enums import NotificationType, TemplateType +from app.enums import NotificationStatus, NotificationType, TemplateType from app.utils import DATETIME_FORMAT from tests import create_service_authorization_header from tests.app.db import create_notification, create_template @@ -284,7 +284,7 @@ def test_get_all_notifications_except_job_notifications_returns_200( assert len(json_response["notifications"]) == 2 assert json_response["notifications"][0]["id"] == str(notification.id) - assert json_response["notifications"][0]["status"] == "created" + assert json_response["notifications"][0]["status"] == NotificationStatus.CREATED assert json_response["notifications"][0]["template"] == { "id": str(notification.template.id), "uri": notification.template.get_link(), @@ -380,7 +380,7 @@ def test_get_all_notifications_filter_by_template_type(client, sample_service): assert len(json_response["notifications"]) == 1 assert json_response["notifications"][0]["id"] == str(notification.id) - assert json_response["notifications"][0]["status"] == "created" + assert json_response["notifications"][0]["status"] == NotificationStatus.CREATED assert json_response["notifications"][0]["template"] == { "id": str(email_template.id), "uri": notification.template.get_link(), @@ -469,7 +469,11 @@ def test_get_all_notifications_filter_by_status_invalid_status( def test_get_all_notifications_filter_by_multiple_statuses(client, sample_template): notifications = [ create_notification(template=sample_template, status=_status) - for _status in ["created", "pending", "sending"] + for _status in [ + NotificationStatus.CREATED, + NotificationStatus.PENDING, + NotificationStatus.SENDING, + ] ] failed_notification = create_notification( template=sample_template, status="permanent-failure" @@ -502,7 +506,8 @@ def test_get_all_notifications_filter_by_multiple_statuses(client, sample_templa def test_get_all_notifications_filter_by_failed_status(client, sample_template): created_notification = create_notification( - template=sample_template, status="created" + template=sample_template, + status=NotificationStatus.CREATED, ) failed_notifications = [ create_notification(template=sample_template, status="failed") @@ -692,6 +697,6 @@ def test_get_all_notifications_renames_letter_statuses( noti["type"] == NotificationType.SMS or noti["type"] == NotificationType.EMAIL ): - assert noti["status"] == "created" + assert noti["status"] == NotificationStatus.CREATED else: pytest.fail() diff --git a/tests/app/v2/notifications/test_notification_schemas.py b/tests/app/v2/notifications/test_notification_schemas.py index 0c6595c20..4d7df0af6 100644 --- a/tests/app/v2/notifications/test_notification_schemas.py +++ b/tests/app/v2/notifications/test_notification_schemas.py @@ -39,7 +39,7 @@ def test_get_notifications_valid_json(input): # multiple invalid statuses (["elephant", "giraffe", "cheetah"], []), # one bad status and one good status - (["elephant"], ["created"]), + (["elephant"], [NotificationStatus.CREATED]), ], ) def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_statuses): From a118b16eb845822c91d4179e2cb92aa232455c4c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 13:18:33 -0500 Subject: [PATCH 205/259] Replaced "delivered". Signed-off-by: Cliff Hill --- app/celery/process_ses_receipts_tasks.py | 8 +- app/clients/__init__.py | 3 - app/clients/email/aws_ses.py | 19 +-- app/enums.py | 6 + app/service/statistics.py | 10 +- .../celery/test_process_ses_receipts_tasks.py | 63 ++++++--- .../app/celery/test_service_callback_tasks.py | 2 +- tests/app/celery/test_test_key_tasks.py | 7 +- tests/app/clients/test_aws_ses.py | 17 +-- tests/app/dao/test_fact_billing_dao.py | 4 +- .../test_notifications_ses_callback.py | 7 +- tests/app/service/test_rest.py | 131 +++++++++++------- tests/app/service/test_statistics_rest.py | 40 +++++- tests/app/test_model.py | 22 ++- .../notifications/test_get_notifications.py | 10 +- 15 files changed, 228 insertions(+), 121 deletions(-) diff --git a/app/celery/process_ses_receipts_tasks.py b/app/celery/process_ses_receipts_tasks.py index 8c5de1f62..63a337ed5 100644 --- a/app/celery/process_ses_receipts_tasks.py +++ b/app/celery/process_ses_receipts_tasks.py @@ -170,22 +170,22 @@ def get_aws_responses(ses_message): "Permanent": { "message": "Hard bounced", "success": False, - "notification_status": "permanent-failure", + "notification_status": NotificationStatus.PERMANENT_FAILURE, }, "Temporary": { "message": "Soft bounced", "success": False, - "notification_status": "temporary-failure", + "notification_status": NotificationStatus.TEMPORARY_FAILURE, }, "Delivery": { "message": "Delivered", "success": True, - "notification_status": "delivered", + "notification_status": NotificationStatus.DELIVERED, }, "Complaint": { "message": "Complaint", "success": True, - "notification_status": "delivered", + "notification_status": NotificationStatus.DELIVERED, }, }[status] diff --git a/app/clients/__init__.py b/app/clients/__init__.py index 66e014354..9c1f4af68 100644 --- a/app/clients/__init__.py +++ b/app/clients/__init__.py @@ -14,9 +14,6 @@ AWS_CLIENT_CONFIG = Config( }, use_fips_endpoint=True, ) -STATISTICS_REQUESTED = "requested" -STATISTICS_DELIVERED = "delivered" -STATISTICS_FAILURE = "failure" class ClientException(Exception): diff --git a/app/clients/email/aws_ses.py b/app/clients/email/aws_ses.py index 7bd68a924..a5c404c2b 100644 --- a/app/clients/email/aws_ses.py +++ b/app/clients/email/aws_ses.py @@ -4,38 +4,39 @@ import botocore from boto3 import client from flask import current_app -from app.clients import AWS_CLIENT_CONFIG, STATISTICS_DELIVERED, STATISTICS_FAILURE +from app.clients import AWS_CLIENT_CONFIG from app.clients.email import ( EmailClient, EmailClientException, EmailClientNonRetryableException, ) from app.cloudfoundry_config import cloud_config +from app.enums import NotificationStatus, StatisticsType ses_response_map = { "Permanent": { "message": "Hard bounced", "success": False, - "notification_status": "permanent-failure", - "notification_statistics_status": STATISTICS_FAILURE, + "notification_status": NotificationStatus.PERMANENT_FAILURE, + "notification_statistics_status": StatisticsType.FAILURE, }, "Temporary": { "message": "Soft bounced", "success": False, - "notification_status": "temporary-failure", - "notification_statistics_status": STATISTICS_FAILURE, + "notification_status": NotificationStatus.TEMPORARY_FAILURE, + "notification_statistics_status": StatisticsType.FAILURE, }, "Delivery": { "message": "Delivered", "success": True, - "notification_status": "delivered", - "notification_statistics_status": STATISTICS_DELIVERED, + "notification_status": NotificationStatus.DELIVERED, + "notification_statistics_status": StatisticsType.DELIVERED, }, "Complaint": { "message": "Complaint", "success": True, - "notification_status": "delivered", - "notification_statistics_status": STATISTICS_DELIVERED, + "notification_status": NotificationStatus.DELIVERED, + "notification_statistics_status": StatisticsType.DELIVERED, }, } diff --git a/app/enums.py b/app/enums.py index a5090463f..709fdf323 100644 --- a/app/enums.py +++ b/app/enums.py @@ -209,3 +209,9 @@ class AgreementType(StrEnum): class AgreementStatus(StrEnum): ACTIVE = "active" EXPIRED = "expired" + + +class StatisticsType(StrEnum): + REQUESTED = "requested" + DELIVERED = "delivered" + FAILURE = "failure" diff --git a/app/service/statistics.py b/app/service/statistics.py index 78359fd6c..c8d882e8b 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -2,7 +2,7 @@ from collections import defaultdict from datetime import datetime from app.dao.date_util import get_months_for_financial_year -from app.enums import NotificationStatus, TemplateType +from app.enums import NotificationStatus, StatisticsType, TemplateType def format_statistics(statistics): @@ -77,16 +77,16 @@ def format_monthly_template_notification_stats(year, rows): def create_zeroed_stats_dicts(): return { - template_type: {status: 0 for status in ("requested", "delivered", "failed")} + template_type: {status: 0 for status in StatisticsType} for template_type in (TemplateType.SMS, TemplateType.EMAIL) } def _update_statuses_from_row(update_dict, row): if row.status != NotificationStatus.CANCELLED: - update_dict["requested"] += row.count + update_dict[StatisticsType.REQUESTED] += row.count if row.status in (NotificationStatus.DELIVERED, NotificationStatus.SENT): - update_dict["delivered"] += row.count + update_dict[StatisticsType.DELIVERED] += row.count elif row.status in ( NotificationStatus.FAILED, NotificationStatus.TECHNICAL_FAILURE, @@ -95,7 +95,7 @@ def _update_statuses_from_row(update_dict, row): NotificationStatus.VALIDATION_FAILED, NotificationStatus.VIRUS_SCAN_FAILED, ): - update_dict["failed"] += row.count + update_dict[StatisticsType.FAILED] += row.count def create_empty_monthly_notification_status_stats_dict(year): diff --git a/tests/app/celery/test_process_ses_receipts_tasks.py b/tests/app/celery/test_process_ses_receipts_tasks.py index 2977f034e..7edfc91ac 100644 --- a/tests/app/celery/test_process_ses_receipts_tasks.py +++ b/tests/app/celery/test_process_ses_receipts_tasks.py @@ -16,6 +16,7 @@ from app.celery.test_key_tasks import ( ses_soft_bounce_callback, ) from app.dao.notifications_dao import get_notification_by_id +from app.enums import NotificationStatus from app.models import Complaint from tests.app.conftest import create_sample_notification from tests.app.db import ( @@ -136,7 +137,7 @@ def test_process_ses_results(sample_email_template): sample_email_template, reference="ref1", sent_at=datetime.utcnow(), - status="sending", + status=NotificationStatus.SENDING, ) assert process_ses_results(response=ses_notification_callback(reference="ref1")) @@ -147,7 +148,7 @@ def test_process_ses_results_retry_called(sample_email_template, mocker): sample_email_template, reference="ref1", sent_at=datetime.utcnow(), - status="sending", + status=NotificationStatus.SENDING, ) mocker.patch( "app.dao.notifications_dao._update_notification_status", @@ -196,15 +197,20 @@ def test_ses_callback_should_update_notification_status( notify_db_session, template=sample_email_template, reference="ref", - status="sending", + status=NotificationStatus.SENDING, sent_at=datetime.utcnow(), ) create_service_callback_api( service=sample_email_template.service, url="https://original_url.com" ) - assert get_notification_by_id(notification.id).status == "sending" + assert ( + get_notification_by_id(notification.id).status == NotificationStatus.SENDING + ) assert process_ses_results(ses_notification_callback(reference="ref")) - assert get_notification_by_id(notification.id).status == "delivered" + assert ( + get_notification_by_id(notification.id).status + == NotificationStatus.DELIVERED + ) send_mock.assert_called_once_with( [str(notification.id), ANY], queue="service-callbacks" ) @@ -222,11 +228,15 @@ def test_ses_callback_should_not_update_notification_status_if_already_delivered "app.celery.process_ses_receipts_tasks.notifications_dao._update_notification_status" ) notification = create_notification( - template=sample_email_template, reference="ref", status="delivered" + template=sample_email_template, + reference="ref", + status=NotificationStatus.DELIVERED, ) assert process_ses_results(ses_notification_callback(reference="ref")) is None - assert get_notification_by_id(notification.id).status == "delivered" - mock_dup.assert_called_once_with(notification, "delivered") + assert ( + get_notification_by_id(notification.id).status == NotificationStatus.DELIVERED + ) + mock_dup.assert_called_once_with(notification, NotificationStatus.DELIVERED) assert mock_upd.call_count == 0 @@ -283,12 +293,17 @@ def test_ses_callback_does_not_call_send_delivery_status_if_no_db_entry( notify_db_session, template=sample_email_template, reference="ref", - status="sending", + status=NotificationStatus.SENDING, sent_at=datetime.utcnow(), ) - assert get_notification_by_id(notification.id).status == "sending" + assert ( + get_notification_by_id(notification.id).status == NotificationStatus.SENDING + ) assert process_ses_results(ses_notification_callback(reference="ref")) - assert get_notification_by_id(notification.id).status == "delivered" + assert ( + get_notification_by_id(notification.id).status + == NotificationStatus.DELIVERED + ) send_mock.assert_not_called() @@ -304,7 +319,7 @@ def test_ses_callback_should_update_multiple_notification_status_sent( template=sample_email_template, reference="ref1", sent_at=datetime.utcnow(), - status="sending", + status=NotificationStatus.SENDING, ) create_sample_notification( _notify_db, @@ -312,7 +327,7 @@ def test_ses_callback_should_update_multiple_notification_status_sent( template=sample_email_template, reference="ref2", sent_at=datetime.utcnow(), - status="sending", + status=NotificationStatus.SENDING, ) create_sample_notification( _notify_db, @@ -320,7 +335,7 @@ def test_ses_callback_should_update_multiple_notification_status_sent( template=sample_email_template, reference="ref3", sent_at=datetime.utcnow(), - status="sending", + status=NotificationStatus.SENDING, ) create_service_callback_api( service=sample_email_template.service, url="https://original_url.com" @@ -342,15 +357,18 @@ def test_ses_callback_should_set_status_to_temporary_failure( notify_db_session, template=sample_email_template, reference="ref", - status="sending", + status=NotificationStatus.SENDING, sent_at=datetime.utcnow(), ) create_service_callback_api( service=notification.service, url="https://original_url.com" ) - assert get_notification_by_id(notification.id).status == "sending" + assert get_notification_by_id(notification.id).status == NotificationStatus.SENDING assert process_ses_results(ses_soft_bounce_callback(reference="ref")) - assert get_notification_by_id(notification.id).status == "temporary-failure" + assert ( + get_notification_by_id(notification.id).status + == NotificationStatus.TEMPORARY_FAILURE + ) assert send_mock.called @@ -365,15 +383,18 @@ def test_ses_callback_should_set_status_to_permanent_failure( notify_db_session, template=sample_email_template, reference="ref", - status="sending", + status=NotificationStatus.SENDING, sent_at=datetime.utcnow(), ) create_service_callback_api( service=sample_email_template.service, url="https://original_url.com" ) - assert get_notification_by_id(notification.id).status == "sending" + assert get_notification_by_id(notification.id).status == NotificationStatus.SENDING assert process_ses_results(ses_hard_bounce_callback(reference="ref")) - assert get_notification_by_id(notification.id).status == "permanent-failure" + assert ( + get_notification_by_id(notification.id).status + == NotificationStatus.PERMANENT_FAILURE + ) assert send_mock.called @@ -392,7 +413,7 @@ def test_ses_callback_should_send_on_complaint_to_user_callback_api( template=sample_email_template, reference="ref1", sent_at=datetime.utcnow(), - status="sending", + status=NotificationStatus.SENDING, ) response = ses_complaint_callback() assert process_ses_results(response) diff --git a/tests/app/celery/test_service_callback_tasks.py b/tests/app/celery/test_service_callback_tasks.py index f1e4db462..2e050e9f8 100644 --- a/tests/app/celery/test_service_callback_tasks.py +++ b/tests/app/celery/test_service_callback_tasks.py @@ -161,7 +161,7 @@ def test__send_data_to_service_callback_api_does_not_retry_if_request_returns_40 created_at=datestr, updated_at=datestr, sent_at=datestr, - status="sent", + status=NotificationStatus.SENT, ) encrypted_data = _set_up_data_for_status_update(callback_api, notification) mocked = mocker.patch( diff --git a/tests/app/celery/test_test_key_tasks.py b/tests/app/celery/test_test_key_tasks.py index 9d2b3109c..dd18c31a2 100644 --- a/tests/app/celery/test_test_key_tasks.py +++ b/tests/app/celery/test_test_key_tasks.py @@ -36,7 +36,10 @@ def test_make_sns_callback(notify_api, rmock, mocker): assert rmock.called assert rmock.request_history[0].url == endpoint - assert json.loads(rmock.request_history[0].text)["status"] == "delivered" + assert ( + json.loads(rmock.request_history[0].text)["status"] + == NotificationStatus.DELIVERED + ) def test_callback_logs_on_api_call_failure(notify_api, rmock, mocker): @@ -86,5 +89,5 @@ def test_delivered_sns_callback(mocker): get_notification_by_id.return_value = n data = json.loads(sns_callback("1234")) - assert data["status"] == "delivered" + assert data["status"] == NotificationStatus.DELIVERED assert data["CID"] == "1234" diff --git a/tests/app/clients/test_aws_ses.py b/tests/app/clients/test_aws_ses.py index 98cbc532f..302fe2dd9 100644 --- a/tests/app/clients/test_aws_ses.py +++ b/tests/app/clients/test_aws_ses.py @@ -12,37 +12,38 @@ from app.clients.email.aws_ses import ( AwsSesClientThrottlingSendRateException, get_aws_responses, ) +from app.enums import NotificationStatus, StatisticsType def test_should_return_correct_details_for_delivery(): response_dict = get_aws_responses("Delivery") assert response_dict["message"] == "Delivered" - assert response_dict["notification_status"] == "delivered" - assert response_dict["notification_statistics_status"] == "delivered" + assert response_dict["notification_status"] == NotificationStatus.DELIVERED + assert response_dict["notification_statistics_status"] == StatisticsType.DELIVERED assert response_dict["success"] def test_should_return_correct_details_for_hard_bounced(): response_dict = get_aws_responses("Permanent") assert response_dict["message"] == "Hard bounced" - assert response_dict["notification_status"] == "permanent-failure" - assert response_dict["notification_statistics_status"] == "failure" + assert response_dict["notification_status"] == NotificationStatus.PERMANENT_FAILURE + assert response_dict["notification_statistics_status"] == StatisticsType.FAILURE assert not response_dict["success"] def test_should_return_correct_details_for_soft_bounced(): response_dict = get_aws_responses("Temporary") assert response_dict["message"] == "Soft bounced" - assert response_dict["notification_status"] == "temporary-failure" - assert response_dict["notification_statistics_status"] == "failure" + assert response_dict["notification_status"] == NotificationStatus.TEMPORARY_FAILURE + assert response_dict["notification_statistics_status"] == StatisticsType.FAILURE assert not response_dict["success"] def test_should_return_correct_details_for_complaint(): response_dict = get_aws_responses("Complaint") assert response_dict["message"] == "Complaint" - assert response_dict["notification_status"] == "delivered" - assert response_dict["notification_statistics_status"] == "delivered" + assert response_dict["notification_status"] == NotificationStatus.DELIVERED + assert response_dict["notification_statistics_status"] == StatisticsType.DELIVERED assert response_dict["success"] diff --git a/tests/app/dao/test_fact_billing_dao.py b/tests/app/dao/test_fact_billing_dao.py index 85d40c3e6..c717aa730 100644 --- a/tests/app/dao/test_fact_billing_dao.py +++ b/tests/app/dao/test_fact_billing_dao.py @@ -116,8 +116,8 @@ def test_fetch_billing_data_for_day_only_calls_query_for_permission_type( service = create_service(service_permissions=[notification_type]) email_template = create_template(service=service, template_type=TemplateType.EMAIL) sms_template = create_template(service=service, template_type=TemplateType.SMS) - create_notification(template=email_template, status="delivered") - create_notification(template=sms_template, status="delivered") + create_notification(template=email_template, status=NotificationStatus.DELIVERED) + create_notification(template=sms_template, status=NotificationStatus.DELIVERED) today = datetime.utcnow() results = fetch_billing_data_for_day( process_day=today.date(), check_permissions=True diff --git a/tests/app/notifications/test_notifications_ses_callback.py b/tests/app/notifications/test_notifications_ses_callback.py index 0260f7665..ec61004d6 100644 --- a/tests/app/notifications/test_notifications_ses_callback.py +++ b/tests/app/notifications/test_notifications_ses_callback.py @@ -7,6 +7,7 @@ from app.celery.process_ses_receipts_tasks import ( handle_complaint, ) from app.dao.notifications_dao import get_notification_by_id +from app.enums import NotificationStatus from app.models import Complaint from tests.app.db import ( create_notification, @@ -23,10 +24,12 @@ def test_ses_callback_should_not_set_status_once_status_is_delivered( ): notification = create_notification( sample_email_template, - status="delivered", + status=NotificationStatus.DELIVERED, ) - assert get_notification_by_id(notification.id).status == "delivered" + assert ( + get_notification_by_id(notification.id).status == NotificationStatus.DELIVERED + ) def test_process_ses_results_in_complaint(sample_email_template): diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 4a5d60941..d7f8e169c 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -21,6 +21,7 @@ from app.enums import ( OrganizationType, PermissionType, ServicePermissionType, + StatisticsType, TemplateType, ) from app.models import ( @@ -1775,10 +1776,14 @@ def test_get_all_notifications_for_service_filters_notifications_when_using_post ) create_notification( - service_1_sms_template, to_field="+447700900000", normalised_to="447700900000" + service_1_sms_template, + to_field="+447700900000", + normalised_to="447700900000", ) create_notification( - service_1_sms_template, status="delivered", normalised_to="447700900855" + service_1_sms_template, + status=NotificationStatus.DELIVERED, + normalised_to="447700900855", ) create_notification(service_1_email_template, normalised_to="447700900855") # create notification for service_2 @@ -2089,8 +2094,22 @@ def test_set_sms_prefixing_for_service_cant_be_none( @pytest.mark.parametrize( "today_only,stats", [ - ("False", {"requested": 2, "delivered": 1, "failed": 0}), - ("True", {"requested": 1, "delivered": 0, "failed": 0}), + ( + "False", + { + StatisticsType.REQUESTED: 2, + StatisticsType.DELIVERED: 1, + StatisticsType.FAILED: 0, + }, + ), + ( + "True", + { + StatisticsType.REQUESTED: 1, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + }, + ), ], ids=["seven_days", "today"], ) @@ -2134,8 +2153,16 @@ def test_get_services_with_detailed_flag(client, sample_template): assert data[0]["name"] == "Sample service" assert data[0]["id"] == str(notifications[0].service_id) assert data[0]["statistics"] == { - NotificationType.EMAIL: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS: {"delivered": 0, "failed": 0, "requested": 3}, + NotificationType.EMAIL: { + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, + }, + NotificationType.SMS: { + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 3, + }, } @@ -2157,8 +2184,16 @@ def test_get_services_with_detailed_flag_excluding_from_test_key( data = resp.json["data"] assert len(data) == 1 assert data[0]["statistics"] == { - NotificationType.EMAIL: {"delivered": 0, "failed": 0, "requested": 0}, - NotificationType.SMS: {"delivered": 0, "failed": 0, "requested": 2}, + NotificationType.EMAIL: { + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, + }, + NotificationType.SMS: { + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 2, + }, } @@ -2228,27 +2263,27 @@ def test_get_detailed_services_groups_by_service(notify_db_session): assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { NotificationType.EMAIL: { - "delivered": 0, - "failed": 0, - "requested": 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { - "delivered": 1, - "failed": 0, - "requested": 3, + StatisticsType.DELIVERED: 1, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 3, }, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { NotificationType.EMAIL: { - "delivered": 0, - "failed": 0, - "requested": 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { - "delivered": 0, - "failed": 0, - "requested": 1, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 1, }, } @@ -2273,27 +2308,27 @@ def test_get_detailed_services_includes_services_with_no_notifications( assert data[0]["id"] == str(service_1.id) assert data[0]["statistics"] == { NotificationType.EMAIL: { - "delivered": 0, - "failed": 0, - "requested": 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { - "delivered": 0, - "failed": 0, - "requested": 1, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 1, }, } assert data[1]["id"] == str(service_2.id) assert data[1]["statistics"] == { NotificationType.EMAIL: { - "delivered": 0, - "failed": 0, - "requested": 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { - "delivered": 0, - "failed": 0, - "requested": 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, }, } @@ -2315,14 +2350,14 @@ def test_get_detailed_services_only_includes_todays_notifications(sample_templat assert len(data) == 1 assert data[0]["statistics"] == { NotificationType.EMAIL: { - "delivered": 0, - "failed": 0, - "requested": 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { - "delivered": 0, - "failed": 0, - "requested": 3, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 3, }, } @@ -2368,14 +2403,14 @@ def test_get_detailed_services_for_date_range( assert len(data) == 1 assert data[0]["statistics"][NotificationType.EMAIL] == { - "delivered": 0, - "failed": 0, - "requested": 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 0, } assert data[0]["statistics"][NotificationType.SMS] == { - "delivered": 2, - "failed": 0, - "requested": 2, + StatisticsType.DELIVERED: 2, + StatisticsType.FAILED: 0, + StatisticsType.REQUESTED: 2, } @@ -2598,13 +2633,13 @@ def test_search_for_notification_by_to_field_filters_by_status(client, sample_te notification1 = create_notification( sample_template, to_field="+447700900855", - status="delivered", + status=NotificationStatus.DELIVERED, normalised_to="447700900855", ) create_notification( sample_template, to_field="+447700900855", - status="sending", + status=NotificationStatus.SENDING, normalised_to="447700900855", ) @@ -2630,13 +2665,13 @@ def test_search_for_notification_by_to_field_filters_by_statuses( notification1 = create_notification( sample_template, to_field="+447700900855", - status="delivered", + status=NotificationStatus.DELIVERED, normalised_to="447700900855", ) notification2 = create_notification( sample_template, to_field="+447700900855", - status="sending", + status=NotificationStatus.SENDING, normalised_to="447700900855", ) diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index 6216f2ab2..a220231ce 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -4,7 +4,13 @@ from datetime import date, datetime import pytest from freezegun import freeze_time -from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType +from app.enums import ( + KeyType, + NotificationStatus, + NotificationType, + StatisticsType, + TemplateType, +) from tests.app.db import ( create_ft_notification_status, create_notification, @@ -106,8 +112,22 @@ def test_get_template_usage_by_month_returns_two_templates( @pytest.mark.parametrize( "today_only, stats", [ - (False, {"requested": 2, "delivered": 1, "failed": 0}), - (True, {"requested": 1, "delivered": 0, "failed": 0}), + ( + False, + { + StatisticsType.REQUESTED: 2, + StatisticsType.DELIVERED: 1, + StatisticsType.FAILED: 0, + }, + ), + ( + True, + { + StatisticsType.REQUESTED: 1, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + }, + ), ], ids=["seven_days", "today"], ) @@ -138,8 +158,16 @@ def test_get_service_notification_statistics_with_unknown_service(admin_request) ) assert resp["data"] == { - NotificationType.SMS: {"requested": 0, "delivered": 0, "failed": 0}, - NotificationType.EMAIL: {"requested": 0, "delivered": 0, "failed": 0}, + NotificationType.SMS: { + StatisticsType.REQUESTED: 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + }, + NotificationType.EMAIL: { + StatisticsType.REQUESTED: 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILED: 0, + }, } @@ -237,7 +265,7 @@ def test_get_monthly_notification_stats_returns_stats(admin_request, sample_serv NotificationStatus.CREATED: 1, NotificationStatus.DELIVERED: 2, }, - NotificationType.EMAIL: {"delivered": 1}, + NotificationType.EMAIL: {StatisticsType.DELIVERED: 1}, } diff --git a/tests/app/test_model.py b/tests/app/test_model.py index 95039b6a3..bbd670412 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -152,21 +152,29 @@ def test_notification_for_csv_returns_correct_job_row_number(sample_job): @pytest.mark.parametrize( "template_type, status, expected_status", [ - (TemplateType.EMAIL, "failed", "Failed"), - (TemplateType.EMAIL, "technical-failure", "Technical failure"), + (TemplateType.EMAIL, NotificationStatus.FAILED, "Failed"), + (TemplateType.EMAIL, NotificationStatus.TECHNICAL_FAILURE, "Technical failure"), ( TemplateType.EMAIL, - "temporary-failure", + NotificationStatus.TEMPORARY_FAILURE, "Inbox not accepting messages right now", ), - (TemplateType.EMAIL, "permanent-failure", "Email address doesn’t exist"), + ( + TemplateType.EMAIL, + NotificationStatus.PERMANENT_FAILURE, + "Email address doesn’t exist", + ), ( TemplateType.SMS, - "temporary-failure", + NotificationStatus.TEMPORARY_FAILURE, "Unable to find carrier response -- still looking", ), - (TemplateType.SMS, "permanent-failure", "Unable to find carrier response."), - (TemplateType.SMS, "sent", "Sent internationally"), + ( + TemplateType.SMS, + NotificationStatus.PERMANENT_FAILURE, + "Unable to find carrier response.", + ), + (TemplateType.SMS, NotificationStatus.SENT, "Sent internationally"), ], ) def test_notification_for_csv_returns_formatted_status( diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index 79fba95be..e551ca4f8 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -630,7 +630,8 @@ def test_get_all_notifications_filter_multiple_query_parameters( # TODO had to change pending to sending. Is that correct? # this is the notification we are looking for older_notification = create_notification( - template=sample_email_template, status="sending" + template=sample_email_template, + status=NotificationStatus.SENDING, ) # wrong status @@ -639,13 +640,16 @@ def test_get_all_notifications_filter_multiple_query_parameters( sample_email_template.service, template_type=TemplateType.SMS ) # wrong template - create_notification(template=wrong_template, status="sending") + create_notification(template=wrong_template, status=NotificationStatus.SENDING) # we only want notifications created before this one newer_notification = create_notification(template=sample_email_template) # this notification was created too recently - create_notification(template=sample_email_template, status="sending") + create_notification( + template=sample_email_template, + status=NotificationStatus.SENDING, + ) auth_header = create_service_authorization_header( service_id=newer_notification.service_id From e9f9a3c6f1e812fa8dc2bd8adecd29d2376a27a9 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 13:47:04 -0500 Subject: [PATCH 206/259] Lots more cleanup. Signed-off-by: Cliff Hill --- app/dao/users_dao.py | 4 ++-- app/service/statistics.py | 16 +++++++------- .../versions/0410_enums_for_everything.py | 1 + tests/app/celery/test_provider_tasks.py | 6 ++--- tests/app/conftest.py | 3 ++- tests/app/dao/test_invited_user_dao.py | 12 +++++----- tests/app/dao/test_jobs_dao.py | 8 +++---- tests/app/dao/test_permissions_dao.py | 15 +++++++------ tests/app/dao/test_users_dao.py | 22 +++++++++---------- tests/app/delivery/test_send_to_providers.py | 4 ++-- tests/app/job/test_rest.py | 4 ++-- tests/app/service/test_rest.py | 2 +- .../test_service_invite_rest.py | 6 ++--- .../notifications/test_get_notifications.py | 8 +++---- 14 files changed, 57 insertions(+), 54 deletions(-) diff --git a/app/dao/users_dao.py b/app/dao/users_dao.py index f50b70c45..7828c5c23 100644 --- a/app/dao/users_dao.py +++ b/app/dao/users_dao.py @@ -9,7 +9,7 @@ from app import db from app.dao.dao_utils import autocommit from app.dao.permissions_dao import permission_dao from app.dao.service_user_dao import dao_get_service_users_by_user_id -from app.enums import AuthType +from app.enums import AuthType, PermissionType from app.errors import InvalidRequest from app.models import User, VerifyCode from app.utils import escape_special_characters, get_archived_db_column_value @@ -198,7 +198,7 @@ def user_can_be_archived(user): return False if not any( - "manage_settings" in user.get_permissions(service.id) + PermissionType.MANAGE_SETTINGS in user.get_permissions(service.id) for user in other_active_users ): # no-one else has manage settings diff --git a/app/service/statistics.py b/app/service/statistics.py index c8d882e8b..6ef87c9ff 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -28,10 +28,10 @@ def format_admin_stats(statistics): else: counts[row.notification_type]["total"] += row.count if row.status in ( - "technical-failure", - "permanent-failure", - "temporary-failure", - "virus-scan-failed", + NotificationStatus.TECHNICAL_FAILURE, + NotificationStatus.PERMANENT_FAILURE, + NotificationStatus.TEMPORARY_FAILURE, + NotificationStatus.VIRUS_SCAN_FAILED, ): counts[row.notification_type]["failures"][row.status] += row.count @@ -47,10 +47,10 @@ def create_stats_dict(): stats_dict[template][status] = 0 stats_dict[template]["failures"] = { - "technical-failure": 0, - "permanent-failure": 0, - "temporary-failure": 0, - "virus-scan-failed": 0, + NotificationStatus.TECHNICAL_FAILURE: 0, + NotificationStatus.PERMANENT_FAILURE: 0, + NotificationStatus.TEMPORARY_FAILURE: 0, + NotificationStatus.VIRUS_SCAN_FAILED: 0, } return stats_dict diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index 879e3c772..e34a3621a 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -25,6 +25,7 @@ from app.enums import ( NotificationStatus, NotificationType, OrganizationType, + PermissionType, RecipientType, ServicePermissionType, TemplateProcessType, diff --git a/tests/app/celery/test_provider_tasks.py b/tests/app/celery/test_provider_tasks.py index e9f27be3d..3b02de283 100644 --- a/tests/app/celery/test_provider_tasks.py +++ b/tests/app/celery/test_provider_tasks.py @@ -106,7 +106,7 @@ def test_should_go_into_technical_error_if_exceeds_retries_on_deliver_sms_task( queue="retry-tasks", countdown=0 ) - assert sample_notification.status == "temporary-failure" + assert sample_notification.status == NotificationStatus.TEMPORARY_FAILURE assert mock_logger_exception.called @@ -164,7 +164,7 @@ def test_should_go_into_technical_error_if_exceeds_retries_on_deliver_email_task assert str(sample_notification.id) in str(e.value) provider_tasks.deliver_email.retry.assert_called_with(queue="retry-tasks") - assert sample_notification.status == "technical-failure" + assert sample_notification.status == NotificationStatus.TECHNICAL_FAILURE def test_should_technical_error_and_not_retry_if_EmailClientNonRetryableException( @@ -179,7 +179,7 @@ def test_should_technical_error_and_not_retry_if_EmailClientNonRetryableExceptio deliver_email(sample_notification.id) assert provider_tasks.deliver_email.retry.called is False - assert sample_notification.status == "technical-failure" + assert sample_notification.status == NotificationStatus.TECHNICAL_FAILURE def test_should_retry_and_log_exception_for_deliver_email_task( diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 2846acfd3..b4714eeb8 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -22,6 +22,7 @@ from app.enums import ( JobStatus, KeyType, NotificationStatus, + PermissionType, RecipientType, ServicePermissionType, TemplateType, @@ -588,7 +589,7 @@ def sample_invited_org_user(sample_user, sample_organization): @pytest.fixture(scope="function") def sample_user_service_permission(sample_user): service = create_service(user=sample_user, check_if_service_exists=True) - permission = "manage_settings" + permission = PermissionType.MANAGE_SETTINGS data = {"user": sample_user, "service": service, "permission": permission} p_model = Permission.query.filter_by( diff --git a/tests/app/dao/test_invited_user_dao.py b/tests/app/dao/test_invited_user_dao.py index de595c415..fb1154c96 100644 --- a/tests/app/dao/test_invited_user_dao.py +++ b/tests/app/dao/test_invited_user_dao.py @@ -12,7 +12,7 @@ from app.dao.invited_user_dao import ( get_invited_users_for_service, save_invited_user, ) -from app.enums import InvitedUserStatus +from app.enums import InvitedUserStatus, PermissionType from app.models import InvitedUser from tests.app.db import create_invited_user @@ -109,12 +109,12 @@ def test_save_invited_user_sets_status_to_cancelled( ): assert InvitedUser.query.count() == 1 saved = InvitedUser.query.get(sample_invited_user.id) - assert saved.status == "pending" - saved.status = "cancelled" + assert saved.status == InvitedUserStatus.PENDING + saved.status = InvitedUserStatus.CANCELLED save_invited_user(saved) assert InvitedUser.query.count() == 1 cancelled_invited_user = InvitedUser.query.get(sample_invited_user.id) - assert cancelled_invited_user.status == "cancelled" + assert cancelled_invited_user.status == InvitedUserStatus.CANCELLED def test_should_delete_all_invitations_more_than_one_day_old( @@ -195,9 +195,9 @@ def make_invitation(user, service, age=None, email_address="test@test.com"): email_address=email_address, from_user=user, service=service, - status="pending", + status=InvitedUserStatus.PENDING, created_at=datetime.utcnow() - (age or timedelta(hours=0)), - permissions="manage_settings", + permissions=PermissionType.MANAGE_SETTINGS, folder_permissions=[str(uuid.uuid4())], ) db.session.add(verify_code) diff --git a/tests/app/dao/test_jobs_dao.py b/tests/app/dao/test_jobs_dao.py index c2de39181..515d453be 100644 --- a/tests/app/dao/test_jobs_dao.py +++ b/tests/app/dao/test_jobs_dao.py @@ -214,7 +214,7 @@ def test_get_jobs_for_service_in_processed_at_then_created_at_order( def test_update_job(sample_job): - assert sample_job.job_status == "pending" + assert sample_job.job_status == JobStatus.PENDING sample_job.job_status = "in progress" @@ -268,8 +268,8 @@ def test_set_scheduled_jobs_to_pending_updates_rows(sample_template): create_job(sample_template, scheduled_for=one_hour_ago, job_status="scheduled") jobs = dao_set_scheduled_jobs_to_pending() assert len(jobs) == 2 - assert jobs[0].job_status == "pending" - assert jobs[1].job_status == "pending" + assert jobs[0].job_status == JobStatus.PENDING + assert jobs[1].job_status == JobStatus.PENDING def test_get_future_scheduled_job_gets_a_job_yet_to_send(sample_scheduled_job): @@ -444,7 +444,7 @@ def test_find_jobs_with_missing_rows_returns_nothing_for_a_job_completed_more_th assert len(results) == 0 -@pytest.mark.parametrize("status", ["pending", "in progress", "cancelled", "scheduled"]) +@pytest.mark.parametrize("status", [JobStatus.PENDING, JobStatus.IN_PROGRESS, JobStatus.CANCELLED, JobStatus.SCHEDULED,],) def test_find_jobs_with_missing_rows_doesnt_return_jobs_that_are_not_finished( sample_email_template, status ): diff --git a/tests/app/dao/test_permissions_dao.py b/tests/app/dao/test_permissions_dao.py index 9d0c85c5f..77c56a2f2 100644 --- a/tests/app/dao/test_permissions_dao.py +++ b/tests/app/dao/test_permissions_dao.py @@ -1,5 +1,6 @@ from app.dao import DAOClass from app.dao.permissions_dao import permission_dao +from app.enums import PermissionType from tests.app.db import create_service @@ -10,13 +11,13 @@ def test_get_permissions_by_user_id_returns_all_permissions(sample_service): assert len(permissions) == 7 assert sorted( [ - "manage_users", - "manage_templates", - "manage_settings", - "send_texts", - "send_emails", - "manage_api_keys", - "view_activity", + PermissionType.MANAGE_USERS, + PermissionType.MANAGE_TEMPLATES, + PermissionType.MANAGE_SETTINGS, + PermissionType.SEND_TEXTS, + PermissionType.SEND_EMAILS, + PermissionType.MANAGE_API_KEYS, + PermissionType.VIEW_ACTIVITY, ] ) == sorted([i.permission for i in permissions]) diff --git a/tests/app/dao/test_users_dao.py b/tests/app/dao/test_users_dao.py index 6e4529310..548840eb1 100644 --- a/tests/app/dao/test_users_dao.py +++ b/tests/app/dao/test_users_dao.py @@ -25,7 +25,7 @@ from app.dao.users_dao import ( update_user_password, user_can_be_archived, ) -from app.enums import AuthType +from app.enums import AuthType, PermissionType from app.errors import InvalidRequest from app.models import User, VerifyCode from tests.app.db import ( @@ -208,14 +208,14 @@ def test_dao_archive_user(sample_user, sample_organization, fake_uuid): service_1 = create_service(service_name="Service 1") service_1_user = create_user(email="1@test.com") service_1.users = [sample_user, service_1_user] - create_permissions(sample_user, service_1, "manage_settings") - create_permissions(service_1_user, service_1, "manage_settings", "view_activity") + create_permissions(sample_user, service_1, PermissionType.MANAGE_SETTINGS) + create_permissions(service_1_user, service_1, PermissionType.MANAGE_SETTINGS, PermissionType.VIEW_ACTIVITY,) service_2 = create_service(service_name="Service 2") service_2_user = create_user(email="2@test.com") service_2.users = [sample_user, service_2_user] - create_permissions(sample_user, service_2, "view_activity") - create_permissions(service_2_user, service_2, "manage_settings") + create_permissions(sample_user, service_2, PermissionType.VIEW_ACTIVITY) + create_permissions(service_2_user, service_2, PermissionType.MANAGE_SETTINGS) # make sample_user an org member sample_organization.users = [sample_user] @@ -265,10 +265,10 @@ def test_user_can_be_archived_if_the_other_service_members_have_the_manage_setti sample_service.users = [user_1, user_2, user_3] - create_permissions(user_1, sample_service, "manage_settings") - create_permissions(user_2, sample_service, "manage_settings", "view_activity") + create_permissions(user_1, sample_service, PermissionType.MANAGE_SETTINGS) + create_permissions(user_2, sample_service, PermissionType.MANAGE_SETTINGS, PermissionType.VIEW_ACTIVITY,) create_permissions( - user_3, sample_service, "manage_settings", "send_emails", "send_texts" + user_3, sample_service, PermissionType.MANAGE_SETTINGS, PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, ) assert len(sample_service.users) == 3 @@ -304,9 +304,9 @@ def test_user_cannot_be_archived_if_the_other_service_members_do_not_have_the_ma sample_service.users = [active_user, pending_user, inactive_user] - create_permissions(active_user, sample_service, "manage_settings") - create_permissions(pending_user, sample_service, "view_activity") - create_permissions(inactive_user, sample_service, "send_emails", "send_texts") + create_permissions(active_user, sample_service, PermissionType.MANAGE_SETTINGS) + create_permissions(pending_user, sample_service, PermissionType.VIEW_ACTIVITY) + create_permissions(inactive_user, sample_service, PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS,) assert len(sample_service.users) == 3 assert not user_can_be_archived(active_user) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index d260f47e6..cb4369a0e 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -153,7 +153,7 @@ def test_should_not_send_email_message_when_service_is_inactive_notifcation_is_i send_to_providers.send_email_to_provider(sample_notification) assert str(sample_notification.id) in str(e.value) send_mock.assert_not_called() - assert Notification.query.get(sample_notification.id).status == "technical-failure" + assert Notification.query.get(sample_notification.id).status == NotificationStatus.TECHNICAL_FAILURE def test_should_not_send_sms_message_when_service_is_inactive_notification_is_in_tech_failure( @@ -166,7 +166,7 @@ def test_should_not_send_sms_message_when_service_is_inactive_notification_is_in send_to_providers.send_sms_to_provider(sample_notification) assert str(sample_notification.id) in str(e.value) send_mock.assert_not_called() - assert Notification.query.get(sample_notification.id).status == "technical-failure" + assert Notification.query.get(sample_notification.id).status == NotificationStatus.TECHNICAL_FAILURE def test_send_sms_should_use_template_version_from_notification_not_latest( diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index b49717e05..acf453c37 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -849,11 +849,11 @@ def test_get_jobs_accepts_page_parameter(admin_request, sample_template): @pytest.mark.parametrize( "statuses_filter, expected_statuses", [ - ("", JobStatus), + ("", list(JobStatus)), ("pending", [JobStatus.PENDING]), ( "pending, in progress, finished, sending limits exceeded, scheduled, cancelled, ready to send, sent to dvla, error", # noqa - JobStatus, + list(JobStatus), ), # bad statuses are accepted, just return no data ("foo", []), diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d7f8e169c..d622be907 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1624,7 +1624,7 @@ def test_remove_user_from_service(client, sample_user_service_permission): Permission( service_id=service.id, user_id=second_user.id, - permission="manage_settings", + permission=PermissionType.MANAGE_SETTINGS, ) ], ) diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index 964a8acc8..482ec1944 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -7,7 +7,7 @@ from flask import current_app from freezegun import freeze_time from notifications_utils.url_safe_token import generate_token -from app.enums import AuthType +from app.enums import AuthType, InvitedUserStatus from app.models import Notification from tests import create_admin_authorization_header from tests.app.db import create_invited_user @@ -224,7 +224,7 @@ def test_resend_expired_invite( assert response.status_code == 200 json_resp = json.loads(response.get_data(as_text=True))["data"] - assert json_resp["status"] == "pending" + assert json_resp["status"] == InvitedUserStatus.PENDING assert mock_send.called @@ -240,7 +240,7 @@ def test_update_invited_user_set_status_to_cancelled(client, sample_invited_user assert response.status_code == 200 json_resp = json.loads(response.get_data(as_text=True))["data"] - assert json_resp["status"] == "cancelled" + assert json_resp["status"] == InvitedUserStatus.CANCELLED def test_update_invited_user_for_wrong_service_returns_404( diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index e551ca4f8..c86dad592 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -415,7 +415,7 @@ def test_get_all_notifications_filter_by_template_type_invalid_template_type( def test_get_all_notifications_filter_by_single_status(client, sample_template): - notification = create_notification(template=sample_template, status="pending") + notification = create_notification(template=sample_template, status=NotificationStatus.PENDING,) create_notification(template=sample_template) auth_header = create_service_authorization_header( @@ -437,7 +437,7 @@ def test_get_all_notifications_filter_by_single_status(client, sample_template): assert len(json_response["notifications"]) == 1 assert json_response["notifications"][0]["id"] == str(notification.id) - assert json_response["notifications"][0]["status"] == "pending" + assert json_response["notifications"][0]["status"] == NotificationStatus.PENDING def test_get_all_notifications_filter_by_status_invalid_status( @@ -476,7 +476,7 @@ def test_get_all_notifications_filter_by_multiple_statuses(client, sample_templa ] ] failed_notification = create_notification( - template=sample_template, status="permanent-failure" + template=sample_template, status=NotificationStatus.PERMANENT_FAILURE, ) auth_header = create_service_authorization_header( @@ -510,7 +510,7 @@ def test_get_all_notifications_filter_by_failed_status(client, sample_template): status=NotificationStatus.CREATED, ) failed_notifications = [ - create_notification(template=sample_template, status="failed") + create_notification(template=sample_template, status=NotificationStatus.FAILED) ] auth_header = create_service_authorization_header( service_id=created_notification.service_id From 7083db9351ff1421e0452af78e7b4da92e5ece8c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 14:14:45 -0500 Subject: [PATCH 207/259] Even more cleanup. Signed-off-by: Cliff Hill --- app/service/statistics.py | 4 +- app/v2/errors.py | 3 +- tests/app/conftest.py | 3 +- tests/app/dao/test_fact_billing_dao.py | 4 +- tests/app/dao/test_invited_user_dao.py | 4 +- tests/app/dao/test_jobs_dao.py | 38 +++++++++++++++---- tests/app/dao/test_services_dao.py | 3 +- tests/app/dao/test_users_dao.py | 27 +++++++++++-- tests/app/delivery/test_send_to_providers.py | 10 ++++- tests/app/service/test_rest.py | 7 +++- tests/app/template/test_rest.py | 2 +- tests/app/template/test_rest_history.py | 5 ++- tests/app/user/test_rest.py | 6 +-- .../notifications/test_get_notifications.py | 8 +++- .../notifications/test_post_notifications.py | 3 +- 15 files changed, 93 insertions(+), 34 deletions(-) diff --git a/app/service/statistics.py b/app/service/statistics.py index 6ef87c9ff..184a5ab0c 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -2,7 +2,7 @@ from collections import defaultdict from datetime import datetime from app.dao.date_util import get_months_for_financial_year -from app.enums import NotificationStatus, StatisticsType, TemplateType +from app.enums import KeyType, NotificationStatus, StatisticsType, TemplateType def format_statistics(statistics): @@ -23,7 +23,7 @@ def format_admin_stats(statistics): counts = create_stats_dict() for row in statistics: - if row.key_type == "test": + if row.key_type == KeyType.TEST: counts[row.notification_type]["test-key"] += row.count else: counts[row.notification_type]["total"] += row.count diff --git a/app/v2/errors.py b/app/v2/errors.py index 63c3fce53..ccb353428 100644 --- a/app/v2/errors.py +++ b/app/v2/errors.py @@ -7,6 +7,7 @@ from sqlalchemy.exc import DataError from sqlalchemy.orm.exc import NoResultFound from app.authentication.auth import AuthError +from app.enums import KeyType from app.errors import InvalidRequest @@ -35,7 +36,7 @@ class RateLimitError(InvalidRequest): def __init__(self, sending_limit, interval, key_type): # normal keys are spoken of as "live" in the documentation # so using this in the error messaging - if key_type == "normal": + if key_type == KeyType.NORMAL: key_type = "live" self.message = self.message_template.format( diff --git a/tests/app/conftest.py b/tests/app/conftest.py index b4714eeb8..fef9e0406 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -25,6 +25,7 @@ from app.enums import ( PermissionType, RecipientType, ServicePermissionType, + TemplateProcessType, TemplateType, ) from app.history_meta import create_history @@ -261,7 +262,7 @@ def sample_template(sample_user): "created_by": sample_user, "archived": False, "hidden": False, - "process_type": "normal", + "process_type": TemplateProcessType.NORMAL, } template = Template(**data) dao_create_template(template) diff --git a/tests/app/dao/test_fact_billing_dao.py b/tests/app/dao/test_fact_billing_dao.py index c717aa730..cebed26d8 100644 --- a/tests/app/dao/test_fact_billing_dao.py +++ b/tests/app/dao/test_fact_billing_dao.py @@ -21,7 +21,7 @@ from app.dao.fact_billing_dao import ( query_organization_sms_usage_for_year, ) from app.dao.organization_dao import dao_add_service_to_organization -from app.enums import NotificationStatus, NotificationType, TemplateType +from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType from app.models import FactBilling from tests.app.db import ( create_annual_billing, @@ -94,7 +94,7 @@ def test_fetch_billing_data_for_today_includes_data_with_the_right_key_type( ): service = create_service() template = create_template(service=service, template_type=TemplateType.EMAIL) - for key_type in ["normal", "test", "team"]: + for key_type in [KeyType.NORMAL, KeyType.TEST, KeyType.TEAM]: create_notification( template=template, status=NotificationStatus.DELIVERED, diff --git a/tests/app/dao/test_invited_user_dao.py b/tests/app/dao/test_invited_user_dao.py index fb1154c96..5331f1851 100644 --- a/tests/app/dao/test_invited_user_dao.py +++ b/tests/app/dao/test_invited_user_dao.py @@ -38,8 +38,8 @@ def test_create_invited_user(notify_db_session, sample_service): assert invited_user.from_user == invite_from permissions = invited_user.get_permissions() assert len(permissions) == 2 - assert "send_messages" in permissions - assert "manage_service" in permissions + assert PermissionType.SEND_EMAILS in permissions + assert PermissionType.MANAGE_SETTINGS in permissions assert invited_user.folder_permissions == [] diff --git a/tests/app/dao/test_jobs_dao.py b/tests/app/dao/test_jobs_dao.py index 515d453be..0831999e1 100644 --- a/tests/app/dao/test_jobs_dao.py +++ b/tests/app/dao/test_jobs_dao.py @@ -216,13 +216,13 @@ def test_get_jobs_for_service_in_processed_at_then_created_at_order( def test_update_job(sample_job): assert sample_job.job_status == JobStatus.PENDING - sample_job.job_status = "in progress" + sample_job.job_status = JobStatus.IN_PROGRESS dao_update_job(sample_job) job_from_db = Job.query.get(sample_job.id) - assert job_from_db.job_status == "in progress" + assert job_from_db.job_status == JobStatus.IN_PROGRESS def test_set_scheduled_jobs_to_pending_gets_all_jobs_in_scheduled_state_before_now( @@ -231,10 +231,14 @@ def test_set_scheduled_jobs_to_pending_gets_all_jobs_in_scheduled_state_before_n one_minute_ago = datetime.utcnow() - timedelta(minutes=1) one_hour_ago = datetime.utcnow() - timedelta(minutes=60) job_new = create_job( - sample_template, scheduled_for=one_minute_ago, job_status="scheduled" + sample_template, + scheduled_for=one_minute_ago, + job_status=JobStatus.SCHEDULED, ) job_old = create_job( - sample_template, scheduled_for=one_hour_ago, job_status="scheduled" + sample_template, + scheduled_for=one_hour_ago, + job_status=JobStatus.SCHEDULED, ) jobs = dao_set_scheduled_jobs_to_pending() assert len(jobs) == 2 @@ -247,7 +251,9 @@ def test_set_scheduled_jobs_to_pending_gets_ignores_jobs_not_scheduled( ): one_minute_ago = datetime.utcnow() - timedelta(minutes=1) job_scheduled = create_job( - sample_template, scheduled_for=one_minute_ago, job_status="scheduled" + sample_template, + scheduled_for=one_minute_ago, + job_status=JobStatus.SCHEDULED, ) jobs = dao_set_scheduled_jobs_to_pending() assert len(jobs) == 1 @@ -264,8 +270,16 @@ def test_set_scheduled_jobs_to_pending_gets_ignores_jobs_scheduled_in_the_future def test_set_scheduled_jobs_to_pending_updates_rows(sample_template): one_minute_ago = datetime.utcnow() - timedelta(minutes=1) one_hour_ago = datetime.utcnow() - timedelta(minutes=60) - create_job(sample_template, scheduled_for=one_minute_ago, job_status="scheduled") - create_job(sample_template, scheduled_for=one_hour_ago, job_status="scheduled") + create_job( + sample_template, + scheduled_for=one_minute_ago, + job_status=JobStatus.SCHEDULED, + ) + create_job( + sample_template, + scheduled_for=one_hour_ago, + job_status=JobStatus.SCHEDULED, + ) jobs = dao_set_scheduled_jobs_to_pending() assert len(jobs) == 2 assert jobs[0].job_status == JobStatus.PENDING @@ -444,7 +458,15 @@ def test_find_jobs_with_missing_rows_returns_nothing_for_a_job_completed_more_th assert len(results) == 0 -@pytest.mark.parametrize("status", [JobStatus.PENDING, JobStatus.IN_PROGRESS, JobStatus.CANCELLED, JobStatus.SCHEDULED,],) +@pytest.mark.parametrize( + "status", + [ + JobStatus.PENDING, + JobStatus.IN_PROGRESS, + JobStatus.CANCELLED, + JobStatus.SCHEDULED, + ], +) def test_find_jobs_with_missing_rows_doesnt_return_jobs_that_are_not_finished( sample_email_template, status ): diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index da9aa0e10..2686b7977 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -46,6 +46,7 @@ from app.enums import ( NotificationStatus, NotificationType, OrganizationType, + PermissionType, ServicePermissionType, TemplateType, ) @@ -916,7 +917,7 @@ def test_add_existing_user_to_another_service_doesnot_change_old_permissions( # adding the other_user to service_one should leave all other_user permissions on service_two intact permissions = [] - for p in ["send_emails", "send_texts"]: + for p in [PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS]: permissions.append(Permission(permission=p)) dao_add_user_to_service(service_one, other_user, permissions=permissions) diff --git a/tests/app/dao/test_users_dao.py b/tests/app/dao/test_users_dao.py index 548840eb1..91c52c170 100644 --- a/tests/app/dao/test_users_dao.py +++ b/tests/app/dao/test_users_dao.py @@ -209,7 +209,12 @@ def test_dao_archive_user(sample_user, sample_organization, fake_uuid): service_1_user = create_user(email="1@test.com") service_1.users = [sample_user, service_1_user] create_permissions(sample_user, service_1, PermissionType.MANAGE_SETTINGS) - create_permissions(service_1_user, service_1, PermissionType.MANAGE_SETTINGS, PermissionType.VIEW_ACTIVITY,) + create_permissions( + service_1_user, + service_1, + PermissionType.MANAGE_SETTINGS, + PermissionType.VIEW_ACTIVITY, + ) service_2 = create_service(service_name="Service 2") service_2_user = create_user(email="2@test.com") @@ -266,9 +271,18 @@ def test_user_can_be_archived_if_the_other_service_members_have_the_manage_setti sample_service.users = [user_1, user_2, user_3] create_permissions(user_1, sample_service, PermissionType.MANAGE_SETTINGS) - create_permissions(user_2, sample_service, PermissionType.MANAGE_SETTINGS, PermissionType.VIEW_ACTIVITY,) create_permissions( - user_3, sample_service, PermissionType.MANAGE_SETTINGS, PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS, + user_2, + sample_service, + PermissionType.MANAGE_SETTINGS, + PermissionType.VIEW_ACTIVITY, + ) + create_permissions( + user_3, + sample_service, + PermissionType.MANAGE_SETTINGS, + PermissionType.SEND_EMAILS, + PermissionType.SEND_TEXTS, ) assert len(sample_service.users) == 3 @@ -306,7 +320,12 @@ def test_user_cannot_be_archived_if_the_other_service_members_do_not_have_the_ma create_permissions(active_user, sample_service, PermissionType.MANAGE_SETTINGS) create_permissions(pending_user, sample_service, PermissionType.VIEW_ACTIVITY) - create_permissions(inactive_user, sample_service, PermissionType.SEND_EMAILS, PermissionType.SEND_TEXTS,) + create_permissions( + inactive_user, + sample_service, + PermissionType.SEND_EMAILS, + PermissionType.SEND_TEXTS, + ) assert len(sample_service.users) == 3 assert not user_can_be_archived(active_user) diff --git a/tests/app/delivery/test_send_to_providers.py b/tests/app/delivery/test_send_to_providers.py index cb4369a0e..2f29a314a 100644 --- a/tests/app/delivery/test_send_to_providers.py +++ b/tests/app/delivery/test_send_to_providers.py @@ -153,7 +153,10 @@ def test_should_not_send_email_message_when_service_is_inactive_notifcation_is_i send_to_providers.send_email_to_provider(sample_notification) assert str(sample_notification.id) in str(e.value) send_mock.assert_not_called() - assert Notification.query.get(sample_notification.id).status == NotificationStatus.TECHNICAL_FAILURE + assert ( + Notification.query.get(sample_notification.id).status + == NotificationStatus.TECHNICAL_FAILURE + ) def test_should_not_send_sms_message_when_service_is_inactive_notification_is_in_tech_failure( @@ -166,7 +169,10 @@ def test_should_not_send_sms_message_when_service_is_inactive_notification_is_in send_to_providers.send_sms_to_provider(sample_notification) assert str(sample_notification.id) in str(e.value) send_mock.assert_not_called() - assert Notification.query.get(sample_notification.id).status == NotificationStatus.TECHNICAL_FAILURE + assert ( + Notification.query.get(sample_notification.id).status + == NotificationStatus.TECHNICAL_FAILURE + ) def test_send_sms_should_use_template_version_from_notification_not_latest( diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d622be907..1166f3034 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1365,7 +1365,10 @@ def test_add_existing_user_to_another_service_with_send_permissions( json_resp = resp.json permissions = json_resp["data"]["permissions"][str(sample_service.id)] - expected_permissions = ["send_texts", "send_emails"] + expected_permissions = [ + PermissionType.SEND_TEXTS, + PermissionType.SEND_EMAILS, + ] assert sorted(expected_permissions) == sorted(permissions) @@ -1498,7 +1501,7 @@ def test_add_existing_user_to_another_service_with_manage_api_keys( json_resp = resp.json permissions = json_resp["data"]["permissions"][str(sample_service.id)] - expected_permissions = ["manage_api_keys"] + expected_permissions = [PermissionType.MANAGE_API_KEYS] assert sorted(expected_permissions) == sorted(permissions) diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index 7691d8966..fb6fdcf74 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -587,7 +587,7 @@ def test_should_get_a_single_template( assert response.status_code == 200 assert data["content"] == content assert data["subject"] == subject - assert data["process_type"] == "normal" + assert data["process_type"] == TemplateProcessType.NORMAL assert not data["redact_personalisation"] diff --git a/tests/app/template/test_rest_history.py b/tests/app/template/test_rest_history.py index 4a8834877..ee902d381 100644 --- a/tests/app/template/test_rest_history.py +++ b/tests/app/template/test_rest_history.py @@ -4,6 +4,7 @@ from datetime import datetime from flask import url_for from app.dao.templates_dao import dao_update_template +from app.enums import TemplateProcessType from tests import create_admin_authorization_header @@ -25,7 +26,7 @@ def test_template_history_version(notify_api, sample_user, sample_template): assert json_resp["data"]["id"] == str(sample_template.id) assert json_resp["data"]["content"] == sample_template.content assert json_resp["data"]["version"] == 1 - assert json_resp["data"]["process_type"] == "normal" + assert json_resp["data"]["process_type"] == TemplateProcessType.NORMAL assert json_resp["data"]["created_by"]["name"] == sample_user.name assert ( datetime.strptime( @@ -57,7 +58,7 @@ def test_previous_template_history_version(notify_api, sample_template): assert json_resp["data"]["id"] == str(sample_template.id) assert json_resp["data"]["version"] == 1 assert json_resp["data"]["content"] == old_content - assert json_resp["data"]["process_type"] == "normal" + assert json_resp["data"]["process_type"] == TemplateProcessType.NORMAL def test_404_missing_template_version(notify_api, sample_template): diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index fdbd9171d..ff59fb0cb 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -8,7 +8,7 @@ from flask import current_app from freezegun import freeze_time from app.dao.service_user_dao import dao_get_service_user, dao_update_service_user -from app.enums import AuthType, NotificationType, PermissionType +from app.enums import AuthType, KeyType, NotificationType, PermissionType from app.models import Notification, Permission, User from tests.app.db import ( create_organization, @@ -261,7 +261,7 @@ def test_post_user_attribute(admin_request, sample_user, user_attribute, user_va "newuser@mail.com", dict( api_key_id=None, - key_type="normal", + key_type=KeyType.NORMAL, notification_type=NotificationType.EMAIL, personalisation={ "name": "Test User", @@ -280,7 +280,7 @@ def test_post_user_attribute(admin_request, sample_user, user_attribute, user_va "+4407700900460", dict( api_key_id=None, - key_type="normal", + key_type=KeyType.NORMAL, notification_type=NotificationType.SMS, personalisation={ "name": "Test User", diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index c86dad592..974d26a51 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -415,7 +415,10 @@ def test_get_all_notifications_filter_by_template_type_invalid_template_type( def test_get_all_notifications_filter_by_single_status(client, sample_template): - notification = create_notification(template=sample_template, status=NotificationStatus.PENDING,) + notification = create_notification( + template=sample_template, + status=NotificationStatus.PENDING, + ) create_notification(template=sample_template) auth_header = create_service_authorization_header( @@ -476,7 +479,8 @@ def test_get_all_notifications_filter_by_multiple_statuses(client, sample_templa ] ] failed_notification = create_notification( - template=sample_template, status=NotificationStatus.PERMANENT_FAILURE, + template=sample_template, + status=NotificationStatus.PERMANENT_FAILURE, ) auth_header = create_service_authorization_header( diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index b7d98fab6..12359a477 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -1406,7 +1406,8 @@ def test_post_notifications_doesnt_use_save_queue_for_test_notifications( headers=[ ("Content-Type", "application/json"), create_service_authorization_header( - service_id=service.id, key_type="test" + service_id=service.id, + key_type=KeyType.TEST, ), ], ) From 06d1e0236fa381ca81b56cdb30e3726f77c00f00 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 21 Feb 2024 14:50:36 -0500 Subject: [PATCH 208/259] String cleanup complete. Signed-off-by: Cliff Hill --- app/celery/process_ses_receipts_tasks.py | 6 ++-- app/dao/annual_billing_dao.py | 11 +++--- .../celery/test_process_ses_receipts_tasks.py | 4 +-- .../app/celery/test_service_callback_tasks.py | 5 ++- tests/app/dao/test_annual_billing_dao.py | 18 +++++----- .../app/dao/test_service_callback_api_dao.py | 9 ++--- tests/app/db.py | 3 +- tests/app/job/test_rest.py | 36 +++++++++---------- tests/app/organization/test_invite_rest.py | 6 ++-- tests/app/organization/test_rest.py | 2 +- .../test_send_notification.py | 6 ++-- .../test_service_invite_rest.py | 4 +-- tests/app/template/test_rest.py | 4 +-- tests/app/template/test_rest_history.py | 2 +- .../notifications/test_post_notifications.py | 5 ++- 15 files changed, 67 insertions(+), 54 deletions(-) diff --git a/app/celery/process_ses_receipts_tasks.py b/app/celery/process_ses_receipts_tasks.py index 63a337ed5..6318d59c1 100644 --- a/app/celery/process_ses_receipts_tasks.py +++ b/app/celery/process_ses_receipts_tasks.py @@ -20,7 +20,7 @@ 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.enums import NotificationStatus +from app.enums import CallbackType, NotificationStatus from app.models import Complaint @@ -209,7 +209,7 @@ def handle_complaint(ses_message): ) return notification = dao_get_notification_history_by_reference(reference) - ses_complaint = ses_message.get("complaint", None) + ses_complaint = ses_message.get(CallbackType.COMPLAINT, None) complaint = Complaint( notification_id=notification.id, @@ -241,7 +241,7 @@ def remove_emails_from_bounce(bounce_dict): def remove_emails_from_complaint(complaint_dict): remove_mail_headers(complaint_dict) - complaint_dict["complaint"].pop("complainedRecipients") + complaint_dict[CallbackType.COMPLAINT].pop("complainedRecipients") return complaint_dict["mail"].pop("destination") diff --git a/app/dao/annual_billing_dao.py b/app/dao/annual_billing_dao.py index c5b05a437..0e4d3b96b 100644 --- a/app/dao/annual_billing_dao.py +++ b/app/dao/annual_billing_dao.py @@ -3,6 +3,7 @@ from flask import current_app from app import db from app.dao.dao_utils import autocommit from app.dao.date_util import get_current_calendar_year_start_year +from app.enums import OrganizationType from app.models import AnnualBilling @@ -65,17 +66,17 @@ def dao_get_all_free_sms_fragment_limit(service_id): def set_default_free_allowance_for_service(service, year_start=None): default_free_sms_fragment_limits = { - "federal": { + OrganizationType.FEDERAL: { 2020: 250_000, 2021: 150_000, 2022: 40_000, }, - "state": { + OrganizationType.STATE: { 2020: 250_000, 2021: 150_000, 2022: 40_000, }, - "other": { + OrganizationType.OTHER: { 2020: 250_000, 2021: 150_000, 2022: 40_000, @@ -97,7 +98,9 @@ def set_default_free_allowance_for_service(service, year_start=None): f"no organization type for service {service.id}. Using other default of " f"{default_free_sms_fragment_limits['other'][year_start]}" ) - free_allowance = default_free_sms_fragment_limits["other"][year_start] + free_allowance = default_free_sms_fragment_limits[OrganizationType.OTHER][ + year_start + ] return dao_create_or_update_annual_billing_for_year( service.id, free_allowance, year_start diff --git a/tests/app/celery/test_process_ses_receipts_tasks.py b/tests/app/celery/test_process_ses_receipts_tasks.py index 7edfc91ac..0b9a45b23 100644 --- a/tests/app/celery/test_process_ses_receipts_tasks.py +++ b/tests/app/celery/test_process_ses_receipts_tasks.py @@ -16,7 +16,7 @@ from app.celery.test_key_tasks import ( ses_soft_bounce_callback, ) from app.dao.notifications_dao import get_notification_by_id -from app.enums import NotificationStatus +from app.enums import CallbackType, NotificationStatus from app.models import Complaint from tests.app.conftest import create_sample_notification from tests.app.db import ( @@ -407,7 +407,7 @@ def test_ses_callback_should_send_on_complaint_to_user_callback_api( create_service_callback_api( service=sample_email_template.service, url="https://original_url.com", - callback_type="complaint", + callback_type=CallbackType.COMPLAINT, ) notification = create_notification( template=sample_email_template, diff --git a/tests/app/celery/test_service_callback_tasks.py b/tests/app/celery/test_service_callback_tasks.py index 2e050e9f8..163893d41 100644 --- a/tests/app/celery/test_service_callback_tasks.py +++ b/tests/app/celery/test_service_callback_tasks.py @@ -27,7 +27,10 @@ from tests.app.db import ( def test_send_delivery_status_to_service_post_https_request_to_service_with_encrypted_data( notify_db_session, notification_type ): - callback_api, template = _set_up_test_data(notification_type, "delivery_status") + callback_api, template = _set_up_test_data( + notification_type, + CallbackType.DELIVERY_STATUS, + ) datestr = datetime(2017, 6, 20) notification = create_notification( diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py index 182d94efc..f4c3e3d57 100644 --- a/tests/app/dao/test_annual_billing_dao.py +++ b/tests/app/dao/test_annual_billing_dao.py @@ -67,17 +67,17 @@ def test_dao_update_annual_billing_for_future_years(notify_db_session, sample_se @pytest.mark.parametrize( "org_type, year, expected_default", [ - ("federal", 2021, 150000), - ("state", 2021, 150000), + (OrganizationType.FEDERAL, 2021, 150000), + (OrganizationType.STATE, 2021, 150000), (None, 2021, 150000), - ("federal", 2020, 250000), - ("state", 2020, 250000), - ("other", 2020, 250000), + (OrganizationType.FEDERAL, 2020, 250000), + (OrganizationType.STATE, 2020, 250000), + (OrganizationType.OTHER, 2020, 250000), (None, 2020, 250000), - ("federal", 2019, 250000), - ("federal", 2022, 40000), - ("state", 2022, 40000), - ("federal", 2023, 40000), + (OrganizationType.FEDERAL, 2019, 250000), + (OrganizationType.FEDERAL, 2022, 40000), + (OrganizationType.STATE, 2022, 40000), + (OrganizationType.FEDERAL, 2023, 40000), ], ) def test_set_default_free_allowance_for_service( diff --git a/tests/app/dao/test_service_callback_api_dao.py b/tests/app/dao/test_service_callback_api_dao.py index ab17dbb23..eb7ac0548 100644 --- a/tests/app/dao/test_service_callback_api_dao.py +++ b/tests/app/dao/test_service_callback_api_dao.py @@ -10,6 +10,7 @@ from app.dao.service_callback_api_dao import ( reset_service_callback_api, save_service_callback_api, ) +from app.enums import CallbackType from app.models import ServiceCallbackApi from tests.app.db import create_service_callback_api @@ -65,7 +66,7 @@ def test_update_service_callback_api_unique_constraint(sample_service): url="https://some_service/callback_endpoint", bearer_token="some_unique_string", updated_by_id=sample_service.users[0].id, - callback_type="delivery_status", + callback_type=CallbackType.DELIVERY_STATUS, ) save_service_callback_api(service_callback_api) another = ServiceCallbackApi( @@ -73,7 +74,7 @@ def test_update_service_callback_api_unique_constraint(sample_service): url="https://some_service/another_callback_endpoint", bearer_token="different_string", updated_by_id=sample_service.users[0].id, - callback_type="delivery_status", + callback_type=CallbackType.DELIVERY_STATUS, ) with pytest.raises(expected_exception=SQLAlchemyError): save_service_callback_api(another) @@ -85,7 +86,7 @@ def test_update_service_callback_can_add_two_api_of_different_types(sample_servi url="https://some_service/callback_endpoint", bearer_token="some_unique_string", updated_by_id=sample_service.users[0].id, - callback_type="delivery_status", + callback_type=CallbackType.DELIVERY_STATUS, ) save_service_callback_api(delivery_status) complaint = ServiceCallbackApi( @@ -93,7 +94,7 @@ def test_update_service_callback_can_add_two_api_of_different_types(sample_servi url="https://some_service/another_callback_endpoint", bearer_token="different_string", updated_by_id=sample_service.users[0].id, - callback_type="complaint", + callback_type=CallbackType.COMPLAINT, ) save_service_callback_api(complaint) results = ServiceCallbackApi.query.order_by(ServiceCallbackApi.callback_type).all() diff --git a/tests/app/db.py b/tests/app/db.py index 632c3a8a6..07f138c41 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -27,6 +27,7 @@ from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template, dao_update_template from app.dao.users_dao import save_model_user from app.enums import ( + CallbackType, JobStatus, KeyType, NotificationStatus, @@ -482,7 +483,7 @@ def create_service_callback_api( service, url="https://something.com", bearer_token="some_super_secret", - callback_type="delivery_status", + callback_type=CallbackType.DELIVERY_STATUS, ): service_callback_api = ServiceCallbackApi( service_id=service.id, diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index acf453c37..289cf0c7f 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -28,7 +28,7 @@ from tests.conftest import set_config def test_get_job_with_invalid_service_id_returns404(client, sample_service): - path = "/service/{}/job".format(sample_service.id) + path = f"/service/{sample_service.id}/job" auth_header = create_admin_authorization_header() response = client.get(path, headers=[auth_header]) assert response.status_code == 200 @@ -38,7 +38,7 @@ def test_get_job_with_invalid_service_id_returns404(client, sample_service): def test_get_job_with_invalid_job_id_returns404(client, sample_template): service_id = sample_template.service.id - path = "/service/{}/job/{}".format(service_id, "bad-id") + path = f"/service/{service_id}/job/{'bad-id'}" auth_header = create_admin_authorization_header() response = client.get(path, headers=[auth_header]) assert response.status_code == 404 @@ -49,7 +49,7 @@ def test_get_job_with_invalid_job_id_returns404(client, sample_template): def test_get_job_with_unknown_id_returns404(client, sample_template, fake_uuid): service_id = sample_template.service.id - path = "/service/{}/job/{}".format(service_id, fake_uuid) + path = f"/service/{service_id}/job/{fake_uuid}" auth_header = create_admin_authorization_header() response = client.get(path, headers=[auth_header]) assert response.status_code == 404 @@ -60,7 +60,7 @@ def test_get_job_with_unknown_id_returns404(client, sample_template, fake_uuid): def test_cancel_job(client, sample_scheduled_job): job_id = str(sample_scheduled_job.id) service_id = sample_scheduled_job.service.id - path = "/service/{}/job/{}/cancel".format(service_id, job_id) + path = f"/service/{service_id}/job/{job_id}/cancel" auth_header = create_admin_authorization_header() response = client.post(path, headers=[auth_header]) assert response.status_code == 200 @@ -73,7 +73,7 @@ def test_cant_cancel_normal_job(client, sample_job, mocker): job_id = str(sample_job.id) service_id = sample_job.service.id mock_update = mocker.patch("app.dao.jobs_dao.dao_update_job") - path = "/service/{}/job/{}/cancel".format(service_id, job_id) + path = f"/service/{service_id}/job/{job_id}/cancel" auth_header = create_admin_authorization_header() response = client.post(path, headers=[auth_header]) assert response.status_code == 404 @@ -95,7 +95,7 @@ def test_create_unscheduled_job(client, sample_template, mocker, fake_uuid): "id": fake_uuid, "created_by": str(sample_template.created_by.id), } - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] @@ -136,7 +136,7 @@ def test_create_unscheduled_job_with_sender_id_in_metadata( "id": fake_uuid, "created_by": str(sample_template.created_by.id), } - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] @@ -168,7 +168,7 @@ def test_create_scheduled_job(client, sample_template, mocker, fake_uuid): "created_by": str(sample_template.created_by.id), "scheduled_for": scheduled_date, } - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] @@ -197,7 +197,7 @@ def test_create_job_returns_403_if_service_is_not_active( mock_job_dao = mocker.patch("app.dao.jobs_dao.dao_create_job") auth_header = create_admin_authorization_header() response = client.post( - "/service/{}/job".format(sample_service.id), + f"/service/{sample_service.id}/job", data="", headers=[("Content-Type", "application/json"), auth_header], ) @@ -229,12 +229,12 @@ def test_create_job_returns_400_if_file_is_invalid( template_id=str(sample_template.id), original_file_name="thisisatest.csv", notification_count=1, - **extra_metadata + **extra_metadata, ) mocker.patch("app.job.rest.get_job_metadata_from_s3", return_value=metadata) data = {"id": fake_uuid} response = client.post( - "/service/{}/job".format(sample_template.service.id), + f"/service/{sample_template.service.id}/job", data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) @@ -266,7 +266,7 @@ def test_should_not_create_scheduled_job_more_then_96_hours_in_the_future( "created_by": str(sample_template.created_by.id), "scheduled_for": scheduled_date, } - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] @@ -303,7 +303,7 @@ def test_should_not_create_scheduled_job_in_the_past( "created_by": str(sample_template.created_by.id), "scheduled_for": scheduled_date, } - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] @@ -327,7 +327,7 @@ def test_create_job_returns_400_if_missing_id(client, sample_template, mocker): }, ) data = {} - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] response = client.post(path, data=json.dumps(data), headers=headers) @@ -354,7 +354,7 @@ def test_create_job_returns_400_if_missing_data( "id": fake_uuid, "valid": "True", } - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] response = client.post(path, data=json.dumps(data), headers=headers) @@ -385,7 +385,7 @@ def test_create_job_returns_404_if_template_does_not_exist( data = { "id": fake_uuid, } - path = "/service/{}/job".format(sample_service.id) + path = f"/service/{sample_service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] response = client.post(path, data=json.dumps(data), headers=headers) @@ -408,7 +408,7 @@ def test_create_job_returns_404_if_missing_service(client, sample_template, mock ) random_id = str(uuid.uuid4()) data = {} - path = "/service/{}/job".format(random_id) + path = f"/service/{random_id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] response = client.post(path, data=json.dumps(data), headers=headers) @@ -437,7 +437,7 @@ def test_create_job_returns_400_if_archived_template( "id": fake_uuid, "valid": "True", } - path = "/service/{}/job".format(sample_template.service.id) + path = f"/service/{sample_template.service.id}/job" auth_header = create_admin_authorization_header() headers = [("Content-Type", "application/json"), auth_header] response = client.post(path, data=json.dumps(data), headers=headers) diff --git a/tests/app/organization/test_invite_rest.py b/tests/app/organization/test_invite_rest.py index fc5cf045d..c57fa8dd0 100644 --- a/tests/app/organization/test_invite_rest.py +++ b/tests/app/organization/test_invite_rest.py @@ -158,7 +158,7 @@ def test_get_invited_user_by_organization_when_user_does_not_belong_to_the_org( def test_update_org_invited_user_set_status_to_cancelled( admin_request, sample_invited_org_user ): - data = {"status": "cancelled"} + data = {"status": InvitedUserStatus.CANCELLED} json_resp = admin_request.post( "organization_invite.update_org_invite_status", @@ -166,13 +166,13 @@ def test_update_org_invited_user_set_status_to_cancelled( invited_org_user_id=sample_invited_org_user.id, _data=data, ) - assert json_resp["data"]["status"] == "cancelled" + assert json_resp["data"]["status"] == InvitedUserStatus.CANCELLED def test_update_org_invited_user_for_wrong_service_returns_404( admin_request, sample_invited_org_user, fake_uuid ): - data = {"status": "cancelled"} + data = {"status": InvitedUserStatus.CANCELLED} json_resp = admin_request.post( "organization_invite.update_org_invite_status", diff --git a/tests/app/organization/test_rest.py b/tests/app/organization/test_rest.py index 74bf7ac71..914dca008 100644 --- a/tests/app/organization/test_rest.py +++ b/tests/app/organization/test_rest.py @@ -580,7 +580,7 @@ def test_post_link_service_to_organization(admin_request, sample_service): _expected_status=204, ) assert len(organization.services) == 1 - assert sample_service.organization_type == "federal" + assert sample_service.organization_type == OrganizationType.FEDERAL @freeze_time("2021-09-24 13:30") diff --git a/tests/app/service/send_notification/test_send_notification.py b/tests/app/service/send_notification/test_send_notification.py index 6b5441efb..d85cb939a 100644 --- a/tests/app/service/send_notification/test_send_notification.py +++ b/tests/app/service/send_notification/test_send_notification.py @@ -12,7 +12,7 @@ from app.dao import notifications_dao from app.dao.api_key_dao import save_model_api_key from app.dao.services_dao import dao_update_service from app.dao.templates_dao import dao_get_all_templates_for_service, dao_update_template -from app.enums import KeyType, NotificationType, TemplateType +from app.enums import KeyType, NotificationType, TemplateProcessType, TemplateType from app.errors import InvalidRequest from app.models import ApiKey, Notification, NotificationHistory, Template from app.service.send_notification import send_one_off_notification @@ -1132,7 +1132,9 @@ def test_send_notification_uses_priority_queue_when_template_is_marked_as_priori send_to, ): sample = create_template( - sample_service, template_type=notification_type, process_type="priority" + sample_service, + template_type=notification_type, + process_type=TemplateProcessType.PRIORITY, ) mocked = mocker.patch( f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" diff --git a/tests/app/service_invite/test_service_invite_rest.py b/tests/app/service_invite/test_service_invite_rest.py index 482ec1944..00574ca3f 100644 --- a/tests/app/service_invite/test_service_invite_rest.py +++ b/tests/app/service_invite/test_service_invite_rest.py @@ -229,7 +229,7 @@ def test_resend_expired_invite( def test_update_invited_user_set_status_to_cancelled(client, sample_invited_user): - data = {"status": "cancelled"} + data = {"status": InvitedUserStatus.CANCELLED} url = f"/service/{sample_invited_user.service_id}/invite/{sample_invited_user.id}" auth_header = create_admin_authorization_header() response = client.post( @@ -246,7 +246,7 @@ def test_update_invited_user_set_status_to_cancelled(client, sample_invited_user def test_update_invited_user_for_wrong_service_returns_404( client, sample_invited_user, fake_uuid ): - data = {"status": "cancelled"} + data = {"status": InvitedUserStatus.CANCELLED} url = f"/service/{fake_uuid}/invite/{sample_invited_user.id}" auth_header = create_admin_authorization_header() response = client.post( diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index fb6fdcf74..93b36939f 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -798,7 +798,7 @@ def test_update_does_not_create_new_version_when_there_is_no_change( def test_update_set_process_type_on_template(client, sample_template): auth_header = create_admin_authorization_header() - data = {"process_type": "priority"} + data = {"process_type": TemplateProcessType.PRIORITY} resp = client.post( f"/service/{sample_template.service_id}/template/{sample_template.id}", data=json.dumps(data), @@ -807,7 +807,7 @@ def test_update_set_process_type_on_template(client, sample_template): assert resp.status_code == 200 template = dao_get_template_by_id(sample_template.id) - assert template.process_type == "priority" + assert template.process_type == TemplateProcessType.PRIORITY @pytest.mark.parametrize( diff --git a/tests/app/template/test_rest_history.py b/tests/app/template/test_rest_history.py index ee902d381..6aa234de0 100644 --- a/tests/app/template/test_rest_history.py +++ b/tests/app/template/test_rest_history.py @@ -39,7 +39,7 @@ def test_template_history_version(notify_api, sample_user, sample_template): def test_previous_template_history_version(notify_api, sample_template): old_content = sample_template.content sample_template.content = "New content" - sample_template.process_type = "priority" + sample_template.process_type = TemplateProcessType.PRIORITY dao_update_template(sample_template) with notify_api.test_request_context(): with notify_api.test_client() as client: diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index 12359a477..5a1888a93 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -13,6 +13,7 @@ from app.enums import ( NotificationStatus, NotificationType, ServicePermissionType, + TemplateProcessType, TemplateType, ) from app.models import Notification @@ -580,7 +581,9 @@ def test_send_notification_uses_priority_queue_when_template_is_marked_as_priori mocker.patch(f"app.celery.provider_tasks.deliver_{notification_type}.apply_async") sample = create_template( - service=sample_service, template_type=notification_type, process_type="priority" + service=sample_service, + template_type=notification_type, + process_type=TemplateProcessType.PRIORITY, ) mocked = mocker.patch( f"app.celery.provider_tasks.deliver_{notification_type}.apply_async" From 9d17d3a9caaabf45278873da25e54d7deecb42b0 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 22 Feb 2024 09:33:36 -0500 Subject: [PATCH 209/259] Fixing tests, fixing tests... Signed-off-by: Cliff Hill --- app/service/statistics.py | 2 +- tests/app/service/test_rest.py | 36 +++++++++---------- tests/app/service/test_statistics_rest.py | 8 ++--- .../notifications/test_get_notifications.py | 6 ++-- .../test_notification_schemas.py | 15 +++----- .../notifications/test_post_notifications.py | 2 +- tests/app/v2/templates/test_get_templates.py | 2 +- .../v2/templates/test_templates_schemas.py | 2 +- 8 files changed, 32 insertions(+), 41 deletions(-) diff --git a/app/service/statistics.py b/app/service/statistics.py index 184a5ab0c..90b933960 100644 --- a/app/service/statistics.py +++ b/app/service/statistics.py @@ -95,7 +95,7 @@ def _update_statuses_from_row(update_dict, row): NotificationStatus.VALIDATION_FAILED, NotificationStatus.VIRUS_SCAN_FAILED, ): - update_dict[StatisticsType.FAILED] += row.count + update_dict[StatisticsType.FAILURE] += row.count def create_empty_monthly_notification_status_stats_dict(year): diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 1166f3034..d15351188 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -2102,7 +2102,7 @@ def test_set_sms_prefixing_for_service_cant_be_none( { StatisticsType.REQUESTED: 2, StatisticsType.DELIVERED: 1, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, }, ), ( @@ -2110,7 +2110,7 @@ def test_set_sms_prefixing_for_service_cant_be_none( { StatisticsType.REQUESTED: 1, StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, }, ), ], @@ -2158,12 +2158,12 @@ def test_get_services_with_detailed_flag(client, sample_template): assert data[0]["statistics"] == { NotificationType.EMAIL: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 3, }, } @@ -2189,12 +2189,12 @@ def test_get_services_with_detailed_flag_excluding_from_test_key( assert data[0]["statistics"] == { NotificationType.EMAIL: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 2, }, } @@ -2267,12 +2267,12 @@ def test_get_detailed_services_groups_by_service(notify_db_session): assert data[0]["statistics"] == { NotificationType.EMAIL: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { StatisticsType.DELIVERED: 1, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 3, }, } @@ -2280,12 +2280,12 @@ def test_get_detailed_services_groups_by_service(notify_db_session): assert data[1]["statistics"] == { NotificationType.EMAIL: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 1, }, } @@ -2312,12 +2312,12 @@ def test_get_detailed_services_includes_services_with_no_notifications( assert data[0]["statistics"] == { NotificationType.EMAIL: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 1, }, } @@ -2325,12 +2325,12 @@ def test_get_detailed_services_includes_services_with_no_notifications( assert data[1]["statistics"] == { NotificationType.EMAIL: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, } @@ -2354,12 +2354,12 @@ def test_get_detailed_services_only_includes_todays_notifications(sample_templat assert data[0]["statistics"] == { NotificationType.EMAIL: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, }, NotificationType.SMS: { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 3, }, } @@ -2407,12 +2407,12 @@ def test_get_detailed_services_for_date_range( assert len(data) == 1 assert data[0]["statistics"][NotificationType.EMAIL] == { StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 0, } assert data[0]["statistics"][NotificationType.SMS] == { StatisticsType.DELIVERED: 2, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, StatisticsType.REQUESTED: 2, } diff --git a/tests/app/service/test_statistics_rest.py b/tests/app/service/test_statistics_rest.py index a220231ce..522c3902b 100644 --- a/tests/app/service/test_statistics_rest.py +++ b/tests/app/service/test_statistics_rest.py @@ -117,7 +117,7 @@ def test_get_template_usage_by_month_returns_two_templates( { StatisticsType.REQUESTED: 2, StatisticsType.DELIVERED: 1, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, }, ), ( @@ -125,7 +125,7 @@ def test_get_template_usage_by_month_returns_two_templates( { StatisticsType.REQUESTED: 1, StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, }, ), ], @@ -161,12 +161,12 @@ def test_get_service_notification_statistics_with_unknown_service(admin_request) NotificationType.SMS: { StatisticsType.REQUESTED: 0, StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, }, NotificationType.EMAIL: { StatisticsType.REQUESTED: 0, StatisticsType.DELIVERED: 0, - StatisticsType.FAILED: 0, + StatisticsType.FAILURE: 0, }, } diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index 974d26a51..695302f15 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -410,7 +410,7 @@ def test_get_all_notifications_filter_by_template_type_invalid_template_type( assert len(json_response["errors"]) == 1 assert ( json_response["errors"][0]["message"] - == "template_type orange is not one of [sms, email, letter]" + == f"template_type orange is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" ) @@ -463,9 +463,7 @@ def test_get_all_notifications_filter_by_status_invalid_status( assert len(json_response["errors"]) == 1 assert ( json_response["errors"][0]["message"] - == "status elephant is not one of [cancelled, created, sending, " - "sent, delivered, pending, failed, technical-failure, temporary-failure, permanent-failure, " - "pending-virus-check, validation-failed, virus-scan-failed]" + == f"status elephant is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in NotificationStatus])}]" ) diff --git a/tests/app/v2/notifications/test_notification_schemas.py b/tests/app/v2/notifications/test_notification_schemas.py index 4d7df0af6..b3b0d6dc4 100644 --- a/tests/app/v2/notifications/test_notification_schemas.py +++ b/tests/app/v2/notifications/test_notification_schemas.py @@ -43,12 +43,7 @@ def test_get_notifications_valid_json(input): ], ) def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_statuses): - partial_error_status = ( - "is not one of " - "[cancelled, created, sending, sent, delivered, pending, failed, " - "technical-failure, temporary-failure, permanent-failure, pending-virus-check, " - "validation-failed, virus-scan-failed]" - ) + partial_error_status = f"is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in NotificationStatus])}]" with pytest.raises(ValidationError) as e: validate( @@ -75,7 +70,7 @@ def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_stat def test_get_notifications_request_invalid_template_types( invalid_template_types, valid_template_types ): - partial_error_template_type = "is not one of [sms, email, letter]" + partial_error_template_type = f"is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" with pytest.raises(ValidationError) as e: validate( @@ -108,15 +103,13 @@ def test_get_notifications_request_invalid_statuses_and_template_types(): error_messages = [error["message"] for error in errors] for invalid_status in ["elephant", "giraffe"]: assert ( - f"status {invalid_status} is not one of [cancelled, created, sending, sent, delivered, " - "pending, failed, technical-failure, temporary-failure, permanent-failure, " - "pending-virus-check, validation-failed, virus-scan-failed]" + f"status {invalid_status} is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in NotificationStatus])}]" in error_messages ) for invalid_template_type in ["orange", "avocado"]: assert ( - f"template_type {invalid_template_type} is not one of [sms, email, letter]" + f"template_type {invalid_template_type} is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" in error_messages ) diff --git a/tests/app/v2/notifications/test_post_notifications.py b/tests/app/v2/notifications/test_post_notifications.py index 5a1888a93..5af6f897d 100644 --- a/tests/app/v2/notifications/test_post_notifications.py +++ b/tests/app/v2/notifications/test_post_notifications.py @@ -76,7 +76,7 @@ def test_post_sms_notification_returns_201( assert resp_json["template"]["version"] == sample_template_with_placeholders.version assert ( f"services/{sample_template_with_placeholders.service_id}/templates/" - f"{sample_template_with_placeholders.service_id}" + f"{sample_template_with_placeholders.id}" ) in resp_json["template"]["uri"] assert not resp_json["scheduled_for"] assert mocked.called diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index ebed4fbcf..c43120754 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -120,7 +120,7 @@ def test_get_all_templates_for_invalid_type_returns_400(client, sample_service): "status_code": 400, "errors": [ { - "message": "type coconut is not one of [sms, email, letter]", + "message": f"type coconut is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]", "error": "ValidationError", } ], diff --git a/tests/app/v2/templates/test_templates_schemas.py b/tests/app/v2/templates/test_templates_schemas.py index 08236d8b1..74006be62 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -280,7 +280,7 @@ def test_get_all_template_request_schema_against_invalid_args_is_invalid(templat assert len(errors["errors"]) == 1 assert ( errors["errors"][0]["message"] - == "type unknown is not one of [sms, email, letter]" + == f"type unknown is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" ) From b5dac890a23aeb6798895465755c28f83efbf43f Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Thu, 22 Feb 2024 17:06:18 -0500 Subject: [PATCH 210/259] Debugging. Signed-off-by: Cliff Hill --- .../test_service_data_retention_rest.py | 2 +- tests/app/service/test_statistics.py | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/app/service/test_service_data_retention_rest.py b/tests/app/service/test_service_data_retention_rest.py index a6b96fc91..9e429736d 100644 --- a/tests/app/service/test_service_data_retention_rest.py +++ b/tests/app/service/test_service_data_retention_rest.py @@ -129,7 +129,7 @@ def test_create_service_data_retention_returns_400_when_notification_type_is_inv assert json_resp["errors"][0]["error"] == "ValidationError" assert ( json_resp["errors"][0]["message"] - == "notification_type unknown is not one of [sms, email]" + == f"notification_type unknown is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in (NotificationType.SMS, NotificationType.EMAIL)])}]" ) diff --git a/tests/app/service/test_statistics.py b/tests/app/service/test_statistics.py index 0ccad3501..28484a8d6 100644 --- a/tests/app/service/test_statistics.py +++ b/tests/app/service/test_statistics.py @@ -5,7 +5,7 @@ from unittest.mock import Mock import pytest from freezegun import freeze_time -from app.enums import KeyType, NotificationStatus, NotificationType +from app.enums import KeyType, NotificationStatus, NotificationType, StatisticsType from app.service.statistics import ( add_monthly_notification_status_stats, create_empty_monthly_notification_status_stats_dict, @@ -84,9 +84,9 @@ def test_format_statistics(stats, email_counts, sms_counts): status: count for status, count in zip( [ - NotificationStatus.REQUESTED, - NotificationStatus.DELIVERED, - NotificationStatus.FAILED, + StatisticsType.REQUESTED, + StatisticsType.DELIVERED, + StatisticsType.FAILURE, ], email_counts, ) @@ -96,9 +96,9 @@ def test_format_statistics(stats, email_counts, sms_counts): status: count for status, count in zip( [ - NotificationStatus.REQUESTED, - NotificationStatus.DELIVERED, - NotificationStatus.FAILED, + StatisticsType.REQUESTED, + StatisticsType.DELIVERED, + StatisticsType.FAILURE, ], sms_counts, ) @@ -108,14 +108,14 @@ def test_format_statistics(stats, email_counts, sms_counts): def test_create_zeroed_stats_dicts(): assert create_zeroed_stats_dicts() == { NotificationType.SMS: { - NotificationStatus.REQUESTED: 0, - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + StatisticsType.REQUESTED: 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILURE: 0, }, NotificationType.EMAIL: { - NotificationStatus.REQUESTED: 0, - NotificationStatus.DELIVERED: 0, - NotificationStatus.FAILED: 0, + StatisticsType.REQUESTED: 0, + StatisticsType.DELIVERED: 0, + StatisticsType.FAILURE: 0, }, } @@ -154,7 +154,7 @@ def test_format_admin_stats_only_includes_test_key_notifications_in_test_key_sec 3, ), NewStatsRow( - NotificationType.SMS, NotificationType.PERMANENT_FAILURE, KeyType.TEST, 4 + NotificationType.SMS, NotificationStatus.PERMANENT_FAILURE, KeyType.TEST, 4 ), ] stats_dict = format_admin_stats(rows) @@ -226,9 +226,9 @@ def test_format_admin_stats_counts_non_test_key_notifications_correctly(): def _stats(requested, delivered, failed): return { - NotificationStatus.REQUESTED: requested, - NotificationStatus.DELIVERED: delivered, - NotificationStatus.FAILED: failed, + StatisticsType.REQUESTED: requested, + StatisticsType.DELIVERED: delivered, + StatisticsType.FAILURE: failed, } From 75cec3a635e90721b715637e416b1eaeb034900c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Mon, 26 Feb 2024 20:07:23 -0500 Subject: [PATCH 211/259] Almost all tests are working. Only 3 left to fix. Signed-off-by: Cliff Hill --- app/performance_dashboard/rest.py | 10 +++++----- tests/__init__.py | 2 +- tests/app/conftest.py | 11 ++++++----- .../app/dao/notification_dao/test_notification_dao.py | 2 +- tests/app/dao/test_invited_user_dao.py | 2 +- tests/app/dao/test_service_callback_api_dao.py | 6 ++++-- tests/app/dao/test_service_data_retention_dao.py | 5 +++-- tests/app/dao/test_services_dao.py | 6 +++--- tests/app/dao/test_users_dao.py | 3 +-- tests/app/email_branding/test_rest.py | 4 ++-- tests/app/notifications/test_validators.py | 5 +++-- 11 files changed, 30 insertions(+), 26 deletions(-) diff --git a/app/performance_dashboard/rest.py b/app/performance_dashboard/rest.py index a225e9798..747c8964d 100644 --- a/app/performance_dashboard/rest.py +++ b/app/performance_dashboard/rest.py @@ -32,18 +32,18 @@ def get_performance_dashboard(): today = str(datetime.utcnow().date()) start_date = datetime.strptime( - request.args.get("start_date", today), "%Y-%m-%d" + request.args.get("start_date", today), "%Y-%m-%d", ).date() - end_date = datetime.strptime(request.args.get("end_date", today), "%Y-%m-%d").date() + end_date = datetime.strptime(request.args.get("end_date", today), "%Y-%m-%d",).date() total_for_all_time = get_total_notifications_for_date_range( - start_date=None, end_date=None + start_date=None, end_date=None, ) total_notifications, emails, sms = transform_results_into_totals(total_for_all_time) totals_for_date_range = get_total_notifications_for_date_range( - start_date=start_date, end_date=end_date + start_date=start_date, end_date=end_date, ) processing_time_results = get_processing_time_percentage_for_date_range( - start_date=start_date, end_date=end_date + start_date=start_date, end_date=end_date, ) services = get_live_services_with_organization() stats = { diff --git a/tests/__init__.py b/tests/__init__.py index 1105a427e..eeb1c2ae2 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -39,7 +39,7 @@ def create_admin_authorization_header(): def create_internal_authorization_header(client_id): secret = current_app.config["INTERNAL_CLIENT_API_KEYS"][client_id][0] token = create_jwt_token(secret=secret, client_id=client_id) - return "Authorization", "Bearer {}".format(token) + return "Authorization", f"Bearer {token}" def unwrap_function(fn): diff --git a/tests/app/conftest.py b/tests/app/conftest.py index fef9e0406..e96ed1069 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -1,7 +1,6 @@ import json import uuid from datetime import datetime, timedelta -from types import CodeType import pytest import pytz @@ -19,6 +18,8 @@ from app.dao.services_dao import dao_add_user_to_service, dao_create_service from app.dao.templates_dao import dao_create_template from app.dao.users_dao import create_secret_code, create_user_code from app.enums import ( + CodeType, + InvitedUserStatus, JobStatus, KeyType, NotificationStatus, @@ -132,9 +133,9 @@ def create_sample_notification( "api_key_id": api_key and api_key.id, "key_type": api_key.key_type if api_key else key_type, "sent_by": sent_by, - "updated_at": created_at - if status in NotificationStatus.completed_types() - else None, + "updated_at": ( + created_at if status in NotificationStatus.completed_types() else None + ), "client_reference": client_reference, "rate_multiplier": rate_multiplier, "normalised_to": normalised_to, @@ -575,7 +576,7 @@ def sample_expired_user(notify_db_session): "permissions": "send_messages,manage_service,manage_api_keys", "folder_permissions": ["folder_1_id", "folder_2_id"], "created_at": datetime.utcnow() - timedelta(days=3), - "status": NotificationStatus.EXPIRED, + "status": InvitedUserStatus.EXPIRED, } expired_user = InvitedUser(**data) save_invited_user(expired_user) diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index 849adfb45..feb380568 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -644,7 +644,7 @@ def test_get_all_notifications_for_job_by_status(sample_job): assert len(notifications(filter_dict={"status": status}).items) == 1 - assert len(notifications(filter_dict={"status": NotificationStatus[:3]}).items) == 3 + assert len(notifications(filter_dict={"status": list(NotificationStatus)[:3]}).items) == 3 def test_dao_get_notification_count_for_job_id(notify_db_session): diff --git a/tests/app/dao/test_invited_user_dao.py b/tests/app/dao/test_invited_user_dao.py index 5331f1851..cedee16ea 100644 --- a/tests/app/dao/test_invited_user_dao.py +++ b/tests/app/dao/test_invited_user_dao.py @@ -26,7 +26,7 @@ def test_create_invited_user(notify_db_session, sample_service): "service": sample_service, "email_address": email_address, "from_user": invite_from, - "permissions": "send_messages,manage_service", + "permissions": "send_emails,manage_settings", "folder_permissions": [], } diff --git a/tests/app/dao/test_service_callback_api_dao.py b/tests/app/dao/test_service_callback_api_dao.py index eb7ac0548..ac7fe2b46 100644 --- a/tests/app/dao/test_service_callback_api_dao.py +++ b/tests/app/dao/test_service_callback_api_dao.py @@ -99,8 +99,10 @@ def test_update_service_callback_can_add_two_api_of_different_types(sample_servi save_service_callback_api(complaint) results = ServiceCallbackApi.query.order_by(ServiceCallbackApi.callback_type).all() assert len(results) == 2 - assert results[0].serialize() == complaint.serialize() - assert results[1].serialize() == delivery_status.serialize() + + callbacks = [complaint.serialize(), delivery_status.serialize()] + assert results[0].serialize() in callbacks + assert results[1].serialize() in callbacks def test_update_service_callback_api(sample_service): diff --git a/tests/app/dao/test_service_data_retention_dao.py b/tests/app/dao/test_service_data_retention_dao.py index 529281b61..1d60c619b 100644 --- a/tests/app/dao/test_service_data_retention_dao.py +++ b/tests/app/dao/test_service_data_retention_dao.py @@ -31,8 +31,9 @@ def test_fetch_service_data_retention(sample_service): list_of_data_retention = fetch_service_data_retention(sample_service.id) assert len(list_of_data_retention) == 2 - assert list_of_data_retention[0] == email_data_retention - assert list_of_data_retention[1] == sms_data_retention + data_retentions = [email_data_retention, sms_data_retention] + assert list_of_data_retention[0] in data_retentions + assert list_of_data_retention[1] in data_retentions def test_fetch_service_data_retention_only_returns_row_for_service(sample_service): diff --git a/tests/app/dao/test_services_dao.py b/tests/app/dao/test_services_dao.py index 2686b7977..565bc52e9 100644 --- a/tests/app/dao/test_services_dao.py +++ b/tests/app/dao/test_services_dao.py @@ -1275,7 +1275,7 @@ def test_dao_fetch_todays_stats_for_all_services_groups_correctly(notify_db_sess service2.active, service2.created_at, NotificationType.SMS, - NotificationType.CREATED, + NotificationStatus.CREATED, 1, ) in stats @@ -1517,7 +1517,7 @@ def test_dao_find_services_with_high_failure_rates(notify_db_session, fake_uuid) create_notification(template, status=NotificationStatus.PERMANENT_FAILURE) create_notification(template, status=NotificationStatus.DELIVERED) create_notification(template, status=NotificationStatus.SENDING) - create_notification(template, status=NotificationStatus.TEMPORART_FAILURE) + create_notification(template, status=NotificationStatus.TEMPORARY_FAILURE) service_6 = create_service(service_name="Service 6") with freeze_time("2019-11-30 15:00:00.000000"): @@ -1538,7 +1538,7 @@ def test_dao_find_services_with_high_failure_rates(notify_db_session, fake_uuid) ) # test key type is excluded create_notification( template_2, - status=NotificationStatus.PERMANET_FAILURE, + status=NotificationStatus.PERMANENT_FAILURE, ) # below threshold is excluded start_date = datetime.utcnow() - timedelta(days=1) diff --git a/tests/app/dao/test_users_dao.py b/tests/app/dao/test_users_dao.py index 91c52c170..57ed65619 100644 --- a/tests/app/dao/test_users_dao.py +++ b/tests/app/dao/test_users_dao.py @@ -1,6 +1,5 @@ import uuid from datetime import datetime, timedelta -from types import CodeType import pytest from freezegun import freeze_time @@ -25,7 +24,7 @@ from app.dao.users_dao import ( update_user_password, user_can_be_archived, ) -from app.enums import AuthType, PermissionType +from app.enums import AuthType, CodeType, PermissionType from app.errors import InvalidRequest from app.models import User, VerifyCode from tests.app.db import ( diff --git a/tests/app/email_branding/test_rest.py b/tests/app/email_branding/test_rest.py index 0b68e9a4d..08b65ede4 100644 --- a/tests/app/email_branding/test_rest.py +++ b/tests/app/email_branding/test_rest.py @@ -247,7 +247,7 @@ def test_create_email_branding_reject_invalid_brand_type(admin_request): assert ( response["errors"][0]["message"] - == "brand_type NOT A TYPE is not one of [org, both, org_banner]" + == f"brand_type NOT A TYPE is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in BrandType])}]" ) @@ -265,5 +265,5 @@ def test_update_email_branding_reject_invalid_brand_type( assert ( response["errors"][0]["message"] - == "brand_type NOT A TYPE is not one of [org, both, org_banner]" + == f"brand_type NOT A TYPE is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in BrandType])}]" ) diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index 56c5e8e33..f2d9cabb8 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -505,7 +505,8 @@ def test_check_service_over_api_rate_limit_when_exceed_rate_limit_request_fails_ ) assert e.value.status_code == 429 assert e.value.message == ( - f"Exceeded rate limit for key type {key_type.upper()} of " + f"Exceeded rate limit for key type " + f"{key_type.name if key_type != KeyType.NORMAL else 'LIVE'} of " f"{sample_service.rate_limit} requests per {60} seconds" ) assert e.value.fields == [] @@ -636,7 +637,7 @@ def test_check_service_email_reply_to_id_where_service_id_is_not_found( ) assert e.value.status_code == 400 assert e.value.message == ( - f"email_reply_to_id {reply_to_address.id} does not exist in database for i" + f"email_reply_to_id {reply_to_address.id} does not exist in database for " f"service id {fake_uuid}" ) From c407d61d603e5c3cd512a97f3c6441eb69bcdfe9 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 27 Feb 2024 10:24:20 -0500 Subject: [PATCH 212/259] Cleaning & debugging Signed-off-by: Cliff Hill --- app/performance_dashboard/rest.py | 17 ++++++++++---- .../notification_dao/test_notification_dao.py | 5 +++- tests/app/email_branding/test_rest.py | 11 ++++++--- .../test_service_data_retention_rest.py | 8 ++++++- .../notifications/test_get_notifications.py | 10 ++++++-- .../test_notification_schemas.py | 23 +++++++++++++------ tests/app/v2/templates/test_get_templates.py | 5 +++- .../v2/templates/test_templates_schemas.py | 6 ++--- 8 files changed, 62 insertions(+), 23 deletions(-) diff --git a/app/performance_dashboard/rest.py b/app/performance_dashboard/rest.py index 747c8964d..f7f2f4a07 100644 --- a/app/performance_dashboard/rest.py +++ b/app/performance_dashboard/rest.py @@ -32,18 +32,25 @@ def get_performance_dashboard(): today = str(datetime.utcnow().date()) start_date = datetime.strptime( - request.args.get("start_date", today), "%Y-%m-%d", + request.args.get("start_date", today), + "%Y-%m-%d", + ).date() + end_date = datetime.strptime( + request.args.get("end_date", today), + "%Y-%m-%d", ).date() - end_date = datetime.strptime(request.args.get("end_date", today), "%Y-%m-%d",).date() total_for_all_time = get_total_notifications_for_date_range( - start_date=None, end_date=None, + start_date=None, + end_date=None, ) total_notifications, emails, sms = transform_results_into_totals(total_for_all_time) totals_for_date_range = get_total_notifications_for_date_range( - start_date=start_date, end_date=end_date, + start_date=start_date, + end_date=end_date, ) processing_time_results = get_processing_time_percentage_for_date_range( - start_date=start_date, end_date=end_date, + start_date=start_date, + end_date=end_date, ) services = get_live_services_with_organization() stats = { diff --git a/tests/app/dao/notification_dao/test_notification_dao.py b/tests/app/dao/notification_dao/test_notification_dao.py index feb380568..e0ca6cd47 100644 --- a/tests/app/dao/notification_dao/test_notification_dao.py +++ b/tests/app/dao/notification_dao/test_notification_dao.py @@ -644,7 +644,10 @@ def test_get_all_notifications_for_job_by_status(sample_job): assert len(notifications(filter_dict={"status": status}).items) == 1 - assert len(notifications(filter_dict={"status": list(NotificationStatus)[:3]}).items) == 3 + assert ( + len(notifications(filter_dict={"status": list(NotificationStatus)[:3]}).items) + == 3 + ) def test_dao_get_notification_count_for_job_id(notify_db_session): diff --git a/tests/app/email_branding/test_rest.py b/tests/app/email_branding/test_rest.py index 08b65ede4..b406ec8be 100644 --- a/tests/app/email_branding/test_rest.py +++ b/tests/app/email_branding/test_rest.py @@ -244,10 +244,12 @@ def test_create_email_branding_reject_invalid_brand_type(admin_request): response = admin_request.post( "email_branding.create_email_branding", _data=data, _expected_status=400 ) - + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in BrandType] + ) assert ( response["errors"][0]["message"] - == f"brand_type NOT A TYPE is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in BrandType])}]" + == f"brand_type NOT A TYPE is not one of [{type_str}]" ) @@ -263,7 +265,10 @@ def test_update_email_branding_reject_invalid_brand_type( email_branding_id=email_branding.id, ) + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in BrandType] + ) assert ( response["errors"][0]["message"] - == f"brand_type NOT A TYPE is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in BrandType])}]" + == f"brand_type NOT A TYPE is not one of [{type_str}]" ) diff --git a/tests/app/service/test_service_data_retention_rest.py b/tests/app/service/test_service_data_retention_rest.py index 9e429736d..f0cff358c 100644 --- a/tests/app/service/test_service_data_retention_rest.py +++ b/tests/app/service/test_service_data_retention_rest.py @@ -127,9 +127,15 @@ def test_create_service_data_retention_returns_400_when_notification_type_is_inv json_resp = json.loads(response.get_data(as_text=True)) assert response.status_code == 400 assert json_resp["errors"][0]["error"] == "ValidationError" + type_str = ", ".join( + [ + f"<{type(e).__name__}.{e.name}: {e.value}>" + for e in (NotificationType.SMS, NotificationType.EMAIL) + ] + ) assert ( json_resp["errors"][0]["message"] - == f"notification_type unknown is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in (NotificationType.SMS, NotificationType.EMAIL)])}]" + == f"notification_type unknown is not one of [{type_str}]" ) diff --git a/tests/app/v2/notifications/test_get_notifications.py b/tests/app/v2/notifications/test_get_notifications.py index 695302f15..5941c73bf 100644 --- a/tests/app/v2/notifications/test_get_notifications.py +++ b/tests/app/v2/notifications/test_get_notifications.py @@ -408,9 +408,12 @@ def test_get_all_notifications_filter_by_template_type_invalid_template_type( assert json_response["status_code"] == 400 assert len(json_response["errors"]) == 1 + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in TemplateType] + ) assert ( json_response["errors"][0]["message"] - == f"template_type orange is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" + == f"template_type orange is not one of [{type_str}]" ) @@ -461,9 +464,12 @@ def test_get_all_notifications_filter_by_status_invalid_status( assert json_response["status_code"] == 400 assert len(json_response["errors"]) == 1 + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in NotificationStatus] + ) assert ( json_response["errors"][0]["message"] - == f"status elephant is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in NotificationStatus])}]" + == f"status elephant is not one of [{type_str}]" ) diff --git a/tests/app/v2/notifications/test_notification_schemas.py b/tests/app/v2/notifications/test_notification_schemas.py index b3b0d6dc4..253faeaae 100644 --- a/tests/app/v2/notifications/test_notification_schemas.py +++ b/tests/app/v2/notifications/test_notification_schemas.py @@ -43,7 +43,10 @@ def test_get_notifications_valid_json(input): ], ) def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_statuses): - partial_error_status = f"is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in NotificationStatus])}]" + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in NotificationStatus] + ) + partial_error_status = f"is not one of [{type_str}]" with pytest.raises(ValidationError) as e: validate( @@ -70,7 +73,10 @@ def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_stat def test_get_notifications_request_invalid_template_types( invalid_template_types, valid_template_types ): - partial_error_template_type = f"is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in TemplateType] + ) + partial_error_template_type = f"is not one of [{type_str}]" with pytest.raises(ValidationError) as e: validate( @@ -101,15 +107,18 @@ def test_get_notifications_request_invalid_statuses_and_template_types(): assert len(errors) == 4 error_messages = [error["message"] for error in errors] + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in NotificationStatus] + ) for invalid_status in ["elephant", "giraffe"]: - assert ( - f"status {invalid_status} is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in NotificationStatus])}]" - in error_messages - ) + assert f"status {invalid_status} is not one of [{type_str}]" in error_messages + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in TemplateType] + ) for invalid_template_type in ["orange", "avocado"]: assert ( - f"template_type {invalid_template_type} is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" + f"template_type {invalid_template_type} is not one of [{type_str}]" in error_messages ) diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index c43120754..f2b7d11a0 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -116,11 +116,14 @@ def test_get_all_templates_for_invalid_type_returns_400(client, sample_service): json_response = json.loads(response.get_data(as_text=True)) + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in TemplateType] + ) assert json_response == { "status_code": 400, "errors": [ { - "message": f"type coconut is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]", + "message": f"type coconut is not one of [{type_str}]", "error": "ValidationError", } ], diff --git a/tests/app/v2/templates/test_templates_schemas.py b/tests/app/v2/templates/test_templates_schemas.py index 74006be62..418770f5d 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -278,10 +278,10 @@ def test_get_all_template_request_schema_against_invalid_args_is_invalid(templat assert errors["status_code"] == 400 assert len(errors["errors"]) == 1 - assert ( - errors["errors"][0]["message"] - == f"type unknown is not one of [{', '.join([f'<{type(e).__name__}.{e.name}: {e.value}>'for e in TemplateType])}]" + type_str = ", ".join( + [f"<{type(e).__name__}.{e.name}: {e.value}>" for e in TemplateType] ) + assert errors["errors"][0]["message"] == f"type unknown is not one of [{type_str}]" @pytest.mark.parametrize("response", valid_json_get_all_response) From a8cf49e995c5a61ff0bc8cc7fa2d03ed13d27989 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 28 Feb 2024 09:28:19 -0500 Subject: [PATCH 213/259] Fixed another error. Signed-off-by: Cliff Hill --- app/job/rest.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/job/rest.py b/app/job/rest.py index 4893baa9d..3e239f14d 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -127,18 +127,30 @@ def get_jobs_by_service(service_id): except ValueError: errors = { "limit_days": [ - "{} is not an integer".format(request.args["limit_days"]) + f"{request.args['limit_days']} is not an integer" ] } raise InvalidRequest(errors, status_code=400) else: limit_days = None + valid_statuses = set(JobStatus) + statuses_arg = request.args.get("statuses", "") + if statuses_arg == "": + statuses = None + else: + statuses = [] + for x in statuses_arg.split(","): + status = x.strip() + if status in valid_statuses: + statuses.append(status) + else: + statuses.append(None) return jsonify( **get_paginated_jobs( service_id, limit_days=limit_days, - statuses=[x.strip() for x in request.args.get("statuses", "").split(",")], + statuses=statuses, page=int(request.args.get("page", 1)), ) ) From c47501c634205e9bbf01bcb5f1a51c95880c5132 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 28 Feb 2024 10:27:29 -0500 Subject: [PATCH 214/259] Fixed test. Signed-off-by: Cliff Hill --- tests/app/service/test_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index d15351188..9a78848ab 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -741,7 +741,7 @@ def test_cant_update_service_org_type_to_random_value(client, sample_service): data=json.dumps(data), headers=[("Content-Type", "application/json"), auth_header], ) - assert resp.status_code == 500 + assert resp.status_code == 400 def test_update_service_remove_email_branding( From 5bab443522d35b92c263dfbc6a25bf3e542bf85c Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 28 Feb 2024 12:08:54 -0500 Subject: [PATCH 215/259] Final test fix Signed-off-by: Cliff Hill --- app/performance_dashboard/rest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/performance_dashboard/rest.py b/app/performance_dashboard/rest.py index f7f2f4a07..73b45bd6e 100644 --- a/app/performance_dashboard/rest.py +++ b/app/performance_dashboard/rest.py @@ -85,7 +85,8 @@ def transform_results_into_totals(total_notifications_results): def transform_into_notification_by_type_json(total_notifications): j = [] for x in total_notifications: - j.append({"date": x.local_date, "emails": x.emails, "sms": x.sms}) + j.append({"date": x.local_date.strftime("%Y-%m-%d"), "emails": x.emails, "sms": x.sms}) + return j From c49e1fa5f00319ed38774985a6b52c3201028298 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 28 Feb 2024 13:08:51 -0500 Subject: [PATCH 216/259] Updated and fixed poetry.lock. Signed-off-by: Cliff Hill --- poetry.lock | 2601 ++++++++++++++++++++++++++------------------------- 1 file changed, 1312 insertions(+), 1289 deletions(-) diff --git a/poetry.lock b/poetry.lock index be7abff41..8f8b2f11f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,87 +2,87 @@ [[package]] name = "aiohttp" -version = "3.9.2" +version = "3.9.3" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:772fbe371788e61c58d6d3d904268e48a594ba866804d08c995ad71b144f94cb"}, - {file = "aiohttp-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:edd4f1af2253f227ae311ab3d403d0c506c9b4410c7fc8d9573dec6d9740369f"}, - {file = "aiohttp-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cfee9287778399fdef6f8a11c9e425e1cb13cc9920fd3a3df8f122500978292b"}, - {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc158466f6a980a6095ee55174d1de5730ad7dec251be655d9a6a9dd7ea1ff9"}, - {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54ec82f45d57c9a65a1ead3953b51c704f9587440e6682f689da97f3e8defa35"}, - {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abeb813a18eb387f0d835ef51f88568540ad0325807a77a6e501fed4610f864e"}, - {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc91d07280d7d169f3a0f9179d8babd0ee05c79d4d891447629ff0d7d8089ec2"}, - {file = "aiohttp-3.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b65e861f4bebfb660f7f0f40fa3eb9f2ab9af10647d05dac824390e7af8f75b7"}, - {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:04fd8ffd2be73d42bcf55fd78cde7958eeee6d4d8f73c3846b7cba491ecdb570"}, - {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3d8d962b439a859b3ded9a1e111a4615357b01620a546bc601f25b0211f2da81"}, - {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:8ceb658afd12b27552597cf9a65d9807d58aef45adbb58616cdd5ad4c258c39e"}, - {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0e4ee4df741670560b1bc393672035418bf9063718fee05e1796bf867e995fad"}, - {file = "aiohttp-3.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2dec87a556f300d3211decf018bfd263424f0690fcca00de94a837949fbcea02"}, - {file = "aiohttp-3.9.2-cp310-cp310-win32.whl", hash = "sha256:3e1a800f988ce7c4917f34096f81585a73dbf65b5c39618b37926b1238cf9bc4"}, - {file = "aiohttp-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:ea510718a41b95c236c992b89fdfc3d04cc7ca60281f93aaada497c2b4e05c46"}, - {file = "aiohttp-3.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6aaa6f99256dd1b5756a50891a20f0d252bd7bdb0854c5d440edab4495c9f973"}, - {file = "aiohttp-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a27d8c70ad87bcfce2e97488652075a9bdd5b70093f50b10ae051dfe5e6baf37"}, - {file = "aiohttp-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:54287bcb74d21715ac8382e9de146d9442b5f133d9babb7e5d9e453faadd005e"}, - {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb3d05569aa83011fcb346b5266e00b04180105fcacc63743fc2e4a1862a891"}, - {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8534e7d69bb8e8d134fe2be9890d1b863518582f30c9874ed7ed12e48abe3c4"}, - {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bd9d5b989d57b41e4ff56ab250c5ddf259f32db17159cce630fd543376bd96b"}, - {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa6904088e6642609981f919ba775838ebf7df7fe64998b1a954fb411ffb4663"}, - {file = "aiohttp-3.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bda42eb410be91b349fb4ee3a23a30ee301c391e503996a638d05659d76ea4c2"}, - {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:193cc1ccd69d819562cc7f345c815a6fc51d223b2ef22f23c1a0f67a88de9a72"}, - {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b9f1cb839b621f84a5b006848e336cf1496688059d2408e617af33e3470ba204"}, - {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:d22a0931848b8c7a023c695fa2057c6aaac19085f257d48baa24455e67df97ec"}, - {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4112d8ba61fbd0abd5d43a9cb312214565b446d926e282a6d7da3f5a5aa71d36"}, - {file = "aiohttp-3.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c4ad4241b52bb2eb7a4d2bde060d31c2b255b8c6597dd8deac2f039168d14fd7"}, - {file = "aiohttp-3.9.2-cp311-cp311-win32.whl", hash = "sha256:ee2661a3f5b529f4fc8a8ffee9f736ae054adfb353a0d2f78218be90617194b3"}, - {file = "aiohttp-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:4deae2c165a5db1ed97df2868ef31ca3cc999988812e82386d22937d9d6fed52"}, - {file = "aiohttp-3.9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6f4cdba12539215aaecf3c310ce9d067b0081a0795dd8a8805fdb67a65c0572a"}, - {file = "aiohttp-3.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:84e843b33d5460a5c501c05539809ff3aee07436296ff9fbc4d327e32aa3a326"}, - {file = "aiohttp-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8008d0f451d66140a5aa1c17e3eedc9d56e14207568cd42072c9d6b92bf19b52"}, - {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61c47ab8ef629793c086378b1df93d18438612d3ed60dca76c3422f4fbafa792"}, - {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc71f748e12284312f140eaa6599a520389273174b42c345d13c7e07792f4f57"}, - {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1c3a4d0ab2f75f22ec80bca62385db2e8810ee12efa8c9e92efea45c1849133"}, - {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a87aa0b13bbee025faa59fa58861303c2b064b9855d4c0e45ec70182bbeba1b"}, - {file = "aiohttp-3.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2cc0d04688b9f4a7854c56c18aa7af9e5b0a87a28f934e2e596ba7e14783192"}, - {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1956e3ac376b1711c1533266dec4efd485f821d84c13ce1217d53e42c9e65f08"}, - {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:114da29f39eccd71b93a0fcacff178749a5c3559009b4a4498c2c173a6d74dff"}, - {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3f17999ae3927d8a9a823a1283b201344a0627272f92d4f3e3a4efe276972fe8"}, - {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:f31df6a32217a34ae2f813b152a6f348154f948c83213b690e59d9e84020925c"}, - {file = "aiohttp-3.9.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7a75307ffe31329928a8d47eae0692192327c599113d41b278d4c12b54e1bd11"}, - {file = "aiohttp-3.9.2-cp312-cp312-win32.whl", hash = "sha256:972b63d589ff8f305463593050a31b5ce91638918da38139b9d8deaba9e0fed7"}, - {file = "aiohttp-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:200dc0246f0cb5405c80d18ac905c8350179c063ea1587580e3335bfc243ba6a"}, - {file = "aiohttp-3.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:158564d0d1020e0d3fe919a81d97aadad35171e13e7b425b244ad4337fc6793a"}, - {file = "aiohttp-3.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:da1346cd0ccb395f0ed16b113ebb626fa43b7b07fd7344fce33e7a4f04a8897a"}, - {file = "aiohttp-3.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:eaa9256de26ea0334ffa25f1913ae15a51e35c529a1ed9af8e6286dd44312554"}, - {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1543e7fb00214fb4ccead42e6a7d86f3bb7c34751ec7c605cca7388e525fd0b4"}, - {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:186e94570433a004e05f31f632726ae0f2c9dee4762a9ce915769ce9c0a23d89"}, - {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d52d20832ac1560f4510d68e7ba8befbc801a2b77df12bd0cd2bcf3b049e52a4"}, - {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c45e4e815ac6af3b72ca2bde9b608d2571737bb1e2d42299fc1ffdf60f6f9a1"}, - {file = "aiohttp-3.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa906b9bdfd4a7972dd0628dbbd6413d2062df5b431194486a78f0d2ae87bd55"}, - {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:68bbee9e17d66f17bb0010aa15a22c6eb28583edcc8b3212e2b8e3f77f3ebe2a"}, - {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4c189b64bd6d9a403a1a3f86a3ab3acbc3dc41a68f73a268a4f683f89a4dec1f"}, - {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8a7876f794523123bca6d44bfecd89c9fec9ec897a25f3dd202ee7fc5c6525b7"}, - {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d23fba734e3dd7b1d679b9473129cd52e4ec0e65a4512b488981a56420e708db"}, - {file = "aiohttp-3.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b141753be581fab842a25cb319f79536d19c2a51995d7d8b29ee290169868eab"}, - {file = "aiohttp-3.9.2-cp38-cp38-win32.whl", hash = "sha256:103daf41ff3b53ba6fa09ad410793e2e76c9d0269151812e5aba4b9dd674a7e8"}, - {file = "aiohttp-3.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:328918a6c2835861ff7afa8c6d2c70c35fdaf996205d5932351bdd952f33fa2f"}, - {file = "aiohttp-3.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5264d7327c9464786f74e4ec9342afbbb6ee70dfbb2ec9e3dfce7a54c8043aa3"}, - {file = "aiohttp-3.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07205ae0015e05c78b3288c1517afa000823a678a41594b3fdc870878d645305"}, - {file = "aiohttp-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0a1e638cffc3ec4d4784b8b4fd1cf28968febc4bd2718ffa25b99b96a741bd"}, - {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d43302a30ba1166325974858e6ef31727a23bdd12db40e725bec0f759abce505"}, - {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16a967685907003765855999af11a79b24e70b34dc710f77a38d21cd9fc4f5fe"}, - {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fa3ee92cd441d5c2d07ca88d7a9cef50f7ec975f0117cd0c62018022a184308"}, - {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b500c5ad9c07639d48615a770f49618130e61be36608fc9bc2d9bae31732b8f"}, - {file = "aiohttp-3.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c07327b368745b1ce2393ae9e1aafed7073d9199e1dcba14e035cc646c7941bf"}, - {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cc7d6502c23a0ec109687bf31909b3fb7b196faf198f8cff68c81b49eb316ea9"}, - {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:07be2be7071723c3509ab5c08108d3a74f2181d4964e869f2504aaab68f8d3e8"}, - {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:122468f6fee5fcbe67cb07014a08c195b3d4c41ff71e7b5160a7bcc41d585a5f"}, - {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:00a9abcea793c81e7f8778ca195a1714a64f6d7436c4c0bb168ad2a212627000"}, - {file = "aiohttp-3.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a9825fdd64ecac5c670234d80bb52bdcaa4139d1f839165f548208b3779c6c6"}, - {file = "aiohttp-3.9.2-cp39-cp39-win32.whl", hash = "sha256:5422cd9a4a00f24c7244e1b15aa9b87935c85fb6a00c8ac9b2527b38627a9211"}, - {file = "aiohttp-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:7d579dcd5d82a86a46f725458418458fa43686f6a7b252f2966d359033ffc8ab"}, - {file = "aiohttp-3.9.2.tar.gz", hash = "sha256:b0ad0a5e86ce73f5368a164c10ada10504bf91869c05ab75d982c6048217fbf7"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, + {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, + {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, + {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, + {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, + {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, + {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, + {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, + {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, + {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, + {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, + {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, ] [package.dependencies] @@ -186,21 +186,22 @@ files = [ [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "awscli" @@ -223,55 +224,61 @@ s3transfer = ">=0.7.0,<0.8.0" [[package]] name = "bandit" -version = "1.7.6" +version = "1.7.7" description = "Security oriented static analyser for python code." optional = false python-versions = ">=3.8" files = [ - {file = "bandit-1.7.6-py3-none-any.whl", hash = "sha256:36da17c67fc87579a5d20c323c8d0b1643a890a2b93f00b3d1229966624694ff"}, - {file = "bandit-1.7.6.tar.gz", hash = "sha256:72ce7bc9741374d96fb2f1c9a8960829885f1243ffde743de70a19cee353e8f3"}, + {file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"}, + {file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"}, ] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=3.1.30" PyYAML = ">=5.3.1" rich = "*" stevedore = ">=1.20.0" [package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +baseline = ["GitPython (>=3.1.30)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] toml = ["tomli (>=1.1.0)"] yaml = ["PyYAML"] [[package]] name = "bcrypt" -version = "4.0.1" +version = "4.1.2" description = "Modern password hashing for your software and your servers" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, - {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, - {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, - {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, - {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, - {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, + {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"}, + {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"}, + {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"}, + {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"}, + {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"}, + {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"}, + {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"}, + {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"}, + {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"}, + {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"}, + {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"}, + {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"}, + {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"}, ] [package.extras] @@ -463,22 +470,22 @@ virtualenv = ["virtualenv (>=20.0.35)"] [[package]] name = "cachecontrol" -version = "0.13.1" +version = "0.14.0" description = "httplib2 caching for requests" optional = false python-versions = ">=3.7" files = [ - {file = "cachecontrol-0.13.1-py3-none-any.whl", hash = "sha256:95dedbec849f46dda3137866dc28b9d133fc9af55f5b805ab1291833e4457aa4"}, - {file = "cachecontrol-0.13.1.tar.gz", hash = "sha256:f012366b79d2243a6118309ce73151bf52a38d4a5dac8ea57f09bd29087e506b"}, + {file = "cachecontrol-0.14.0-py3-none-any.whl", hash = "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0"}, + {file = "cachecontrol-0.14.0.tar.gz", hash = "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938"}, ] [package.dependencies] filelock = {version = ">=3.8.0", optional = true, markers = "extra == \"filecache\""} -msgpack = ">=0.5.2" +msgpack = ">=0.5.2,<2.0.0" requests = ">=2.16.0" [package.extras] -dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "mypy", "pytest", "pytest-cov", "sphinx", "tox", "types-redis", "types-requests"] +dev = ["CacheControl[filecache,redis]", "black", "build", "cherrypy", "furo", "mypy", "pytest", "pytest-cov", "sphinx", "sphinx-copybutton", "tox", "types-redis", "types-requests"] filecache = ["filelock (>=3.8.0)"] redis = ["redis (>=2.10.5)"] @@ -831,13 +838,13 @@ testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] [[package]] name = "cloudfoundry-client" -version = "1.35.2" +version = "1.36.0" description = "A client library for CloudFoundry" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "cloudfoundry-client-1.35.2.tar.gz", hash = "sha256:dd0f89d595ca2511ee98a2410146381630bc90a1206c206512df80089b2d2eb4"}, - {file = "cloudfoundry_client-1.35.2-py3-none-any.whl", hash = "sha256:1c110189fe0f511a1b53948e9ec2ea9343ac1b6a19092f12a8f401b1d8a9f73d"}, + {file = "cloudfoundry-client-1.36.0.tar.gz", hash = "sha256:9d087ad114ee68b2153917a03a7a724513f17a3c1299f2957a4ace109b1665bd"}, + {file = "cloudfoundry_client-1.36.0-py3-none-any.whl", hash = "sha256:790396326d6af17728a69495a1921865bf5cdf91bac173ec2157a1a02e774e96"}, ] [package.dependencies] @@ -847,7 +854,7 @@ polling2 = "0.5.0" protobuf = ">=3.20.0,<5.0.0dev" PyYAML = ">=6.0" requests = ">=2.5.0" -websocket-client = ">=1.6.1,<1.7.0" +websocket-client = ">=1.7.0,<1.8.0" [[package]] name = "colorama" @@ -862,63 +869,63 @@ files = [ [[package]] name = "coverage" -version = "7.3.2" +version = "7.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, - {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, - {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, - {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, - {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, - {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, - {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, - {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, - {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, - {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, - {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, - {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, - {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, - {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.dependencies] @@ -940,43 +947,43 @@ files = [ [[package]] name = "cryptography" -version = "42.0.4" +version = "42.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449"}, - {file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18"}, - {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2"}, - {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1"}, - {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b"}, - {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1"}, - {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992"}, - {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885"}, - {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824"}, - {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b"}, - {file = "cryptography-42.0.4-cp37-abi3-win32.whl", hash = "sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925"}, - {file = "cryptography-42.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923"}, - {file = "cryptography-42.0.4-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7"}, - {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52"}, - {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a"}, - {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9"}, - {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764"}, - {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff"}, - {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257"}, - {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929"}, - {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0"}, - {file = "cryptography-42.0.4-cp39-abi3-win32.whl", hash = "sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129"}, - {file = "cryptography-42.0.4-cp39-abi3-win_amd64.whl", hash = "sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854"}, - {file = "cryptography-42.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298"}, - {file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88"}, - {file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20"}, - {file = "cryptography-42.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce"}, - {file = "cryptography-42.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74"}, - {file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd"}, - {file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b"}, - {file = "cryptography-42.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660"}, - {file = "cryptography-42.0.4.tar.gz", hash = "sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, + {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, + {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, + {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, + {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, + {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, + {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, + {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, + {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, + {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, + {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, + {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, + {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, + {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, ] [package.dependencies] @@ -994,19 +1001,19 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "cyclonedx-python-lib" -version = "6.3.0" +version = "6.4.1" description = "Python library for CycloneDX" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "cyclonedx_python_lib-6.3.0-py3-none-any.whl", hash = "sha256:0e73c1036c2f7fc67adc28aef807e6b44340ea70202aab197fb06b20ea165de8"}, - {file = "cyclonedx_python_lib-6.3.0.tar.gz", hash = "sha256:82f2489de3c0cadad5af1ad7fa6b6a185f985746370245d38769699c734533c6"}, + {file = "cyclonedx_python_lib-6.4.1-py3-none-any.whl", hash = "sha256:42d50052c4604e8d6a91753e51bca33d668fb82adc1aab3f4eb54b89fa61cbc0"}, + {file = "cyclonedx_python_lib-6.4.1.tar.gz", hash = "sha256:aca5d8cf10f8d8420ba621e0cf4a24b98708afb68ca2ca72d7f2cc6394c75681"}, ] [package.dependencies] license-expression = ">=30,<31" -packageurl-python = ">=0.11" -py-serializable = ">=0.16,<0.18" +packageurl-python = ">=0.11,<2" +py-serializable = ">=0.16,<2" sortedcontainers = ">=2.4.0,<3.0.0" [package.extras] @@ -1044,33 +1051,34 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] [[package]] name = "distlib" -version = "0.3.7" +version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] name = "dnspython" -version = "2.4.2" +version = "2.6.1" description = "DNS toolkit" optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.8" files = [ - {file = "dnspython-2.4.2-py3-none-any.whl", hash = "sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8"}, - {file = "dnspython-2.4.2.tar.gz", hash = "sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984"}, + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, ] [package.extras] -dnssec = ["cryptography (>=2.6,<42.0)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.24.1)"] -doq = ["aioquic (>=0.9.20)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.23)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] [[package]] name = "docopt" @@ -1095,67 +1103,80 @@ files = [ [[package]] name = "dulwich" -version = "0.21.6" +version = "0.21.7" description = "Python Git Library" optional = false python-versions = ">=3.7" files = [ - {file = "dulwich-0.21.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7f89bee4c97372e8aaf8ffaf5899f1bcd5184b5306d7eaf68738c1101ceba10e"}, - {file = "dulwich-0.21.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:847bb52562a211b596453a602e75739350c86d7edb846b5b1c46896a5c86b9bb"}, - {file = "dulwich-0.21.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e09d0b4e985b371aa6728773781b19298d361a00772e20f98522868cf7edc6f"}, - {file = "dulwich-0.21.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfb50b3915e223a97f50fbac0dbc298d5fffeaac004eeeb3d552c57fe38416f"}, - {file = "dulwich-0.21.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a64eca1601e79c16df78afe08da9ac9497b934cbc5765990ca7d89a4b87453d9"}, - {file = "dulwich-0.21.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fedd924763a5d640348db43a267a394aa80d551228ad45708e0b0cc2130bb62"}, - {file = "dulwich-0.21.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:edc21c3784dd9d9b85abd9fe53f81a884e2cdcc4e5e09ada17287420d64cfd46"}, - {file = "dulwich-0.21.6-cp310-cp310-win32.whl", hash = "sha256:daa3584beabfcf0da76df57535a23c80ff6d8ccde6ddbd23bdc79d317a0e20a7"}, - {file = "dulwich-0.21.6-cp310-cp310-win_amd64.whl", hash = "sha256:40623cc39a3f1634663d22d87f86e2e406cc8ff17ae7a3edc7fcf963c288992f"}, - {file = "dulwich-0.21.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e8ed878553f0b76facbb620b455fafa0943162fe8e386920717781e490444efa"}, - {file = "dulwich-0.21.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a89b19f4960e759915dbc23a4dd0abc067b55d8d65e9df50961b73091b87b81a"}, - {file = "dulwich-0.21.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28acbd08d6b38720d99cc01da9dd307a2e0585e00436c95bcac6357b9a9a6f76"}, - {file = "dulwich-0.21.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2f2683e0598f7c7071ef08a0822f062d8744549a0d45f2c156741033b7e3d7d"}, - {file = "dulwich-0.21.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54342cf96fe8a44648505c65f23d18889595762003a168d67d7263df66143bd2"}, - {file = "dulwich-0.21.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2a3fc071e5b14f164191286f7ffc02f60fe8b439d01fad0832697cc08c2237dd"}, - {file = "dulwich-0.21.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32d7acfe3fe2ce4502446d8f7a5ab34cfd24c9ff8961e60337638410906a8fbb"}, - {file = "dulwich-0.21.6-cp311-cp311-win32.whl", hash = "sha256:5e58171a5d70f7910f73d25ff82a058edff09a4c1c3bd1de0dc6b1fbc9a42c3e"}, - {file = "dulwich-0.21.6-cp311-cp311-win_amd64.whl", hash = "sha256:ceabe8f96edfb9183034a860f5dc77586700b517457032867b64a03c44e5cf96"}, - {file = "dulwich-0.21.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4fdc2f081bc3e9e120079c2cea4be213e3f127335aca7c0ab0c19fe791270caa"}, - {file = "dulwich-0.21.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fe957564108f74325d0d042d85e0c67ef470921ca92b6e7d330c7c49a3b9c1d"}, - {file = "dulwich-0.21.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2912c8a845c8ccbc79d068a89db7172e355adeb84eb31f062cd3a406d528b30"}, - {file = "dulwich-0.21.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:81e237a6b1b20c79ef62ca19a8fb231f5519bab874b9a1c2acf9c05edcabd600"}, - {file = "dulwich-0.21.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:513d045e74307eeb31592255c38f37042c9aa68ce845a167943018ab5138b0e3"}, - {file = "dulwich-0.21.6-cp37-cp37m-win32.whl", hash = "sha256:e1ac882afa890ef993b8502647e6c6d2b3977ce56e3fe80058ce64607cbc7107"}, - {file = "dulwich-0.21.6-cp37-cp37m-win_amd64.whl", hash = "sha256:5d2ccf3d355850674f75655154a6519bf1f1664176c670109fa7041019b286f9"}, - {file = "dulwich-0.21.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:28c9724a167c84a83fc6238e0781f4702b5fe8c53ede31604525fb1a9d1833f4"}, - {file = "dulwich-0.21.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c816be529680659b6a19798287b4ec6de49040f58160d40b1b2934fd6c28e93f"}, - {file = "dulwich-0.21.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b0545f0fa9444a0eb84977d08e302e3f55fd7c34a0466ec28bedc3c839b2fc1f"}, - {file = "dulwich-0.21.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b1682e8e826471ea3c22b8521435e93799e3db8ad05dd3c8f9b1aaacfa78147"}, - {file = "dulwich-0.21.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24ad45928a65f39ea0f451f9989b7aaedba9893d48c3189b544a70c6a1043f71"}, - {file = "dulwich-0.21.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1c9e55233f19cd19c484f607cd90ab578ac50ebfef607f77e3b35c2b6049470"}, - {file = "dulwich-0.21.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:18697b58e0fc5972de68b529b08ac9ddda3f39af27bcf3f6999635ed3da7ef68"}, - {file = "dulwich-0.21.6-cp38-cp38-win32.whl", hash = "sha256:22798e9ba59e32b8faff5d9067e2b5a308f6b0fba9b1e1e928571ad278e7b36c"}, - {file = "dulwich-0.21.6-cp38-cp38-win_amd64.whl", hash = "sha256:6c91e1ed20d3d9a6aaaed9e75adae37272b3fcbcc72bab1eb09574806da88563"}, - {file = "dulwich-0.21.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8b84450766a3b151c3676fec3e3ed76304e52a84d5d69ade0f34fff2782c1b41"}, - {file = "dulwich-0.21.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3da632648ee27b64bb5b285a3a94fddf297a596891cca12ac0df43c4f59448f"}, - {file = "dulwich-0.21.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cef50c0a19f322b7150248b8fa0862ce1652dec657e340c4020573721e85f215"}, - {file = "dulwich-0.21.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ac20dfcfd6057efb8499158d23f2c059f933aefa381e192100e6d8bc25d562"}, - {file = "dulwich-0.21.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81d10aa50c0a9a6dd495990c639358e3a3bbff39e17ff302179be6e93b573da7"}, - {file = "dulwich-0.21.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a9b52a08d49731375662936d05a12c4a64a6fe0ce257111f62638e475fb5d26d"}, - {file = "dulwich-0.21.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed2f1f638b9adfba862719693b371ffe5d58e94d552ace9a23dea0fb0db6f468"}, - {file = "dulwich-0.21.6-cp39-cp39-win32.whl", hash = "sha256:bf90f2f9328a82778cf85ab696e4a7926918c3f315c75fc432ba31346bfa89b7"}, - {file = "dulwich-0.21.6-cp39-cp39-win_amd64.whl", hash = "sha256:e0dee3840c3c72e1d60c8f87a7a715d8eac023b9e1b80199d97790f7a1c60d9c"}, - {file = "dulwich-0.21.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:32d3a35caad6879d04711b358b861142440a543f5f4e02df67b13cbcd57f84a6"}, - {file = "dulwich-0.21.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c04df87098053b7767b46fc04b7943d75443f91c73560ca50157cdc22e27a5d3"}, - {file = "dulwich-0.21.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e07f145c7b0d82a9f77d157f493a61900e913d1c1f8b1f40d07d919ffb0929a4"}, - {file = "dulwich-0.21.6-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:008ff08629ab16d3638a9f36cfc6f5bd74b4d594657f2dc1583d8d3201794571"}, - {file = "dulwich-0.21.6-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bf469cd5076623c2aad69d01ce9d5392fcb38a5faef91abe1501be733453e37d"}, - {file = "dulwich-0.21.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6592ef2d16ac61a27022647cf64a048f5be6e0a6ab2ebc7322bfbe24fb2b971b"}, - {file = "dulwich-0.21.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99577b2b37f64bc87280079245fb2963494c345d7db355173ecec7ab3d64b949"}, - {file = "dulwich-0.21.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d7cd9fb896c65e4c28cb9332f2be192817805978dd8dc299681c4fe83c631158"}, - {file = "dulwich-0.21.6-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9002094198e57e88fe77412d3aa64dd05978046ae725a16123ba621a7704628"}, - {file = "dulwich-0.21.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9b6f8a16f32190aa88c37ef013858b3e01964774bc983900bd0d74ecb6576e6"}, - {file = "dulwich-0.21.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee8aba4dec4d0a52737a8a141f3456229c87dcfd7961f8115786a27b6ebefed"}, - {file = "dulwich-0.21.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a780e2a0ff208c4f218e72eff8d13f9aff485ff9a6f3066c22abe4ec8cec7dcd"}, - {file = "dulwich-0.21.6.tar.gz", hash = "sha256:30fbe87e8b51f3813c131e2841c86d007434d160bd16db586b40d47f31dd05b0"}, + {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d4c0110798099bb7d36a110090f2688050703065448895c4f53ade808d889dd3"}, + {file = "dulwich-0.21.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bc12697f0918bee324c18836053644035362bb3983dc1b210318f2fed1d7132"}, + {file = "dulwich-0.21.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:471305af74790827fcbafe330fc2e8bdcee4fb56ca1177c8c481b1c8f806c4a4"}, + {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d54c9d0e845be26f65f954dff13a1cd3f2b9739820c19064257b8fd7435ab263"}, + {file = "dulwich-0.21.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12d61334a575474e707614f2e93d6ed4cdae9eb47214f9277076d9e5615171d3"}, + {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e274cebaf345f0b1e3b70197f2651de92b652386b68020cfd3bf61bc30f6eaaa"}, + {file = "dulwich-0.21.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:817822f970e196e757ae01281ecbf21369383285b9f4a83496312204cf889b8c"}, + {file = "dulwich-0.21.7-cp310-cp310-win32.whl", hash = "sha256:7836da3f4110ce684dcd53489015fb7fa94ed33c5276e3318b8b1cbcb5b71e08"}, + {file = "dulwich-0.21.7-cp310-cp310-win_amd64.whl", hash = "sha256:4a043b90958cec866b4edc6aef5fe3c2c96a664d0b357e1682a46f6c477273c4"}, + {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ce8db196e79c1f381469410d26fb1d8b89c6b87a4e7f00ff418c22a35121405c"}, + {file = "dulwich-0.21.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:62bfb26bdce869cd40be443dfd93143caea7089b165d2dcc33de40f6ac9d812a"}, + {file = "dulwich-0.21.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c01a735b9a171dcb634a97a3cec1b174cfbfa8e840156870384b633da0460f18"}, + {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa4d14767cf7a49c9231c2e52cb2a3e90d0c83f843eb6a2ca2b5d81d254cf6b9"}, + {file = "dulwich-0.21.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bca4b86e96d6ef18c5bc39828ea349efb5be2f9b1f6ac9863f90589bac1084d"}, + {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7b5624b02ef808cdc62dabd47eb10cd4ac15e8ac6df9e2e88b6ac6b40133673"}, + {file = "dulwich-0.21.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3a539b4696a42fbdb7412cb7b66a4d4d332761299d3613d90a642923c7560e1"}, + {file = "dulwich-0.21.7-cp311-cp311-win32.whl", hash = "sha256:675a612ce913081beb0f37b286891e795d905691dfccfb9bf73721dca6757cde"}, + {file = "dulwich-0.21.7-cp311-cp311-win_amd64.whl", hash = "sha256:460ba74bdb19f8d498786ae7776745875059b1178066208c0fd509792d7f7bfc"}, + {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4c51058ec4c0b45dc5189225b9e0c671b96ca9713c1daf71d622c13b0ab07681"}, + {file = "dulwich-0.21.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4bc4c5366eaf26dda3fdffe160a3b515666ed27c2419f1d483da285ac1411de0"}, + {file = "dulwich-0.21.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a0650ec77d89cb947e3e4bbd4841c96f74e52b4650830112c3057a8ca891dc2f"}, + {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f18f0a311fb7734b033a3101292b932158cade54b74d1c44db519e42825e5a2"}, + {file = "dulwich-0.21.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c589468e5c0cd84e97eb7ec209ab005a2cb69399e8c5861c3edfe38989ac3a8"}, + {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d62446797163317a397a10080c6397ffaaca51a7804c0120b334f8165736c56a"}, + {file = "dulwich-0.21.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e84cc606b1f581733df4350ca4070e6a8b30be3662bbb81a590b177d0c996c91"}, + {file = "dulwich-0.21.7-cp312-cp312-win32.whl", hash = "sha256:c3d1685f320907a52c40fd5890627945c51f3a5fa4bcfe10edb24fec79caadec"}, + {file = "dulwich-0.21.7-cp312-cp312-win_amd64.whl", hash = "sha256:6bd69921fdd813b7469a3c77bc75c1783cc1d8d72ab15a406598e5a3ba1a1503"}, + {file = "dulwich-0.21.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d8ab29c660125db52106775caa1f8f7f77a69ed1fe8bc4b42bdf115731a25bf"}, + {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0d2e4485b98695bf95350ce9d38b1bb0aaac2c34ad00a0df789aa33c934469b"}, + {file = "dulwich-0.21.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e138d516baa6b5bafbe8f030eccc544d0d486d6819b82387fc0e285e62ef5261"}, + {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f34bf9b9fa9308376263fd9ac43143c7c09da9bc75037bb75c6c2423a151b92c"}, + {file = "dulwich-0.21.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2e2c66888207b71cd1daa2acb06d3984a6bc13787b837397a64117aa9fc5936a"}, + {file = "dulwich-0.21.7-cp37-cp37m-win32.whl", hash = "sha256:10893105c6566fc95bc2a67b61df7cc1e8f9126d02a1df6a8b2b82eb59db8ab9"}, + {file = "dulwich-0.21.7-cp37-cp37m-win_amd64.whl", hash = "sha256:460b3849d5c3d3818a80743b4f7a0094c893c559f678e56a02fff570b49a644a"}, + {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74700e4c7d532877355743336c36f51b414d01e92ba7d304c4f8d9a5946dbc81"}, + {file = "dulwich-0.21.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c92e72c43c9e9e936b01a57167e0ea77d3fd2d82416edf9489faa87278a1cdf7"}, + {file = "dulwich-0.21.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d097e963eb6b9fa53266146471531ad9c6765bf390849230311514546ed64db2"}, + {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:808e8b9cc0aa9ac74870b49db4f9f39a52fb61694573f84b9c0613c928d4caf8"}, + {file = "dulwich-0.21.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1957b65f96e36c301e419d7adaadcff47647c30eb072468901bb683b1000bc5"}, + {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4b09bc3a64fb70132ec14326ecbe6e0555381108caff3496898962c4136a48c6"}, + {file = "dulwich-0.21.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5882e70b74ac3c736a42d3fdd4f5f2e6570637f59ad5d3e684760290b58f041"}, + {file = "dulwich-0.21.7-cp38-cp38-win32.whl", hash = "sha256:29bb5c1d70eba155ded41ed8a62be2f72edbb3c77b08f65b89c03976292f6d1b"}, + {file = "dulwich-0.21.7-cp38-cp38-win_amd64.whl", hash = "sha256:25c3ab8fb2e201ad2031ddd32e4c68b7c03cb34b24a5ff477b7a7dcef86372f5"}, + {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8929c37986c83deb4eb500c766ee28b6670285b512402647ee02a857320e377c"}, + {file = "dulwich-0.21.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc1e11be527ac06316539b57a7688bcb1b6a3e53933bc2f844397bc50734e9ae"}, + {file = "dulwich-0.21.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0fc3078a1ba04c588fabb0969d3530efd5cd1ce2cf248eefb6baf7cbc15fc285"}, + {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40dcbd29ba30ba2c5bfbab07a61a5f20095541d5ac66d813056c122244df4ac0"}, + {file = "dulwich-0.21.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8869fc8ec3dda743e03d06d698ad489b3705775fe62825e00fa95aa158097fc0"}, + {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d96ca5e0dde49376fbcb44f10eddb6c30284a87bd03bb577c59bb0a1f63903fa"}, + {file = "dulwich-0.21.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0064363bd5e814359657ae32517fa8001e8573d9d040bd997908d488ab886ed"}, + {file = "dulwich-0.21.7-cp39-cp39-win32.whl", hash = "sha256:869eb7be48243e695673b07905d18b73d1054a85e1f6e298fe63ba2843bb2ca1"}, + {file = "dulwich-0.21.7-cp39-cp39-win_amd64.whl", hash = "sha256:404b8edeb3c3a86c47c0a498699fc064c93fa1f8bab2ffe919e8ab03eafaaad3"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e598d743c6c0548ebcd2baf94aa9c8bfacb787ea671eeeb5828cfbd7d56b552f"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a2d76c96426e791556836ef43542b639def81be4f1d6d4322cd886c115eae1"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6c88acb60a1f4d31bd6d13bfba465853b3df940ee4a0f2a3d6c7a0778c705b7"}, + {file = "dulwich-0.21.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ecd315847dea406a4decfa39d388a2521e4e31acde3bd9c2609c989e817c6d62"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d05d3c781bc74e2c2a2a8f4e4e2ed693540fbe88e6ac36df81deac574a6dad99"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6de6f8de4a453fdbae8062a6faa652255d22a3d8bce0cd6d2d6701305c75f2b3"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e25953c7acbbe4e19650d0225af1c0c0e6882f8bddd2056f75c1cc2b109b88ad"}, + {file = "dulwich-0.21.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4637cbd8ed1012f67e1068aaed19fcc8b649bcf3e9e26649826a303298c89b9d"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:858842b30ad6486aacaa607d60bab9c9a29e7c59dc2d9cb77ae5a94053878c08"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739b191f61e1c4ce18ac7d520e7a7cbda00e182c3489552408237200ce8411ad"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:274c18ec3599a92a9b67abaf110e4f181a4f779ee1aaab9e23a72e89d71b2bd9"}, + {file = "dulwich-0.21.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2590e9b431efa94fc356ae33b38f5e64f1834ec3a94a6ac3a64283b206d07aa3"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed60d1f610ef6437586f7768254c2a93820ccbd4cfdac7d182cf2d6e615969bb"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8278835e168dd097089f9e53088c7a69c6ca0841aef580d9603eafe9aea8c358"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffc27fb063f740712e02b4d2f826aee8bbed737ed799962fef625e2ce56e2d29"}, + {file = "dulwich-0.21.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61e3451bd3d3844f2dca53f131982553be4d1b1e1ebd9db701843dd76c4dba31"}, + {file = "dulwich-0.21.7.tar.gz", hash = "sha256:a9e9c66833cea580c3ac12927e4b9711985d76afca98da971405d414de60e968"}, ] [package.dependencies] @@ -1229,13 +1250,13 @@ tests = ["coverage", "coveralls", "dill", "mock", "nose"] [[package]] name = "fastjsonschema" -version = "2.19.0" +version = "2.19.1" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.19.0-py3-none-any.whl", hash = "sha256:b9fd1a2dd6971dbc7fee280a95bd199ae0dd9ce22beb91cc75e9c1c528a5170e"}, - {file = "fastjsonschema-2.19.0.tar.gz", hash = "sha256:e25df6647e1bc4a26070b700897b07b542ec898dd4f1f6ea013e7f6a88417225"}, + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, ] [package.extras] @@ -1275,13 +1296,13 @@ pyflakes = ">=3.2.0,<3.3.0" [[package]] name = "flake8-bugbear" -version = "24.1.17" +version = "24.2.6" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-bugbear-24.1.17.tar.gz", hash = "sha256:bcb388a4f3b516258749b1e690ee394c082eff742f44595e3754cf5c7781c2c7"}, - {file = "flake8_bugbear-24.1.17-py3-none-any.whl", hash = "sha256:46cc840ddaed26507cd0ada530d1526418b717ee76c9b5dfdbd238b5eab34139"}, + {file = "flake8-bugbear-24.2.6.tar.gz", hash = "sha256:f9cb5f2a9e792dd80ff68e89a14c12eed8620af8b41a49d823b7a33064ac9658"}, + {file = "flake8_bugbear-24.2.6-py3-none-any.whl", hash = "sha256:663ef5de80cd32aacd39d362212983bc4636435a6f83700b4ed35acbd0b7d1b8"}, ] [package.dependencies] @@ -1429,72 +1450,88 @@ python-dateutil = ">=2.7" [[package]] name = "frozenlist" -version = "1.4.0" +version = "1.4.1" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.8" files = [ - {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, - {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, - {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, - {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, - {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, - {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, - {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, - {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, - {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, - {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, - {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, - {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, - {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, - {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, - {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, - {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, - {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, - {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, - {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, - {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, - {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, - {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, - {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] [[package]] @@ -1508,37 +1545,6 @@ files = [ {file = "geojson-3.1.0.tar.gz", hash = "sha256:58a7fa40727ea058efc28b0e9ff0099eadf6d0965e04690830208d3ef571adac"}, ] -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.41" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"}, - {file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"] - [[package]] name = "govuk-bank-holidays" version = "0.13" @@ -1555,72 +1561,73 @@ requests = "*" [[package]] name = "greenlet" -version = "3.0.1" +version = "3.0.3" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"}, - {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"}, - {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"}, - {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"}, - {file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"}, - {file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"}, - {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"}, - {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"}, - {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"}, - {file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"}, - {file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"}, - {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"}, - {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"}, - {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"}, - {file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"}, - {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"}, - {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"}, - {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"}, - {file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"}, - {file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"}, - {file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"}, - {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"}, - {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"}, - {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"}, - {file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"}, - {file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"}, - {file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"}, - {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"}, - {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"}, - {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"}, - {file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"}, - {file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"}, - {file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"}, + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, ] [package.extras] -docs = ["Sphinx"] +docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] [[package]] @@ -1684,13 +1691,13 @@ lxml = ["lxml"] [[package]] name = "identify" -version = "2.5.31" +version = "2.5.35" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.31-py2.py3-none-any.whl", hash = "sha256:90199cb9e7bd3c5407a9b7e81b4abec4bb9d249991c79439ec8af740afc6293d"}, - {file = "identify-2.5.31.tar.gz", hash = "sha256:7736b3c7a28233637e3c36550646fc6389bedd74ae84cb788200cc8e2dd60b75"}, + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, ] [package.extras] @@ -1698,31 +1705,31 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.4" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] name = "importlib-metadata" -version = "6.8.0" +version = "7.0.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] @@ -1800,21 +1807,21 @@ files = [ [[package]] name = "jaraco-classes" -version = "3.3.0" +version = "3.3.1" description = "Utility functions for Python class constructs" optional = false python-versions = ">=3.8" files = [ - {file = "jaraco.classes-3.3.0-py3-none-any.whl", hash = "sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb"}, - {file = "jaraco.classes-3.3.0.tar.gz", hash = "sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621"}, + {file = "jaraco.classes-3.3.1-py3-none-any.whl", hash = "sha256:86b534de565381f6b3c1c830d13f931d7be1a75f0081c57dff615578676e2206"}, + {file = "jaraco.classes-3.3.1.tar.gz", hash = "sha256:cb28a5ebda8bc47d8c8015307d93163464f9f2b91ab4006e09ff0ce07e8bfb30"}, ] [package.dependencies] more-itertools = "*" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "jeepney" @@ -1922,13 +1929,13 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-specifications" -version = "2023.11.1" +version = "2023.12.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema_specifications-2023.11.1-py3-none-any.whl", hash = "sha256:f596778ab612b3fd29f72ea0d990393d0540a5aab18bf0407a46632eab540779"}, - {file = "jsonschema_specifications-2023.11.1.tar.gz", hash = "sha256:c9b234904ffe02f079bf91b14d79987faa685fd4b39c377a0996954c0090b9ca"}, + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, ] [package.dependencies] @@ -1936,13 +1943,13 @@ referencing = ">=0.31.0" [[package]] name = "keyring" -version = "24.3.0" +version = "24.3.1" description = "Store and access your passwords safely." optional = false python-versions = ">=3.8" files = [ - {file = "keyring-24.3.0-py3-none-any.whl", hash = "sha256:4446d35d636e6a10b8bce7caa66913dd9eca5fd222ca03a3d42c38608ac30836"}, - {file = "keyring-24.3.0.tar.gz", hash = "sha256:e730ecffd309658a08ee82535a3b5ec4b4c8669a9be11efb66249d8e0aeb9a25"}, + {file = "keyring-24.3.1-py3-none-any.whl", hash = "sha256:df38a4d7419a6a60fea5cef1e45a948a3e8430dd12ad88b0f423c5c143906218"}, + {file = "keyring-24.3.1.tar.gz", hash = "sha256:c3327b6ffafc0e8befbdb597cacdb4928ffe5c1212f7645f186e6d9957a898db"}, ] [package.dependencies] @@ -1955,17 +1962,17 @@ SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] completion = ["shtab (>=1.1.0)"] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [[package]] name = "kombu" -version = "5.3.4" +version = "5.3.5" description = "Messaging library for Python." optional = false python-versions = ">=3.8" files = [ - {file = "kombu-5.3.4-py3-none-any.whl", hash = "sha256:63bb093fc9bb80cfb3a0972336a5cec1fa7ac5f9ef7e8237c6bf8dda9469313e"}, - {file = "kombu-5.3.4.tar.gz", hash = "sha256:0bb2e278644d11dea6272c17974a3dbb9688a949f3bb60aeb5b791329c44fadc"}, + {file = "kombu-5.3.5-py3-none-any.whl", hash = "sha256:0eac1bbb464afe6fb0924b21bf79460416d25d8abc52546d4f16cad94f789488"}, + {file = "kombu-5.3.5.tar.gz", hash = "sha256:30e470f1a6b49c70dc6f6d13c3e4cc4e178aa6c469ceb6bcd55645385fc84b93"}, ] [package.dependencies] @@ -1992,20 +1999,20 @@ zookeeper = ["kazoo (>=2.8.0)"] [[package]] name = "license-expression" -version = "30.1.1" +version = "30.2.0" description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic." optional = false python-versions = ">=3.7" files = [ - {file = "license-expression-30.1.1.tar.gz", hash = "sha256:42375df653ad85e6f5b4b0385138b2dbea1f5d66360783d8625c3e4f97f11f0c"}, - {file = "license_expression-30.1.1-py3-none-any.whl", hash = "sha256:8d7e5e2de0d04fc104a4f952c440e8f08a5ba63480a0dad015b294770b7e58ec"}, + {file = "license-expression-30.2.0.tar.gz", hash = "sha256:599928edd995c43fc335e0af342076144dc71cb858afa1ed9c1c30c4e81794f5"}, + {file = "license_expression-30.2.0-py3-none-any.whl", hash = "sha256:1a7dc2bb2d09cdc983d072e4f9adc787e107e09def84cbb3919baaaf4f8e6fa1"}, ] [package.dependencies] "boolean.py" = ">=4.0" [package.extras] -docs = ["Sphinx (==5.1.0)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)", "sphinxcontrib-apidoc (>=0.3.0)"] +docs = ["Sphinx (>=5.0.2)", "doc8 (>=0.11.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-reredirects (>=0.1.2)", "sphinx-rtd-dark-mode (>=1.3.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-apidoc (>=0.4.0)"] testing = ["black", "isort", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)", "twine"] [[package]] @@ -2103,13 +2110,13 @@ source = ["Cython (>=3.0.7)"] [[package]] name = "mako" -version = "1.3.0" +version = "1.3.2" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" files = [ - {file = "Mako-1.3.0-py3-none-any.whl", hash = "sha256:57d4e997349f1a92035aa25c17ace371a4213f2ca42f99bee9a602500cfd54d9"}, - {file = "Mako-1.3.0.tar.gz", hash = "sha256:e3a9d388fd00e87043edbe8792f45880ac0114e9c4adc69f6e9bfb2c55e3b11b"}, + {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"}, + {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"}, ] [package.dependencies] @@ -2163,71 +2170,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] @@ -2307,13 +2314,13 @@ files = [ [[package]] name = "more-itertools" -version = "10.1.0" +version = "10.2.0" description = "More routines for operating on iterables, beyond itertools" optional = false python-versions = ">=3.8" files = [ - {file = "more-itertools-10.1.0.tar.gz", hash = "sha256:626c369fa0eb37bac0291bce8259b332fd59ac792fa5497b59837309cd5b114a"}, - {file = "more_itertools-10.1.0-py3-none-any.whl", hash = "sha256:64e0735fcfdc6f3464ea133afe8ea4483b1c5fe3a3d69852e6503b43a0b222e6"}, + {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"}, + {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"}, ] [[package]] @@ -2432,85 +2439,101 @@ files = [ [[package]] name = "multidict" -version = "6.0.4" +version = "6.0.5" description = "multidict implementation" optional = false python-versions = ">=3.7" files = [ - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, - {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, - {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, - {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, - {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, - {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, - {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, - {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, - {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, - {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, - {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, - {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, - {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, - {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, - {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, - {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, - {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, - {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, - {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, - {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, - {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, - {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, - {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, - {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, - {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, - {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, - {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, ] [[package]] @@ -2526,40 +2549,40 @@ files = [ [[package]] name = "newrelic" -version = "9.5.0" +version = "9.7.0" description = "New Relic Python Agent" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ - {file = "newrelic-9.5.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:87c5ea1db9b2f573c7fb0961b7a856a91db0c9f130303de1839c542586b8dc87"}, - {file = "newrelic-9.5.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8c62c07a69b86c54d3b9ce187551f36683ad8b70b6301273e7c1e89b121b8245"}, - {file = "newrelic-9.5.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:847dd07961adec25c4c1128e8666f72ba0ab03640a44a65bcf25ffebd6fc2a75"}, - {file = "newrelic-9.5.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:12be024a5dfd1667d05d7cce968189e2490b6f6d24f194695365607b2340be20"}, - {file = "newrelic-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c381d300d904e2a5eb60953a29a729dbd4d8213f6dd629cecaa3033db70a9060"}, - {file = "newrelic-9.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2e4ccd5fe97496806fe0ea1acb8b15791c1f012aa44fb047fc8016bcfb80ab9"}, - {file = "newrelic-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:14ef5e94d490315a73cf4bea4cd0186b2ec68009e90ab2c16a0b501942fa8629"}, - {file = "newrelic-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:306e54751fe1eba61884b0ed63e9583711b37e24db9c7b36725317c21aa609f5"}, - {file = "newrelic-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ac2b3603cc781ef58e51a95b0b58220feb3e322c282c758c3bf5430398a47bc"}, - {file = "newrelic-9.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5b0a3fbbd6ef1dca0d2434c8ddd15d945fef2cd343d09b3a352b8de825d2e83"}, - {file = "newrelic-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab045fe37dcfd9c1fefc84f72373c54abd546d7517909fabd6920ea1d87c3f7f"}, - {file = "newrelic-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e54492c5f70cf0951ffc97ddd71b3cf64d68b4d670012d7a7d5ff3dc19417dab"}, - {file = "newrelic-9.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92968b7c6951f3ee0fe85a465ae79d448e587a3d10bf71d85b2cab09aa319776"}, - {file = "newrelic-9.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847d75a08ca3ba356b6d6317618243e546dc7725251a453d4730ac6e9f043b71"}, - {file = "newrelic-9.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec0fe958c5ed52cd8b496339df8661badbd24bdc290dea8744de94da705ccef0"}, - {file = "newrelic-9.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a2af10be7893f60588e2103b2f5ff616baf8132ecce90d9a8c7bfb832da08748"}, - {file = "newrelic-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2556ae2b9c13127dc46a6326c1ea682eae2c2b64b6873cfd4d5e177c7e0ce58a"}, - {file = "newrelic-9.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e9818646a7a72332e5ab3853277187af5b0d51312c51585a5581453fbdc1508"}, - {file = "newrelic-9.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b370179f644c0265e4467221e895dccc75440388c5cce8d6a89d150c6b265021"}, - {file = "newrelic-9.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c514f82942f7759bc4a360d908c4be37b8764a3ff1589ba3ad8b4baf201bad36"}, - {file = "newrelic-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15d4c2dd584c7604a03b2c73464ed3054cd8f0a513ed25832f1a3af4eb6e80ab"}, - {file = "newrelic-9.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2af79b43e3244ce7775eb7a0bf2e8fe2cb1624f6eb1be03cb6c35f9e8a5be77"}, - {file = "newrelic-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1dab3ef8baffebcf3f088a4131d93f3ac0e1fc08fa6045927c6bd2746b4decf4"}, - {file = "newrelic-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78ca19f47b2bc38e18516c2a7c485eeac6979b271a1a5a4ac3b950581ca8a230"}, - {file = "newrelic-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63314277c983cd412b87dfa3ed101b88c40ccab683a98046d76bf44a33cb7637"}, - {file = "newrelic-9.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68917968adba4abde385be1c23d8fdb13714968883c3a3d1a83fb676ea614245"}, - {file = "newrelic-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fb49e3a2e8ab9b509c8d276382f7ce0f6b4e51758b0edec38131ae583532b121"}, - {file = "newrelic-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4638e6c28ebf2b5a8a860ae33105a1816425f6ece0113df626c88e5626b22f41"}, - {file = "newrelic-9.5.0.tar.gz", hash = "sha256:2442449c4798989d92f527a82e60c446821c97896d739492b607bf6d4018ba1b"}, + {file = "newrelic-9.7.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:bf9485a5c9efaa30c645683eab427ce8b41164213bc003f7e7ad31772eb1f481"}, + {file = "newrelic-9.7.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:333ec033d13646f2221fdaf3822d3b8360d1935d1baea6879c1ae7f0d5020217"}, + {file = "newrelic-9.7.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c005bfb53c7090652839e9b38a3ec2462fe4e125fe976e2b9fcd778efa1c4a12"}, + {file = "newrelic-9.7.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:be2a7697b8407cea2ebe962ec990935dff300d9c4f78d3d7335b9dc280d33c53"}, + {file = "newrelic-9.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4adf292b529771536b417f46f84c497413f467f1ae7534009404580e259cb1b1"}, + {file = "newrelic-9.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3656b546aced2c6a4443e5e76f89e17a1672d69dfe47940119c688ab4426a76"}, + {file = "newrelic-9.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:91e2ad1da28c76d67344daca7ddd6166a5e190f7031f9a5bd683db17542f91ef"}, + {file = "newrelic-9.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b180f099aabff875f83364b6314b9954e29dfca753ccc1d353a8135c1430f9a6"}, + {file = "newrelic-9.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e851365bf5e5f8e7ca21e63d01bd2ce9327afc18417e071a3d50590f2747a8"}, + {file = "newrelic-9.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563342155edbed8276ddef9e2e15a61a31953ff9f42015a426f94660adf104cb"}, + {file = "newrelic-9.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0344e718ddc4ffe78a1441c6313a6af2f9aa3001e93a8a5197caac091f8bc9b3"}, + {file = "newrelic-9.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e57d78ef1291710968e872412a8d7c765f077de0aaf225aaab216c552ee1775a"}, + {file = "newrelic-9.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd32c76427782a3cf6994cab1217a1da79327d5b9cb2bad11917df5eb55dc0d"}, + {file = "newrelic-9.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:172732a71d4ff053c1c724a8dfbb8b1efc24c398c25e78f7aaf7966551d3fb09"}, + {file = "newrelic-9.7.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:288ed42949fd4a5d535507cb15b8f602111244663eceab1716a0a77e529ee2b6"}, + {file = "newrelic-9.7.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4966e4be00eab203903796a4b5aa864d866ba45d17bf823d71a932f99330ceee"}, + {file = "newrelic-9.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8958e575f7ada2ed8937dafff297790aeb960499b08d209b76a8a3c72f0841fc"}, + {file = "newrelic-9.7.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a687a521950da96b7daa553d1ab6371aebc5bfd1f3cb4ceb5d6dc859b0956602"}, + {file = "newrelic-9.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:eb94aabd4b575f4fa2068343781614cc249630c8bcbc07f115affeb1311736cd"}, + {file = "newrelic-9.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4cefc2b264122e9f99db557ec9f1c5b287f4b95229957f7f78269cc462d47065"}, + {file = "newrelic-9.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59f2c94a2e256f00b344efc909eb1f058cd411e9a95a6ad1d7adf957223a747d"}, + {file = "newrelic-9.7.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb3e40be0f1ba2b2d1ad070d7913952efb1ceee13e6548d63bb973dcdf2c9d32"}, + {file = "newrelic-9.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9c41a571d0889409044bfb22194382731e18fd7962ba6a91ff640b274ca3fc1a"}, + {file = "newrelic-9.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78f604a2622a6795320a6ff54262816aeff86da79400429c34346fc5feecb235"}, + {file = "newrelic-9.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc5af6e7d7b6f30b03cec4f265b84fa8d370e006332854181214507e2deb421e"}, + {file = "newrelic-9.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e229fb5406a3c0752723bc5444d75dc863456a0305621be4159356f2880488e9"}, + {file = "newrelic-9.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b7733168eae4c718f885f188bcfc265c299f51d43130350b32f86f3754bc809b"}, + {file = "newrelic-9.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0fdd25b9969a4c85a53a1dc2cade462164c6603e85ffe50da732ad4e69347659"}, + {file = "newrelic-9.7.0.tar.gz", hash = "sha256:e731ac5b66dbeda1e990ba41cecda8ea865c69f72b0267574d6b1727113f7de2"}, ] [package.extras] @@ -2652,47 +2675,47 @@ resolved_reference = "df8ece836cad303c5d161edf485cf9bbdc666688" [[package]] name = "numpy" -version = "1.26.2" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" files = [ - {file = "numpy-1.26.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3703fc9258a4a122d17043e57b35e5ef1c5a5837c3db8be396c82e04c1cf9b0f"}, - {file = "numpy-1.26.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc392fdcbd21d4be6ae1bb4475a03ce3b025cd49a9be5345d76d7585aea69440"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36340109af8da8805d8851ef1d74761b3b88e81a9bd80b290bbfed61bd2b4f75"}, - {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc008217145b3d77abd3e4d5ef586e3bdfba8fe17940769f8aa09b99e856c00"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ced40d4e9e18242f70dd02d739e44698df3dcb010d31f495ff00a31ef6014fe"}, - {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b272d4cecc32c9e19911891446b72e986157e6a1809b7b56518b4f3755267523"}, - {file = "numpy-1.26.2-cp310-cp310-win32.whl", hash = "sha256:22f8fc02fdbc829e7a8c578dd8d2e15a9074b630d4da29cda483337e300e3ee9"}, - {file = "numpy-1.26.2-cp310-cp310-win_amd64.whl", hash = "sha256:26c9d33f8e8b846d5a65dd068c14e04018d05533b348d9eaeef6c1bd787f9919"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b96e7b9c624ef3ae2ae0e04fa9b460f6b9f17ad8b4bec6d7756510f1f6c0c841"}, - {file = "numpy-1.26.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa18428111fb9a591d7a9cc1b48150097ba6a7e8299fb56bdf574df650e7d1f1"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06fa1ed84aa60ea6ef9f91ba57b5ed963c3729534e6e54055fc151fad0423f0a"}, - {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca5482c3dbdd051bcd1fce8034603d6ebfc125a7bd59f55b40d8f5d246832b"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:854ab91a2906ef29dc3925a064fcd365c7b4da743f84b123002f6139bcb3f8a7"}, - {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f43740ab089277d403aa07567be138fc2a89d4d9892d113b76153e0e412409f8"}, - {file = "numpy-1.26.2-cp311-cp311-win32.whl", hash = "sha256:a2bbc29fcb1771cd7b7425f98b05307776a6baf43035d3b80c4b0f29e9545186"}, - {file = "numpy-1.26.2-cp311-cp311-win_amd64.whl", hash = "sha256:2b3fca8a5b00184828d12b073af4d0fc5fdd94b1632c2477526f6bd7842d700d"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a4cd6ed4a339c21f1d1b0fdf13426cb3b284555c27ac2f156dfdaaa7e16bfab0"}, - {file = "numpy-1.26.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d5244aabd6ed7f312268b9247be47343a654ebea52a60f002dc70c769048e75"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3cdb4d9c70e6b8c0814239ead47da00934666f668426fc6e94cce869e13fd7"}, - {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa317b2325f7aa0a9471663e6093c210cb2ae9c0ad824732b307d2c51983d5b6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:174a8880739c16c925799c018f3f55b8130c1f7c8e75ab0a6fa9d41cab092fd6"}, - {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f79b231bf5c16b1f39c7f4875e1ded36abee1591e98742b05d8a0fb55d8a3eec"}, - {file = "numpy-1.26.2-cp312-cp312-win32.whl", hash = "sha256:4a06263321dfd3598cacb252f51e521a8cb4b6df471bb12a7ee5cbab20ea9167"}, - {file = "numpy-1.26.2-cp312-cp312-win_amd64.whl", hash = "sha256:b04f5dc6b3efdaab541f7857351aac359e6ae3c126e2edb376929bd3b7f92d7e"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4eb8df4bf8d3d90d091e0146f6c28492b0be84da3e409ebef54349f71ed271ef"}, - {file = "numpy-1.26.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a13860fdcd95de7cf58bd6f8bc5a5ef81c0b0625eb2c9a783948847abbef2c2"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64308ebc366a8ed63fd0bf426b6a9468060962f1a4339ab1074c228fa6ade8e3"}, - {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf8aab04a2c0e859da118f0b38617e5ee65d75b83795055fb66c0d5e9e9b818"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d73a3abcac238250091b11caef9ad12413dab01669511779bc9b29261dd50210"}, - {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b361d369fc7e5e1714cf827b731ca32bff8d411212fccd29ad98ad622449cc36"}, - {file = "numpy-1.26.2-cp39-cp39-win32.whl", hash = "sha256:bd3f0091e845164a20bd5a326860c840fe2af79fa12e0469a12768a3ec578d80"}, - {file = "numpy-1.26.2-cp39-cp39-win_amd64.whl", hash = "sha256:2beef57fb031dcc0dc8fa4fe297a742027b954949cabb52a2a376c144e5e6060"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1cc3d5029a30fb5f06704ad6b23b35e11309491c999838c31f124fee32107c79"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc3c222bb9fb5a12e334d0479b97bb2df446fbe622b470928f5284ffca3f8d"}, - {file = "numpy-1.26.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe6b44fb8fcdf7eda4ef4461b97b3f63c466b27ab151bec2366db8b197387841"}, - {file = "numpy-1.26.2.tar.gz", hash = "sha256:f65738447676ab5777f11e6bbbdb8ce11b785e105f690bc45966574816b6d3ea"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] @@ -2735,17 +2758,17 @@ asn1crypto = ">=1.5.1" [[package]] name = "packageurl-python" -version = "0.11.2" +version = "0.13.4" description = "A purl aka. Package URL parser and builder" optional = false python-versions = ">=3.7" files = [ - {file = "packageurl-python-0.11.2.tar.gz", hash = "sha256:01fbf74a41ef85cf413f1ede529a1411f658bda66ed22d45d27280ad9ceba471"}, - {file = "packageurl_python-0.11.2-py3-none-any.whl", hash = "sha256:799acfe8d9e6e3534bbc19660be97d5b66754bc033e62c39f1e2f16323fcfa84"}, + {file = "packageurl-python-0.13.4.tar.gz", hash = "sha256:6eb5e995009cc73387095e0b507ab65df51357d25ddc5fce3d3545ad6dcbbee8"}, + {file = "packageurl_python-0.13.4-py3-none-any.whl", hash = "sha256:62aa13d60a0082ff115784fefdfe73a12f310e455365cca7c6d362161067f35f"}, ] [package.extras] -build = ["wheel"] +build = ["setuptools", "wheel"] lint = ["black", "isort", "mypy"] sqlalchemy = ["sqlalchemy (>=2.0.0)"] test = ["pytest"] @@ -2763,13 +2786,13 @@ files = [ [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] @@ -2785,13 +2808,13 @@ files = [ [[package]] name = "pexpect" -version = "4.8.0" +version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" files = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] [package.dependencies] @@ -2799,35 +2822,35 @@ ptyprocess = ">=0.5" [[package]] name = "phonenumbers" -version = "8.13.25" +version = "8.13.31" description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." optional = false python-versions = "*" files = [ - {file = "phonenumbers-8.13.25-py2.py3-none-any.whl", hash = "sha256:7a57cceb8145d3099a0cda7a1f2581b6829936069224790be5de0adf14b39f13"}, - {file = "phonenumbers-8.13.25.tar.gz", hash = "sha256:4ae2d2e253a4752a269ae1147822b9aa500f14b2506a91f884e68b136901f128"}, + {file = "phonenumbers-8.13.31-py2.py3-none-any.whl", hash = "sha256:e45d0bc852516a3a6594bcec0ae47b825421a8adcfcf5f114ad148f1523d8646"}, + {file = "phonenumbers-8.13.31.tar.gz", hash = "sha256:2742071c9d0af09274c8a5b2a26d9a36acbf2ea5cb62943cc2ceadb5c0c87641"}, ] [[package]] name = "pip" -version = "23.3.1" +version = "24.0" description = "The PyPA recommended tool for installing Python packages." optional = false python-versions = ">=3.7" files = [ - {file = "pip-23.3.1-py3-none-any.whl", hash = "sha256:55eb67bb6171d37447e82213be585b75fe2b12b359e993773aca4de9247a052b"}, - {file = "pip-23.3.1.tar.gz", hash = "sha256:1fcaa041308d01f14575f6d0d2ea4b75a3e2871fe4f9c694976f908768e14174"}, + {file = "pip-24.0-py3-none-any.whl", hash = "sha256:ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc"}, + {file = "pip-24.0.tar.gz", hash = "sha256:ea9bd1a847e8c5774a5777bb398c19e80bcd4e2aa16a4b301b718fe6f593aba2"}, ] [[package]] name = "pip-api" -version = "0.0.30" +version = "0.0.33" description = "An unofficial, importable pip API" optional = false python-versions = ">=3.7" files = [ - {file = "pip-api-0.0.30.tar.gz", hash = "sha256:a05df2c7aa9b7157374bcf4273544201a0c7bae60a9c65bcf84f3959ef3896f3"}, - {file = "pip_api-0.0.30-py3-none-any.whl", hash = "sha256:2a0314bd31522eb9ffe8a99668b0d07fee34ebc537931e7b6483001dbedcbdc9"}, + {file = "pip-api-0.0.33.tar.gz", hash = "sha256:1c2522ae21efcb034d89cc99f6cf1025293b31c63c29ee98b23f03a85f36bdae"}, + {file = "pip_api-0.0.33-py3-none-any.whl", hash = "sha256:b8d6eb5a87d3a9e112a20a8e9d24a6fc12d4e1c94d7595eeaf74be11ad47276c"}, ] [package.dependencies] @@ -2835,13 +2858,13 @@ pip = "*" [[package]] name = "pip-audit" -version = "2.7.0" +version = "2.7.1" description = "A tool for scanning Python environments for known vulnerabilities" optional = false python-versions = ">=3.8" files = [ - {file = "pip_audit-2.7.0-py3-none-any.whl", hash = "sha256:83e039740653eb9ef1a78b1540ed441600cd88a560588ba2c0a169180685a522"}, - {file = "pip_audit-2.7.0.tar.gz", hash = "sha256:67740c5b1d5d967a258c3dfefc46f9713a2819c48062505ddf4b29de101c2b75"}, + {file = "pip_audit-2.7.1-py3-none-any.whl", hash = "sha256:b9b4230d1ac685d669b4a36b1d5f849ea3d1ce371501aff73047bd278b22c055"}, + {file = "pip_audit-2.7.1.tar.gz", hash = "sha256:66001c73bc6e5ebc998ef31a32432f7b479dc3bfeb40f7101d0fe7eb564a2c2a"}, ] [package.dependencies] @@ -2858,7 +2881,7 @@ toml = ">=0.10" [package.extras] dev = ["build", "bump (>=1.3.2)", "pip-audit[doc,lint,test]"] doc = ["pdoc"] -lint = ["interrogate", "mypy", "ruff (<0.1.12)", "types-html5lib", "types-requests", "types-toml"] +lint = ["interrogate", "mypy", "ruff (<0.2.2)", "types-html5lib", "types-requests", "types-toml"] test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"] [[package]] @@ -2896,28 +2919,28 @@ testing = ["pytest", "pytest-cov"] [[package]] name = "platformdirs" -version = "3.11.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, - {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -2926,18 +2949,18 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "poetry" -version = "1.7.1" +version = "1.8.1" description = "Python dependency management and packaging made easy." optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "poetry-1.7.1-py3-none-any.whl", hash = "sha256:03d3807a0fb3bc1028cc3707dfd646aae629d58e476f7e7f062437680741c561"}, - {file = "poetry-1.7.1.tar.gz", hash = "sha256:b348a70e7d67ad9c0bd3d0ea255bc6df84c24cf4b16f8d104adb30b425d6ff32"}, + {file = "poetry-1.8.1-py3-none-any.whl", hash = "sha256:cf133946661025822672422769567980f8e489ed5b6fc170d1025a4d6c9aac29"}, + {file = "poetry-1.8.1.tar.gz", hash = "sha256:23519cc45eb3cf48e899145bc762425a141e3afd52ecc53ec443ca635122327f"}, ] [package.dependencies] build = ">=1.0.3,<2.0.0" -cachecontrol = {version = ">=0.13.0,<0.14.0", extras = ["filecache"]} +cachecontrol = {version = ">=0.14.0,<0.15.0", extras = ["filecache"]} cleo = ">=2.1.0,<3.0.0" crashtest = ">=0.4.1,<0.5.0" dulwich = ">=0.21.2,<0.22.0" @@ -2945,31 +2968,31 @@ fastjsonschema = ">=2.18.0,<3.0.0" importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} installer = ">=0.7.0,<0.8.0" keyring = ">=24.0.0,<25.0.0" -packaging = ">=20.5" +packaging = ">=23.1" pexpect = ">=4.7.0,<5.0.0" pkginfo = ">=1.9.4,<2.0.0" -platformdirs = ">=3.0.0,<4.0.0" -poetry-core = "1.8.1" +platformdirs = ">=3.0.0,<5" +poetry-core = "1.9.0" poetry-plugin-export = ">=1.6.0,<2.0.0" pyproject-hooks = ">=1.0.0,<2.0.0" requests = ">=2.26,<3.0" -requests-toolbelt = ">=0.9.1,<2" +requests-toolbelt = ">=1.0.0,<2.0.0" shellingham = ">=1.5,<2.0" tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} tomlkit = ">=0.11.4,<1.0.0" trove-classifiers = ">=2022.5.19" virtualenv = ">=20.23.0,<21.0.0" -xattr = {version = ">=0.10.0,<0.11.0", markers = "sys_platform == \"darwin\""} +xattr = {version = ">=1.0.0,<2.0.0", markers = "sys_platform == \"darwin\""} [[package]] name = "poetry-core" -version = "1.8.1" +version = "1.9.0" description = "Poetry PEP 517 Build Backend" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "poetry_core-1.8.1-py3-none-any.whl", hash = "sha256:194832b24f3283e01c5402eae71a6aae850ecdfe53f50a979c76bf7aa5010ffa"}, - {file = "poetry_core-1.8.1.tar.gz", hash = "sha256:67a76c671da2a70e55047cddda83566035b701f7e463b32a2abfeac6e2a16376"}, + {file = "poetry_core-1.9.0-py3-none-any.whl", hash = "sha256:4e0c9c6ad8cf89956f03b308736d84ea6ddb44089d16f2adc94050108ec1f5a1"}, + {file = "poetry_core-1.9.0.tar.gz", hash = "sha256:fa7a4001eae8aa572ee84f35feb510b321bd652e5cf9293249d62853e1f935a2"}, ] [[package]] @@ -3015,13 +3038,13 @@ files = [ [[package]] name = "pre-commit" -version = "3.6.0" +version = "3.6.2" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.6.0-py2.py3-none-any.whl", hash = "sha256:c255039ef399049a5544b6ce13d135caba8f2c28c3b4033277a788f434308376"}, - {file = "pre_commit-3.6.0.tar.gz", hash = "sha256:d30bad9abf165f7785c15a21a1f46da7d0677cb00ee7ff4c579fd38922efe15d"}, + {file = "pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c"}, + {file = "pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"}, ] [package.dependencies] @@ -3033,13 +3056,13 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.41" +version = "3.0.43" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.41-py3-none-any.whl", hash = "sha256:f36fe301fafb7470e86aaf90f036eef600a3210be4decf461a5b1ca8403d3cb2"}, - {file = "prompt_toolkit-3.0.41.tar.gz", hash = "sha256:941367d97fc815548822aa26c2a269fdc4eb21e9ec05fc5d447cf09bad5d75f0"}, + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, ] [package.dependencies] @@ -3047,22 +3070,22 @@ wcwidth = "*" [[package]] name = "protobuf" -version = "4.25.1" +version = "4.25.3" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.1-cp310-abi3-win32.whl", hash = "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7"}, - {file = "protobuf-4.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b"}, - {file = "protobuf-4.25.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd"}, - {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb"}, - {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7"}, - {file = "protobuf-4.25.1-cp38-cp38-win32.whl", hash = "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd"}, - {file = "protobuf-4.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0"}, - {file = "protobuf-4.25.1-cp39-cp39-win32.whl", hash = "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510"}, - {file = "protobuf-4.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10"}, - {file = "protobuf-4.25.1-py3-none-any.whl", hash = "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6"}, - {file = "protobuf-4.25.1.tar.gz", hash = "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2"}, + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, ] [[package]] @@ -3159,13 +3182,13 @@ files = [ [[package]] name = "py-serializable" -version = "0.17.1" +version = "1.0.1" description = "Library for serializing and deserializing Python Objects to and from JSON and XML." optional = false -python-versions = ">=3.7,<4.0" +python-versions = ">=3.8,<4.0" files = [ - {file = "py-serializable-0.17.1.tar.gz", hash = "sha256:875bb9c01df77f563dfcd1e75bb4244b5596083d3aad4ccd3fb63e1f5a9d3e5f"}, - {file = "py_serializable-0.17.1-py3-none-any.whl", hash = "sha256:389c2254d912bec3a44acdac667c947d73c59325050d5ae66386e1ed7108a45a"}, + {file = "py_serializable-1.0.1-py3-none-any.whl", hash = "sha256:edcc51ac91a39e0cdde147463cae4dc34f5ab72907f7e71721ff3ecef3731a70"}, + {file = "py_serializable-1.0.1.tar.gz", hash = "sha256:98b81e565c23b3cc2ac799f5096dc7e11cafe8215c551d20a1c16dd38a113861"}, ] [package.dependencies] @@ -3173,13 +3196,13 @@ defusedxml = ">=0.7.1,<0.8.0" [[package]] name = "pyasn1" -version = "0.5.0" +version = "0.5.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, - {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, ] [[package]] @@ -3217,17 +3240,18 @@ files = [ [[package]] name = "pygments" -version = "2.16.1" +version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyjwt" @@ -3410,13 +3434,13 @@ files = [ [[package]] name = "pytz" -version = "2023.3.post1" +version = "2023.4" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2023.4-py2.py3-none-any.whl", hash = "sha256:f90ef520d95e7c46951105338d918664ebfd6f1d995bd7d153127ce90efafa6a"}, + {file = "pytz-2023.4.tar.gz", hash = "sha256:31d4583c4ed539cd037956140d695e42c033a19e984bfce9964a3f7d59bc2b40"}, ] [[package]] @@ -3510,101 +3534,101 @@ toml = ["tomli (>=2.0.1)"] [[package]] name = "rapidfuzz" -version = "3.5.2" +version = "3.6.1" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.8" files = [ - {file = "rapidfuzz-3.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1a047d6e58833919d742bbc0dfa66d1de4f79e8562ee195007d3eae96635df39"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22877c027c492b7dc7e3387a576a33ed5aad891104aa90da2e0844c83c5493ef"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0f448b0eacbcc416feb634e1232a48d1cbde5e60f269c84e4fb0912f7bbb001"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05146497672f869baf41147d5ec1222788c70e5b8b0cfcd6e95597c75b5b96b"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f2df3968738a38d2a0058b5e721753f5d3d602346a1027b0dde31b0476418f3"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5afc1fcf1830f9bb87d3b490ba03691081b9948a794ea851befd2643069a30c1"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84be69ea65f64fa01e5c4976be9826a5aa949f037508887add42da07420d65d6"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8658c1045766e87e0038323aa38b4a9f49b7f366563271f973c8890a98aa24b5"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:852b3f93c15fce58b8dc668bd54123713bfdbbb0796ba905ea5df99cfd083132"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:12424a06ad9bd0cbf5f7cea1015e78d924a0034a0e75a5a7b39c0703dcd94095"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b4e9ded8e80530bd7205a7a2b01802f934a4695ca9e9fbe1ce9644f5e0697864"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:affb8fe36157c2dc8a7bc45b6a1875eb03e2c49167a1d52789144bdcb7ab3b8c"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1d33a622572d384f4c90b5f7a139328246ab5600141e90032b521c2127bd605"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-win32.whl", hash = "sha256:2cf9f2ed4a97b388cffd48d534452a564c2491f68f4fd5bc140306f774ceb63a"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:6541ffb70097885f7302cd73e2efd77be99841103023c2f9408551f27f45f7a5"}, - {file = "rapidfuzz-3.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:1dd2542e5103fb8ca46500a979ae14d1609dcba11d2f9fe01e99eec03420e193"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bff7d3127ebc5cd908f3a72f6517f31f5247b84666137556a8fcc5177c560939"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdfdb3685b631d8efbb6d6d3d86eb631be2b408d9adafcadc11e63e3f9c96dec"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97b043fe8185ec53bb3ff0e59deb89425c0fc6ece6e118939963aab473505801"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a4a7832737f87583f3863dc62e6f56dd4a9fefc5f04a7bdcb4c433a0f36bb1b"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d876dba9a11fcf60dcf1562c5a84ef559db14c2ceb41e1ad2d93cd1dc085889"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa4c0612893716bbb6595066ca9ecb517c982355abe39ba9d1f4ab834ace91ad"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:120316824333e376b88b284724cfd394c6ccfcb9818519eab5d58a502e5533f0"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cdbe8e80cc186d55f748a34393533a052d855357d5398a1ccb71a5021b58e8d"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1062425c8358a547ae5ebad148f2e0f02417716a571b803b0c68e4d552e99d32"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:66be181965aff13301dd5f9b94b646ce39d99c7fe2fd5de1656f4ca7fafcb38c"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:53df7aea3cf301633cfa2b4b2c2d2441a87dfc878ef810e5b4eddcd3e68723ad"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:76639dca5eb0afc6424ac5f42d43d3bd342ac710e06f38a8c877d5b96de09589"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:27689361c747b5f7b8a26056bc60979875323f1c3dcaaa9e2fec88f03b20a365"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-win32.whl", hash = "sha256:99c9fc5265566fb94731dc6826f43c5109e797078264e6389a36d47814473692"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:666928ee735562a909d81bd2f63207b3214afd4ca41f790ab3025d066975c814"}, - {file = "rapidfuzz-3.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:d55de67c48f06b7772541e8d4c062a2679205799ce904236e2836cb04c106442"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:04e1e02b182283c43c866e215317735e91d22f5d34e65400121c04d5ed7ed859"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:365e544aba3ac13acf1a62cb2e5909ad2ba078d0bfc7d69b1f801dfd673b9782"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b61f77d834f94b0099fa9ed35c189b7829759d4e9c2743697a130dd7ba62259f"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43fb368998b9703fa8c63db292a8ab9e988bf6da0c8a635754be8e69da1e7c1d"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25510b5d142c47786dbd27cfd9da7cae5bdea28d458379377a3644d8460a3404"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf3093443751e5a419834162af358d1e31dec75f84747a91dbbc47b2c04fc085"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fbaf546f15a924613f89d609ff66b85b4f4c2307ac14d93b80fe1025b713138"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32d580df0e130ed85400ff77e1c32d965e9bc7be29ac4072ab637f57e26d29fb"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:358a0fbc49343de20fee8ebdb33c7fa8f55a9ff93ff42d1ffe097d2caa248f1b"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fb379ac0ddfc86c5542a225d194f76ed468b071b6f79ff57c4b72e635605ad7d"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7fb21e182dc6d83617e88dea002963d5cf99cf5eabbdbf04094f503d8fe8d723"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c04f9f1310ce414ab00bdcbf26d0906755094bfc59402cb66a7722c6f06d70b2"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6da61cc38c1a95efc5edcedf258759e6dbab73191651a28c5719587f32a56ad"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-win32.whl", hash = "sha256:f823fd1977071486739f484e27092765d693da6beedaceece54edce1dfeec9b2"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:a8162d81486de85ab1606e48e076431b66d44cf431b2b678e9cae458832e7147"}, - {file = "rapidfuzz-3.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:dfc63fabb7d8da8483ca836bae7e55766fe39c63253571e103c034ba8ea80950"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:df8fae2515a1e4936affccac3e7d506dd904de5ff82bc0b1433b4574a51b9bfb"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dd6384780c2a16097d47588844cd677316a90e0f41ef96ff485b62d58de79dcf"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:467a4d730ae3bade87dba6bd769e837ab97e176968ce20591fe8f7bf819115b1"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54576669c1502b751b534bd76a4aeaaf838ed88b30af5d5c1b7d0a3ca5d4f7b5"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abafeb82f85a651a9d6d642a33dc021606bc459c33e250925b25d6b9e7105a2e"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73e14617a520c0f1bc15eb78c215383477e5ca70922ecaff1d29c63c060e04ca"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7cdf92116e9dfe40da17f921cdbfa0039dde9eb158914fa5f01b1e67a20b19cb"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1962d5ccf8602589dbf8e85246a0ee2b4050d82fade1568fb76f8a4419257704"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:db45028eae2fda7a24759c69ebeb2a7fbcc1a326606556448ed43ee480237a3c"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b685abb8b6d97989f6c69556d7934e0e533aa8822f50b9517ff2da06a1d29f23"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:40139552961018216b8cd88f6df4ecbbe984f907a62a5c823ccd907132c29a14"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0fef4705459842ef8f79746d6f6a0b5d2b6a61a145d7d8bbe10b2e756ea337c8"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6b2ad5516f7068c7d9cbcda8ac5906c589e99bc427df2e1050282ee2d8bc2d58"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-win32.whl", hash = "sha256:2da3a24c2f7dfca7f26ba04966b848e3bbeb93e54d899908ff88dfe3e1def9dc"}, - {file = "rapidfuzz-3.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:e3f2be79d4114d01f383096dbee51b57df141cb8b209c19d0cf65f23a24e75ba"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:089a7e96e5032821af5964d8457fcb38877cc321cdd06ad7c5d6e3d852264cb9"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75d8a52bf8d1aa2ac968ae4b21b83b94fc7e5ea3dfbab34811fc60f32df505b2"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2bacce6bbc0362f0789253424269cc742b1f45e982430387db3abe1d0496e371"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5fd627e604ddc02db2ddb9ddc4a91dd92b7a6d6378fcf30bb37b49229072b89"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2e8b369f23f00678f6e673572209a5d3b0832f4991888e3df97af7b8b9decf3"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c29958265e4c2b937269e804b8a160c027ee1c2627d6152655008a8b8083630e"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00be97f9219355945c46f37ac9fa447046e6f7930f7c901e5d881120d1695458"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0d8d57e0f556ef38c24fee71bfe8d0db29c678bff2acd1819fc1b74f331c2"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de89585268ed8ee44e80126814cae63ff6b00d08416481f31b784570ef07ec59"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:908ff2de9c442b379143d1da3c886c63119d4eba22986806e2533cee603fe64b"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:54f0061028723c026020f5bb20649c22bc8a0d9f5363c283bdc5901d4d3bff01"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b581107ec0c610cdea48b25f52030770be390db4a9a73ca58b8d70fa8a5ec32e"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1d5a686ea258931aaa38019204bdc670bbe14b389a230b1363d84d6cf4b9dc38"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-win32.whl", hash = "sha256:97f811ca7709c6ee8c0b55830f63b3d87086f4abbcbb189b4067e1cd7014db7b"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:58ee34350f8c292dd24a050186c0e18301d80da904ef572cf5fda7be6a954929"}, - {file = "rapidfuzz-3.5.2-cp39-cp39-win_arm64.whl", hash = "sha256:c5075ce7b9286624cafcf36720ef1cfb2946d75430b87cb4d1f006e82cd71244"}, - {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af5221e4f7800db3e84c46b79dba4112e3b3cc2678f808bdff4fcd2487073846"}, - {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8501d7875b176930e6ed9dbc1bc35adb37ef312f6106bd6bb5c204adb90160ac"}, - {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e414e1ca40386deda4291aa2d45062fea0fbaa14f95015738f8bb75c4d27f862"}, - {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2059cd73b7ea779a9307d7a78ed743f0e3d33b88ccdcd84569abd2953cd859f"}, - {file = "rapidfuzz-3.5.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:58e3e21f6f13a7cca265cce492bc797425bd4cb2025fdd161a9e86a824ad65ce"}, - {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b847a49377e64e92e11ef3d0a793de75451526c83af015bdafdd5d04de8a058a"}, - {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a42c7a8c62b29c4810e39da22b42524295fcb793f41c395c2cb07c126b729e83"}, - {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51b5166be86e09e011e92d9862b1fe64c4c7b9385f443fb535024e646d890460"}, - {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f808dcb0088a7a496cc9895e66a7b8de55ffea0eb9b547c75dfb216dd5f76ed"}, - {file = "rapidfuzz-3.5.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d4b05a8f4ab7e7344459394094587b033fe259eea3a8720035e8ba30e79ab39b"}, - {file = "rapidfuzz-3.5.2.tar.gz", hash = "sha256:9e9b395743e12c36a3167a3a9fd1b4e11d92fb0aa21ec98017ee6df639ed385e"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ac434fc71edda30d45db4a92ba5e7a42c7405e1a54cb4ec01d03cc668c6dcd40"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a791168e119cfddf4b5a40470620c872812042f0621e6a293983a2d52372db0"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a2f3e9df346145c2be94e4d9eeffb82fab0cbfee85bd4a06810e834fe7c03fa"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23de71e7f05518b0bbeef55d67b5dbce3bcd3e2c81e7e533051a2e9401354eb0"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d056e342989248d2bdd67f1955bb7c3b0ecfa239d8f67a8dfe6477b30872c607"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01835d02acd5d95c1071e1da1bb27fe213c84a013b899aba96380ca9962364bc"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f712e0bb5fea327e92aec8a937afd07ba8de4c529735d82e4c4124c10d5a0"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96cd19934f76a1264e8ecfed9d9f5291fde04ecb667faef5f33bdbfd95fe2d1f"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e06c4242a1354cf9d48ee01f6f4e6e19c511d50bb1e8d7d20bcadbb83a2aea90"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d73dcfe789d37c6c8b108bf1e203e027714a239e50ad55572ced3c004424ed3b"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e98ff000e2619e7cfe552d086815671ed09b6899408c2c1b5103658261f6f3"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:08b6fb47dd889c69fbc0b915d782aaed43e025df6979b6b7f92084ba55edd526"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a1788ebb5f5b655a15777e654ea433d198f593230277e74d51a2a1e29a986283"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-win32.whl", hash = "sha256:c65f92881753aa1098c77818e2b04a95048f30edbe9c3094dc3707d67df4598b"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:4243a9c35667a349788461aae6471efde8d8800175b7db5148a6ab929628047f"}, + {file = "rapidfuzz-3.6.1-cp310-cp310-win_arm64.whl", hash = "sha256:f59d19078cc332dbdf3b7b210852ba1f5db8c0a2cd8cc4c0ed84cc00c76e6802"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fbc07e2e4ac696497c5f66ec35c21ddab3fc7a406640bffed64c26ab2f7ce6d6"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cced1a8852652813f30fb5d4b8f9b237112a0bbaeebb0f4cc3611502556764"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82300e5f8945d601c2daaaac139d5524d7c1fdf719aa799a9439927739917460"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf97c321fd641fea2793abce0e48fa4f91f3c202092672f8b5b4e781960b891"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7420e801b00dee4a344ae2ee10e837d603461eb180e41d063699fb7efe08faf0"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060bd7277dc794279fa95522af355034a29c90b42adcb7aa1da358fc839cdb11"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7e3375e4f2bfec77f907680328e4cd16cc64e137c84b1886d547ab340ba6928"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a490cd645ef9d8524090551016f05f052e416c8adb2d8b85d35c9baa9d0428ab"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2e03038bfa66d2d7cffa05d81c2f18fd6acbb25e7e3c068d52bb7469e07ff382"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b19795b26b979c845dba407fe79d66975d520947b74a8ab6cee1d22686f7967"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:064c1d66c40b3a0f488db1f319a6e75616b2e5fe5430a59f93a9a5e40a656d15"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3c772d04fb0ebeece3109d91f6122b1503023086a9591a0b63d6ee7326bd73d9"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:841eafba6913c4dfd53045835545ba01a41e9644e60920c65b89c8f7e60c00a9"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-win32.whl", hash = "sha256:266dd630f12696ea7119f31d8b8e4959ef45ee2cbedae54417d71ae6f47b9848"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:d79aec8aeee02ab55d0ddb33cea3ecd7b69813a48e423c966a26d7aab025cdfe"}, + {file = "rapidfuzz-3.6.1-cp311-cp311-win_arm64.whl", hash = "sha256:484759b5dbc5559e76fefaa9170147d1254468f555fd9649aea3bad46162a88b"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b2ef4c0fd3256e357b70591ffb9e8ed1d439fb1f481ba03016e751a55261d7c1"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:588c4b20fa2fae79d60a4e438cf7133d6773915df3cc0a7f1351da19eb90f720"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7142ee354e9c06e29a2636b9bbcb592bb00600a88f02aa5e70e4f230347b373e"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dfc557c0454ad22382373ec1b7df530b4bbd974335efe97a04caec936f2956a"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03f73b381bdeccb331a12c3c60f1e41943931461cdb52987f2ecf46bfc22f50d"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b0ccc2ec1781c7e5370d96aef0573dd1f97335343e4982bdb3a44c133e27786"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da3e8c9f7e64bb17faefda085ff6862ecb3ad8b79b0f618a6cf4452028aa2222"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fde9b14302a31af7bdafbf5cfbb100201ba21519be2b9dedcf4f1048e4fbe65d"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1a23eee225dfb21c07f25c9fcf23eb055d0056b48e740fe241cbb4b22284379"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e49b9575d16c56c696bc7b06a06bf0c3d4ef01e89137b3ddd4e2ce709af9fe06"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:0a9fc714b8c290261669f22808913aad49553b686115ad0ee999d1cb3df0cd66"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a3ee4f8f076aa92184e80308fc1a079ac356b99c39408fa422bbd00145be9854"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f056ba42fd2f32e06b2c2ba2443594873cfccc0c90c8b6327904fc2ddf6d5799"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-win32.whl", hash = "sha256:5d82b9651e3d34b23e4e8e201ecd3477c2baa17b638979deeabbb585bcb8ba74"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:dad55a514868dae4543ca48c4e1fc0fac704ead038dafedf8f1fc0cc263746c1"}, + {file = "rapidfuzz-3.6.1-cp312-cp312-win_arm64.whl", hash = "sha256:3c84294f4470fcabd7830795d754d808133329e0a81d62fcc2e65886164be83b"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e19d519386e9db4a5335a4b29f25b8183a1c3f78cecb4c9c3112e7f86470e37f"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01eb03cd880a294d1bf1a583fdd00b87169b9cc9c9f52587411506658c864d73"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:be368573255f8fbb0125a78330a1a40c65e9ba3c5ad129a426ff4289099bfb41"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e5af946f419c30f5cb98b69d40997fe8580efe78fc83c2f0f25b60d0e56efb"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f382f7ffe384ce34345e1c0b2065451267d3453cadde78946fbd99a59f0cc23c"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be156f51f3a4f369e758505ed4ae64ea88900dcb2f89d5aabb5752676d3f3d7e"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1936d134b6c513fbe934aeb668b0fee1ffd4729a3c9d8d373f3e404fbb0ce8a0"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ff8eaf4a9399eb2bebd838f16e2d1ded0955230283b07376d68947bbc2d33d"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae598a172e3a95df3383634589660d6b170cc1336fe7578115c584a99e0ba64d"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cd4ba4c18b149da11e7f1b3584813159f189dc20833709de5f3df8b1342a9759"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:0402f1629e91a4b2e4aee68043a30191e5e1b7cd2aa8dacf50b1a1bcf6b7d3ab"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:1e12319c6b304cd4c32d5db00b7a1e36bdc66179c44c5707f6faa5a889a317c0"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bbfae35ce4de4c574b386c43c78a0be176eeddfdae148cb2136f4605bebab89"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-win32.whl", hash = "sha256:7fec74c234d3097612ea80f2a80c60720eec34947066d33d34dc07a3092e8105"}, + {file = "rapidfuzz-3.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:a553cc1a80d97459d587529cc43a4c7c5ecf835f572b671107692fe9eddf3e24"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:757dfd7392ec6346bd004f8826afb3bf01d18a723c97cbe9958c733ab1a51791"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2963f4a3f763870a16ee076796be31a4a0958fbae133dbc43fc55c3968564cf5"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d2f0274595cc5b2b929c80d4e71b35041104b577e118cf789b3fe0a77b37a4c5"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f211e366e026de110a4246801d43a907cd1a10948082f47e8a4e6da76fef52"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a59472b43879012b90989603aa5a6937a869a72723b1bf2ff1a0d1edee2cc8e6"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a03863714fa6936f90caa7b4b50ea59ea32bb498cc91f74dc25485b3f8fccfe9"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd95b6b7bfb1584f806db89e1e0c8dbb9d25a30a4683880c195cc7f197eaf0c"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7183157edf0c982c0b8592686535c8b3e107f13904b36d85219c77be5cefd0d8"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ad9d74ef7c619b5b0577e909582a1928d93e07d271af18ba43e428dc3512c2a1"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b53137d81e770c82189e07a8f32722d9e4260f13a0aec9914029206ead38cac3"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:49b9ed2472394d306d5dc967a7de48b0aab599016aa4477127b20c2ed982dbf9"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:dec307b57ec2d5054d77d03ee4f654afcd2c18aee00c48014cb70bfed79597d6"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4381023fa1ff32fd5076f5d8321249a9aa62128eb3f21d7ee6a55373e672b261"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-win32.whl", hash = "sha256:8d7a072f10ee57c8413c8ab9593086d42aaff6ee65df4aa6663eecdb7c398dca"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:ebcfb5bfd0a733514352cfc94224faad8791e576a80ffe2fd40b2177bf0e7198"}, + {file = "rapidfuzz-3.6.1-cp39-cp39-win_arm64.whl", hash = "sha256:1c47d592e447738744905c18dda47ed155620204714e6df20eb1941bb1ba315e"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eef8b346ab331bec12bbc83ac75641249e6167fab3d84d8f5ca37fd8e6c7a08c"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53251e256017e2b87f7000aee0353ba42392c442ae0bafd0f6b948593d3f68c6"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dede83a6b903e3ebcd7e8137e7ff46907ce9316e9d7e7f917d7e7cdc570ee05"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4da90e4c2b444d0a171d7444ea10152e07e95972bb40b834a13bdd6de1110c"}, + {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ca3dfcf74f2b6962f411c33dd95b0adf3901266e770da6281bc96bb5a8b20de9"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bcc957c0a8bde8007f1a8a413a632a1a409890f31f73fe764ef4eac55f59ca87"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c9a50bea7a8537442834f9bc6b7d29d8729a5b6379df17c31b6ab4df948c2"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c23ceaea27e790ddd35ef88b84cf9d721806ca366199a76fd47cfc0457a81b"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b155e67fff215c09f130555002e42f7517d0ea72cbd58050abb83cb7c880cec"}, + {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3028ee8ecc48250607fa8a0adce37b56275ec3b1acaccd84aee1f68487c8557b"}, + {file = "rapidfuzz-3.6.1.tar.gz", hash = "sha256:35660bee3ce1204872574fa041c7ad7ec5175b3053a4cb6e181463fc07013de7"}, ] [package.extras] @@ -3612,17 +3636,17 @@ full = ["numpy"] [[package]] name = "redis" -version = "5.0.1" +version = "5.0.2" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"}, - {file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"}, + {file = "redis-5.0.2-py3-none-any.whl", hash = "sha256:4caa8e1fcb6f3c0ef28dba99535101d80934b7d4cd541bbb47f4a3826ee472d1"}, + {file = "redis-5.0.2.tar.gz", hash = "sha256:3f82cc80d350e93042c8e6e7a5d0596e4dd68715babffba79492733e1f367037"}, ] [package.dependencies] -async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} +async-timeout = ">=4.0.3" [package.extras] hiredis = ["hiredis (>=1.0.0)"] @@ -3630,13 +3654,13 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)" [[package]] name = "referencing" -version = "0.31.0" +version = "0.33.0" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.31.0-py3-none-any.whl", hash = "sha256:381b11e53dd93babb55696c71cf42aef2d36b8a150c49bf0bc301e36d536c882"}, - {file = "referencing-0.31.0.tar.gz", hash = "sha256:cc28f2c88fbe7b961a7817a0abc034c09a1e36358f82fedb4ffdf29a25398863"}, + {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, + {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, ] [package.dependencies] @@ -3645,99 +3669,104 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2023.10.3" +version = "2023.12.25" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.7" files = [ - {file = "regex-2023.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4c34d4f73ea738223a094d8e0ffd6d2c1a1b4c175da34d6b0de3d8d69bee6bcc"}, - {file = "regex-2023.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8f4e49fc3ce020f65411432183e6775f24e02dff617281094ba6ab079ef0915"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cd1bccf99d3ef1ab6ba835308ad85be040e6a11b0977ef7ea8c8005f01a3c29"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81dce2ddc9f6e8f543d94b05d56e70d03a0774d32f6cca53e978dc01e4fc75b8"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c6b4d23c04831e3ab61717a707a5d763b300213db49ca680edf8bf13ab5d91b"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c15ad0aee158a15e17e0495e1e18741573d04eb6da06d8b84af726cfc1ed02ee"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6239d4e2e0b52c8bd38c51b760cd870069f0bdf99700a62cd509d7a031749a55"}, - {file = "regex-2023.10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4a8bf76e3182797c6b1afa5b822d1d5802ff30284abe4599e1247be4fd6b03be"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9c727bbcf0065cbb20f39d2b4f932f8fa1631c3e01fcedc979bd4f51fe051c5"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3ccf2716add72f80714b9a63899b67fa711b654be3fcdd34fa391d2d274ce767"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:107ac60d1bfdc3edb53be75e2a52aff7481b92817cfdddd9b4519ccf0e54a6ff"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:00ba3c9818e33f1fa974693fb55d24cdc8ebafcb2e4207680669d8f8d7cca79a"}, - {file = "regex-2023.10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0a47efb1dbef13af9c9a54a94a0b814902e547b7f21acb29434504d18f36e3a"}, - {file = "regex-2023.10.3-cp310-cp310-win32.whl", hash = "sha256:36362386b813fa6c9146da6149a001b7bd063dabc4d49522a1f7aa65b725c7ec"}, - {file = "regex-2023.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:c65a3b5330b54103e7d21cac3f6bf3900d46f6d50138d73343d9e5b2900b2353"}, - {file = "regex-2023.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90a79bce019c442604662d17bf69df99090e24cdc6ad95b18b6725c2988a490e"}, - {file = "regex-2023.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c7964c2183c3e6cce3f497e3a9f49d182e969f2dc3aeeadfa18945ff7bdd7051"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef80829117a8061f974b2fda8ec799717242353bff55f8a29411794d635d964"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5addc9d0209a9afca5fc070f93b726bf7003bd63a427f65ef797a931782e7edc"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c148bec483cc4b421562b4bcedb8e28a3b84fcc8f0aa4418e10898f3c2c0eb9b"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d1f21af4c1539051049796a0f50aa342f9a27cde57318f2fc41ed50b0dbc4ac"}, - {file = "regex-2023.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b9ac09853b2a3e0d0082104036579809679e7715671cfbf89d83c1cb2a30f58"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ebedc192abbc7fd13c5ee800e83a6df252bec691eb2c4bedc9f8b2e2903f5e2a"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d8a993c0a0ffd5f2d3bda23d0cd75e7086736f8f8268de8a82fbc4bd0ac6791e"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:be6b7b8d42d3090b6c80793524fa66c57ad7ee3fe9722b258aec6d0672543fd0"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4023e2efc35a30e66e938de5aef42b520c20e7eda7bb5fb12c35e5d09a4c43f6"}, - {file = "regex-2023.10.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0d47840dc05e0ba04fe2e26f15126de7c755496d5a8aae4a08bda4dd8d646c54"}, - {file = "regex-2023.10.3-cp311-cp311-win32.whl", hash = "sha256:9145f092b5d1977ec8c0ab46e7b3381b2fd069957b9862a43bd383e5c01d18c2"}, - {file = "regex-2023.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:b6104f9a46bd8743e4f738afef69b153c4b8b592d35ae46db07fc28ae3d5fb7c"}, - {file = "regex-2023.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff507ae210371d4b1fe316d03433ac099f184d570a1a611e541923f78f05037"}, - {file = "regex-2023.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be5e22bbb67924dea15039c3282fa4cc6cdfbe0cbbd1c0515f9223186fc2ec5f"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a992f702c9be9c72fa46f01ca6e18d131906a7180950958f766c2aa294d4b41"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7434a61b158be563c1362d9071358f8ab91b8d928728cd2882af060481244c9e"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2169b2dcabf4e608416f7f9468737583ce5f0a6e8677c4efbf795ce81109d7c"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9e908ef5889cda4de038892b9accc36d33d72fb3e12c747e2799a0e806ec841"}, - {file = "regex-2023.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12bd4bc2c632742c7ce20db48e0d99afdc05e03f0b4c1af90542e05b809a03d9"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bc72c231f5449d86d6c7d9cc7cd819b6eb30134bb770b8cfdc0765e48ef9c420"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bce8814b076f0ce5766dc87d5a056b0e9437b8e0cd351b9a6c4e1134a7dfbda9"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ba7cd6dc4d585ea544c1412019921570ebd8a597fabf475acc4528210d7c4a6f"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b0c7d2f698e83f15228ba41c135501cfe7d5740181d5903e250e47f617eb4292"}, - {file = "regex-2023.10.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5a8f91c64f390ecee09ff793319f30a0f32492e99f5dc1c72bc361f23ccd0a9a"}, - {file = "regex-2023.10.3-cp312-cp312-win32.whl", hash = "sha256:ad08a69728ff3c79866d729b095872afe1e0557251da4abb2c5faff15a91d19a"}, - {file = "regex-2023.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:39cdf8d141d6d44e8d5a12a8569d5a227f645c87df4f92179bd06e2e2705e76b"}, - {file = "regex-2023.10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4a3ee019a9befe84fa3e917a2dd378807e423d013377a884c1970a3c2792d293"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76066d7ff61ba6bf3cb5efe2428fc82aac91802844c022d849a1f0f53820502d"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe50b61bab1b1ec260fa7cd91106fa9fece57e6beba05630afe27c71259c59b"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fd88f373cb71e6b59b7fa597e47e518282455c2734fd4306a05ca219a1991b0"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ab05a182c7937fb374f7e946f04fb23a0c0699c0450e9fb02ef567412d2fa3"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dac37cf08fcf2094159922edc7a2784cfcc5c70f8354469f79ed085f0328ebdf"}, - {file = "regex-2023.10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e54ddd0bb8fb626aa1f9ba7b36629564544954fff9669b15da3610c22b9a0991"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3367007ad1951fde612bf65b0dffc8fd681a4ab98ac86957d16491400d661302"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:16f8740eb6dbacc7113e3097b0a36065a02e37b47c936b551805d40340fb9971"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:f4f2ca6df64cbdd27f27b34f35adb640b5d2d77264228554e68deda54456eb11"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:39807cbcbe406efca2a233884e169d056c35aa7e9f343d4e78665246a332f597"}, - {file = "regex-2023.10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7eece6fbd3eae4a92d7c748ae825cbc1ee41a89bb1c3db05b5578ed3cfcfd7cb"}, - {file = "regex-2023.10.3-cp37-cp37m-win32.whl", hash = "sha256:ce615c92d90df8373d9e13acddd154152645c0dc060871abf6bd43809673d20a"}, - {file = "regex-2023.10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0f649fa32fe734c4abdfd4edbb8381c74abf5f34bc0b3271ce687b23729299ed"}, - {file = "regex-2023.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b98b7681a9437262947f41c7fac567c7e1f6eddd94b0483596d320092004533"}, - {file = "regex-2023.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:91dc1d531f80c862441d7b66c4505cd6ea9d312f01fb2f4654f40c6fdf5cc37a"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82fcc1f1cc3ff1ab8a57ba619b149b907072e750815c5ba63e7aa2e1163384a4"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7979b834ec7a33aafae34a90aad9f914c41fd6eaa8474e66953f3f6f7cbd4368"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef71561f82a89af6cfcbee47f0fabfdb6e63788a9258e913955d89fdd96902ab"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd829712de97753367153ed84f2de752b86cd1f7a88b55a3a775eb52eafe8a94"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00e871d83a45eee2f8688d7e6849609c2ca2a04a6d48fba3dff4deef35d14f07"}, - {file = "regex-2023.10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:706e7b739fdd17cb89e1fbf712d9dc21311fc2333f6d435eac2d4ee81985098c"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cc3f1c053b73f20c7ad88b0d1d23be7e7b3901229ce89f5000a8399746a6e039"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f85739e80d13644b981a88f529d79c5bdf646b460ba190bffcaf6d57b2a9863"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:741ba2f511cc9626b7561a440f87d658aabb3d6b744a86a3c025f866b4d19e7f"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e77c90ab5997e85901da85131fd36acd0ed2221368199b65f0d11bca44549711"}, - {file = "regex-2023.10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:979c24cbefaf2420c4e377ecd1f165ea08cc3d1fbb44bdc51bccbbf7c66a2cb4"}, - {file = "regex-2023.10.3-cp38-cp38-win32.whl", hash = "sha256:58837f9d221744d4c92d2cf7201c6acd19623b50c643b56992cbd2b745485d3d"}, - {file = "regex-2023.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:c55853684fe08d4897c37dfc5faeff70607a5f1806c8be148f1695be4a63414b"}, - {file = "regex-2023.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2c54e23836650bdf2c18222c87f6f840d4943944146ca479858404fedeb9f9af"}, - {file = "regex-2023.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69c0771ca5653c7d4b65203cbfc5e66db9375f1078689459fe196fe08b7b4930"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ac965a998e1388e6ff2e9781f499ad1eaa41e962a40d11c7823c9952c77123e"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c0e8fae5b27caa34177bdfa5a960c46ff2f78ee2d45c6db15ae3f64ecadde14"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c56c3d47da04f921b73ff9415fbaa939f684d47293f071aa9cbb13c94afc17d"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ef1e014eed78ab650bef9a6a9cbe50b052c0aebe553fb2881e0453717573f52"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d29338556a59423d9ff7b6eb0cb89ead2b0875e08fe522f3e068b955c3e7b59b"}, - {file = "regex-2023.10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9c6d0ced3c06d0f183b73d3c5920727268d2201aa0fe6d55c60d68c792ff3588"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:994645a46c6a740ee8ce8df7911d4aee458d9b1bc5639bc968226763d07f00fa"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:66e2fe786ef28da2b28e222c89502b2af984858091675044d93cb50e6f46d7af"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:11175910f62b2b8c055f2b089e0fedd694fe2be3941b3e2633653bc51064c528"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:06e9abc0e4c9ab4779c74ad99c3fc10d3967d03114449acc2c2762ad4472b8ca"}, - {file = "regex-2023.10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fb02e4257376ae25c6dd95a5aec377f9b18c09be6ebdefa7ad209b9137b73d48"}, - {file = "regex-2023.10.3-cp39-cp39-win32.whl", hash = "sha256:3b2c3502603fab52d7619b882c25a6850b766ebd1b18de3df23b2f939360e1bd"}, - {file = "regex-2023.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:adbccd17dcaff65704c856bd29951c58a1bd4b2b0f8ad6b826dbd543fe740988"}, - {file = "regex-2023.10.3.tar.gz", hash = "sha256:3fef4f844d2290ee0ba57addcec17eec9e3df73f10a2748485dfd6a3a188cc0f"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, ] [[package]] @@ -3796,13 +3825,13 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "responses" -version = "0.24.1" +version = "0.25.0" description = "A utility library for mocking out the `requests` Python library." optional = false python-versions = ">=3.8" files = [ - {file = "responses-0.24.1-py3-none-any.whl", hash = "sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9"}, - {file = "responses-0.24.1.tar.gz", hash = "sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c"}, + {file = "responses-0.25.0-py3-none-any.whl", hash = "sha256:2f0b9c2b6437db4b528619a77e5d565e4ec2a9532162ac1a131a83529db7be1a"}, + {file = "responses-0.25.0.tar.gz", hash = "sha256:01ae6a02b4f34e39bffceb0fc6786b67a25eae919c6368d05eabc8d9576c2a66"}, ] [package.dependencies] @@ -3840,13 +3869,13 @@ files = [ [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] @@ -3858,110 +3887,110 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rpds-py" -version = "0.13.0" +version = "0.18.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.13.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:1758197cc8d7ff383c07405f188253535b4aa7fa745cbc54d221ae84b18e0702"}, - {file = "rpds_py-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:715df74cbcef4387d623c917f295352127f4b3e0388038d68fa577b4e4c6e540"}, - {file = "rpds_py-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a9cec0f49df9bac252d92f138c0d7708d98828e21fd57db78087d8f50b5656"}, - {file = "rpds_py-0.13.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c2545bba02f68abdf398ef4990dc77592cc1e5d29438b35b3a3ca34d171fb4b"}, - {file = "rpds_py-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:95375c44ffb9ea2bc25d67fb66e726ea266ff1572df50b9556fe28a5f3519cd7"}, - {file = "rpds_py-0.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:54e513df45a8a9419e7952ffd26ac9a5b7b1df97fe72530421794b0de29f9d72"}, - {file = "rpds_py-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a25f514a53927b6b4bd04a9a6a13b55209df54f548660eeed673336c0c946d14"}, - {file = "rpds_py-0.13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1a920fa679ec2758411d66bf68840b0a21317b9954ab0e973742d723bb67709"}, - {file = "rpds_py-0.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f9339d1404b87e6d8cb35e485945753be57a99ab9bb389f42629215b2f6bda0f"}, - {file = "rpds_py-0.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c99f9dda2c959f7bb69a7125e192c74fcafb7a534a95ccf49313ae3a04807804"}, - {file = "rpds_py-0.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bad6758df5f1042b35683bd1811d5432ac1b17700a5a2a51fdc293f7df5f7827"}, - {file = "rpds_py-0.13.0-cp310-none-win32.whl", hash = "sha256:2a29ec68fa9655ce9501bc6ae074b166e8b45c2dfcd2d71d90d1a61758ed8c73"}, - {file = "rpds_py-0.13.0-cp310-none-win_amd64.whl", hash = "sha256:244be953f13f148b0071d67a610f89cd72eb5013a147e517d6ca3f3f3b7e0380"}, - {file = "rpds_py-0.13.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:240279ca0b2afd6d4710afce1c94bf9e75fc161290bf62c0feba64d64780d80b"}, - {file = "rpds_py-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:25c9727da2dabc93664a18eda7a70feedf478f0c4c8294e4cdba7f60a479a246"}, - {file = "rpds_py-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981e46e1e5064f95460381bff4353783b4b5ce351c930e5b507ebe0278c61dac"}, - {file = "rpds_py-0.13.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6052bb47ea583646b8ff562acacb9a2ec5ec847267049cbae3919671929e94c6"}, - {file = "rpds_py-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f591ff8cc834fa01ca5899ab5edcd7ee590492a9cdcf43424ac142e731ce3e"}, - {file = "rpds_py-0.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62772259b3381e2aabf274c74fd1e1ac03b0524de0a6593900684becfa8cfe4b"}, - {file = "rpds_py-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4de9d20fe68c16b4d97f551a09920745add0c86430262230528b83c2ed2fe90"}, - {file = "rpds_py-0.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b70a54fb628c1d6400e351674a31ba63d2912b8c5b707f99b408674a5d8b69ab"}, - {file = "rpds_py-0.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2063ab9cd1be7ef6b5ed0f408e2bdf32c060b6f40c097a468f32864731302636"}, - {file = "rpds_py-0.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:84f7f3f18d29a1c645729634003d21d84028bd9c2fd78eba9d028998f46fa5aa"}, - {file = "rpds_py-0.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7c7ddc8d1a64623068da5a15e28001fbd0f0aff754aae7a75a4be5042191638"}, - {file = "rpds_py-0.13.0-cp311-none-win32.whl", hash = "sha256:8a33d2b6340261191bb59adb5a453fa6c7d99de85552bd4e8196411f0509c9bf"}, - {file = "rpds_py-0.13.0-cp311-none-win_amd64.whl", hash = "sha256:8b9c1dd90461940315981499df62a627571c4f0992e8bafc5396d33916224cac"}, - {file = "rpds_py-0.13.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:15a2d542de5cbfc6abddc4846d9412b59f8ee9c8dfa0b9c92a29321297c91745"}, - {file = "rpds_py-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8dd69e01b29ff45a0062cad5c480d8aa9301c3ef09da471f86337a78eb2d3405"}, - {file = "rpds_py-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efdd02971a02f98492a72b25484f1f6125fb9f2166e48cc4c9bfa563349c851b"}, - {file = "rpds_py-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91ca9aaee7ccdfa66d800b5c4ec634fefca947721bab52d6ad2f6350969a3771"}, - {file = "rpds_py-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afcec1f5b09d0db70aeb2d90528a9164acb61841a3124e28f6ac0137f4c36cb4"}, - {file = "rpds_py-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c6824673f66c47f7ee759c21e973bfce3ceaf2c25cb940cb45b41105dc914e8"}, - {file = "rpds_py-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50b6d80925dfeb573fc5e38582fb9517c6912dc462cc858a11c8177b0837127a"}, - {file = "rpds_py-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a1a38512925829784b5dc38591c757b80cfce115c72c594dc59567dab62b9c4"}, - {file = "rpds_py-0.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:977c6123c359dcc70ce3161b781ab70b0d342de2666944b776617e01a0a7822a"}, - {file = "rpds_py-0.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c472409037e05ed87b99430f97a6b82130328bb977502813547e8ee6a3392502"}, - {file = "rpds_py-0.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:28bb22019f4a783ea06a6b81437d5996551869e8a722ee8720b744f7684d97f4"}, - {file = "rpds_py-0.13.0-cp312-none-win32.whl", hash = "sha256:46be9c0685cce2ea02151aa8308f2c1b78581be41a5dd239448a941a210ef5dd"}, - {file = "rpds_py-0.13.0-cp312-none-win_amd64.whl", hash = "sha256:3c5b9ad4d3e05dfcf8629f0d534f92610e9805dbce2fcb9b3c801ddb886431d5"}, - {file = "rpds_py-0.13.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:66eb5aa36e857f768c598d2082fafb733eaf53e06e1169c6b4de65636e04ffd0"}, - {file = "rpds_py-0.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c9f4c2b7d989426e9fe9b720211172cf10eb5f7aa16c63de2e5dc61457abcf35"}, - {file = "rpds_py-0.13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e37dfffe8959a492b7b331995f291847a41a035b4aad82d6060f38e8378a2b"}, - {file = "rpds_py-0.13.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8220321f2dccd9d66f72639185247cb7bbdd90753bf0b6bfca0fa31dba8af23c"}, - {file = "rpds_py-0.13.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8f1d466a9747213d3cf7e1afec849cc51edb70d5b4ae9a82eca0f172bfbb6d0"}, - {file = "rpds_py-0.13.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c4c4b4ff3de834ec5c1c690e5a18233ca78547d003eb83664668ccf09ef1398"}, - {file = "rpds_py-0.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:525d19ef0a999229ef0f0a7687ab2c9a00d1b6a47a005006f4d8c4b8975fdcec"}, - {file = "rpds_py-0.13.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0982b59d014efb84a57128e7e69399fb29ad8f2da5b0a5bcbfd12e211c00492e"}, - {file = "rpds_py-0.13.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f714dd5b705f1c394d1b361d96486c4981055c434a7eafb1a3147ac75e34a3de"}, - {file = "rpds_py-0.13.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:766b573a964389ef0d91a26bb31e1b59dbc5d06eff7707f3dfcec23d93080ba3"}, - {file = "rpds_py-0.13.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2ed65ad3fc5065d13e31e90794e0b52e405b63ae4fab1080caeaadc10a3439c5"}, - {file = "rpds_py-0.13.0-cp38-none-win32.whl", hash = "sha256:9645f7fe10a68b2396d238250b4b264c2632d2eb6ce2cb90aa0fe08adee194be"}, - {file = "rpds_py-0.13.0-cp38-none-win_amd64.whl", hash = "sha256:42d0ad129c102856a364ccc7d356faec017af86b3543a8539795f22b6cabad11"}, - {file = "rpds_py-0.13.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:95c11647fac2a3515ea2614a79e14b7c75025724ad54c91c7db4a6ea5c25ef19"}, - {file = "rpds_py-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9435bf4832555c4f769c6be9401664357be33d5f5d8dc58f5c20fb8d21e2c45d"}, - {file = "rpds_py-0.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b1d671a74395344239ee3adbcd8c496525f6a2b2e54c40fec69620a31a8dcb"}, - {file = "rpds_py-0.13.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13c8061115f1468de6ffdfb1d31b446e1bd814f1ff6e556862169aacb9fbbc5d"}, - {file = "rpds_py-0.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a78861123b002725633871a2096c3a4313224aab3d11b953dced87cfba702418"}, - {file = "rpds_py-0.13.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97c1be5a018cdad54fa7e5f7d36b9ab45ef941a1d185987f18bdab0a42344012"}, - {file = "rpds_py-0.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e33b17915c8e4fb2ea8b91bb4c46cba92242c63dd38b87e869ead5ba217e2970"}, - {file = "rpds_py-0.13.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:153b6d8cf7ae4b9ffd09de6abeda661e351e3e06eaafd18a8c104ea00099b131"}, - {file = "rpds_py-0.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:da2852201e8e00c86be82c43d6893e6c380ef648ae53f337ffd1eaa35e3dfb8a"}, - {file = "rpds_py-0.13.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a2383f400691fd7bd63347d4d75eb2fd525de9d901799a33a4e896c9885609f8"}, - {file = "rpds_py-0.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d5bf560634ea6e9a59ceb2181a6cd6195a03f48cef9a400eb15e197e18f14548"}, - {file = "rpds_py-0.13.0-cp39-none-win32.whl", hash = "sha256:fdaef49055cc0c701fb17b9b34a38ef375e5cdb230b3722d4a12baf9b7cbc6d3"}, - {file = "rpds_py-0.13.0-cp39-none-win_amd64.whl", hash = "sha256:26660c74a20fe249fad75ca00bbfcf60e57c3fdbde92971c88a20e07fea1de64"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:28324f2f0247d407daabf7ff357ad9f36126075c92a0cf5319396d96ff4e1248"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b431c2c0ff1ea56048a2b066d99d0c2d151ae7625b20be159b7e699f3e80390b"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7472bd60a8293217444bdc6a46e516feb8d168da44d5f3fccea0336e88e3b79a"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:169063f346b8fd84f47d986c9c48e6094eb38b839c1287e7cb886b8a2b32195d"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eef7ee7c70f8b8698be468d54f9f5e01804f3a1dd5657e8a96363dbd52b9b5ec"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:762013dd59df12380c5444f61ccbf9ae1297027cabbd7aa25891f724ebf8c8f7"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:152570689a27ae0be1d5f50b21dad38d450b9227d0974f23bd400400ea087e88"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d70a93a40e55da117c511ddc514642bc7d59a95a99137168a5f3f2f876b47962"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e6c6fed07d13b9e0fb689356c40c81f1aa92e3c9d91d8fd5816a0348ccd999f7"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:cdded3cf9e36840b09ccef714d5fa74a03f4eb6cf81e694226ed9cb5e6f90de0"}, - {file = "rpds_py-0.13.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e1f40faf406c52c7ae7d208b9140377c06397248978ccb03fbfbb30a0571e359"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c10326e30c97a95b7e1d75e5200ef0b9827aa0f861e331e43b15dfdfd63e669b"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:afde37e3763c602d0385bce5c12f262e7b1dd2a0f323e239fa9d7b2d4d5d8509"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4084ab6840bd4d79eff3b5f497add847a7db31ce5a0c2d440c90b2d2b7011857"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c9c9cb48ab77ebfa47db25b753f594d4f44959cfe43b713439ca6e3c9329671"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:533d728ea5ad5253af3395102723ca8a77b62de47b2295155650c9a88fcdeec8"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f22cab655b41033d430f20266bf563b35038a7f01c9a099b0ccfd30a7fb9247"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a0507342c37132813449393e6e6f351bbff376031cfff1ee6e616402ac7908"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4eb1faf8e2ee9a2de3cb3ae4c8c355914cdc85f2cd7f27edf76444c9550ce1e7"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:a61a152d61e3ae26e0bbba7b2f568f6f25ca0abdeb6553eca7e7c45b59d9b1a9"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:e499bf2200eb74774a6f85a7465e3bc5273fa8ef0055590d97a88c1e7ea02eea"}, - {file = "rpds_py-0.13.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1e5becd0de924616ca9a12abeb6458568d1dc8fe5c670d5cdb738402a8a8429d"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:70cfe098d915f566eeebcb683f49f9404d2f948432891b6e075354336eda9dfb"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:2e73511e88368f93c24efe7c9a20b319eaa828bc7431f8a17713efb9e31a39fa"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c07cb9bcccd08f9bc2fd05bf586479df4272ea5a6a70fbcb59b018ed48a5a84d"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c4e84016ba225e09df20fed8befe8c68d14fbeff6078f4a0ff907ae2095e17e"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ad465e5a70580ca9c1944f43a9a71bca3a7b74554347fc96ca0479eca8981f9"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:189aebd44a07fa7b7966cf78b85bde8335b0b6c3b1c4ef5589f8c03176830107"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f50ca0460f1f7a89ab9b8355d83ac993d5998ad4218e76654ecf8afe648d8aa"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f6c225011467021879c0482316e42d8a28852fc29f0c15d2a435ff457cadccd4"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1e63b32b856c0f08a56b76967d61b6ad811d8d330a8aebb9d21afadd82a296f6"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e5fbe9800f09c56967fda88c4d9272955e781699a66102bd098f22511a3f260"}, - {file = "rpds_py-0.13.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:fea99967d4a978ce95dd52310bcb4a943b77c61725393bca631b0908047d6e2f"}, - {file = "rpds_py-0.13.0.tar.gz", hash = "sha256:35cc91cbb0b775705e0feb3362490b8418c408e9e3c3b9cb3b02f6e495f03ee7"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, ] [[package]] @@ -4012,72 +4041,72 @@ jeepney = ">=0.6" [[package]] name = "setuptools" -version = "69.0.3" +version = "69.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, - {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, + {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, + {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "shapely" -version = "2.0.2" +version = "2.0.3" description = "Manipulation and analysis of geometric objects" optional = false python-versions = ">=3.7" files = [ - {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6ca8cffbe84ddde8f52b297b53f8e0687bd31141abb2c373fd8a9f032df415d6"}, - {file = "shapely-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:baa14fc27771e180c06b499a0a7ba697c7988c7b2b6cba9a929a19a4d2762de3"}, - {file = "shapely-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36480e32c434d168cdf2f5e9862c84aaf4d714a43a8465ae3ce8ff327f0affb7"}, - {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ef753200cbffd4f652efb2c528c5474e5a14341a473994d90ad0606522a46a2"}, - {file = "shapely-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9a41ff4323fc9d6257759c26eb1cf3a61ebc7e611e024e6091f42977303fd3a"}, - {file = "shapely-2.0.2-cp310-cp310-win32.whl", hash = "sha256:72b5997272ae8c25f0fd5b3b967b3237e87fab7978b8d6cd5fa748770f0c5d68"}, - {file = "shapely-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:34eac2337cbd67650248761b140d2535855d21b969d76d76123317882d3a0c1a"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b0c052709c8a257c93b0d4943b0b7a3035f87e2d6a8ac9407b6a992d206422f"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2d217e56ae067e87b4e1731d0dc62eebe887ced729ba5c2d4590e9e3e9fdbd88"}, - {file = "shapely-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94ac128ae2ab4edd0bffcd4e566411ea7bdc738aeaf92c32a8a836abad725f9f"}, - {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa3ee28f5e63a130ec5af4dc3c4cb9c21c5788bb13c15e89190d163b14f9fb89"}, - {file = "shapely-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737dba15011e5a9b54a8302f1748b62daa207c9bc06f820cd0ad32a041f1c6f2"}, - {file = "shapely-2.0.2-cp311-cp311-win32.whl", hash = "sha256:45ac6906cff0765455a7b49c1670af6e230c419507c13e2f75db638c8fc6f3bd"}, - {file = "shapely-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:dc9342fc82e374130db86a955c3c4525bfbf315a248af8277a913f30911bed9e"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:06f193091a7c6112fc08dfd195a1e3846a64306f890b151fa8c63b3e3624202c"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eebe544df5c018134f3c23b6515877f7e4cd72851f88a8d0c18464f414d141a2"}, - {file = "shapely-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e92e7c255f89f5cdf777690313311f422aa8ada9a3205b187113274e0135cd8"}, - {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be46d5509b9251dd9087768eaf35a71360de6afac82ce87c636990a0871aa18b"}, - {file = "shapely-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5533a925d8e211d07636ffc2fdd9a7f9f13d54686d00577eeb11d16f00be9c4"}, - {file = "shapely-2.0.2-cp312-cp312-win32.whl", hash = "sha256:084b023dae8ad3d5b98acee9d3bf098fdf688eb0bb9b1401e8b075f6a627b611"}, - {file = "shapely-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:ea84d1cdbcf31e619d672b53c4532f06253894185ee7acb8ceb78f5f33cbe033"}, - {file = "shapely-2.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ed1e99702125e7baccf401830a3b94d810d5c70b329b765fe93451fe14cf565b"}, - {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7d897e6bdc6bc64f7f65155dbbb30e49acaabbd0d9266b9b4041f87d6e52b3a"}, - {file = "shapely-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0521d76d1e8af01e712db71da9096b484f081e539d4f4a8c97342e7971d5e1b4"}, - {file = "shapely-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:5324be299d4c533ecfcfd43424dfd12f9428fd6f12cda38a4316da001d6ef0ea"}, - {file = "shapely-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:78128357a0cee573257a0c2c388d4b7bf13cb7dbe5b3fe5d26d45ebbe2a39e25"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:87dc2be34ac3a3a4a319b963c507ac06682978a5e6c93d71917618b14f13066e"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42997ac806e4583dad51c80a32d38570fd9a3d4778f5e2c98f9090aa7db0fe91"}, - {file = "shapely-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ccfd5fa10a37e67dbafc601c1ddbcbbfef70d34c3f6b0efc866ddbdb55893a6c"}, - {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7c95d3379ae3abb74058938a9fcbc478c6b2e28d20dace38f8b5c587dde90aa"}, - {file = "shapely-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a21353d28209fb0d8cc083e08ca53c52666e0d8a1f9bbe23b6063967d89ed24"}, - {file = "shapely-2.0.2-cp38-cp38-win32.whl", hash = "sha256:03e63a99dfe6bd3beb8d5f41ec2086585bb969991d603f9aeac335ad396a06d4"}, - {file = "shapely-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:c6fd29fbd9cd76350bd5cc14c49de394a31770aed02d74203e23b928f3d2f1aa"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f217d28ecb48e593beae20a0082a95bd9898d82d14b8fcb497edf6bff9a44d7"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:394e5085b49334fd5b94fa89c086edfb39c3ecab7f669e8b2a4298b9d523b3a5"}, - {file = "shapely-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd3ad17b64466a033848c26cb5b509625c87d07dcf39a1541461cacdb8f7e91c"}, - {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d41a116fcad58048d7143ddb01285e1a8780df6dc1f56c3b1e1b7f12ed296651"}, - {file = "shapely-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dea9a0651333cf96ef5bb2035044e3ad6a54f87d90e50fe4c2636debf1b77abc"}, - {file = "shapely-2.0.2-cp39-cp39-win32.whl", hash = "sha256:b8eb0a92f7b8c74f9d8fdd1b40d395113f59bd8132ca1348ebcc1f5aece94b96"}, - {file = "shapely-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:794affd80ca0f2c536fc948a3afa90bd8fb61ebe37fe873483ae818e7f21def4"}, - {file = "shapely-2.0.2.tar.gz", hash = "sha256:1713cc04c171baffc5b259ba8531c58acc2a301707b7f021d88a15ed090649e7"}, + {file = "shapely-2.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:af7e9abe180b189431b0f490638281b43b84a33a960620e6b2e8d3e3458b61a1"}, + {file = "shapely-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98040462b36ced9671e266b95c326b97f41290d9d17504a1ee4dc313a7667b9c"}, + {file = "shapely-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71eb736ef2843f23473c6e37f6180f90f0a35d740ab284321548edf4e55d9a52"}, + {file = "shapely-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:881eb9dbbb4a6419667e91fcb20313bfc1e67f53dbb392c6840ff04793571ed1"}, + {file = "shapely-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f10d2ccf0554fc0e39fad5886c839e47e207f99fdf09547bc687a2330efda35b"}, + {file = "shapely-2.0.3-cp310-cp310-win32.whl", hash = "sha256:6dfdc077a6fcaf74d3eab23a1ace5abc50c8bce56ac7747d25eab582c5a2990e"}, + {file = "shapely-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:64c5013dacd2d81b3bb12672098a0b2795c1bf8190cfc2980e380f5ef9d9e4d9"}, + {file = "shapely-2.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:56cee3e4e8159d6f2ce32e421445b8e23154fd02a0ac271d6a6c0b266a8e3cce"}, + {file = "shapely-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:619232c8276fded09527d2a9fd91a7885ff95c0ff9ecd5e3cb1e34fbb676e2ae"}, + {file = "shapely-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2a7d256db6f5b4b407dc0c98dd1b2fcf1c9c5814af9416e5498d0a2e4307a4b"}, + {file = "shapely-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45f0c8cd4583647db3216d965d49363e6548c300c23fd7e57ce17a03f824034"}, + {file = "shapely-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13cb37d3826972a82748a450328fe02a931dcaed10e69a4d83cc20ba021bc85f"}, + {file = "shapely-2.0.3-cp311-cp311-win32.whl", hash = "sha256:9302d7011e3e376d25acd30d2d9e70d315d93f03cc748784af19b00988fc30b1"}, + {file = "shapely-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6b464f2666b13902835f201f50e835f2f153f37741db88f68c7f3b932d3505fa"}, + {file = "shapely-2.0.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e86e7cb8e331a4850e0c2a8b2d66dc08d7a7b301b8d1d34a13060e3a5b4b3b55"}, + {file = "shapely-2.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c91981c99ade980fc49e41a544629751a0ccd769f39794ae913e53b07b2f78b9"}, + {file = "shapely-2.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd45d456983dc60a42c4db437496d3f08a4201fbf662b69779f535eb969660af"}, + {file = "shapely-2.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:882fb1ffc7577e88c1194f4f1757e277dc484ba096a3b94844319873d14b0f2d"}, + {file = "shapely-2.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9f2d93bff2ea52fa93245798cddb479766a18510ea9b93a4fb9755c79474889"}, + {file = "shapely-2.0.3-cp312-cp312-win32.whl", hash = "sha256:99abad1fd1303b35d991703432c9481e3242b7b3a393c186cfb02373bf604004"}, + {file = "shapely-2.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:6f555fe3304a1f40398977789bc4fe3c28a11173196df9ece1e15c5bc75a48db"}, + {file = "shapely-2.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983cc418c1fa160b7d797cfef0e0c9f8c6d5871e83eae2c5793fce6a837fad9"}, + {file = "shapely-2.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18bddb8c327f392189a8d5d6b9a858945722d0bb95ccbd6a077b8e8fc4c7890d"}, + {file = "shapely-2.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:442f4dcf1eb58c5a4e3428d88e988ae153f97ab69a9f24e07bf4af8038536325"}, + {file = "shapely-2.0.3-cp37-cp37m-win32.whl", hash = "sha256:31a40b6e3ab00a4fd3a1d44efb2482278642572b8e0451abdc8e0634b787173e"}, + {file = "shapely-2.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:59b16976c2473fec85ce65cc9239bef97d4205ab3acead4e6cdcc72aee535679"}, + {file = "shapely-2.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:705efbce1950a31a55b1daa9c6ae1c34f1296de71ca8427974ec2f27d57554e3"}, + {file = "shapely-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:601c5c0058a6192df704cb889439f64994708563f57f99574798721e9777a44b"}, + {file = "shapely-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f24ecbb90a45c962b3b60d8d9a387272ed50dc010bfe605f1d16dfc94772d8a1"}, + {file = "shapely-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c2a2989222c6062f7a0656e16276c01bb308bc7e5d999e54bf4e294ce62e76"}, + {file = "shapely-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42bceb9bceb3710a774ce04908fda0f28b291323da2688f928b3f213373b5aee"}, + {file = "shapely-2.0.3-cp38-cp38-win32.whl", hash = "sha256:54d925c9a311e4d109ec25f6a54a8bd92cc03481a34ae1a6a92c1fe6729b7e01"}, + {file = "shapely-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:300d203b480a4589adefff4c4af0b13919cd6d760ba3cbb1e56275210f96f654"}, + {file = "shapely-2.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:083d026e97b6c1f4a9bd2a9171c7692461092ed5375218170d91705550eecfd5"}, + {file = "shapely-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:27b6e1910094d93e9627f2664121e0e35613262fc037051680a08270f6058daf"}, + {file = "shapely-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:71b2de56a9e8c0e5920ae5ddb23b923490557ac50cb0b7fa752761bf4851acde"}, + {file = "shapely-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d279e56bbb68d218d63f3efc80c819cedcceef0e64efbf058a1df89dc57201b"}, + {file = "shapely-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88566d01a30f0453f7d038db46bc83ce125e38e47c5f6bfd4c9c287010e9bf74"}, + {file = "shapely-2.0.3-cp39-cp39-win32.whl", hash = "sha256:58afbba12c42c6ed44c4270bc0e22f3dadff5656d711b0ad335c315e02d04707"}, + {file = "shapely-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:5026b30433a70911979d390009261b8c4021ff87c7c3cbd825e62bb2ffa181bc"}, + {file = "shapely-2.0.3.tar.gz", hash = "sha256:4d65d0aa7910af71efa72fd6447e02a8e5dd44da81a983de9d736d6e6ccbe674"}, ] [package.dependencies] -numpy = ">=1.14" +numpy = ">=1.14,<2" [package.extras] docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] @@ -4115,17 +4144,6 @@ files = [ {file = "smartypants-2.0.1-py2.py3-none-any.whl", hash = "sha256:8db97f7cbdf08d15b158a86037cd9e116b4cf37703d24e0419a0d64ca5808f0d"}, ] -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - [[package]] name = "sortedcontainers" version = "2.4.0" @@ -4219,13 +4237,13 @@ sqlcipher = ["sqlcipher3-binary"] [[package]] name = "stevedore" -version = "5.1.0" +version = "5.2.0" description = "Manage dynamic plugins for Python applications" optional = false python-versions = ">=3.8" files = [ - {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, - {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, + {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, + {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, ] [package.dependencies] @@ -4271,57 +4289,57 @@ files = [ [[package]] name = "tomlkit" -version = "0.12.3" +version = "0.12.4" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"}, - {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"}, + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, ] [[package]] name = "trove-classifiers" -version = "2023.11.14" +version = "2024.2.23" description = "Canonical source for classifiers on PyPI (pypi.org)." optional = false python-versions = "*" files = [ - {file = "trove-classifiers-2023.11.14.tar.gz", hash = "sha256:64b5e78305a5de347f2cd7ec8c12d704a3ef0cb85cc10c0ca5f73488d1c201f8"}, - {file = "trove_classifiers-2023.11.14-py3-none-any.whl", hash = "sha256:2893a80cf3c48645462dea97c72c02a9fb1f7bd3c65f9908d3ffb1a7ef0b3833"}, + {file = "trove-classifiers-2024.2.23.tar.gz", hash = "sha256:8385160a12aac69c93fff058fb613472ed773a24a27eb3cd4b144cfbdd79f38c"}, + {file = "trove_classifiers-2024.2.23-py3-none-any.whl", hash = "sha256:3094534b8021dc1822aadb1d11d4c7b62a854d464d19458fd0a49d6fe2b68b77"}, ] [[package]] name = "types-python-dateutil" -version = "2.8.19.14" +version = "2.8.19.20240106" description = "Typing stubs for python-dateutil" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.8.19.14.tar.gz", hash = "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b"}, - {file = "types_python_dateutil-2.8.19.14-py3-none-any.whl", hash = "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9"}, + {file = "types-python-dateutil-2.8.19.20240106.tar.gz", hash = "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f"}, + {file = "types_python_dateutil-2.8.19.20240106-py3-none-any.whl", hash = "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2"}, ] [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] name = "tzdata" -version = "2023.3" +version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, - {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] @@ -4367,19 +4385,19 @@ files = [ [[package]] name = "virtualenv" -version = "20.24.6" +version = "20.25.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, - {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<4" +platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] @@ -4387,27 +4405,27 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "vulture" -version = "2.10" +version = "2.11" description = "Find dead code" optional = false python-versions = ">=3.8" files = [ - {file = "vulture-2.10-py2.py3-none-any.whl", hash = "sha256:568a4176db7468d0157817ae3bb1847a19f1ddc629849af487f9d3b279bff77d"}, - {file = "vulture-2.10.tar.gz", hash = "sha256:2a5c3160bffba77595b6e6dfcc412016bd2a09cd4b66cdf7fbba913684899f6f"}, + {file = "vulture-2.11-py2.py3-none-any.whl", hash = "sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba"}, + {file = "vulture-2.11.tar.gz", hash = "sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"}, ] [package.dependencies] -toml = "*" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "wcwidth" -version = "0.2.10" +version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.10-py2.py3-none-any.whl", hash = "sha256:aec5179002dd0f0d40c456026e74a729661c9d468e1ed64405e3a6c2176ca36f"}, - {file = "wcwidth-0.2.10.tar.gz", hash = "sha256:390c7454101092a6a5e43baad8f83de615463af459201709556b6e4b1c861f97"}, + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] [[package]] @@ -4438,13 +4456,13 @@ files = [ [[package]] name = "websocket-client" -version = "1.6.4" +version = "1.7.0" description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" files = [ - {file = "websocket-client-1.6.4.tar.gz", hash = "sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df"}, - {file = "websocket_client-1.6.4-py3-none-any.whl", hash = "sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24"}, + {file = "websocket-client-1.7.0.tar.gz", hash = "sha256:10e511ea3a8c744631d3bd77e61eb17ed09304c413ad42cf6ddfa4c7787e8fe6"}, + {file = "websocket_client-1.7.0-py3-none-any.whl", hash = "sha256:f4c3d22fec12a2461427a29957ff07d35098ee2d976d3ba244e688b8b4057588"}, ] [package.extras] @@ -4550,87 +4568,76 @@ files = [ [[package]] name = "xattr" -version = "0.10.1" +version = "1.1.0" description = "Python wrapper for extended filesystem attributes" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "xattr-0.10.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:16a660a883e703b311d1bbbcafc74fa877585ec081cd96e8dd9302c028408ab1"}, - {file = "xattr-0.10.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1e2973e72faa87ca29d61c23b58c3c89fe102d1b68e091848b0e21a104123503"}, - {file = "xattr-0.10.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:13279fe8f7982e3cdb0e088d5cb340ce9cbe5ef92504b1fd80a0d3591d662f68"}, - {file = "xattr-0.10.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1dc9b9f580ef4b8ac5e2c04c16b4d5086a611889ac14ecb2e7e87170623a0b75"}, - {file = "xattr-0.10.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:485539262c2b1f5acd6b6ea56e0da2bc281a51f74335c351ea609c23d82c9a79"}, - {file = "xattr-0.10.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:295b3ab335fcd06ca0a9114439b34120968732e3f5e9d16f456d5ec4fa47a0a2"}, - {file = "xattr-0.10.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:a126eb38e14a2f273d584a692fe36cff760395bf7fc061ef059224efdb4eb62c"}, - {file = "xattr-0.10.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:b0e919c24f5b74428afa91507b15e7d2ef63aba98e704ad13d33bed1288dca81"}, - {file = "xattr-0.10.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:e31d062cfe1aaeab6ba3db6bd255f012d105271018e647645941d6609376af18"}, - {file = "xattr-0.10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:209fb84c09b41c2e4cf16dd2f481bb4a6e2e81f659a47a60091b9bcb2e388840"}, - {file = "xattr-0.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c4120090dac33eddffc27e487f9c8f16b29ff3f3f8bcb2251b2c6c3f974ca1e1"}, - {file = "xattr-0.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3e739d624491267ec5bb740f4eada93491de429d38d2fcdfb97b25efe1288eca"}, - {file = "xattr-0.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2677d40b95636f3482bdaf64ed9138fb4d8376fb7933f434614744780e46e42d"}, - {file = "xattr-0.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40039f1532c4456fd0f4c54e9d4e01eb8201248c321c6c6856262d87e9a99593"}, - {file = "xattr-0.10.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:148466e5bb168aba98f80850cf976e931469a3c6eb11e9880d9f6f8b1e66bd06"}, - {file = "xattr-0.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0aedf55b116beb6427e6f7958ccd80a8cbc80e82f87a4cd975ccb61a8d27b2ee"}, - {file = "xattr-0.10.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c3024a9ff157247c8190dd0eb54db4a64277f21361b2f756319d9d3cf20e475f"}, - {file = "xattr-0.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f1be6e733e9698f645dbb98565bb8df9b75e80e15a21eb52787d7d96800e823b"}, - {file = "xattr-0.10.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7880c8a54c18bc091a4ce0adc5c6d81da1c748aec2fe7ac586d204d6ec7eca5b"}, - {file = "xattr-0.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:89c93b42c3ba8aedbc29da759f152731196c2492a2154371c0aae3ef8ba8301b"}, - {file = "xattr-0.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6b905e808df61b677eb972f915f8a751960284358b520d0601c8cbc476ba2df6"}, - {file = "xattr-0.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1ef954d0655f93a34d07d0cc7e02765ec779ff0b59dc898ee08c6326ad614d5"}, - {file = "xattr-0.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:199b20301b6acc9022661412346714ce764d322068ef387c4de38062474db76c"}, - {file = "xattr-0.10.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec0956a8ab0f0d3f9011ba480f1e1271b703d11542375ef73eb8695a6bd4b78b"}, - {file = "xattr-0.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffcb57ca1be338d69edad93cf59aac7c6bb4dbb92fd7bf8d456c69ea42f7e6d2"}, - {file = "xattr-0.10.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f0563196ee54756fe2047627d316977dc77d11acd7a07970336e1a711e934db"}, - {file = "xattr-0.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc354f086f926a1c7f04886f97880fed1a26d20e3bc338d0d965fd161dbdb8ab"}, - {file = "xattr-0.10.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c0cd2d02ef2fb45ecf2b0da066a58472d54682c6d4f0452dfe7ae2f3a76a42ea"}, - {file = "xattr-0.10.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49626096ddd72dcc1654aadd84b103577d8424f26524a48d199847b5d55612d0"}, - {file = "xattr-0.10.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ceaa26bef8fcb17eb59d92a7481c2d15d20211e217772fb43c08c859b01afc6a"}, - {file = "xattr-0.10.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8c014c371391f28f8cd27d73ea59f42b30772cd640b5a2538ad4f440fd9190b"}, - {file = "xattr-0.10.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:46c32cd605673606b9388a313b0050ee7877a0640d7561eea243ace4fa2cc5a6"}, - {file = "xattr-0.10.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:772b22c4ff791fe5816a7c2a1c9fcba83f9ab9bea138eb44d4d70f34676232b4"}, - {file = "xattr-0.10.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:183ad611a2d70b5a3f5f7aadef0fcef604ea33dcf508228765fd4ddac2c7321d"}, - {file = "xattr-0.10.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8068df3ebdfa9411e58d5ae4a05d807ec5994645bb01af66ec9f6da718b65c5b"}, - {file = "xattr-0.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bc40570155beb85e963ae45300a530223d9822edfdf09991b880e69625ba38a"}, - {file = "xattr-0.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:436e1aaf23c07e15bed63115f1712d2097e207214fc6bcde147c1efede37e2c5"}, - {file = "xattr-0.10.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7298455ccf3a922d403339781b10299b858bb5ec76435445f2da46fb768e31a5"}, - {file = "xattr-0.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:986c2305c6c1a08f78611eb38ef9f1f47682774ce954efb5a4f3715e8da00d5f"}, - {file = "xattr-0.10.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5dc6099e76e33fa3082a905fe59df766b196534c705cf7a2e3ad9bed2b8a180e"}, - {file = "xattr-0.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:042ad818cda6013162c0bfd3816f6b74b7700e73c908cde6768da824686885f8"}, - {file = "xattr-0.10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9d4c306828a45b41b76ca17adc26ac3dc00a80e01a5ba85d71df2a3e948828f2"}, - {file = "xattr-0.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a606280b0c9071ef52572434ecd3648407b20df3d27af02c6592e84486b05894"}, - {file = "xattr-0.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5b49d591cf34cda2079fd7a5cb2a7a1519f54dc2e62abe3e0720036f6ed41a85"}, - {file = "xattr-0.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8705ac6791426559c1a5c2b88bb2f0e83dc5616a09b4500899bfff6a929302"}, - {file = "xattr-0.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5ea974930e876bc5c146f54ac0f85bb39b7b5de2b6fc63f90364712ae368ebe"}, - {file = "xattr-0.10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f55a2dd73a12a1ae5113c5d9cd4b4ab6bf7950f4d76d0a1a0c0c4264d50da61d"}, - {file = "xattr-0.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:475c38da0d3614cc5564467c4efece1e38bd0705a4dbecf8deeb0564a86fb010"}, - {file = "xattr-0.10.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:925284a4a28e369459b2b7481ea22840eed3e0573a4a4c06b6b0614ecd27d0a7"}, - {file = "xattr-0.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa32f1b45fed9122bed911de0fcc654da349e1f04fa4a9c8ef9b53e1cc98b91e"}, - {file = "xattr-0.10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c5d3d0e728bace64b74c475eb4da6148cd172b2d23021a1dcd055d92f17619ac"}, - {file = "xattr-0.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8faaacf311e2b5cc67c030c999167a78a9906073e6abf08eaa8cf05b0416515c"}, - {file = "xattr-0.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc6b8d5ca452674e1a96e246a3d2db5f477aecbc7c945c73f890f56323e75203"}, - {file = "xattr-0.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3725746a6502f40f72ef27e0c7bfc31052a239503ff3eefa807d6b02a249be22"}, - {file = "xattr-0.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:789bd406d1aad6735e97b20c6d6a1701e1c0661136be9be862e6a04564da771f"}, - {file = "xattr-0.10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9a7a807ab538210ff8532220d8fc5e2d51c212681f63dbd4e7ede32543b070f"}, - {file = "xattr-0.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3e5825b5fc99ecdd493b0cc09ec35391e7a451394fdf623a88b24726011c950d"}, - {file = "xattr-0.10.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:80638d1ce7189dc52f26c234cee3522f060fadab6a8bc3562fe0ddcbe11ba5a4"}, - {file = "xattr-0.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3ff0dbe4a6ce2ce065c6de08f415bcb270ecfd7bf1655a633ddeac695ce8b250"}, - {file = "xattr-0.10.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5267e5f9435c840d2674194150b511bef929fa7d3bc942a4a75b9eddef18d8d8"}, - {file = "xattr-0.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b27dfc13b193cb290d5d9e62f806bb9a99b00cd73bb6370d556116ad7bb5dc12"}, - {file = "xattr-0.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:636ebdde0277bce4d12d2ef2550885804834418fee0eb456b69be928e604ecc4"}, - {file = "xattr-0.10.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d60c27922ec80310b45574351f71e0dd3a139c5295e8f8b19d19c0010196544f"}, - {file = "xattr-0.10.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b34df5aad035d0343bd740a95ca30db99b776e2630dca9cc1ba8e682c9cc25ea"}, - {file = "xattr-0.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f24a7c04ff666d0fe905dfee0a84bc899d624aeb6dccd1ea86b5c347f15c20c1"}, - {file = "xattr-0.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3878e1aff8eca64badad8f6d896cb98c52984b1e9cd9668a3ab70294d1ef92d"}, - {file = "xattr-0.10.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4abef557028c551d59cf2fb3bf63f2a0c89f00d77e54c1c15282ecdd56943496"}, - {file = "xattr-0.10.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0e14bd5965d3db173d6983abdc1241c22219385c22df8b0eb8f1846c15ce1fee"}, - {file = "xattr-0.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f9be588a4b6043b03777d50654c6079af3da60cc37527dbb80d36ec98842b1e"}, - {file = "xattr-0.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bc4ae264aa679aacf964abf3ea88e147eb4a22aea6af8c6d03ebdebd64cfd6"}, - {file = "xattr-0.10.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:827b5a97673b9997067fde383a7f7dc67342403093b94ea3c24ae0f4f1fec649"}, - {file = "xattr-0.10.1.tar.gz", hash = "sha256:c12e7d81ffaa0605b3ac8c22c2994a8e18a9cf1c59287a1b7722a2289c952ec5"}, + {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef2fa0f85458736178fd3dcfeb09c3cf423f0843313e25391db2cfd1acec8888"}, + {file = "xattr-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ccab735d0632fe71f7d72e72adf886f45c18b7787430467ce0070207882cfe25"}, + {file = "xattr-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9013f290387f1ac90bccbb1926555ca9aef75651271098d99217284d9e010f7c"}, + {file = "xattr-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcd5dfbcee73c7be057676ecb900cabb46c691aff4397bf48c579ffb30bb963"}, + {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6480589c1dac7785d1f851347a32c4a97305937bf7b488b857fe8b28a25de9e9"}, + {file = "xattr-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08f61cbed52dc6f7c181455826a9ff1e375ad86f67dd9d5eb7663574abb32451"}, + {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:918e1f83f2e8a072da2671eac710871ee5af337e9bf8554b5ce7f20cdb113186"}, + {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0f06e0c1e4d06b4e0e49aaa1184b6f0e81c3758c2e8365597918054890763b53"}, + {file = "xattr-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46a641ac038a9f53d2f696716147ca4dbd6a01998dc9cd4bc628801bc0df7f4d"}, + {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7e4ca0956fd11679bb2e0c0d6b9cdc0f25470cc00d8da173bb7656cc9a9cf104"}, + {file = "xattr-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6881b120f9a4b36ccd8a28d933bc0f6e1de67218b6ce6e66874e0280fc006844"}, + {file = "xattr-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dab29d9288aa28e68a6f355ddfc3f0a7342b40c9012798829f3e7bd765e85c2c"}, + {file = "xattr-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c80bbf55339c93770fc294b4b6586b5bf8e85ec00a4c2d585c33dbd84b5006"}, + {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1418705f253b6b6a7224b69773842cac83fcbcd12870354b6e11dd1cd54630f"}, + {file = "xattr-1.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687e7d18611ef8d84a6ecd8f4d1ab6757500c1302f4c2046ce0aa3585e13da3f"}, + {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6ceb9efe0657a982ccb8b8a2efe96b690891779584c901d2f920784e5d20ae3"}, + {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b489b7916f239100956ea0b39c504f3c3a00258ba65677e4c8ba1bd0b5513446"}, + {file = "xattr-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0a9c431b0e66516a078125e9a273251d4b8e5ba84fe644b619f2725050d688a0"}, + {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1a5921ea3313cc1c57f2f53b63ea8ca9a91e48f4cc7ebec057d2447ec82c7efe"}, + {file = "xattr-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6ad2a7bd5e6cf71d4a862413234a067cf158ca0ae94a40d4b87b98b62808498"}, + {file = "xattr-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0683dae7609f7280b0c89774d00b5957e6ffcb181c6019c46632b389706b77e6"}, + {file = "xattr-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54cb15cd94e5ef8a0ef02309f1bf973ba0e13c11e87686e983f371948cfee6af"}, + {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff6223a854229055e803c2ad0c0ea9a6da50c6be30d92c198cf5f9f28819a921"}, + {file = "xattr-1.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d44e8f955218638c9ab222eed21e9bd9ab430d296caf2176fb37abe69a714e5c"}, + {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:caab2c2986c30f92301f12e9c50415d324412e8e6a739a52a603c3e6a54b3610"}, + {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d6eb7d5f281014cd44e2d847a9107491af1bf3087f5afeded75ed3e37ec87239"}, + {file = "xattr-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:47a3bdfe034b4fdb70e5941d97037405e3904accc28e10dbef6d1c9061fb6fd7"}, + {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:00d2b415cf9d6a24112d019e721aa2a85652f7bbc9f3b9574b2d1cd8668eb491"}, + {file = "xattr-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:78b377832dd0ee408f9f121a354082c6346960f7b6b1480483ed0618b1912120"}, + {file = "xattr-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6461a43b585e5f2e049b39bcbfcb6391bfef3c5118231f1b15d10bdb89ef17fe"}, + {file = "xattr-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24d97f0d28f63695e3344ffdabca9fcc30c33e5c8ccc198c7524361a98d526f2"}, + {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ad47d89968c9097900607457a0c89160b4771601d813e769f68263755516065"}, + {file = "xattr-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc53cab265f6e8449bd683d5ee3bc5a191e6dd940736f3de1a188e6da66b0653"}, + {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cd11e917f5b89f2a0ad639d9875943806c6c9309a3dd02da5a3e8ef92db7bed9"}, + {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9c5a78c7558989492c4cb7242e490ffb03482437bf782967dfff114e44242343"}, + {file = "xattr-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cebcf8a303a44fbc439b68321408af7267507c0d8643229dbb107f6c132d389c"}, + {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b0d73150f2f9655b4da01c2369eb33a294b7f9d56eccb089819eafdbeb99f896"}, + {file = "xattr-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:793c01deaadac50926c0e1481702133260c7cb5e62116762f6fe1543d07b826f"}, + {file = "xattr-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e189e440bcd04ccaad0474720abee6ee64890823ec0db361fb0a4fb5e843a1bf"}, + {file = "xattr-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afacebbc1fa519f41728f8746a92da891c7755e6745164bd0d5739face318e86"}, + {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b1664edf003153ac8d1911e83a0fc60db1b1b374ee8ac943f215f93754a1102"}, + {file = "xattr-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda2684228798e937a7c29b0e1c7ef3d70e2b85390a69b42a1c61b2039ba81de"}, + {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b735ac2625a4fc2c9343b19f806793db6494336338537d2911c8ee4c390dda46"}, + {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fa6a7af7a4ada43f15ccc58b6f9adcdbff4c36ba040013d2681e589e07ae280a"}, + {file = "xattr-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1059b2f726e2702c8bbf9bbf369acfc042202a4cc576c2dec6791234ad5e948"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e2255f36ebf2cb2dbf772a7437ad870836b7396e60517211834cf66ce678b595"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba4f80b9855cc98513ddf22b7ad8551bc448c70d3147799ea4f6c0b758fb466"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb70c16e7c3ae6ba0ab6c6835c8448c61d8caf43ea63b813af1f4dbe83dd156"}, + {file = "xattr-1.1.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83652910ef6a368b77b00825ad67815e5c92bfab551a848ca66e9981d14a7519"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7a92aff66c43fa3e44cbeab7cbeee66266c91178a0f595e044bf3ce51485743b"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d4f71b673339aeaae1f6ea9ef8ea6c9643c8cd0df5003b9a0eaa75403e2e06c"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a20de1c47b5cd7b47da61799a3b34e11e5815d716299351f82a88627a43f9a96"}, + {file = "xattr-1.1.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23705c7079b05761ff2fa778ad17396e7599c8759401abc05b312dfb3bc99f69"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:27272afeba8422f2a9d27e1080a9a7b807394e88cce73db9ed8d2dde3afcfb87"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd43978966de3baf4aea367c99ffa102b289d6c2ea5f3d9ce34a203dc2f2ab73"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded771eaf27bb4eb3c64c0d09866460ee8801d81dc21097269cf495b3cac8657"}, + {file = "xattr-1.1.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca300c0acca4f0cddd2332bb860ef58e1465d376364f0e72a1823fdd58e90d"}, + {file = "xattr-1.1.0.tar.gz", hash = "sha256:fecbf3b05043ed3487a28190dec3e4c4d879b2fcec0e30bafd8ec5d4b6043630"}, ] [package.dependencies] -cffi = ">=1.0" +cffi = ">=1.16.0" + +[package.extras] +test = ["pytest"] [[package]] name = "xmltodict" @@ -4645,85 +4652,101 @@ files = [ [[package]] name = "yarl" -version = "1.9.2" +version = "1.9.4" description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, - {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, - {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, - {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, - {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, - {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, - {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, - {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, - {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, - {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, - {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, - {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, - {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, ] [package.dependencies] @@ -4748,4 +4771,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "f6d89a19888ec8a666200c9c2552434c0f9efb49fb0d29d69b56707bae47315a" +content-hash = "ccfd855abf7309cf520ea5ca02e77fc9d8ab911819ea9690298fa0549a9d7c42" From 11992be740d705de4753266e651c936e1a771424 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Wed, 28 Feb 2024 15:54:35 -0500 Subject: [PATCH 217/259] Fixing things is fun. Signed-off-by: Cliff Hill --- app/job/rest.py | 6 +- app/performance_dashboard/rest.py | 8 +- tests/app/celery/test_reporting_tasks.py | 68 +++++----- .../dao/test_fact_notification_status_dao.py | 123 +++++++++--------- .../test_process_notification.py | 18 ++- 5 files changed, 123 insertions(+), 100 deletions(-) diff --git a/app/job/rest.py b/app/job/rest.py index 3e239f14d..f2ec7068a 100644 --- a/app/job/rest.py +++ b/app/job/rest.py @@ -125,11 +125,7 @@ def get_jobs_by_service(service_id): try: limit_days = int(request.args["limit_days"]) except ValueError: - errors = { - "limit_days": [ - f"{request.args['limit_days']} is not an integer" - ] - } + errors = {"limit_days": [f"{request.args['limit_days']} is not an integer"]} raise InvalidRequest(errors, status_code=400) else: limit_days = None diff --git a/app/performance_dashboard/rest.py b/app/performance_dashboard/rest.py index 73b45bd6e..4810dc17b 100644 --- a/app/performance_dashboard/rest.py +++ b/app/performance_dashboard/rest.py @@ -85,7 +85,13 @@ def transform_results_into_totals(total_notifications_results): def transform_into_notification_by_type_json(total_notifications): j = [] for x in total_notifications: - j.append({"date": x.local_date.strftime("%Y-%m-%d"), "emails": x.emails, "sms": x.sms}) + j.append( + { + "date": x.local_date.strftime("%Y-%m-%d"), + "emails": x.emails, + "sms": x.sms, + } + ) return j diff --git a/tests/app/celery/test_reporting_tasks.py b/tests/app/celery/test_reporting_tasks.py index 296d1a1c0..031f4e9b0 100644 --- a/tests/app/celery/test_reporting_tasks.py +++ b/tests/app/celery/test_reporting_tasks.py @@ -475,39 +475,43 @@ def test_create_nightly_notification_status_for_service_and_day(notify_db_sessio assert len(new_fact_data) == 4 - email_delivered_row = new_fact_data[0] - assert email_delivered_row.template_id == second_template.id - assert email_delivered_row.service_id == second_service.id - assert email_delivered_row.notification_type == NotificationType.EMAIL - assert email_delivered_row.notification_status == NotificationStatus.DELIVERED - assert email_delivered_row.notification_count == 1 - assert email_delivered_row.key_type == KeyType.NORMAL + email_delivered = (NotificationType.EMAIL, NotificationStatus.DELIVERED) + email_sending = (NotificationType.EMAIL, NotificationStatus.SENDING) + email_failure = (NotificationType.EMAIL, NotificationStatus.FAILED) + sms_delivered = (NotificationType.SMS, NotificationStatus.DELIVERED) - email_sending_row = new_fact_data[1] - assert email_sending_row.template_id == second_template.id - assert email_sending_row.service_id == second_service.id - assert email_sending_row.notification_type == NotificationType.EMAIL - assert email_sending_row.notification_status == NotificationStatus.FAILED - assert email_sending_row.notification_count == 1 - assert email_sending_row.key_type == KeyType.NORMAL - - email_failure_row = new_fact_data[2] - assert email_failure_row.local_date == process_day - assert email_failure_row.template_id == second_template.id - assert email_failure_row.service_id == second_service.id - assert email_failure_row.job_id == UUID("00000000-0000-0000-0000-000000000000") - assert email_failure_row.notification_type == NotificationType.EMAIL - assert email_failure_row.notification_status == NotificationStatus.SENDING - assert email_failure_row.notification_count == 1 - assert email_failure_row.key_type == KeyType.TEAM - - sms_delivered_row = new_fact_data[3] - assert sms_delivered_row.template_id == first_template.id - assert sms_delivered_row.service_id == first_service.id - assert sms_delivered_row.notification_type == NotificationType.SMS - assert sms_delivered_row.notification_status == NotificationStatus.DELIVERED - assert sms_delivered_row.notification_count == 1 - assert sms_delivered_row.key_type == KeyType.NORMAL + for row in new_fact_data: + current = (row.notification_type, row.notification_status) + if current == email_delivered: + assert row.template_id == second_template.id + assert row.service_id == second_service.id + assert row.notification_type == NotificationType.EMAIL + assert row.notification_status == NotificationStatus.DELIVERED + assert row.notification_count == 1 + assert row.key_type == KeyType.NORMAL + elif current == email_failure: + assert row.template_id == second_template.id + assert row.service_id == second_service.id + assert row.notification_type == NotificationType.EMAIL + assert row.notification_status == NotificationStatus.FAILED + assert row.notification_count == 1 + assert row.key_type == KeyType.NORMAL + elif current == email_sending: + assert row.local_date == process_day + assert row.template_id == second_template.id + assert row.service_id == second_service.id + assert row.job_id == UUID("00000000-0000-0000-0000-000000000000") + assert row.notification_type == NotificationType.EMAIL + assert row.notification_status == NotificationStatus.SENDING + assert row.notification_count == 1 + assert row.key_type == KeyType.TEAM + elif current == sms_delivered: + assert row.template_id == first_template.id + assert row.service_id == first_service.id + assert row.notification_type == NotificationType.SMS + assert row.notification_status == NotificationStatus.DELIVERED + assert row.notification_count == 1 + assert row.key_type == KeyType.NORMAL def test_create_nightly_notification_status_for_service_and_day_overwrites_old_data( diff --git a/tests/app/dao/test_fact_notification_status_dao.py b/tests/app/dao/test_fact_notification_status_dao.py index 24812f5b2..d7d5cc9cb 100644 --- a/tests/app/dao/test_fact_notification_status_dao.py +++ b/tests/app/dao/test_fact_notification_status_dao.py @@ -871,67 +871,72 @@ def test_fetch_monthly_notification_statuses_per_service(notify_db_session): assert len(results) == 5 # column order: date, service_id, service_name, notifaction_type, count_sending, count_delivered, # count_technical_failure, count_temporary_failure, count_permanent_failure, count_sent - assert [x for x in results[0]] == [ - date(2019, 3, 1), - service_two.id, - "service two", - NotificationType.SMS, - 0, - 0, - 0, - 0, - 2, - 0, - ] - assert [x for x in results[1]] == [ - date(2019, 3, 1), - service_one.id, - "service one", - NotificationType.EMAIL, - 5, - 0, - 3, - 0, - 0, - 0, - ] - assert [x for x in results[2]] == [ - date(2019, 3, 1), - service_one.id, - "service one", - NotificationType.SMS, - 0, - 1, - 0, - 0, - 0, - 1, - ] - assert [x for x in results[3]] == [ - date(2019, 4, 1), - service_two.id, - "service two", - NotificationType.SMS, - 0, - 0, - 0, - 10, - 0, - 0, - ] - assert [x for x in results[4]] == [ - date(2019, 4, 1), - service_one.id, - "service one", - NotificationType.SMS, - 0, - 1, - 0, - 0, - 0, - 0, + expected = [ + [ + date(2019, 3, 1), + service_two.id, + "service two", + NotificationType.SMS, + 0, + 0, + 0, + 0, + 2, + 0, + ], + [ + date(2019, 3, 1), + service_one.id, + "service one", + NotificationType.EMAIL, + 5, + 0, + 3, + 0, + 0, + 0, + ], + [ + date(2019, 3, 1), + service_one.id, + "service one", + NotificationType.SMS, + 0, + 1, + 0, + 0, + 0, + 1, + ], + [ + date(2019, 4, 1), + service_two.id, + "service two", + NotificationType.SMS, + 0, + 0, + 0, + 10, + 0, + 0, + ], + [ + date(2019, 4, 1), + service_one.id, + "service one", + NotificationType.SMS, + 0, + 1, + 0, + 0, + 0, + 0, + ], ] + for row in results: + assert [x for x in row] in expected + @freeze_time("2019-04-10 14:00") def test_fetch_monthly_notification_statuses_per_service_for_rows_that_should_be_excluded( diff --git a/tests/app/notifications/test_process_notification.py b/tests/app/notifications/test_process_notification.py index 591ddfaad..52198071a 100644 --- a/tests/app/notifications/test_process_notification.py +++ b/tests/app/notifications/test_process_notification.py @@ -276,9 +276,21 @@ def test_send_notification_to_queue_throws_exception_deletes_notification( [ ("+14254147755", NotificationType.SMS, True), ("+14254147167", NotificationType.SMS, True), - ("simulate-delivered@notifications.service.gov.uk", NotificationType.EMAIL, True), - ("simulate-delivered-2@notifications.service.gov.uk", NotificationType.EMAIL, True), - ("simulate-delivered-3@notifications.service.gov.uk", NotificationType.EMAIL, True), + ( + "simulate-delivered@notifications.service.gov.uk", + NotificationType.EMAIL, + True, + ), + ( + "simulate-delivered-2@notifications.service.gov.uk", + NotificationType.EMAIL, + True, + ), + ( + "simulate-delivered-3@notifications.service.gov.uk", + NotificationType.EMAIL, + True, + ), ("2028675309", NotificationType.SMS, False), ("valid_email@test.com", NotificationType.EMAIL, False), ], From 611a8c8bc681b3fa63dbb4165a8362e0591d9517 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Fri, 1 Mar 2024 12:23:05 -0500 Subject: [PATCH 218/259] Add E2E configuration information to docs This changeset adds a bit of missing information to our README and documentation for E2E test configuration. This is required for the project to be set up correctly, and while it will work out of the box, there are a couple of adjustments that need to be made to ensure everything is configured correctly. Signed-off-by: Carlo Costino --- README.md | 31 +++++++++++++++++++++++++++++++ docs/all.md | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/README.md b/README.md index 94aea0c41..d435cfc7b 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,36 @@ brew services start postgresql@15 brew services start redis ``` +### Final environment setup + +There's one final thing to adjust in the newly created `.env` file. This +project has support for end-to-end (E2E) tests and has some additional checks +for the presence of an E2E test user so that it can be authenticated properly. + +In the `.env` file, you should see this section: + +``` +############################################################# + +# E2E Testing + +NOTIFY_E2E_TEST_EMAIL=example@fake.gov +NOTIFY_E2E_TEST_PASSWORD="don't write secrets to the sample file" +``` + +You can leave the email address alone or change it to something else to your +liking. + +**You should absolutely change the `NOTIFY_E2E_TEST_PASSWORD` environment +variable to something else, preferably a lengthy passphrase.** + +With those two environment variable set, the database migrations will run +properly and an E2E test user will be ready to go for use in the admin project. + +_Note: Whatever you set these two environment variables to, you'll need to +match their values on the admin side. Please see the admin README and +documentation for more details._ + ## Running the Project and Routine Maintenance The first time you run the project you'll need to run the project setup from the @@ -410,6 +440,7 @@ instructions above for more details. - [CI testing](./docs/all.md#ci-testing) - [Manual testing](./docs/all.md#manual-testing) - [To run a local OWASP scan](./docs/all.md#to-run-a-local-owasp-scan) + - [End-to-end testing](./docs/all.md#end-to-end-testing) - [Deploying](./docs/all.md#deploying) - [Egress Proxy](./docs/all.md#egress-proxy) - [Managing environment variables](./docs/all.md#managing-environment-variables) diff --git a/docs/all.md b/docs/all.md index 4803102dd..57743f97c 100644 --- a/docs/all.md +++ b/docs/all.md @@ -11,6 +11,7 @@ - [CI testing](#ci-testing) - [Manual testing](#manual-testing) - [To run a local OWASP scan](#to-run-a-local-owasp-scan) + - [End-to-end testing](#end-to-end-testing) - [Deploying](#deploying) - [Egress Proxy](#egress-proxy) - [Managing environment variables](#managing-environment-variables) @@ -307,6 +308,37 @@ The equivalent command if you are running the API locally: docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-weekly zap-api-scan.py -t http://host.docker.internal:6011/docs/openapi.yml -f openapi -c zap.conf -r report.html ``` +## End-to-end Testing + +In order to run end-to-end (E2E) tests, which are managed and handled in the +admin project, a bit of extra configuration needs to be accounted for here on +the API side as well. These instructions are in the README as they are +necessary for project setup, and they're copied here for reference. + +In the `.env` file, you should see this section: + +``` +############################################################# + +# E2E Testing + +NOTIFY_E2E_TEST_EMAIL=example@fake.gov +NOTIFY_E2E_TEST_PASSWORD="don't write secrets to the sample file" +``` + +You can leave the email address alone or change it to something else to your +liking. + +**You should absolutely change the `NOTIFY_E2E_TEST_PASSWORD` environment +variable to something else, preferably a lengthy passphrase.** + +With those two environment variable set, the database migrations will run +properly and an E2E test user will be ready to go for use in the admin project. + +_Note: Whatever you set these two environment variables to, you'll need to +match their values on the admin side. Please see the admin README and +documentation for more details._ + # Deploying The API has 3 deployment environments, all of which deploy to cloud.gov: From 08fe6beff764fc2f46bb0d6f94f7c23f5a88b69a Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Fri, 1 Mar 2024 12:13:29 -0700 Subject: [PATCH 219/259] Added commands to add test data to DB Updated old .uk strings in db.py --- app/commands.py | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/app/db.py | 6 +- 2 files changed, 156 insertions(+), 4 deletions(-) diff --git a/app/commands.py b/app/commands.py index 32cbd219b..d708da590 100644 --- a/app/commands.py +++ b/app/commands.py @@ -1,6 +1,7 @@ import csv import functools import itertools +import random import uuid from datetime import datetime, timedelta from os import getenv @@ -8,6 +9,7 @@ from os import getenv import click import flask from click_datetime import Datetime as click_dt +from faker import Faker from flask import current_app, json from notifications_python_client.authentication import create_jwt_token from notifications_utils.recipients import RecipientCSV @@ -64,6 +66,14 @@ from app.models import ( User, ) from app.utils import get_midnight_in_utc +from tests.app.db import ( + create_job, + create_notification, + create_organization, + create_service, + create_template, + create_user, +) @click.group(name="command", help="Additional commands") @@ -833,3 +843,147 @@ def purge_csv_bucket(): print("ABOUT TO RUN PURGE CSV BUCKET") s3.purge_bucket(bucket_name, access_key, secret, region) print("RAN PURGE CSV BUCKET") + + +""" +Commands to load test data into the database for +Orgs, Services, Users, Jobs, Notifications + +faker is used to generate some random fields. All +database commands were used from tests/app/db.py +where possible to enable better maintainability. +""" +fake = Faker(["en_US"]) + + +# generate n number of test orgs into the dev DB +@notify_command(name="add-test-organization-to-db") +@click.option("-g", "--generate", required=True, prompt=True, default=1) +def add_test_organization_to_db(generate): + def generate_gov_agency(): + agency_names = [ + "Bureau", + "Department", + "Administration", + "Authority", + "Commission", + "Division", + "Office", + "Institute", + "Agency", + "Council", + "Board", + "Committee", + "Corporation", + "Service", + "Center", + "Registry", + "Foundation", + "Task Force", + "Unit", + ] + + government_sectors = [ + "Healthcare", + "Education", + "Transportation", + "Defense", + "Law Enforcement", + "Environmental Protection", + "Housing and Urban Development", + "Finance and Economy", + "Social Services", + "Energy", + "Agriculture", + "Labor and Employment", + "Foreign Affairs", + "Trade and Commerce", + "Science and Technology", + ] + + agency = random.choice(agency_names) + speciality = random.choice(government_sectors) + + return f"{fake.word().capitalize()} {speciality} {agency}" + + for num in range(1, int(generate) + 1): + org = create_organization( + name=generate_gov_agency(), + organization_type=random.choice(["federal", "state", "other"]), + ) + print(f"{num} {org.name} created") + + +# generate n number of test services into the dev DB +@notify_command(name="add-test-service-to-db") +@click.option("-g", "--generate", required=True, prompt=True, default=1) +def add_test_service_to_db(generate): + if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: + current_app.logger.error("Can only be run in development") + return + + for num in range(1, int(generate) + 1): + service_name = f"{fake.company()} sample service" + service = create_service(service_name=service_name) + print(f"{num} {service.name} created") + + +# generate n number of test jobs into the dev DB +@notify_command(name="add-test-job-to-db") +@click.option("-g", "--generate", required=True, prompt=True, default=1) +def add_test_job_to_db(generate): + if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: + current_app.logger.error("Can only be run in development") + return + + for num in range(1, int(generate) + 1): + service = create_service(check_if_service_exists=True) + template = create_template(service=service) + job = create_job(template) + print(f"{num} {job.id} created") + + +# generate n number of notifications into the dev DB +@notify_command(name="add-test-notification-to-db") +@click.option("-g", "--generate", required=True, prompt=True, default=1) +def add_test_notification_to_db(generate): + if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: + current_app.logger.error("Can only be run in development") + return + + for num in range(1, int(generate) + 1): + service = create_service(check_if_service_exists=True) + template = create_template(service=service) + job = create_job(template=template) + notification = create_notification( + template=template, + job=job, + ) + print(f"{num} {notification.id} created") + + +# generate n number of test users into the dev DB +@notify_command(name="add-test-users") +@click.option("-g", "--generate", required=True, prompt=True, default="1") +@click.option("-s", "--state", default="active") +@click.option("-d", "--admin", default=False, type=bool) +def add_test_users(generate, state, admin): + if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: + current_app.logger.error("Can only be run in development") + return + + for num in range(1, int(generate) + 1): + + def fake_email(name): + first_name, last_name = name.split(maxsplit=1) + username = f"{first_name.lower()}.{last_name.lower()}" + return f"{username}@test.gsa.gov" + + name = fake.name() + user = create_user( + name=name, + email=fake_email(name), + state=state, + platform_admin=admin, + ) + print(f"{num} {user.email_address} created") diff --git a/tests/app/db.py b/tests/app/db.py index 56a33335f..a5d72a944 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -75,7 +75,7 @@ def create_user( data = { "id": id_ or uuid.uuid4(), "name": name, - "email_address": email or f"{uuid.uuid4()}@digital.cabinet-office.gov.uk", + "email_address": email or f"{uuid.uuid4()}@test.gsa.gov", "password": "password", "mobile_number": mobile_number, "state": state, @@ -133,9 +133,7 @@ def create_service( else service_name.lower().replace(" ", "."), created_by=user if user - else create_user( - email="{}@digital.cabinet-office.gov.uk".format(uuid.uuid4()) - ), + else create_user(email="{}@test.gsa.gov".format(uuid.uuid4())), prefix_sms=prefix_sms, organization_type=organization_type, organization=organization, From eb52cdff0d08f8ff31454b7062ed39760e856ddb Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Fri, 1 Mar 2024 12:34:01 -0700 Subject: [PATCH 220/259] New lock file --- poetry.lock | 159 ++++++++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 80 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8f8b2f11f..089587a20 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -446,18 +446,18 @@ crt = ["awscrt (==0.19.12)"] [[package]] name = "build" -version = "1.0.3" +version = "1.1.1" description = "A simple, correct Python build frontend" optional = false python-versions = ">= 3.7" files = [ - {file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"}, - {file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"}, + {file = "build-1.1.1-py3-none-any.whl", hash = "sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73"}, + {file = "build-1.1.1.tar.gz", hash = "sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31"}, ] [package.dependencies] colorama = {version = "*", markers = "os_name == \"nt\""} -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} packaging = ">=19.0" pyproject_hooks = "*" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -1001,13 +1001,13 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "cyclonedx-python-lib" -version = "6.4.1" +version = "6.4.2" description = "Python library for CycloneDX" optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "cyclonedx_python_lib-6.4.1-py3-none-any.whl", hash = "sha256:42d50052c4604e8d6a91753e51bca33d668fb82adc1aab3f4eb54b89fa61cbc0"}, - {file = "cyclonedx_python_lib-6.4.1.tar.gz", hash = "sha256:aca5d8cf10f8d8420ba621e0cf4a24b98708afb68ca2ca72d7f2cc6394c75681"}, + {file = "cyclonedx_python_lib-6.4.2-py3-none-any.whl", hash = "sha256:47ed05c7a82d0aa355f09a7fd0048e13f1440b6a96b603dc26d02ba782e6274e"}, + {file = "cyclonedx_python_lib-6.4.2.tar.gz", hash = "sha256:b857972c6a7317faa66ec4ed4b6040dae7fca82e40eddfbeaa7f0f37f9a7cac7"}, ] [package.dependencies] @@ -2374,67 +2374,67 @@ xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] [[package]] name = "msgpack" -version = "1.0.7" +version = "1.0.8" description = "MessagePack serializer" optional = false python-versions = ">=3.8" files = [ - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, - {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, - {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, - {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, - {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, - {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, - {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, - {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, - {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, - {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, - {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, - {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, - {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, - {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, - {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, - {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, - {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, - {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, - {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, - {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, - {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, - {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, - {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, - {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, - {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, - {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, - {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, + {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, + {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, + {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, + {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, + {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, + {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, + {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, + {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, + {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, + {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, + {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, + {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, + {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, + {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, + {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, + {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, + {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, + {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, + {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, + {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, + {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, + {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, + {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, + {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, + {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, ] [[package]] @@ -2671,7 +2671,7 @@ werkzeug = "^3.0.1" type = "git" url = "https://github.com/GSA/notifications-utils.git" reference = "HEAD" -resolved_reference = "df8ece836cad303c5d161edf485cf9bbdc666688" +resolved_reference = "46af64b4f2540e44b3dda4a49cff04008dfe1ee8" [[package]] name = "numpy" @@ -2758,13 +2758,13 @@ asn1crypto = ">=1.5.1" [[package]] name = "packageurl-python" -version = "0.13.4" +version = "0.14.0" description = "A purl aka. Package URL parser and builder" optional = false python-versions = ">=3.7" files = [ - {file = "packageurl-python-0.13.4.tar.gz", hash = "sha256:6eb5e995009cc73387095e0b507ab65df51357d25ddc5fce3d3545ad6dcbbee8"}, - {file = "packageurl_python-0.13.4-py3-none-any.whl", hash = "sha256:62aa13d60a0082ff115784fefdfe73a12f310e455365cca7c6d362161067f35f"}, + {file = "packageurl-python-0.14.0.tar.gz", hash = "sha256:ff09147cddaae9e5c59ffcb12df8ec0e1b774b45099399f28c36b1a3dfdf52e2"}, + {file = "packageurl_python-0.14.0-py3-none-any.whl", hash = "sha256:cf5e55cdcd61e6de858f47c4986aa87ba493bfa56ba58de11103dfdc2c00e4e1"}, ] [package.extras] @@ -2858,13 +2858,13 @@ pip = "*" [[package]] name = "pip-audit" -version = "2.7.1" +version = "2.7.2" description = "A tool for scanning Python environments for known vulnerabilities" optional = false python-versions = ">=3.8" files = [ - {file = "pip_audit-2.7.1-py3-none-any.whl", hash = "sha256:b9b4230d1ac685d669b4a36b1d5f849ea3d1ce371501aff73047bd278b22c055"}, - {file = "pip_audit-2.7.1.tar.gz", hash = "sha256:66001c73bc6e5ebc998ef31a32432f7b479dc3bfeb40f7101d0fe7eb564a2c2a"}, + {file = "pip_audit-2.7.2-py3-none-any.whl", hash = "sha256:49907430115baacb8bb7ffc1a2b689acfeac9d8534a43bffad3c73f8d8b32d52"}, + {file = "pip_audit-2.7.2.tar.gz", hash = "sha256:a12905e42dd452f43a2dbf895606d59c35348deed27b8cbaff8516423576fdfb"}, ] [package.dependencies] @@ -2881,7 +2881,7 @@ toml = ">=0.10" [package.extras] dev = ["build", "bump (>=1.3.2)", "pip-audit[doc,lint,test]"] doc = ["pdoc"] -lint = ["interrogate", "mypy", "ruff (<0.2.2)", "types-html5lib", "types-requests", "types-toml"] +lint = ["interrogate", "mypy", "ruff (<0.2.3)", "types-html5lib", "types-requests", "types-toml"] test = ["coverage[toml] (>=7.0,!=7.3.3,<8.0)", "pretend", "pytest", "pytest-cov"] [[package]] @@ -3182,13 +3182,13 @@ files = [ [[package]] name = "py-serializable" -version = "1.0.1" +version = "1.0.2" description = "Library for serializing and deserializing Python Objects to and from JSON and XML." optional = false python-versions = ">=3.8,<4.0" files = [ - {file = "py_serializable-1.0.1-py3-none-any.whl", hash = "sha256:edcc51ac91a39e0cdde147463cae4dc34f5ab72907f7e71721ff3ecef3731a70"}, - {file = "py_serializable-1.0.1.tar.gz", hash = "sha256:98b81e565c23b3cc2ac799f5096dc7e11cafe8215c551d20a1c16dd38a113861"}, + {file = "py_serializable-1.0.2-py3-none-any.whl", hash = "sha256:f09dee8595a583117ba446c50be183eff9699b7d54529e0506d4f0f2e093e4a3"}, + {file = "py_serializable-1.0.2.tar.gz", hash = "sha256:158a98a7ffda067d21f844594ce571d97f36172ba538aee1a93196f8b5888bd8"}, ] [package.dependencies] @@ -3395,13 +3395,13 @@ testing = ["filelock"] [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -3479,7 +3479,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, From c0e3a68a07f790f7383ec666e326b9eb64e121e8 Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Fri, 1 Mar 2024 12:43:35 -0700 Subject: [PATCH 221/259] Added faker to poetry lock file --- poetry.lock | 16 +++++++++++++++- pyproject.toml | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 089587a20..00e656a4a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1248,6 +1248,20 @@ files = [ [package.extras] tests = ["coverage", "coveralls", "dill", "mock", "nose"] +[[package]] +name = "faker" +version = "23.3.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-23.3.0-py3-none-any.whl", hash = "sha256:117ce1a2805c1bc5ca753b3dc6f9d567732893b2294b827d3164261ee8f20267"}, + {file = "Faker-23.3.0.tar.gz", hash = "sha256:458d93580de34403a8dec1e8d5e6be2fee96c4deca63b95d71df7a6a80a690de"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + [[package]] name = "fastjsonschema" version = "2.19.1" @@ -4770,4 +4784,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "ccfd855abf7309cf520ea5ca02e77fc9d8ab911819ea9690298fa0549a9d7c42" +content-hash = "ed0a2c9f32e010a33a24e243a58550c4849d10ee8205f6c459ecdde57d3509ac" diff --git a/pyproject.toml b/pyproject.toml index 970059f5a..4b8ab01ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,7 @@ python-dotenv = "==1.0.0" sqlalchemy = "==1.4.40" werkzeug = "^3.0.1" strenum = "^0.4.15" +faker = "^23.3.0" [tool.poetry.group.dev.dependencies] From 980e32c86c1c0898b90572760046fc064a41c00f Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Fri, 1 Mar 2024 14:43:36 -0700 Subject: [PATCH 222/259] Swapped random for secrets --- app/commands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/commands.py b/app/commands.py index 1377808db..6e401b3bd 100644 --- a/app/commands.py +++ b/app/commands.py @@ -1,7 +1,7 @@ import csv import functools import itertools -import random +import secrets import uuid from datetime import datetime, timedelta from os import getenv @@ -900,15 +900,15 @@ def add_test_organization_to_db(generate): "Science and Technology", ] - agency = random.choice(agency_names) - speciality = random.choice(government_sectors) + agency = secrets.choice(agency_names) + speciality = secrets.choice(government_sectors) return f"{fake.word().capitalize()} {speciality} {agency}" for num in range(1, int(generate) + 1): org = create_organization( name=generate_gov_agency(), - organization_type=random.choice(["federal", "state", "other"]), + organization_type=secrets.choice(["federal", "state", "other"]), ) print(f"{num} {org.name} created") From 0d856f07fc565b932a169e3619cf5124ad09afd9 Mon Sep 17 00:00:00 2001 From: Andrew Shumway Date: Mon, 4 Mar 2024 10:23:45 -0700 Subject: [PATCH 223/259] Removed gov.uk text from org invite --- app/organization/invite_rest.py | 2 +- tests/app/organization/test_invite_rest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index 18874e9a2..254de322c 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -55,7 +55,7 @@ def invite_user_to_org(organization_id): service=template.service, personalisation={ "user_name": ( - "The GOV.UK Notify team" + "The Notify.gov team" if invited_org_user.invited_by.platform_admin else invited_org_user.invited_by.name ), diff --git a/tests/app/organization/test_invite_rest.py b/tests/app/organization/test_invite_rest.py index c57fa8dd0..a68ec409f 100644 --- a/tests/app/organization/test_invite_rest.py +++ b/tests/app/organization/test_invite_rest.py @@ -13,7 +13,7 @@ from tests.app.db import create_invited_org_user @pytest.mark.parametrize( "platform_admin, expected_invited_by", - ((True, "The GOV.UK Notify team"), (False, "Test User")), + ((True, "The Notify.gov team"), (False, "Test User")), ) @pytest.mark.parametrize( "extra_args, expected_start_of_invite_url", From abce798f5b119069479d40f7946d5b5eca9a3159 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 4 Mar 2024 10:32:19 -0800 Subject: [PATCH 224/259] remove celery from notifications-utils (notify-api-130) --- app/__init__.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++-- poetry.lock | 9 +++--- pyproject.toml | 2 +- 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 5a364f151..9184c4815 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -3,15 +3,16 @@ import secrets import string import time import uuid +from contextlib import contextmanager from time import monotonic -from celery import current_task +from celery import Celery, Task, current_task from flask import current_app, g, has_request_context, jsonify, make_response, request +from flask.ctx import has_app_context from flask_marshmallow import Marshmallow from flask_migrate import Migrate from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy from notifications_utils import logging, request_helper -from notifications_utils.celery import NotifyCelery from notifications_utils.clients.encryption.encryption_client import Encryption from notifications_utils.clients.redis.redis_client import RedisClient from notifications_utils.clients.zendesk.zendesk_client import ZendeskClient @@ -27,6 +28,25 @@ from app.clients.email.aws_ses_stub import AwsSesStubClient from app.clients.sms.aws_sns import AwsSnsClient +class NotifyCelery(Celery): + def init_app(self, app): + self.task_cls = make_task(app) + + # Configure Celery app with options from the main app config. + self.config_from_object(app.config["CELERY"]) + + def send_task(self, name, args=None, kwargs=None, **other_kwargs): + other_kwargs["headers"] = other_kwargs.get("headers") or {} + + if has_request_context() and hasattr(request, "request_id"): + other_kwargs["headers"]["notify_request_id"] = request.request_id + + elif has_app_context() and "request_id" in g: + other_kwargs["headers"]["notify_request_id"] = g.request_id + + return super().send_task(name, args, kwargs, **other_kwargs) + + class SQLAlchemy(_SQLAlchemy): """We need to subclass SQLAlchemy in order to override create_engine options""" @@ -366,3 +386,58 @@ def setup_sqlalchemy_events(app): @event.listens_for(db.engine, "checkin") def checkin(dbapi_connection, connection_record): # noqa pass + + +def make_task(app): + class NotifyTask(Task): + abstract = True + start = None + + @property + def queue_name(self): + delivery_info = self.request.delivery_info or {} + return delivery_info.get("routing_key", "none") + + @property + def request_id(self): + # Note that each header is a direct attribute of the + # task context (aka "request"). + return self.request.get("notify_request_id") + + @contextmanager + def app_context(self): + with app.app_context(): + # Add 'request_id' to 'g' so that it gets logged. + g.request_id = self.request_id + yield + + def on_success(self, retval, task_id, args, kwargs): # noqa + # enables request id tracing for these logs + with self.app_context(): + elapsed_time = time.monotonic() - self.start + + app.logger.info( + "Celery task {task_name} (queue: {queue_name}) took {time}".format( + task_name=self.name, + queue_name=self.queue_name, + time="{0:.4f}".format(elapsed_time), + ) + ) + + def on_failure(self, exc, task_id, args, kwargs, einfo): # noqa + # enables request id tracing for these logs + with self.app_context(): + app.logger.exception( + "Celery task {task_name} (queue: {queue_name}) failed".format( + task_name=self.name, + queue_name=self.queue_name, + ) + ) + + def __call__(self, *args, **kwargs): + # ensure task has flask context to access config, logger, etc + with self.app_context(): + self.start = time.monotonic() + return super().__call__(*args, **kwargs) + + return NotifyTask diff --git a/poetry.lock b/poetry.lock index 8f8b2f11f..2030fc693 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohttp" @@ -2670,8 +2670,8 @@ werkzeug = "^3.0.1" [package.source] type = "git" url = "https://github.com/GSA/notifications-utils.git" -reference = "HEAD" -resolved_reference = "df8ece836cad303c5d161edf485cf9bbdc666688" +reference = "053fe30" +resolved_reference = "053fe304a1bbc1ae6559869b1fd0a2c3f83981ea" [[package]] name = "numpy" @@ -3479,7 +3479,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -4771,4 +4770,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "ccfd855abf7309cf520ea5ca02e77fc9d8ab911819ea9690298fa0549a9d7c42" +content-hash = "1041afd9fe9642567640966a7f7b1129f8f084f169419d8f2c7db9071672eb57" diff --git a/pyproject.toml b/pyproject.toml index 970059f5a..a87fefe96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ marshmallow = "==3.20.2" marshmallow-sqlalchemy = "==0.30.0" newrelic = "*" notifications-python-client = "==9.0.0" -notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"} +notifications-utils = {git = "https://github.com/GSA/notifications-utils.git", rev="053fe30"} oscrypto = "==1.3.0" packaging = "==23.2" poetry-dotenv-plugin = "==0.2.0" From ee6dfacad15f106b5fdbfafb44ac5ffdeb31c127 Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Mon, 4 Mar 2024 14:34:41 -0700 Subject: [PATCH 225/259] Standardized command names and updated documentation --- app/commands.py | 20 ++++++++++---------- docs/all.md | 12 ++++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/commands.py b/app/commands.py index 6e401b3bd..2c6d6bf75 100644 --- a/app/commands.py +++ b/app/commands.py @@ -856,9 +856,9 @@ fake = Faker(["en_US"]) # generate n number of test orgs into the dev DB -@notify_command(name="add-test-organization-to-db") +@notify_command(name="add-test-organizations-to-db") @click.option("-g", "--generate", required=True, prompt=True, default=1) -def add_test_organization_to_db(generate): +def add_test_organizations_to_db(generate): def generate_gov_agency(): agency_names = [ "Bureau", @@ -914,9 +914,9 @@ def add_test_organization_to_db(generate): # generate n number of test services into the dev DB -@notify_command(name="add-test-service-to-db") +@notify_command(name="add-test-services-to-db") @click.option("-g", "--generate", required=True, prompt=True, default=1) -def add_test_service_to_db(generate): +def add_test_services_to_db(generate): if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: current_app.logger.error("Can only be run in development") return @@ -928,9 +928,9 @@ def add_test_service_to_db(generate): # generate n number of test jobs into the dev DB -@notify_command(name="add-test-job-to-db") +@notify_command(name="add-test-jobs-to-db") @click.option("-g", "--generate", required=True, prompt=True, default=1) -def add_test_job_to_db(generate): +def add_test_jobs_to_db(generate): if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: current_app.logger.error("Can only be run in development") return @@ -943,9 +943,9 @@ def add_test_job_to_db(generate): # generate n number of notifications into the dev DB -@notify_command(name="add-test-notification-to-db") +@notify_command(name="add-test-notifications-to-db") @click.option("-g", "--generate", required=True, prompt=True, default=1) -def add_test_notification_to_db(generate): +def add_test_notifications_to_db(generate): if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: current_app.logger.error("Can only be run in development") return @@ -962,11 +962,11 @@ def add_test_notification_to_db(generate): # generate n number of test users into the dev DB -@notify_command(name="add-test-users") +@notify_command(name="add-test-users-to-db") @click.option("-g", "--generate", required=True, prompt=True, default="1") @click.option("-s", "--state", default="active") @click.option("-d", "--admin", default=False, type=bool) -def add_test_users(generate, state, admin): +def add_test_users_to_db(generate, state, admin): if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: current_app.logger.error("Can only be run in development") return diff --git a/docs/all.md b/docs/all.md index 4803102dd..c131a92d6 100644 --- a/docs/all.md +++ b/docs/all.md @@ -495,6 +495,18 @@ cf run-task CLOUD-GOV-APP --command "flask command update-templates" --name YOUR [Here's more documentation](https://docs.cloudfoundry.org/devguide/using-tasks.html) about Cloud Foundry tasks. +# Commands for test loading the local dev database + +All commands use the `-g` or `--generate` to determine how many instances to load to the db. The `-g` or `--generate` option is required and will always defult to 1. An example: `flask command add-test-uses-to-db -g 6` will generate 6 random users and insert them into the db. + +## Test commands list +- `add-test-organizations-to-db` +- `add-test-services-to-db` +- `add-test-jobs-to-db` +- `add-test-notifications-to-db` +- `add-test-users-to-db` (extra options include `-s` or `--state` and `-d` or `--admin`) + + # How messages are queued and sent There are several ways for notifications to come into the API. From 6cf9c811ae53e620a1d2b2f89d2bd52267718cbc Mon Sep 17 00:00:00 2001 From: Anastasia Gradova <108748167+anagradova@users.noreply.github.com> Date: Mon, 4 Mar 2024 15:59:44 -0700 Subject: [PATCH 226/259] Update app/commands.py Co-authored-by: Carlo Costino --- app/commands.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/commands.py b/app/commands.py index 2c6d6bf75..707cab466 100644 --- a/app/commands.py +++ b/app/commands.py @@ -859,6 +859,9 @@ fake = Faker(["en_US"]) @notify_command(name="add-test-organizations-to-db") @click.option("-g", "--generate", required=True, prompt=True, default=1) def add_test_organizations_to_db(generate): + if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: + current_app.logger.error("Can only be run in development") + return def generate_gov_agency(): agency_names = [ "Bureau", From 117ac98491a039aa63eb67bf378a86321b82dc55 Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Mon, 4 Mar 2024 16:02:21 -0700 Subject: [PATCH 227/259] moved faker to under imports according to PEP-8 --- app/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/commands.py b/app/commands.py index 2c6d6bf75..10e6f1f09 100644 --- a/app/commands.py +++ b/app/commands.py @@ -73,6 +73,8 @@ from tests.app.db import ( create_user, ) +#used in add-test-* commands +fake = Faker(["en_US"]) @click.group(name="command", help="Additional commands") def command_group(): @@ -852,8 +854,6 @@ faker is used to generate some random fields. All database commands were used from tests/app/db.py where possible to enable better maintainability. """ -fake = Faker(["en_US"]) - # generate n number of test orgs into the dev DB @notify_command(name="add-test-organizations-to-db") From 8e44e4d981ebd6bb7a636276719d289ca264e19d Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Tue, 5 Mar 2024 08:10:05 -0700 Subject: [PATCH 228/259] Corrected flake spacing --- app/commands.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/commands.py b/app/commands.py index 6fa858b75..17e8e1058 100644 --- a/app/commands.py +++ b/app/commands.py @@ -73,9 +73,10 @@ from tests.app.db import ( create_user, ) -#used in add-test-* commands +# used in add-test-* commands fake = Faker(["en_US"]) + @click.group(name="command", help="Additional commands") def command_group(): pass @@ -855,6 +856,7 @@ database commands were used from tests/app/db.py where possible to enable better maintainability. """ + # generate n number of test orgs into the dev DB @notify_command(name="add-test-organizations-to-db") @click.option("-g", "--generate", required=True, prompt=True, default=1) @@ -862,6 +864,7 @@ def add_test_organizations_to_db(generate): if getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: current_app.logger.error("Can only be run in development") return + def generate_gov_agency(): agency_names = [ "Bureau", From 703500274bc85de99035b03a533b0b9400f8e54a Mon Sep 17 00:00:00 2001 From: Anastasia Gradova Date: Tue, 5 Mar 2024 08:43:20 -0700 Subject: [PATCH 229/259] Updated doucmentation in README and all.md --- README.md | 1 + docs/all.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 94aea0c41..6f3aa924b 100644 --- a/README.md +++ b/README.md @@ -421,6 +421,7 @@ instructions above for more details. - [Migrations](./docs/all.md#migrations) - [Purging user data](./docs/all.md#purging-user-data) - [One-off tasks](./docs/all.md#one-off-tasks) +- [Test Loading Commands](./docs/all.md#commands-for-test-loading-the-local-dev-database) - [How messages are queued and sent](./docs/all.md#how-messages-are-queued-and-sent) - [Writing public APIs](./docs/all.md#writing-public-apis) - [Overview](./docs/all.md#overview) diff --git a/docs/all.md b/docs/all.md index c131a92d6..fecf705f4 100644 --- a/docs/all.md +++ b/docs/all.md @@ -22,6 +22,7 @@ - [Migrations](#migrations) - [Purging user data](#purging-user-data) - [One-off tasks](#one-off-tasks) +- [Test Loading Commands](#commands-for-test-loading-the-local-dev-database) - [How messages are queued and sent](#how-messages-are-queued-and-sent) - [Writing public APIs](#writing-public-apis) - [Overview](#overview) From a8dcda4c55f3d0ce18ecc984d8751aae22d42ab8 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Tue, 5 Mar 2024 11:21:38 -0800 Subject: [PATCH 230/259] fix test --- app/organization/invite_rest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/organization/invite_rest.py b/app/organization/invite_rest.py index 12b5eabc8..41b2b4660 100644 --- a/app/organization/invite_rest.py +++ b/app/organization/invite_rest.py @@ -53,7 +53,7 @@ def invite_user_to_org(organization_id): personalisation = { "user_name": ( - "The GOV.UK Notify team" + "The Notify.gov team" if invited_org_user.invited_by.platform_admin else invited_org_user.invited_by.name ), From 492daee970d39360186e427a9aa16ac9afb3c037 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 6 Mar 2024 10:28:34 -0800 Subject: [PATCH 231/259] fix personalisation for emails --- app/celery/provider_tasks.py | 7 ++++++- app/user/rest.py | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/celery/provider_tasks.py b/app/celery/provider_tasks.py index 3610126d4..47e4c0bd8 100644 --- a/app/celery/provider_tasks.py +++ b/app/celery/provider_tasks.py @@ -1,10 +1,11 @@ +import json import os from datetime import datetime, timedelta from flask import current_app from sqlalchemy.orm.exc import NoResultFound -from app import aws_cloudwatch_client, notify_celery +from app import aws_cloudwatch_client, notify_celery, redis_store from app.clients.email import EmailClientNonRetryableException from app.clients.email.aws_ses import AwsSesClientThrottlingSendRateException from app.clients.sms import SmsClientResponseException @@ -162,8 +163,12 @@ def deliver_email(self, notification_id): "Start sending email for notification id: {}".format(notification_id) ) notification = notifications_dao.get_notification_by_id(notification_id) + if not notification: raise NoResultFound() + personalisation = redis_store.get(f"email-personalisation-{notification_id}") + + notification.personalisation = json.loads(personalisation) send_to_providers.send_email_to_provider(notification) except EmailClientNonRetryableException as e: current_app.logger.exception( diff --git a/app/user/rest.py b/app/user/rest.py index 507e6c9e0..58a3d8e24 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -437,6 +437,11 @@ def send_new_user_email_verification(user_id): str(user_to_send_to.email_address), ex=60 * 60, ) + redis_store.set( + f"email-personalisation-{saved_notification.id}", + json.dumps(personalisation), + ex=60 * 60, + ) current_app.logger.info("Sending notification to queue") send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) From e2b2cdcfbc4364c49a7137a6d5a3e4bd15dd58c8 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 6 Mar 2024 10:55:03 -0800 Subject: [PATCH 232/259] fix tests --- tests/app/celery/test_provider_tasks.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/app/celery/test_provider_tasks.py b/tests/app/celery/test_provider_tasks.py index 3b02de283..4305f3aea 100644 --- a/tests/app/celery/test_provider_tasks.py +++ b/tests/app/celery/test_provider_tasks.py @@ -1,3 +1,5 @@ +import json + import pytest from botocore.exceptions import ClientError from celery.exceptions import MaxRetriesExceededError @@ -117,7 +119,7 @@ def test_should_call_send_email_to_provider_from_deliver_email_task( sample_notification, mocker ): mocker.patch("app.delivery.send_to_providers.send_email_to_provider") - + mocker.patch("app.redis_store.get", return_value=json.dumps({})) deliver_email(sample_notification.id) app.delivery.send_to_providers.send_email_to_provider.assert_called_with( sample_notification @@ -174,6 +176,7 @@ def test_should_technical_error_and_not_retry_if_EmailClientNonRetryableExceptio "app.delivery.send_to_providers.send_email_to_provider", side_effect=EmailClientNonRetryableException("bad email"), ) + mocker.patch("app.redis_store.get", return_value=json.dumps({})) mocker.patch("app.celery.provider_tasks.deliver_email.retry") deliver_email(sample_notification.id) @@ -197,6 +200,7 @@ def test_should_retry_and_log_exception_for_deliver_email_task( "app.delivery.send_to_providers.send_email_to_provider", side_effect=AwsSesClientException(str(ex)), ) + mocker.patch("app.celery.provider_tasks.deliver_email.retry") mock_logger_exception = mocker.patch( "app.celery.tasks.current_app.logger.exception" @@ -220,6 +224,7 @@ def test_if_ses_send_rate_throttle_then_should_retry_and_log_warning( } } ex = ClientError(error_response=error_response, operation_name="opname") + mocker.patch("app.redis_store.get", return_value=json.dumps({})) mocker.patch( "app.delivery.send_to_providers.send_email_to_provider", side_effect=AwsSesClientThrottlingSendRateException(str(ex)), From 89200e5f4d83082f3ea83193d579795ad900a12a Mon Sep 17 00:00:00 2001 From: Steven Reilly Date: Wed, 6 Mar 2024 14:18:01 -0500 Subject: [PATCH 233/259] Update README.md with logo --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a1deb4eca..78e542a9a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![notify-logo](https://github.com/GSA/notifications-api/assets/4156602/6b2905d2-a232-4414-8815-25dba6008f17) + # Notify.gov API This project is the core of [Notify.gov](https://notify-demo.app.cloud.gov). From 92d417171792158c21d767e58dbde2f16af976fe Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Mar 2024 11:35:26 -0500 Subject: [PATCH 234/259] We hope this is right. Signed-off-by: Cliff Hill --- terraform/shared/egress_space/main.tf | 1 + terraform/staging/main.tf | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/terraform/shared/egress_space/main.tf b/terraform/shared/egress_space/main.tf index 4b841ad14..a4cdd7311 100644 --- a/terraform/shared/egress_space/main.tf +++ b/terraform/shared/egress_space/main.tf @@ -11,6 +11,7 @@ data "cloudfoundry_org" "org" { ### resource "cloudfoundry_space" "public_egress" { + delete_recursive_allowed = false name = "${var.cf_restricted_space_name}-egress" org = data.cloudfoundry_org.org.id } diff --git a/terraform/staging/main.tf b/terraform/staging/main.tf index c46e0d3fa..4d14701ca 100644 --- a/terraform/staging/main.tf +++ b/terraform/staging/main.tf @@ -3,7 +3,6 @@ locals { cf_space_name = "notify-staging" env = "staging" app_name = "notify-api" - recursive_delete = true } module "database" { @@ -12,7 +11,6 @@ module "database" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-rds-${local.env}" - recursive_delete = local.recursive_delete rds_plan_name = "micro-psql" } @@ -22,7 +20,6 @@ module "redis" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-redis-${local.env}" - recursive_delete = local.recursive_delete redis_plan_name = "redis-dev" } @@ -31,7 +28,6 @@ module "csv_upload_bucket" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name - recursive_delete = local.recursive_delete name = "${local.app_name}-csv-upload-bucket-${local.env}" } @@ -53,7 +49,6 @@ module "ses_email" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-ses-${local.env}" - recursive_delete = local.recursive_delete aws_region = "us-west-2" mail_from_subdomain = "mail" email_receipt_error = "notify-support@gsa.gov" @@ -65,7 +60,6 @@ module "sns_sms" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-sns-${local.env}" - recursive_delete = local.recursive_delete aws_region = "us-west-2" monthly_spend_limit = 25 } From ed9896fdcefdd0d76aa79dedccf8e83b85a35342 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Mar 2024 11:37:36 -0500 Subject: [PATCH 235/259] Updating the versions for things. Signed-off-by: Cliff Hill --- terraform/shared/egress_space/providers.tf | 2 +- terraform/staging/providers.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/shared/egress_space/providers.tf b/terraform/shared/egress_space/providers.tf index 21ac567a2..01ab1f803 100644 --- a/terraform/shared/egress_space/providers.tf +++ b/terraform/shared/egress_space/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } } diff --git a/terraform/staging/providers.tf b/terraform/staging/providers.tf index 11dceea7d..0f09460ef 100644 --- a/terraform/staging/providers.tf +++ b/terraform/staging/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } From 7c95211649f51122202a872a95951aee24c44e8b Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Fri, 8 Mar 2024 08:44:27 -0800 Subject: [PATCH 236/259] fix login.gov to use user uuid instead of email (notify-admin-1277) --- app/dao/users_dao.py | 26 ++++++++++++++++++++++ app/models.py | 1 + app/user/rest.py | 11 +++++++++ migrations/versions/0411_add_login_uuid.py | 20 +++++++++++++++++ tests/app/dao/test_users_dao.py | 7 ++++++ 5 files changed, 65 insertions(+) create mode 100644 migrations/versions/0411_add_login_uuid.py diff --git a/app/dao/users_dao.py b/app/dao/users_dao.py index 7828c5c23..8180e6f11 100644 --- a/app/dao/users_dao.py +++ b/app/dao/users_dao.py @@ -25,6 +25,32 @@ def create_secret_code(length=6): return "{:0{length}d}".format(random_number, length=length) +def get_login_gov_user(login_uuid, email_address): + """ + We want to check to see if the user is registered with login.gov + If we can find the login.gov uuid in our user table, then they are. + + Also, because we originally keyed off email address we might have a few + older users who registered with login.gov but we don't know what their + login.gov uuids are. Eventually the code that checks by email address + should be removed. + """ + + print(User.query.filter_by(login_uuid=login_uuid).first()) + user = User.query.filter_by(login_uuid=login_uuid).first() + if user: + if user.email_address != email_address: + save_user_attribute(user, {"email_address": email_address}) + return user + # Remove this 1 July 2025, all users should have login.gov uuids by now + user = User.query.filter_by(email_address=email_address).first() + if user: + save_user_attribute(user, {"login_uuid": login_uuid}) + return user + + return None + + def save_user_attribute(usr, update_dict=None): db.session.query(User).filter_by(id=usr.id).update(update_dict or {}) db.session.commit() diff --git a/app/models.py b/app/models.py index 07806365f..953624126 100644 --- a/app/models.py +++ b/app/models.py @@ -109,6 +109,7 @@ class User(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) name = db.Column(db.String, nullable=False, index=True, unique=False) email_address = db.Column(db.String(255), nullable=False, index=True, unique=True) + login_uuid = db.Column(db.Text, nullable=True, index=True, unique=True) created_at = db.Column( db.DateTime, index=False, diff --git a/app/user/rest.py b/app/user/rest.py index 098d23de8..8f5f38833 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -19,6 +19,7 @@ from app.dao.users_dao import ( create_secret_code, create_user_code, dao_archive_user, + get_login_gov_user, get_user_and_accounts, get_user_by_email, get_user_by_id, @@ -528,6 +529,16 @@ def set_permissions(user_id, service_id): return jsonify({}), 204 +@user_blueprint.route("/get-login-gov-user", methods=["POST"]) +def get_user_login_gov_user(): + request_args = request.get_json() + login_uuid = request_args["login_uuid"] + email = request_args["email"] + user = get_login_gov_user(login_uuid, email) + result = user.serialize() + return jsonify(data=result) + + @user_blueprint.route("/email", methods=["POST"]) def fetch_user_by_email(): email = email_data_request_schema.load(request.get_json()) diff --git a/migrations/versions/0411_add_login_uuid.py b/migrations/versions/0411_add_login_uuid.py new file mode 100644 index 000000000..88032bf89 --- /dev/null +++ b/migrations/versions/0411_add_login_uuid.py @@ -0,0 +1,20 @@ +""" + +Revision ID: 0411_add_login_uuid +Revises: 410_enums_for_everything +Create Date: 2023-04-24 11:35:22.873930 + +""" +import sqlalchemy as sa +from alembic import op + +revision = "0411_add_login_uuid" +down_revision = "0410_enums_for_everything" + + +def upgrade(): + op.add_column("users", sa.Column("login_uuid", sa.Text)) + + +def downgrade(): + op.drop_column("users", "login_uuid") diff --git a/tests/app/dao/test_users_dao.py b/tests/app/dao/test_users_dao.py index 57ed65619..e38a395b5 100644 --- a/tests/app/dao/test_users_dao.py +++ b/tests/app/dao/test_users_dao.py @@ -15,6 +15,7 @@ from app.dao.users_dao import ( dao_archive_user, delete_codes_older_created_more_than_a_day_ago, delete_model_user, + get_login_gov_user, get_user_by_email, get_user_by_id, increment_failed_login_count, @@ -110,6 +111,12 @@ def test_get_user_by_email(sample_user): assert sample_user == user_from_db +def test_get_login_gov_user(sample_user): + user_from_db = get_login_gov_user("fake_login_gov_uuid", sample_user.email_address) + assert sample_user.email_address == user_from_db.email_address + assert user_from_db.login_uuid is not None + + def test_get_user_by_email_is_case_insensitive(sample_user): email = sample_user.email_address user_from_db = get_user_by_email(email.upper()) From 23167c5f16796f6b0a2db5502355537f2f0fd8ed Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Mar 2024 11:47:12 -0500 Subject: [PATCH 237/259] Formatting is annoying. Signed-off-by: Cliff Hill --- terraform/shared/egress_space/main.tf | 4 ++-- terraform/staging/main.tf | 30 +++++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/terraform/shared/egress_space/main.tf b/terraform/shared/egress_space/main.tf index a4cdd7311..5d4b53354 100644 --- a/terraform/shared/egress_space/main.tf +++ b/terraform/shared/egress_space/main.tf @@ -12,8 +12,8 @@ data "cloudfoundry_org" "org" { resource "cloudfoundry_space" "public_egress" { delete_recursive_allowed = false - name = "${var.cf_restricted_space_name}-egress" - org = data.cloudfoundry_org.org.id + name = "${var.cf_restricted_space_name}-egress" + org = data.cloudfoundry_org.org.id } ### diff --git a/terraform/staging/main.tf b/terraform/staging/main.tf index 4d14701ca..8cae5a8da 100644 --- a/terraform/staging/main.tf +++ b/terraform/staging/main.tf @@ -1,34 +1,34 @@ locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-staging" - env = "staging" - app_name = "notify-api" + cf_org_name = "gsa-tts-benefits-studio" + cf_space_name = "notify-staging" + env = "staging" + app_name = "notify-api" } module "database" { source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - rds_plan_name = "micro-psql" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-rds-${local.env}" + rds_plan_name = "micro-psql" } module "redis" { source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-${local.env}" - redis_plan_name = "redis-dev" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-redis-${local.env}" + redis_plan_name = "redis-dev" } module "csv_upload_bucket" { source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-csv-upload-bucket-${local.env}" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-csv-upload-bucket-${local.env}" } module "egress-space" { From b46bad8b7772d01f0246074ea31b6da10a2a5315 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Mar 2024 11:50:30 -0500 Subject: [PATCH 238/259] Version bumps aren't fun. Signed-off-by: Cliff Hill --- terraform/bootstrap/providers.tf | 2 +- terraform/development/providers.tf | 2 +- terraform/sandbox/providers.tf | 2 +- terraform/shared/ses/providers.tf | 2 +- terraform/shared/sns/providers.tf | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/terraform/bootstrap/providers.tf b/terraform/bootstrap/providers.tf index 5dcaece3e..3c699e728 100644 --- a/terraform/bootstrap/providers.tf +++ b/terraform/bootstrap/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } } diff --git a/terraform/development/providers.tf b/terraform/development/providers.tf index 5dcaece3e..3c699e728 100644 --- a/terraform/development/providers.tf +++ b/terraform/development/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } } diff --git a/terraform/sandbox/providers.tf b/terraform/sandbox/providers.tf index d5a3313de..590be4e3d 100644 --- a/terraform/sandbox/providers.tf +++ b/terraform/sandbox/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } diff --git a/terraform/shared/ses/providers.tf b/terraform/shared/ses/providers.tf index 21ac567a2..01ab1f803 100644 --- a/terraform/shared/ses/providers.tf +++ b/terraform/shared/ses/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } } diff --git a/terraform/shared/sns/providers.tf b/terraform/shared/sns/providers.tf index 21ac567a2..01ab1f803 100644 --- a/terraform/shared/sns/providers.tf +++ b/terraform/shared/sns/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } } From e843b05e00bb3401f6d44732b02aa965f9c46be1 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Mar 2024 12:00:36 -0500 Subject: [PATCH 239/259] Removing all the references! Signed-off-by: Cliff Hill --- terraform/development/main.tf | 2 -- terraform/sandbox/main.tf | 6 ------ terraform/shared/ses/main.tf | 6 +++--- terraform/shared/ses/variables.tf | 6 ------ terraform/shared/sns/main.tf | 6 +++--- terraform/shared/sns/variables.tf | 6 ------ 6 files changed, 6 insertions(+), 26 deletions(-) diff --git a/terraform/development/main.tf b/terraform/development/main.tf index 1f45b2b6a..3bb8ed886 100644 --- a/terraform/development/main.tf +++ b/terraform/development/main.tf @@ -1,7 +1,6 @@ locals { cf_org_name = "gsa-tts-benefits-studio" cf_space_name = "notify-local-dev" - recursive_delete = true key_name = "${var.username}-api-dev-key" } @@ -10,7 +9,6 @@ module "csv_upload_bucket" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name - recursive_delete = local.recursive_delete name = "${var.username}-csv-upload-bucket" } resource "cloudfoundry_service_key" "csv_key" { diff --git a/terraform/sandbox/main.tf b/terraform/sandbox/main.tf index fae30073c..b60723fe7 100644 --- a/terraform/sandbox/main.tf +++ b/terraform/sandbox/main.tf @@ -3,7 +3,6 @@ locals { cf_space_name = "notify-sandbox" env = "sandbox" app_name = "notify-api" - recursive_delete = true } module "database" { @@ -12,7 +11,6 @@ module "database" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-rds-${local.env}" - recursive_delete = local.recursive_delete rds_plan_name = "micro-psql" } @@ -22,7 +20,6 @@ module "redis" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-redis-${local.env}" - recursive_delete = local.recursive_delete redis_plan_name = "redis-dev" } @@ -31,7 +28,6 @@ module "csv_upload_bucket" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name - recursive_delete = local.recursive_delete name = "${local.app_name}-csv-upload-bucket-${local.env}" } @@ -53,7 +49,6 @@ module "ses_email" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-ses-${local.env}" - recursive_delete = local.recursive_delete aws_region = "us-west-2" email_receipt_error = "notify-support@gsa.gov" } @@ -64,7 +59,6 @@ module "sns_sms" { cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-sns-${local.env}" - recursive_delete = local.recursive_delete aws_region = "us-east-2" monthly_spend_limit = 1 } diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index a29a8ce10..3cf0edc73 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -3,8 +3,9 @@ ### data "cloudfoundry_space" "space" { - org_name = var.cf_org_name - name = var.cf_space_name + delete_recursive_allowed = true + org_name = var.cf_org_name + name = var.cf_space_name } ### @@ -19,7 +20,6 @@ resource "cloudfoundry_service_instance" "ses" { name = var.name space = data.cloudfoundry_space.space.id service_plan = data.cloudfoundry_service.ses.service_plans["base"] - recursive_delete = var.recursive_delete json_params = jsonencode({ region = var.aws_region domain = var.email_domain diff --git a/terraform/shared/ses/variables.tf b/terraform/shared/ses/variables.tf index 74e852cf6..a92261656 100644 --- a/terraform/shared/ses/variables.tf +++ b/terraform/shared/ses/variables.tf @@ -13,12 +13,6 @@ variable "name" { description = "name of the service instance" } -variable "recursive_delete" { - type = bool - description = "when true, deletes service bindings attached to the resource (not recommended for production)" - default = false -} - variable "aws_region" { type = string description = "AWS region the SES instance is in" diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index a23c4e872..b94728524 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -3,8 +3,9 @@ ### data "cloudfoundry_space" "space" { - org_name = var.cf_org_name - name = var.cf_space_name + delete_recursive_allowed = true + org_name = var.cf_org_name + name = var.cf_space_name } ### @@ -19,7 +20,6 @@ resource "cloudfoundry_service_instance" "sns" { name = var.name space = data.cloudfoundry_space.space.id service_plan = data.cloudfoundry_service.sns.service_plans["base"] - recursive_delete = var.recursive_delete json_params = jsonencode({ region = var.aws_region monthly_spend_limit = var.monthly_spend_limit diff --git a/terraform/shared/sns/variables.tf b/terraform/shared/sns/variables.tf index 611050337..acf7c5010 100644 --- a/terraform/shared/sns/variables.tf +++ b/terraform/shared/sns/variables.tf @@ -13,12 +13,6 @@ variable "name" { description = "name of the service instance" } -variable "recursive_delete" { - type = bool - description = "when true, deletes service bindings attached to the resource (not recommended for production)" - default = false -} - variable "aws_region" { type = string description = "AWS region the SNS settings are set in" From 9ce1e4816f587d46c87e5bf965bd0484fd97c8f3 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Mar 2024 12:01:06 -0500 Subject: [PATCH 240/259] Terraform formatting Signed-off-by: Cliff Hill --- terraform/development/main.tf | 12 ++++++------ terraform/sandbox/main.tf | 30 +++++++++++++++--------------- terraform/shared/ses/main.tf | 6 +++--- terraform/shared/sns/main.tf | 6 +++--- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/terraform/development/main.tf b/terraform/development/main.tf index 3bb8ed886..4cc26b4d7 100644 --- a/terraform/development/main.tf +++ b/terraform/development/main.tf @@ -1,15 +1,15 @@ locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-local-dev" - key_name = "${var.username}-api-dev-key" + cf_org_name = "gsa-tts-benefits-studio" + cf_space_name = "notify-local-dev" + key_name = "${var.username}-api-dev-key" } module "csv_upload_bucket" { source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${var.username}-csv-upload-bucket" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${var.username}-csv-upload-bucket" } resource "cloudfoundry_service_key" "csv_key" { name = local.key_name diff --git a/terraform/sandbox/main.tf b/terraform/sandbox/main.tf index b60723fe7..4c93f8a2c 100644 --- a/terraform/sandbox/main.tf +++ b/terraform/sandbox/main.tf @@ -1,34 +1,34 @@ locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-sandbox" - env = "sandbox" - app_name = "notify-api" + cf_org_name = "gsa-tts-benefits-studio" + cf_space_name = "notify-sandbox" + env = "sandbox" + app_name = "notify-api" } module "database" { source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - rds_plan_name = "micro-psql" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-rds-${local.env}" + rds_plan_name = "micro-psql" } module "redis" { source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-${local.env}" - redis_plan_name = "redis-dev" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-redis-${local.env}" + redis_plan_name = "redis-dev" } module "csv_upload_bucket" { source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-csv-upload-bucket-${local.env}" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-csv-upload-bucket-${local.env}" } module "egress-space" { diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index 3cf0edc73..e4d63fb53 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -17,9 +17,9 @@ data "cloudfoundry_service" "ses" { } resource "cloudfoundry_service_instance" "ses" { - name = var.name - space = data.cloudfoundry_space.space.id - service_plan = data.cloudfoundry_service.ses.service_plans["base"] + name = var.name + space = data.cloudfoundry_space.space.id + service_plan = data.cloudfoundry_service.ses.service_plans["base"] json_params = jsonencode({ region = var.aws_region domain = var.email_domain diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index b94728524..7b99c83af 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -17,9 +17,9 @@ data "cloudfoundry_service" "sns" { } resource "cloudfoundry_service_instance" "sns" { - name = var.name - space = data.cloudfoundry_space.space.id - service_plan = data.cloudfoundry_service.sns.service_plans["base"] + name = var.name + space = data.cloudfoundry_space.space.id + service_plan = data.cloudfoundry_service.sns.service_plans["base"] json_params = jsonencode({ region = var.aws_region monthly_spend_limit = var.monthly_spend_limit From 4d89e6451dc3eff108f1d6834981beabef2e7486 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Fri, 8 Mar 2024 12:04:03 -0500 Subject: [PATCH 241/259] Reversing the over-engineering. Signed-off-by: Cliff Hill --- terraform/shared/ses/main.tf | 5 ++--- terraform/shared/sns/main.tf | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index e4d63fb53..4c1bb54b9 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -3,9 +3,8 @@ ### data "cloudfoundry_space" "space" { - delete_recursive_allowed = true - org_name = var.cf_org_name - name = var.cf_space_name + org_name = var.cf_org_name + name = var.cf_space_name } ### diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index 7b99c83af..aa0079f92 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -3,9 +3,8 @@ ### data "cloudfoundry_space" "space" { - delete_recursive_allowed = true - org_name = var.cf_org_name - name = var.cf_space_name + org_name = var.cf_org_name + name = var.cf_space_name } ### From 6c4c8b075a37c07e34610fcb19d4c018502b7163 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Mon, 11 Mar 2024 10:34:59 -0700 Subject: [PATCH 242/259] fix code coverage reporting --- .github/workflows/checks.yml | 3 ++- Makefile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 280d2566e..dd0fe3275 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -61,7 +61,8 @@ jobs: NOTIFY_E2E_TEST_HTTP_AUTH_USER: ${{ secrets.NOTIFY_E2E_TEST_HTTP_AUTH_USER }} NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }} - name: Check coverage threshold - run: poetry run coverage report --fail-under=50 + # TODO get this back up to 95 + run: poetry run coverage report --fail-under=87 validate-new-relic-config: runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 4273f7b7e..e86cfc1c3 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ test: ## Run tests and create coverage report poetry run black . poetry run flake8 . poetry run isort --check-only ./app ./tests - poetry run coverage run -m pytest -vv --maxfail=10 + poetry run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 poetry run coverage report -m --fail-under=95 poetry run coverage html -d .coverage_cache From 61703471bbad855c972a9e9062a8977cab59db4f Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 12 Mar 2024 13:59:08 -0400 Subject: [PATCH 243/259] Fix remaining Terraform for production and demo This changeset adjusts our Terraform for the production and demo environments so that the new delete_recursive_allowed with the Cloud Foundry Cloud Controller is set to false. It also updates the shared modules to all explicitly account for this property so that we can set the flag easily. Signed-off-by: Carlo Costino --- terraform/demo/main.tf | 73 ++++++++++++++------------ terraform/demo/providers.tf | 2 +- terraform/production/main.tf | 75 +++++++++++++++------------ terraform/production/providers.tf | 2 +- terraform/shared/egress_space/main.tf | 2 +- terraform/shared/ses/main.tf | 9 ++++ terraform/shared/ses/variables.tf | 6 +++ terraform/shared/sns/main.tf | 9 ++++ terraform/shared/sns/variables.tf | 6 +++ 9 files changed, 116 insertions(+), 68 deletions(-) diff --git a/terraform/demo/main.tf b/terraform/demo/main.tf index 615f92670..5f3f8525e 100644 --- a/terraform/demo/main.tf +++ b/terraform/demo/main.tf @@ -1,38 +1,46 @@ locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-demo" - env = "demo" - app_name = "notify-api" - recursive_delete = false + cf_org_name = "gsa-tts-benefits-studio" + cf_space_name = "notify-demo" + env = "demo" + app_name = "notify-api" + delete_recursive_allowed = false +} + +data "cloudfoundry_space" "demo" { + org_name = local.cf_org_name + name = local.cf_space_name +} + +resource "cloudfoundry_space" "notify-demo" { + delete_recursive_allowed = local.delete_recursive_allowed + name = local.cf_space_name + org = data.cloudfoundry_org.org.id } module "database" { source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - recursive_delete = local.recursive_delete - rds_plan_name = "micro-psql" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-rds-${local.env}" + rds_plan_name = "micro-psql" } module "redis" { source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-${local.env}" - recursive_delete = local.recursive_delete - redis_plan_name = "redis-dev" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-redis-${local.env}" + redis_plan_name = "redis-dev" } module "csv_upload_bucket" { source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - recursive_delete = local.recursive_delete - name = "${local.app_name}-csv-upload-bucket-${local.env}" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-csv-upload-bucket-${local.env}" } module "egress-space" { @@ -40,6 +48,7 @@ module "egress-space" { cf_org_name = local.cf_org_name cf_restricted_space_name = local.cf_space_name + delete_recursive_allowed = local.delete_recursive_allowed deployers = [ var.cf_user, "steven.reilly@gsa.gov" @@ -49,22 +58,22 @@ module "egress-space" { module "ses_email" { source = "../shared/ses" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - recursive_delete = local.recursive_delete - aws_region = "us-west-2" - email_domain = "notify.sandbox.10x.gsa.gov" - email_receipt_error = "notify-support@gsa.gov" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-ses-${local.env}" + aws_region = "us-west-2" + email_domain = "notify.sandbox.10x.gsa.gov" + email_receipt_error = "notify-support@gsa.gov" + delete_recursive_allowed = local.delete_recursive_allowed } module "sns_sms" { source = "../shared/sns" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - recursive_delete = local.recursive_delete - aws_region = "us-east-1" - monthly_spend_limit = 25 + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-sns-${local.env}" + aws_region = "us-east-1" + monthly_spend_limit = 25 + delete_recursive_allowed = local.delete_recursive_allowed } diff --git a/terraform/demo/providers.tf b/terraform/demo/providers.tf index f13333d3e..34ba30a62 100644 --- a/terraform/demo/providers.tf +++ b/terraform/demo/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } diff --git a/terraform/production/main.tf b/terraform/production/main.tf index 5a2c520b1..c4ca005f5 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -1,38 +1,46 @@ locals { - cf_org_name = "gsa-tts-benefits-studio" - cf_space_name = "notify-production" - env = "production" - app_name = "notify-api" - recursive_delete = false + cf_org_name = "gsa-tts-benefits-studio" + cf_space_name = "notify-production" + env = "production" + app_name = "notify-api" + delete_recursive_allowed = false +} + +data "cloudfoundry_space" "production" { + org_name = local.cf_org_name + name = local.cf_space_name +} + +resource "cloudfoundry_space" "notify-production" { + delete_recursive_allowed = local.delete_recursive_allowed + name = local.cf_space_name + org = data.cloudfoundry_org.org.id } module "database" { source = "github.com/18f/terraform-cloudgov//database?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-rds-${local.env}" - recursive_delete = local.recursive_delete - rds_plan_name = "small-psql-redundant" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-rds-${local.env}" + rds_plan_name = "small-psql-redundant" } module "redis" { source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-redis-${local.env}" - recursive_delete = local.recursive_delete - redis_plan_name = "redis-3node-large" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-redis-${local.env}" + redis_plan_name = "redis-3node-large" } module "csv_upload_bucket" { source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - recursive_delete = local.recursive_delete - name = "${local.app_name}-csv-upload-bucket-${local.env}" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-csv-upload-bucket-${local.env}" } module "egress-space" { @@ -40,6 +48,7 @@ module "egress-space" { cf_org_name = local.cf_org_name cf_restricted_space_name = local.cf_space_name + delete_recursive_allowed = local.delete_recursive_allowed deployers = [ var.cf_user ] @@ -48,25 +57,25 @@ module "egress-space" { module "ses_email" { source = "../shared/ses" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - recursive_delete = local.recursive_delete - aws_region = "us-gov-west-1" - email_domain = "notify.gov" - mail_from_subdomain = "mail" - email_receipt_error = "notify-support@gsa.gov" + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-ses-${local.env}" + aws_region = "us-gov-west-1" + email_domain = "notify.gov" + mail_from_subdomain = "mail" + email_receipt_error = "notify-support@gsa.gov" + delete_recursive_allowed = local.delete_recursive_allowed } module "sns_sms" { source = "../shared/sns" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - recursive_delete = local.recursive_delete - aws_region = "us-gov-west-1" - monthly_spend_limit = 1000 + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-sns-${local.env}" + aws_region = "us-gov-west-1" + monthly_spend_limit = 1000 + delete_recursive_allowed = local.delete_recursive_allowed } ########################################################################### diff --git a/terraform/production/providers.tf b/terraform/production/providers.tf index 499759f48..b5c45f63e 100644 --- a/terraform/production/providers.tf +++ b/terraform/production/providers.tf @@ -3,7 +3,7 @@ terraform { required_providers { cloudfoundry = { source = "cloudfoundry-community/cloudfoundry" - version = "0.53.0" + version = "0.53.1" } } diff --git a/terraform/shared/egress_space/main.tf b/terraform/shared/egress_space/main.tf index 5d4b53354..066f0ba58 100644 --- a/terraform/shared/egress_space/main.tf +++ b/terraform/shared/egress_space/main.tf @@ -11,7 +11,7 @@ data "cloudfoundry_org" "org" { ### resource "cloudfoundry_space" "public_egress" { - delete_recursive_allowed = false + delete_recursive_allowed = var.delete_recursive_allowed name = "${var.cf_restricted_space_name}-egress" org = data.cloudfoundry_org.org.id } diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index 4c1bb54b9..1bee9a74a 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -7,6 +7,15 @@ data "cloudfoundry_space" "space" { name = var.cf_space_name } +### +# SES Space +### +resource "cloudfoundry_space" "cf_ses_service_space" { + delete_recursive_allowed = var.delete_recursive_allowed + name = data.cloudfoundry_space.space.name + org = data.cloudfoundry_org.org.id +} + ### # SES instance ### diff --git a/terraform/shared/ses/variables.tf b/terraform/shared/ses/variables.tf index a92261656..35e8cad8c 100644 --- a/terraform/shared/ses/variables.tf +++ b/terraform/shared/ses/variables.tf @@ -34,3 +34,9 @@ variable "mail_from_subdomain" { description = "Subdomain of email_domain to set as the mail-from header" default = "" } + +variable "delete_recursive_allowed" { + type = bool + default = true + description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" +} diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index aa0079f92..2df89907d 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -7,6 +7,15 @@ data "cloudfoundry_space" "space" { name = var.cf_space_name } +### +# SNS Space +### +resource "cloudfoundry_space" "cf_sns_service_space" { + delete_recursive_allowed = var.delete_recursive_allowed + name = data.cloudfoundry_space.space.name + org = data.cloudfoundry_org.org.id +} + ### # SES instance ### diff --git a/terraform/shared/sns/variables.tf b/terraform/shared/sns/variables.tf index acf7c5010..801e41504 100644 --- a/terraform/shared/sns/variables.tf +++ b/terraform/shared/sns/variables.tf @@ -22,3 +22,9 @@ variable "monthly_spend_limit" { type = number description = "SMS budget limit in USD. Support request must be made before raising above 1" } + +variable "delete_recursive_allowed" { + type = bool + default = true + description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" +} From ef46ddcb37d2ee4394c72a9d4574674226707837 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 12 Mar 2024 14:19:59 -0400 Subject: [PATCH 244/259] Fixed reference to the Cloud Foundry org instead of space Signed-off-by: Carlo Costino --- terraform/shared/ses/main.tf | 5 ++--- terraform/shared/sns/main.tf | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index 1bee9a74a..174704d01 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -2,9 +2,8 @@ # Target space/org ### -data "cloudfoundry_space" "space" { - org_name = var.cf_org_name - name = var.cf_space_name +data "cloudfoundry_org" "org" { + name = var.cf_org_name } ### diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index 2df89907d..57d85e562 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -2,9 +2,8 @@ # Target space/org ### -data "cloudfoundry_space" "space" { - org_name = var.cf_org_name - name = var.cf_space_name +data "cloudfoundry_org" "org" { + name = var.cf_org_name } ### From 68fa12340ad4ad236cb7e9bba5072dda16971aa8 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 12 Mar 2024 14:22:58 -0400 Subject: [PATCH 245/259] Add space data back in - we still need it! Signed-off-by: Carlo Costino --- terraform/shared/ses/main.tf | 5 +++++ terraform/shared/sns/main.tf | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index 174704d01..764cd3999 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -6,6 +6,11 @@ data "cloudfoundry_org" "org" { name = var.cf_org_name } +data "cloudfoundry_space" "space" { + org_name = var.cf_org_name + name = var.cf_space_name +} + ### # SES Space ### diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index 57d85e562..46319581f 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -6,6 +6,11 @@ data "cloudfoundry_org" "org" { name = var.cf_org_name } +data "cloudfoundry_space" "space" { + org_name = var.cf_org_name + name = var.cf_space_name +} + ### # SNS Space ### From f0e66886d21dc2c41f8c53db4477834ce7432ae1 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Tue, 12 Mar 2024 14:26:28 -0400 Subject: [PATCH 246/259] Adding missing variable for egress_space Signed-off-by: Carlo Costino --- terraform/shared/egress_space/variables.tf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/terraform/shared/egress_space/variables.tf b/terraform/shared/egress_space/variables.tf index 45bcc717d..6325fa7cf 100644 --- a/terraform/shared/egress_space/variables.tf +++ b/terraform/shared/egress_space/variables.tf @@ -3,3 +3,9 @@ variable "cf_restricted_space_name" {} variable "deployers" { type = set(string) } + +variable "delete_recursive_allowed" { + type = bool + default = true + description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" +} From 111135a9b6a93970117f3868e4bcb5813d5b2904 Mon Sep 17 00:00:00 2001 From: Cliff Hill Date: Tue, 12 Mar 2024 15:05:34 -0400 Subject: [PATCH 247/259] Helping the migration succeed! Signed-off-by: Cliff Hill --- migrations/versions/0410_enums_for_everything.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/migrations/versions/0410_enums_for_everything.py b/migrations/versions/0410_enums_for_everything.py index e34a3621a..b6c9042c6 100644 --- a/migrations/versions/0410_enums_for_everything.py +++ b/migrations/versions/0410_enums_for_everything.py @@ -468,6 +468,19 @@ def upgrade(): existing_nullable=False, postgresql_using=enum_using("notification_type", NotificationType), ) + # Clobbering bad data here. These are values we don't use any more, and anything with them is unnecessary. + op.execute(""" + delete from + service_permissions + where + permission in ( + 'letter', + 'letters_as_pdf', + 'upload_letters', + 'international_letters', + 'broadcast' + ); + """) op.alter_column( "service_permissions", "permission", From 15f8be7aee0ffb97033c5b22c881fd45b65f1a72 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 13 Mar 2024 09:51:41 -0400 Subject: [PATCH 248/259] Explicitly add allow_ssh flag and disable for production This will also ensure any drift is picked up by our infrastructure verification checks Signed-off-by: Carlo Costino --- terraform/production/main.tf | 5 +++++ terraform/shared/egress_space/main.tf | 1 + terraform/shared/egress_space/variables.tf | 6 ++++++ terraform/shared/ses/main.tf | 1 + terraform/shared/ses/variables.tf | 6 ++++++ terraform/shared/sns/main.tf | 1 + terraform/shared/sns/variables.tf | 6 ++++++ 7 files changed, 26 insertions(+) diff --git a/terraform/production/main.tf b/terraform/production/main.tf index c4ca005f5..e2c321d37 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -4,6 +4,7 @@ locals { env = "production" app_name = "notify-api" delete_recursive_allowed = false + allow_ssh = false } data "cloudfoundry_space" "production" { @@ -12,6 +13,7 @@ data "cloudfoundry_space" "production" { } resource "cloudfoundry_space" "notify-production" { + allow_ssh = local.allow_ssh delete_recursive_allowed = local.delete_recursive_allowed name = local.cf_space_name org = data.cloudfoundry_org.org.id @@ -46,6 +48,7 @@ module "csv_upload_bucket" { module "egress-space" { source = "../shared/egress_space" + allow_ssh = local.allow_ssh cf_org_name = local.cf_org_name cf_restricted_space_name = local.cf_space_name delete_recursive_allowed = local.delete_recursive_allowed @@ -57,6 +60,7 @@ module "egress-space" { module "ses_email" { source = "../shared/ses" + allow_ssh = local.allow_ssh cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-ses-${local.env}" @@ -70,6 +74,7 @@ module "ses_email" { module "sns_sms" { source = "../shared/sns" + allow_ssh = local.allow_ssh cf_org_name = local.cf_org_name cf_space_name = local.cf_space_name name = "${local.app_name}-sns-${local.env}" diff --git a/terraform/shared/egress_space/main.tf b/terraform/shared/egress_space/main.tf index 066f0ba58..cc91e9c42 100644 --- a/terraform/shared/egress_space/main.tf +++ b/terraform/shared/egress_space/main.tf @@ -11,6 +11,7 @@ data "cloudfoundry_org" "org" { ### resource "cloudfoundry_space" "public_egress" { + allow_ssh = var.allow_ssh delete_recursive_allowed = var.delete_recursive_allowed name = "${var.cf_restricted_space_name}-egress" org = data.cloudfoundry_org.org.id diff --git a/terraform/shared/egress_space/variables.tf b/terraform/shared/egress_space/variables.tf index 6325fa7cf..5bdff893f 100644 --- a/terraform/shared/egress_space/variables.tf +++ b/terraform/shared/egress_space/variables.tf @@ -9,3 +9,9 @@ variable "delete_recursive_allowed" { default = true description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" } + +variable "allow_ssh" { + type = bool + default = true + description = "Flag for allowing SSH access in a space - not recommended in production environments" +} diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index 764cd3999..0661d1089 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -15,6 +15,7 @@ data "cloudfoundry_space" "space" { # SES Space ### resource "cloudfoundry_space" "cf_ses_service_space" { + allow_ssh = var.allow_ssh delete_recursive_allowed = var.delete_recursive_allowed name = data.cloudfoundry_space.space.name org = data.cloudfoundry_org.org.id diff --git a/terraform/shared/ses/variables.tf b/terraform/shared/ses/variables.tf index 35e8cad8c..e6a1b2b62 100644 --- a/terraform/shared/ses/variables.tf +++ b/terraform/shared/ses/variables.tf @@ -40,3 +40,9 @@ variable "delete_recursive_allowed" { default = true description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" } + +variable "allow_ssh" { + type = bool + default = true + description = "Flag for allowing SSH access in a space - not recommended in production environments" +} diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index 46319581f..a00171a98 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -15,6 +15,7 @@ data "cloudfoundry_space" "space" { # SNS Space ### resource "cloudfoundry_space" "cf_sns_service_space" { + allow_ssh = var.allow_ssh delete_recursive_allowed = var.delete_recursive_allowed name = data.cloudfoundry_space.space.name org = data.cloudfoundry_org.org.id diff --git a/terraform/shared/sns/variables.tf b/terraform/shared/sns/variables.tf index 801e41504..d75ddeda3 100644 --- a/terraform/shared/sns/variables.tf +++ b/terraform/shared/sns/variables.tf @@ -28,3 +28,9 @@ variable "delete_recursive_allowed" { default = true description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" } + +variable "allow_ssh" { + type = bool + default = true + description = "Flag for allowing SSH access in a space - not recommended in production environments" +} From 1484c2ffa0b9253632fa5f0ae630b06860595360 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 13 Mar 2024 10:05:02 -0400 Subject: [PATCH 249/259] Adjust properties further to only apply to spaces The SES and SNS modules are strictly for services that are instantiated within a space, while the egress_space is creating a separate space. The shift of the recursive delete from being on a service to being at the space level means that the SES and SNS modules do not have to track it at all, it should only be handled at the space level. The same goes for the allow_ssh flag. Signed-off-by: Carlo Costino --- terraform/demo/main.tf | 24 +++++++++++------------- terraform/production/main.tf | 28 ++++++++++++---------------- terraform/shared/ses/main.tf | 10 ---------- terraform/shared/ses/variables.tf | 12 ------------ terraform/shared/sns/main.tf | 10 ---------- terraform/shared/sns/variables.tf | 12 ------------ 6 files changed, 23 insertions(+), 73 deletions(-) diff --git a/terraform/demo/main.tf b/terraform/demo/main.tf index 5f3f8525e..0ac491fc5 100644 --- a/terraform/demo/main.tf +++ b/terraform/demo/main.tf @@ -58,22 +58,20 @@ module "egress-space" { module "ses_email" { source = "../shared/ses" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - aws_region = "us-west-2" - email_domain = "notify.sandbox.10x.gsa.gov" - email_receipt_error = "notify-support@gsa.gov" - delete_recursive_allowed = local.delete_recursive_allowed + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-ses-${local.env}" + aws_region = "us-west-2" + email_domain = "notify.sandbox.10x.gsa.gov" + email_receipt_error = "notify-support@gsa.gov" } module "sns_sms" { source = "../shared/sns" - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - aws_region = "us-east-1" - monthly_spend_limit = 25 - delete_recursive_allowed = local.delete_recursive_allowed + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-sns-${local.env}" + aws_region = "us-east-1" + monthly_spend_limit = 25 } diff --git a/terraform/production/main.tf b/terraform/production/main.tf index e2c321d37..f7fc93d68 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -60,27 +60,23 @@ module "egress-space" { module "ses_email" { source = "../shared/ses" - allow_ssh = local.allow_ssh - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-ses-${local.env}" - aws_region = "us-gov-west-1" - email_domain = "notify.gov" - mail_from_subdomain = "mail" - email_receipt_error = "notify-support@gsa.gov" - delete_recursive_allowed = local.delete_recursive_allowed + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-ses-${local.env}" + aws_region = "us-gov-west-1" + email_domain = "notify.gov" + mail_from_subdomain = "mail" + email_receipt_error = "notify-support@gsa.gov" } module "sns_sms" { source = "../shared/sns" - allow_ssh = local.allow_ssh - cf_org_name = local.cf_org_name - cf_space_name = local.cf_space_name - name = "${local.app_name}-sns-${local.env}" - aws_region = "us-gov-west-1" - monthly_spend_limit = 1000 - delete_recursive_allowed = local.delete_recursive_allowed + cf_org_name = local.cf_org_name + cf_space_name = local.cf_space_name + name = "${local.app_name}-sns-${local.env}" + aws_region = "us-gov-west-1" + monthly_spend_limit = 1000 } ########################################################################### diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index 0661d1089..80c40042b 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -11,16 +11,6 @@ data "cloudfoundry_space" "space" { name = var.cf_space_name } -### -# SES Space -### -resource "cloudfoundry_space" "cf_ses_service_space" { - allow_ssh = var.allow_ssh - delete_recursive_allowed = var.delete_recursive_allowed - name = data.cloudfoundry_space.space.name - org = data.cloudfoundry_org.org.id -} - ### # SES instance ### diff --git a/terraform/shared/ses/variables.tf b/terraform/shared/ses/variables.tf index e6a1b2b62..a92261656 100644 --- a/terraform/shared/ses/variables.tf +++ b/terraform/shared/ses/variables.tf @@ -34,15 +34,3 @@ variable "mail_from_subdomain" { description = "Subdomain of email_domain to set as the mail-from header" default = "" } - -variable "delete_recursive_allowed" { - type = bool - default = true - description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" -} - -variable "allow_ssh" { - type = bool - default = true - description = "Flag for allowing SSH access in a space - not recommended in production environments" -} diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index a00171a98..73cb8a815 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -11,16 +11,6 @@ data "cloudfoundry_space" "space" { name = var.cf_space_name } -### -# SNS Space -### -resource "cloudfoundry_space" "cf_sns_service_space" { - allow_ssh = var.allow_ssh - delete_recursive_allowed = var.delete_recursive_allowed - name = data.cloudfoundry_space.space.name - org = data.cloudfoundry_org.org.id -} - ### # SES instance ### diff --git a/terraform/shared/sns/variables.tf b/terraform/shared/sns/variables.tf index d75ddeda3..acf7c5010 100644 --- a/terraform/shared/sns/variables.tf +++ b/terraform/shared/sns/variables.tf @@ -22,15 +22,3 @@ variable "monthly_spend_limit" { type = number description = "SMS budget limit in USD. Support request must be made before raising above 1" } - -variable "delete_recursive_allowed" { - type = bool - default = true - description = "Flag for allowing resources to be recursively deleted - not recommended in production environments" -} - -variable "allow_ssh" { - type = bool - default = true - description = "Flag for allowing SSH access in a space - not recommended in production environments" -} From 0404348ec0738117be93e3850732da8570513224 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 13 Mar 2024 10:12:54 -0400 Subject: [PATCH 250/259] Removed last bit of extraneous config that is not needed Signed-off-by: Carlo Costino --- terraform/shared/ses/main.tf | 4 ---- terraform/shared/sns/main.tf | 4 ---- 2 files changed, 8 deletions(-) diff --git a/terraform/shared/ses/main.tf b/terraform/shared/ses/main.tf index 80c40042b..4c1bb54b9 100644 --- a/terraform/shared/ses/main.tf +++ b/terraform/shared/ses/main.tf @@ -2,10 +2,6 @@ # Target space/org ### -data "cloudfoundry_org" "org" { - name = var.cf_org_name -} - data "cloudfoundry_space" "space" { org_name = var.cf_org_name name = var.cf_space_name diff --git a/terraform/shared/sns/main.tf b/terraform/shared/sns/main.tf index 73cb8a815..aa0079f92 100644 --- a/terraform/shared/sns/main.tf +++ b/terraform/shared/sns/main.tf @@ -2,10 +2,6 @@ # Target space/org ### -data "cloudfoundry_org" "org" { - name = var.cf_org_name -} - data "cloudfoundry_space" "space" { org_name = var.cf_org_name name = var.cf_space_name From 9d60e6e8b7350f825215caf68a2f0f4b9050b468 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Wed, 13 Mar 2024 11:26:01 -0400 Subject: [PATCH 251/259] Fix reference to CF org vs. space This changeset fixes a reference to properly load the Cloud Foundry org for modifying the space. Signed-off-by: Carlo Costino --- terraform/demo/main.tf | 5 ++--- terraform/production/main.tf | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/terraform/demo/main.tf b/terraform/demo/main.tf index 0ac491fc5..e594264c2 100644 --- a/terraform/demo/main.tf +++ b/terraform/demo/main.tf @@ -6,9 +6,8 @@ locals { delete_recursive_allowed = false } -data "cloudfoundry_space" "demo" { - org_name = local.cf_org_name - name = local.cf_space_name +data "cloudfoundry_org" "org" { + name = local.cf_org_name } resource "cloudfoundry_space" "notify-demo" { diff --git a/terraform/production/main.tf b/terraform/production/main.tf index f7fc93d68..ff1daad88 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -7,9 +7,8 @@ locals { allow_ssh = false } -data "cloudfoundry_space" "production" { - org_name = local.cf_org_name - name = local.cf_space_name +data "cloudfoundry_org" "org" { + name = local.cf_org_name } resource "cloudfoundry_space" "notify-production" { From a8640a65b6f73c89866d75b1918e41ca6c1577c1 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Wed, 13 Mar 2024 10:52:55 -0700 Subject: [PATCH 252/259] fix email notifications missing personalisation (notify-api-853) --- app/organization/rest.py | 9 +++++++++ app/user/rest.py | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/organization/rest.py b/app/organization/rest.py index 7ca9eec8a..8da757cbc 100644 --- a/app/organization/rest.py +++ b/app/organization/rest.py @@ -1,6 +1,9 @@ +import json + from flask import Blueprint, abort, current_app, jsonify, request from sqlalchemy.exc import IntegrityError +from app import redis_store from app.config import QueueNames from app.dao.annual_billing_dao import set_default_free_allowance_for_service from app.dao.dao_utils import transaction @@ -210,6 +213,12 @@ def send_notifications_on_mou_signed(organization_id): reply_to_text=notify_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) personalisation = { diff --git a/app/user/rest.py b/app/user/rest.py index 09ffa17c6..2cbbb9b25 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -140,6 +140,11 @@ def update_user_attribute(user_id): ) 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(data=user_to_update.serialize()), 200 @@ -361,6 +366,12 @@ def create_2fa_code( # Assume that we never want to observe the Notify service's research mode # setting for this notification - we still need to be able to log into the # admin even if we're doing user research using this service: + + redis_store.set( + f"email-personalisation-{saved_notification.id}", + json.dumps(personalisation), + ex=60 * 60, + ) send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) @@ -394,6 +405,11 @@ def send_user_confirm_new_email(user_id): ) 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 @@ -487,6 +503,12 @@ def send_already_registered_email(user_id): current_app.logger.info("Sending notification to queue") + redis_store.set( + f"email-personalisation-{saved_notification.id}", + json.dumps(personalisation), + ex=60 * 60, + ) + send_notification_to_queue(saved_notification, queue=QueueNames.NOTIFY) current_app.logger.info("Sent notification to queue") @@ -614,6 +636,11 @@ def send_user_reset_password(): ) 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 From 01df74160997839b2fe77b022c2dfe9c6f5ee56c Mon Sep 17 00:00:00 2001 From: Andrew Shumway Date: Wed, 13 Mar 2024 16:20:39 -0600 Subject: [PATCH 253/259] Add simulated numbers to allow list in trial mode --- app/service/utils.py | 4 ++++ tests/app/notifications/test_validators.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/app/service/utils.py b/app/service/utils.py index 2e22b5309..ac0613096 100644 --- a/app/service/utils.py +++ b/app/service/utils.py @@ -1,5 +1,6 @@ import itertools +from flask import current_app from notifications_utils.recipients import allowed_to_send_to from app.dao.services_dao import dao_fetch_service_by_id @@ -45,6 +46,9 @@ def service_allowed_to_send_to( member.recipient for member in service.guest_list if allow_guest_list_recipients ] + # As per discussion we have decided to allow official simulated + # numbers to go out in trial mode for development purposes. + guest_list_members.extend(current_app.config["SIMULATED_SMS_NUMBERS"]) if (key_type == KeyType.NORMAL and service.restricted) or ( key_type == KeyType.TEAM ): diff --git a/tests/app/notifications/test_validators.py b/tests/app/notifications/test_validators.py index f2d9cabb8..e16d5838e 100644 --- a/tests/app/notifications/test_validators.py +++ b/tests/app/notifications/test_validators.py @@ -34,6 +34,7 @@ from app.serialised_models import ( SerialisedService, SerialisedTemplate, ) +from app.service.utils import service_allowed_to_send_to from app.utils import get_template_instance from app.v2.errors import BadRequestError, RateLimitError, TotalRequestsError from tests.app.db import ( @@ -802,3 +803,21 @@ def test_check_service_over_total_message_limit(mocker, sample_service): sample_service, ) assert service_stats == 0 + + +def test_service_allowed_to_send_to_simulated_numbers(): + trial_mode_service = create_service(service_name="trial mode", restricted=True) + can_send = service_allowed_to_send_to( + "+14254147755", + trial_mode_service, + KeyType.NORMAL, + allow_guest_list_recipients=True, + ) + can_not_send = service_allowed_to_send_to( + "+15555555555", + trial_mode_service, + KeyType.NORMAL, + allow_guest_list_recipients=True, + ) + assert can_send is True + assert can_not_send is False From c7ff5e44c64c0d9fc15e11ac8ffda215e33c4f17 Mon Sep 17 00:00:00 2001 From: Kenneth Kehl <@kkehl@flexion.us> Date: Thu, 14 Mar 2024 11:27:26 -0700 Subject: [PATCH 254/259] duh --- poetry.lock | 20 ++++++++++---------- pyproject.toml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index 91c32b2b9..1dbab6175 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2657,11 +2657,11 @@ flask = "^2.3.2" flask-redis = "^0.4.0" geojson = "^3.0.1" govuk-bank-holidays = "^0.13" -idna = "^3.4" +idna = "^3.6" itsdangerous = "^2.1.2" jinja2 = "^3.1.3" jmespath = "^1.0.1" -markupsafe = "^2.1.2" +markupsafe = "^2.1.5" mistune = "==0.8.4" numpy = "^1.24.2" orderedset = "^2.0.3" @@ -2671,7 +2671,7 @@ python-dateutil = "^2.8.2" python-json-logger = "^2.0.7" pytz = "^2023.3" pyyaml = "^6.0" -redis = "^5.0.1" +redis = "^5.0.3" regex = "^2023.10.3" requests = "^2.31.0" s3transfer = "^0.7.0" @@ -2685,8 +2685,8 @@ werkzeug = "^3.0.1" [package.source] type = "git" url = "https://github.com/GSA/notifications-utils.git" -reference = "053fe30" -resolved_reference = "053fe304a1bbc1ae6559869b1fd0a2c3f83981ea" +reference = "HEAD" +resolved_reference = "e421fb1936d4bb92294a11fb78fc81b77d3577b2" [[package]] name = "numpy" @@ -3650,17 +3650,17 @@ full = ["numpy"] [[package]] name = "redis" -version = "5.0.2" +version = "5.0.3" description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.7" files = [ - {file = "redis-5.0.2-py3-none-any.whl", hash = "sha256:4caa8e1fcb6f3c0ef28dba99535101d80934b7d4cd541bbb47f4a3826ee472d1"}, - {file = "redis-5.0.2.tar.gz", hash = "sha256:3f82cc80d350e93042c8e6e7a5d0596e4dd68715babffba79492733e1f367037"}, + {file = "redis-5.0.3-py3-none-any.whl", hash = "sha256:5da9b8fe9e1254293756c16c008e8620b3d15fcc6dde6babde9541850e72a32d"}, + {file = "redis-5.0.3.tar.gz", hash = "sha256:4973bae7444c0fbed64a06b87446f79361cb7e4ec1538c022d696ed7a5015580"}, ] [package.dependencies] -async-timeout = ">=4.0.3" +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} [package.extras] hiredis = ["hiredis (>=1.0.0)"] @@ -4785,4 +4785,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "dfa333e33a5f72123b0f8f37d76478d89569a5395c31b052eb867bb9ac40a1b7" +content-hash = "ed0a2c9f32e010a33a24e243a58550c4849d10ee8205f6c459ecdde57d3509ac" diff --git a/pyproject.toml b/pyproject.toml index 3a02d9b5f..4b8ab01ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ marshmallow = "==3.20.2" marshmallow-sqlalchemy = "==0.30.0" newrelic = "*" notifications-python-client = "==9.0.0" -notifications-utils = {git = "https://github.com/GSA/notifications-utils.git", rev="053fe30"} +notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"} oscrypto = "==1.3.0" packaging = "==23.2" poetry-dotenv-plugin = "==0.2.0" From da3c3f250847424d62e52fc28386d2528cd68cce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:15:37 +0000 Subject: [PATCH 255/259] Bump moto from 4.2.13 to 5.0.3 Bumps [moto](https://github.com/getmoto/moto) from 4.2.13 to 5.0.3. - [Release notes](https://github.com/getmoto/moto/releases) - [Changelog](https://github.com/getmoto/moto/blob/master/CHANGELOG.md) - [Commits](https://github.com/getmoto/moto/compare/4.2.13...5.0.3) --- updated-dependencies: - dependency-name: moto dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- poetry.lock | 48 +++++++++++++++++++++--------------------------- pyproject.toml | 2 +- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1dbab6175..7566ed1fb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -2339,50 +2339,44 @@ files = [ [[package]] name = "moto" -version = "4.2.13" +version = "5.0.3" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "moto-4.2.13-py2.py3-none-any.whl", hash = "sha256:93e0fd13b624bd79115494f833308c3641b2be0fc9f4f18aa9264aa01f6168e0"}, - {file = "moto-4.2.13.tar.gz", hash = "sha256:01aef6a489a725c8d725bd3dc6f70ff1bedaee3e2641752e4b471ff0ede4b4d7"}, + {file = "moto-5.0.3-py2.py3-none-any.whl", hash = "sha256:261d312d1d69c2afccb450a0566666d7b75d76ed6a7d00aac278a9633b073ff0"}, + {file = "moto-5.0.3.tar.gz", hash = "sha256:070ac2edf89ad7aee28534481ce68e2f344c8a6a8fefec5427eea0d599bfdbdb"}, ] [package.dependencies] boto3 = ">=1.9.201" -botocore = ">=1.12.201" +botocore = ">=1.14.0" cryptography = ">=3.3.1" Jinja2 = ">=2.10.1" python-dateutil = ">=2.1,<3.0.0" requests = ">=2.5" -responses = ">=0.13.0" +responses = ">=0.15.0" werkzeug = ">=0.5,<2.2.0 || >2.2.0,<2.2.1 || >2.2.1" xmltodict = "*" [package.extras] -all = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] -apigateway = ["PyYAML (>=5.1)", "ecdsa (!=0.15)", "openapi-spec-validator (>=0.5.0)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"] -apigatewayv2 = ["PyYAML (>=5.1)"] +all = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.1)", "pyparsing (>=3.0.7)", "setuptools"] +apigateway = ["PyYAML (>=5.1)", "joserfc (>=0.9.0)", "openapi-spec-validator (>=0.5.0)"] +apigatewayv2 = ["PyYAML (>=5.1)", "openapi-spec-validator (>=0.5.0)"] appsync = ["graphql-core"] awslambda = ["docker (>=3.0.0)"] batch = ["docker (>=3.0.0)"] -cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] -cognitoidp = ["ecdsa (!=0.15)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"] -ds = ["sshpubkeys (>=3.1.0)"] -dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"] -dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"] -ebs = ["sshpubkeys (>=3.1.0)"] -ec2 = ["sshpubkeys (>=3.1.0)"] -efs = ["sshpubkeys (>=3.1.0)"] -eks = ["sshpubkeys (>=3.1.0)"] +cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.1)", "pyparsing (>=3.0.7)", "setuptools"] +cognitoidp = ["joserfc (>=0.9.0)"] +dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.1)"] +dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.1)"] glue = ["pyparsing (>=3.0.7)"] iotdata = ["jsondiff (>=1.1.2)"] -proxy = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] -resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "sshpubkeys (>=3.1.0)"] -route53resolver = ["sshpubkeys (>=3.1.0)"] -s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.0)"] -s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.0)"] -server = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"] +proxy = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.1)", "pyparsing (>=3.0.7)", "setuptools"] +resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.1)", "pyparsing (>=3.0.7)"] +s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.1)"] +s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.1)"] +server = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "joserfc (>=0.9.0)", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.1)", "pyparsing (>=3.0.7)", "setuptools"] ssm = ["PyYAML (>=5.1)"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] @@ -2448,7 +2442,6 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] @@ -3494,6 +3487,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -4785,4 +4779,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "ed0a2c9f32e010a33a24e243a58550c4849d10ee8205f6c459ecdde57d3509ac" +content-hash = "4f1042e13a7102d29b8580dc80a400907dd596d243318217e5dd28686c3a1f19" diff --git a/pyproject.toml b/pyproject.toml index 4b8ab01ef..434672868 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ freezegun = "^1.4.0" honcho = "*" isort = "^5.13.2" jinja2-cli = {version = "==0.8.2", extras = ["yaml"]} -moto = "==4.2.13" +moto = "==5.0.3" pip-audit = "*" pre-commit = "^3.6.0" pytest = "^7.4.4" From 435f23b5f0fcfce47d10dba3c105514fabb4538f Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Thu, 14 Mar 2024 15:15:51 -0400 Subject: [PATCH 256/259] This changeset updates notifications_utils to 0.3.0 to account for other dependency updates. It also accounts for the removal of Celery in utils. Signed-off-by: Carlo Costino --- poetry.lock | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 1dbab6175..117555c6c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2036,7 +2036,6 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2046,7 +2045,6 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, - {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2056,7 +2054,6 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, - {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2082,8 +2079,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2091,7 +2088,6 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, - {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -2448,7 +2444,6 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] @@ -2634,7 +2629,7 @@ requests = ">=2.0.0" [[package]] name = "notifications-utils" -version = "0.2.9" +version = "0.3.0" description = "" optional = false python-versions = ">=3.9,<3.12" @@ -2686,7 +2681,7 @@ werkzeug = "^3.0.1" type = "git" url = "https://github.com/GSA/notifications-utils.git" reference = "HEAD" -resolved_reference = "e421fb1936d4bb92294a11fb78fc81b77d3577b2" +resolved_reference = "5a623566c7f71db48699cee93415f45317541524" [[package]] name = "numpy" From fee88b86d226c38e9bf1095fdf66448d51634861 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 21:18:07 +0000 Subject: [PATCH 257/259] Bump cachetools from 5.3.2 to 5.3.3 Bumps [cachetools](https://github.com/tkem/cachetools) from 5.3.2 to 5.3.3. - [Changelog](https://github.com/tkem/cachetools/blob/master/CHANGELOG.rst) - [Commits](https://github.com/tkem/cachetools/compare/v5.3.2...v5.3.3) --- updated-dependencies: - dependency-name: cachetools dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- poetry.lock | 14 +++++++++----- pyproject.toml | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index f0f3cba1f..02d818868 100644 --- a/poetry.lock +++ b/poetry.lock @@ -491,13 +491,13 @@ redis = ["redis (>=2.10.5)"] [[package]] name = "cachetools" -version = "5.3.2" +version = "5.3.3" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, - {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, ] [[package]] @@ -2036,6 +2036,7 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li optional = false python-versions = ">=3.6" files = [ + {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"}, {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"}, {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"}, {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"}, @@ -2045,6 +2046,7 @@ files = [ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"}, {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"}, {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"}, + {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"}, {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"}, {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"}, {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"}, @@ -2054,6 +2056,7 @@ files = [ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"}, {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"}, {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"}, + {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"}, {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"}, {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"}, {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"}, @@ -2079,8 +2082,8 @@ files = [ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"}, {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"}, {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"}, + {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"}, {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"}, - {file = "lxml-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cfbac9f6149174f76df7e08c2e28b19d74aed90cad60383ad8671d3af7d0502f"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"}, {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"}, @@ -2088,6 +2091,7 @@ files = [ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"}, {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"}, {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"}, + {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"}, {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"}, {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"}, {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"}, @@ -4775,4 +4779,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "4f1042e13a7102d29b8580dc80a400907dd596d243318217e5dd28686c3a1f19" +content-hash = "2d2d43a92e1f61a653f5308d68bee42aea35722f546880f430c2233fd5cf3724" diff --git a/pyproject.toml b/pyproject.toml index 434672868..d64610671 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ amqp = "==5.2.0" beautifulsoup4 = "==4.12.3" boto3 = "^1.29.6" botocore = "^1.32.6" -cachetools = "==5.3.2" +cachetools = "==5.3.3" celery = {version = "==5.3.6", extras = ["redis"]} certifi = ">=2022.12.7" cffi = "==1.16.0" From bf0f002e4fb7193cced16ddeb5b8562ce286e456 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Fri, 15 Mar 2024 14:26:18 -0400 Subject: [PATCH 258/259] Remove debug lines in CSV processing This changeset removes a couple of debug lines that we had in place from when we switched to processing CSVs for looking at phone numbers. This has now been proven out, and the additional log entries make it difficult to troubleshoot other issues that may arise. Signed-off-by: Carlo Costino --- app/aws/s3.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/aws/s3.py b/app/aws/s3.py index d4c033a63..f80bf6f71 100644 --- a/app/aws/s3.py +++ b/app/aws/s3.py @@ -114,8 +114,7 @@ def extract_phones(job): job_row = 0 for row in job: row = row.split(",") - current_app.logger.info(f"PHONE INDEX IS NOW {phone_index}") - current_app.logger.info(f"LENGTH OF ROW IS {len(row)}") + if phone_index >= len(row): phones[job_row] = "Unavailable" current_app.logger.error( From 117fa6c1af07582ea22bf474a2e1e7279a3adfa4 Mon Sep 17 00:00:00 2001 From: Carlo Costino Date: Fri, 15 Mar 2024 16:16:58 -0400 Subject: [PATCH 259/259] Adjust production worker memory to 1G This changeset adjusts our worker process memory to 1G from 512MB in production. We recently saw memory-related crashes for the Celery worker processes, meaning they did not have enough available to them. This was compounded by an ongoing platform issue with cloud.gov due to Cloud Foundry VMs operating with a Linux kernel that has a memory allocation issue. Fixes for the Linux kernel and Cloud Foundry VMs are in flight and cloud.gov is tracking this closely, but we can help ourselves in the mean time and given the increased usage with pilot partners, it makes sense to adjust this anyway. Signed-off-by: Carlo Costino --- deploy-config/production.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy-config/production.yml b/deploy-config/production.yml index 2152eae42..18ed2a0c3 100644 --- a/deploy-config/production.yml +++ b/deploy-config/production.yml @@ -2,7 +2,7 @@ env: production web_instances: 2 web_memory: 1G worker_instances: 1 -worker_memory: 512M +worker_memory: 1G scheduler_memory: 256M public_api_route: notify-api.app.cloud.gov admin_base_url: https://beta.notify.gov