From 6356a5320a07b5c8b5fcebbad041a9936c8552f3 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Wed, 27 Sep 2017 10:36:25 +0100 Subject: [PATCH 01/14] Updated model with a new table notification_to_email_sender and created db migration script --- app/models.py | 21 +++++++++++++ .../versions/0123_add_noti_to_email_sender.py | 31 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 migrations/versions/0123_add_noti_to_email_sender.py diff --git a/app/models.py b/app/models.py index be6d54cf1..e0c56f36a 100644 --- a/app/models.py +++ b/app/models.py @@ -1414,3 +1414,24 @@ class ServiceLetterContact(db.Model): 'created_at': self.created_at.strftime(DATETIME_FORMAT), 'updated_at': self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None } + + +class NotificationSmsSender(db.Model): + __tablename__ = "notification_to_email_sender" + + notification_id = db.Column( + UUID(as_uuid=True), + db.ForeignKey('notifications.id'), + unique=False, + index=True, + nullable=False, + primary_key=True + ) + service_email_reply_to_id = db.Column( + UUID(as_uuid=True), + db.ForeignKey('service_email_reply_to.id'), + unique=False, + index=True, + nullable=False, + primary_key=True + ) diff --git a/migrations/versions/0123_add_noti_to_email_sender.py b/migrations/versions/0123_add_noti_to_email_sender.py new file mode 100644 index 000000000..2a49df3da --- /dev/null +++ b/migrations/versions/0123_add_noti_to_email_sender.py @@ -0,0 +1,31 @@ +""" + +Revision ID: 0123_add_noti_to_email_sender +Revises: 0122_add_service_letter_contact +Create Date: 2017-09-27 09:42:39.412731 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +revision = '0123_add_noti_to_email_sender' +down_revision = '0122_add_service_letter_contact' + + +def upgrade(): + op.create_table('notification_to_email_sender', + sa.Column('notification_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('service_email_reply_to_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.ForeignKeyConstraint(['notification_id'], ['notifications.id'], ), + sa.ForeignKeyConstraint(['service_email_reply_to_id'], ['service_email_reply_to.id'], ), + sa.PrimaryKeyConstraint('notification_id', 'service_email_reply_to_id') + ) + op.create_index(op.f('ix_notification_to_email_sender_notification_id'), 'notification_to_email_sender', ['notification_id'], unique=False) + op.create_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), 'notification_to_email_sender', ['service_email_reply_to_id'], unique=False) + + +def downgrade(): + op.drop_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), table_name='notification_to_email_sender') + op.drop_index(op.f('ix_notification_to_email_sender_notification_id'), table_name='notification_to_email_sender') + op.drop_table('notification_to_email_sender') From 912728713a71fe65888c747cbcd3002d1d4d5f18 Mon Sep 17 00:00:00 2001 From: Athanasios Voutsadakis Date: Wed, 27 Sep 2017 12:11:44 +0100 Subject: [PATCH 02/14] Unbind the db service before pushing the app This should cause the app to get new credentials for the database upon pushing. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index d8c650d5f..eac8473d8 100644 --- a/Makefile +++ b/Makefile @@ -283,6 +283,7 @@ cf-deploy: ## Deploys the app to Cloud Foundry .PHONY: cf-deploy-api-db-migration cf-deploy-api-db-migration: $(if ${CF_SPACE},,$(error Must specify CF_SPACE)) + cf unbind-service notify-api-db-migration notify-db cf push notify-api-db-migration -f manifest-api-${CF_SPACE}.yml cf run-task notify-api-db-migration "python db.py db upgrade" --name api_db_migration From fdc4d4c24f1011d7f261a3ab28f0a5bb2aca4ccf Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Thu, 28 Sep 2017 11:11:41 +0100 Subject: [PATCH 03/14] Reanmed the link table to be a bteer description of what the table is for as it was previously confusing. Updated the migration script to reflect those changes --- app/models.py | 4 ++-- ...sender.py => 0123_add_noti_to_email_reply.py} | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) rename migrations/versions/{0123_add_noti_to_email_sender.py => 0123_add_noti_to_email_reply.py} (65%) diff --git a/app/models.py b/app/models.py index e0c56f36a..1103ce4f7 100644 --- a/app/models.py +++ b/app/models.py @@ -1416,8 +1416,8 @@ class ServiceLetterContact(db.Model): } -class NotificationSmsSender(db.Model): - __tablename__ = "notification_to_email_sender" +class NotificationEmailReplyTo(db.Model): + __tablename__ = "notification_to_email_reply_to" notification_id = db.Column( UUID(as_uuid=True), diff --git a/migrations/versions/0123_add_noti_to_email_sender.py b/migrations/versions/0123_add_noti_to_email_reply.py similarity index 65% rename from migrations/versions/0123_add_noti_to_email_sender.py rename to migrations/versions/0123_add_noti_to_email_reply.py index 2a49df3da..e7484b327 100644 --- a/migrations/versions/0123_add_noti_to_email_sender.py +++ b/migrations/versions/0123_add_noti_to_email_reply.py @@ -1,6 +1,6 @@ """ -Revision ID: 0123_add_noti_to_email_sender +Revision ID: 0123_add_noti_to_email_reply Revises: 0122_add_service_letter_contact Create Date: 2017-09-27 09:42:39.412731 @@ -9,23 +9,23 @@ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql -revision = '0123_add_noti_to_email_sender' +revision = '0123_add_noti_to_email_reply' down_revision = '0122_add_service_letter_contact' def upgrade(): - op.create_table('notification_to_email_sender', + op.create_table('notification_to_email_reply_to', sa.Column('notification_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('service_email_reply_to_id', postgresql.UUID(as_uuid=True), nullable=False), sa.ForeignKeyConstraint(['notification_id'], ['notifications.id'], ), sa.ForeignKeyConstraint(['service_email_reply_to_id'], ['service_email_reply_to.id'], ), sa.PrimaryKeyConstraint('notification_id', 'service_email_reply_to_id') ) - op.create_index(op.f('ix_notification_to_email_sender_notification_id'), 'notification_to_email_sender', ['notification_id'], unique=False) - op.create_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), 'notification_to_email_sender', ['service_email_reply_to_id'], unique=False) + op.create_index(op.f('ix_notification_to_email_sender_notification_id'), 'notification_to_email_reply_to', ['notification_id'], unique=False) + op.create_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), 'notification_to_email_reply_to', ['service_email_reply_to_id'], unique=False) def downgrade(): - op.drop_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), table_name='notification_to_email_sender') - op.drop_index(op.f('ix_notification_to_email_sender_notification_id'), table_name='notification_to_email_sender') - op.drop_table('notification_to_email_sender') + op.drop_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), table_name='notification_to_email_reply_to') + op.drop_index(op.f('ix_notification_to_email_sender_notification_id'), table_name='notification_to_email_reply_to') + op.drop_table('notification_to_email_reply_to') From 1dd3298290598f64993b396e094a6c218fae2603 Mon Sep 17 00:00:00 2001 From: Imdad Ahad Date: Mon, 25 Sep 2017 16:53:15 +0100 Subject: [PATCH 04/14] Add command to populate new service letter contact table from existing data --- app/commands.py | 20 ++++++++++++++++++++ application.py | 1 + 2 files changed, 21 insertions(+) diff --git a/app/commands.py b/app/commands.py index 16724f395..b9825d8d6 100644 --- a/app/commands.py +++ b/app/commands.py @@ -285,3 +285,23 @@ class PopulateServiceSmsSender(Command): print("Populated sms sender {} services from services".format(second_result.rowcount)) print("{} services in table".format(services_count_query)) print("{} service_sms_senders".format(service_sms_sender_count_query)) + + +class PopulateServiceLetterContact(Command): + + def run(self): + services_to_update = """ + INSERT INTO service_letter_contacts(id, service_id, contact_block, is_default, created_at) + SELECT uuid_in(md5(random()::text || now()::text)::cstring), id, letter_contact_block, true, '{}' + FROM services + WHERE letter_contact_block IS NOT NULL + AND id NOT IN( + SELECT service_id + FROM service_letter_contacts + ) + """.format(datetime.utcnow()) + + result = db.session.execute(services_to_update) + db.session.commit() + + print("Populated letter contacts for {} services".format(result.rowcount)) diff --git a/application.py b/application.py index d6876a85e..0cee490a5 100644 --- a/application.py +++ b/application.py @@ -20,6 +20,7 @@ manager.add_command('populate_monthly_billing', commands.PopulateMonthlyBilling) manager.add_command('backfill_processing_time', commands.BackfillProcessingTime) manager.add_command('populate_service_email_reply_to', commands.PopulateServiceEmailReplyTo) manager.add_command('populate_service_sms_sender', commands.PopulateServiceSmsSender) +manager.add_command('populate_service_letter_contact', commands.PopulateServiceLetterContact) @manager.command From 9a5addb896d4c7d6b579fc456cb2b771fe4c2d80 Mon Sep 17 00:00:00 2001 From: venusbb Date: Mon, 2 Oct 2017 12:00:52 +0100 Subject: [PATCH 05/14] Reverse codes that uses filtering of trial mode services --- app/dao/services_dao.py | 7 +------ app/service/rest.py | 10 +++------- tests/app/service/test_rest.py | 6 ++---- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/app/dao/services_dao.py b/app/dao/services_dao.py index 919856416..5ae25c0e6 100644 --- a/app/dao/services_dao.py +++ b/app/dao/services_dao.py @@ -358,15 +358,13 @@ def dao_fetch_monthly_historical_stats_for_service(service_id, year): @statsd(namespace='dao') -def dao_fetch_todays_stats_for_all_services(include_from_test_key=True, trial_mode_services=None): +def dao_fetch_todays_stats_for_all_services(include_from_test_key=True): query = db.session.query( Notification.notification_type, Notification.status, Notification.service_id, func.count(Notification.id).label('count') - ).join( - Service ).filter( func.date(Notification.created_at) == date.today(), ).group_by( @@ -380,9 +378,6 @@ def dao_fetch_todays_stats_for_all_services(include_from_test_key=True, trial_mo if not include_from_test_key: query = query.filter(Notification.key_type != KEY_TYPE_TEST) - if trial_mode_services is not None: - query = query.filter(Service.restricted == trial_mode_services) - return query.all() diff --git a/app/service/rest.py b/app/service/rest.py index 1a1ab79cc..df1457556 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -90,7 +90,6 @@ def get_services(): detailed = request.args.get('detailed') == 'True' user_id = request.args.get('user_id', None) include_from_test_key = request.args.get('include_from_test_key', 'True') != 'False' - trial_mode_services = request.args.get('trial_mode_services') # If start and end date are not set, we are expecting today's stats. today = str(datetime.utcnow().date()) @@ -103,8 +102,7 @@ def get_services(): elif detailed: result = jsonify(data=get_detailed_services(start_date=start_date, end_date=end_date, only_active=only_active, - include_from_test_key=include_from_test_key, - trial_mode_services=trial_mode_services + include_from_test_key=include_from_test_key )) return result else: @@ -369,12 +367,10 @@ def get_detailed_service(service_id, today_only=False): return detailed_service_schema.dump(service).data -def get_detailed_services(start_date, end_date, only_active=False, include_from_test_key=True, - trial_mode_services=None): +def get_detailed_services(start_date, end_date, only_active=False, include_from_test_key=True): services = {service.id: service for service in dao_fetch_all_services(only_active)} if start_date == datetime.utcnow().date(): - stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=include_from_test_key, - trial_mode_services=trial_mode_services) + stats = dao_fetch_todays_stats_for_all_services(include_from_test_key=include_from_test_key) else: stats = fetch_stats_by_date_range_for_all_services(start_date=start_date, diff --git a/tests/app/service/test_rest.py b/tests/app/service/test_rest.py index 5227206a2..c41b4ff13 100644 --- a/tests/app/service/test_rest.py +++ b/tests/app/service/test_rest.py @@ -1548,8 +1548,7 @@ def test_get_services_with_detailed_flag_accepts_date_range(client, mocker): start_date=date(2001, 1, 1), end_date=date(2002, 2, 2), only_active=ANY, - include_from_test_key=ANY, - trial_mode_services=ANY + include_from_test_key=ANY ) assert resp.status_code == 200 @@ -1566,8 +1565,7 @@ def test_get_services_with_detailed_flag_defaults_to_today(client, mocker): end_date=date(2002, 2, 2), include_from_test_key=ANY, only_active=ANY, - start_date=date(2002, 2, 2), - trial_mode_services=ANY + start_date=date(2002, 2, 2) ) assert resp.status_code == 200 From 2c590acff57987a93a06fa37b827f557cd123b74 Mon Sep 17 00:00:00 2001 From: Athanasios Voutsadakis Date: Mon, 2 Oct 2017 16:25:49 +0100 Subject: [PATCH 06/14] Also unbind notify-config and notify-aws --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index eac8473d8..aac64e7c4 100644 --- a/Makefile +++ b/Makefile @@ -284,6 +284,8 @@ cf-deploy: ## Deploys the app to Cloud Foundry cf-deploy-api-db-migration: $(if ${CF_SPACE},,$(error Must specify CF_SPACE)) cf unbind-service notify-api-db-migration notify-db + cf unbind-service notify-api-db-migration notify-config + cf unbind-service notify-api-db-migration notify-aws cf push notify-api-db-migration -f manifest-api-${CF_SPACE}.yml cf run-task notify-api-db-migration "python db.py db upgrade" --name api_db_migration From d21d9cabd134abda85668755839d8c21fb10e8ef Mon Sep 17 00:00:00 2001 From: Imdad Ahad Date: Tue, 26 Sep 2017 15:06:09 +0100 Subject: [PATCH 07/14] Use new default letter contact in the DVLA celery task --- app/celery/tasks.py | 2 +- tests/app/celery/test_tasks.py | 35 +++++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/app/celery/tasks.py b/app/celery/tasks.py index a34cae506..a64bb9cae 100644 --- a/app/celery/tasks.py +++ b/app/celery/tasks.py @@ -370,7 +370,7 @@ def create_dvla_file_contents_for_notifications(notifications): notification.template.__dict__, notification.personalisation, notification_reference=notification.reference, - contact_block=notification.service.letter_contact_block, + contact_block=notification.service.get_default_letter_contact(), org_id=notification.service.dvla_organisation.id, )) for notification in notifications diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index e16f819ea..574ed2eb7 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -2,7 +2,6 @@ import json import uuid from datetime import datetime from unittest.mock import Mock - import pytest import requests_mock from flask import current_app @@ -29,14 +28,17 @@ from app.celery.tasks import ( from app.config import QueueNames from app.dao import jobs_dao, services_dao from app.models import ( - Notification, + EMAIL_TYPE, + JOB_STATUS_ERROR, + KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, - KEY_TYPE_NORMAL, - SMS_TYPE, - EMAIL_TYPE, LETTER_TYPE, - Job) + SERVICE_PERMISSION_TYPES, + SMS_TYPE, + Job, + Notification +) from tests.app import load_example_csv from tests.app.conftest import ( @@ -46,7 +48,16 @@ from tests.app.conftest import ( sample_email_template as create_sample_email_template, sample_notification as create_sample_notification ) -from tests.app.db import create_user, create_notification, create_job, create_service_inbound_api, create_inbound_sms +from tests.app.db import ( + create_inbound_sms, + create_job, + create_letter_contact, + create_notification, + create_service_inbound_api, + create_service, + create_template, + create_user +) class AnyStringWith(str): @@ -1076,8 +1087,11 @@ def test_build_dvla_file_retries_if_all_notifications_are_not_created(sample_let mocked_send_task.assert_not_called() -def test_create_dvla_file_contents_for_job(sample_letter_template, mocker): - job = create_job(template=sample_letter_template, notification_count=2) +def test_create_dvla_file_contents(notify_db_session, mocker): + service = create_service(service_permissions=SERVICE_PERMISSION_TYPES) + create_letter_contact(service=service, contact_block='London,\nNW1A 1AA') + letter_template = create_template(service=service, template_type=LETTER_TYPE) + job = create_job(template=letter_template, notification_count=2) create_notification(template=job.template, job=job, reference=1) create_notification(template=job.template, job=job, reference=2) mocked_letter_template = mocker.patch("app.celery.tasks.LetterDVLATemplate") @@ -1093,9 +1107,8 @@ def test_create_dvla_file_contents_for_job(sample_letter_template, mocker): # Personalisation assert not calls[0][0][1] assert not calls[1][0][1] - # Named arguments - assert calls[1][1]['contact_block'] == 'London,\nSW1A 1AA' + assert calls[1][1]['contact_block'] == 'London,\nNW1A 1AA' assert calls[0][1]['notification_reference'] == '1' assert calls[1][1]['notification_reference'] == '2' assert calls[1][1]['org_id'] == '001' From 4eebd9a83c36c4284da02eec26cf8c35e7a0a285 Mon Sep 17 00:00:00 2001 From: Imdad Ahad Date: Tue, 26 Sep 2017 15:08:08 +0100 Subject: [PATCH 08/14] Remove the letter_contact_block from the Service model --- app/models.py | 6 ++---- tests/app/test_model.py | 5 ----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/app/models.py b/app/models.py index 930fe0744..a7ecfdddd 100644 --- a/app/models.py +++ b/app/models.py @@ -208,8 +208,7 @@ class Service(db.Model, Versioned): created_by = db.relationship('User') created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False) _reply_to_email_address = db.Column("reply_to_email_address", db.Text, index=False, unique=False, nullable=True) - letter_contact_block = db.Column(db.Text, index=False, unique=False, nullable=True) - # This column is now deprecated + _letter_contact_block = db.Column('letter_contact_block', db.Text, index=False, unique=False, nullable=True) sms_sender = db.Column(db.String(11), nullable=False, default=lambda: current_app.config['FROM_NUMBER']) organisation_id = db.Column(UUID(as_uuid=True), db.ForeignKey('organisation.id'), index=True, nullable=True) organisation = db.relationship('Organisation') @@ -268,8 +267,7 @@ class Service(db.Model, Versioned): if len(default_letter_contact) > 1: raise Exception("There should only ever be one default") else: - return default_letter_contact[0].contact_block if default_letter_contact else \ - self.letter_contact_block # need to update this to None after dropping the letter_contact_block column + return default_letter_contact[0].contact_block if default_letter_contact else None class InboundNumber(db.Model): diff --git a/tests/app/test_model.py b/tests/app/test_model.py index 0303782ff..03f3fb146 100644 --- a/tests/app/test_model.py +++ b/tests/app/test_model.py @@ -274,8 +274,3 @@ def test_service_get_default_contact_letter(sample_service): create_letter_contact(service=sample_service, contact_block='London,\nNW1A 1AA') assert sample_service.get_default_letter_contact() == 'London,\nNW1A 1AA' - - -# this test will need to be removed after letter_contact_block is dropped -def test_service_get_default_letter_contact_block_from_service(sample_service): - assert sample_service.get_default_letter_contact() == sample_service.letter_contact_block From cb37ba5f783c1a6dd65171526235a40ef8725b52 Mon Sep 17 00:00:00 2001 From: Imdad Ahad Date: Tue, 26 Sep 2017 15:09:02 +0100 Subject: [PATCH 09/14] Update schema to return default letter contact for a service --- app/schemas.py | 6 +++++- app/service/rest.py | 1 - tests/app/conftest.py | 3 +-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/schemas.py b/app/schemas.py index e880a1589..38799baa2 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -183,6 +183,7 @@ class ServiceSchema(BaseSchema): permissions = fields.Method("service_permissions") override_flag = False reply_to_email_address = fields.Method(method_name="get_reply_to_email_address") + letter_contact_block = fields.Method(method_name="get_letter_contact") def get_free_sms_fragment_limit(selfs, service): return service.free_sms_fragment_limit() @@ -193,9 +194,12 @@ class ServiceSchema(BaseSchema): def get_reply_to_email_address(self, service): return service.get_default_reply_to_email_address() + def get_letter_contact(self, service): + return service.get_default_letter_contact() + class Meta: model = models.Service - dump_only = ['free_sms_fragment_limit', 'reply_to_email_address'] + dump_only = ['free_sms_fragment_limit', 'reply_to_email_address', 'letter_contact_block'] exclude = ( 'updated_at', 'created_at', diff --git a/app/service/rest.py b/app/service/rest.py index df1457556..c4a2d9e59 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -147,7 +147,6 @@ def update_service(service_id): fetched_service = dao_fetch_service_by_id(service_id) # Capture the status change here as Marshmallow changes this later service_going_live = fetched_service.restricted and not req_json.get('restricted', True) - current_data = dict(service_schema.dump(fetched_service).data.items()) current_data.update(request.get_json()) update_dict = service_schema.load(current_data).data diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 042a20905..86c748df3 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -150,8 +150,7 @@ def sample_service( 'message_limit': limit, 'restricted': restricted, 'email_from': email_from, - 'created_by': user, - 'letter_contact_block': letter_contact_block, + 'created_by': user } service = Service.query.filter_by(name=service_name).first() if not service: From d8e1a34610bd0ba7a498a3bdbae936c77e768ed8 Mon Sep 17 00:00:00 2001 From: Richard Chapman Date: Tue, 3 Oct 2017 11:03:31 +0100 Subject: [PATCH 10/14] Added a unique constraint to the notification_id column of the notification_to_email_reply_to table so that each notification can only have one mapping to service_email_reply_to and hence one email address. --- app/models.py | 2 +- migrations/versions/0123_add_noti_to_email_reply.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/models.py b/app/models.py index 1103ce4f7..d1909d67c 100644 --- a/app/models.py +++ b/app/models.py @@ -1422,7 +1422,7 @@ class NotificationEmailReplyTo(db.Model): notification_id = db.Column( UUID(as_uuid=True), db.ForeignKey('notifications.id'), - unique=False, + unique=True, index=True, nullable=False, primary_key=True diff --git a/migrations/versions/0123_add_noti_to_email_reply.py b/migrations/versions/0123_add_noti_to_email_reply.py index e7484b327..f16c1605d 100644 --- a/migrations/versions/0123_add_noti_to_email_reply.py +++ b/migrations/versions/0123_add_noti_to_email_reply.py @@ -21,11 +21,10 @@ def upgrade(): sa.ForeignKeyConstraint(['service_email_reply_to_id'], ['service_email_reply_to.id'], ), sa.PrimaryKeyConstraint('notification_id', 'service_email_reply_to_id') ) - op.create_index(op.f('ix_notification_to_email_sender_notification_id'), 'notification_to_email_reply_to', ['notification_id'], unique=False) - op.create_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), 'notification_to_email_reply_to', ['service_email_reply_to_id'], unique=False) - + op.create_index(op.f('ix_notification_to_email_reply_to_notification_id'), 'notification_to_email_reply_to', ['notification_id'], unique=True) + op.create_index(op.f('ix_notification_to_email_reply_to_service_email_reply_to_id'), 'notification_to_email_reply_to', ['service_email_reply_to_id'], unique=False) def downgrade(): - op.drop_index(op.f('ix_notification_to_email_sender_service_email_reply_to_id'), table_name='notification_to_email_reply_to') - op.drop_index(op.f('ix_notification_to_email_sender_notification_id'), table_name='notification_to_email_reply_to') + op.drop_index(op.f('ix_notification_to_email_reply_to_service_email_reply_to_id'), table_name='notification_to_email_reply_to') + op.drop_index(op.f('ix_notification_to_email_reply_to_notification_id'), table_name='notification_to_email_reply_to') op.drop_table('notification_to_email_reply_to') From 244bacc7636dd07dea751ae18a677d98a9f1e5f9 Mon Sep 17 00:00:00 2001 From: Katie Smith Date: Tue, 3 Oct 2017 11:27:07 +0100 Subject: [PATCH 11/14] Update runtime Python version to 3.5.4 See https://github.com/cloudfoundry/python-buildpack/commit/40995d807b5cab59a840ed2278a904af13aab3c7 --- runtime.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime.txt b/runtime.txt index c0354eefe..13751becb 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -python-3.5.2 +python-3.5.4 From dede336b3bcbfee9083cb2c05f2b97c2e47f11a4 Mon Sep 17 00:00:00 2001 From: Katie Smith Date: Tue, 3 Oct 2017 13:35:09 +0100 Subject: [PATCH 12/14] Update tests to not use letter contact block from services table A few test updates were needed after rebasing onto master. --- app/models.py | 2 +- tests/app/conftest.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/models.py b/app/models.py index a7ecfdddd..edf0745a4 100644 --- a/app/models.py +++ b/app/models.py @@ -541,7 +541,7 @@ class Template(db.Model): return LetterDVLATemplate( {'content': self.content, 'subject': self.subject}, notification_reference=1, - contact_block=self.service.letter_contact_block, + contact_block=self.service.get_default_letter_contact(), ) def serialize(self): diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 86c748df3..a9dfa0a1b 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -46,7 +46,8 @@ from tests.app.db import ( create_notification, create_service, create_api_key, - create_inbound_number + create_inbound_number, + create_letter_contact, ) @@ -138,7 +139,6 @@ def sample_service( email_from=None, permissions=[SMS_TYPE, EMAIL_TYPE], research_mode=None, - letter_contact_block='London,\nSW1A 1AA', ): if user is None: user = create_user() @@ -183,7 +183,9 @@ def sample_service_full_permissions(notify_db, notify_db_session): @pytest.fixture(scope='function') def sample_service_custom_letter_contact_block(notify_db, notify_db_session): - return sample_service(notify_db, notify_db_session, letter_contact_block='((contact block))') + service = sample_service(notify_db, notify_db_session) + create_letter_contact(service, contact_block='((contact block))') + return service @pytest.fixture(scope='function') From c362bd06b0e6c111a523af0f339471dbf91474f2 Mon Sep 17 00:00:00 2001 From: Athanasios Voutsadakis Date: Wed, 4 Oct 2017 10:58:22 +0100 Subject: [PATCH 13/14] Pin to a specific version of cffi Version 1.11.1 seems to be [broken](https://bitbucket.org/cffi/cffi/issues?status=new&status=open) It's pulled in because of [Flask-bcrypt](https://github.com/alphagov/notifications-api/blob/master/requirements.txt#L4) which [depends on bcrypt](https://github.com/maxcountryman/flask-bcrypt/blob/master/setup.py#L33) which [asks for a version of cffi >= 1.1](https://github.com/pyca/bcrypt/blob/master/setup.py#L12) --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 645eafb0d..98ef334b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ boto3==1.4.7 +cffi==1.11.0 celery==3.1.25 # pyup: <4 docopt==0.6.2 Flask-Bcrypt==0.7.1 From 80c2d85508ab639774f9ced2bfbe3b43a08b0a12 Mon Sep 17 00:00:00 2001 From: Athanasios Voutsadakis Date: Wed, 4 Oct 2017 12:20:21 +0100 Subject: [PATCH 14/14] Instruct pyup to skip cffi==1.11.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 98ef334b7..af913af4a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ boto3==1.4.7 -cffi==1.11.0 +cffi==1.11.0 # pyup: != 1.11.1 # 1.11.1 is missing .whl celery==3.1.25 # pyup: <4 docopt==0.6.2 Flask-Bcrypt==0.7.1