From 6b2c2962c92d72039049cc3ef363d82617901cdf Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Thu, 14 Sep 2017 17:54:38 +0100 Subject: [PATCH] New endpoint to insert new service reply to email address and update existing one. --- app/dao/service_email_reply_to_dao.py | 56 ++++++++- app/models.py | 4 +- app/service/rest.py | 23 +++- app/service/service_senders_schema.py | 11 ++ .../dao/test_service_email_reply_to_dao.py | 117 +++++++++++++++++- tests/app/service/test_rest.py | 69 +++++++++++ 6 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 app/service/service_senders_schema.py diff --git a/app/dao/service_email_reply_to_dao.py b/app/dao/service_email_reply_to_dao.py index 3066c229d..b31549bca 100644 --- a/app/dao/service_email_reply_to_dao.py +++ b/app/dao/service_email_reply_to_dao.py @@ -9,7 +9,7 @@ def dao_get_reply_to_by_service_id(service_id): ServiceEmailReplyTo ).filter( ServiceEmailReplyTo.service_id == service_id - ).all() + ).order_by(ServiceEmailReplyTo.created_at).all() return reply_to @@ -37,3 +37,57 @@ def dao_create_reply_to_email_address(reply_to_email): @transactional def dao_update_reply_to_email(reply_to): db.session.add(reply_to) + + +@transactional +def add_reply_to_email_address_for_service(service_id, email_address, is_default=True): + old_default = _get_existing_default(service_id) + if is_default: + _reset_old_default_to_false(old_default) + else: + _raise_when_no_default(old_default) + + new_reply_to = ServiceEmailReplyTo(service_id=service_id, email_address=email_address, is_default=is_default) + db.session.add(new_reply_to) + return new_reply_to + + +@transactional +def update_reply_to_email_address(service_id, reply_to_id, email_address, is_default=True): + old_default = _get_existing_default(service_id) + if is_default: + _reset_old_default_to_false(old_default) + else: + if old_default.id == reply_to_id: + raise InvalidRequest("You must have at least one reply to email address as the default.", 400) + + reply_to_update = ServiceEmailReplyTo.query.get(reply_to_id) + reply_to_update.email_address = email_address + reply_to_update.is_default = is_default + db.session.add(reply_to_update) + return reply_to_update + + +def _get_existing_default(service_id): + existing_reply_to = dao_get_reply_to_by_service_id(service_id=service_id) + if existing_reply_to: + old_default = [x for x in existing_reply_to if x.is_default] + if len(old_default) == 1: + return old_default[0] + else: + # is this check necessary + raise InvalidRequest( + "There should only be one default reply to email for each service. Service {} has {}".format( + service_id, len(old_default))) + + +def _reset_old_default_to_false(old_default): + if old_default: + old_default.is_default = False + db.session.add(old_default) + + +def _raise_when_no_default(old_default): + # check that the update is not updating the only default to false + if not old_default: + raise InvalidRequest("You must have at least one reply to email address as the default.", 400) diff --git a/app/models.py b/app/models.py index f689cc3c4..0a30f544d 100644 --- a/app/models.py +++ b/app/models.py @@ -1348,6 +1348,6 @@ class ServiceEmailReplyTo(db.Model): return { 'email_address': self.email_address, 'is_default': self.is_default, - 'created_at': self.created_at, - 'updated_at': self.updated_at + 'created_at': self.created_at.strftime(DATETIME_FORMAT), + 'updated_at': self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None } diff --git a/app/service/rest.py b/app/service/rest.py index e64678939..fdcb6f28d 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -46,7 +46,8 @@ from app.dao.service_whitelist_dao import ( dao_add_and_commit_whitelisted_contacts, dao_remove_service_whitelist ) -from app.dao.service_email_reply_to_dao import create_or_update_email_reply_to, dao_get_reply_to_by_service_id +from app.dao.service_email_reply_to_dao import create_or_update_email_reply_to, dao_get_reply_to_by_service_id, \ + add_reply_to_email_address_for_service, update_reply_to_email_address from app.dao.provider_statistics_dao import get_fragment_count from app.dao.users_dao import get_user_by_id from app.errors import ( @@ -57,6 +58,7 @@ from app.models import Service, ServiceInboundApi from app.schema_validation import validate from app.service import statistics from app.service.service_inbound_api_schema import service_inbound_api, update_service_inbound_api_schema +from app.service.service_senders_schema import add_service_email_reply_to_request from app.service.utils import get_whitelist_objects from app.service.sender import send_notification_to_service_users from app.service.send_notification import send_one_off_notification @@ -529,6 +531,25 @@ def get_email_reply_to_addresses(service_id): return jsonify([i.serialize() for i in result]), 200 +@service_blueprint.route('//email-reply-to', methods=['POST']) +def add_service_reply_to_email_address(service_id): + form = validate(request.get_json(), add_service_email_reply_to_request) + new_reply_to = add_reply_to_email_address_for_service(service_id=service_id, + email_address=form['email_address'], + is_default=form.get('is_default', True)) + return jsonify(data=new_reply_to.serialize()), 201 + + +@service_blueprint.route('//email-reply-to/', methods=['POST']) +def update_service_reply_to_email_address(service_id, id): + form = validate(request.get_json(), add_service_email_reply_to_request) + new_reply_to = update_reply_to_email_address(service_id=service_id, + reply_to_id=id, + email_address=form['email_address'], + is_default=form.get('is_default', True)) + return jsonify(data=new_reply_to.serialize()), 200 + + @service_blueprint.route('/unique', methods=["GET"]) def is_service_name_unique(): name, email_from = check_request_args(request) diff --git a/app/service/service_senders_schema.py b/app/service/service_senders_schema.py new file mode 100644 index 000000000..277820837 --- /dev/null +++ b/app/service/service_senders_schema.py @@ -0,0 +1,11 @@ +add_service_email_reply_to_request = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "POST service email reply to address", + "type": "object", + "title": "Add new email reply to address for service", + "properties": { + "email_address": {"type": "string", "format": "email_address"}, + "is_default": {"type": "boolean"} + }, + "required": ["email_address"] +} diff --git a/tests/app/dao/test_service_email_reply_to_dao.py b/tests/app/dao/test_service_email_reply_to_dao.py index 58ba416a8..f16dba808 100644 --- a/tests/app/dao/test_service_email_reply_to_dao.py +++ b/tests/app/dao/test_service_email_reply_to_dao.py @@ -2,8 +2,8 @@ import pytest from app.dao.service_email_reply_to_dao import ( create_or_update_email_reply_to, - dao_get_reply_to_by_service_id -) + dao_get_reply_to_by_service_id, + add_reply_to_email_address_for_service, update_reply_to_email_address) from app.errors import InvalidRequest from app.models import ServiceEmailReplyTo from tests.app.db import create_reply_to_email, create_service @@ -65,3 +65,116 @@ def test_dao_get_reply_to_by_service_id(notify_db_session): assert len(results) == 2 assert default_reply_to in results assert another_reply_to in results + + +def test_add_reply_to_email_address_for_service_creates_first_email_for_service(notify_db_session): + service = create_service() + add_reply_to_email_address_for_service(service_id=service.id, email_address='new@address.com') + + results = dao_get_reply_to_by_service_id(service_id=service.id) + assert len(results) == 1 + assert results[0].email_address == 'new@address.com' + assert results[0].is_default + + +def test_add_reply_to_email_address_for_service_creates_another_email_for_service(notify_db_session): + service = create_service() + first_reply_to = create_reply_to_email(service=service, email_address="first@address.com") + + add_reply_to_email_address_for_service(service_id=service.id, email_address='second@address.com', is_default=False) + + results = dao_get_reply_to_by_service_id(service_id=service.id) + assert len(results) == 2 + for x in results: + if x.email_address == 'first@address.com': + assert x.is_default + elif x.email_address == 'second@address.com': + assert not x.is_default + else: + assert False + + +def test_add_reply_to_email_address_new_reply_to_is_default_existing_reply_to_is_not(notify_db_session): + service = create_service() + first_reply_to = create_reply_to_email(service=service, email_address="first@address.com", is_default=True) + add_reply_to_email_address_for_service(service_id=service.id, email_address='second@address.com', is_default=True) + + results = dao_get_reply_to_by_service_id(service_id=service.id) + assert len(results) == 2 + for x in results: + if x.email_address == 'first@address.com': + assert not x.is_default + elif x.email_address == 'second@address.com': + assert x.is_default + else: + assert False + + +def test_add_reply_to_email_address_can_add_a_third_reply_to_address(sample_service): + add_reply_to_email_address_for_service(service_id=sample_service.id, + email_address="first@address.com") + add_reply_to_email_address_for_service(service_id=sample_service.id, email_address='second@address.com', + is_default=False) + add_reply_to_email_address_for_service(service_id=sample_service.id, email_address='third@address.com', + is_default=False) + + results = dao_get_reply_to_by_service_id(service_id=sample_service.id) + assert len(results) == 3 + + for x in results: + if x.email_address == 'first@address.com': + assert x.is_default + elif x.email_address == 'second@address.com': + assert not x.is_default + elif x.email_address == 'third@address.com': + assert not x.is_default + else: + assert False + + +def test_add_reply_to_email_address_ensures_first_reply_to_is_default(sample_service): + with pytest.raises(expected_exception=InvalidRequest): + add_reply_to_email_address_for_service(service_id=sample_service.id, + email_address="first@address.com", is_default=False) + + +def test_update_reply_to_email_address(sample_service): + first_reply_to = create_reply_to_email(service=sample_service, email_address="first@address.com") + update_reply_to_email_address(service_id=sample_service.id, reply_to_id=first_reply_to.id, + email_address='change_address@email.com') + updated_reply_to = ServiceEmailReplyTo.query.get(first_reply_to.id) + + assert updated_reply_to.email_address == 'change_address@email.com' + assert updated_reply_to.updated_at + assert updated_reply_to.is_default + + +def test_update_reply_to_email_address_set_updated_to_default(sample_service): + first_reply_to = create_reply_to_email(service=sample_service, email_address="first@address.com") + second_reply_to = create_reply_to_email(service=sample_service, + email_address="second@address.com", + is_default=False) + + update_reply_to_email_address(service_id=sample_service.id, + reply_to_id=second_reply_to.id, + email_address='change_address@email.com', + is_default=True) + + results = ServiceEmailReplyTo.query.all() + assert len(results) == 2 + for x in results: + if x.email_address == 'change_address@email.com': + assert x.is_default + elif x.email_address == 'first@address.com': + assert not x.is_default + else: + assert False + + +def test_update_reply_to_email_address_raises_exception_if_single_reply_to_and_setting_default_to_false(sample_service): + first_reply_to = create_reply_to_email(service=sample_service, email_address="first@address.com") + with pytest.raises(expected_exception=InvalidRequest): + update_reply_to_email_address(service_id=sample_service.id, + reply_to_id=first_reply_to.id, + email_address='should@fail.com', + is_default=False) diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 39e5f776c..d5d01cd78 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -2198,3 +2198,72 @@ def test_get_email_reply_to_addresses_with_multiple_email_addresses(client, noti assert not json_response[1]['is_default'] assert json_response[1]['created_at'] assert not json_response[1]['updated_at'] + + +def test_add_service_reply_to_email_address(client, sample_service): + data = json.dumps({"email_address": "new@reply.com"}) + response = client.post('/service/{}/email-reply-to'.format(sample_service.id), + data=data, + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + assert response.status_code == 201 + json_resp = json.loads(response.get_data(as_text=True)) + results = ServiceEmailReplyTo.query.all() + assert len(results) == 1 + assert json_resp['data'] == results[0].serialize() + + +def test_add_service_reply_to_email_address_can_add_multiple_addresses(client, sample_service): + data = json.dumps({"email_address": "first@reply.com"}) + client.post('/service/{}/email-reply-to'.format(sample_service.id), + data=data, + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + second = json.dumps({"email_address": "second@reply.com"}) + response = client.post('/service/{}/email-reply-to'.format(sample_service.id), + data=second, + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + assert response.status_code == 201 + json_resp = json.loads(response.get_data(as_text=True)) + results = ServiceEmailReplyTo.query.all() + assert len(results) == 2 + default = [x for x in results if x.is_default] + assert json_resp['data'] == default[0].serialize() + first_reply_to_not_default = [x for x in results if not x.is_default] + assert first_reply_to_not_default[0].email_address == 'first@reply.com' + + +def test_add_service_reply_to_email_address_raise_exception_if_no_default(client, sample_service): + data = json.dumps({"email_address": "first@reply.com", "is_default": False}) + response = client.post('/service/{}/email-reply-to'.format(sample_service.id), + data=data, + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + assert response.status_code == 400 + json_resp = json.loads(response.get_data(as_text=True)) + assert json_resp['message'] == 'You must have at least one reply to email address as the default.' + + +def test_update_service_reply_to_email_address(client, sample_service): + original_reply_to = create_reply_to_email(service=sample_service, email_address="some@email.com") + data = json.dumps({"email_address": "changed@reply.com"}) + response = client.post('/service/{}/email-reply-to/{}'.format(sample_service.id, original_reply_to.id), + data=data, + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + assert response.status_code == 200 + json_resp = json.loads(response.get_data(as_text=True)) + results = ServiceEmailReplyTo.query.all() + assert len(results) == 1 + assert json_resp['data'] == results[0].serialize() + + +def test_update_service_reply_to_email_address_returns_400_when_no_default(client, sample_service): + original_reply_to = create_reply_to_email(service=sample_service, email_address="some@email.com") + data = json.dumps({"email_address": "changed@reply.com", "is_default": False}) + response = client.post('/service/{}/email-reply-to/{}'.format(sample_service.id, original_reply_to.id), + data=data, + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + assert response.status_code == 400 + json_resp = json.loads(response.get_data(as_text=True)) + assert json_resp['message'] == 'You must have at least one reply to email address as the default.'