mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-06 11:23:48 -05:00
Allow uploaded letters to be sent if valid
Added a send button which only appears on the page if the query string indicates that the PDF is valid. Before actually sending, we check that the service has the right permissions and that the metadata for the letter confirms the letter is valid (because the query string can be changed).
This commit is contained in:
@@ -2,6 +2,7 @@ import uuid
|
||||
from io import BytesIO
|
||||
|
||||
from flask import (
|
||||
abort,
|
||||
current_app,
|
||||
flash,
|
||||
redirect,
|
||||
@@ -13,7 +14,7 @@ from notifications_utils.pdf import pdf_page_count
|
||||
from PyPDF2.utils import PdfReadError
|
||||
from requests import RequestException
|
||||
|
||||
from app import current_service, service_api_client
|
||||
from app import current_service, notification_api_client, service_api_client
|
||||
from app.extensions import antivirus_client
|
||||
from app.main import main
|
||||
from app.main.forms import PDFUploadForm
|
||||
@@ -115,6 +116,7 @@ def uploaded_letter_preview(service_id, file_id):
|
||||
original_filename=original_filename,
|
||||
template=template,
|
||||
status=status,
|
||||
file_id=file_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -130,3 +132,27 @@ def view_letter_upload_as_preview(service_id, file_id):
|
||||
return TemplatePreview.from_invalid_pdf_file(pdf_file, page)
|
||||
else:
|
||||
return TemplatePreview.from_valid_pdf_file(pdf_file, page)
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/upload-letter/send", methods=['POST'])
|
||||
@user_has_permissions('send_messages', restrict_admin_usage=True)
|
||||
def send_uploaded_letter(service_id):
|
||||
filename = request.form['filename']
|
||||
file_id = request.form['file_id']
|
||||
|
||||
if not (current_service.has_permission('letter') and current_service.has_permission('upload_letters')):
|
||||
abort(403)
|
||||
|
||||
file_location = get_transient_letter_file_location(service_id, file_id)
|
||||
_, metadata = get_letter_pdf_and_metadata(file_location)
|
||||
|
||||
if metadata.get('status') != 'valid':
|
||||
abort(403)
|
||||
|
||||
notification_api_client.send_precompiled_letter(service_id, filename, file_id)
|
||||
|
||||
return redirect(url_for(
|
||||
'.view_notification',
|
||||
service_id=service_id,
|
||||
notification_id=file_id,
|
||||
))
|
||||
|
||||
@@ -244,6 +244,7 @@ class HeaderNavigation(Navigation):
|
||||
'send_test',
|
||||
'send_test_preview',
|
||||
'send_test_step',
|
||||
'send_uploaded_letter',
|
||||
'service_add_email_reply_to',
|
||||
'service_add_letter_contact',
|
||||
'service_add_sms_sender',
|
||||
@@ -556,6 +557,7 @@ class MainNavigation(Navigation):
|
||||
'robots',
|
||||
'security',
|
||||
'send_notification',
|
||||
'send_uploaded_letter',
|
||||
'service_dashboard_updates',
|
||||
'service_delete_email_reply_to',
|
||||
'service_delete_letter_contact',
|
||||
@@ -792,6 +794,7 @@ class CaseworkNavigation(Navigation):
|
||||
'send_messages',
|
||||
'send_notification',
|
||||
'send_test_preview',
|
||||
'send_uploaded_letter',
|
||||
'service_add_email_reply_to',
|
||||
'service_add_letter_contact',
|
||||
'service_add_sms_sender',
|
||||
@@ -1074,6 +1077,7 @@ class OrgNavigation(Navigation):
|
||||
'send_test',
|
||||
'send_test_preview',
|
||||
'send_test_step',
|
||||
'send_uploaded_letter',
|
||||
'service_add_email_reply_to',
|
||||
'service_add_letter_contact',
|
||||
'service_add_sms_sender',
|
||||
|
||||
@@ -59,6 +59,14 @@ class NotificationApiClient(NotifyAdminAPIClient):
|
||||
data = _attach_current_user(data)
|
||||
return self.post(url='/service/{}/send-notification'.format(service_id), data=data)
|
||||
|
||||
def send_precompiled_letter(self, service_id, filename, file_id):
|
||||
data = {
|
||||
'filename': filename,
|
||||
'file_id': file_id,
|
||||
}
|
||||
data = _attach_current_user(data)
|
||||
return self.post(url='/service/{}/send-pdf-letter'.format(service_id), data=data)
|
||||
|
||||
def get_notification(self, service_id, notification_id):
|
||||
return self.get(url='/service/{}/notifications/{}'.format(service_id, notification_id))
|
||||
|
||||
|
||||
@@ -20,4 +20,18 @@
|
||||
<div class="letter-sent">
|
||||
{{ template|string }}
|
||||
</div>
|
||||
|
||||
{% if status == 'valid' %}
|
||||
<div class="js-stick-at-bottom-when-scrolling">
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for(
|
||||
'main.send_uploaded_letter',
|
||||
service_id=current_service.id,
|
||||
)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="hidden" name="filename" value="{{ original_filename }}" />
|
||||
<input type="hidden" name="file_id" value="{{ file_id }}" />
|
||||
<button type="submit" class="button">Send 1 letter</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -50,6 +50,10 @@ def test_post_upload_letter_redirects_for_valid_file(mocker, client_request):
|
||||
assert page.find('h1').text == 'tests/test_pdf_files/one_page_pdf.pdf'
|
||||
assert not page.find(id='validation-error-message')
|
||||
|
||||
assert page.find('input', {'type': 'hidden', 'name': 'filename', 'value': 'tests/test_pdf_files/one_page_pdf.pdf'})
|
||||
assert page.find('input', {'type': 'hidden', 'name': 'file_id', 'value': 'fake-uuid'})
|
||||
assert page.find('button', {'type': 'submit'}).text == 'Send 1 letter'
|
||||
|
||||
|
||||
def test_post_upload_letter_shows_letter_preview_for_valid_file(mocker, client_request):
|
||||
letter_template = {'template_type': 'letter',
|
||||
@@ -182,6 +186,7 @@ def test_post_upload_letter_with_invalid_file(mocker, client_request):
|
||||
assert normalize_spaces(
|
||||
page.find(id='validation-error-message').text
|
||||
) == 'Validation failed'
|
||||
assert not page.find('button', {'type': 'submit'})
|
||||
|
||||
|
||||
def test_post_upload_letter_shows_letter_preview_for_invalid_file(mocker, client_request):
|
||||
@@ -254,3 +259,66 @@ def test_uploaded_letter_preview(mocker, client_request):
|
||||
|
||||
assert page.find('h1').text == 'my_letter.pdf'
|
||||
assert page.find('div', class_='letter-sent')
|
||||
|
||||
|
||||
def test_send_uploaded_letter_sends_letter_and_redirects_to_notification_page(mocker, service_one, client_request):
|
||||
mocker.patch('app.main.views.uploads.get_letter_pdf_and_metadata', return_value=('file', {'status': 'valid'}))
|
||||
mock_send = mocker.patch('app.main.views.uploads.notification_api_client.send_precompiled_letter')
|
||||
|
||||
service_one['permissions'] = ['letter', 'upload_letters']
|
||||
file_id = 'abcd-1234'
|
||||
|
||||
client_request.post(
|
||||
'main.send_uploaded_letter',
|
||||
service_id=SERVICE_ONE_ID,
|
||||
_data={'filename': 'my_file.pdf', 'file_id': file_id},
|
||||
_expected_redirect=url_for(
|
||||
'main.view_notification',
|
||||
service_id=SERVICE_ONE_ID,
|
||||
notification_id=file_id,
|
||||
_external=True
|
||||
)
|
||||
)
|
||||
mock_send.assert_called_once_with(SERVICE_ONE_ID, 'my_file.pdf', file_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('permissions', [
|
||||
['email'],
|
||||
['letter'],
|
||||
['upload_letters'],
|
||||
])
|
||||
def test_send_uploaded_letter_when_service_does_not_have_correct_permissions(
|
||||
mocker,
|
||||
service_one,
|
||||
client_request,
|
||||
permissions,
|
||||
):
|
||||
mocker.patch('app.main.views.uploads.get_letter_pdf_and_metadata', return_value=('file', {'status': 'valid'}))
|
||||
mock_send = mocker.patch('app.main.views.uploads.notification_api_client.send_precompiled_letter')
|
||||
|
||||
service_one['permissions'] = permissions
|
||||
file_id = 'abcd-1234'
|
||||
|
||||
client_request.post(
|
||||
'main.send_uploaded_letter',
|
||||
service_id=SERVICE_ONE_ID,
|
||||
_data={'filename': 'my_file.pdf', 'file_id': file_id},
|
||||
_expected_status=403
|
||||
)
|
||||
assert not mock_send.called
|
||||
|
||||
|
||||
def test_send_uploaded_letter_when_metadata_states_pdf_is_invalid(mocker, service_one, client_request):
|
||||
mocker.patch('app.main.views.uploads.get_letter_pdf_and_metadata', return_value=('file', {'status': 'invalid'}))
|
||||
mock_send = mocker.patch('app.main.views.uploads.notification_api_client.send_precompiled_letter')
|
||||
|
||||
service_one['permissions'] = ['letter', 'upload_letters']
|
||||
file_id = 'abcd-1234'
|
||||
|
||||
client_request.post(
|
||||
'main.send_uploaded_letter',
|
||||
service_id=SERVICE_ONE_ID,
|
||||
_data={'filename': 'my_file.pdf', 'file_id': file_id},
|
||||
_expected_status=403
|
||||
)
|
||||
assert not mock_send.called
|
||||
|
||||
@@ -59,6 +59,23 @@ def test_send_notification(mocker, logged_in_client, active_user_with_permission
|
||||
)
|
||||
|
||||
|
||||
def test_send_precompiled_letter(mocker, logged_in_client, active_user_with_permissions):
|
||||
mock_post = mocker.patch('app.notify_client.notification_api_client.NotificationApiClient.post')
|
||||
NotificationApiClient().send_precompiled_letter(
|
||||
'abcd-1234',
|
||||
'my_file.pdf',
|
||||
'file-ID'
|
||||
)
|
||||
mock_post.assert_called_once_with(
|
||||
url='/service/abcd-1234/send-pdf-letter',
|
||||
data={
|
||||
'filename': 'my_file.pdf',
|
||||
'file_id': 'file-ID',
|
||||
'created_by': active_user_with_permissions['id']
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_get_notification(mocker):
|
||||
mock_get = mocker.patch('app.notify_client.notification_api_client.NotificationApiClient.get')
|
||||
NotificationApiClient().get_notification('foo', 'bar')
|
||||
|
||||
Reference in New Issue
Block a user