Return 404 if reference from cancel message does not match

If the reference from cancel CAP XML we received via API does not
match with any existing broadcast, return 404.

Do the same if service id doesn't match.

Also refactor code to cancel broadcast out into separate function

It should be a separate function that is only called by create_broadcast
function. This will prevent create_broadcast from becoming too
big and complex and doing too many things.
This commit is contained in:
Pea Tyczynska
2022-01-14 16:46:20 +00:00
parent 3b4a9d8942
commit a4c20e8ba6
4 changed files with 65 additions and 17 deletions

View File

@@ -24,9 +24,10 @@ def dao_get_broadcast_message_by_id_and_service_id(broadcast_message_id, service
).one() ).one()
def dao_get_broadcast_message_by_references(references_to_original_broadcast): def dao_get_broadcast_message_by_references_and_service_id(references_to_original_broadcast, service_id):
return BroadcastMessage.query.filter( return BroadcastMessage.query.filter(
BroadcastMessage.reference.in_(references_to_original_broadcast), BroadcastMessage.reference.in_(references_to_original_broadcast),
BroadcastMessage.service_id == service_id
).one() ).one()

View File

@@ -3,6 +3,7 @@ from itertools import chain
from flask import current_app, jsonify, request from flask import current_app, jsonify, request
from notifications_utils.polygons import Polygons from notifications_utils.polygons import Polygons
from notifications_utils.template import BroadcastMessageTemplate from notifications_utils.template import BroadcastMessageTemplate
from sqlalchemy.orm.exc import NoResultFound
from app import api_user, authenticated_service from app import api_user, authenticated_service
from app.broadcast_message.rest import ( from app.broadcast_message.rest import (
@@ -10,7 +11,7 @@ from app.broadcast_message.rest import (
) )
from app.broadcast_message.translators import cap_xml_to_dict from app.broadcast_message.translators import cap_xml_to_dict
from app.dao.broadcast_message_dao import ( from app.dao.broadcast_message_dao import (
dao_get_broadcast_message_by_references, dao_get_broadcast_message_by_references_and_service_id,
) )
from app.dao.dao_utils import dao_save_object from app.dao.dao_utils import dao_save_object
from app.models import BROADCAST_TYPE, BroadcastMessage, BroadcastStatusType from app.models import BROADCAST_TYPE, BroadcastMessage, BroadcastStatusType
@@ -48,18 +49,9 @@ def create_broadcast():
validate(broadcast_json, post_broadcast_schema) validate(broadcast_json, post_broadcast_schema)
if broadcast_json["msgType"] == "Cancel": if broadcast_json["msgType"] == "Cancel":
references_to_original_broadcast = broadcast_json["references"].split(",") broadcast_message = _cancel_or_reject_broadcast(
broadcast_message = dao_get_broadcast_message_by_references(references_to_original_broadcast) broadcast_json["references"].split(","),
# do we need to check if service is active? authenticated_service.id
if broadcast_message.status == BroadcastStatusType.PENDING_APPROVAL:
new_status = BroadcastStatusType.REJECTED
else:
new_status = BroadcastStatusType.CANCELLED
validate_and_update_broadcast_message_status(
broadcast_message,
new_status,
updating_user=None,
from_api=True
) )
return jsonify(broadcast_message.serialize()), 201 return jsonify(broadcast_message.serialize()), 201
@@ -108,6 +100,31 @@ def create_broadcast():
return jsonify(broadcast_message.serialize()), 201 return jsonify(broadcast_message.serialize()), 201
def _cancel_or_reject_broadcast(references_to_original_broadcast, service_id):
try:
broadcast_message = dao_get_broadcast_message_by_references_and_service_id(
references_to_original_broadcast,
service_id
)
except NoResultFound:
raise BadRequestError(
message="Broadcast message reference and service id didn't match with any existing broadcasts",
status_code=404,
)
# do we need to check if service is active?
if broadcast_message.status == BroadcastStatusType.PENDING_APPROVAL:
new_status = BroadcastStatusType.REJECTED
else:
new_status = BroadcastStatusType.CANCELLED
validate_and_update_broadcast_message_status(
broadcast_message,
new_status,
updating_user=None,
from_api=True
)
return broadcast_message
def _validate_template(broadcast_json): def _validate_template(broadcast_json):
template = BroadcastMessageTemplate.from_content( template = BroadcastMessageTemplate.from_content(
broadcast_json['content'] broadcast_json['content']

View File

@@ -189,7 +189,7 @@ WITH_PLACEHOLDER_FOR_CONTENT = """
WINDEMERE = """ WINDEMERE = """
<alert xmlns="urn:oasis:names:tc:emergency:cap:1.2"> <alert xmlns="urn:oasis:names:tc:emergency:cap:1.2">
<identifier>50385fcb0ab7aa447bbd46d848ce8466E</identifier> <identifier>4f6d28b10ab7aa447bbd46d85f1e9effE</identifier>
<sender>www.gov.uk/environment-agency</sender> <sender>www.gov.uk/environment-agency</sender>
<sent>2020-02-16T23:01:13-00:00</sent> <sent>2020-02-16T23:01:13-00:00</sent>
<status>Actual</status> <status>Actual</status>

View File

@@ -205,8 +205,38 @@ def test_valid_cancel_broadcast_request_cancels_active_alert_and_returns_201(
assert broadcast_message.updated_at is not None assert broadcast_message.updated_at is not None
def test_cancel_request_does_not_cancel_broadcast_if_reference_does_not_match(): def test_cancel_request_does_not_cancel_broadcast_if_reference_does_not_match(
pass client,
sample_broadcast_service
):
auth_header = create_service_authorization_header(service_id=sample_broadcast_service.id)
# create a broadcast
response_for_create = client.post(
path='/v2/broadcast',
data=sample_cap_xml_documents.WINDEMERE,
headers=[('Content-Type', 'application/cap+xml'), auth_header],
)
assert response_for_create.status_code == 201
response_json_for_create = json.loads(response_for_create.get_data(as_text=True))
assert response_json_for_create['cancelled_at'] is None
assert response_json_for_create['cancelled_by_id'] is None
assert response_json_for_create['reference'] == '4f6d28b10ab7aa447bbd46d85f1e9effE'
assert response_json_for_create['status'] == 'pending-approval'
# try to cancel broadcast, but reference doesn't match
response_for_cancel = client.post(
path='/v2/broadcast',
data=sample_cap_xml_documents.WAINFLEET_CANCEL,
headers=[('Content-Type', 'application/cap+xml'), auth_header],
)
assert response_for_cancel.status_code == 404
response = json.loads(response_for_cancel.get_data(as_text=True))
expected_error_message = "Broadcast message reference and service id didn't match with any existing broadcasts"
assert response["errors"][0]["message"] == expected_error_message
def test_large_polygon_is_simplified( def test_large_polygon_is_simplified(