diff --git a/app/__init__.py b/app/__init__.py index 0927cbca1..cb9e346a1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -5,7 +5,7 @@ import uuid from flask import Flask, _request_ctx_stack from flask import request, g, jsonify -from flask.ext.sqlalchemy import SQLAlchemy +from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from monotonic import monotonic from notifications_utils.clients.statsd.statsd_client import StatsdClient diff --git a/app/commands.py b/app/commands.py index f50508f5c..051cd7fc8 100644 --- a/app/commands.py +++ b/app/commands.py @@ -1,7 +1,7 @@ import uuid from datetime import datetime from decimal import Decimal -from flask.ext.script import Command, Manager, Option +from flask_script import Command, Manager, Option from app import db from app.dao.monthly_billing_dao import ( diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py index 5f137e778..4514d4310 100644 --- a/app/dao/notifications_dao.py +++ b/app/dao/notifications_dao.py @@ -270,27 +270,6 @@ def get_notifications_for_job(service_id, job_id, filter_dict=None, page=1, page ) -@statsd(namespace="dao") -def get_notification_billable_unit_count_per_month(service_id, year): - month = get_london_month_from_utc_column(NotificationHistory.created_at) - - start_date, end_date = get_financial_year(year) - notifications = db.session.query( - month, - func.sum(NotificationHistory.billable_units) - ).filter( - NotificationHistory.billable_units != 0, - NotificationHistory.service_id == service_id, - NotificationHistory.created_at.between(start_date, end_date) - ).group_by( - month - ).order_by( - month - ).all() - - return [(datetime.strftime(x[0], "%B"), x[1]) for x in notifications] - - @statsd(namespace="dao") def get_notification_with_personalisation(service_id, notification_id, key_type): filter_dict = {'service_id': service_id, 'id': notification_id} diff --git a/app/encryption.py b/app/encryption.py index 2b4755803..34577c0d6 100644 --- a/app/encryption.py +++ b/app/encryption.py @@ -1,4 +1,4 @@ -from flask.ext.bcrypt import generate_password_hash, check_password_hash +from flask_bcrypt import generate_password_hash, check_password_hash from itsdangerous import URLSafeSerializer diff --git a/app/service/rest.py b/app/service/rest.py index 25a4052df..485806ea9 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -450,16 +450,6 @@ def resume_service(service_id): return '', 204 -@service_blueprint.route('//billable-units') -def get_billable_unit_count(service_id): - try: - return jsonify(notifications_dao.get_notification_billable_unit_count_per_month( - service_id, int(request.args.get('year')) - )) - except TypeError: - return jsonify(result='error', message='No valid year provided'), 400 - - @service_blueprint.route('//notifications/templates/monthly', methods=['GET']) def get_monthly_template_stats(service_id): service = dao_fetch_service_by_id(service_id) diff --git a/application.py b/application.py index 5ff7dd1e4..0cb5377d8 100644 --- a/application.py +++ b/application.py @@ -2,8 +2,8 @@ from __future__ import print_function import os -from flask.ext.script import Manager, Server -from flask.ext.migrate import Migrate, MigrateCommand +from flask_script import Manager, Server +from flask_migrate import Migrate, MigrateCommand from app import (create_app, db, commands) application = create_app() diff --git a/db.py b/db.py index bc558cbd2..7be1aa052 100644 --- a/db.py +++ b/db.py @@ -1,4 +1,4 @@ -from flask.ext.script import Manager, Server +from flask_script import Manager, Server from flask_migrate import Migrate, MigrateCommand from app import create_app, db diff --git a/requirements.txt b/requirements.txt index 131d9c523..49bff4d46 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,25 +1,24 @@ apispec==0.25.0 -Flask==0.10.1 -Flask-Script==2.0.5 -Flask-Migrate==2.1.0 -Flask-SQLAlchemy==2.0 -psycopg2==2.7.3 -SQLAlchemy==1.0.15 -SQLAlchemy-Utils==0.32.14 -PyJWT==1.5.2 -marshmallow==2.4.2 -marshmallow-sqlalchemy==0.13.1 -flask-marshmallow==0.6.2 -Flask-Bcrypt==0.6.2 -boto3==1.4.4 -monotonic==1.3 -statsd==3.2.1 -jsonschema==2.6.0 -gunicorn==19.7.1 -docopt==0.6.2 -six==1.10.0 -iso8601==0.1.12 +boto3==1.4.6 celery==3.1.25 # pyup: <4 +docopt==0.6.2 +Flask-Bcrypt==0.6.2 +Flask-Migrate==2.1.0 +Flask-Script==2.0.5 +Flask-SQLAlchemy==2.2 +Flask==0.12.2 +gunicorn==19.7.1 +iso8601==0.1.12 +jsonschema==2.6.0 +marshmallow-sqlalchemy==0.13.1 +marshmallow==2.13.6 +monotonic==1.3 +psycopg2==2.7.3 +PyJWT==1.5.2 +six==1.10.0 +SQLAlchemy-Utils==0.32.14 +SQLAlchemy==1.1.13 +statsd==3.2.1 notifications-python-client==4.3.1 diff --git a/server_commands.py b/server_commands.py index af071db47..9f0ce8a60 100644 --- a/server_commands.py +++ b/server_commands.py @@ -1,4 +1,4 @@ -from flask.ext.script import Manager, Server +from flask_script import Manager, Server from flask_migrate import Migrate, MigrateCommand from app import (create_app, db, commands) import os diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index c6b09e333..717dc7b82 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -1184,10 +1184,13 @@ def test_send_inbound_sms_to_service_retries_if_request_returns_500(notify_api, status_code=500) send_inbound_sms_to_service(inbound_sms.id, inbound_sms.service_id) - mocked.assert_called_with( - exc='Unable to send_inbound_sms_to_service for service_id: {} ' - 'and url: {}. \n500 Server Error: None'.format(sample_service.id, inbound_api.url), - queue="retry-tasks") + exc_msg = 'Unable to send_inbound_sms_to_service for service_id: {} and url: {url}'.format( + sample_service.id, + url=inbound_api.url + ) + assert mocked.call_count == 1 + assert mocked.call_args[1]['queue'] == 'retry-tasks' + assert exc_msg in mocked.call_args[1]['exc'] def test_send_inbound_sms_to_service_does_not_retries_if_request_returns_404(notify_api, sample_service, mocker): diff --git a/tests/app/dao/test_notification_dao.py b/tests/app/dao/test_notification_dao.py index db5892955..7d191da56 100644 --- a/tests/app/dao/test_notification_dao.py +++ b/tests/app/dao/test_notification_dao.py @@ -31,7 +31,6 @@ from app.dao.notifications_dao import ( delete_notifications_created_more_than_a_week_ago_by_type, get_notification_by_id, get_notification_for_job, - get_notification_billable_unit_count_per_month, get_notification_with_personalisation, get_notifications_for_job, get_notifications_for_service, @@ -914,38 +913,6 @@ def test_get_all_notifications_for_job_by_status(notify_db, notify_db_session, s assert len(notifications(filter_dict={'status': NOTIFICATION_STATUS_TYPES[:3]}).items) == 3 -def test_get_notification_billable_unit_count_per_month(notify_db, notify_db_session, sample_service): - - for year, month, day, hour, minute, second in ( - (2017, 1, 15, 23, 59, 59), # ↓ 2016 financial year - (2016, 9, 30, 23, 59, 59), # counts in October with BST conversion - (2016, 6, 30, 23, 50, 20), - (2016, 7, 15, 9, 20, 25), - (2016, 4, 1, 1, 1, 00), - (2016, 4, 1, 0, 0, 00), - (2016, 3, 31, 23, 00, 1), # counts in April with BST conversion - (2015, 4, 1, 13, 8, 59), # ↓ 2015 financial year - (2015, 11, 20, 22, 40, 45), - (2016, 1, 31, 23, 30, 40) # counts in January no BST conversion in winter - ): - sample_notification( - notify_db, notify_db_session, service=sample_service, - created_at=datetime( - year, month, day, hour, minute, second, 0 - ) - ) - - for financial_year, months in ( - (2017, []), - (2016, [('April', 3), ('July', 2), ('October', 1), ('January', 1)]), - (2015, [('April', 1), ('November', 1), ('January', 1)]), - (2014, []) - ): - assert get_notification_billable_unit_count_per_month( - sample_service.id, financial_year - ) == months - - def test_update_notification_sets_status(sample_notification): assert sample_notification.status == 'created' sample_notification.status = 'failed' diff --git a/tests/app/notifications/test_receive_notification.py b/tests/app/notifications/test_receive_notification.py index d1f14cc13..b8bf2da9b 100644 --- a/tests/app/notifications/test_receive_notification.py +++ b/tests/app/notifications/test_receive_notification.py @@ -41,56 +41,65 @@ def test_receive_notification_returns_received_to_mmg(client, mocker, sample_ser [str(inbound_sms_id), str(sample_service_full_permissions.id)], queue="notify-internal-tasks") -@pytest.mark.parametrize('provider,headers,data,expected_response', [ - ( - 'mmg', - [('Content-Type', 'application/json')], - json.dumps({ - "ID": "1234", - "MSISDN": "447700900855", - "Message": "Some message to notify", - "Trigger": "Trigger?", - "Number": "testing", - "Channel": "SMS", - "DateRecieved": "2012-06-27 12:33:00" - }), - 'RECEIVED' - ), - ( - 'firetext', - None, - { - "Message": "Some message to notify", - "source": "Source", - "time": "2012-06-27 12:33:00", - "destination": "447700900855" - }, - '{\n "status": "ok"\n}' - ), -]) @pytest.mark.parametrize('permissions', [ - ([SMS_TYPE]), - ([INBOUND_SMS_TYPE]), + [SMS_TYPE], + [INBOUND_SMS_TYPE], ]) -def test_receive_notification_without_permissions_does_not_create_inbound( - client, mocker, notify_db, notify_db_session, permissions, provider, headers, data, expected_response): - service = sample_service(notify_db, notify_db_session, permissions=permissions) - mocker.patch("app.notifications.receive_notifications.dao_fetch_services_by_sms_sender", - return_value=[service]) - mocked_send_inbound_sms = mocker.patch( - "app.notifications.receive_notifications.tasks.send_inbound_sms_to_service.apply_async") - mocked_has_permissions = mocker.patch( - "app.notifications.receive_notifications.has_inbound_sms_permissions", return_value=False) - - response = client.post(path='/notifications/sms/receive/{}'.format(provider), - data=data, - headers=headers) +def test_receive_notification_from_mmg_without_permissions_does_not_persist( + client, + mocker, + notify_db_session, + permissions +): + mocked = mocker.patch("app.notifications.receive_notifications.tasks.send_inbound_sms_to_service.apply_async") + service = create_service(sms_sender='07111111111', service_permissions=permissions) + data = { + "ID": "1234", + "MSISDN": "07111111111", + "Message": "Some message to notify", + "Trigger": "Trigger?", + "Number": "testing", + "Channel": "SMS", + "DateRecieved": "2012-06-27 12:33:00" + } + response = client.post( + path='/notifications/sms/receive/mmg', + data=json.dumps(data), + headers=[('Content-Type', 'application/json')] + ) assert response.status_code == 200 - assert response.get_data(as_text=True) == expected_response - assert len(InboundSms.query.all()) == 0 - assert mocked_has_permissions.called - mocked_send_inbound_sms.assert_not_called() + assert response.get_data(as_text=True) == 'RECEIVED' + assert InboundSms.query.count() == 0 + assert mocked.called is False + + +@pytest.mark.parametrize('permissions', [ + [SMS_TYPE], + [INBOUND_SMS_TYPE], +]) +def test_receive_notification_from_firetext_without_permissions_does_not_persist( + client, + mocker, + notify_db_session, + permissions +): + service = create_service(sms_sender='07111111111', service_permissions=permissions) + mocked = mocker.patch("app.notifications.receive_notifications.tasks.send_inbound_sms_to_service.apply_async") + + data = "source=07999999999&destination=07111111111&message=this is a message&time=2017-01-01 12:00:00" + response = client.post( + path='/notifications/sms/receive/firetext', + data=data, + headers=[('Content-Type', 'application/x-www-form-urlencoded')] + ) + + assert response.status_code == 200 + result = json.loads(response.get_data(as_text=True)) + + assert result['status'] == 'ok' + assert InboundSms.query.count() == 0 + assert mocked.called is False def test_receive_notification_without_permissions_does_not_create_inbound_even_with_inbound_number_set( diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 48225ab2f..0c6397cbb 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1665,30 +1665,6 @@ def test_get_detailed_services_for_date_range(notify_db, notify_db_session, set_ } -@freeze_time('2012-12-12T12:00:01') -def test_get_notification_billable_unit_count(client, notify_db, notify_db_session): - notification = create_sample_notification(notify_db, notify_db_session) - response = client.get( - '/service/{}/billable-units?year=2012'.format(notification.service_id), - headers=[create_authorization_header()] - ) - assert response.status_code == 200 - assert json.loads(response.get_data(as_text=True)) == { - 'December': 1 - } - - -def test_get_notification_billable_unit_count_missing_year(client, sample_service): - response = client.get( - '/service/{}/billable-units'.format(sample_service.id), - headers=[create_authorization_header()] - ) - assert response.status_code == 400 - assert json.loads(response.get_data(as_text=True)) == { - 'message': 'No valid year provided', 'result': 'error' - } - - @pytest.mark.parametrize('query_string, expected_status, expected_json', [ ('', 200, {'data': {'email_count': 0, 'sms_count': 0}}), ('?year=2000', 200, {'data': {'email_count': 0, 'sms_count': 0}}), diff --git a/tests/conftest.py b/tests/conftest.py index 31de496db..191fa5e56 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,8 +5,8 @@ import boto3 import pytest from alembic.command import upgrade from alembic.config import Config -from flask.ext.migrate import Migrate, MigrateCommand -from flask.ext.script import Manager +from flask_migrate import Migrate, MigrateCommand +from flask_script import Manager from app import create_app, db @@ -18,16 +18,16 @@ def notify_api(): # deattach server-error error handlers - error_handler_spec looks like: # {'blueprint_name': { # status_code: [error_handlers], - # None: [ tuples of (exception, )] + # None: { ExceptionClass: error_handler } # }} for error_handlers in app.error_handler_spec.values(): error_handlers.pop(500, None) if None in error_handlers: - error_handlers[None] = [ - exception_handler - for exception_handler in error_handlers[None] - if exception_handler[0] != Exception - ] + error_handlers[None] = { + exc_class: error_handler + for exc_class, error_handler in error_handlers[None].items() + if exc_class != Exception + } if error_handlers[None] == []: error_handlers.pop(None)