From 38f07db23ec7d66c06e7e4e8ef44269f86bfd18e Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Mon, 18 Jan 2021 10:01:46 +0000 Subject: [PATCH] Accept CAP XML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit makes the existing endpoint also accept CAP XML, should the appropriate `Content-Type` header be set. It uses the translation code we added in a previous commit to convert the CAP to a dict. We can then validate that dict against with the JSON schema to ensure it’s something we can work with. --- app/v2/broadcast/post_broadcast.py | 13 ++++- .../v2/broadcast/sample_cap_xml_documents.py | 34 +++++++++++++ tests/app/v2/broadcast/test_post_broadcast.py | 49 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 tests/app/v2/broadcast/sample_cap_xml_documents.py diff --git a/app/v2/broadcast/post_broadcast.py b/app/v2/broadcast/post_broadcast.py index 09eed0e81..a1b483808 100644 --- a/app/v2/broadcast/post_broadcast.py +++ b/app/v2/broadcast/post_broadcast.py @@ -1,6 +1,7 @@ from itertools import chain from flask import jsonify, request from app import authenticated_service, api_user +from app.broadcast_message.translators import cap_xml_to_dict from app.dao.dao_utils import dao_save_object from app.notifications.validators import check_service_has_permission from app.models import BROADCAST_TYPE, BroadcastMessage, BroadcastStatusType @@ -17,7 +18,17 @@ def create_broadcast(): authenticated_service.permissions, ) - broadcast_json = validate(request.get_json(), post_broadcast_schema) + if request.content_type == 'application/json': + broadcast_json = request.get_json() + elif request.content_type == 'application/cap+xml': + broadcast_json = cap_xml_to_dict(request.get_data(as_text=True)) + else: + raise BadRequestError( + message=f'Content type {request.content_type} not supported', + status_code=400, + ) + + validate(broadcast_json, post_broadcast_schema) broadcast_message = BroadcastMessage( service_id=authenticated_service.id, diff --git a/tests/app/v2/broadcast/sample_cap_xml_documents.py b/tests/app/v2/broadcast/sample_cap_xml_documents.py new file mode 100644 index 000000000..86dba8bd3 --- /dev/null +++ b/tests/app/v2/broadcast/sample_cap_xml_documents.py @@ -0,0 +1,34 @@ +WAINFLEET = """ + + 50385fcb0ab7aa447bbd46d848ce8466E + www.gov.uk/environment-agency + 2020-02-16T23:01:13-00:00 + Actual + Update + Flood warning service + Public + www.gov.uk/environment-agency,4f6d28b10ab7aa447bbd46d85f1e9effE,2020-02-16T19:20:03+00:00 + + en-GB + Met + 053/055 Issue Severe Flood Warning EA + Immediate + Severe + Likely + 2020-02-26T23:01:14-00:00 + Environment Agency + A severe flood warning has been issued. Storm Dennis has resulted in significant rainfall in the Steeping River catchment with several bands of heavy rain passing through the area during today (Sunday 16 Feb). River levels along the Steeping River and the Wainfleet relief channel are expected to be similar to those in June 2019. This could result in flood embankments being overtopped. Should this happen, there is an increased risk that flood embankments could breach. It is expected that peak levels along the Steeping River in Wainfleet will be between midnight and 3am tonight. A multi-agency meeting is taking place this evening. Further messages will be issued should this be required. Do not walk on flood embankments and avoid riverside paths. Our staff are out in the area to check the flood defences and assist the emergency services and council. We will be closely monitoring the situation throughout the night. + # To check the latest information for your area - Visit [GOV.UK](https://flood-warning-information.service.gov.uk) to see the current flood warnings, view river and sea levels or check the 5-day flood risk forecast: https://flood-warning-information.service.gov.uk - Follow [@EnvAgency](https://twitter.com/EnvAgency) and [#floodaware](https://twitter.com/hashtag/floodaware) on Twitter. - Tune into weather, news and travel bulletins on local television and radio. - For access to flood warning information offline call Floodline on 0345 988 1188 using quickdial code: 307052. # What you should consider doing now - Call 999 if you are in immediate danger. - Co-operate with the emergency services and evacuate your property if told to do so. Most evacuation centres will let you bring your pets. - Act on your flood plan if you have one. - Move your family and pets to a safe place with a means of escape. - Use flood protection equipment (such as flood barriers, air brick covers and pumps) to protect your property. Unless you have proper equipment do not waste valuable time trying to keep the water out. - Move important items upstairs or to a safe place in your property, starting with cherished items of personal value that you will not be able to replace (such as family photographs). Next move valuables (such as computers), movable furniture and furnishings. - You may need to leave your property, so pack a bag with enough items for a few nights away. Include essential items including a torch with spare batteries, mobile phone and charger, warm clothes, home insurance information, water, food, first aid kit and any prescription medicines or baby care items you may need. - Turn off gas, electricity and water mains supplies before flood water starts to enter your property. Never touch an electrical switch if you are standing in water. - If it is safe to do so, make sure neighbours are aware of the situation and offer help to anyone who may need it. - Avoid walking, cycling or driving through flood water - 30 cm of fast-flowing water can move a car and 6 inches can knock an adult off their feet. - Flood water is dangerous and may be polluted. Wash your hands thoroughly if you’ve been in contact with it. ##### Businesses - Act on your business flood plan if you have one. - Move your staff and customers to a safe place with a means of escape. - Move stock and other valuable items upstairs or to a safe place in your building. For media enquiries please contact our media teams: https://www.gov.uk/government/organisations/environment-agency/about/media-enquiries + https://flood-warning-information.service.gov.uk + 0345 988 1188 + + River Steeping in Wainfleet All Saints + 53.10569,0.24453 53.10593,0.24430 53.10601,0.24375 53.10615,0.24349 53.10629,0.24356 53.10656,0.24336 53.10697,0.24354 53.10684,0.24298 53.10694,0.24264 53.10721,0.24302 53.10752,0.24310 53.10777,0.24308 53.10805,0.24320 53.10803,0.24187 53.10776,0.24085 53.10774,0.24062 53.10702,0.24056 53.10679,0.24088 53.10658,0.24071 53.10651,0.24049 53.10656,0.24022 53.10642,0.24022 53.10632,0.24052 53.10629,0.24082 53.10612,0.24093 53.10583,0.24133 53.10564,0.24178 53.10541,0.24282 53.10569,0.24453 + + TargetAreaCode + 053FWFSTEEP4 + + + + +""" diff --git a/tests/app/v2/broadcast/test_post_broadcast.py b/tests/app/v2/broadcast/test_post_broadcast.py index 944e58ed5..587fe489b 100644 --- a/tests/app/v2/broadcast/test_post_broadcast.py +++ b/tests/app/v2/broadcast/test_post_broadcast.py @@ -2,6 +2,7 @@ from flask import json from freezegun import freeze_time from tests import create_authorization_header from unittest.mock import ANY +from . import sample_cap_xml_documents def test_broadcast_for_service_without_permission_returns_400( @@ -73,3 +74,51 @@ def test_valid_post_broadcast_returns_201( assert response_json['template_name'] is None assert response_json['template_version'] is None assert response_json['updated_at'] is None + + +def test_valid_post_cap_xml_broadcast_returns_201( + client, + sample_broadcast_service, +): + auth_header = create_authorization_header(service_id=sample_broadcast_service.id) + + response = client.post( + path='/v2/broadcast', + data=sample_cap_xml_documents.WAINFLEET, + headers=[('Content-Type', 'application/cap+xml'), auth_header], + ) + + assert response.status_code == 201 + + response_json = json.loads(response.get_data(as_text=True)) + + assert response_json['approved_at'] is None + assert response_json['approved_by_id'] == None + assert response_json['areas'] == [ + 'River Steeping in Wainfleet All Saints' + ] + assert response_json['cancelled_at'] == None + assert response_json['cancelled_by_id'] == None + assert response_json['content'].startswith( + 'A severe flood warning has been issued. Storm Dennis' + ) + assert response_json['content'].endswith( + 'closely monitoring the situation throughout the night. ' + ) + assert response_json['reference'] == '50385fcb0ab7aa447bbd46d848ce8466E' + assert response_json['created_at'] # datetime generated by the DB so can’t freeze it + assert response_json['created_by_id'] == None + assert response_json['finishes_at'] is None + assert response_json['id'] == ANY + assert response_json['personalisation'] is None + assert response_json['service_id'] == str(sample_broadcast_service.id) + assert len(response_json['simple_polygons']) == 1 + assert len(response_json['simple_polygons'][0]) == 29 + assert response_json['simple_polygons'][0][0] == [53.10569, 0.24453] + assert response_json['simple_polygons'][0][-1] == [53.10569, 0.24453] + assert response_json['starts_at'] is None + assert response_json['status'] == 'pending-approval' + assert response_json['template_id'] is None + assert response_json['template_name'] is None + assert response_json['template_version'] is None + assert response_json['updated_at'] is None