mirror of
https://github.com/GSA/notifications-api.git
synced 2026-09-02 17:48:36 -04:00
Merge pull request #3497 from alphagov/broadcast-alert-earlier-181423293
Alert earlier when a broadcast is approved
This commit is contained in:
@@ -2,14 +2,12 @@ import iso8601
|
|||||||
from flask import Blueprint, jsonify, request
|
from flask import Blueprint, jsonify, request
|
||||||
from notifications_utils.template import BroadcastMessageTemplate
|
from notifications_utils.template import BroadcastMessageTemplate
|
||||||
|
|
||||||
|
from app.broadcast_message import utils as broadcast_utils
|
||||||
from app.broadcast_message.broadcast_message_schema import (
|
from app.broadcast_message.broadcast_message_schema import (
|
||||||
create_broadcast_message_schema,
|
create_broadcast_message_schema,
|
||||||
update_broadcast_message_schema,
|
update_broadcast_message_schema,
|
||||||
update_broadcast_message_status_schema,
|
update_broadcast_message_status_schema,
|
||||||
)
|
)
|
||||||
from app.broadcast_message.utils import (
|
|
||||||
validate_and_update_broadcast_message_status,
|
|
||||||
)
|
|
||||||
from app.dao.broadcast_message_dao import (
|
from app.dao.broadcast_message_dao import (
|
||||||
dao_get_broadcast_message_by_id_and_service_id,
|
dao_get_broadcast_message_by_id_and_service_id,
|
||||||
dao_get_broadcast_messages_for_service,
|
dao_get_broadcast_messages_for_service,
|
||||||
@@ -162,6 +160,6 @@ def update_broadcast_message_status(service_id, broadcast_message_id):
|
|||||||
status_code=400
|
status_code=400
|
||||||
)
|
)
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(broadcast_message, new_status, updating_user)
|
broadcast_utils.update_broadcast_message_status(broadcast_message, new_status, updating_user)
|
||||||
|
|
||||||
return jsonify(broadcast_message.serialize()), 200
|
return jsonify(broadcast_message.serialize()), 200
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
import inspect
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
from notifications_utils.clients.zendesk.zendesk_client import (
|
||||||
|
NotifySupportTicket,
|
||||||
|
)
|
||||||
|
|
||||||
|
from app import zendesk_client
|
||||||
from app.celery.broadcast_message_tasks import send_broadcast_event
|
from app.celery.broadcast_message_tasks import send_broadcast_event
|
||||||
from app.config import QueueNames
|
from app.config import QueueNames
|
||||||
from app.dao.dao_utils import dao_save_object
|
from app.dao.dao_utils import dao_save_object
|
||||||
@@ -13,7 +18,31 @@ from app.models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_and_update_broadcast_message_status(broadcast_message, new_status, updating_user=None, api_key_id=None):
|
def update_broadcast_message_status(broadcast_message, new_status, updating_user=None, api_key_id=None):
|
||||||
|
_validate_broadcast_update(broadcast_message, new_status, updating_user)
|
||||||
|
|
||||||
|
if new_status == BroadcastStatusType.BROADCASTING:
|
||||||
|
broadcast_message.approved_at = datetime.utcnow()
|
||||||
|
broadcast_message.approved_by = updating_user
|
||||||
|
|
||||||
|
if new_status == BroadcastStatusType.CANCELLED:
|
||||||
|
broadcast_message.cancelled_at = datetime.utcnow()
|
||||||
|
broadcast_message.cancelled_by = updating_user
|
||||||
|
broadcast_message.cancelled_by_api_key_id = api_key_id
|
||||||
|
|
||||||
|
current_app.logger.info(
|
||||||
|
f'broadcast_message {broadcast_message.id} moving from {broadcast_message.status} to {new_status}'
|
||||||
|
)
|
||||||
|
broadcast_message.status = new_status
|
||||||
|
|
||||||
|
dao_save_object(broadcast_message)
|
||||||
|
_create_p1_zendesk_alert(broadcast_message)
|
||||||
|
|
||||||
|
if new_status in {BroadcastStatusType.BROADCASTING, BroadcastStatusType.CANCELLED}:
|
||||||
|
_create_broadcast_event(broadcast_message)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_broadcast_update(broadcast_message, new_status, updating_user):
|
||||||
if new_status not in BroadcastStatusType.ALLOWED_STATUS_TRANSITIONS[broadcast_message.status]:
|
if new_status not in BroadcastStatusType.ALLOWED_STATUS_TRANSITIONS[broadcast_message.status]:
|
||||||
raise InvalidRequest(
|
raise InvalidRequest(
|
||||||
f'Cannot move broadcast_message {broadcast_message.id} from {broadcast_message.status} to {new_status}',
|
f'Cannot move broadcast_message {broadcast_message.id} from {broadcast_message.status} to {new_status}',
|
||||||
@@ -32,24 +61,39 @@ def validate_and_update_broadcast_message_status(broadcast_message, new_status,
|
|||||||
f'broadcast_message {broadcast_message.id} has no selected areas and so cannot be broadcasted.',
|
f'broadcast_message {broadcast_message.id} has no selected areas and so cannot be broadcasted.',
|
||||||
status_code=400
|
status_code=400
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
broadcast_message.approved_at = datetime.utcnow()
|
|
||||||
broadcast_message.approved_by = updating_user
|
|
||||||
|
|
||||||
if new_status == BroadcastStatusType.CANCELLED:
|
|
||||||
broadcast_message.cancelled_at = datetime.utcnow()
|
|
||||||
broadcast_message.cancelled_by = updating_user
|
|
||||||
broadcast_message.cancelled_by_api_key_id = api_key_id
|
|
||||||
|
|
||||||
current_app.logger.info(
|
def _create_p1_zendesk_alert(broadcast_message):
|
||||||
f'broadcast_message {broadcast_message.id} moving from {broadcast_message.status} to {new_status}'
|
if current_app.config['NOTIFY_ENVIRONMENT'] != 'live':
|
||||||
|
return
|
||||||
|
|
||||||
|
if broadcast_message.status != BroadcastStatusType.BROADCASTING:
|
||||||
|
return
|
||||||
|
|
||||||
|
message = inspect.cleandoc(f"""
|
||||||
|
Broadcast Sent
|
||||||
|
|
||||||
|
https://www.notifications.service.gov.uk/services/{broadcast_message.service_id}/current-alerts/{broadcast_message.id}
|
||||||
|
|
||||||
|
Sent on channel {broadcast_message.service.broadcast_channel} to {broadcast_message.areas["names"]}.
|
||||||
|
|
||||||
|
Content starts "{broadcast_message.content[:100]}".
|
||||||
|
|
||||||
|
Follow the runbook to check the broadcast went out OK:
|
||||||
|
https://docs.google.com/document/d/1J99yOlfp4nQz6et0w5oJVqi-KywtIXkxrEIyq_g2XUs/edit#heading=h.lzr9aq5b4wg
|
||||||
|
""")
|
||||||
|
|
||||||
|
ticket = NotifySupportTicket(
|
||||||
|
subject='Live broadcast sent',
|
||||||
|
message=message,
|
||||||
|
ticket_type=NotifySupportTicket.TYPE_INCIDENT,
|
||||||
|
technical_ticket=True,
|
||||||
|
org_id=current_app.config['BROADCAST_ORGANISATION_ID'],
|
||||||
|
org_type='central',
|
||||||
|
service_id=str(broadcast_message.service_id),
|
||||||
|
p1=True
|
||||||
)
|
)
|
||||||
broadcast_message.status = new_status
|
zendesk_client.send_ticket_to_zendesk(ticket)
|
||||||
|
|
||||||
dao_save_object(broadcast_message)
|
|
||||||
|
|
||||||
if new_status in {BroadcastStatusType.BROADCASTING, BroadcastStatusType.CANCELLED}:
|
|
||||||
_create_broadcast_event(broadcast_message)
|
|
||||||
|
|
||||||
|
|
||||||
def _create_broadcast_event(broadcast_message):
|
def _create_broadcast_event(broadcast_message):
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from notifications_utils.clients.zendesk.zendesk_client import (
|
|
||||||
NotifySupportTicket,
|
|
||||||
)
|
|
||||||
|
|
||||||
from app import cbc_proxy_client, notify_celery, zendesk_client
|
from app import cbc_proxy_client, notify_celery
|
||||||
from app.clients.cbc_proxy import CBCProxyRetryableException
|
from app.clients.cbc_proxy import CBCProxyRetryableException
|
||||||
from app.config import QueueNames, TaskNames
|
from app.config import QueueNames, TaskNames
|
||||||
from app.dao.broadcast_message_dao import (
|
from app.dao.broadcast_message_dao import (
|
||||||
@@ -126,37 +123,6 @@ def check_event_makes_sense_in_sequence(broadcast_event, provider):
|
|||||||
def send_broadcast_event(broadcast_event_id):
|
def send_broadcast_event(broadcast_event_id):
|
||||||
broadcast_event = dao_get_broadcast_event_by_id(broadcast_event_id)
|
broadcast_event = dao_get_broadcast_event_by_id(broadcast_event_id)
|
||||||
|
|
||||||
if (
|
|
||||||
current_app.config['NOTIFY_ENVIRONMENT'] == 'live' and
|
|
||||||
broadcast_event.message_type == BroadcastEventMessageType.ALERT
|
|
||||||
):
|
|
||||||
broadcast_message = broadcast_event.broadcast_message
|
|
||||||
# raise a zendesk ticket to alert team that broadcast is going out.
|
|
||||||
message = '\n'.join([
|
|
||||||
'Broadcast Sent',
|
|
||||||
'',
|
|
||||||
f'https://www.notifications.service.gov.uk/services/{broadcast_message.service_id}/current-alerts/{broadcast_message.id}', # noqa
|
|
||||||
'',
|
|
||||||
f'This broacast has been sent on channel {broadcast_message.service.broadcast_channel}.',
|
|
||||||
f'This broadcast is targeted at areas {broadcast_message.areas.get("names", [])}.', # noqa
|
|
||||||
''
|
|
||||||
f'This broadcast\'s content starts "{broadcast_message.content[:100]}"'
|
|
||||||
'',
|
|
||||||
'If this alert is not expected refer to the runbook for instructions.',
|
|
||||||
'https://docs.google.com/document/d/1J99yOlfp4nQz6et0w5oJVqi-KywtIXkxrEIyq_g2XUs',
|
|
||||||
])
|
|
||||||
ticket = NotifySupportTicket(
|
|
||||||
subject='Live broadcast sent',
|
|
||||||
message=message,
|
|
||||||
ticket_type=NotifySupportTicket.TYPE_INCIDENT,
|
|
||||||
technical_ticket=True,
|
|
||||||
org_id=current_app.config['BROADCAST_ORGANISATION_ID'],
|
|
||||||
org_type='central',
|
|
||||||
service_id=str(broadcast_message.service_id)
|
|
||||||
)
|
|
||||||
zendesk_client.send_ticket_to_zendesk(ticket)
|
|
||||||
current_app.logger.error(message)
|
|
||||||
|
|
||||||
notify_celery.send_task(
|
notify_celery.send_task(
|
||||||
name=TaskNames.PUBLISH_GOVUK_ALERTS,
|
name=TaskNames.PUBLISH_GOVUK_ALERTS,
|
||||||
queue=QueueNames.GOVUK_ALERTS
|
queue=QueueNames.GOVUK_ALERTS
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ from notifications_utils.template import BroadcastMessageTemplate
|
|||||||
from sqlalchemy.orm.exc import MultipleResultsFound
|
from sqlalchemy.orm.exc import MultipleResultsFound
|
||||||
|
|
||||||
from app import api_user, authenticated_service, redis_store
|
from app import api_user, authenticated_service, redis_store
|
||||||
|
from app.broadcast_message import utils as broadcast_utils
|
||||||
from app.broadcast_message.translators import cap_xml_to_dict
|
from app.broadcast_message.translators import cap_xml_to_dict
|
||||||
from app.broadcast_message.utils import (
|
|
||||||
validate_and_update_broadcast_message_status,
|
|
||||||
)
|
|
||||||
from app.dao.broadcast_message_dao import (
|
from app.dao.broadcast_message_dao import (
|
||||||
dao_get_broadcast_message_by_references_and_service_id,
|
dao_get_broadcast_message_by_references_and_service_id,
|
||||||
)
|
)
|
||||||
@@ -121,7 +119,7 @@ def _cancel_or_reject_broadcast(references_to_original_broadcast, service_id):
|
|||||||
new_status = BroadcastStatusType.REJECTED
|
new_status = BroadcastStatusType.REJECTED
|
||||||
else:
|
else:
|
||||||
new_status = BroadcastStatusType.CANCELLED
|
new_status = BroadcastStatusType.CANCELLED
|
||||||
validate_and_update_broadcast_message_status(
|
broadcast_utils.update_broadcast_message_status(
|
||||||
broadcast_message,
|
broadcast_message,
|
||||||
new_status,
|
new_status,
|
||||||
api_key_id=api_user.id
|
api_key_id=api_user.id
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.broadcast_message.utils import (
|
from app.broadcast_message.utils import (
|
||||||
validate_and_update_broadcast_message_status,
|
_create_p1_zendesk_alert,
|
||||||
|
update_broadcast_message_status,
|
||||||
)
|
)
|
||||||
from app.errors import InvalidRequest
|
from app.errors import InvalidRequest
|
||||||
from app.models import (
|
from app.models import (
|
||||||
@@ -15,9 +16,10 @@ from tests.app.db import (
|
|||||||
create_template,
|
create_template,
|
||||||
create_user,
|
create_user,
|
||||||
)
|
)
|
||||||
|
from tests.conftest import set_config
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_stores_approved_by_and_approved_at_and_queues_task(
|
def test_update_broadcast_message_status_stores_approved_by_and_approved_at_and_queues_task(
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker
|
mocker
|
||||||
):
|
):
|
||||||
@@ -34,7 +36,7 @@ def test_validate_and_update_broadcast_message_status_stores_approved_by_and_app
|
|||||||
sample_broadcast_service.users.append(approver)
|
sample_broadcast_service.users.append(approver)
|
||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.BROADCASTING, approver
|
broadcast_message, BroadcastStatusType.BROADCASTING, approver
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ def test_validate_and_update_broadcast_message_status_stores_approved_by_and_app
|
|||||||
assert alert_event.transmitted_content == {"body": "emergency broadcast"}
|
assert alert_event.transmitted_content == {"body": "emergency broadcast"}
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_from_admin_interface(
|
def test_update_broadcast_message_status_for_cancelling_broadcast_from_admin_interface(
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker,
|
mocker,
|
||||||
):
|
):
|
||||||
@@ -71,7 +73,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
|
|||||||
|
|
||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.CANCELLED, updating_user=canceller, api_key_id=None
|
broadcast_message, BroadcastStatusType.CANCELLED, updating_user=canceller, api_key_id=None
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -89,7 +91,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
|
|||||||
assert alert_event.message_type == BroadcastEventMessageType.CANCEL
|
assert alert_event.message_type == BroadcastEventMessageType.CANCEL
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_from_API_call(
|
def test_update_broadcast_message_status_for_cancelling_broadcast_from_API_call(
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker,
|
mocker,
|
||||||
):
|
):
|
||||||
@@ -105,7 +107,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
|
|||||||
)
|
)
|
||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.CANCELLED, updating_user=None, api_key_id=api_key.id
|
broadcast_message, BroadcastStatusType.CANCELLED, updating_user=None, api_key_id=api_key.id
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -123,7 +125,7 @@ def test_validate_and_update_broadcast_message_status_for_cancelling_broadcast_f
|
|||||||
assert alert_event.message_type == BroadcastEventMessageType.CANCEL
|
assert alert_event.message_type == BroadcastEventMessageType.CANCEL
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_via_admin_interface(
|
def test_update_broadcast_message_status_for_rejecting_broadcast_via_admin_interface(
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker
|
mocker
|
||||||
):
|
):
|
||||||
@@ -138,7 +140,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_vi
|
|||||||
)
|
)
|
||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.REJECTED, updating_user=sample_broadcast_service.created_by
|
broadcast_message, BroadcastStatusType.REJECTED, updating_user=sample_broadcast_service.created_by
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -151,7 +153,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_vi
|
|||||||
assert len(broadcast_message.events) == 0
|
assert len(broadcast_message.events) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_from_API_call(
|
def test_update_broadcast_message_status_for_rejecting_broadcast_from_API_call(
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker
|
mocker
|
||||||
):
|
):
|
||||||
@@ -167,7 +169,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_fr
|
|||||||
)
|
)
|
||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.REJECTED, api_key_id=api_key.id
|
broadcast_message, BroadcastStatusType.REJECTED, api_key_id=api_key.id
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -209,7 +211,7 @@ def test_validate_and_update_broadcast_message_status_for_rejecting_broadcast_fr
|
|||||||
(BroadcastStatusType.CANCELLED, BroadcastStatusType.BROADCASTING),
|
(BroadcastStatusType.CANCELLED, BroadcastStatusType.BROADCASTING),
|
||||||
(BroadcastStatusType.CANCELLED, BroadcastStatusType.COMPLETED),
|
(BroadcastStatusType.CANCELLED, BroadcastStatusType.COMPLETED),
|
||||||
])
|
])
|
||||||
def test_validate_and_update_broadcast_message_status_restricts_status_transitions_to_explicit_list(
|
def test_update_broadcast_message_status_restricts_status_transitions_to_explicit_list(
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker,
|
mocker,
|
||||||
current_status,
|
current_status,
|
||||||
@@ -222,14 +224,14 @@ def test_validate_and_update_broadcast_message_status_restricts_status_transitio
|
|||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
with pytest.raises(expected_exception=InvalidRequest) as e:
|
with pytest.raises(expected_exception=InvalidRequest) as e:
|
||||||
validate_and_update_broadcast_message_status(broadcast_message, new_status, approver)
|
update_broadcast_message_status(broadcast_message, new_status, approver)
|
||||||
|
|
||||||
assert mock_task.called is False
|
assert mock_task.called is False
|
||||||
assert f'from {current_status} to {new_status}' in str(e.value)
|
assert f'from {current_status} to {new_status}' in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('is_platform_admin', [True, False])
|
@pytest.mark.parametrize('is_platform_admin', [True, False])
|
||||||
def test_validate_and_update_broadcast_message_status_rejects_approval_from_creator(
|
def test_update_broadcast_message_status_rejects_approval_from_creator(
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker,
|
mocker,
|
||||||
is_platform_admin
|
is_platform_admin
|
||||||
@@ -241,7 +243,7 @@ def test_validate_and_update_broadcast_message_status_rejects_approval_from_crea
|
|||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
with pytest.raises(expected_exception=InvalidRequest) as e:
|
with pytest.raises(expected_exception=InvalidRequest) as e:
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.BROADCASTING, creator_and_approver
|
broadcast_message, BroadcastStatusType.BROADCASTING, creator_and_approver
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -249,7 +251,7 @@ def test_validate_and_update_broadcast_message_status_rejects_approval_from_crea
|
|||||||
assert 'cannot approve their own broadcast' in str(e.value)
|
assert 'cannot approve their own broadcast' in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_rejects_approval_of_broadcast_with_no_areas(
|
def test_update_broadcast_message_status_rejects_approval_of_broadcast_with_no_areas(
|
||||||
admin_request,
|
admin_request,
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker
|
mocker
|
||||||
@@ -261,13 +263,13 @@ def test_validate_and_update_broadcast_message_status_rejects_approval_of_broadc
|
|||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
with pytest.raises(expected_exception=InvalidRequest) as e:
|
with pytest.raises(expected_exception=InvalidRequest) as e:
|
||||||
validate_and_update_broadcast_message_status(broadcast, BroadcastStatusType.BROADCASTING, approver)
|
update_broadcast_message_status(broadcast, BroadcastStatusType.BROADCASTING, approver)
|
||||||
|
|
||||||
assert mock_task.called is False
|
assert mock_task.called is False
|
||||||
assert f'broadcast_message {broadcast.id} has no selected areas and so cannot be broadcasted.' in str(e.value)
|
assert f'broadcast_message {broadcast.id} has no selected areas and so cannot be broadcasted.' in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_allows_trial_mode_services_to_approve_own_message(
|
def test_update_broadcast_message_status_allows_trial_mode_services_to_approve_own_message(
|
||||||
notify_db,
|
notify_db,
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker
|
mocker
|
||||||
@@ -282,7 +284,7 @@ def test_validate_and_update_broadcast_message_status_allows_trial_mode_services
|
|||||||
creator_and_approver = sample_broadcast_service.created_by
|
creator_and_approver = sample_broadcast_service.created_by
|
||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.BROADCASTING, creator_and_approver
|
broadcast_message, BroadcastStatusType.BROADCASTING, creator_and_approver
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -298,7 +300,7 @@ def test_validate_and_update_broadcast_message_status_allows_trial_mode_services
|
|||||||
(True, False),
|
(True, False),
|
||||||
(False, True),
|
(False, True),
|
||||||
])
|
])
|
||||||
def test_validate_and_update_broadcast_message_status_when_broadcast_message_is_stubbed_or_service_not_live(
|
def test_update_broadcast_message_status_when_broadcast_message_is_stubbed_or_service_not_live(
|
||||||
admin_request,
|
admin_request,
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker,
|
mocker,
|
||||||
@@ -319,7 +321,7 @@ def test_validate_and_update_broadcast_message_status_when_broadcast_message_is_
|
|||||||
|
|
||||||
sample_broadcast_service.restricted = service_restricted_before_approval
|
sample_broadcast_service.restricted = service_restricted_before_approval
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.BROADCASTING, approver
|
broadcast_message, BroadcastStatusType.BROADCASTING, approver
|
||||||
)
|
)
|
||||||
assert broadcast_message.status == BroadcastStatusType.BROADCASTING
|
assert broadcast_message.status == BroadcastStatusType.BROADCASTING
|
||||||
@@ -331,7 +333,7 @@ def test_validate_and_update_broadcast_message_status_when_broadcast_message_is_
|
|||||||
assert len(mock_task.mock_calls) == 0
|
assert len(mock_task.mock_calls) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_validate_and_update_broadcast_message_status_creates_event_with_correct_content_if_broadcast_has_no_template(
|
def test_update_broadcast_message_status_creates_event_with_correct_content_if_broadcast_has_no_template(
|
||||||
admin_request,
|
admin_request,
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker
|
mocker
|
||||||
@@ -350,7 +352,7 @@ def test_validate_and_update_broadcast_message_status_creates_event_with_correct
|
|||||||
sample_broadcast_service.users.append(approver)
|
sample_broadcast_service.users.append(approver)
|
||||||
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
mock_task = mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
|
||||||
validate_and_update_broadcast_message_status(
|
update_broadcast_message_status(
|
||||||
broadcast_message, BroadcastStatusType.BROADCASTING, approver
|
broadcast_message, BroadcastStatusType.BROADCASTING, approver
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -362,3 +364,93 @@ def test_validate_and_update_broadcast_message_status_creates_event_with_correct
|
|||||||
mock_task.assert_called_once_with(kwargs={'broadcast_event_id': str(alert_event.id)}, queue='broadcast-tasks')
|
mock_task.assert_called_once_with(kwargs={'broadcast_event_id': str(alert_event.id)}, queue='broadcast-tasks')
|
||||||
|
|
||||||
assert alert_event.transmitted_content == {"body": "tailor made emergency broadcast content"}
|
assert alert_event.transmitted_content == {"body": "tailor made emergency broadcast content"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_broadcast_message_status_creates_zendesk_ticket(
|
||||||
|
mocker,
|
||||||
|
notify_api,
|
||||||
|
sample_broadcast_service
|
||||||
|
):
|
||||||
|
broadcast_message = create_broadcast_message(
|
||||||
|
service=sample_broadcast_service,
|
||||||
|
content='tailor made emergency broadcast content',
|
||||||
|
status=BroadcastStatusType.PENDING_APPROVAL,
|
||||||
|
areas={"names": ["England", "Scotland"], "simple_polygons": ['polygons']}
|
||||||
|
)
|
||||||
|
approver = create_user(email='approver@gov.uk')
|
||||||
|
sample_broadcast_service.users.append(approver)
|
||||||
|
|
||||||
|
mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_event.apply_async')
|
||||||
|
mock_send_ticket_to_zendesk = mocker.patch(
|
||||||
|
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
|
||||||
|
autospec=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
|
||||||
|
update_broadcast_message_status(
|
||||||
|
broadcast_message, BroadcastStatusType.BROADCASTING, approver
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_send_ticket_to_zendesk.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_p1_zendesk_alert(sample_broadcast_service, mocker, notify_api):
|
||||||
|
broadcast_message = create_broadcast_message(
|
||||||
|
service=sample_broadcast_service,
|
||||||
|
content='tailor made emergency broadcast content',
|
||||||
|
status=BroadcastStatusType.BROADCASTING,
|
||||||
|
areas={"names": ["England", "Scotland"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_send_ticket_to_zendesk = mocker.patch(
|
||||||
|
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
|
||||||
|
autospec=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
|
||||||
|
_create_p1_zendesk_alert(broadcast_message)
|
||||||
|
|
||||||
|
ticket = mock_send_ticket_to_zendesk.call_args_list[0].args[0]
|
||||||
|
assert ticket.subject == 'Live broadcast sent'
|
||||||
|
assert ticket.ticket_type == 'incident'
|
||||||
|
assert str(broadcast_message.id) in ticket.message
|
||||||
|
assert "Sent on channel severe to ['England', 'Scotland']" in ticket.message
|
||||||
|
assert 'Content starts "tailor made emergency' in ticket.message
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_p1_zendesk_alert_doesnt_alert_when_cancelling(mocker, notify_api, sample_broadcast_service):
|
||||||
|
broadcast_message = create_broadcast_message(
|
||||||
|
service=sample_broadcast_service,
|
||||||
|
content='tailor made emergency broadcast content',
|
||||||
|
status=BroadcastStatusType.CANCELLED,
|
||||||
|
areas={"names": ["England", "Scotland"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_send_ticket_to_zendesk = mocker.patch(
|
||||||
|
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
|
||||||
|
autospec=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
|
||||||
|
_create_p1_zendesk_alert(broadcast_message)
|
||||||
|
|
||||||
|
mock_send_ticket_to_zendesk.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_p1_zendesk_alert_doesnt_alert_on_staging(mocker, notify_api, sample_broadcast_service):
|
||||||
|
broadcast_message = create_broadcast_message(
|
||||||
|
service=sample_broadcast_service,
|
||||||
|
content='tailor made emergency broadcast content',
|
||||||
|
status=BroadcastStatusType.BROADCASTING,
|
||||||
|
areas={"names": ["England", "Scotland"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_send_ticket_to_zendesk = mocker.patch(
|
||||||
|
'app.broadcast_message.utils.zendesk_client.send_ticket_to_zendesk',
|
||||||
|
autospec=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'staging'):
|
||||||
|
_create_p1_zendesk_alert(broadcast_message)
|
||||||
|
|
||||||
|
mock_send_ticket_to_zendesk.assert_not_called()
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ from unittest.mock import ANY, Mock, call
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from celery.exceptions import Retry
|
from celery.exceptions import Retry
|
||||||
from flask import current_app
|
|
||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
from notifications_utils.clients.zendesk.zendesk_client import (
|
|
||||||
NotifySupportTicket,
|
|
||||||
)
|
|
||||||
|
|
||||||
from app.celery.broadcast_message_tasks import (
|
from app.celery.broadcast_message_tasks import (
|
||||||
BroadcastIntegrityError,
|
BroadcastIntegrityError,
|
||||||
@@ -41,10 +37,6 @@ def test_send_broadcast_event_queues_up_for_active_providers(mocker, notify_api,
|
|||||||
|
|
||||||
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
|
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
|
||||||
|
|
||||||
mock_send_ticket_to_zendesk = mocker.patch(
|
|
||||||
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
|
|
||||||
autospec=True,
|
|
||||||
)
|
|
||||||
mock_send_broadcast_provider_message = mocker.patch(
|
mock_send_broadcast_provider_message = mocker.patch(
|
||||||
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
|
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
|
||||||
)
|
)
|
||||||
@@ -57,9 +49,6 @@ def test_send_broadcast_event_queues_up_for_active_providers(mocker, notify_api,
|
|||||||
call(kwargs={'broadcast_event_id': event.id, 'provider': 'vodafone'}, queue='broadcast-tasks')
|
call(kwargs={'broadcast_event_id': event.id, 'provider': 'vodafone'}, queue='broadcast-tasks')
|
||||||
]
|
]
|
||||||
|
|
||||||
# we're on test env so this isn't called
|
|
||||||
assert mock_send_ticket_to_zendesk.called is False
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('message_status', [
|
@pytest.mark.parametrize('message_status', [
|
||||||
BroadcastStatusType.BROADCASTING,
|
BroadcastStatusType.BROADCASTING,
|
||||||
@@ -71,10 +60,6 @@ def test_send_broadcast_event_calls_publish_govuk_alerts_task(
|
|||||||
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
|
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
|
||||||
broadcast_message = create_broadcast_message(template, status=message_status)
|
broadcast_message = create_broadcast_message(template, status=message_status)
|
||||||
event = create_broadcast_event(broadcast_message)
|
event = create_broadcast_event(broadcast_message)
|
||||||
mocker.patch(
|
|
||||||
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
|
|
||||||
autospec=True,
|
|
||||||
)
|
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
|
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
|
||||||
)
|
)
|
||||||
@@ -137,104 +122,6 @@ def test_send_broadcast_event_does_nothing_if_provider_set_on_service_isnt_enabl
|
|||||||
assert mock_send_broadcast_provider_message.apply_async.called is False
|
assert mock_send_broadcast_provider_message.apply_async.called is False
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('area_data,expected_message', [
|
|
||||||
({'names': ['England', 'Scotland']}, ['England', 'Scotland']),
|
|
||||||
({}, [])
|
|
||||||
])
|
|
||||||
def test_send_broadcast_event_creates_zendesk(
|
|
||||||
area_data,
|
|
||||||
expected_message,
|
|
||||||
mocker,
|
|
||||||
notify_api,
|
|
||||||
sample_broadcast_service
|
|
||||||
):
|
|
||||||
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
|
|
||||||
broadcast_message = create_broadcast_message(
|
|
||||||
template,
|
|
||||||
status=BroadcastStatusType.BROADCASTING,
|
|
||||||
areas={**area_data, 'simple_polygons': []},
|
|
||||||
)
|
|
||||||
event = create_broadcast_event(broadcast_message)
|
|
||||||
mock_create_ticket = mocker.spy(NotifySupportTicket, '__init__')
|
|
||||||
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
|
|
||||||
|
|
||||||
mock_send_ticket_to_zendesk = mocker.patch(
|
|
||||||
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
|
|
||||||
autospec=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_provider_message')
|
|
||||||
|
|
||||||
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
|
|
||||||
send_broadcast_event(event.id)
|
|
||||||
|
|
||||||
mock_create_ticket.assert_called_once_with(
|
|
||||||
ANY,
|
|
||||||
subject='Live broadcast sent',
|
|
||||||
message=ANY,
|
|
||||||
ticket_type='incident',
|
|
||||||
technical_ticket=True,
|
|
||||||
org_id=current_app.config['BROADCAST_ORGANISATION_ID'],
|
|
||||||
org_type='central',
|
|
||||||
service_id=str(sample_broadcast_service.id)
|
|
||||||
)
|
|
||||||
ticket_message = mock_create_ticket.call_args_list[0][1]['message']
|
|
||||||
|
|
||||||
assert str(broadcast_message.id) in ticket_message
|
|
||||||
assert 'channel severe' in ticket_message
|
|
||||||
assert f"areas {expected_message}" in ticket_message
|
|
||||||
# the start of the content from the broadcast template
|
|
||||||
assert "Dear Sir/Madam" in ticket_message
|
|
||||||
|
|
||||||
mock_send_ticket_to_zendesk.assert_called_once()
|
|
||||||
|
|
||||||
|
|
||||||
def test_send_broadcast_event_doesnt_create_zendesk_when_cancelling(mocker, notify_api, sample_broadcast_service):
|
|
||||||
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
|
|
||||||
broadcast_message = create_broadcast_message(
|
|
||||||
template,
|
|
||||||
status=BroadcastStatusType.BROADCASTING,
|
|
||||||
areas={'areas': ['wd20-S13002775', 'wd20-S13002773'], 'simple_polygons': []},
|
|
||||||
)
|
|
||||||
create_broadcast_event(broadcast_message, message_type=BroadcastEventMessageType.ALERT)
|
|
||||||
cancel_event = create_broadcast_event(broadcast_message, message_type=BroadcastEventMessageType.CANCEL)
|
|
||||||
|
|
||||||
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
|
|
||||||
|
|
||||||
mock_send_ticket_to_zendesk = mocker.patch(
|
|
||||||
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
|
|
||||||
autospec=True,
|
|
||||||
)
|
|
||||||
mocker.patch('app.celery.broadcast_message_tasks.send_broadcast_provider_message')
|
|
||||||
|
|
||||||
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'live'):
|
|
||||||
send_broadcast_event(cancel_event.id)
|
|
||||||
|
|
||||||
assert mock_send_ticket_to_zendesk.called is False
|
|
||||||
|
|
||||||
|
|
||||||
def test_send_broadcast_event_doesnt_create_zendesk_on_staging(mocker, notify_api, sample_broadcast_service):
|
|
||||||
template = create_template(sample_broadcast_service, BROADCAST_TYPE)
|
|
||||||
broadcast_message = create_broadcast_message(template, status=BroadcastStatusType.BROADCASTING)
|
|
||||||
event = create_broadcast_event(broadcast_message)
|
|
||||||
|
|
||||||
mocker.patch('app.celery.broadcast_message_tasks.notify_celery.send_task')
|
|
||||||
|
|
||||||
mock_send_ticket_to_zendesk = mocker.patch(
|
|
||||||
'app.celery.broadcast_message_tasks.zendesk_client.send_ticket_to_zendesk',
|
|
||||||
autospec=True,
|
|
||||||
)
|
|
||||||
mock_send_broadcast_provider_message = mocker.patch(
|
|
||||||
'app.celery.broadcast_message_tasks.send_broadcast_provider_message',
|
|
||||||
)
|
|
||||||
|
|
||||||
with set_config(notify_api, 'NOTIFY_ENVIRONMENT', 'staging'):
|
|
||||||
send_broadcast_event(event.id)
|
|
||||||
|
|
||||||
assert mock_send_broadcast_provider_message.apply_async.called is True
|
|
||||||
assert mock_send_ticket_to_zendesk.called is False
|
|
||||||
|
|
||||||
|
|
||||||
@freeze_time('2020-08-01 12:00')
|
@freeze_time('2020-08-01 12:00')
|
||||||
@pytest.mark.parametrize('provider,provider_capitalised', [
|
@pytest.mark.parametrize('provider,provider_capitalised', [
|
||||||
['ee', 'EE'],
|
['ee', 'EE'],
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ def test_valid_post_cap_xml_broadcast_returns_201(
|
|||||||
[True, "cancelled"],
|
[True, "cancelled"],
|
||||||
[False, "rejected"]
|
[False, "rejected"]
|
||||||
])
|
])
|
||||||
def test_valid_cancel_broadcast_request_calls_validate_and_update_broadcast_message_status_and_returns_201(
|
def test_valid_cancel_broadcast_request_calls_update_broadcast_message_status_and_returns_201(
|
||||||
client,
|
client,
|
||||||
sample_broadcast_service,
|
sample_broadcast_service,
|
||||||
mocker,
|
mocker,
|
||||||
@@ -153,7 +153,9 @@ def test_valid_cancel_broadcast_request_calls_validate_and_update_broadcast_mess
|
|||||||
if is_approved:
|
if is_approved:
|
||||||
broadcast_message.status = 'broadcasting'
|
broadcast_message.status = 'broadcasting'
|
||||||
|
|
||||||
mock_update = mocker.patch('app.v2.broadcast.post_broadcast.validate_and_update_broadcast_message_status')
|
mock_update = mocker.patch(
|
||||||
|
'app.v2.broadcast.post_broadcast.broadcast_utils.update_broadcast_message_status'
|
||||||
|
)
|
||||||
|
|
||||||
# cancel broadcast
|
# cancel broadcast
|
||||||
response_for_cancel = client.post(
|
response_for_cancel = client.post(
|
||||||
|
|||||||
Reference in New Issue
Block a user