mirror of
https://github.com/GSA/notifications-api.git
synced 2026-07-28 19:59:47 -04:00
We don’t store everything that comes in the CAP XML when someone creates a broadcast via the API. One thing we do store is `<identifier>` (in a column called `reference`) which is a unique (to the external system) identifier for the broadcast. We show this in the front end instead of the template name, because broadcasts created from the API don’t use templates. However this ID isn’t very friendly – the Environment Agency just supply a UUID. The Environment Agency also populate the `<event>` field with some human readable text, for example: > 013 Issue Severe Flood Warning EA (013 is an area code which will be meaningful to the Flood Warning Service team) We should show this in the UI instead of the reference. The first step towards this is storing it in the database and returning it in the REST endpoints. Later we can have the admin app prefer `cap_event` over `reference`, where `cap_event` is present. We can’t backfill this data because we don’t keep a copy of the original XML. Seems like `<event>` is a mandatory property of `<info>`, so we don’t need to worry about the field being missing (`<info>` is optional in CAP but we require it because it contains stuff like the areas which we need in order to send out the broadcast`). *** https://www.pivotaltracker.com/story/show/176927060
96 lines
3.2 KiB
Python
96 lines
3.2 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 app import api_user, authenticated_service
|
||
from app.broadcast_message.translators import cap_xml_to_dict
|
||
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)
|
||
_validate_template(broadcast_json)
|
||
|
||
polygons = Polygons(list(chain.from_iterable((
|
||
area['polygons'] for area in broadcast_json['areas']
|
||
))))
|
||
|
||
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': polygons.smooth.simplify.as_coordinate_pairs_long_lat,
|
||
},
|
||
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 _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,
|
||
)
|