2017-07-07 17:10:16 +01:00
|
|
|
import functools
|
|
|
|
|
|
2017-07-20 15:23:46 +01:00
|
|
|
from flask import request, jsonify, current_app, abort
|
2016-10-31 12:22:26 +00:00
|
|
|
|
2017-12-18 16:17:20 +00:00
|
|
|
from notifications_utils.recipients import try_validate_and_format_phone_number
|
|
|
|
|
|
2017-05-05 15:23:06 +01:00
|
|
|
from app import api_user, authenticated_service
|
2017-07-19 13:50:29 +01:00
|
|
|
from app.config import QueueNames
|
2018-01-26 14:08:01 +00:00
|
|
|
from app.dao.notifications_dao import update_notification_status_by_reference
|
2018-02-23 10:38:36 +00:00
|
|
|
from app.dao.templates_dao import dao_create_template
|
|
|
|
|
from app.dao.users_dao import get_user_by_id
|
2017-09-26 09:56:09 +01:00
|
|
|
from app.models import (
|
2018-02-23 10:38:36 +00:00
|
|
|
Template,
|
2017-09-26 09:56:09 +01:00
|
|
|
SMS_TYPE,
|
|
|
|
|
EMAIL_TYPE,
|
|
|
|
|
LETTER_TYPE,
|
|
|
|
|
PRIORITY,
|
|
|
|
|
KEY_TYPE_TEST,
|
|
|
|
|
KEY_TYPE_TEAM,
|
|
|
|
|
NOTIFICATION_CREATED,
|
2018-01-26 14:08:01 +00:00
|
|
|
NOTIFICATION_SENDING,
|
|
|
|
|
NOTIFICATION_DELIVERED
|
2017-09-26 09:56:09 +01:00
|
|
|
)
|
2017-12-11 16:23:09 +00:00
|
|
|
from app.celery.letters_pdf_tasks import create_letters_pdf
|
2018-01-26 14:08:01 +00:00
|
|
|
from app.celery.research_mode_tasks import create_fake_letter_response_file
|
2017-04-24 14:15:08 +01:00
|
|
|
from app.notifications.process_notifications import (
|
|
|
|
|
persist_notification,
|
2017-10-05 13:22:00 +01:00
|
|
|
persist_scheduled_notification,
|
2017-10-05 16:29:11 +01:00
|
|
|
send_notification_to_queue,
|
2017-11-29 16:47:23 +00:00
|
|
|
simulated_recipient
|
|
|
|
|
)
|
2017-07-26 15:57:30 +01:00
|
|
|
from app.notifications.process_letter_notifications import (
|
|
|
|
|
create_letter_notification
|
|
|
|
|
)
|
2017-04-24 14:15:08 +01:00
|
|
|
from app.notifications.validators import (
|
|
|
|
|
validate_and_format_recipient,
|
2017-06-13 17:33:04 +01:00
|
|
|
check_rate_limiting,
|
2017-07-03 13:25:02 +01:00
|
|
|
check_service_can_schedule_notification,
|
|
|
|
|
check_service_has_permission,
|
2017-10-04 14:34:45 +01:00
|
|
|
validate_template,
|
2017-10-30 13:36:49 +00:00
|
|
|
check_service_email_reply_to_id,
|
|
|
|
|
check_service_sms_sender_id
|
2017-06-13 17:33:04 +01:00
|
|
|
)
|
2016-10-27 11:46:37 +01:00
|
|
|
from app.schema_validation import validate
|
2017-07-31 18:28:00 +01:00
|
|
|
from app.v2.errors import BadRequestError
|
2017-03-16 18:15:49 +00:00
|
|
|
from app.v2.notifications import v2_notification_blueprint
|
2017-04-24 14:15:08 +01:00
|
|
|
from app.v2.notifications.notification_schemas import (
|
|
|
|
|
post_sms_request,
|
|
|
|
|
post_email_request,
|
2018-02-23 10:38:36 +00:00
|
|
|
post_letter_request,
|
|
|
|
|
post_precompiled_letter_request
|
2017-07-20 15:23:46 +01:00
|
|
|
)
|
|
|
|
|
from app.v2.notifications.create_response import (
|
2017-07-07 17:10:16 +01:00
|
|
|
create_post_sms_response_from_notification,
|
|
|
|
|
create_post_email_response_from_notification,
|
|
|
|
|
create_post_letter_response_from_notification
|
|
|
|
|
)
|
2016-10-25 18:04:03 +01:00
|
|
|
|
|
|
|
|
|
2018-02-23 10:38:36 +00:00
|
|
|
@v2_notification_blueprint.route('/{}'.format(LETTER_TYPE), methods=['POST'])
|
|
|
|
|
def post_precompiled_letter_notification():
|
|
|
|
|
if 'content' not in (request.get_json() or {}):
|
|
|
|
|
return post_notification(LETTER_TYPE)
|
|
|
|
|
|
|
|
|
|
form = validate(request.get_json(), post_precompiled_letter_request)
|
|
|
|
|
|
|
|
|
|
#check_service_has_permission(notification_type, authenticated_service.permissions)
|
|
|
|
|
|
|
|
|
|
check_rate_limiting(authenticated_service, api_user)
|
|
|
|
|
|
|
|
|
|
template = get_precompiled_letter_template(authenticated_service.id)
|
|
|
|
|
|
|
|
|
|
form['personalisation'] = {
|
|
|
|
|
'address_line_1': form['reference']
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reply_to = get_reply_to_text(LETTER_TYPE, form, template)
|
|
|
|
|
|
|
|
|
|
notification = process_letter_notification(
|
|
|
|
|
letter_data=form,
|
|
|
|
|
api_key=api_user,
|
|
|
|
|
template=template,
|
|
|
|
|
reply_to_text=reply_to
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
create_resp_partial = functools.partial(
|
|
|
|
|
create_post_letter_response_from_notification,
|
|
|
|
|
subject=template.subject,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
resp = create_resp_partial(
|
|
|
|
|
notification=notification,
|
|
|
|
|
content=None,
|
|
|
|
|
url_root=request.url_root,
|
|
|
|
|
scheduled_for=None,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return jsonify(resp), 201
|
|
|
|
|
|
|
|
|
|
|
2017-03-16 18:15:49 +00:00
|
|
|
@v2_notification_blueprint.route('/<notification_type>', methods=['POST'])
|
2017-01-17 12:08:24 +00:00
|
|
|
def post_notification(notification_type):
|
|
|
|
|
if notification_type == EMAIL_TYPE:
|
|
|
|
|
form = validate(request.get_json(), post_email_request)
|
2017-07-07 17:10:16 +01:00
|
|
|
elif notification_type == SMS_TYPE:
|
2017-01-17 12:08:24 +00:00
|
|
|
form = validate(request.get_json(), post_sms_request)
|
2017-07-07 17:10:16 +01:00
|
|
|
elif notification_type == LETTER_TYPE:
|
|
|
|
|
form = validate(request.get_json(), post_letter_request)
|
|
|
|
|
else:
|
2017-07-20 15:23:46 +01:00
|
|
|
abort(404)
|
2017-04-26 15:56:45 +01:00
|
|
|
|
2017-07-03 13:25:02 +01:00
|
|
|
check_service_has_permission(notification_type, authenticated_service.permissions)
|
2017-06-29 11:13:32 +01:00
|
|
|
|
2017-05-24 16:27:15 +01:00
|
|
|
scheduled_for = form.get("scheduled_for", None)
|
2017-10-04 14:34:45 +01:00
|
|
|
|
2017-07-03 13:25:02 +01:00
|
|
|
check_service_can_schedule_notification(authenticated_service.permissions, scheduled_for)
|
2017-05-26 15:41:14 +01:00
|
|
|
|
2017-05-05 15:23:06 +01:00
|
|
|
check_rate_limiting(authenticated_service, api_user)
|
2017-04-24 14:15:08 +01:00
|
|
|
|
2017-06-13 17:33:04 +01:00
|
|
|
template, template_with_content = validate_template(
|
|
|
|
|
form['template_id'],
|
|
|
|
|
form.get('personalisation', {}),
|
|
|
|
|
authenticated_service,
|
2017-06-29 11:13:32 +01:00
|
|
|
notification_type,
|
2017-06-13 17:33:04 +01:00
|
|
|
)
|
2016-11-14 13:56:09 +00:00
|
|
|
|
2017-12-15 17:13:55 +00:00
|
|
|
reply_to = get_reply_to_text(notification_type, form, template)
|
|
|
|
|
|
2017-07-07 17:10:16 +01:00
|
|
|
if notification_type == LETTER_TYPE:
|
|
|
|
|
notification = process_letter_notification(
|
2017-07-26 15:57:30 +01:00
|
|
|
letter_data=form,
|
2017-07-07 17:10:16 +01:00
|
|
|
api_key=api_user,
|
|
|
|
|
template=template,
|
2017-12-15 17:13:55 +00:00
|
|
|
reply_to_text=reply_to
|
2017-07-07 17:10:16 +01:00
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
notification = process_sms_or_email_notification(
|
|
|
|
|
form=form,
|
|
|
|
|
notification_type=notification_type,
|
|
|
|
|
api_key=api_user,
|
|
|
|
|
template=template,
|
2017-11-23 14:55:49 +00:00
|
|
|
service=authenticated_service,
|
2017-11-23 14:55:49 +00:00
|
|
|
reply_to_text=reply_to
|
2017-07-07 17:10:16 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if notification_type == SMS_TYPE:
|
|
|
|
|
create_resp_partial = functools.partial(
|
|
|
|
|
create_post_sms_response_from_notification,
|
2017-11-23 14:55:49 +00:00
|
|
|
from_number=reply_to
|
2017-07-07 17:10:16 +01:00
|
|
|
)
|
|
|
|
|
elif notification_type == EMAIL_TYPE:
|
|
|
|
|
create_resp_partial = functools.partial(
|
|
|
|
|
create_post_email_response_from_notification,
|
|
|
|
|
subject=template_with_content.subject,
|
2017-09-11 11:10:45 +01:00
|
|
|
email_from='{}@{}'.format(authenticated_service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN'])
|
2017-07-07 17:10:16 +01:00
|
|
|
)
|
|
|
|
|
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=notification,
|
|
|
|
|
content=str(template_with_content),
|
|
|
|
|
url_root=request.url_root,
|
|
|
|
|
scheduled_for=scheduled_for
|
|
|
|
|
)
|
|
|
|
|
return jsonify(resp), 201
|
|
|
|
|
|
|
|
|
|
|
2017-11-23 14:55:49 +00:00
|
|
|
def process_sms_or_email_notification(*, form, notification_type, api_key, template, service, reply_to_text=None):
|
2017-07-07 17:10:16 +01:00
|
|
|
form_send_to = form['email_address'] if notification_type == EMAIL_TYPE else form['phone_number']
|
|
|
|
|
|
|
|
|
|
send_to = validate_and_format_recipient(send_to=form_send_to,
|
|
|
|
|
key_type=api_key.key_type,
|
|
|
|
|
service=service,
|
|
|
|
|
notification_type=notification_type)
|
|
|
|
|
|
2017-01-17 12:08:24 +00:00
|
|
|
# Do not persist or send notification to the queue if it is a simulated recipient
|
|
|
|
|
simulated = simulated_recipient(send_to, notification_type)
|
2017-04-24 14:15:08 +01:00
|
|
|
|
2017-07-07 17:10:16 +01:00
|
|
|
notification = persist_notification(
|
|
|
|
|
template_id=template.id,
|
|
|
|
|
template_version=template.version,
|
|
|
|
|
recipient=form_send_to,
|
|
|
|
|
service=service,
|
|
|
|
|
personalisation=form.get('personalisation', None),
|
|
|
|
|
notification_type=notification_type,
|
|
|
|
|
api_key_id=api_key.id,
|
|
|
|
|
key_type=api_key.key_type,
|
|
|
|
|
client_reference=form.get('reference', None),
|
2017-11-23 14:55:49 +00:00
|
|
|
simulated=simulated,
|
|
|
|
|
reply_to_text=reply_to_text
|
2017-07-07 17:10:16 +01:00
|
|
|
)
|
2017-04-24 14:15:08 +01:00
|
|
|
|
2017-07-07 17:10:16 +01:00
|
|
|
scheduled_for = form.get("scheduled_for", None)
|
2017-05-15 17:27:38 +01:00
|
|
|
if scheduled_for:
|
|
|
|
|
persist_scheduled_notification(notification.id, form["scheduled_for"])
|
2017-01-17 12:08:24 +00:00
|
|
|
else:
|
2017-05-15 17:27:38 +01:00
|
|
|
if not simulated:
|
2017-05-30 10:18:18 +01:00
|
|
|
queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None
|
2017-05-22 14:05:57 +01:00
|
|
|
send_notification_to_queue(
|
|
|
|
|
notification=notification,
|
2017-07-07 17:10:16 +01:00
|
|
|
research_mode=service.research_mode,
|
2017-05-22 14:05:57 +01:00
|
|
|
queue=queue_name
|
|
|
|
|
)
|
2017-05-15 17:27:38 +01:00
|
|
|
else:
|
As Notify matures we probably need less logging, especially to report happy path events.
This PR is a proposal to reduce the average messages we see for a single notification from about 7 messages to 2.
Messaging would change to something like this:
February 2nd 2018, 15:39:05.885 Full delivery response from Firetext for notification: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
{'status': ['0'], 'reference': ['8eda51d5-cd82-4569-bfc9-d5570cdf2126'], 'time': ['2018-02-02 15:39:01'], 'code': ['000']}
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:57.727 SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to provider firetext at 2018-02-02 15:38:56.716814
February 2nd 2018, 15:38:56.727 Starting sending SMS 8eda51d5-cd82-4569-bfc9-d5570cdf2126 to provider at 2018-02-02 15:38:56.408181
February 2nd 2018, 15:38:56.727 Firetext request for 8eda51d5-cd82-4569-bfc9-d5570cdf2126 finished in 0.30376038211397827
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 sent to the priority-tasks queue for delivery
To somthing like this:
February 2nd 2018, 15:39:05.885 Firetext callback return status of 0 for reference: 8eda51d5-cd82-4569-bfc9-d5570cdf2126
February 2nd 2018, 15:38:49.449 sms 8eda51d5-cd82-4569-bfc9-d5570cdf2126 created at 2018-02-02 15:38:48.439113
2018-02-02 15:55:25 +00:00
|
|
|
current_app.logger.debug("POST simulated notification for id: {}".format(notification.id))
|
2017-05-15 17:27:38 +01:00
|
|
|
|
2017-07-07 17:10:16 +01:00
|
|
|
return notification
|
|
|
|
|
|
|
|
|
|
|
2017-12-15 17:13:55 +00:00
|
|
|
def process_letter_notification(*, letter_data, api_key, template, reply_to_text):
|
2017-07-31 18:28:00 +01:00
|
|
|
if api_key.key_type == KEY_TYPE_TEAM:
|
|
|
|
|
raise BadRequestError(message='Cannot send letters with a team api key', status_code=403)
|
|
|
|
|
|
2018-01-26 14:08:01 +00:00
|
|
|
if not api_key.service.research_mode and api_key.service.restricted and api_key.key_type != KEY_TYPE_TEST:
|
2017-09-12 16:13:07 +01:00
|
|
|
raise BadRequestError(message='Cannot send letters when service is in trial mode', status_code=403)
|
2017-08-25 16:07:30 +01:00
|
|
|
|
2017-09-26 09:56:09 +01:00
|
|
|
should_send = not (api_key.service.research_mode or api_key.key_type == KEY_TYPE_TEST)
|
2017-08-01 18:23:29 +01:00
|
|
|
|
2017-09-26 09:56:09 +01:00
|
|
|
# if we don't want to actually send the letter, then start it off in SENDING so we don't pick it up
|
|
|
|
|
status = NOTIFICATION_CREATED if should_send else NOTIFICATION_SENDING
|
2017-11-24 16:17:25 +00:00
|
|
|
notification = create_letter_notification(letter_data=letter_data,
|
|
|
|
|
template=template,
|
|
|
|
|
api_key=api_key,
|
|
|
|
|
status=status,
|
2017-12-15 17:13:55 +00:00
|
|
|
reply_to_text=reply_to_text)
|
2017-09-26 09:56:09 +01:00
|
|
|
|
2018-02-13 18:38:32 +00:00
|
|
|
if should_send:
|
|
|
|
|
create_letters_pdf.apply_async(
|
|
|
|
|
[str(notification.id)],
|
|
|
|
|
queue=QueueNames.CREATE_LETTERS_PDF
|
|
|
|
|
)
|
|
|
|
|
elif (api_key.service.research_mode and
|
|
|
|
|
current_app.config['NOTIFY_ENVIRONMENT'] in ['preview', 'development']):
|
|
|
|
|
create_fake_letter_response_file.apply_async(
|
|
|
|
|
(notification.reference,),
|
|
|
|
|
queue=QueueNames.RESEARCH_MODE
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
update_notification_status_by_reference(notification.reference, NOTIFICATION_DELIVERED)
|
2017-12-11 16:23:09 +00:00
|
|
|
|
2017-07-26 15:57:30 +01:00
|
|
|
return notification
|
2017-11-23 14:55:49 +00:00
|
|
|
|
|
|
|
|
|
2017-12-15 17:13:55 +00:00
|
|
|
def get_reply_to_text(notification_type, form, template):
|
2017-11-23 16:57:17 +00:00
|
|
|
reply_to = None
|
|
|
|
|
if notification_type == EMAIL_TYPE:
|
2017-11-27 11:33:04 +00:00
|
|
|
service_email_reply_to_id = form.get("email_reply_to_id", None)
|
2017-11-23 16:57:17 +00:00
|
|
|
reply_to = check_service_email_reply_to_id(
|
2017-11-23 14:55:49 +00:00
|
|
|
str(authenticated_service.id), service_email_reply_to_id, notification_type
|
2017-12-15 17:13:55 +00:00
|
|
|
) or template.get_reply_to_text()
|
2017-11-23 16:57:17 +00:00
|
|
|
|
|
|
|
|
elif notification_type == SMS_TYPE:
|
2017-11-27 11:33:04 +00:00
|
|
|
service_sms_sender_id = form.get("sms_sender_id", None)
|
2017-12-18 16:17:20 +00:00
|
|
|
sms_sender_id = check_service_sms_sender_id(
|
2017-11-23 16:57:17 +00:00
|
|
|
str(authenticated_service.id), service_sms_sender_id, notification_type
|
2017-12-18 16:17:20 +00:00
|
|
|
)
|
|
|
|
|
if sms_sender_id:
|
|
|
|
|
reply_to = try_validate_and_format_phone_number(sms_sender_id)
|
|
|
|
|
else:
|
|
|
|
|
reply_to = template.get_reply_to_text()
|
2017-11-23 16:57:17 +00:00
|
|
|
|
|
|
|
|
elif notification_type == LETTER_TYPE:
|
2017-12-15 17:13:55 +00:00
|
|
|
reply_to = template.get_reply_to_text()
|
2017-11-23 16:57:17 +00:00
|
|
|
|
2017-11-23 14:55:49 +00:00
|
|
|
return reply_to
|
2018-02-23 10:38:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_precompiled_letter_template(service_id):
|
|
|
|
|
template = Template.query.filter_by(
|
|
|
|
|
service_id=service_id,
|
|
|
|
|
template_type=LETTER_TYPE,
|
|
|
|
|
hidden=True
|
|
|
|
|
).first()
|
|
|
|
|
if template is not None:
|
|
|
|
|
return template
|
|
|
|
|
|
|
|
|
|
template = Template(
|
|
|
|
|
name='Pre-compiled PDF letter',
|
|
|
|
|
created_by=get_user_by_id(api_user.created_by_id),
|
|
|
|
|
service_id=service_id,
|
|
|
|
|
template_type=LETTER_TYPE,
|
|
|
|
|
hidden=True,
|
|
|
|
|
subject='Pre-compiled PDF',
|
|
|
|
|
content='',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
dao_create_template(template)
|
|
|
|
|
|
|
|
|
|
return template
|