From 37a2dc7925621ee7f608fda32c44dc5d33d837f0 Mon Sep 17 00:00:00 2001 From: Martyn Inglis Date: Thu, 22 Sep 2016 14:01:25 +0100 Subject: [PATCH] Implemented the REST endpoint to communicate with the new send-to-provider code - this allows us to send a notification to a provider by means of an API call - This is in addition to the celery code. - idea is that we can use this method to help speed up throughput by generating API traffic by node/lambda etc to supplement the celery code in times of high load. --- app/__init__.py | 2 + app/delivery/rest.py | 27 ++++-- tests/app/celery/test_provider_tasks.py | 27 +++--- tests/app/delivery/__init__.py | 0 tests/app/delivery/test_rest.py | 105 ++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 tests/app/delivery/__init__.py create mode 100644 tests/app/delivery/test_rest.py diff --git a/app/__init__.py b/app/__init__.py index cf47632f9..c602003fb 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -70,6 +70,7 @@ def create_app(app_name=None): from app.provider_details.rest import provider_details as provider_details_blueprint from app.spec.rest import spec as spec_blueprint from app.organisation.rest import organisation_blueprint + from app.delivery.rest import delivery_blueprint application.register_blueprint(service_blueprint, url_prefix='/service') application.register_blueprint(user_blueprint, url_prefix='/user') @@ -78,6 +79,7 @@ def create_app(app_name=None): application.register_blueprint(notifications_blueprint) application.register_blueprint(job_blueprint) application.register_blueprint(invite_blueprint) + application.register_blueprint(delivery_blueprint) application.register_blueprint(accept_invite, url_prefix='/invite') application.register_blueprint(template_statistics_blueprint) diff --git a/app/delivery/rest.py b/app/delivery/rest.py index 5f534f631..0bacb43bb 100644 --- a/app/delivery/rest.py +++ b/app/delivery/rest.py @@ -4,25 +4,33 @@ from app.delivery import send_to_providers from app.models import EMAIL_TYPE from app.celery import provider_tasks from app.dao import notifications_dao +from flask import current_app -delivery = Blueprint('delivery', __name__) +delivery_blueprint = Blueprint('delivery', __name__) from app.errors import register_errors -register_errors(delivery) +register_errors(delivery_blueprint) -@delivery.route('/deliver/notification/', methods=['POST']) +@delivery_blueprint.route('/deliver/notification/', methods=['POST']) def send_notification_to_provider(notification_id): - notification = notifications_dao.get_notification_by_id(notification_id) if not notification: - return jsonify(notification={"result": "error", "message": "No result found"}), 404 + return jsonify({"result": "error", "message": "No result found"}), 404 if notification.notification_type == EMAIL_TYPE: - send_response(send_to_providers.send_email_response, provider_tasks.deliver_email, notification_id, 'send-email') + send_response( + send_to_providers.send_email_to_provider, + provider_tasks.deliver_email, + notification, + 'send-email') else: - send_response(send_to_providers.send_sms_response, provider_tasks.deliver_sms, notification_id, 'send-sms') + send_response( + send_to_providers.send_sms_to_provider, + provider_tasks.deliver_sms, + notification, + 'send-sms') return jsonify({}), 204 @@ -30,4 +38,9 @@ def send_response(send_call, task_call, notification, queue): try: send_call(notification) except Exception as e: + current_app.logger.exception( + "Failed to send notification, retrying in celery. ID {} type {}".format( + notification.id, + notification.notification_type), + e) task_call.apply_async((str(notification.id)), queue=queue) diff --git a/tests/app/celery/test_provider_tasks.py b/tests/app/celery/test_provider_tasks.py index df5dc8ef6..b2f7f28e7 100644 --- a/tests/app/celery/test_provider_tasks.py +++ b/tests/app/celery/test_provider_tasks.py @@ -66,11 +66,10 @@ def test_should_add_to_retry_queue_if_notification_not_found_in_deliver_sms_task def test_should_call_send_sms_to_provider_from_send_sms_to_provider_task( - notify_db, - notify_db_session, - sample_notification, - mocker): - + notify_db, + notify_db_session, + sample_notification, + mocker): mocker.patch('app.delivery.send_to_providers.send_sms_to_provider') send_sms_to_provider(sample_notification.service_id, sample_notification.id) @@ -93,11 +92,10 @@ def test_should_add_to_retry_queue_if_notification_not_found_in_send_sms_to_prov def test_should_call_send_email_to_provider_from_deliver_email_task( - notify_db, - notify_db_session, - sample_notification, - mocker): - + notify_db, + notify_db_session, + sample_notification, + mocker): mocker.patch('app.delivery.send_to_providers.send_email_to_provider') deliver_email(sample_notification.id) @@ -119,11 +117,10 @@ def test_should_add_to_retry_queue_if_notification_not_found_in_deliver_email_ta def test_should_call_send_email_to_provider_from_email_task( - notify_db, - notify_db_session, - sample_notification, - mocker): - + notify_db, + notify_db_session, + sample_notification, + mocker): mocker.patch('app.delivery.send_to_providers.send_email_to_provider') send_email_to_provider(sample_notification.service_id, sample_notification.id) diff --git a/tests/app/delivery/__init__.py b/tests/app/delivery/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/app/delivery/test_rest.py b/tests/app/delivery/test_rest.py new file mode 100644 index 000000000..fc51fb508 --- /dev/null +++ b/tests/app/delivery/test_rest.py @@ -0,0 +1,105 @@ +from flask import json + +import app +from tests import create_authorization_header + + +def test_should_reject_if_not_authenticated(notify_api): + with notify_api.test_request_context(): + with notify_api.test_client() as client: + response = client.post('/deliver/notification/{}'.format(app.create_uuid())) + assert response.status_code == 401 + + +def test_should_reject_if_invalid_uuid(notify_api): + with notify_api.test_request_context(): + with notify_api.test_client() as client: + auth = create_authorization_header() + response = client.post( + '/deliver/notification/{}', + headers=[auth] + ) + body = json.loads(response.get_data(as_text=True)) + assert response.status_code == 404 + assert body['message'] == 'The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.' # noqa + assert body['result'] == 'error' + + +def test_should_reject_if_notification_id_cannot_be_found(notify_api): + with notify_api.test_request_context(): + with notify_api.test_client() as client: + auth = create_authorization_header() + response = client.post( + '/deliver/notification/{}'.format(app.create_uuid()), + headers=[auth] + ) + body = json.loads(response.get_data(as_text=True)) + assert response.status_code == 404 + assert body['message'] == 'No result found' + assert body['result'] == 'error' + + +def test_should_call_send_sms_to_provider_as_primary(notify_api, sample_notification, mocker): + mocker.patch('app.delivery.send_to_providers.send_sms_to_provider') + with notify_api.test_request_context(): + with notify_api.test_client() as client: + auth = create_authorization_header() + response = client.post( + '/deliver/notification/{}'.format(sample_notification.id), + headers=[auth] + ) + app.delivery.send_to_providers.send_sms_to_provider.assert_called_with(sample_notification) + assert response.status_code == 204 + + +def test_should_call_send_email_to_provider_as_primary(notify_api, sample_email_notification, mocker): + mocker.patch('app.delivery.send_to_providers.send_email_to_provider') + with notify_api.test_request_context(): + with notify_api.test_client() as client: + auth = create_authorization_header() + response = client.post( + '/deliver/notification/{}'.format(sample_email_notification.id), + headers=[auth] + ) + app.delivery.send_to_providers.send_email_to_provider.assert_called_with(sample_email_notification) + assert response.status_code == 204 + + +def test_should_call_deliver_sms_task_if_send_sms_to_provider_fails(notify_api, sample_notification, mocker): + mocker.patch('app.delivery.send_to_providers.send_sms_to_provider', side_effect=Exception()) + mocker.patch('app.celery.provider_tasks.deliver_sms.apply_async') + + with notify_api.test_request_context(): + with notify_api.test_client() as client: + auth = create_authorization_header() + response = client.post( + '/deliver/notification/{}'.format(sample_notification.id), + headers=[auth] + ) + app.delivery.send_to_providers.send_sms_to_provider.assert_called_with(sample_notification) + app.celery.provider_tasks.deliver_sms.apply_async.assert_called_with( + (str(sample_notification.id)), queue='send-sms' + ) + assert response.status_code == 204 + + +def test_should_call_deliver_email_task_if_send_email_to_provider_fails( + notify_api, + sample_email_notification, + mocker +): + mocker.patch('app.delivery.send_to_providers.send_email_to_provider', side_effect=Exception()) + mocker.patch('app.celery.provider_tasks.deliver_email.apply_async') + + with notify_api.test_request_context(): + with notify_api.test_client() as client: + auth = create_authorization_header() + response = client.post( + '/deliver/notification/{}'.format(sample_email_notification.id), + headers=[auth] + ) + app.delivery.send_to_providers.send_email_to_provider.assert_called_with(sample_email_notification) + app.celery.provider_tasks.deliver_email.apply_async.assert_called_with( + (str(sample_email_notification.id)), queue='send-email' + ) + assert response.status_code == 204