mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-22 16:31:15 -05:00
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.
144 lines
4.9 KiB
Python
144 lines
4.9 KiB
Python
from itertools import chain
|
||
|
||
from flask import current_app, jsonify, request
|
||
from notifications_utils.polygons import Polygons
|
||
from notifications_utils.template import BroadcastMessageTemplate
|
||
from sqlalchemy.orm.exc import NoResultFound
|
||
|
||
from app import api_user, authenticated_service
|
||
from app.broadcast_message.rest import (
|
||
validate_and_update_broadcast_message_status,
|
||
)
|
||
from app.broadcast_message.translators import cap_xml_to_dict
|
||
from app.dao.broadcast_message_dao import (
|
||
dao_get_broadcast_message_by_references_and_service_id,
|
||
)
|
||
from app.dao.dao_utils import dao_save_object
|
||
from app.models import BROADCAST_TYPE, BroadcastMessage, BroadcastStatusType
|
||
from app.notifications.validators import check_service_has_permission
|
||
from app.schema_validation import validate
|
||
from app.v2.broadcast import v2_broadcast_blueprint
|
||
from app.v2.broadcast.broadcast_schemas import post_broadcast_schema
|
||
from app.v2.errors import BadRequestError, ValidationError
|
||
from app.xml_schemas import validate_xml
|
||
|
||
|
||
@v2_broadcast_blueprint.route("", methods=['POST'])
|
||
def create_broadcast():
|
||
|
||
check_service_has_permission(
|
||
BROADCAST_TYPE,
|
||
authenticated_service.permissions,
|
||
)
|
||
|
||
if request.content_type != 'application/cap+xml':
|
||
raise BadRequestError(
|
||
message=f'Content type {request.content_type} not supported',
|
||
status_code=415,
|
||
)
|
||
|
||
cap_xml = request.get_data()
|
||
|
||
if not validate_xml(cap_xml, 'CAP-v1.2.xsd'):
|
||
raise BadRequestError(
|
||
message='Request data is not valid CAP XML',
|
||
status_code=400,
|
||
)
|
||
broadcast_json = cap_xml_to_dict(cap_xml)
|
||
|
||
validate(broadcast_json, post_broadcast_schema)
|
||
|
||
if broadcast_json["msgType"] == "Cancel":
|
||
broadcast_message = _cancel_or_reject_broadcast(
|
||
broadcast_json["references"].split(","),
|
||
authenticated_service.id
|
||
)
|
||
return jsonify(broadcast_message.serialize()), 201
|
||
|
||
else:
|
||
_validate_template(broadcast_json)
|
||
|
||
polygons = Polygons(list(chain.from_iterable((
|
||
[
|
||
[[y, x] for x, y in polygon]
|
||
for polygon in area['polygons']
|
||
] for area in broadcast_json['areas']
|
||
))))
|
||
|
||
if len(polygons) > 12 or polygons.point_count > 250:
|
||
simple_polygons = polygons.smooth.simplify
|
||
else:
|
||
simple_polygons = polygons
|
||
|
||
broadcast_message = BroadcastMessage(
|
||
service_id=authenticated_service.id,
|
||
content=broadcast_json['content'],
|
||
reference=broadcast_json['reference'],
|
||
cap_event=broadcast_json['cap_event'],
|
||
areas={
|
||
'names': [
|
||
area['name'] for area in broadcast_json['areas']
|
||
],
|
||
'simple_polygons': simple_polygons.as_coordinate_pairs_lat_long,
|
||
},
|
||
status=BroadcastStatusType.PENDING_APPROVAL,
|
||
api_key_id=api_user.id,
|
||
stubbed=authenticated_service.restricted
|
||
# The client may pass in broadcast_json['expires'] but it’s
|
||
# simpler for now to ignore it and have the rules around expiry
|
||
# for broadcasts created with the API match those created from
|
||
# the admin app
|
||
)
|
||
|
||
dao_save_object(broadcast_message)
|
||
|
||
current_app.logger.info(
|
||
f'Broadcast message {broadcast_message.id} created for service '
|
||
f'{authenticated_service.id} with reference {broadcast_json["reference"]}'
|
||
)
|
||
|
||
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):
|
||
template = BroadcastMessageTemplate.from_content(
|
||
broadcast_json['content']
|
||
)
|
||
|
||
if template.content_too_long:
|
||
raise ValidationError(
|
||
message=(
|
||
f'description must be {template.max_content_count:,.0f} '
|
||
f'characters or fewer'
|
||
) + (
|
||
' (because it could not be GSM7 encoded)'
|
||
if template.non_gsm_characters else ''
|
||
),
|
||
status_code=400,
|
||
)
|