diff --git a/app/__init__.py b/app/__init__.py index 7169dcd27..dd4d89b2c 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -251,6 +251,7 @@ def register_v2_blueprints(application): from app.v2.template.get_template import v2_template_blueprint as get_template from app.v2.templates.get_templates import v2_templates_blueprint as get_templates from app.v2.template.post_template import v2_template_blueprint as post_template + from app.v2.broadcast.post_broadcast import v2_broadcast_blueprint as post_broadcast from app.authentication.auth import requires_auth post_notifications.before_request(requires_auth) @@ -271,6 +272,9 @@ def register_v2_blueprints(application): get_inbound_sms.before_request(requires_auth) application.register_blueprint(get_inbound_sms) + post_broadcast.before_request(requires_auth) + application.register_blueprint(post_broadcast) + def init_app(app): diff --git a/app/v2/broadcast/__init__.py b/app/v2/broadcast/__init__.py new file mode 100644 index 000000000..0a2b29987 --- /dev/null +++ b/app/v2/broadcast/__init__.py @@ -0,0 +1,10 @@ +from flask import Blueprint +from app.v2.errors import register_errors + +v2_broadcast_blueprint = Blueprint( + "v2_broadcast_blueprint", + __name__, + url_prefix='/v2/broadcast', +) + +register_errors(v2_broadcast_blueprint) diff --git a/app/v2/broadcast/post_broadcast.py b/app/v2/broadcast/post_broadcast.py new file mode 100644 index 000000000..a6d5adc2a --- /dev/null +++ b/app/v2/broadcast/post_broadcast.py @@ -0,0 +1,37 @@ +from flask import jsonify, request +from app import authenticated_service, api_user +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 +from app.v2.broadcast import v2_broadcast_blueprint + + +@v2_broadcast_blueprint.route("", methods=['POST']) +def create_broadcast(): + + check_service_has_permission( + BROADCAST_TYPE, + authenticated_service.permissions, + ) + + request_json = request.get_json() + + broadcast_message = BroadcastMessage( + service_id=authenticated_service.id, + content=request_json['content'], + reference=request_json['reference'], + areas={ + "areas": [], + "simple_polygons": request_json['polygons'], + }, + status=BroadcastStatusType.PENDING_APPROVAL, + api_key_id=api_user.id, + # 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) + + return jsonify(broadcast_message.serialize()), 201 diff --git a/tests/app/v2/broadcast/__init__.py b/tests/app/v2/broadcast/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/app/v2/broadcast/test_post_broadcast.py b/tests/app/v2/broadcast/test_post_broadcast.py new file mode 100644 index 000000000..439d749ab --- /dev/null +++ b/tests/app/v2/broadcast/test_post_broadcast.py @@ -0,0 +1,65 @@ +from flask import json +from freezegun import freeze_time +from tests import create_authorization_header +from unittest.mock import ANY + + +def test_broadcast_for_service_without_permission_returns_400( + client, + sample_service, +): + auth_header = create_authorization_header(service_id=sample_service.id) + response = client.post( + path='/v2/broadcast', + data='', + headers=[('Content-Type', 'application/json'), auth_header], + ) + + assert response.status_code == 400 + assert response.get_json()['errors'][0]['message'] == ( + 'Service is not allowed to send broadcast messages' + ) + + +def test_valid_post_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=json.dumps({ + 'content': 'This is a test', + 'reference': 'abc123', + 'polygons': [[ + [1, 2], [3, 4], [5, 6], + ]], + }), + headers=[('Content-Type', 'application/json'), 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'] == [] + assert response_json['cancelled_at'] == None + assert response_json['cancelled_by_id'] == None + assert response_json['content'] == 'This is a test' + assert response_json['reference'] == 'abc123' + 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 response_json['simple_polygons'] == [[[1, 2], [3, 4], [5, 6]]] + 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