mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-21 07:51:13 -05:00
This is because that function is used both when broadcast status is updated via API and via admin, so it's a shared resource. Also move and update tests for updating broadcast message status so things are tested at source and repetition is avoided.
168 lines
6.5 KiB
Python
168 lines
6.5 KiB
Python
import iso8601
|
|
from flask import Blueprint, jsonify, request
|
|
from notifications_utils.template import BroadcastMessageTemplate
|
|
|
|
from app.broadcast_message.broadcast_message_schema import (
|
|
create_broadcast_message_schema,
|
|
update_broadcast_message_schema,
|
|
update_broadcast_message_status_schema,
|
|
)
|
|
from app.broadcast_message.utils import (
|
|
validate_and_update_broadcast_message_status,
|
|
)
|
|
from app.dao.broadcast_message_dao import (
|
|
dao_get_broadcast_message_by_id_and_service_id,
|
|
dao_get_broadcast_messages_for_service,
|
|
)
|
|
from app.dao.dao_utils import dao_save_object
|
|
from app.dao.services_dao import dao_fetch_service_by_id
|
|
from app.dao.templates_dao import dao_get_template_by_id_and_service_id
|
|
from app.dao.users_dao import get_user_by_id
|
|
from app.errors import InvalidRequest, register_errors
|
|
from app.models import BroadcastMessage, BroadcastStatusType
|
|
from app.schema_validation import validate
|
|
|
|
broadcast_message_blueprint = Blueprint(
|
|
'broadcast_message',
|
|
__name__,
|
|
url_prefix='/service/<uuid:service_id>/broadcast-message'
|
|
)
|
|
register_errors(broadcast_message_blueprint)
|
|
|
|
|
|
def _parse_nullable_datetime(dt):
|
|
if dt:
|
|
return iso8601.parse_date(dt).replace(tzinfo=None)
|
|
return dt
|
|
|
|
|
|
@broadcast_message_blueprint.route('', methods=['GET'])
|
|
def get_broadcast_messages_for_service(service_id):
|
|
# TODO: should this return template content/data in some way? or can we rely on them being cached admin side.
|
|
# we might need stuff like template name for showing on the dashboard.
|
|
# TODO: should this paginate or filter on dates or anything?
|
|
broadcast_messages = [o.serialize() for o in dao_get_broadcast_messages_for_service(service_id)]
|
|
return jsonify(broadcast_messages=broadcast_messages)
|
|
|
|
|
|
@broadcast_message_blueprint.route('/<uuid:broadcast_message_id>', methods=['GET'])
|
|
def get_broadcast_message(service_id, broadcast_message_id):
|
|
return jsonify(dao_get_broadcast_message_by_id_and_service_id(broadcast_message_id, service_id).serialize())
|
|
|
|
|
|
@broadcast_message_blueprint.route('', methods=['POST'])
|
|
def create_broadcast_message(service_id):
|
|
data = request.get_json()
|
|
|
|
validate(data, create_broadcast_message_schema)
|
|
service = dao_fetch_service_by_id(data['service_id'])
|
|
user = get_user_by_id(data['created_by'])
|
|
personalisation = data.get('personalisation', {})
|
|
template_id = data.get('template_id')
|
|
|
|
if template_id:
|
|
template = dao_get_template_by_id_and_service_id(
|
|
template_id, data['service_id']
|
|
)
|
|
content = str(template._as_utils_template_with_personalisation(
|
|
personalisation
|
|
))
|
|
reference = None
|
|
else:
|
|
temporary_template = BroadcastMessageTemplate.from_content(data['content'])
|
|
if temporary_template.content_too_long:
|
|
raise InvalidRequest(
|
|
(
|
|
f'Content must be '
|
|
f'{temporary_template.max_content_count:,.0f} '
|
|
f'characters or fewer'
|
|
) + (
|
|
' (because it could not be GSM7 encoded)'
|
|
if temporary_template.non_gsm_characters else ''
|
|
),
|
|
status_code=400,
|
|
)
|
|
template = None
|
|
content = str(temporary_template)
|
|
reference = data['reference']
|
|
|
|
broadcast_message = BroadcastMessage(
|
|
service_id=service.id,
|
|
template_id=template_id,
|
|
template_version=template.version if template else None,
|
|
personalisation=personalisation,
|
|
areas=data.get("areas", {}),
|
|
status=BroadcastStatusType.DRAFT,
|
|
starts_at=_parse_nullable_datetime(data.get('starts_at')),
|
|
finishes_at=_parse_nullable_datetime(data.get('finishes_at')),
|
|
created_by_id=user.id,
|
|
content=content,
|
|
reference=reference,
|
|
stubbed=service.restricted
|
|
)
|
|
|
|
dao_save_object(broadcast_message)
|
|
|
|
return jsonify(broadcast_message.serialize()), 201
|
|
|
|
|
|
@broadcast_message_blueprint.route('/<uuid:broadcast_message_id>', methods=['POST'])
|
|
def update_broadcast_message(service_id, broadcast_message_id):
|
|
data = request.get_json()
|
|
validate(data, update_broadcast_message_schema)
|
|
|
|
broadcast_message = dao_get_broadcast_message_by_id_and_service_id(broadcast_message_id, service_id)
|
|
|
|
if broadcast_message.status not in BroadcastStatusType.PRE_BROADCAST_STATUSES:
|
|
raise InvalidRequest(
|
|
f'Cannot update broadcast_message {broadcast_message.id} while it has status {broadcast_message.status}',
|
|
status_code=400
|
|
)
|
|
|
|
areas = data.get("areas", {})
|
|
|
|
if ('ids' in areas and 'simple_polygons' not in areas) or ('ids' not in areas and 'simple_polygons' in areas):
|
|
raise InvalidRequest(
|
|
f'Cannot update broadcast_message {broadcast_message.id}, area IDs or polygons are missing.',
|
|
status_code=400
|
|
)
|
|
|
|
if 'personalisation' in data:
|
|
broadcast_message.personalisation = data['personalisation']
|
|
if 'starts_at' in data:
|
|
broadcast_message.starts_at = _parse_nullable_datetime(data['starts_at'])
|
|
if 'finishes_at' in data:
|
|
broadcast_message.finishes_at = _parse_nullable_datetime(data['finishes_at'])
|
|
if 'ids' in areas and 'simple_polygons' in areas:
|
|
broadcast_message.areas = areas
|
|
|
|
dao_save_object(broadcast_message)
|
|
|
|
return jsonify(broadcast_message.serialize()), 200
|
|
|
|
|
|
@broadcast_message_blueprint.route('/<uuid:broadcast_message_id>/status', methods=['POST'])
|
|
def update_broadcast_message_status(service_id, broadcast_message_id):
|
|
data = request.get_json()
|
|
|
|
validate(data, update_broadcast_message_status_schema)
|
|
broadcast_message = dao_get_broadcast_message_by_id_and_service_id(broadcast_message_id, service_id)
|
|
|
|
if not broadcast_message.service.active:
|
|
raise InvalidRequest("Updating broadcast message is not allowed: service is inactive ", 403)
|
|
|
|
new_status = data['status']
|
|
updating_user = get_user_by_id(data['created_by'])
|
|
|
|
if updating_user not in broadcast_message.service.users:
|
|
# we allow platform admins to cancel broadcasts, and we don't check user if request was done via API
|
|
if not (new_status == BroadcastStatusType.CANCELLED and updating_user.platform_admin):
|
|
raise InvalidRequest(
|
|
f'User {updating_user.id} cannot update broadcast_message {broadcast_message.id} from other service',
|
|
status_code=400
|
|
)
|
|
|
|
validate_and_update_broadcast_message_status(broadcast_message, new_status, updating_user)
|
|
|
|
return jsonify(broadcast_message.serialize()), 200
|