Create query to sum notificaitons for the message limit check, this is a bit more efficient.

Refactored send_notifications method so that it is more readible.
Refectored the test_send_notificaitons so that it uses parametrized test to avoid duplication.
This commit is contained in:
Rebecca Law
2016-10-03 10:57:10 +01:00
parent d27e1595d5
commit e3d418506a
5 changed files with 272 additions and 414 deletions

View File

@@ -1,12 +1,10 @@
from datetime import (datetime)
from flask import current_app
from notifications_utils.recipients import (
RecipientCSV
)
from notifications_utils.template import Template
from sqlalchemy.exc import SQLAlchemyError
from app import (
create_uuid,
DATETIME_FORMAT,
@@ -20,14 +18,13 @@ from app.dao.jobs_dao import (
dao_get_job_by_id
)
from app.dao.notifications_dao import (dao_create_notification)
from app.dao.services_dao import dao_fetch_service_by_id, dao_fetch_todays_stats_for_service
from app.dao.services_dao import dao_fetch_service_by_id, fetch_todays_total_message_count
from app.dao.templates_dao import dao_get_template_by_id
from app.models import (
Notification,
EMAIL_TYPE,
SMS_TYPE,
KEY_TYPE_NORMAL,
KEY_TYPE_TEST
KEY_TYPE_NORMAL
)
from app.service.utils import service_allowed_to_send_to
from app.statsd_decorators import statsd
@@ -41,7 +38,7 @@ def process_job(job_id):
service = job.service
total_sent = sum(row.count for row in dao_fetch_todays_stats_for_service(service.id))
total_sent = fetch_todays_total_message_count(service.id)
if total_sent + job.notification_count > service.message_limit:
job.status = 'sending limits exceeded'

View File

@@ -150,6 +150,20 @@ def dao_fetch_todays_stats_for_service(service_id):
).all()
def fetch_todays_total_message_count(service_id):
result = db.session.query(
func.count(Notification.id).label('count')
).filter(
Notification.service_id == service_id,
Notification.key_type != KEY_TYPE_TEST,
func.date(Notification.created_at) == date.today()
).group_by(
Notification.notification_type,
Notification.status,
).first()
return 0 if result is None else result.count
def _stats_for_service_query(service_id):
return db.session.query(
Notification.notification_type,

View File

@@ -1,5 +1,4 @@
from datetime import datetime
import itertools
from flask import (
Blueprint,
@@ -9,13 +8,11 @@ from flask import (
json
)
from notifications_utils.recipients import first_column_heading
from notifications_utils.template import Template
from notifications_utils.renderers import PassThrough
from app.clients.email.aws_ses import get_aws_responses
from app import api_user, create_uuid, DATETIME_FORMAT, statsd_client
from app.dao.notifications_dao import dao_create_notification, dao_delete_notifications_and_history_by_id
from app.dao.services_dao import dao_fetch_todays_stats_for_service
from app.models import KEY_TYPE_TEAM, KEY_TYPE_TEST, Notification, KEY_TYPE_NORMAL, EMAIL_TYPE
from app.dao import (
templates_dao,
@@ -195,6 +192,16 @@ def get_all_notifications():
), 200
@notifications.route('/notifications/statistics')
def get_notification_statistics_for_day():
data = day_schema.load(request.args).data
statistics = notifications_dao.dao_get_potential_notification_statistics_for_day(
day=data['day']
)
data, errors = notifications_statistics_schema.dump(statistics, many=True)
return jsonify(data=data), 200
@notifications.route('/notifications/<string:notification_type>', methods=['POST'])
def send_notification(notification_type):
if notification_type not in ['sms', 'email']:
@@ -209,8 +216,9 @@ def send_notification(notification_type):
if errors:
raise InvalidRequest(errors, status_code=400)
if all((api_user.key_type != KEY_TYPE_TEST, service.restricted)):
service_stats = sum(row.count for row in dao_fetch_todays_stats_for_service(service.id))
if all((api_user.key_type != KEY_TYPE_TEST,
service.restricted)):
service_stats = services_dao.fetch_todays_total_message_count(service.id)
if service_stats >= service.message_limit:
error = 'Exceeded send limits ({}) for today'.format(service.message_limit)
raise InvalidRequest(error, status_code=429)
@@ -229,42 +237,9 @@ def send_notification(notification_type):
if errors:
raise InvalidRequest(errors, status_code=400)
template_object = Template(
template.__dict__,
notification.get('personalisation', {}),
renderer=PassThrough()
)
if template_object.missing_data:
message = 'Missing personalisation: {}'.format(", ".join(template_object.missing_data))
errors = {'template': [message]}
raise InvalidRequest(errors, status_code=400)
template_object = create_template_object_for_notification(template, notification.get('personalisation', {}))
if template_object.additional_data:
message = 'Personalisation not needed for template: {}'.format(", ".join(template_object.additional_data))
errors = {'template': [message]}
raise InvalidRequest(errors, status_code=400)
if (
template_object.template_type == SMS_TYPE and
template_object.replaced_content_count > current_app.config.get('SMS_CHAR_COUNT_LIMIT')
):
char_count = current_app.config.get('SMS_CHAR_COUNT_LIMIT')
message = 'Content has a character count greater than the limit of {}'.format(char_count)
errors = {'content': [message]}
raise InvalidRequest(errors, status_code=400)
if not service_allowed_to_send_to(notification['to'], service, api_user.key_type):
if (api_user.key_type == KEY_TYPE_TEAM):
message = 'Cant send to this recipient using a team-only API key'
else:
message = (
'Cant send to this recipient when service is in trial mode '
' see https://www.notifications.service.gov.uk/trial-mode'
)
raise InvalidRequest(
{'to': [message]},
status_code=400
)
_service_allowed_to_send_to(notification, service)
notification_id = create_uuid()
notification.update({"template_version": template.version})
@@ -300,16 +275,6 @@ def get_notification_return_data(notification_id, notification, template):
return output
@notifications.route('/notifications/statistics')
def get_notification_statistics_for_day():
data = day_schema.load(request.args).data
statistics = notifications_dao.dao_get_potential_notification_statistics_for_day(
day=data['day']
)
data, errors = notifications_statistics_schema.dump(statistics, many=True)
return jsonify(data=data), 200
def _simulated_recipient(to_address, notification_type):
return (to_address in current_app.config['SIMULATED_SMS_NUMBERS']
if notification_type == SMS_TYPE
@@ -344,3 +309,45 @@ def persist_notification(
current_app.logger.info(
"{} {} created at {}".format(notification_type, notification_id, created_at)
)
def _service_allowed_to_send_to(notification, service):
if not service_allowed_to_send_to(notification['to'], service, api_user.key_type):
if api_user.key_type == KEY_TYPE_TEAM:
message = 'Cant send to this recipient using a team-only API key'
else:
message = (
'Cant send to this recipient when service is in trial mode '
' see https://www.notifications.service.gov.uk/trial-mode'
)
raise InvalidRequest(
{'to': [message]},
status_code=400
)
def create_template_object_for_notification(template, personalisation):
template_object = Template(
template.__dict__,
personalisation,
renderer=PassThrough()
)
if template_object.missing_data:
message = 'Missing personalisation: {}'.format(", ".join(template_object.missing_data))
errors = {'template': [message]}
raise InvalidRequest(errors, status_code=400)
if template_object.additional_data:
message = 'Personalisation not needed for template: {}'.format(", ".join(template_object.additional_data))
errors = {'template': [message]}
raise InvalidRequest(errors, status_code=400)
if (
template_object.template_type == SMS_TYPE and
template_object.replaced_content_count > current_app.config.get('SMS_CHAR_COUNT_LIMIT')
):
char_count = current_app.config.get('SMS_CHAR_COUNT_LIMIT')
message = 'Content has a character count greater than the limit of {}'.format(char_count)
errors = {'content': [message]}
raise InvalidRequest(errors, status_code=400)
return template_object

View File

@@ -19,7 +19,8 @@ from app.dao.services_dao import (
delete_service_and_all_associated_db_objects,
dao_fetch_stats_for_service,
dao_fetch_todays_stats_for_service,
dao_fetch_weekly_historical_stats_for_service
dao_fetch_weekly_historical_stats_for_service,
fetch_todays_total_message_count
)
from app.dao.users_dao import save_model_user
from app.models import (
@@ -556,3 +557,14 @@ def test_fetch_weekly_historical_stats_separates_types(notify_db,
assert ret[1].week_start == datetime(2016, 7, 25)
assert ret[1].count == 1
assert ret[1].notification_type == 'sms'
def test_dao_fetch_todays_total_message_count_returns_0_when_no_messages_for_today(notify_db,
notify_db_session,
sample_notification):
assert fetch_todays_total_message_count(sample_notification.service.id) == 1
def test_dao_fetch_todays_total_message_count_returns_0_when_no_messages_for_today(notify_db,
notify_db_session):
assert fetch_todays_total_message_count(uuid.uuid4()) == 0

View File

@@ -23,21 +23,28 @@ from tests.app.conftest import (
sample_api_key as create_sample_api_key
)
from app.models import Template
from app.errors import InvalidRequest
def test_create_sms_should_reject_if_missing_required_fields(notify_api, sample_api_key, mocker):
@pytest.mark.parametrize('template_type',
['sms', 'email'])
def test_create_notification_should_reject_if_missing_required_fields(notify_api,
sample_api_key, mocker, template_type):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
data = {}
auth_header = create_authorization_header(service_id=sample_api_key.service_id)
response = client.post(
path='/notifications/sms',
path='/notifications/{}'.format(template_type),
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
json_resp = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_not_called()
assert json_resp['result'] == 'error'
assert 'Missing data for required field.' in json_resp['message']['to'][0]
assert 'Missing data for required field.' in json_resp['message']['template'][0]
@@ -68,19 +75,22 @@ def test_should_reject_bad_phone_numbers(notify_api, sample_template, mocker):
assert response.status_code == 400
def test_send_notification_invalid_template_id(notify_api, sample_template, mocker, fake_uuid):
@pytest.mark.parametrize('template_type, to',
[('sms', '+447700900855'),
('email', 'ok@ok.com')])
def test_send_notification_invalid_template_id(notify_api, sample_template, mocker, fake_uuid, template_type, to):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
data = {
'to': '+447700900855',
'to': to,
'template': fake_uuid
}
auth_header = create_authorization_header(service_id=sample_template.service_id)
response = client.post(
path='/notifications/sms',
path='/notifications/{}'.format(template_type),
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
@@ -146,77 +156,30 @@ def test_should_not_send_notification_for_archived_template(notify_api, sample_t
assert 'Template has been deleted' in json_resp['message']['template']
def test_send_notification_with_missing_personalisation(notify_api, sample_template_with_placeholders, mocker):
@pytest.mark.parametrize('template_type, to',
[('sms', '+447700900855'),
('email', 'not-someone-we-trust@email-address.com')])
def test_should_not_send_notification_if_restricted_and_not_a_service_user(notify_api,
sample_template,
sample_email_template,
mocker,
template_type,
to):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
template = sample_template if template_type == 'sms' else sample_email_template
template.service.restricted = True
dao_update_service(template.service)
data = {
'to': '+447700900855',
'template': sample_template_with_placeholders.id,
'personalisation': {
'foo': 'bar'
}
}
auth_header = create_authorization_header(service_id=sample_template_with_placeholders.service.id)
response = client.post(
path='/notifications/sms',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
json_resp = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_not_called()
assert response.status_code == 400
assert 'Missing personalisation: name' in json_resp['message']['template']
def test_send_notification_with_too_much_personalisation_data(
notify_api, sample_template_with_placeholders, mocker
):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
data = {
'to': '+447700900855',
'template': sample_template_with_placeholders.id,
'personalisation': {
'name': 'Jo', 'foo': 'bar'
}
}
auth_header = create_authorization_header(service_id=sample_template_with_placeholders.service.id)
response = client.post(
path='/notifications/sms',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
json_resp = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_not_called()
assert response.status_code == 400
assert 'Personalisation not needed for template: foo' in json_resp['message']['template']
def test_should_not_send_sms_if_restricted_and_not_a_service_user(notify_api, sample_template, mocker):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
sample_template.service.restricted = True
dao_update_service(sample_template.service)
invalid_mob = '+447700900855'
data = {
'to': invalid_mob,
'template': sample_template.id
'to': to,
'template': template.id
}
auth_header = create_authorization_header(service_id=sample_template.service_id)
auth_header = create_authorization_header(service_id=template.service_id)
response = client.post(
path='/notifications/sms',
path='/notifications/{}'.format(template_type),
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
@@ -230,131 +193,84 @@ def test_should_not_send_sms_if_restricted_and_not_a_service_user(notify_api, sa
)] == json_resp['message']['to']
def test_should_send_sms_if_restricted_and_a_service_user(notify_api, sample_template, mocker):
@pytest.mark.parametrize('template_type',
['sms', 'email'])
def test_should_send_notification_if_restricted_and_a_service_user(notify_api,
sample_template,
sample_email_template,
template_type,
mocker):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
if template_type == 'sms':
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
else:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
sample_template.service.restricted = True
dao_update_service(sample_template.service)
template = sample_template if template_type == 'sms' else sample_email_template
to = template.service.created_by.mobile_number if template_type == 'sms' \
else template.service.created_by.email_address
template.service.restricted = True
dao_update_service(template.service)
data = {
'to': sample_template.service.created_by.mobile_number,
'template': sample_template.id
'to': to,
'template': template.id
}
auth_header = create_authorization_header(service_id=sample_template.service_id)
auth_header = create_authorization_header(service_id=template.service_id)
response = client.post(
path='/notifications/sms',
path='/notifications/{}'.format(template_type),
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
assert app.celery.provider_tasks.send_sms_to_provider.apply_async.called
if template_type == 'sms':
assert app.celery.provider_tasks.send_sms_to_provider.apply_async.called
else:
assert app.celery.provider_tasks.send_email_to_provider.apply_async.called
assert response.status_code == 201
def test_should_send_email_if_restricted_and_a_service_user(notify_api, sample_email_template, mocker):
@pytest.mark.parametrize('template_type',
['sms', 'email'])
def test_should_not_allow_template_from_another_service(notify_api,
service_factory,
sample_user,
mocker,
template_type):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
sample_email_template.service.restricted = True
dao_update_service(sample_email_template.service)
data = {
'to': sample_email_template.service.created_by.email_address,
'template': sample_email_template.id
}
auth_header = create_authorization_header(service_id=sample_email_template.service_id)
response = client.post(
path='/notifications/email',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
assert app.celery.provider_tasks.send_email_to_provider.apply_async.called
assert response.status_code == 201
def test_should_not_allow_template_from_another_service(notify_api, service_factory, sample_user, mocker):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
if template_type == 'sms':
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
else:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
service_1 = service_factory.get('service 1', user=sample_user, email_from='service.1')
service_2 = service_factory.get('service 2', user=sample_user, email_from='service.2')
service_2_templates = dao_get_all_templates_for_service(service_id=service_2.id)
to = sample_user.mobile_number if template_type == 'sms' else sample_user.email_address
data = {
'to': sample_user.mobile_number,
'to': to,
'template': service_2_templates[0].id
}
auth_header = create_authorization_header(service_id=service_1.id)
response = client.post(
path='/notifications/sms',
path='/notifications/{}'.format(template_type),
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
json_resp = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_not_called()
if template_type == 'sms':
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_not_called()
else:
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_not_called()
assert response.status_code == 404
test_string = 'No result found'
assert test_string in json_resp['message']
@pytest.mark.parametrize(
'template_type, should_error', [
('sms', True),
('email', False)
]
)
def test_should_not_allow_template_content_too_large(
notify_api,
notify_db,
notify_db_session,
sample_user,
template_type,
mocker,
should_error
):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
template = create_sample_template(
notify_db,
notify_db_session,
content="((long_text))",
template_type=template_type
)
limit = current_app.config.get('SMS_CHAR_COUNT_LIMIT')
json_data = json.dumps({
'to': sample_user.mobile_number if template_type == 'sms' else sample_user.email_address,
'template': template.id,
'personalisation': {
'long_text': ''.join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(limit + 1))
}
})
auth_header = create_authorization_header(service_id=template.service_id)
resp = client.post(
path='/notifications/{}'.format(template_type),
data=json_data,
headers=[('Content-Type', 'application/json'), auth_header]
)
if should_error:
assert resp.status_code == 400
json_resp = json.loads(resp.get_data(as_text=True))
assert json_resp['message']['content'][0] == (
'Content has a character count greater'
' than the limit of {}').format(limit)
else:
assert resp.status_code == 201
@freeze_time("2016-01-01 11:09:00.061258")
def test_should_allow_valid_sms_notification(notify_api, sample_template, mocker):
with notify_api.test_request_context():
@@ -386,27 +302,6 @@ def test_should_allow_valid_sms_notification(notify_api, sample_template, mocker
assert response_data['template_version'] == sample_template.version
def test_create_email_should_reject_if_missing_required_fields(notify_api, sample_api_key, mocker):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
data = {}
auth_header = create_authorization_header(service_id=sample_api_key.service_id)
response = client.post(
path='/notifications/email',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
json_resp = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_not_called()
assert json_resp['result'] == 'error'
assert 'Missing data for required field.' in json_resp['message']['to'][0]
assert 'Missing data for required field.' in json_resp['message']['template'][0]
assert response.status_code == 400
def test_should_reject_email_notification_with_bad_email(notify_api, sample_email_template, mocker):
with notify_api.test_request_context():
with notify_api.test_client() as client:
@@ -430,92 +325,6 @@ def test_should_reject_email_notification_with_bad_email(notify_api, sample_emai
assert data['message']['to'][0] == 'Not a valid email address'
def test_should_reject_email_notification_with_template_id_that_cant_be_found(
notify_api, sample_email_template, mocker, fake_uuid):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
data = {
'to': 'ok@ok.com',
'template': fake_uuid
}
auth_header = create_authorization_header(service_id=sample_email_template.service_id)
response = client.post(
path='/notifications/email',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
data = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_not_called()
assert response.status_code == 404
assert data['result'] == 'error'
test_string = 'No result found'
assert test_string in data['message']
def test_should_not_allow_email_template_from_another_service(notify_api, service_factory, sample_user, mocker):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
service_1 = service_factory.get('service 1', template_type='email', user=sample_user,
email_from='service.1')
service_2 = service_factory.get('service 2', template_type='email', user=sample_user,
email_from='service.2')
service_2_templates = dao_get_all_templates_for_service(service_id=service_2.id)
data = {
'to': sample_user.email_address,
'template': str(service_2_templates[0].id)
}
auth_header = create_authorization_header(service_id=service_1.id)
response = client.post(
path='/notifications/email',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
json_resp = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_not_called()
assert response.status_code == 404
test_string = 'No result found'
assert test_string in json_resp['message']
def test_should_not_send_email_if_restricted_and_not_a_service_user(notify_api, sample_email_template, mocker):
with notify_api.test_request_context():
with notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
sample_email_template.service.restricted = True
dao_update_service(sample_email_template.service)
data = {
'to': "not-someone-we-trust@email-address.com",
'template': str(sample_email_template.id)
}
auth_header = create_authorization_header(service_id=sample_email_template.service_id)
response = client.post(
path='/notifications/email',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), auth_header])
json_resp = json.loads(response.get_data(as_text=True))
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_not_called()
assert response.status_code == 400
assert [(
'Cant send to this recipient when service is in trial mode see '
'https://www.notifications.service.gov.uk/trial-mode'
)] == json_resp['message']['to']
@freeze_time("2016-01-01 11:09:00.061258")
def test_should_allow_valid_email_notification(notify_api, sample_email_template, mocker):
with notify_api.test_request_context():
@@ -808,134 +617,99 @@ def test_should_send_sms_if_team_api_key_and_a_service_user(notify_api, sample_t
assert response.status_code == 201
def test_should_persist_sms_notification(notify_api, sample_template, fake_uuid, mocker):
@pytest.mark.parametrize('template_type',
['sms', 'email'])
def test_should_persist_notification(notify_api, sample_template,
sample_email_template,
template_type,
fake_uuid, mocker):
with notify_api.test_request_context(), notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
if template_type == 'sms':
mocker.patch('app.celery.provider_tasks.send_sms_to_provider.apply_async')
else:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
mocker.patch('app.notifications.rest.create_uuid', return_value=fake_uuid)
template = sample_template if template_type == 'sms' else sample_email_template
to = sample_template.service.created_by.mobile_number if template_type == 'sms' \
else sample_email_template.service.created_by.email_address
data = {
'to': sample_template.service.created_by.mobile_number,
'template': sample_template.id
'to': to,
'template': template.id
}
api_key = ApiKey(
service=sample_template.service,
service=template.service,
name='team_key',
created_by=sample_template.created_by,
created_by=template.created_by,
key_type=KEY_TYPE_TEAM)
save_model_api_key(api_key)
auth_header = create_jwt_token(secret=api_key.unsigned_secret, client_id=str(api_key.service_id))
response = client.post(
path='/notifications/sms',
path='/notifications/{}'.format(template_type),
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))])
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_called_once_with(
(str(sample_template.service.id), fake_uuid), queue='send-sms')
if template_type == 'sms':
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_called_once_with(
(str(template.service.id), fake_uuid), queue='send-sms')
else:
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_called_once_with(
(str(template.service.id), fake_uuid), queue='send-email')
assert response.status_code == 201
notification = notifications_dao.get_notification_by_id(fake_uuid)
assert notification.to == sample_template.service.created_by.mobile_number
assert notification.template_id == sample_template.id
assert notification.notification_type == 'sms'
assert notification.to == to
assert notification.template_id == template.id
assert notification.notification_type == template_type
def test_should_persist_email_notification(notify_api, sample_email_template, fake_uuid, mocker):
with notify_api.test_request_context(), notify_api.test_client() as client:
mocker.patch('app.celery.provider_tasks.send_email_to_provider.apply_async')
mocker.patch('app.notifications.rest.create_uuid', return_value=fake_uuid)
data = {
'to': sample_email_template.service.created_by.email_address,
'template': sample_email_template.id
}
api_key = ApiKey(
service=sample_email_template.service,
name='team_key',
created_by=sample_email_template.created_by,
key_type=KEY_TYPE_TEAM)
save_model_api_key(api_key)
auth_header = create_jwt_token(secret=api_key.unsigned_secret, client_id=str(api_key.service_id))
response = client.post(
path='/notifications/email',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))])
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_called_once_with(
(str(sample_email_template.service.id), fake_uuid), queue='send-email')
assert response.status_code == 201
notification = notifications_dao.get_notification_by_id(fake_uuid)
assert notification.to == sample_email_template.service.created_by.email_address
assert notification.template_id == sample_email_template.id
assert notification.notification_type == 'email'
def test_should_delete_email_notification_and_return_error_if_sqs_fails(
@pytest.mark.parametrize('template_type',
['sms', 'email'])
def test_should_delete_notification_and_return_error_if_sqs_fails(
notify_api,
sample_email_template,
sample_template,
fake_uuid,
mocker):
mocker,
template_type):
with notify_api.test_request_context(), notify_api.test_client() as client:
mocker.patch(
'app.celery.provider_tasks.send_email_to_provider.apply_async',
side_effect=Exception("failed to talk to SQS")
)
if template_type == 'email':
mocker.patch(
'app.celery.provider_tasks.send_email_to_provider.apply_async',
side_effect=Exception("failed to talk to SQS")
)
else:
mocker.patch(
'app.celery.provider_tasks.send_sms_to_provider.apply_async',
side_effect=Exception("failed to talk to SQS")
)
mocker.patch('app.notifications.rest.create_uuid', return_value=fake_uuid)
template = sample_template if template_type == 'sms' else sample_email_template
to = sample_template.service.created_by.mobile_number if template_type == 'sms' \
else sample_email_template.service.created_by.email_address
data = {
'to': sample_email_template.service.created_by.email_address,
'template': sample_email_template.id
'to': to,
'template': template.id
}
api_key = ApiKey(
service=sample_email_template.service,
service=template.service,
name='team_key',
created_by=sample_email_template.created_by,
created_by=template.created_by,
key_type=KEY_TYPE_TEAM)
save_model_api_key(api_key)
auth_header = create_jwt_token(secret=api_key.unsigned_secret, client_id=str(api_key.service_id))
response = client.post(
path='/notifications/email',
path='/notifications/{}'.format(template_type),
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))])
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_called_once_with(
(str(sample_email_template.service.id), fake_uuid), queue='send-email')
assert response.status_code == 500
assert not notifications_dao.get_notification_by_id(fake_uuid)
assert not NotificationHistory.query.get(fake_uuid)
def test_should_delete_sms_notification_and_return_error_if_sqs_fails(notify_api, sample_template, fake_uuid, mocker):
with notify_api.test_request_context(), notify_api.test_client() as client:
mocker.patch(
'app.celery.provider_tasks.send_sms_to_provider.apply_async',
side_effect=Exception("failed to talk to SQS")
)
mocker.patch('app.notifications.rest.create_uuid', return_value=fake_uuid)
data = {
'to': sample_template.service.created_by.mobile_number,
'template': sample_template.id
}
api_key = ApiKey(
service=sample_template.service,
name='team_key',
created_by=sample_template.created_by,
key_type=KEY_TYPE_TEAM)
save_model_api_key(api_key)
auth_header = create_jwt_token(secret=api_key.unsigned_secret, client_id=str(api_key.service_id))
response = client.post(
path='/notifications/sms',
data=json.dumps(data),
headers=[('Content-Type', 'application/json'), ('Authorization', 'Bearer {}'.format(auth_header))])
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_called_once_with(
(str(sample_template.service.id), fake_uuid), queue='send-sms')
if template_type == 'email':
app.celery.provider_tasks.send_email_to_provider.apply_async.assert_called_once_with(
(str(template.service.id), fake_uuid), queue='send-email')
else:
app.celery.provider_tasks.send_sms_to_provider.apply_async.assert_called_once_with(
(str(template.service.id), fake_uuid), queue='send-sms')
assert response.status_code == 500
assert not notifications_dao.get_notification_by_id(fake_uuid)
@@ -1128,3 +902,57 @@ def test_should_error_if_notification_type_does_not_match_template_type(
assert json_resp['result'] == 'error'
assert '{0} template is not suitable for {1} notification'.format(template_type, notification_type) \
in json_resp['message']
def test_create_template_raises_invalid_request_exception_with_missing_personalisation(
sample_template_with_placeholders):
template = Template.query.get(sample_template_with_placeholders.id)
from app.notifications.rest import create_template_object_for_notification
with pytest.raises(InvalidRequest) as e:
create_template_object_for_notification(template, {})
assert {'template': ['Missing personalisation: name']} == e.value.message
def test_create_template_raises_invalid_request_exception_with_too_much_personalisation_data(
sample_template_with_placeholders
):
from app.notifications.rest import create_template_object_for_notification
template = Template.query.get(sample_template_with_placeholders.id)
with pytest.raises(InvalidRequest) as e:
create_template_object_for_notification(template, {'name': 'Jo', 'extra': 'stuff'})
assert {'template': ['Personalisation not needed for template: foo']} in e.value.message
@pytest.mark.parametrize(
'template_type, should_error', [
('sms', True),
('email', False)
]
)
def test_create_template_raises_invalid_request_when_content_too_large(
notify_db,
notify_db_session,
template_type,
should_error
):
sample = create_sample_template(
notify_db,
notify_db_session,
content="((long_text))",
template_type=template_type
)
limit = current_app.config.get('SMS_CHAR_COUNT_LIMIT')
template = Template.query.get(sample.id)
from app.notifications.rest import create_template_object_for_notification
try:
create_template_object_for_notification(template,
{'long_text':
''.join(
random.choice(string.ascii_uppercase + string.digits) for _ in
range(limit + 1))})
if should_error:
pytest.fail("expected an InvalidRequest")
except InvalidRequest as e:
if not should_error:
pytest.fail("do not expect an InvalidRequest")
assert e.message == {'content': ['Content has a character count greater than the limit of {}'.format(limit)]}