Add endpoints to serve the agreement

Rather than making users contact us to get the agreement, we should just
let them download it, when we know which version to send them.

This commit adds two endpoints:
- one to serve a page which links to the agreement
- one to serve the agreement itself

These pages are not linked to anywhere because the underlying files
don’t exist yet. So I haven’t bothered putting real content on the page
yet either. I imagine the deploy sequence will be:

1. Upload the files to the buckets in each environment
2. Deploy this code through each enviroment, checking the links work
3. Make another PR to start linking to the endpoints added by this
   commit
This commit is contained in:
Chris Hill-Scott
2018-03-16 17:27:47 +00:00
parent d1ae9cc1a6
commit 68292d2299
8 changed files with 191 additions and 1 deletions

View File

@@ -69,6 +69,7 @@ class Config(object):
STATSD_PORT = 8125
NOTIFY_ENVIRONMENT = 'development'
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-local'
MOU_BUCKET_NAME = 'local-mou'
ROUTE_SECRET_KEY_1 = os.environ.get('ROUTE_SECRET_KEY_1', '')
ROUTE_SECRET_KEY_2 = os.environ.get('ROUTE_SECRET_KEY_2', '')
CHECK_PROXY_HEADER = False
@@ -82,6 +83,7 @@ class Development(Config):
STATSD_ENABLED = False
CSV_UPLOAD_BUCKET_NAME = 'development-notifications-csv-upload'
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-tools'
MOU_BUCKET_NAME = 'notify.tools-mou'
ADMIN_CLIENT_SECRET = 'dev-notify-secret-key'
API_HOST_NAME = 'http://localhost:6011'
@@ -98,6 +100,7 @@ class Test(Development):
WTF_CSRF_ENABLED = False
CSV_UPLOAD_BUCKET_NAME = 'test-notifications-csv-upload'
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-test'
MOU_BUCKET_NAME = 'test-mou'
NOTIFY_ENVIRONMENT = 'test'
API_HOST_NAME = 'http://you-forgot-to-mock-an-api-call-to'
TEMPLATE_PREVIEW_API_HOST = 'http://localhost:9999'
@@ -109,6 +112,7 @@ class Preview(Config):
STATSD_ENABLED = True
CSV_UPLOAD_BUCKET_NAME = 'preview-notifications-csv-upload'
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-preview'
MOU_BUCKET_NAME = 'notify.works-mou'
NOTIFY_ENVIRONMENT = 'preview'
CHECK_PROXY_HEADER = True
@@ -120,6 +124,7 @@ class Staging(Config):
STATSD_ENABLED = True
CSV_UPLOAD_BUCKET_NAME = 'staging-notify-csv-upload'
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-staging'
MOU_BUCKET_NAME = 'staging-notify.works-mou'
NOTIFY_ENVIRONMENT = 'staging'
CHECK_PROXY_HEADER = True
@@ -131,6 +136,7 @@ class Live(Config):
STATSD_ENABLED = True
CSV_UPLOAD_BUCKET_NAME = 'live-notifications-csv-upload'
LOGO_UPLOAD_BUCKET_NAME = 'public-logos-production'
MOU_BUCKET_NAME = 'notifications.service.gov.uk-mou'
NOTIFY_ENVIRONMENT = 'live'
CHECK_PROXY_HEADER = False

View File

@@ -21,6 +21,7 @@ marinemanagement.org.uk:
agreement_signed: true
cabinet-office.gov.uk:
owner: Cabinet Office
crown: true
agreement_signed: true
cica.gsi.gov.uk: cica.gov.uk
cica.gov.uk:

View File

@@ -32,5 +32,6 @@ from app.main.views import ( # noqa
conversation,
organisations,
notifications,
inbound_number
inbound_number,
agreement,
)

View File

@@ -61,6 +61,26 @@ def s3download(service_id, upload_id):
return contents
def get_mou(organisation_is_crown):
bucket = current_app.config['MOU_BUCKET_NAME']
filename = 'crown.pdf' if organisation_is_crown else 'non-crown.pdf'
attachment_filename = 'GOV.UK Notify data sharing and financial agreement{}.pdf'.format(
'' if organisation_is_crown else ' (non-crown)'
)
try:
key = get_s3_object(bucket, filename)
return {
'filename_or_fp': key.get()['Body'],
'attachment_filename': attachment_filename,
'as_attachment': True,
}
except botocore.exceptions.ClientError as exception:
current_app.logger.error("Unable to download s3 file {}/{}".format(
bucket, filename
))
raise exception
def upload_logo(filename, filedata, region, user_id):
upload_file_name = LOGO_LOCATION_STRUCTURE.format(
temp=TEMP_TAG.format(user_id=user_id),

View File

@@ -0,0 +1,26 @@
from flask import render_template, send_file
from flask_login import login_required
from app.main import main
from app.main.views.sub_navigation_dictionaries import features_nav
from app.main.s3_client import get_mou
from app.utils import AgreementInfo
@main.route('/agreement')
@login_required
def agreement():
return render_template(
'views/agreement.html',
crown_status=AgreementInfo.from_current_user().crown_status_or_404,
navigation_links=features_nav(),
)
@main.route('/agreement.pdf')
@login_required
def download_agreement():
return send_file(**get_mou(
AgreementInfo.from_current_user().crown_status_or_404
))

View File

@@ -0,0 +1,27 @@
{% extends "withoutnav_template.html" %}
{% from "components/sub-navigation.html" import sub_navigation %}
{% block per_page_title %}
GOV.UK Notify data sharing and financial agreement
{% endblock %}
{% block maincolumn_content %}
<div class="grid-row">
<div class="column-one-third">
{{ sub_navigation(navigation_links) }}
</div>
<div class="column-two-thirds">
<h1 class="heading-large">
GOV.UK Notify data sharing and financial agreement
</h1>
<p>
<a href="{{ url_for('main.download_agreement') }}">Download</a>.
</p>
</div>
</div>
{% endblock %}

View File

@@ -464,6 +464,12 @@ class AgreementInfo:
else:
return 'Cant tell'
@property
def crown_status_or_404(self):
if self.crown_status is None:
abort(404)
return self.crown_status
def as_request_for_agreement(self, with_owner=False):
if with_owner and self.owner:
return (

View File

@@ -0,0 +1,103 @@
from io import BytesIO
import pytest
from flask import url_for
from tests.conftest import active_user_with_permissions
class _MockS3Object():
def __init__(self, data=None):
self.data = data or b''
def get(self):
return {'Body': BytesIO(self.data)}
@pytest.mark.parametrize('email_address, expected_status', [
('test@cabinet-office.gov.uk', 200),
('test@aylesburytowncouncil.gov.uk', 200),
('test@unknown.gov.uk', 404),
])
def test_show_agreement_page(
client_request,
mocker,
fake_uuid,
email_address,
expected_status,
):
user = active_user_with_permissions(fake_uuid)
user.email_address = email_address
mocker.patch('app.user_api_client.get_user', return_value=user)
client_request.get(
'main.agreement',
_expected_status=expected_status,
)
@pytest.mark.parametrize('email_address, expected_file_fetched, expected_file_served', [
(
'test@cabinet-office.gov.uk',
'crown.pdf',
'GOV.UK Notify data sharing and financial agreement.pdf',
),
(
'test@aylesburytowncouncil.gov.uk',
'non-crown.pdf',
'GOV.UK Notify data sharing and financial agreement (non-crown).pdf',
),
])
def test_downloading_agreement(
logged_in_client,
mocker,
fake_uuid,
email_address,
expected_file_fetched,
expected_file_served,
):
mock_get_s3_object = mocker.patch(
'app.main.s3_client.get_s3_object',
return_value=_MockS3Object(b'foo')
)
user = active_user_with_permissions(fake_uuid)
user.email_address = email_address
mocker.patch('app.user_api_client.get_user', return_value=user)
response = logged_in_client.get(url_for('main.download_agreement'))
assert response.status_code == 200
assert response.get_data() == b'foo'
assert response.headers['Content-Type'] == 'application/pdf'
assert response.headers['Content-Disposition'] == (
'attachment; filename="{}"'.format(expected_file_served)
)
mock_get_s3_object.assert_called_once_with('test-mou', expected_file_fetched)
def test_agreement_cant_be_downloaded_unknown_crown_status(
logged_in_client,
mocker,
fake_uuid,
):
mock_get_s3_object = mocker.patch(
'app.main.s3_client.get_s3_object',
return_value=_MockS3Object()
)
user = active_user_with_permissions(fake_uuid)
user.email_address = 'test@unknown.gov.uk'
mocker.patch('app.user_api_client.get_user', return_value=user)
response = logged_in_client.get(url_for('main.download_agreement'))
assert response.status_code == 404
assert mock_get_s3_object.call_args_list == []
def test_agreement_requires_login(
client,
mocker,
):
mock_get_s3_object = mocker.patch(
'app.main.s3_client.get_s3_object',
return_value=_MockS3Object()
)
response = client.get(url_for('main.download_agreement'))
assert response.status_code == 302
assert response.location == 'http://localhost/sign-in?next=%2Fagreement.pdf'
assert mock_get_s3_object.call_args_list == []