diff --git a/app/__init__.py b/app/__init__.py index 43a0dbad4d..c3cad34297 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -30,7 +30,6 @@ from werkzeug.local import LocalProxy from app.clients import NotificationProviderClients -from app.clients.cbc_proxy import CBCProxyClient from app.clients.document_download import DocumentDownloadClient from app.clients.email.aws_ses import AwsSesClient from app.clients.email.aws_ses_stub import AwsSesStubClient @@ -51,7 +50,6 @@ zendesk_client = ZendeskClient() statsd_client = StatsdClient() redis_store = RedisClient() -cbc_proxy_client = CBCProxyClient() document_download_client = DocumentDownloadClient() metrics = GDSMetrics() @@ -104,8 +102,6 @@ def create_app(application): redis_store.init_app(application) document_download_client.init_app(application) - cbc_proxy_client.init_app(application) - register_blueprint(application) register_v2_blueprints(application) diff --git a/app/celery/nightly_tasks.py b/app/celery/nightly_tasks.py index 81ee5399d2..80fd7e0ca4 100644 --- a/app/celery/nightly_tasks.py +++ b/app/celery/nightly_tasks.py @@ -367,10 +367,8 @@ def delete_unneeded_notification_history_by_hour(): # We pass datetimes as args to the next task but celery will actually call `isoformat` on these # and send them over as strings [start_datetime, end_datetime], - # We use the broadcasts queue temporarily as it pulled from by a worker doing no work - # We don't want to put the tasks on the periodic queue because they make take up all - # the workers capacity, stopping other important tasks from happening - queue=QueueNames.BROADCASTS, + # We use the reporting queue as it's not used for most of the day + queue=QueueNames.REPORTING, ) current_app.logger.info( "Created delete_unneeded_notification_history_for_specific_hour task between %s and %s", diff --git a/app/clients/cbc_proxy.py b/app/clients/cbc_proxy.py deleted file mode 100644 index 2f5c47bce7..0000000000 --- a/app/clients/cbc_proxy.py +++ /dev/null @@ -1,298 +0,0 @@ -import json -import uuid -from abc import ABC, abstractmethod - -import boto3 -import botocore -from flask import current_app -from notifications_utils.template import non_gsm_characters -from sqlalchemy.schema import Sequence - -from app.config import BroadcastProvider -from app.utils import DATETIME_FORMAT, format_sequential_number - -# The variable names in this file have specific meaning in a CAP message -# -# identifier is a unique field for each CAP message -# -# headline is a field which we are not sure if we will use -# -# description is the body of the message - -# areas is a list of dicts, with the following items -# * description is a string which populates the areaDesc field -# * polygon is a list of lat/long pairs -# -# previous_provider_messages is a list of previous events (models.py::BroadcastProviderMessage) -# ie a Cancel message would have a unique event but have the event of -# the preceeding Alert message in the previous_provider_messages field - - -class CBCProxyRetryableException(Exception): - pass - - -class CBCProxyClient: - _lambda_client = None - - def init_app(self, app): - if app.config.get("CBC_PROXY_ENABLED"): - self._lambda_client = boto3.client( - "lambda", - region_name="eu-west-2", - aws_access_key_id=app.config["CBC_PROXY_AWS_ACCESS_KEY_ID"], - aws_secret_access_key=app.config["CBC_PROXY_AWS_SECRET_ACCESS_KEY"], - ) - - def get_proxy(self, provider): - proxy_classes = { - BroadcastProvider.EE: CBCProxyEE, - BroadcastProvider.THREE: CBCProxyThree, - BroadcastProvider.O2: CBCProxyO2, - BroadcastProvider.VODAFONE: CBCProxyVodafone, - } - return proxy_classes[provider](self._lambda_client) - - -class CBCProxyClientBase(ABC): - @property - @abstractmethod - def lambda_name(self): - pass - - @property - @abstractmethod - def failover_lambda_name(self): - pass - - @property - @abstractmethod - def LANGUAGE_ENGLISH(self): - pass - - @property - @abstractmethod - def LANGUAGE_WELSH(self): - pass - - def __init__(self, lambda_client): - self._lambda_client = lambda_client - - def send_link_test(self): - self._send_link_test(self.lambda_name) - self._send_link_test(self.failover_lambda_name) - - @abstractmethod - def _send_link_test( - self, - lambda_name, - ): - pass - - @abstractmethod - def create_and_send_broadcast( - self, identifier, headline, description, areas, sent, expires, channel, message_number=None - ): - pass - - # We have not implemented updating a broadcast - def update_and_send_broadcast( # noqa: B027 - self, - identifier, - previous_provider_messages, - headline, - description, - areas, - sent, - expires, - channel, - message_number=None, - ): - pass - - @abstractmethod - def cancel_broadcast( - self, identifier, previous_provider_messages, headline, description, areas, sent, expires, message_number=None - ): - pass - - def _invoke_lambda_with_failover(self, payload): - result = self._invoke_lambda(self.lambda_name, payload) - - if not result: - failover_result = self._invoke_lambda(self.failover_lambda_name, payload) - if not failover_result: - raise CBCProxyRetryableException( - f"Lambda failed for both {self.lambda_name} and {self.failover_lambda_name}" - ) - - return result - - def _invoke_lambda(self, lambda_name, payload): - payload_bytes = bytes(json.dumps(payload), encoding="utf8") - try: - current_app.logger.info("Calling lambda %s with payload %s", lambda_name, str(payload)[:1000]) - - result = self._lambda_client.invoke( - FunctionName=lambda_name, - InvocationType="RequestResponse", - Payload=payload_bytes, - ) - except botocore.exceptions.ClientError: - current_app.logger.exception("Boto ClientError calling lambda %s", lambda_name) - success = False - return success - - if result["StatusCode"] > 299: - current_app.logger.info( - "Error calling lambda %s with status code %s, %s", - lambda_name, - result["StatusCode"], - result.get("Payload"), - ) - success = False - - elif "FunctionError" in result: - current_app.logger.info( - "Error calling lambda %s with function error %s", lambda_name, result["Payload"].read() - ) - success = False - - else: - success = True - - return success - - def infer_language_from(self, content): - if non_gsm_characters(content): - return self.LANGUAGE_WELSH - return self.LANGUAGE_ENGLISH - - -class CBCProxyOne2ManyClient(CBCProxyClientBase): - LANGUAGE_ENGLISH = "en-GB" - LANGUAGE_WELSH = "cy-GB" - - def _send_link_test( - self, - lambda_name, - ): - """ - link test - open up a connection to a specific provider, and send them an xml payload with a of - test. - """ - payload = {"message_type": "test", "identifier": str(uuid.uuid4()), "message_format": "cap"} - - self._invoke_lambda(lambda_name=lambda_name, payload=payload) - - def create_and_send_broadcast( - self, identifier, headline, description, areas, sent, expires, channel, message_number=None - ): - payload = { - "message_type": "alert", - "identifier": identifier, - "message_format": "cap", - "headline": headline, - "description": description, - "areas": areas, - "sent": sent, - "expires": expires, - "language": self.infer_language_from(description), - "channel": channel, - } - self._invoke_lambda_with_failover(payload=payload) - - def cancel_broadcast(self, identifier, previous_provider_messages, sent, message_number=None): - payload = { - "message_type": "cancel", - "identifier": identifier, - "message_format": "cap", - "references": [ - {"message_id": str(message.id), "sent": message.created_at.strftime(DATETIME_FORMAT)} - for message in previous_provider_messages - ], - "sent": sent, - } - self._invoke_lambda_with_failover(payload=payload) - - -class CBCProxyEE(CBCProxyOne2ManyClient): - lambda_name = "ee-1-proxy" - failover_lambda_name = "ee-2-proxy" - - -class CBCProxyThree(CBCProxyOne2ManyClient): - lambda_name = "three-1-proxy" - failover_lambda_name = "three-2-proxy" - - -class CBCProxyO2(CBCProxyOne2ManyClient): - lambda_name = "o2-1-proxy" - failover_lambda_name = "o2-2-proxy" - - -class CBCProxyVodafone(CBCProxyClientBase): - lambda_name = "vodafone-1-proxy" - failover_lambda_name = "vodafone-2-proxy" - - LANGUAGE_ENGLISH = "English" - LANGUAGE_WELSH = "Welsh" - - def _send_link_test( - self, - lambda_name, - ): - """ - link test - open up a connection to a specific provider, and send them an xml payload with a of - test. - """ - from app import db - - sequence = Sequence("broadcast_provider_message_number_seq") - sequential_number = db.session.connection().execute(sequence) - formatted_seq_number = format_sequential_number(sequential_number) - - payload = { - "message_type": "test", - "identifier": str(uuid.uuid4()), - "message_number": formatted_seq_number, - "message_format": "ibag", - } - - self._invoke_lambda(lambda_name=lambda_name, payload=payload) - - def create_and_send_broadcast( - self, identifier, message_number, headline, description, areas, sent, expires, channel - ): - payload = { - "message_type": "alert", - "identifier": identifier, - "message_number": message_number, - "message_format": "ibag", - "headline": headline, - "description": description, - "areas": areas, - "sent": sent, - "expires": expires, - "language": self.infer_language_from(description), - "channel": channel, - } - self._invoke_lambda_with_failover(payload=payload) - - def cancel_broadcast(self, identifier, previous_provider_messages, sent, message_number): - payload = { - "message_type": "cancel", - "identifier": identifier, - "message_number": message_number, - "message_format": "ibag", - "references": [ - { - "message_id": str(message.id), - "message_number": format_sequential_number(message.message_number), - "sent": message.created_at.strftime(DATETIME_FORMAT), - } - for message in previous_provider_messages - ], - "sent": sent, - } - self._invoke_lambda_with_failover(payload=payload) diff --git a/app/commands.py b/app/commands.py index eaeb6e9218..5de515e033 100644 --- a/app/commands.py +++ b/app/commands.py @@ -45,7 +45,6 @@ dao_get_organisation_by_email_address, dao_get_organisation_by_id, ) -from app.dao.permissions_dao import permission_dao from app.dao.services_dao import ( dao_create_service, dao_fetch_all_services_by_user, @@ -66,7 +65,6 @@ LetterBranding, Notification, Organisation, - Permission, Service, Template, User, @@ -789,35 +787,6 @@ def populate_annual_billing_with_the_previous_years_allowance(year): ) -@click.option("-u", "--user-id", required=True) -@notify_command(name="local-dev-broadcast-permissions") -def local_dev_broadcast_permissions(user_id): - if os.getenv("NOTIFY_ENVIRONMENT", "") not in ["development", "test"]: - current_app.logger.error("Can only be run in development") - return - - user = User.query.filter_by(id=user_id).one() - - user_broadcast_services = Service.query.filter( - Service.permissions.any(permission="broadcast"), Service.users.any(id=user_id) - ) - - for service in user_broadcast_services: - permission_list = [ - Permission(service_id=service.id, user_id=user_id, permission=permission) - for permission in [ - "reject_broadcasts", - "cancel_broadcasts", # required to create / approve - "create_broadcasts", - "approve_broadcasts", # minimum for testing - "manage_templates", # unlikely but might be useful - "view_activity", # normally added on invite / service creation - ] - ] - - permission_dao.set_user_service_permission(user, service, permission_list, _commit=True, replace=True) - - @click.option("-u", "--user-id", required=True) @notify_command(name="generate-bulktest-data") def generate_bulktest_data(user_id): diff --git a/app/config.py b/app/config.py index 77ea552375..7318278db9 100644 --- a/app/config.py +++ b/app/config.py @@ -27,8 +27,6 @@ class QueueNames(object): SANITISE_LETTERS = "sanitise-letter-tasks" SAVE_API_EMAIL = "save-api-email-tasks" SAVE_API_SMS = "save-api-sms-tasks" - BROADCASTS = "broadcast-tasks" - GOVUK_ALERTS = "govuk-alerts" @staticmethod def all_queues(): @@ -51,26 +49,15 @@ def all_queues(): QueueNames.SMS_CALLBACKS, QueueNames.SAVE_API_EMAIL, QueueNames.SAVE_API_SMS, - QueueNames.BROADCASTS, ] -class BroadcastProvider: - EE = "ee" - VODAFONE = "vodafone" - THREE = "three" - O2 = "o2" - - PROVIDERS = [EE, VODAFONE, THREE, O2] - - class TaskNames(object): PROCESS_INCOMPLETE_JOBS = "process-incomplete-jobs" ZIP_AND_SEND_LETTER_PDFS = "zip-and-send-letter-pdfs" SCAN_FILE = "scan-file" SANITISE_LETTER = "sanitise-and-upload-letter" CREATE_PDF_FOR_TEMPLATED_LETTER = "create-pdf-for-templated-letter" - PUBLISH_GOVUK_ALERTS = "publish-govuk-alerts" RECREATE_PDF_FOR_PRECOMPILED_LETTER = "recreate-pdf-for-precompiled-letter" @@ -156,7 +143,6 @@ class Config(object): NOTIFY_SERVICE_ID = "d6aa2c68-a2d9-4437-ab19-3ae8eb202553" NOTIFY_USER_ID = "6af522d0-2915-4e52-83a3-3690455a5fe6" INVITATION_EMAIL_TEMPLATE_ID = "4f46df42-f795-4cc4-83bb-65ca312f49cc" - BROADCAST_INVITATION_EMAIL_TEMPLATE_ID = "46152f7c-6901-41d5-8590-a5624d0d4359" SMS_CODE_TEMPLATE_ID = "36fb0730-6259-4da1-8a80-c8de22ad4246" EMAIL_2FA_TEMPLATE_ID = "299726d2-dba6-42b8-8209-30e1d66ea164" NEW_USER_EMAIL_VERIFICATION_TEMPLATE_ID = "ece42649-22a8-4d06-b87f-d52d5d3f0a27" @@ -417,15 +403,6 @@ class Config(object): AWS_REGION = "eu-west-1" - CBC_PROXY_ENABLED = True - CBC_PROXY_AWS_ACCESS_KEY_ID = os.environ.get("CBC_PROXY_AWS_ACCESS_KEY_ID", "") - CBC_PROXY_AWS_SECRET_ACCESS_KEY = os.environ.get("CBC_PROXY_AWS_SECRET_ACCESS_KEY", "") - - ENABLED_CBCS = {BroadcastProvider.EE, BroadcastProvider.THREE, BroadcastProvider.O2, BroadcastProvider.VODAFONE} - - # as defined in api db migration 0331_add_broadcast_org.py - BROADCAST_ORGANISATION_ID = "38e4bf69-93b0-445d-acee-53ea53fe02df" - DVLA_API_BASE_URL = os.environ.get("DVLA_API_BASE_URL", "https://uat.driver-vehicle-licensing.api.gov.uk") DVLA_API_TLS_CIPHERS = os.environ.get("DVLA_API_TLS_CIPHERS") @@ -488,8 +465,6 @@ class Development(Config): API_RATE_LIMIT_ENABLED = True DVLA_EMAIL_ADDRESSES = ["success@simulator.amazonses.com"] - CBC_PROXY_ENABLED = False - class Test(Development): NOTIFY_EMAIL_DOMAIN = "test.notify.com" @@ -533,7 +508,6 @@ class Test(Development): MMG_URL = "https://example.com/mmg" FIRETEXT_URL = "https://example.com/firetext" - CBC_PROXY_ENABLED = True DVLA_EMAIL_ADDRESSES = ["success@simulator.amazonses.com", "success+2@simulator.amazonses.com"] DVLA_API_BASE_URL = "https://test-dvla-api.com" diff --git a/app/constants.py b/app/constants.py index af208cf6e8..35952c402f 100644 --- a/app/constants.py +++ b/app/constants.py @@ -122,9 +122,8 @@ SMS_TYPE = "sms" EMAIL_TYPE = "email" LETTER_TYPE = "letter" -BROADCAST_TYPE = "broadcast" -TEMPLATE_TYPES = [SMS_TYPE, EMAIL_TYPE, LETTER_TYPE, BROADCAST_TYPE] -NOTIFICATION_TYPES = [SMS_TYPE, EMAIL_TYPE, LETTER_TYPE] # not broadcast +TEMPLATE_TYPES = [SMS_TYPE, EMAIL_TYPE, LETTER_TYPE] +NOTIFICATION_TYPES = [SMS_TYPE, EMAIL_TYPE, LETTER_TYPE] NOTIFICATION_TYPE = [EMAIL_TYPE, SMS_TYPE, LETTER_TYPE] # duplicate that can probably be cleaned up @@ -168,10 +167,6 @@ class LetterLanguageOptions(str, enum.Enum): MANAGE_API_KEYS = "manage_api_keys" PLATFORM_ADMIN = "platform_admin" VIEW_ACTIVITY = "view_activity" -CREATE_BROADCASTS = "create_broadcasts" -APPROVE_BROADCASTS = "approve_broadcasts" -CANCEL_BROADCASTS = "cancel_broadcasts" -REJECT_BROADCASTS = "reject_broadcasts" INTERNATIONAL_SMS_TYPE = "international_sms" INBOUND_SMS_TYPE = "inbound_sms" SCHEDULE_NOTIFICATIONS = "schedule_notifications" @@ -188,7 +183,6 @@ class LetterLanguageOptions(str, enum.Enum): EMAIL_TYPE, SMS_TYPE, LETTER_TYPE, - BROADCAST_TYPE, INTERNATIONAL_SMS_TYPE, INBOUND_SMS_TYPE, SCHEDULE_NOTIFICATIONS, @@ -213,10 +207,6 @@ class LetterLanguageOptions(str, enum.Enum): MANAGE_API_KEYS, PLATFORM_ADMIN, VIEW_ACTIVITY, - CREATE_BROADCASTS, - APPROVE_BROADCASTS, - CANCEL_BROADCASTS, - REJECT_BROADCASTS, ] @@ -268,7 +258,6 @@ class OrganisationUserPermissionTypes(enum.Enum): SMS_PROVIDERS = [MMG_PROVIDER, FIRETEXT_PROVIDER] EMAIL_PROVIDERS = [SES_PROVIDER] PROVIDERS = SMS_PROVIDERS + EMAIL_PROVIDERS -ALL_BROADCAST_PROVIDERS = "all" # Jobs JOB_STATUS_PENDING = "pending" diff --git a/app/dao/templates_dao.py b/app/dao/templates_dao.py index acfa1a2b95..ece9d2ae58 100644 --- a/app/dao/templates_dao.py +++ b/app/dao/templates_dao.py @@ -64,7 +64,6 @@ def dao_update_template_reply_to(template_id, reply_to): "archived": template.archived, "process_type": template.process_type, "service_letter_contact_id": template.service_letter_contact_id, - "broadcast_data": template.broadcast_data, } ) db.session.add(history) diff --git a/app/models.py b/app/models.py index 3739453cad..105c27d495 100644 --- a/app/models.py +++ b/app/models.py @@ -36,13 +36,10 @@ from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm.collections import attribute_mapped_collection -from sqlalchemy.schema import Sequence from app import db, encryption from app.constants import ( - ALL_BROADCAST_PROVIDERS, BRANDING_ORG, - BROADCAST_TYPE, EMAIL_TYPE, GUEST_LIST_RECIPIENT_TYPE, INVITE_PENDING, @@ -131,11 +128,7 @@ def can_use_webauthn(self): if self.auth_type == "webauthn_auth": return True - return any( - str(service.organisation_id) == current_app.config["BROADCAST_ORGANISATION_ID"] - or str(service.id) == current_app.config["NOTIFY_SERVICE_ID"] - for service in self.services - ) + return any(str(service.id) == current_app.config["NOTIFY_SERVICE_ID"] for service in self.services) @password.setter def password(self, password): @@ -570,9 +563,6 @@ class Service(db.Model, Versioned): backref=db.backref("services", lazy="dynamic"), ) - allowed_broadcast_provider = association_proxy("service_broadcast_settings", "provider") - broadcast_channel = association_proxy("service_broadcast_settings", "channel") - @hybrid_property # a hybrid_property enables us to still use it in queries def name(self): return self._name @@ -652,13 +642,6 @@ def serialize_for_org_dashboard(self): "restricted": self.restricted, } - def get_available_broadcast_providers(self): - # There may be future checks here if we add, for example, platform admin level provider killswitches. - if self.allowed_broadcast_provider != ALL_BROADCAST_PROVIDERS: - return [x for x in current_app.config["ENABLED_CBCS"] if x == self.allowed_broadcast_provider] - else: - return current_app.config["ENABLED_CBCS"] - class DefaultAnnualAllowance(db.Model): """This table represents default allowances that organisations will get for free notifications. @@ -1032,7 +1015,6 @@ def __init__(self, **kwargs): hidden = db.Column(db.Boolean, nullable=False, default=False) subject = db.Column(db.Text) postage = db.Column(db.String, nullable=True) - broadcast_data = db.Column(JSONB(none_as_null=True), nullable=True) letter_welsh_content = db.Column(db.Text) letter_welsh_subject = db.Column(db.Text) @@ -1131,8 +1113,6 @@ def _as_utils_template(self): return PlainTextEmailTemplate(self.__dict__) if self.template_type == SMS_TYPE: return SMSMessageTemplate(self.__dict__) - if self.template_type == BROADCAST_TYPE: - return SMSMessageTemplate(self.__dict__ | {"template_type": "sms"}) if self.template_type == LETTER_TYPE: return LetterPrintTemplate( self.__dict__, @@ -2129,353 +2109,6 @@ def serialize(self): return contact_list -class BroadcastStatusType(db.Model): - __tablename__ = "broadcast_status_type" - DRAFT = "draft" - PENDING_APPROVAL = "pending-approval" - REJECTED = "rejected" - BROADCASTING = "broadcasting" - COMPLETED = "completed" - CANCELLED = "cancelled" - TECHNICAL_FAILURE = "technical-failure" - - STATUSES = [DRAFT, PENDING_APPROVAL, REJECTED, BROADCASTING, COMPLETED, CANCELLED, TECHNICAL_FAILURE] - - # a broadcast message can be edited while in one of these states - PRE_BROADCAST_STATUSES = [DRAFT, PENDING_APPROVAL, REJECTED] - LIVE_STATUSES = [BROADCASTING, COMPLETED, CANCELLED] - - # these are only the transitions we expect to administer via the API code. - ALLOWED_STATUS_TRANSITIONS = { - DRAFT: {PENDING_APPROVAL}, - PENDING_APPROVAL: {REJECTED, DRAFT, BROADCASTING}, - REJECTED: {DRAFT, PENDING_APPROVAL}, - BROADCASTING: {COMPLETED, CANCELLED}, - COMPLETED: {}, - CANCELLED: {}, - TECHNICAL_FAILURE: {}, - } - - name = db.Column(db.String, primary_key=True) - - -class BroadcastMessage(db.Model): - """ - This is for creating a message, viewing it in notify, adding areas, approvals, drafts, etc. Notify logic before - hitting send. - """ - - __tablename__ = "broadcast_message" - __table_args__ = ( - db.ForeignKeyConstraint( - ["template_id", "template_version"], - ["templates_history.id", "templates_history.version"], - ), - {}, - ) - - 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")) - service = db.relationship("Service", backref="broadcast_messages") - - template_id = db.Column(UUID(as_uuid=True), nullable=True) - template_version = db.Column(db.Integer, nullable=True) - template = db.relationship("TemplateHistory", backref="broadcast_messages") - - _personalisation = db.Column(db.String, nullable=True) - content = db.Column(db.Text, nullable=False) - # defaults to empty list - areas = db.Column(JSONB(none_as_null=True), nullable=False, default=list) - - status = db.Column( - db.String, db.ForeignKey("broadcast_status_type.name"), nullable=False, default=BroadcastStatusType.DRAFT - ) - - # these times are related to the actual broadcast, rather than auditing purposes - starts_at = db.Column(db.DateTime, nullable=True) - finishes_at = db.Column(db.DateTime, nullable=True) # isn't updated if user cancels - - # these times correspond to when - created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) - approved_at = db.Column(db.DateTime, nullable=True) - cancelled_at = db.Column(db.DateTime, nullable=True) - updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow) - - created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=True) - approved_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=True) - cancelled_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey("users.id"), nullable=True) - - created_by = db.relationship("User", foreign_keys=[created_by_id]) - approved_by = db.relationship("User", foreign_keys=[approved_by_id]) - cancelled_by = db.relationship("User", foreign_keys=[cancelled_by_id]) - - created_by_api_key_id = db.Column(UUID(as_uuid=True), db.ForeignKey("api_keys.id"), nullable=True) - cancelled_by_api_key_id = db.Column(UUID(as_uuid=True), db.ForeignKey("api_keys.id"), nullable=True) - created_by_api_key = db.relationship("ApiKey", foreign_keys=[created_by_api_key_id]) - cancelled_by_api_key = db.relationship("ApiKey", foreign_keys=[cancelled_by_api_key_id]) - - reference = db.Column(db.String(255), nullable=True) - cap_event = db.Column(db.String(255), nullable=True) - - stubbed = db.Column(db.Boolean, nullable=False) - - CheckConstraint("created_by_id is not null or created_by_api_key_id is not null") - - @property - def personalisation(self): - if self._personalisation: - return encryption.decrypt(self._personalisation) - return {} - - @personalisation.setter - def personalisation(self, personalisation): - self._personalisation = encryption.encrypt(personalisation or {}) - - def serialize(self): - return { - "id": str(self.id), - "reference": self.reference, - "cap_event": self.cap_event, - "service_id": str(self.service_id), - "template_id": str(self.template_id) if self.template else None, - "template_version": self.template_version, - "template_name": self.template.name if self.template else None, - "personalisation": self.personalisation if self.template else None, - "content": self.content, - "areas": self.areas, - "status": self.status, - "starts_at": get_dt_string_or_none(self.starts_at), - "finishes_at": get_dt_string_or_none(self.finishes_at), - "created_at": get_dt_string_or_none(self.created_at), - "approved_at": get_dt_string_or_none(self.approved_at), - "cancelled_at": get_dt_string_or_none(self.cancelled_at), - "updated_at": get_dt_string_or_none(self.updated_at), - "created_by_id": get_uuid_string_or_none(self.created_by_id), - "approved_by_id": get_uuid_string_or_none(self.approved_by_id), - "cancelled_by_id": get_uuid_string_or_none(self.cancelled_by_id), - } - - -class BroadcastEventMessageType: - ALERT = "alert" - UPDATE = "update" - CANCEL = "cancel" - - MESSAGE_TYPES = [ALERT, UPDATE, CANCEL] - - -class BroadcastEvent(db.Model): - """ - This table represents an instruction that we will send to the broadcast providers. It directly correlates with an - instruction from the admin - to broadcast a message, to cancel an existing message, or to update an existing one. - - We should be able to create the complete CAP message without joining from this to any other tables, eg - template, service, or broadcast_message. - - The only exception to this is that we will have to join to itself to find other broadcast_events with the - same broadcast_message_id when building up the `` xml field for updating/cancelling an existing message. - - As such, this shouldn't have foreign keys to things that can change or be deleted. - """ - - __tablename__ = "broadcast_event" - - 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")) - service = db.relationship("Service") - - broadcast_message_id = db.Column(UUID(as_uuid=True), db.ForeignKey("broadcast_message.id"), nullable=False) - broadcast_message = db.relationship("BroadcastMessage", backref="events") - - # this is used for in the cap xml - sent_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) - - # msgType. alert, cancel, or update. (other options in the spec are "ack" and "error") - message_type = db.Column(db.String, nullable=False) - - # this will be json containing anything that isnt hardcoded in utils/cbc proxy. for now just body but may grow to - # include, eg, title, headline, instructions. - transmitted_content = db.Column(JSONB(none_as_null=True), nullable=True) - # unsubstantiated reckon: even if we're sending a cancel, we'll still need to provide areas - transmitted_areas = db.Column(JSONB(none_as_null=True), nullable=False, default=list) - transmitted_sender = db.Column(db.String(), nullable=False) - - # we may only need this starts_at if this is scheduled for the future. Interested to see how this affects - # updates/cancels (ie: can you schedule an update for the future?) - transmitted_starts_at = db.Column(db.DateTime, nullable=True) - transmitted_finishes_at = db.Column(db.DateTime, nullable=True) - - @property - def reference(self): - notify_email_domain = current_app.config["NOTIFY_EMAIL_DOMAIN"] - return f"https://www.{notify_email_domain}/,{self.id},{self.sent_at_as_cap_datetime_string}" - - @property - def sent_at_as_cap_datetime_string(self): - return self.formatted_datetime_for("sent_at") - - @property - def transmitted_finishes_at_as_cap_datetime_string(self): - return self.formatted_datetime_for("transmitted_finishes_at") - - def formatted_datetime_for(self, property_name): - return self.convert_naive_utc_datetime_to_cap_standard_string(getattr(self, property_name)) - - @staticmethod - def convert_naive_utc_datetime_to_cap_standard_string(dt): - """ - As defined in section 3.3.2 of - http://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html - They define the standard "YYYY-MM-DDThh:mm:ssXzh:zm", where X is - `+` if the timezone is > UTC, otherwise `-` - """ - return f"{dt.strftime('%Y-%m-%dT%H:%M:%S')}-00:00" - - def get_provider_message(self, provider): - return next( - (provider_message for provider_message in self.provider_messages if provider_message.provider == provider), - None, - ) - - def serialize(self): - return { - "id": str(self.id), - "service_id": str(self.service_id), - "broadcast_message_id": str(self.broadcast_message_id), - # sent_at is required by BroadcastMessageTemplate.from_broadcast_event - "sent_at": self.sent_at.strftime(DATETIME_FORMAT), - "message_type": self.message_type, - "transmitted_content": self.transmitted_content, - "transmitted_areas": self.transmitted_areas, - "transmitted_sender": self.transmitted_sender, - "transmitted_starts_at": get_dt_string_or_none(self.transmitted_starts_at), - # transmitted_finishes_at is required by BroadcastMessageTemplate.from_broadcast_event - "transmitted_finishes_at": self.transmitted_finishes_at.strftime(DATETIME_FORMAT), - } - - -class BroadcastProvider: - EE = "ee" - VODAFONE = "vodafone" - THREE = "three" - O2 = "o2" - - PROVIDERS = [EE, VODAFONE, THREE, O2] - - -class BroadcastProviderMessageStatus: - TECHNICAL_FAILURE = "technical-failure" # Couldn’t send (cbc proxy 5xx/4xx) - SENDING = "sending" # Sent to cbc, awaiting response - ACK = "returned-ack" # Received ack response - ERR = "returned-error" # Received error response - - STATES = [TECHNICAL_FAILURE, SENDING, ACK, ERR] - - -class BroadcastProviderMessage(db.Model): - """ - A row in this table represents the XML blob sent to a single provider. - """ - - __tablename__ = "broadcast_provider_message" - - id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) - - broadcast_event_id = db.Column(UUID(as_uuid=True), db.ForeignKey("broadcast_event.id")) - broadcast_event = db.relationship("BroadcastEvent", backref="provider_messages") - - # 'ee', 'three', 'vodafone', etc - provider = db.Column(db.String) - - status = db.Column(db.String) - - created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) - updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow) - - UniqueConstraint(broadcast_event_id, provider) - - message_number = association_proxy("broadcast_provider_message_number", "broadcast_provider_message_number") - - -class BroadcastProviderMessageNumber(db.Model): - """ - To send IBAG messages via the CBC proxy to Nokia CBC appliances, Notify must generate and store a numeric - message_number alongside the message ID (GUID). - Subsequent messages (Update, Cancel) in IBAG format must reference the original message_number & message_id. - This model relates broadcast_provider_message_id to that numeric message_number. - """ - - __tablename__ = "broadcast_provider_message_number" - - sequence = Sequence("broadcast_provider_message_number_seq") - broadcast_provider_message_number = db.Column( - db.Integer, sequence, server_default=sequence.next_value(), primary_key=True - ) - broadcast_provider_message_id = db.Column( - UUID(as_uuid=True), db.ForeignKey("broadcast_provider_message.id"), nullable=False - ) - broadcast_provider_message = db.relationship( - "BroadcastProviderMessage", backref=db.backref("broadcast_provider_message_number", uselist=False) - ) - - -class ServiceBroadcastSettings(db.Model): - """ - Every broadcast service should have one and only one row in this table. - """ - - __tablename__ = "service_broadcast_settings" - - service_id = db.Column(UUID(as_uuid=True), db.ForeignKey("services.id"), primary_key=True, nullable=False) - service = db.relationship(Service, backref=db.backref("service_broadcast_settings", uselist=False)) - channel = db.Column(db.String(255), db.ForeignKey("broadcast_channel_types.name"), nullable=False) - provider = db.Column(db.String, db.ForeignKey("broadcast_provider_types.name"), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) - updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow) - - -class BroadcastChannelTypes(db.Model): - __tablename__ = "broadcast_channel_types" - - name = db.Column(db.String(255), primary_key=True) - - -class BroadcastProviderTypes(db.Model): - __tablename__ = "broadcast_provider_types" - - name = db.Column(db.String(255), primary_key=True) - - -class BroadcastProviderMessageStatusType(db.Model): - __tablename__ = "broadcast_provider_message_status_type" - - name = db.Column(db.String(), primary_key=True) - - -class ServiceBroadcastProviderRestriction(db.Model): - """ - TODO: Drop this table as no longer used - - Most services don't send broadcasts. Of those that do, most send to all broadcast providers. - However, some services don't send to all providers. These services are test services that we or the providers - themselves use. - - This table links those services. There should only be one row per service in this table, and this is enforced by - the service_id being a primary key. - """ - - __tablename__ = "service_broadcast_provider_restriction" - - service_id = db.Column(UUID(as_uuid=True), db.ForeignKey("services.id"), primary_key=True, nullable=False) - service = db.relationship(Service, backref=db.backref("service_broadcast_provider_restriction", uselist=False)) - - provider = db.Column(db.String, nullable=False) - - created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) - - class WebauthnCredential(db.Model): """ A table that stores data for registered webauthn credentials. diff --git a/app/schemas.py b/app/schemas.py index 2f5a996507..791a4eaeb4 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -223,8 +223,6 @@ class ServiceSchema(BaseSchema, UUIDsAsStringsMixin): sms_message_limit = field_for(models.Service, "sms_message_limit", required=True) letter_message_limit = field_for(models.Service, "letter_message_limit", required=True) go_live_at = field_for(models.Service, "go_live_at", format=DATETIME_FORMAT_NO_TIMEZONE) - allowed_broadcast_provider = fields.Method(dump_only=True, serialize="_get_allowed_broadcast_provider") - broadcast_channel = fields.Method(dump_only=True, serialize="_get_broadcast_channel") name = fields.String(required=True) custom_email_sender_name = fields.String(allow_none=True) # this can only be set via custom_email_sender_name or name @@ -238,12 +236,6 @@ def service_delivery_status_callback_api(self, service): if callback.callback_type == app.constants.DELIVERY_STATUS_CALLBACK_TYPE ] - def _get_allowed_broadcast_provider(self, service): - return service.allowed_broadcast_provider - - def _get_broadcast_channel(self, service): - return service.broadcast_channel - def get_letter_logo_filename(self, service): return service.letter_branding and service.letter_branding.filename @@ -271,7 +263,6 @@ class Meta(BaseSchema.Meta): "all_template_folders", "annual_billing", "api_keys", - "broadcast_messages", "complaints", "contact_list", "created_at", @@ -285,8 +276,6 @@ class Meta(BaseSchema.Meta): "letter_logo_filename", "reply_to_email_addresses", "returned_letters", - "service_broadcast_provider_restriction", - "service_broadcast_settings", "service_sms_senders", "templates", "updated_at", @@ -347,7 +336,6 @@ class Meta(BaseSchema.Meta): "all_template_folders", "annual_billing", "api_keys", - "broadcast_messages", "contact_list", "created_by", "crown", @@ -444,7 +432,6 @@ class TemplateSchemaNoDetail(TemplateSchema): class Meta(TemplateSchema.Meta): exclude = TemplateSchema.Meta.exclude + ( "archived", - "broadcast_data", "created_at", "created_by", "created_by_id", @@ -466,13 +453,6 @@ class Meta(TemplateSchema.Meta): "letter_languages", ) - @pre_dump - def remove_content_for_non_broadcast_templates(self, template, **kwargs): - if template.template_type != app.constants.BROADCAST_TYPE: - template.content = None - - return template - class TemplateHistorySchema(BaseSchema): reply_to = fields.Method("get_reply_to", allow_none=True) @@ -499,7 +479,6 @@ def get_is_precompiled_letter(self, template): class Meta(BaseSchema.Meta): model = models.TemplateHistory - exclude = ("broadcast_messages",) class ApiKeySchema(BaseSchema): diff --git a/app/service/service_broadcast_settings_schema.py b/app/service/service_broadcast_settings_schema.py deleted file mode 100644 index 0a333c2f28..0000000000 --- a/app/service/service_broadcast_settings_schema.py +++ /dev/null @@ -1,12 +0,0 @@ -service_broadcast_settings_schema = { - "$schema": "http://json-schema.org/draft-07/schema#", - "description": "Set a services broadcast settings", - "type": "object", - "title": "Set a services broadcast settings", - "properties": { - "broadcast_channel": {"enum": ["operator", "test", "severe", "government"]}, - "service_mode": {"enum": ["training", "live"]}, - "provider_restriction": {"enum": ["three", "o2", "vodafone", "ee", "all"]}, - }, - "required": ["broadcast_channel", "service_mode", "provider_restriction"], -} diff --git a/app/service_invite/rest.py b/app/service_invite/rest.py index f9c88aa0a6..fc99074b67 100644 --- a/app/service_invite/rest.py +++ b/app/service_invite/rest.py @@ -3,7 +3,7 @@ from notifications_utils.url_safe_token import check_token, generate_token from app.config import QueueNames -from app.constants import BROADCAST_TYPE, EMAIL_TYPE, KEY_TYPE_NORMAL +from app.constants import EMAIL_TYPE, KEY_TYPE_NORMAL from app.dao.invited_user_dao import ( get_invited_user_by_id, get_invited_user_by_service_and_id, @@ -33,10 +33,7 @@ def create_invited_user(service_id): invited_user = invited_user_schema.load(request_json) save_invited_user(invited_user) - if invited_user.service.has_permission(BROADCAST_TYPE): - template_id = current_app.config["BROADCAST_INVITATION_EMAIL_TEMPLATE_ID"] - else: - template_id = current_app.config["INVITATION_EMAIL_TEMPLATE_ID"] + 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"]) diff --git a/app/template/rest.py b/app/template/rest.py index 481bb4d97a..c6cfb024b5 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -13,7 +13,7 @@ from requests import post as requests_post from sqlalchemy.orm.exc import NoResultFound -from app.constants import BROADCAST_TYPE, LETTER_TYPE, QR_CODE_TOO_LONG, SECOND_CLASS, SMS_TYPE +from app.constants import LETTER_TYPE, QR_CODE_TOO_LONG, SECOND_CLASS, SMS_TYPE from app.dao.notifications_dao import get_notification_by_id from app.dao.services_dao import dao_fetch_service_by_id from app.dao.template_folder_dao import ( @@ -52,7 +52,7 @@ def _content_count_greater_than_limit(content, template_type): - if template_type in {SMS_TYPE, BROADCAST_TYPE}: + if template_type == SMS_TYPE: template = SMSMessageTemplate({"content": content, "template_type": SMS_TYPE}) return template.is_message_too_long() return False diff --git a/app/utils.py b/app/utils.py index 311b9273f4..4b0b56ba07 100644 --- a/app/utils.py +++ b/app/utils.py @@ -12,7 +12,6 @@ from sqlalchemy import func from app.constants import ( - BROADCAST_TYPE, EMAIL_TYPE, LETTER_TYPE, PRECOMPILED_LETTER, @@ -59,12 +58,9 @@ def url_with_token(data, url, config, base_url=None): def get_template_instance(template, values): - return { - SMS_TYPE: SMSMessageTemplate, - EMAIL_TYPE: HTMLEmailTemplate, - LETTER_TYPE: LetterPrintTemplate, - BROADCAST_TYPE: SMSMessageTemplate, - }[template["template_type"]](template, values) + return {SMS_TYPE: SMSMessageTemplate, EMAIL_TYPE: HTMLEmailTemplate, LETTER_TYPE: LetterPrintTemplate}[ + template["template_type"] + ](template, values) def get_london_midnight_in_utc(date): @@ -103,8 +99,6 @@ def get_public_notify_type_text(notify_type, plural=False): notify_type_text = "document" elif notify_type == PRECOMPILED_LETTER: notify_type_text = "precompiled letter" - elif notify_type == BROADCAST_TYPE: - notify_type_text = "broadcast message" return "{}{}".format(notify_type_text, "s" if plural else "") diff --git a/app/xml_schemas/__init__.py b/app/xml_schemas/__init__.py deleted file mode 100644 index 1ba15cb978..0000000000 --- a/app/xml_schemas/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -from pathlib import Path - -from lxml import etree - - -def validate_xml(document, schema_file_name): - path = Path(__file__).resolve().parent / schema_file_name - contents = path.read_text() - - schema_root = etree.XML(contents.encode("utf-8")) - schema = etree.XMLSchema(schema_root) - parser = etree.XMLParser(schema=schema) - - try: - etree.fromstring(document, parser) - except etree.XMLSyntaxError: - return False - - return True diff --git a/docs/writing-public-apis.md b/docs/writing-public-apis.md index 3bca466834..53413a7618 100644 --- a/docs/writing-public-apis.md +++ b/docs/writing-public-apis.md @@ -45,6 +45,6 @@ Each adapter should be documented in each client ([example](https://github.com/a This is done as part of registering the blueprint in `app/__init__.py` e.g. ``` -post_broadcast.before_request(requires_auth) -application.register_blueprint(post_broadcast) +v2_notification_blueprint.before_request(requires_auth) +application.register_blueprint(v2_notification_blueprint) ``` diff --git a/entrypoint.sh b/entrypoint.sh index 21f17a62b6..1d1195f3fe 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/bash #Cater for specific concurrency level -if [ "$1" == "api-worker-periodic" ] || [ "$1" == "api-worker-broadcasts" ] +if [ "$1" == "api-worker-periodic" ] then CONCURRENCY=2 elif [ "$1" == "api-worker-sender-letters" ] @@ -68,10 +68,6 @@ elif [ "$1" == "api-worker-internal" ] then $COMMON_CMD notify-internal-tasks -elif [ "$1" == "api-worker-broadcasts" ] -then - $COMMON_CMD broadcast-tasks - elif [ "$1" == "api-worker-receipts" ] then $COMMON_CMD ses-callbacks,sms-callbacks diff --git a/manifest.yml.j2 b/manifest.yml.j2 index d254fc312d..754ec2185c 100644 --- a/manifest.yml.j2 +++ b/manifest.yml.j2 @@ -70,13 +70,6 @@ 'notify-delivery-worker-letters': {'memory': '2G'}, 'notify-delivery-worker-retry-tasks': {}, 'notify-delivery-worker-internal': {}, - 'notify-delivery-worker-broadcasts': { - 'additional_env_vars': { - 'CELERYD_PREFETCH_MULTIPLIER': 1, - 'CBC_PROXY_AWS_ACCESS_KEY_ID': CBC_PROXY_AWS_ACCESS_KEY_ID, - 'CBC_PROXY_AWS_SECRET_ACCESS_KEY': CBC_PROXY_AWS_SECRET_ACCESS_KEY, - } - }, 'notify-delivery-worker-receipts': {}, 'notify-delivery-worker-service-callbacks': {'disk_quota': '4G'}, 'notify-delivery-worker-save-api-notifications': {'disk_quota': '4G'}, diff --git a/migrations/versions/0323_broadcast_message.py b/migrations/versions/0323_broadcast_message.py index 84c0c84876..29940dbfd8 100644 --- a/migrations/versions/0323_broadcast_message.py +++ b/migrations/versions/0323_broadcast_message.py @@ -10,8 +10,6 @@ from sqlalchemy.dialects import postgresql from sqlalchemy.sql import column, func -from app.models import BroadcastMessage - revision = "0323_broadcast_message" down_revision = "0322_broadcast_service_perm" diff --git a/migrations/versions/0336_broadcast_msg_content_2.py b/migrations/versions/0336_broadcast_msg_content_2.py index a3d6fc26e9..08fd5ac419 100644 --- a/migrations/versions/0336_broadcast_msg_content_2.py +++ b/migrations/versions/0336_broadcast_msg_content_2.py @@ -7,10 +7,6 @@ """ import sqlalchemy as sa from alembic import op -from sqlalchemy.dialects import postgresql -from sqlalchemy.orm.session import Session - -from app.models import BroadcastMessage revision = "0336_broadcast_msg_content_2" down_revision = "0335_broadcast_msg_content" @@ -19,7 +15,7 @@ def upgrade(): conn = op.get_bind() - results = conn.execute( + conn.execute( sa.text( """ UPDATE diff --git a/scripts/paas_app_wrapper.sh b/scripts/paas_app_wrapper.sh index 56ec32e7a0..91f65af1b3 100755 --- a/scripts/paas_app_wrapper.sh +++ b/scripts/paas_app_wrapper.sh @@ -25,7 +25,7 @@ case $NOTIFY_APP_NAME in --logfile=/dev/null --pidfile=/tmp/celery%N.pid -Q send-sms-tasks,send-email-tasks ;; delivery-worker-sender-letters) - # at the default of 2 instances with 4 concurrent workers, we hit DVLA's 50rps rate limit + # at the default of 2 instances with 4 concurrent workers, we hit DVLA's 50rps rate limit exec scripts/run_app_paas.sh celery -A run_celery.notify_celery worker --loglevel=INFO --concurrency=3 \ -Q send-letter-tasks 2> /dev/null ;; @@ -42,10 +42,6 @@ case $NOTIFY_APP_NAME in exec scripts/run_app_paas.sh celery -A run_celery.notify_celery worker --loglevel=INFO --concurrency=4 \ -Q notify-internal-tasks 2> /dev/null ;; - delivery-worker-broadcasts) - exec scripts/run_app_paas.sh celery -A run_celery.notify_celery worker --loglevel=INFO --concurrency=2 \ - -Q broadcast-tasks 2> /dev/null - ;; delivery-worker-receipts) exec scripts/run_app_paas.sh celery -A run_celery.notify_celery worker --loglevel=INFO --concurrency=4 \ -Q ses-callbacks,sms-callbacks 2> /dev/null diff --git a/tests/app/clients/test_cbc_proxy.py b/tests/app/clients/test_cbc_proxy.py deleted file mode 100644 index 40aa811e24..0000000000 --- a/tests/app/clients/test_cbc_proxy.py +++ /dev/null @@ -1,607 +0,0 @@ -import json -import uuid -from collections import namedtuple -from datetime import datetime -from io import BytesIO -from unittest.mock import Mock, call - -import pytest -from botocore.exceptions import ClientError as BotoClientError - -from app import db -from app.clients.cbc_proxy import ( - CBCProxyClient, - CBCProxyEE, - CBCProxyO2, - CBCProxyRetryableException, - CBCProxyThree, - CBCProxyVodafone, -) -from app.utils import DATETIME_FORMAT - -EXAMPLE_AREAS = [ - { - "description": "london", - "polygon": [ - [51.12, -1.2], - [51.12, 1.2], - [51.74, 1.2], - [51.74, -1.2], - [51.12, -1.2], - ], - } -] - - -@pytest.fixture(scope="function") -def cbc_proxy_client(client, mocker): - client = CBCProxyClient() - current_app = mocker.Mock( - config={ - "CBC_PROXY_AWS_ACCESS_KEY_ID": "cbc-proxy-aws-access-key-id", - "CBC_PROXY_AWS_SECRET_ACCESS_KEY": "cbc-proxy-aws-secret-access-key", - "CBC_PROXY_ENABLED": True, - } - ) - client.init_app(current_app) - return client - - -@pytest.fixture -def cbc_proxy_ee(cbc_proxy_client): - return cbc_proxy_client.get_proxy("ee") - - -@pytest.fixture -def cbc_proxy_vodafone(cbc_proxy_client): - return cbc_proxy_client.get_proxy("vodafone") - - -@pytest.mark.parametrize( - "provider_name, expected_provider_class", - [ - ("ee", CBCProxyEE), - ("three", CBCProxyThree), - ("o2", CBCProxyO2), - ("vodafone", CBCProxyVodafone), - ], -) -def test_cbc_proxy_client_returns_correct_client(provider_name, expected_provider_class): - mock_lambda = Mock() - cbc_proxy_client = CBCProxyClient() - cbc_proxy_client._lambda_client = mock_lambda - - ret = cbc_proxy_client.get_proxy(provider_name) - - assert type(ret) == expected_provider_class - assert ret._lambda_client == mock_lambda - - -def test_cbc_proxy_lambda_client_has_correct_region(cbc_proxy_ee): - assert cbc_proxy_ee._lambda_client._client_config.region_name == "eu-west-2" - - -def test_cbc_proxy_lambda_client_has_correct_keys(cbc_proxy_ee): - key = cbc_proxy_ee._lambda_client._request_signer._credentials.access_key - secret = cbc_proxy_ee._lambda_client._request_signer._credentials.secret_key - - assert key == "cbc-proxy-aws-access-key-id" - assert secret == "cbc-proxy-aws-secret-access-key" - - -def test_cbc_proxy_send_link_test(mocker, cbc_proxy_ee): - mock_send_link_test = mocker.patch.object(cbc_proxy_ee, "_send_link_test") - cbc_proxy_ee.send_link_test() - - mock_send_link_test.assert_any_call(cbc_proxy_ee.lambda_name) - mock_send_link_test.assert_any_call(cbc_proxy_ee.failover_lambda_name) - - -@pytest.mark.parametrize( - "description, expected_language", - ( - ("my-description", "en-GB"), - ("mŷ-description", "cy-GB"), - ), -) -@pytest.mark.parametrize("cbc", ["ee", "three", "o2"]) -def test_cbc_proxy_one_2_many_create_and_send_invokes_function( - mocker, - cbc_proxy_client, - description, - cbc, - expected_language, -): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - identifier = "my-identifier" - headline = "my-headline" - - sent = "a-passed-through-sent-value" - expires = "a-passed-through-expires-value" - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 200, - } - - cbc_proxy.create_and_send_broadcast( - identifier=identifier, - message_number="0000007b", - headline=headline, - description=description, - areas=EXAMPLE_AREAS, - sent=sent, - expires=expires, - channel="severe", - ) - - ld_client_mock.invoke.assert_called_once_with( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ) - - kwargs = ld_client_mock.invoke.mock_calls[0][-1] - payload_bytes = kwargs["Payload"] - payload = json.loads(payload_bytes) - - assert payload["identifier"] == identifier - assert "message_number" not in payload - assert payload["message_format"] == "cap" - assert payload["message_type"] == "alert" - assert payload["headline"] == headline - assert payload["description"] == description - assert payload["areas"] == EXAMPLE_AREAS - assert payload["sent"] == sent - assert payload["expires"] == expires - assert payload["language"] == expected_language - assert payload["channel"] == "severe" - - -@pytest.mark.parametrize("cbc", ["ee", "three", "o2"]) -def test_cbc_proxy_one_2_many_cancel_invokes_function(mocker, cbc_proxy_client, cbc): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - identifier = "my-identifier" - MockProviderMessage = namedtuple("BroadcastProviderMessage", ["id", "message_number", "created_at"]) - - provider_messages = [ - MockProviderMessage(uuid.uuid4(), "0000007b", datetime(2020, 12, 16)), - MockProviderMessage(uuid.uuid4(), "0000004e", datetime(2020, 12, 17)), - ] - sent = "2020-12-17 14:19:44.130585" - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 200, - } - - cbc_proxy.cancel_broadcast( - identifier=identifier, message_number="00000050", previous_provider_messages=provider_messages, sent=sent - ) - - ld_client_mock.invoke.assert_called_once_with( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ) - - kwargs = ld_client_mock.invoke.mock_calls[0][-1] - payload_bytes = kwargs["Payload"] - payload = json.loads(payload_bytes) - - assert payload["identifier"] == identifier - assert "message_number" not in payload - assert payload["message_format"] == "cap" - assert payload["message_type"] == "cancel" - assert payload["references"] == [ - {"message_id": str(provider_messages[0].id), "sent": provider_messages[0].created_at.strftime(DATETIME_FORMAT)}, - {"message_id": str(provider_messages[1].id), "sent": provider_messages[1].created_at.strftime(DATETIME_FORMAT)}, - ] - assert payload["sent"] == sent - - -@pytest.mark.parametrize( - "description, expected_language", - ( - ("my-description", "English"), - ("mŷ-description", "Welsh"), - ), -) -def test_cbc_proxy_vodafone_create_and_send_invokes_function( - mocker, - cbc_proxy_vodafone, - description, - expected_language, -): - identifier = "my-identifier" - headline = "my-headline" - - sent = "a-passed-through-sent-value" - expires = "a-passed-through-expires-value" - - ld_client_mock = mocker.patch.object( - cbc_proxy_vodafone, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 200, - } - - cbc_proxy_vodafone.create_and_send_broadcast( - identifier=identifier, - message_number="0000007b", - headline=headline, - description=description, - areas=EXAMPLE_AREAS, - sent=sent, - expires=expires, - channel="test", - ) - - ld_client_mock.invoke.assert_called_once_with( - FunctionName="vodafone-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ) - - kwargs = ld_client_mock.invoke.mock_calls[0][-1] - payload_bytes = kwargs["Payload"] - payload = json.loads(payload_bytes) - - assert payload["identifier"] == identifier - assert payload["message_number"] == "0000007b" - assert payload["message_format"] == "ibag" - assert payload["message_type"] == "alert" - assert payload["headline"] == headline - assert payload["description"] == description - assert payload["areas"] == EXAMPLE_AREAS - assert payload["sent"] == sent - assert payload["expires"] == expires - assert payload["language"] == expected_language - assert payload["channel"] == "test" - - -def test_cbc_proxy_vodafone_cancel_invokes_function(mocker, cbc_proxy_vodafone): - identifier = "my-identifier" - MockProviderMessage = namedtuple("BroadcastProviderMessage", ["id", "message_number", "created_at"]) - - provider_messages = [ - MockProviderMessage(uuid.uuid4(), 78, datetime(2020, 12, 16)), - MockProviderMessage(uuid.uuid4(), 123, datetime(2020, 12, 17)), - ] - sent = "2020-12-18 14:19:44.130585" - - ld_client_mock = mocker.patch.object( - cbc_proxy_vodafone, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 200, - } - - cbc_proxy_vodafone.cancel_broadcast( - identifier=identifier, message_number="00000050", previous_provider_messages=provider_messages, sent=sent - ) - - ld_client_mock.invoke.assert_called_once_with( - FunctionName="vodafone-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ) - - kwargs = ld_client_mock.invoke.mock_calls[0][-1] - payload_bytes = kwargs["Payload"] - payload = json.loads(payload_bytes) - - assert payload["identifier"] == identifier - assert payload["message_number"] == "00000050" - assert payload["message_format"] == "ibag" - assert payload["message_type"] == "cancel" - assert payload["references"] == [ - { - "message_id": str(provider_messages[0].id), - "message_number": "0000004e", - "sent": provider_messages[0].created_at.strftime(DATETIME_FORMAT), - }, - { - "message_id": str(provider_messages[1].id), - "message_number": "0000007b", - "sent": provider_messages[1].created_at.strftime(DATETIME_FORMAT), - }, - ] - assert payload["sent"] == sent - - -@pytest.mark.parametrize("cbc", ["ee", "vodafone", "three", "o2"]) -def test_cbc_proxy_will_failover_to_second_lambda_if_boto_client_error(mocker, cbc_proxy_client, cbc): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.side_effect = BotoClientError({}, "error") - - with pytest.raises(CBCProxyRetryableException) as e: - cbc_proxy.create_and_send_broadcast( - identifier="my-identifier", - message_number="0000007b", - headline="my-headline", - description="test-description", - areas=EXAMPLE_AREAS, - sent="a-passed-through-sent-value", - expires="a-passed-through-expires-value", - channel="severe", - ) - - assert e.match(f"Lambda failed for both {cbc}-1-proxy and {cbc}-2-proxy") - - assert ld_client_mock.invoke.call_args_list == [ - call( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - call( - FunctionName=f"{cbc}-2-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - ] - - -@pytest.mark.parametrize("cbc", ["ee", "vodafone", "three", "o2"]) -def test_cbc_proxy_will_failover_to_second_lambda_if_function_error(mocker, cbc_proxy_client, cbc): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.side_effect = [ - { - "StatusCode": 200, - "FunctionError": "Handled", - "Payload": BytesIO(json.dumps({"errorMessage": "", "errorType": "CBCNewConnectionError"}).encode("utf-8")), - }, - {"StatusCode": 200}, - ] - - cbc_proxy.create_and_send_broadcast( - identifier="my-identifier", - message_number="0000007b", - headline="my-headline", - description="test-description", - areas=EXAMPLE_AREAS, - sent="a-passed-through-sent-value", - expires="a-passed-through-expires-value", - channel="severe", - ) - - assert ld_client_mock.invoke.call_args_list == [ - call( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - call( - FunctionName=f"{cbc}-2-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - ] - - -@pytest.mark.parametrize("cbc", ["ee", "vodafone", "three", "o2"]) -def test_cbc_proxy_will_failover_to_second_lambda_if_invoke_error(mocker, cbc_proxy_client, cbc): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.side_effect = [{"StatusCode": 400}, {"StatusCode": 200}] - - cbc_proxy.create_and_send_broadcast( - identifier="my-identifier", - message_number="0000007b", - headline="my-headline", - description="test-description", - areas=EXAMPLE_AREAS, - sent="a-passed-through-sent-value", - expires="a-passed-through-expires-value", - channel="test", - ) - - assert ld_client_mock.invoke.call_args_list == [ - call( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - call( - FunctionName=f"{cbc}-2-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - ] - - -@pytest.mark.parametrize("cbc", ["ee", "vodafone", "three", "o2"]) -def test_cbc_proxy_create_and_send_tries_failover_lambda_on_invoke_error_and_raises_if_both_invoke_error( - mocker, cbc_proxy_client, cbc -): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 400, - } - - with pytest.raises(CBCProxyRetryableException) as e: - cbc_proxy.create_and_send_broadcast( - identifier="my-identifier", - message_number="0000007b", - headline="my-headline", - description="my-description", - areas=EXAMPLE_AREAS, - sent="a-passed-through-sent-value", - expires="a-passed-through-expires-value", - channel="test", - ) - - assert e.match(f"Lambda failed for both {cbc}-1-proxy and {cbc}-2-proxy") - - assert ld_client_mock.invoke.call_args_list == [ - call( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - call( - FunctionName=f"{cbc}-2-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - ] - - -@pytest.mark.parametrize("cbc", ["ee", "vodafone", "three", "o2"]) -def test_cbc_proxy_create_and_send_tries_failover_lambda_on_function_error_and_raises_if_both_function_error( - mocker, cbc_proxy_client, cbc -): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 200, - "FunctionError": "something", - "Payload": BytesIO(json.dumps({"errorMessage": "some message", "errorType": "SomeErrorType"}).encode("utf-8")), - } - - with pytest.raises(CBCProxyRetryableException) as e: - cbc_proxy.create_and_send_broadcast( - identifier="my-identifier", - message_number="0000007b", - headline="my-headline", - description="my-description", - areas=EXAMPLE_AREAS, - sent="a-passed-through-sent-value", - expires="a-passed-through-expires-value", - channel="severe", - ) - - assert e.match(f"Lambda failed for both {cbc}-1-proxy and {cbc}-2-proxy") - - assert ld_client_mock.invoke.call_args_list == [ - call( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - call( - FunctionName=f"{cbc}-2-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ), - ] - - -@pytest.mark.parametrize("cbc", ["ee", "three", "o2"]) -def test_cbc_proxy_one_2_many_send_link_test_invokes_function(mocker, cbc_proxy_client, cbc): - cbc_proxy = cbc_proxy_client.get_proxy(cbc) - - mocker.patch("app.clients.cbc_proxy.uuid.uuid4", return_value=123) - - ld_client_mock = mocker.patch.object( - cbc_proxy, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 200, - } - - cbc_proxy._send_link_test(lambda_name=f"{cbc}-1-proxy") - - ld_client_mock.invoke.assert_called_once_with( - FunctionName=f"{cbc}-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ) - - kwargs = ld_client_mock.invoke.mock_calls[0][-1] - payload_bytes = kwargs["Payload"] - payload = json.loads(payload_bytes) - - assert payload["identifier"] == "123" - assert payload["message_type"] == "test" - assert "message_number" not in payload - assert payload["message_format"] == "cap" - - -@pytest.mark.skip -def test_cbc_proxy_vodafone_send_link_test_invokes_function(mocker, cbc_proxy_vodafone): - mocker.patch("app.clients.cbc_proxy.uuid.uuid4", return_value=123) - - db.session.connection().execute("ALTER SEQUENCE broadcast_provider_message_number_seq RESTART WITH 1") - - ld_client_mock = mocker.patch.object( - cbc_proxy_vodafone, - "_lambda_client", - create=True, - ) - - ld_client_mock.invoke.return_value = { - "StatusCode": 200, - } - - cbc_proxy_vodafone._send_link_test(lambda_name="vodafone-1-proxy") - - ld_client_mock.invoke.assert_called_once_with( - FunctionName="vodafone-1-proxy", - InvocationType="RequestResponse", - Payload=mocker.ANY, - ) - - kwargs = ld_client_mock.invoke.mock_calls[0][-1] - payload_bytes = kwargs["Payload"] - payload = json.loads(payload_bytes) - - assert payload["identifier"] == "123" - assert payload["message_type"] == "test" - assert payload["message_number"] == "00000001" - assert payload["message_format"] == "ibag" diff --git a/tests/app/db.py b/tests/app/db.py index 45f3fe3781..c5e52f2d79 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -2,8 +2,6 @@ import uuid from datetime import date, datetime, timedelta -import pytest - from app import db from app.constants import ( EMAIL_TYPE, @@ -38,12 +36,6 @@ from app.models import ( AnnualBilling, ApiKey, - BroadcastEvent, - BroadcastMessage, - BroadcastProvider, - BroadcastProviderMessage, - BroadcastProviderMessageNumber, - BroadcastStatusType, Complaint, DailySortedLetter, Domain, @@ -1153,100 +1145,6 @@ def create_service_contact_list( return contact_list -def create_broadcast_message( - template=None, - *, - service=None, # only used if template is not provided - created_by=None, - personalisation=None, - content=None, - status=BroadcastStatusType.DRAFT, - starts_at=None, - finishes_at=None, - areas=None, - stubbed=False, - cap_event=None, -): - if template: - service = template.service - template_id = template.id - template_version = template.version - personalisation = personalisation or {} - content = template._as_utils_template_with_personalisation(personalisation).content_with_placeholders_filled_in - elif content: - template_id = None - template_version = None - personalisation = None - content = content - else: - pytest.fail("Provide template or content") - - broadcast_message = BroadcastMessage( - service_id=service.id, - template_id=template_id, - template_version=template_version, - personalisation=personalisation, - status=status, - starts_at=starts_at, - finishes_at=finishes_at, - created_by_id=created_by.id if created_by else service.created_by_id, - areas=areas or {"ids": [], "simple_polygons": []}, - content=content, - stubbed=stubbed, - cap_event=cap_event, - ) - db.session.add(broadcast_message) - db.session.commit() - return broadcast_message - - -def create_broadcast_event( - broadcast_message, - sent_at=None, - message_type="alert", - transmitted_content=None, - transmitted_areas=None, - transmitted_sender=None, - transmitted_starts_at=None, - transmitted_finishes_at=None, -): - b_e = BroadcastEvent( - service=broadcast_message.service, - broadcast_message=broadcast_message, - sent_at=sent_at or datetime.utcnow(), - message_type=message_type, - transmitted_content=transmitted_content or {"body": "this is an emergency broadcast message"}, - transmitted_areas=transmitted_areas or broadcast_message.areas, - transmitted_sender=transmitted_sender or "www.notifications.service.gov.uk", - transmitted_starts_at=transmitted_starts_at, - transmitted_finishes_at=transmitted_finishes_at or datetime.utcnow() + timedelta(hours=24), - ) - db.session.add(b_e) - db.session.commit() - return b_e - - -def create_broadcast_provider_message(broadcast_event, provider, status="sending"): - broadcast_provider_message_id = uuid.uuid4() - provider_message = BroadcastProviderMessage( - id=broadcast_provider_message_id, - broadcast_event=broadcast_event, - provider=provider, - status=status, - ) - db.session.add(provider_message) - db.session.commit() - - provider_message_number = None - if provider == BroadcastProvider.VODAFONE: - provider_message_number = BroadcastProviderMessageNumber( - broadcast_provider_message_id=broadcast_provider_message_id - ) - db.session.add(provider_message_number) - db.session.commit() - return provider_message - - def create_webauthn_credential( user, name="my key", diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 78f796390d..7f58977259 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -9,7 +9,6 @@ from sqlalchemy.exc import SQLAlchemyError from app.constants import ( - BROADCAST_TYPE, EMAIL_AUTH_TYPE, EMAIL_TYPE, INBOUND_SMS_TYPE, @@ -235,16 +234,12 @@ def test_get_service_by_id(admin_request, sample_service): assert json_resp["data"]["id"] == str(sample_service.id) assert json_resp["data"]["email_branding"] is None assert json_resp["data"]["prefix_sms"] is True - assert json_resp["data"]["allowed_broadcast_provider"] is None - assert json_resp["data"]["broadcast_channel"] is None assert set(json_resp["data"].keys()) == { "active", - "allowed_broadcast_provider", "billing_contact_email_addresses", "billing_contact_names", "billing_reference", - "broadcast_channel", "consent_to_research", "contact_link", "count_as_live", @@ -921,13 +916,12 @@ def test_update_service_permissions_will_add_service_permissions(client, sample_ @pytest.mark.parametrize( "permission_to_add", [ - (EMAIL_TYPE), - (SMS_TYPE), - (INTERNATIONAL_SMS_TYPE), - (LETTER_TYPE), - (INBOUND_SMS_TYPE), - (EMAIL_AUTH_TYPE), - (BROADCAST_TYPE), # TODO: remove this ability to set broadcast permission this way + EMAIL_TYPE, + SMS_TYPE, + INTERNATIONAL_SMS_TYPE, + LETTER_TYPE, + INBOUND_SMS_TYPE, + EMAIL_AUTH_TYPE, ], ) def test_add_service_permission_will_add_permission(client, service_with_no_permissions, permission_to_add): diff --git a/tests/app/template/test_rest.py b/tests/app/template/test_rest.py index 68a3955493..a10fcc6223 100644 --- a/tests/app/template/test_rest.py +++ b/tests/app/template/test_rest.py @@ -12,7 +12,7 @@ from notifications_utils import SMS_CHAR_COUNT_LIMIT from pypdf.errors import PdfReadError -from app.constants import BROADCAST_TYPE, EMAIL_TYPE, LETTER_TYPE, SMS_TYPE +from app.constants import EMAIL_TYPE, LETTER_TYPE, SMS_TYPE from app.dao.templates_dao import ( dao_get_template_by_id, dao_get_template_versions, @@ -35,7 +35,6 @@ @pytest.mark.parametrize( "template_type, subject", [ - (BROADCAST_TYPE, None), (SMS_TYPE, None), (EMAIL_TYPE, "subject"), (LETTER_TYPE, "subject"), @@ -209,12 +208,6 @@ def test_should_raise_error_if_service_does_not_exist_on_create(client, sample_u @pytest.mark.parametrize( "permissions, template_type, subject, expected_error", [ - ( - [EMAIL_TYPE, SMS_TYPE, LETTER_TYPE], - BROADCAST_TYPE, - None, - {"template_type": ["Creating broadcast message templates is not allowed"]}, - ), # noqa ([EMAIL_TYPE], SMS_TYPE, None, {"template_type": ["Creating text message templates is not allowed"]}), ([SMS_TYPE], EMAIL_TYPE, "subject", {"template_type": ["Creating email templates is not allowed"]}), ([SMS_TYPE], LETTER_TYPE, "subject", {"template_type": ["Creating letter templates is not allowed"]}), @@ -592,7 +585,6 @@ def test_should_get_return_all_fields_by_default( ) assert json_response["data"][0].keys() == { "archived", - "broadcast_data", "content", "created_at", "created_by", @@ -633,7 +625,6 @@ def test_should_get_return_all_fields_by_default( (EMAIL_TYPE, None), (SMS_TYPE, None), (LETTER_TYPE, None), - (BROADCAST_TYPE, "This is a test"), ), ) def test_should_not_return_content_and_subject_if_requested( @@ -783,25 +774,12 @@ def test_should_return_404_if_no_templates_for_service_with_id(client, sample_se assert json_resp["message"] == "No result found" -@pytest.mark.parametrize( - "template_type", - ( - SMS_TYPE, - BROADCAST_TYPE, - ), -) -def test_create_400_for_over_limit_content( - client, - notify_api, - sample_user, - fake_uuid, - template_type, -): - sample_service = create_service(service_permissions=[template_type]) +def test_create_400_for_over_limit_content(client, notify_api, sample_user, fake_uuid): + sample_service = create_service(service_permissions=[SMS_TYPE]) content = "".join(random.choice(string.ascii_uppercase + string.digits) for _ in range(SMS_CHAR_COUNT_LIMIT + 1)) data = { "name": "too big template", - "template_type": template_type, + "template_type": SMS_TYPE, "content": content, "service": str(sample_service.id), "created_by": str(sample_service.created_by.id), diff --git a/tests/app/test_config.py b/tests/app/test_config.py index c917207a8d..aaf66d32a5 100644 --- a/tests/app/test_config.py +++ b/tests/app/test_config.py @@ -7,7 +7,7 @@ def test_queue_names_all_queues_correct(): # Need to ensure that all_queues() only returns queue names used in API queues = QueueNames.all_queues() - assert len(queues) == 19 + assert len(queues) == 18 assert set( [ QueueNames.PERIODIC, @@ -28,7 +28,6 @@ def test_queue_names_all_queues_correct(): QueueNames.SMS_CALLBACKS, QueueNames.SAVE_API_EMAIL, QueueNames.SAVE_API_SMS, - QueueNames.BROADCASTS, ] ) == set(queues) diff --git a/tests/app/v2/templates/test_get_templates.py b/tests/app/v2/templates/test_get_templates.py index f49fb57b03..92d26f5ec2 100644 --- a/tests/app/v2/templates/test_get_templates.py +++ b/tests/app/v2/templates/test_get_templates.py @@ -82,7 +82,5 @@ def test_get_all_templates_for_invalid_type_returns_400(api_client_request, samp assert json_response == { "status_code": 400, - "errors": [ - {"message": "type coconut is not one of [sms, email, letter, broadcast]", "error": "ValidationError"} - ], + "errors": [{"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 1ab453bcfb..3cf9cf05e2 100644 --- a/tests/app/v2/templates/test_templates_schemas.py +++ b/tests/app/v2/templates/test_templates_schemas.py @@ -275,7 +275,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, letter, broadcast]" + assert errors["errors"][0]["message"] == "type unknown is not one of [sms, email, letter]" @pytest.mark.parametrize("response", valid_json_get_all_response) diff --git a/tests/conftest.py b/tests/conftest.py index 8c5657d628..affdaac7bf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -152,11 +152,8 @@ def notify_db_session(_notify_db, sms_providers): "organisation_types", "service_permission_types", "auth_type", - "broadcast_status_type", "invite_status_type", "service_callback_type", - "broadcast_channel_types", - "broadcast_provider_types", "default_annual_allowance", ]: _notify_db.engine.execute(tbl.delete())