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:
Steven Reilly
2023-03-02 20:20:31 -05:00
committed by GitHub
parent b07b95f795
commit ff4190a8eb
141 changed files with 1108 additions and 12083 deletions

View File

@@ -1,61 +0,0 @@
import json
from functools import wraps
from flask import Blueprint, current_app, jsonify, request
from app.celery.tasks import (
record_daily_sorted_counts,
update_letter_notifications_statuses,
)
from app.config import QueueNames
from app.notifications.utils import autoconfirm_subscription
from app.schema_validation import validate
from app.v2.errors import register_errors
letter_callback_blueprint = Blueprint('notifications_letter_callback', __name__)
register_errors(letter_callback_blueprint)
dvla_sns_callback_schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "sns callback received on s3 update",
"type": "object",
"title": "dvla internal sns callback",
"properties": {
"Type": {"enum": ["Notification", "SubscriptionConfirmation"]},
"MessageId": {"type": "string"},
"Message": {"type": ["string", "object"]}
},
"required": ["Type", "MessageId", "Message"]
}
def validate_schema(schema):
def decorator(f):
@wraps(f)
def wrapper(*args, **kw):
validate(request.get_json(force=True), schema)
return f(*args, **kw)
return wrapper
return decorator
@letter_callback_blueprint.route('/notifications/letter/dvla', methods=['POST'])
@validate_schema(dvla_sns_callback_schema)
def process_letter_response():
req_json = request.get_json(force=True)
current_app.logger.debug('Received SNS callback: {}'.format(req_json))
if not autoconfirm_subscription(req_json):
# The callback should have one record for an S3 Put Event.
message = json.loads(req_json['Message'])
filename = message['Records'][0]['s3']['object']['key']
current_app.logger.info('Received file from DVLA: {}'.format(filename))
if filename.lower().endswith('rs.txt') or filename.lower().endswith('rsp.txt'):
current_app.logger.info('DVLA callback: Calling task to update letter notifications')
update_letter_notifications_statuses.apply_async([filename], queue=QueueNames.NOTIFY)
record_daily_sorted_counts.apply_async([filename], queue=QueueNames.NOTIFY)
return jsonify(
result="success", message="DVLA callback succeeded"
), 200

View File

@@ -1,41 +0,0 @@
from notifications_utils.postal_address import PostalAddress
from app import create_random_identifier
from app.models import LETTER_TYPE
from app.notifications.process_notifications import persist_notification
def create_letter_notification(
letter_data,
template,
service,
api_key,
status,
reply_to_text=None,
billable_units=None,
updated_at=None,
postage=None
):
notification = persist_notification(
template_id=template.id,
template_version=template.version,
# we only accept addresses_with_underscores from the API (from CSV we also accept dashes, spaces etc)
recipient=PostalAddress.from_personalisation(letter_data['personalisation']).normalised,
service=service,
personalisation=letter_data['personalisation'],
notification_type=LETTER_TYPE,
api_key_id=api_key.id,
key_type=api_key.key_type,
job_id=None,
job_row_number=None,
reference=create_random_identifier(),
client_reference=letter_data.get('reference'),
status=status,
reply_to_text=reply_to_text,
billable_units=billable_units,
# letter_data.get('postage') is only set for precompiled letters (if international it is set after sanitise)
# letters from a template will pass in 'europe' or 'rest-of-world' if None then use postage from template
postage=postage or letter_data.get('postage') or template.postage,
updated_at=updated_at
)
return notification

View File

@@ -10,14 +10,12 @@ from notifications_utils.recipients import (
validate_and_format_phone_number,
)
from notifications_utils.template import (
LetterPrintTemplate,
PlainTextEmailTemplate,
SMSMessageTemplate,
)
from app import redis_store
from app.celery import provider_tasks
from app.celery.letters_pdf_tasks import get_pdf_for_templated_letter
from app.config import QueueNames
from app.dao.notifications_dao import (
dao_create_notification,
@@ -25,9 +23,7 @@ from app.dao.notifications_dao import (
)
from app.models import (
EMAIL_TYPE,
INTERNATIONAL_POSTAGE_TYPES,
KEY_TYPE_TEST,
LETTER_TYPE,
NOTIFICATION_CREATED,
SMS_TYPE,
Notification,
@@ -58,16 +54,6 @@ def create_content_for_notification(template, personalisation):
},
personalisation,
)
if template.template_type == LETTER_TYPE:
template_object = LetterPrintTemplate(
{
'content': template.content,
'subject': template.subject,
'template_type': template.template_type,
},
personalisation,
contact_block=template.reply_to_text,
)
check_placeholders(template_object)
@@ -101,7 +87,6 @@ def persist_notification(
status=NOTIFICATION_CREATED,
reply_to_text=None,
billable_units=None,
postage=None,
document_download_count=None,
updated_at=None
):
@@ -149,10 +134,6 @@ def persist_notification(
current_app.logger.info('Persisting notification with type: {}'.format(EMAIL_TYPE))
notification.normalised_to = format_email_address(notification.to)
current_app.logger.info('Persisting notification to formatted email: {}'.format(notification.normalised_to))
elif notification_type == LETTER_TYPE:
notification.postage = postage
notification.international = postage in INTERNATIONAL_POSTAGE_TYPES
notification.normalised_to = ''.join(notification.to.split()).lower()
# if simulated create a Notification model to return but do not persist the Notification to the dB
if not simulated:
@@ -194,10 +175,6 @@ def send_notification_to_queue_detached(
if not queue:
queue = QueueNames.SEND_EMAIL
deliver_task = provider_tasks.deliver_email
if notification_type == LETTER_TYPE:
if not queue:
queue = QueueNames.CREATE_LETTERS_PDF
deliver_task = get_pdf_for_templated_letter
try:
deliver_task.apply_async([str(notification_id)], queue=queue)

View File

@@ -5,13 +5,7 @@ from app import api_user, authenticated_service
from app.config import QueueNames
from app.dao import notifications_dao
from app.errors import InvalidRequest, register_errors
from app.models import (
EMAIL_TYPE,
KEY_TYPE_TEAM,
LETTER_TYPE,
PRIORITY,
SMS_TYPE,
)
from app.models import EMAIL_TYPE, KEY_TYPE_TEAM, PRIORITY, SMS_TYPE
from app.notifications.process_notifications import (
persist_notification,
send_notification_to_queue,
@@ -81,7 +75,6 @@ def send_notification(notification_type):
if notification_type not in [SMS_TYPE, EMAIL_TYPE]:
msg = "{} notification type is not supported".format(notification_type)
msg = msg + ", please use the latest version of the client" if notification_type == LETTER_TYPE else msg
raise InvalidRequest(msg, 400)
notification_form = (
@@ -111,7 +104,6 @@ def send_notification(notification_type):
simulated = simulated_recipient(notification_form['to'], notification_type)
notification_model = persist_notification(template_id=template.id,
template_version=template.version,
postage=template.postage,
recipient=request.get_json()['to'],
service=authenticated_service,
personalisation=notification_form.get('personalisation', None),

View File

@@ -5,7 +5,6 @@ from notifications_utils.clients.redis import (
daily_limit_cache_key,
rate_limit_cache_key,
)
from notifications_utils.postal_address import PostalAddress
from notifications_utils.recipients import (
get_international_phone_info,
validate_and_format_email_address,
@@ -15,15 +14,12 @@ from sqlalchemy.orm.exc import NoResultFound
from app import redis_store
from app.dao.service_email_reply_to_dao import dao_get_reply_to_by_id
from app.dao.service_letter_contact_dao import dao_get_letter_contact_by_id
from app.dao.service_sms_sender_dao import dao_get_service_sms_senders_by_id
from app.models import (
EMAIL_TYPE,
INTERNATIONAL_LETTERS,
INTERNATIONAL_SMS_TYPE,
KEY_TYPE_TEAM,
KEY_TYPE_TEST,
LETTER_TYPE,
SMS_TYPE,
ServicePermission,
)
@@ -33,12 +29,7 @@ from app.notifications.process_notifications import (
from app.serialised_models import SerialisedTemplate
from app.service.utils import service_allowed_to_send_to
from app.utils import get_public_notify_type_text
from app.v2.errors import (
BadRequestError,
RateLimitError,
TooManyRequestsError,
ValidationError,
)
from app.v2.errors import BadRequestError, RateLimitError, TooManyRequestsError
REDIS_EXCEEDED_RATE_LIMIT_DURATION_SECONDS = Histogram(
'redis_exceeded_rate_limit_duration_seconds',
@@ -208,8 +199,6 @@ def check_reply_to(service_id, reply_to_id, type_):
return check_service_email_reply_to_id(service_id, reply_to_id, type_)
elif type_ == SMS_TYPE:
return check_service_sms_sender_id(service_id, reply_to_id, type_)
elif type_ == LETTER_TYPE:
return check_service_letter_contact_id(service_id, reply_to_id, type_)
def check_service_email_reply_to_id(service_id, reply_to_id, notification_type):
@@ -230,44 +219,3 @@ def check_service_sms_sender_id(service_id, sms_sender_id, notification_type):
message = 'sms_sender_id {} does not exist in database for service id {}' \
.format(sms_sender_id, service_id)
raise BadRequestError(message=message)
def check_service_letter_contact_id(service_id, letter_contact_id, notification_type):
if letter_contact_id:
try:
return dao_get_letter_contact_by_id(service_id, letter_contact_id).contact_block
except NoResultFound:
message = 'letter_contact_id {} does not exist in database for service id {}' \
.format(letter_contact_id, service_id)
raise BadRequestError(message=message)
def validate_address(service, letter_data):
address = PostalAddress.from_personalisation(
letter_data,
allow_international_letters=(INTERNATIONAL_LETTERS in str(service.permissions)),
)
if not address.has_enough_lines:
raise ValidationError(
message=f'Address must be at least {PostalAddress.MIN_LINES} lines'
)
if address.has_too_many_lines:
raise ValidationError(
message=f'Address must be no more than {PostalAddress.MAX_LINES} lines'
)
if not address.has_valid_last_line:
if address.allow_international_letters:
raise ValidationError(
message='Last line of address must be a real UK postcode or another country'
)
raise ValidationError(
message='Must be a real UK postcode'
)
if address.has_invalid_characters:
raise ValidationError(
message='Address lines must not start with any of the following characters: @ ( ) = [ ] ” \\ / , < >'
)
if address.international:
return address.postage
else:
return None