From ef52337d851146fb8e1662f708155c9d189a63ff Mon Sep 17 00:00:00 2001 From: Leo Hemsted Date: Wed, 31 May 2017 14:49:14 +0100 Subject: [PATCH] add inbound sms api two endpoints: * get all inbound sms for a service (you can limit to the X most recent, or filter by user's phone number [which will be normalised]) * get a summary of inbound sms for a service - returns the count of inbound sms in the database, and the date that the most recent was sent --- app/__init__.py | 4 ++ app/dao/inbound_sms_dao.py | 23 +++++++ app/inbound_sms/__init__.py | 0 app/inbound_sms/rest.py | 41 ++++++++++++ app/models.py | 12 ++++ tests/app/dao/test_inbound_sms_dao.py | 59 +++++++++++++++++ tests/app/db.py | 23 +++++++ tests/app/inbound_sms/__init__.py | 0 tests/app/inbound_sms/test_rest.py | 93 +++++++++++++++++++++++++++ 9 files changed, 255 insertions(+) create mode 100644 app/inbound_sms/__init__.py create mode 100644 app/inbound_sms/rest.py create mode 100644 tests/app/dao/test_inbound_sms_dao.py create mode 100644 tests/app/inbound_sms/__init__.py create mode 100644 tests/app/inbound_sms/test_rest.py diff --git a/app/__init__.py b/app/__init__.py index c5b509f30..f8fefbe15 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -93,6 +93,7 @@ def register_blueprint(application): from app.organisation.rest import organisation_blueprint from app.dvla_organisation.rest import dvla_organisation_blueprint from app.delivery.rest import delivery_blueprint + from app.inbound_sms.rest import inbound_sms as inbound_sms_blueprint from app.notifications.receive_notifications import receive_notifications_blueprint from app.notifications.notifications_ses_callback import ses_callback_blueprint from app.notifications.notifications_sms_callback import sms_callback_blueprint @@ -133,6 +134,9 @@ def register_blueprint(application): delivery_blueprint.before_request(requires_admin_auth) application.register_blueprint(delivery_blueprint) + inbound_sms_blueprint.before_request(requires_admin_auth) + application.register_blueprint(inbound_sms_blueprint) + accept_invite.before_request(requires_admin_auth) application.register_blueprint(accept_invite, url_prefix='/invite') diff --git a/app/dao/inbound_sms_dao.py b/app/dao/inbound_sms_dao.py index 92f1c79e0..597748731 100644 --- a/app/dao/inbound_sms_dao.py +++ b/app/dao/inbound_sms_dao.py @@ -1,7 +1,30 @@ from app import db from app.dao.dao_utils import transactional +from app.models import InboundSms @transactional def dao_create_inbound_sms(inbound_sms): db.session.add(inbound_sms) + + +def dao_get_inbound_sms_for_service(service_id, limit=None, user_number=None): + q = InboundSms.query.filter( + InboundSms.service_id == service_id + ).order_by( + InboundSms.created_at.desc() + ) + + if user_number: + q = q.filter(InboundSms.user_number == user_number) + + if limit: + q = q.limit(limit) + + return q.all() + + +def dao_count_inbound_sms_for_service(service_id): + return InboundSms.query.filter( + InboundSms.service_id == service_id + ).count() diff --git a/app/inbound_sms/__init__.py b/app/inbound_sms/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/inbound_sms/rest.py b/app/inbound_sms/rest.py new file mode 100644 index 000000000..0ee2b9e90 --- /dev/null +++ b/app/inbound_sms/rest.py @@ -0,0 +1,41 @@ +from flask import ( + Blueprint, + jsonify, + request +) +from notifications_utils.recipients import normalise_phone_number + +from app.dao.inbound_sms_dao import dao_get_inbound_sms_for_service, dao_count_inbound_sms_for_service +from app.errors import register_errors + +inbound_sms = Blueprint( + 'inbound_sms', + __name__, + url_prefix='/service//inbound-sms' +) + +register_errors(inbound_sms) + + +@inbound_sms.route('') +def get_inbound_sms_for_service(service_id): + limit = request.args.get('limit') + user_number = request.args.get('user_number') + + if user_number: + user_number = normalise_phone_number(user_number) + + results = dao_get_inbound_sms_for_service(service_id, limit, user_number) + + return jsonify(data=[row.serialize() for row in results]) + + +@inbound_sms.route('/summary') +def get_inbound_sms_summary_for_service(service_id): + count = dao_count_inbound_sms_for_service(service_id) + most_recent = dao_get_inbound_sms_for_service(service_id, limit=1) + + return jsonify( + count=count, + most_recent=most_recent[0].created_at.isoformat() if most_recent else None + ) diff --git a/app/models.py b/app/models.py index 4680b6274..29272c380 100644 --- a/app/models.py +++ b/app/models.py @@ -1175,6 +1175,18 @@ class InboundSms(db.Model): def content(self, content): self._content = encryption.encrypt(content) + def serialize(self): + return { + 'id': str(self.id), + 'created_at': self.created_at.isoformat(), + 'service_id': str(self.service_id), + 'notify_number': self.notify_number, + 'user_number': self.user_number, + 'content': self.content, + 'provider_date': self.provider_date and self.provider_date.isoformat(), + 'provider_reference': self.provider_reference + } + class LetterRate(db.Model): __tablename__ = 'letter_rates' diff --git a/tests/app/dao/test_inbound_sms_dao.py b/tests/app/dao/test_inbound_sms_dao.py new file mode 100644 index 000000000..f0fc6d8b3 --- /dev/null +++ b/tests/app/dao/test_inbound_sms_dao.py @@ -0,0 +1,59 @@ +from datetime import datetime + +from freezegun import freeze_time + +from app.dao.inbound_sms_dao import dao_get_inbound_sms_for_service, dao_count_inbound_sms_for_service + +from tests.app.db import create_inbound_sms, create_service + + +def test_get_all_inbound_sms(sample_service): + inbound = create_inbound_sms(sample_service) + + res = dao_get_inbound_sms_for_service(sample_service.id) + assert len(res) == 1 + assert res[0] == inbound + + +def test_get_all_inbound_sms_when_none_exist(sample_service): + res = dao_get_inbound_sms_for_service(sample_service.id) + assert len(res) == 0 + + +def test_get_all_inbound_sms_limits_and_orders(sample_service): + with freeze_time('2017-01-01'): + one = create_inbound_sms(sample_service) + with freeze_time('2017-01-03'): + three = create_inbound_sms(sample_service) + with freeze_time('2017-01-02'): + two = create_inbound_sms(sample_service) + + res = dao_get_inbound_sms_for_service(sample_service.id, limit=2) + assert len(res) == 2 + assert res[0] == three + assert res[0].created_at == datetime(2017, 1, 3) + assert res[1] == two + assert res[1].created_at == datetime(2017, 1, 2) + + +def test_get_all_inbound_sms_filters_on_service(notify_db_session): + service_one = create_service(service_name='one') + service_two = create_service(service_name='two') + + sms_one = create_inbound_sms(service_one) + sms_two = create_inbound_sms(service_two) + + res = dao_get_inbound_sms_for_service(service_one.id) + assert len(res) == 1 + assert res[0] == sms_one + + +def test_count_inbound_sms_for_service(notify_db_session): + service_one = create_service(service_name='one') + service_two = create_service(service_name='two') + + create_inbound_sms(service_one) + create_inbound_sms(service_one) + create_inbound_sms(service_two) + + assert dao_count_inbound_sms_for_service(service_one.id) == 2 diff --git a/tests/app/db.py b/tests/app/db.py index 76b458dd9..cdc0d7a79 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -4,6 +4,7 @@ import uuid from app.dao.jobs_dao import dao_create_job from app.models import ( + InboundSms, Service, User, Template, @@ -20,6 +21,7 @@ from app.dao.notifications_dao import dao_create_notification, dao_created_sched from app.dao.templates_dao import dao_create_template from app.dao.services_dao import dao_create_service from app.dao.service_permissions_dao import dao_add_service_permission +from app.dao.inbound_sms_dao import dao_create_inbound_sms def create_user(mobile_number="+447700900986", email="notify@digital.cabinet-office.gov.uk", state='active'): @@ -183,3 +185,24 @@ def create_service_permission(service_id, permission=EMAIL_TYPE): service_permissions = ServicePermission.query.all() return service_permissions + + +def create_inbound_sms( + service, + notify_number=None, + user_number='7700900111', + provider_date=None, + provider_reference=None, + content='Hello' +): + inbound = InboundSms( + service=service, + created_at=datetime.utcnow(), + notify_number=notify_number or service.sms_sender, + user_number=user_number, + provider_date=provider_date or datetime.utcnow(), + provider_reference=provider_reference or 'foo', + content=content, + ) + dao_create_inbound_sms(inbound) + return inbound diff --git a/tests/app/inbound_sms/__init__.py b/tests/app/inbound_sms/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/app/inbound_sms/test_rest.py b/tests/app/inbound_sms/test_rest.py new file mode 100644 index 000000000..70acaddd2 --- /dev/null +++ b/tests/app/inbound_sms/test_rest.py @@ -0,0 +1,93 @@ +from datetime import datetime + +from freezegun import freeze_time + +from tests.app.db import create_inbound_sms, create_service + + +def test_get_inbound_sms(admin_request, sample_service): + one = create_inbound_sms(sample_service) + two = create_inbound_sms(sample_service) + + json_resp = admin_request.get( + 'inbound_sms.get_inbound_sms_for_service', + endpoint_kwargs={'service_id': sample_service.id} + ) + + sms = json_resp['data'] + + assert len(sms) == 2 + assert {inbound['id'] for inbound in sms} == {str(one.id), str(two.id)} + assert sms[0]['content'] == 'Hello' + assert set(sms[0].keys()) == { + 'id', + 'created_at', + 'service_id', + 'notify_number', + 'user_number', + 'content', + 'provider_date', + 'provider_reference' + } + + +def test_get_inbound_sms_limits(admin_request, sample_service): + with freeze_time('2017-01-01'): + one = create_inbound_sms(sample_service) + with freeze_time('2017-01-02'): + two = create_inbound_sms(sample_service) + + sms = admin_request.get( + 'inbound_sms.get_inbound_sms_for_service', + endpoint_kwargs={'service_id': sample_service.id, 'limit': 1} + ) + + assert len(sms['data']) == 1 + assert sms['data'][0]['id'] == str(two.id) + + +def test_get_inbound_sms_filters_user_number(admin_request, sample_service): + # user_number in the db is normalised + one = create_inbound_sms(sample_service, user_number='7700900001') + two = create_inbound_sms(sample_service, user_number='7700900002') + + sms = admin_request.get( + 'inbound_sms.get_inbound_sms_for_service', + endpoint_kwargs={'service_id': sample_service.id, 'user_number': '(07700) 900-001'} + ) + + assert len(sms['data']) == 1 + assert sms['data'][0]['id'] == str(one.id) + assert sms['data'][0]['user_number'] == str(one.user_number) + + +def test_get_inbound_sms_summary(admin_request, sample_service): + other_service = create_service(service_name='other_service') + with freeze_time('2017-01-01'): + create_inbound_sms(sample_service) + with freeze_time('2017-01-02'): + create_inbound_sms(sample_service) + with freeze_time('2017-01-03'): + create_inbound_sms(other_service) + + summary = admin_request.get( + 'inbound_sms.get_inbound_sms_summary_for_service', + endpoint_kwargs={'service_id': sample_service.id} + ) + + assert summary == { + 'count': 2, + 'most_recent': datetime(2017, 1, 2).isoformat() + } + + +def test_get_inbound_sms_summary_with_no_inbound(admin_request, sample_service): + summary = admin_request.get( + 'inbound_sms.get_inbound_sms_summary_for_service', + endpoint_kwargs={'service_id': sample_service.id} + ) + + assert summary == { + 'count': 0, + 'most_recent': None + }