From fc1345acdf6754a534cccd50d05e8c6ba48b3afe Mon Sep 17 00:00:00 2001 From: Adam Shimali Date: Tue, 10 May 2016 12:39:35 +0100 Subject: [PATCH 1/6] Trigger dependent build for preview --- scripts/trigger-dependent-build.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/trigger-dependent-build.sh b/scripts/trigger-dependent-build.sh index 7dee89d50..cf1405dfe 100755 --- a/scripts/trigger-dependent-build.sh +++ b/scripts/trigger-dependent-build.sh @@ -13,5 +13,4 @@ # travis token # -curl -vvv -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $auth_token" -d '{"request":{"branch":"master"}}' https://api.travis-ci.org/repo/alphagov%2Fnotifications-functional-tests/requests - +curl -vvv -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $auth_token" -d '{"request":{"branch":"master","config":{"env":{"global":["ENVIRONMENT=preview"]}}}}' https://api.travis-ci.org/repo/alphagov%2Fnotifications-functional-tests/requests From 8f8245885e0992bfbf46bcc31cd863ff5eaa44ab Mon Sep 17 00:00:00 2001 From: Adam Shimali Date: Tue, 10 May 2016 14:15:38 +0100 Subject: [PATCH 2/6] Pass branch name to dependent build --- scripts/trigger-dependent-build.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/scripts/trigger-dependent-build.sh b/scripts/trigger-dependent-build.sh index cf1405dfe..389e1c120 100755 --- a/scripts/trigger-dependent-build.sh +++ b/scripts/trigger-dependent-build.sh @@ -7,10 +7,6 @@ # - ./trigger-dependent-build # -# An authorization token generated by running: -# gem install travis -# travis login -# travis token -# +echo "Triggering dependent build for $TRAVIS_BRANCH" -curl -vvv -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $auth_token" -d '{"request":{"branch":"master","config":{"env":{"global":["ENVIRONMENT=preview"]}}}}' https://api.travis-ci.org/repo/alphagov%2Fnotifications-functional-tests/requests +curl -vvv -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $auth_token" -d '{"request":{"branch":"master","config":{"env":{"global":["ENVIRONMENT='$TRAVIS_BRANCH'"]}}}}' https://api.travis-ci.org/repo/alphagov%2Fnotifications-functional-tests/requests From 083d3d75ae990e11e862fed47ced42c4142f812d Mon Sep 17 00:00:00 2001 From: Nicholas Staples Date: Tue, 10 May 2016 14:55:59 +0100 Subject: [PATCH 3/6] Add user details to template schema dump. --- app/schemas.py | 14 ++++++++++++-- app/template/rest.py | 17 ++++++++++------- tests/app/template/test_rest_history.py | 6 +++++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/app/schemas.py b/app/schemas.py index d01a4e978..16bcac996 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -123,9 +123,19 @@ class TemplateSchema(BaseTemplateSchema): raise ValidationError('Invalid template subject', 'subject') -class TemplateHistorySchema(BaseTemplateSchema): +class TemplateHistorySchema(BaseSchema): - created_by = field_for(models.Template, 'created_by', required=True) + class Meta: + # Use the base model class that the history class is created from + model = models.Template + # We have to use a method here because the relationship field on the + # history object is not created. + created_by = fields.Method("populate_created_by", dump_only=True) + created_at = field_for(models.Template, 'created_at', format='%Y-%m-%d %H:%M:%S.%f') + + def populate_created_by(self, data): + usr = models.User.query.filter_by(id=data.created_by_id).one() + return {'id': str(usr.id), 'name': usr.name, 'email_address': usr.email_address} class NotificationsStatisticsSchema(BaseSchema): diff --git a/app/template/rest.py b/app/template/rest.py index 17e457dee..d520a3e06 100644 --- a/app/template/rest.py +++ b/app/template/rest.py @@ -99,12 +99,13 @@ def get_template_by_id_and_service_id(service_id, template_id): @template.route('//version/') def get_template_version(service_id, template_id, version): - fetched_template = dao_get_template_by_id_and_service_id( - template_id=template_id, - service_id=service_id, - version=version + data, errors = template_history_schema.dump( + dao_get_template_by_id_and_service_id( + template_id=template_id, + service_id=service_id, + version=version + ) ) - data, errors = template_history_schema.dump(fetched_template) if errors: return json_resp(result='error', message=errors), 400 return jsonify(data=data) @@ -112,8 +113,10 @@ def get_template_version(service_id, template_id, version): @template.route('//version') def get_template_versions(service_id, template_id): - fetched_templates = dao_get_template_versions(service_id, template_id) - data, errors = template_history_schema.dump(fetched_templates, many=True) + data, errors = template_history_schema.dump( + dao_get_template_versions(service_id, template_id), + many=True + ) if errors: return json_resp(result='error', message=errors), 400 return jsonify(data=data) diff --git a/tests/app/template/test_rest_history.py b/tests/app/template/test_rest_history.py index c7a62e7ec..ce3c2619d 100644 --- a/tests/app/template/test_rest_history.py +++ b/tests/app/template/test_rest_history.py @@ -1,11 +1,13 @@ import json +from datetime import (datetime, date) from flask import url_for from app.models import Template +from freezegun import freeze_time from app.dao.templates_dao import dao_update_template from tests import create_authorization_header -def test_template_history_version(notify_api, sample_template): +def test_template_history_version(notify_api, sample_user, sample_template): with notify_api.test_request_context(): with notify_api.test_client() as client: auth_header = create_authorization_header() @@ -23,6 +25,8 @@ def test_template_history_version(notify_api, sample_template): assert json_resp['data']['id'] == str(sample_template.id) assert json_resp['data']['content'] == sample_template.content assert json_resp['data']['version'] == 1 + assert json_resp['data']['created_by']['name'] == sample_user.name + assert datetime.strptime(json_resp['data']['created_at'], '%Y-%m-%d %H:%M:%S.%f').date() == date.today() def test_previous_template_history_version(notify_api, sample_template): From 03f15d6af993aa490b8fe700ac925ee8a822f909 Mon Sep 17 00:00:00 2001 From: Nicholas Staples Date: Wed, 11 May 2016 10:56:24 +0100 Subject: [PATCH 4/6] Update now to utcnow. All tests passing. --- app/dao/users_dao.py | 4 ++-- app/history_meta.py | 2 +- app/models.py | 8 ++++---- app/status/healthcheck.py | 2 +- app/user/rest.py | 7 +++---- tests/app/authentication/test_authentication.py | 4 ++-- tests/app/celery/test_tasks.py | 2 +- tests/app/dao/test_provider_rates_dao.py | 2 +- tests/app/invite/test_invite_rest.py | 7 ++++--- tests/app/status/test_delivery_status.py | 2 +- tests/app/user/test_rest_verify.py | 2 +- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/app/dao/users_dao.py b/app/dao/users_dao.py index 7960ed101..56626b1f9 100644 --- a/app/dao/users_dao.py +++ b/app/dao/users_dao.py @@ -12,7 +12,7 @@ def create_secret_code(): def save_model_user(usr, update_dict={}, pwd=None): if pwd: usr.password = pwd - usr.password_changed_at = datetime.now() + usr.password_changed_at = datetime.utcnow() if update_dict: if update_dict.get('id'): del update_dict['id'] @@ -25,7 +25,7 @@ def save_model_user(usr, update_dict={}, pwd=None): def create_user_code(user, code, code_type): verify_code = VerifyCode(code_type=code_type, - expiry_datetime=datetime.now() + timedelta(hours=1), + expiry_datetime=datetime.utcnow() + timedelta(hours=1), user=user) verify_code.code = code db.session.add(verify_code) diff --git a/app/history_meta.py b/app/history_meta.py index a7ee0f3d9..0ccc06f09 100644 --- a/app/history_meta.py +++ b/app/history_meta.py @@ -205,7 +205,7 @@ def create_history(obj): if not obj.version: obj.version = 1 - obj.created_at = datetime.datetime.now() + obj.created_at = datetime.datetime.utcnow() else: obj.version += 1 diff --git a/app/models.py b/app/models.py index d080a4c5d..3aae8ad2b 100644 --- a/app/models.py +++ b/app/models.py @@ -81,13 +81,13 @@ class Service(db.Model, Versioned): index=False, unique=False, nullable=False, - default=datetime.datetime.now) + default=datetime.datetime.utcnow) updated_at = db.Column( db.DateTime, index=False, unique=False, nullable=True, - onupdate=datetime.datetime.now) + onupdate=datetime.datetime.utcnow) active = db.Column(db.Boolean, index=False, unique=False, nullable=False) message_limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False) users = db.relationship( @@ -114,13 +114,13 @@ class ApiKey(db.Model, Versioned): index=False, unique=False, nullable=False, - default=datetime.datetime.now) + default=datetime.datetime.utcnow) updated_at = db.Column( db.DateTime, index=False, unique=False, nullable=True, - onupdate=datetime.datetime.now) + onupdate=datetime.datetime.utcnow) created_by = db.relationship('User') created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False) diff --git a/app/status/healthcheck.py b/app/status/healthcheck.py index a7ac8d3be..205906db8 100644 --- a/app/status/healthcheck.py +++ b/app/status/healthcheck.py @@ -35,7 +35,7 @@ def show_delivery_status(): return jsonify(status="ok"), 200 else: notifications_alert = current_app.config['NOTIFICATIONS_ALERT'] - some_number_of_minutes_ago = datetime.now() - timedelta(minutes=notifications_alert) + some_number_of_minutes_ago = datetime.utcnow() - timedelta(minutes=notifications_alert) notifications = Notification.query.filter(Notification.status == 'sending', Notification.created_at < some_number_of_minutes_ago).all() message = "{} notifications in sending state over {} minutes".format(len(notifications), notifications_alert) diff --git a/app/user/rest.py b/app/user/rest.py index d6f0cd7c5..2d46d2d7a 100644 --- a/app/user/rest.py +++ b/app/user/rest.py @@ -1,3 +1,4 @@ +import json from datetime import datetime from flask import (jsonify, request, abort, Blueprint, current_app) from app import encryption @@ -109,7 +110,7 @@ def verify_user_code(user_id): code = get_user_code(user_to_verify, txt_code, txt_type) if not code: return jsonify(result="error", message="Code not found"), 404 - if datetime.now() > code.expiry_datetime or code.code_used: + if datetime.utcnow() > code.expiry_datetime or code.code_used: return jsonify(result="error", message="Code has expired"), 400 use_user_code(code.id) return jsonify({}), 204 @@ -210,8 +211,7 @@ def send_user_reset_password(): def _create_reset_password_url(email): from notifications_utils.url_safe_token import generate_token - import json - data = json.dumps({'email': email, 'created_at': str(datetime.now())}) + data = json.dumps({'email': email, 'created_at': str(datetime.utcnow())}) token = generate_token(data, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT']) return current_app.config['ADMIN_BASE_URL'] + '/new-password/' + token @@ -219,7 +219,6 @@ def _create_reset_password_url(email): def _create_verification_url(user, secret_code): from notifications_utils.url_safe_token import generate_token - import json data = json.dumps({'user_id': str(user.id), 'email': user.email_address, 'secret_code': secret_code}) token = generate_token(data, current_app.config['SECRET_KEY'], current_app.config['DANGEROUS_SALT']) diff --git a/tests/app/authentication/test_authentication.py b/tests/app/authentication/test_authentication.py index a9ec360d3..d81fba0e3 100644 --- a/tests/app/authentication/test_authentication.py +++ b/tests/app/authentication/test_authentication.py @@ -121,7 +121,7 @@ def test_authentication_passes_when_service_has_multiple_keys_some_expired( with notify_api.test_client() as client: expired_key_data = {'service': sample_api_key.service, 'name': 'expired_key', - 'expiry_date': datetime.now(), + 'expiry_date': datetime.utcnow(), 'created_by': sample_api_key.created_by } expired_key = ApiKey(**expired_key_data) @@ -166,7 +166,7 @@ def test_authentication_returns_token_expired_when_service_uses_expired_key_and_ expire_the_key = {'id': expired_api_key.id, 'service': sample_api_key.service, 'name': 'expired_key', - 'expiry_date': datetime.now() + timedelta(hours=-2), + 'expiry_date': datetime.utcnow() + timedelta(hours=-2), 'created_by': sample_api_key.created_by} save_model_api_key(expired_api_key, expire_the_key) response = client.get( diff --git a/tests/app/celery/test_tasks.py b/tests/app/celery/test_tasks.py index 9536febb1..24ccd2a44 100644 --- a/tests/app/celery/test_tasks.py +++ b/tests/app/celery/test_tasks.py @@ -781,7 +781,7 @@ def test_email_invited_user_should_send_email(notify_api, mocker): 'service_id': '123123', 'service_name': 'Blacksmith Service', 'token': 'the-token', - 'expiry_date': str(datetime.now() + timedelta(days=1)) + 'expiry_date': str(datetime.utcnow() + timedelta(days=1)) } mocker.patch('app.aws_ses_client.send_email') diff --git a/tests/app/dao/test_provider_rates_dao.py b/tests/app/dao/test_provider_rates_dao.py index c49584f2c..d9ce53e41 100644 --- a/tests/app/dao/test_provider_rates_dao.py +++ b/tests/app/dao/test_provider_rates_dao.py @@ -5,7 +5,7 @@ from app.models import ProviderRates def test_create_provider_rates(notify_db, notify_db_session, mmg_provider_name): - now = datetime.now() + now = datetime.utcnow() rate = Decimal("1.00000") create_provider_rates(mmg_provider_name, now, rate) assert ProviderRates.query.count() == 1 diff --git a/tests/app/invite/test_invite_rest.py b/tests/app/invite/test_invite_rest.py index 8ef85e71b..65a6bff5a 100644 --- a/tests/app/invite/test_invite_rest.py +++ b/tests/app/invite/test_invite_rest.py @@ -41,9 +41,10 @@ def test_create_invited_user(notify_api, sample_service, mocker): assert json_resp['data']['permissions'] == 'send_messages,manage_service,manage_api_keys' assert json_resp['data']['id'] invitation_expiration_days = notify_api.config['INVITATION_EXPIRATION_DAYS'] - expiry_date = (datetime.now() + timedelta(days=invitation_expiration_days)).replace(hour=0, minute=0, - second=0, - microsecond=0) + expiry_date = (datetime.utcnow() + timedelta(days=invitation_expiration_days)).replace(hour=0, + minute=0, + second=0, + microsecond=0) encrypted_invitation = {'to': email_address, 'user_name': invite_from.name, 'service_id': str(sample_service.id), diff --git a/tests/app/status/test_delivery_status.py b/tests/app/status/test_delivery_status.py index a46cea93a..d8b3f6bb3 100644 --- a/tests/app/status/test_delivery_status.py +++ b/tests/app/status/test_delivery_status.py @@ -19,7 +19,7 @@ def test_get_delivery_status_all_ok(notify_api, notify_db): def test_get_delivery_status_with_undelivered_notification(notify_api, notify_db, sample_notification): - more_than_five_mins_ago = datetime.now() - timedelta(minutes=10) + more_than_five_mins_ago = datetime.utcnow() - timedelta(minutes=10) sample_notification.created_at = more_than_five_mins_ago notify_db.session.add(sample_notification) notify_db.session.commit() diff --git a/tests/app/user/test_rest_verify.py b/tests/app/user/test_rest_verify.py index dd5b297fc..c426a3469 100644 --- a/tests/app/user/test_rest_verify.py +++ b/tests/app/user/test_rest_verify.py @@ -110,7 +110,7 @@ def test_user_verify_code_email_expired_code(notify_api, with notify_api.test_client() as client: assert not VerifyCode.query.first().code_used sample_email_code.expiry_datetime = ( - datetime.now() - timedelta(hours=1)) + datetime.utcnow() - timedelta(hours=1)) db.session.add(sample_email_code) db.session.commit() data = json.dumps({ From aff0abb78c5426c80ac552a3db3265f3389fbfa8 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 11 May 2016 12:03:25 +0100 Subject: [PATCH 5/6] Add a load only and dump only for the created_by attribute of the JobSchema --- app/schemas.py | 4 ++++ tests/app/job/test_rest.py | 1 + 2 files changed, 5 insertions(+) diff --git a/app/schemas.py b/app/schemas.py index d01a4e978..bcd3c64e8 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -143,6 +143,10 @@ class ApiKeySchema(BaseSchema): class JobSchema(BaseSchema): + created_by_user = fields.Nested(UserSchema, attribute="created_by", + dump_to="created_by", only=["id", "name"], dump_only=True) + created_by = field_for(models.Job, 'created_by', required=True, load_only=True) + class Meta: model = models.Job diff --git a/tests/app/job/test_rest.py b/tests/app/job/test_rest.py index f80400f45..9733c932a 100644 --- a/tests/app/job/test_rest.py +++ b/tests/app/job/test_rest.py @@ -71,6 +71,7 @@ def test_get_job_by_id(notify_api, sample_job): assert response.status_code == 200 resp_json = json.loads(response.get_data(as_text=True)) assert resp_json['data']['id'] == job_id + assert resp_json['data']['created_by']['name'] == 'Test User' def test_create_job(notify_api, sample_template, mocker, fake_uuid): From ba874cfd43b08b05eda0b527dfe0543394bed1b1 Mon Sep 17 00:00:00 2001 From: Adam Shimali Date: Wed, 11 May 2016 12:39:39 +0100 Subject: [PATCH 6/6] Only trigger dependent build for master, staging and live --- scripts/trigger-dependent-build.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/trigger-dependent-build.sh b/scripts/trigger-dependent-build.sh index 389e1c120..20c820add 100755 --- a/scripts/trigger-dependent-build.sh +++ b/scripts/trigger-dependent-build.sh @@ -7,6 +7,12 @@ # - ./trigger-dependent-build # -echo "Triggering dependent build for $TRAVIS_BRANCH" - -curl -vvv -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $auth_token" -d '{"request":{"branch":"master","config":{"env":{"global":["ENVIRONMENT='$TRAVIS_BRANCH'"]}}}}' https://api.travis-ci.org/repo/alphagov%2Fnotifications-functional-tests/requests +case $TRAVIS_BRANCH in + master|staging|live) + echo "Triggering dependent build for $TRAVIS_BRANCH" + curl -vvv -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $auth_token" -d '{"request":{"branch":"master","config":{"env":{"global":["ENVIRONMENT='$TRAVIS_BRANCH'"]}}}}' https://api.travis-ci.org/repo/alphagov%2Fnotifications-functional-tests/requests + ;; + *) + echo "Not triggering dependent build for $TRAVIS_BRANCH" + ;; +esac