mirror of
https://github.com/GSA/notifications-api.git
synced 2026-01-30 14:31:57 -05:00
Remove letters-related code (#175)
This deletes a big ol' chunk of code related to letters. It's not everything—there are still a few things that might be tied to sms/email—but it's the the heart of letters function. SMS and email function should be untouched by this. Areas affected: - Things obviously about letters - PDF tasks, used for precompiling letters - Virus scanning, used for those PDFs - FTP, used to send letters to the printer - Postage stuff
This commit is contained in:
@@ -49,11 +49,6 @@ class ValidationError(InvalidRequest):
|
||||
self.message = message if message else self.message
|
||||
|
||||
|
||||
class PDFNotReadyError(BadRequestError):
|
||||
def __init__(self):
|
||||
super().__init__(message='PDF not available yet, try again later', status_code=400)
|
||||
|
||||
|
||||
def register_errors(blueprint):
|
||||
@blueprint.errorhandler(InvalidEmailError)
|
||||
def invalid_format(error):
|
||||
|
||||
@@ -36,20 +36,6 @@ def create_post_email_response_from_notification(
|
||||
return resp
|
||||
|
||||
|
||||
def create_post_letter_response_from_notification(
|
||||
notification_id, client_reference, template_id, template_version, service_id,
|
||||
content, subject, url_root
|
||||
):
|
||||
resp = __create_notification_response(
|
||||
notification_id, client_reference, template_id, template_version, service_id, url_root
|
||||
)
|
||||
resp['content'] = {
|
||||
"body": content,
|
||||
"subject": subject
|
||||
}
|
||||
return resp
|
||||
|
||||
|
||||
def __create_notification_response(
|
||||
notification_id, client_reference, template_id, template_version, service_id, url_root
|
||||
):
|
||||
|
||||
@@ -1,18 +1,8 @@
|
||||
from io import BytesIO
|
||||
|
||||
from flask import current_app, jsonify, request, send_file, url_for
|
||||
from flask import current_app, jsonify, request, url_for
|
||||
|
||||
from app import api_user, authenticated_service
|
||||
from app.dao import notifications_dao
|
||||
from app.letters.utils import get_letter_pdf_and_metadata
|
||||
from app.models import (
|
||||
LETTER_TYPE,
|
||||
NOTIFICATION_PENDING_VIRUS_CHECK,
|
||||
NOTIFICATION_TECHNICAL_FAILURE,
|
||||
NOTIFICATION_VIRUS_SCAN_FAILED,
|
||||
)
|
||||
from app.schema_validation import validate
|
||||
from app.v2.errors import BadRequestError, PDFNotReadyError
|
||||
from app.v2.notifications import v2_notification_blueprint
|
||||
from app.v2.notifications.notification_schemas import (
|
||||
get_notifications_request,
|
||||
@@ -30,34 +20,6 @@ def get_notification_by_id(notification_id):
|
||||
return jsonify(notification.serialize()), 200
|
||||
|
||||
|
||||
@v2_notification_blueprint.route('/<notification_id>/pdf', methods=['GET'])
|
||||
def get_pdf_for_notification(notification_id):
|
||||
_data = {"notification_id": notification_id}
|
||||
validate(_data, notification_by_id)
|
||||
notification = notifications_dao.get_notification_by_id(
|
||||
notification_id, authenticated_service.id, _raise=True
|
||||
)
|
||||
|
||||
if notification.notification_type != LETTER_TYPE:
|
||||
raise BadRequestError(message="Notification is not a letter")
|
||||
|
||||
if notification.status == NOTIFICATION_VIRUS_SCAN_FAILED:
|
||||
raise BadRequestError(message='File did not pass the virus scan')
|
||||
|
||||
if notification.status == NOTIFICATION_TECHNICAL_FAILURE:
|
||||
raise BadRequestError(message='PDF not available for letters in status {}'.format(notification.status))
|
||||
|
||||
if notification.status == NOTIFICATION_PENDING_VIRUS_CHECK:
|
||||
raise PDFNotReadyError()
|
||||
|
||||
try:
|
||||
pdf_data, metadata = get_letter_pdf_and_metadata(notification)
|
||||
except Exception:
|
||||
raise PDFNotReadyError()
|
||||
|
||||
return send_file(path_or_file=BytesIO(pdf_data), mimetype='application/pdf')
|
||||
|
||||
|
||||
@v2_notification_blueprint.route("", methods=['GET'])
|
||||
def get_notifications():
|
||||
_data = request.args.to_dict(flat=False)
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
from app.models import (
|
||||
NOTIFICATION_STATUS_LETTER_ACCEPTED,
|
||||
NOTIFICATION_STATUS_LETTER_RECEIVED,
|
||||
NOTIFICATION_STATUS_TYPES,
|
||||
NOTIFICATION_TYPES,
|
||||
)
|
||||
from app.models import NOTIFICATION_STATUS_TYPES, NOTIFICATION_TYPES
|
||||
from app.schema_validation.definitions import personalisation, uuid
|
||||
|
||||
template = {
|
||||
@@ -48,7 +43,7 @@ get_notification_response = {
|
||||
"line_5": {"type": ["string", "null"]},
|
||||
"line_6": {"type": ["string", "null"]},
|
||||
"postcode": {"type": ["string", "null"]},
|
||||
"type": {"enum": ["sms", "letter", "email"]},
|
||||
"type": {"enum": ["sms", "email"]},
|
||||
"status": {"type": "string"},
|
||||
"template": template,
|
||||
"body": {"type": "string"},
|
||||
@@ -75,8 +70,7 @@ get_notifications_request = {
|
||||
"status": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": NOTIFICATION_STATUS_TYPES +
|
||||
[NOTIFICATION_STATUS_LETTER_ACCEPTED + ', ' + NOTIFICATION_STATUS_LETTER_RECEIVED]
|
||||
"enum": NOTIFICATION_STATUS_TYPES
|
||||
}
|
||||
},
|
||||
"template_type": {
|
||||
@@ -216,60 +210,3 @@ post_email_response = {
|
||||
},
|
||||
"required": ["id", "content", "uri", "template"]
|
||||
}
|
||||
|
||||
post_letter_request = {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "POST letter notification schema",
|
||||
"type": "object",
|
||||
"title": "POST v2/notifications/letter",
|
||||
"properties": {
|
||||
"reference": {"type": "string"},
|
||||
"template_id": uuid,
|
||||
"personalisation": personalisation
|
||||
},
|
||||
"required": ["template_id", "personalisation"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
post_precompiled_letter_request = {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "POST precompiled letter notification schema",
|
||||
"type": "object",
|
||||
"title": "POST v2/notifications/letter",
|
||||
"properties": {
|
||||
"reference": {"type": "string"},
|
||||
"content": {"type": "string"},
|
||||
"postage": {"type": "string", "format": "postage"}
|
||||
},
|
||||
"required": ["reference", "content"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
|
||||
letter_content = {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "Letter content for POST letter notification",
|
||||
"type": "object",
|
||||
"title": "notification letter content",
|
||||
"properties": {
|
||||
"body": {"type": "string"},
|
||||
"subject": {"type": "string"}
|
||||
},
|
||||
"required": ["body", "subject"]
|
||||
}
|
||||
|
||||
post_letter_response = {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "POST sms notification response schema",
|
||||
"type": "object",
|
||||
"title": "response v2/notifications/letter",
|
||||
"properties": {
|
||||
"id": uuid,
|
||||
"reference": {"type": ["string", "null"]},
|
||||
"content": letter_content,
|
||||
"uri": {"type": "string", "format": "uri"},
|
||||
"template": template,
|
||||
# letters cannot be scheduled
|
||||
"scheduled_for": {"type": "null"}
|
||||
},
|
||||
"required": ["id", "content", "uri", "template"]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import base64
|
||||
import functools
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
@@ -13,36 +12,18 @@ from app import (
|
||||
authenticated_service,
|
||||
document_download_client,
|
||||
encryption,
|
||||
notify_celery,
|
||||
)
|
||||
from app.celery.letters_pdf_tasks import (
|
||||
get_pdf_for_templated_letter,
|
||||
sanitise_letter,
|
||||
)
|
||||
from app.celery.research_mode_tasks import create_fake_letter_response_file
|
||||
from app.celery.tasks import save_api_email, save_api_sms
|
||||
from app.clients.document_download import DocumentDownloadError
|
||||
from app.config import QueueNames, TaskNames
|
||||
from app.dao.dao_utils import transaction
|
||||
from app.dao.templates_dao import get_precompiled_letter_template
|
||||
from app.letters.utils import upload_letter_pdf
|
||||
from app.config import QueueNames
|
||||
from app.models import (
|
||||
EMAIL_TYPE,
|
||||
KEY_TYPE_NORMAL,
|
||||
KEY_TYPE_TEAM,
|
||||
KEY_TYPE_TEST,
|
||||
LETTER_TYPE,
|
||||
NOTIFICATION_CREATED,
|
||||
NOTIFICATION_DELIVERED,
|
||||
NOTIFICATION_PENDING_VIRUS_CHECK,
|
||||
NOTIFICATION_SENDING,
|
||||
PRIORITY,
|
||||
SMS_TYPE,
|
||||
Notification,
|
||||
)
|
||||
from app.notifications.process_letter_notifications import (
|
||||
create_letter_notification,
|
||||
)
|
||||
from app.notifications.process_notifications import (
|
||||
persist_notification,
|
||||
send_notification_to_queue_detached,
|
||||
@@ -55,7 +36,6 @@ from app.notifications.validators import (
|
||||
check_service_email_reply_to_id,
|
||||
check_service_has_permission,
|
||||
check_service_sms_sender_id,
|
||||
validate_address,
|
||||
validate_and_format_recipient,
|
||||
validate_template,
|
||||
)
|
||||
@@ -65,13 +45,10 @@ from app.v2.errors import BadRequestError
|
||||
from app.v2.notifications import v2_notification_blueprint
|
||||
from app.v2.notifications.create_response import (
|
||||
create_post_email_response_from_notification,
|
||||
create_post_letter_response_from_notification,
|
||||
create_post_sms_response_from_notification,
|
||||
)
|
||||
from app.v2.notifications.notification_schemas import (
|
||||
post_email_request,
|
||||
post_letter_request,
|
||||
post_precompiled_letter_request,
|
||||
post_sms_request,
|
||||
)
|
||||
from app.v2.utils import get_valid_json
|
||||
@@ -82,40 +59,6 @@ POST_NOTIFICATION_JSON_PARSE_DURATION_SECONDS = Histogram(
|
||||
)
|
||||
|
||||
|
||||
@v2_notification_blueprint.route('/{}'.format(LETTER_TYPE), methods=['POST'])
|
||||
def post_precompiled_letter_notification():
|
||||
request_json = get_valid_json()
|
||||
if 'content' not in (request_json or {}):
|
||||
return post_notification(LETTER_TYPE)
|
||||
|
||||
form = validate(request_json, post_precompiled_letter_request)
|
||||
|
||||
# Check permission to send letters
|
||||
check_service_has_permission(LETTER_TYPE, authenticated_service.permissions)
|
||||
|
||||
check_rate_limiting(authenticated_service, api_user)
|
||||
|
||||
template = get_precompiled_letter_template(authenticated_service.id)
|
||||
|
||||
# For precompiled letters the to field will be set to Provided as PDF until the validation passes,
|
||||
# then the address of the letter will be set as the to field
|
||||
form['personalisation'] = {
|
||||
'address_line_1': 'Provided as PDF'
|
||||
}
|
||||
|
||||
notification = process_letter_notification(
|
||||
letter_data=form,
|
||||
api_key=api_user,
|
||||
service=authenticated_service,
|
||||
template=template,
|
||||
template_with_content=None, # not required for precompiled
|
||||
reply_to_text='', # not required for precompiled
|
||||
precompiled=True
|
||||
)
|
||||
|
||||
return jsonify(notification), 201
|
||||
|
||||
|
||||
@v2_notification_blueprint.route('/<notification_type>', methods=['POST'])
|
||||
def post_notification(notification_type):
|
||||
with POST_NOTIFICATION_JSON_PARSE_DURATION_SECONDS.time():
|
||||
@@ -125,8 +68,6 @@ def post_notification(notification_type):
|
||||
form = validate(request_json, post_email_request)
|
||||
elif notification_type == SMS_TYPE:
|
||||
form = validate(request_json, post_sms_request)
|
||||
elif notification_type == LETTER_TYPE:
|
||||
form = validate(request_json, post_letter_request)
|
||||
else:
|
||||
abort(404)
|
||||
|
||||
@@ -144,25 +85,15 @@ def post_notification(notification_type):
|
||||
|
||||
reply_to = get_reply_to_text(notification_type, form, template)
|
||||
|
||||
if notification_type == LETTER_TYPE:
|
||||
notification = process_letter_notification(
|
||||
letter_data=form,
|
||||
api_key=api_user,
|
||||
service=authenticated_service,
|
||||
template=template,
|
||||
template_with_content=template_with_content,
|
||||
reply_to_text=reply_to
|
||||
)
|
||||
else:
|
||||
notification = process_sms_or_email_notification(
|
||||
form=form,
|
||||
notification_type=notification_type,
|
||||
template=template,
|
||||
template_with_content=template_with_content,
|
||||
template_process_type=template.process_type,
|
||||
service=authenticated_service,
|
||||
reply_to_text=reply_to
|
||||
)
|
||||
notification = process_sms_or_email_notification(
|
||||
form=form,
|
||||
notification_type=notification_type,
|
||||
template=template,
|
||||
template_with_content=template_with_content,
|
||||
template_process_type=template.process_type,
|
||||
service=authenticated_service,
|
||||
reply_to_text=reply_to
|
||||
)
|
||||
|
||||
return jsonify(notification), 201
|
||||
|
||||
@@ -342,113 +273,6 @@ def process_document_uploads(personalisation_data, service, simulated=False):
|
||||
return personalisation_data, len(file_keys)
|
||||
|
||||
|
||||
def process_letter_notification(
|
||||
*, letter_data, api_key, service, template, template_with_content, reply_to_text, precompiled=False
|
||||
):
|
||||
if api_key.key_type == KEY_TYPE_TEAM:
|
||||
raise BadRequestError(message='Cannot send letters with a team api key', status_code=403)
|
||||
|
||||
if not service.research_mode and service.restricted and api_key.key_type != KEY_TYPE_TEST:
|
||||
raise BadRequestError(message='Cannot send letters when service is in trial mode', status_code=403)
|
||||
|
||||
if precompiled:
|
||||
return process_precompiled_letter_notifications(letter_data=letter_data,
|
||||
api_key=api_key,
|
||||
service=service,
|
||||
template=template,
|
||||
reply_to_text=reply_to_text)
|
||||
|
||||
postage = validate_address(service, letter_data['personalisation'])
|
||||
|
||||
test_key = api_key.key_type == KEY_TYPE_TEST
|
||||
|
||||
status = NOTIFICATION_CREATED
|
||||
updated_at = None
|
||||
if test_key:
|
||||
# if we don't want to actually send the letter, then start it off in SENDING so we don't pick it up
|
||||
if current_app.config['NOTIFY_ENVIRONMENT'] in ['preview', 'development']:
|
||||
status = NOTIFICATION_SENDING
|
||||
# mark test letter as delivered and do not create a fake response later
|
||||
else:
|
||||
status = NOTIFICATION_DELIVERED
|
||||
updated_at = datetime.utcnow()
|
||||
|
||||
queue = QueueNames.CREATE_LETTERS_PDF if not test_key else QueueNames.RESEARCH_MODE
|
||||
|
||||
notification = create_letter_notification(letter_data=letter_data,
|
||||
service=service,
|
||||
template=template,
|
||||
api_key=api_key,
|
||||
status=status,
|
||||
reply_to_text=reply_to_text,
|
||||
updated_at=updated_at,
|
||||
postage=postage
|
||||
)
|
||||
|
||||
get_pdf_for_templated_letter.apply_async(
|
||||
[str(notification.id)],
|
||||
queue=queue
|
||||
)
|
||||
|
||||
if test_key and current_app.config['NOTIFY_ENVIRONMENT'] in ['preview', 'development']:
|
||||
create_fake_letter_response_file.apply_async(
|
||||
(notification.reference,),
|
||||
queue=queue
|
||||
)
|
||||
|
||||
resp = create_response_for_post_notification(
|
||||
notification_id=notification.id,
|
||||
client_reference=notification.client_reference,
|
||||
template_id=notification.template_id,
|
||||
template_version=notification.template_version,
|
||||
notification_type=notification.notification_type,
|
||||
reply_to=reply_to_text,
|
||||
service_id=notification.service_id,
|
||||
template_with_content=template_with_content
|
||||
)
|
||||
return resp
|
||||
|
||||
|
||||
def process_precompiled_letter_notifications(*, letter_data, api_key, service, template, reply_to_text):
|
||||
try:
|
||||
status = NOTIFICATION_PENDING_VIRUS_CHECK
|
||||
letter_content = base64.b64decode(letter_data['content'])
|
||||
except ValueError:
|
||||
raise BadRequestError(message='Cannot decode letter content (invalid base64 encoding)', status_code=400)
|
||||
|
||||
with transaction():
|
||||
notification = create_letter_notification(letter_data=letter_data,
|
||||
service=service,
|
||||
template=template,
|
||||
api_key=api_key,
|
||||
status=status,
|
||||
reply_to_text=reply_to_text)
|
||||
filename = upload_letter_pdf(notification, letter_content, precompiled=True)
|
||||
|
||||
resp = {
|
||||
'id': notification.id,
|
||||
'reference': notification.client_reference,
|
||||
'postage': notification.postage
|
||||
}
|
||||
|
||||
# call task to add the filename to anti virus queue
|
||||
if current_app.config['ANTIVIRUS_ENABLED']:
|
||||
current_app.logger.info('Calling task scan-file for {}'.format(filename))
|
||||
notify_celery.send_task(
|
||||
name=TaskNames.SCAN_FILE,
|
||||
kwargs={'filename': filename},
|
||||
queue=QueueNames.ANTIVIRUS,
|
||||
)
|
||||
else:
|
||||
# stub out antivirus in dev
|
||||
sanitise_letter.apply_async(
|
||||
[filename],
|
||||
queue=QueueNames.LETTERS
|
||||
)
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
def get_reply_to_text(notification_type, form, template):
|
||||
reply_to = None
|
||||
if notification_type == EMAIL_TYPE:
|
||||
@@ -467,9 +291,6 @@ def get_reply_to_text(notification_type, form, template):
|
||||
else:
|
||||
reply_to = template.reply_to_text
|
||||
|
||||
elif notification_type == LETTER_TYPE:
|
||||
reply_to = template.reply_to_text
|
||||
|
||||
return reply_to
|
||||
|
||||
|
||||
@@ -494,11 +315,6 @@ def create_response_for_post_notification(
|
||||
subject=template_with_content.subject,
|
||||
email_from='{}@{}'.format(authenticated_service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN']),
|
||||
)
|
||||
elif notification_type == LETTER_TYPE:
|
||||
create_resp_partial = functools.partial(
|
||||
create_post_letter_response_from_notification,
|
||||
subject=template_with_content.subject,
|
||||
)
|
||||
resp = create_resp_partial(
|
||||
notification_id, client_reference, template_id, template_version, service_id,
|
||||
url_root=request.url_root,
|
||||
|
||||
@@ -36,7 +36,6 @@ get_template_by_id_response = {
|
||||
"body": {"type": "string"},
|
||||
"subject": {"type": ["string", "null"]},
|
||||
"name": {"type": "string"},
|
||||
"postage": {"type": "string", "format": "postage"}
|
||||
},
|
||||
"required": ["id", "type", "created_at", "updated_at", "version", "created_by", "body", "name"],
|
||||
}
|
||||
@@ -64,7 +63,6 @@ post_template_preview_response = {
|
||||
"version": {"type": "integer"},
|
||||
"body": {"type": "string"},
|
||||
"subject": {"type": ["string", "null"]},
|
||||
"postage": {"type": "string", "format": "postage"},
|
||||
"html": {"type": ["string", "null"]},
|
||||
},
|
||||
"required": ["id", "type", "version", "body"],
|
||||
@@ -80,5 +78,4 @@ def create_post_template_preview_response(template, template_object):
|
||||
"body": template_object.content_with_placeholders_filled_in,
|
||||
"html": getattr(template_object, 'html_body', None),
|
||||
"subject": getattr(template_object, 'subject', None),
|
||||
"postage": template.postage
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user