def send_one_off_notification(service_id, post_data): service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id_and_service_id( template_id=post_data['template_id'], service_id=service_id ) personalisation = post_data.get('personalisation', None) validate_template(template.id, personalisation, service, template.template_type) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) validate_and_format_recipient( send_to=post_data['to'], key_type=KEY_TYPE_NORMAL, service=service, notification_type=template.template_type, allow_whitelisted_recipients=False, ) validate_created_by(service, post_data['created_by']) sender_id = post_data.get('sender_id', None) reply_to = get_reply_to_text( notification_type=template.template_type, sender_id=sender_id, service=service, template=template ) notification = persist_notification( template_id=template.id, template_version=template.version, template_postage=template.postage, recipient=post_data['to'], service=service, personalisation=personalisation, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=post_data['created_by'], reply_to_text=reply_to, reference=create_one_off_reference(template.template_type), ) queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None if template.template_type == LETTER_TYPE and service.research_mode: _update_notification_status( notification, NOTIFICATION_DELIVERED, ) else: send_notification_to_queue( notification=notification, research_mode=service.research_mode, queue=queue_name, ) return {'id': str(notification.id)}
def test_rejects_api_calls_with_international_numbers_if_service_does_not_allow_int_sms( key_type, notify_db_session, ): service = create_service(service_permissions=[SMS_TYPE]) with pytest.raises(BadRequestError) as e: validate_and_format_recipient('+20-12-1234-1234', key_type, service, SMS_TYPE) assert e.value.status_code == 400 assert e.value.message == 'Cannot send to international mobile numbers' assert e.value.fields == []
def send_one_off_notification(service_id, post_data): service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id_and_service_id(template_id=post_data["template_id"], service_id=service_id) personalisation = post_data.get("personalisation", None) validate_template(template.id, personalisation, service, template.template_type) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) validate_and_format_recipient( send_to=post_data["to"], key_type=KEY_TYPE_NORMAL, service=service, notification_type=template.template_type, allow_safelisted_recipients=False, ) validate_created_by(service, post_data["created_by"]) sender_id = post_data.get("sender_id", None) reply_to = get_reply_to_text( notification_type=template.template_type, sender_id=sender_id, service=service, template=template, ) notification = persist_notification( template_id=template.id, template_version=template.version, template_postage=template.postage, recipient=post_data["to"], service=service, personalisation=personalisation, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=post_data["created_by"], reply_to_text=reply_to, reference=create_one_off_reference(template.template_type), ) if template.template_type == LETTER_TYPE and service.research_mode: _update_notification_status( notification, NOTIFICATION_DELIVERED, ) else: send_notification_to_queue( notification=notification, research_mode=service.research_mode, queue=template.queue_to_use(), ) return {"id": str(notification.id)}
def test_allows_api_calls_with_international_numbers_if_service_does_allow_int_sms( key_type, notify_db, notify_db_session): service = create_service(notify_db, notify_db_session, permissions=[SMS_TYPE, INTERNATIONAL_SMS_TYPE]) result = validate_and_format_recipient("+20-12-1234-1234", key_type, service, SMS_TYPE) assert result == "+201212341234"
def process_sms_or_email_notification(*, form, notification_type, api_key, template, service, reply_to_text=None): 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) # Do not persist or send notification to the queue if it is a simulated recipient simulated = simulated_recipient(send_to, notification_type) personalisation = process_document_uploads(form.get('personalisation'), service, simulated=simulated) additional_email_parameters = {"importance": form.get('importance', None), "cc_address": form.get('cc_address', None)} \ if notification_type == EMAIL_TYPE else {} notification = persist_notification( template_id=template.id, template_version=template.version, recipient=form_send_to, service=service, personalisation=personalisation, notification_type=notification_type, api_key_id=api_key.id, key_type=api_key.key_type, client_reference=form.get('reference', None), simulated=simulated, reply_to_text=reply_to_text, additional_email_parameters=additional_email_parameters) scheduled_for = form.get("scheduled_for", None) if scheduled_for: persist_scheduled_notification(notification.id, form["scheduled_for"]) else: if not simulated: queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None send_notification_to_queue(notification=notification, research_mode=service.research_mode, queue=queue_name) else: current_app.logger.debug( "POST simulated notification for id: {}".format( notification.id)) return notification
def process_sms_or_email_notification(*, form, notification_type, api_key, template, service, reply_to_text=None): 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) # Do not persist or send notification to the queue if it is a simulated recipient simulated = simulated_recipient(send_to, notification_type) 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), simulated=simulated, reply_to_text=reply_to_text, status_callback_url=form.get('status_callback_url', None), status_callback_bearer_token=form.get('status_callback_bearer_token', None), ) scheduled_for = form.get("scheduled_for", None) if scheduled_for: persist_scheduled_notification(notification.id, form["scheduled_for"]) else: if not simulated: queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None send_notification_to_queue(notification=notification, research_mode=service.research_mode, queue=queue_name) else: current_app.logger.debug( "POST simulated notification for id: {}".format( notification.id)) return notification
def send_pdf_letter_notification(service_id, post_data): service = dao_fetch_service_by_id(service_id) check_service_has_permission(LETTER_TYPE, [p.permission for p in service.permissions]) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) validate_created_by(service, post_data['created_by']) validate_and_format_recipient( send_to=post_data['recipient_address'], key_type=KEY_TYPE_NORMAL, service=service, notification_type=LETTER_TYPE, allow_guest_list_recipients=False, ) template = get_precompiled_letter_template(service.id) file_location = 'service-{}/{}.pdf'.format(service.id, post_data['file_id']) try: letter = utils_s3download( current_app.config['TRANSIENT_UPLOADED_LETTERS'], file_location) except S3ObjectNotFound as e: current_app.logger.exception( 'Letter {}.pdf not in transient {} bucket'.format( post_data['file_id'], current_app.config['TRANSIENT_UPLOADED_LETTERS'])) raise e # Getting the page count won't raise an error since admin has already checked the PDF is valid page_count = get_page_count(letter.read()) billable_units = get_billable_units_for_letter_page_count(page_count) personalisation = {'address_line_1': post_data['filename']} notification = persist_notification( notification_id=post_data['file_id'], template_id=template.id, template_version=template.version, recipient=urllib.parse.unquote(post_data['recipient_address']), service=service, personalisation=personalisation, notification_type=LETTER_TYPE, api_key_id=None, key_type=KEY_TYPE_NORMAL, reference=create_one_off_reference(LETTER_TYPE), client_reference=post_data['filename'], created_by_id=post_data['created_by'], billable_units=billable_units, postage=post_data['postage'] or template.postage, ) upload_filename = get_letter_pdf_filename( reference=notification.reference, crown=notification.service.crown, created_at=notification.created_at, ignore_folder=False, postage=notification.postage) move_uploaded_pdf_to_letters_bucket(file_location, upload_filename) return {'id': str(notification.id)}
def test_rejects_api_calls_with_no_recipient(): with pytest.raises(BadRequestError) as e: validate_and_format_recipient(None, 'key_type', 'service', 'SMS_TYPE') assert e.value.status_code == 400 assert e.value.message == "Recipient can't be empty"
def process_sms_or_email_notification(*, form, notification_type, api_key, template, service, reply_to_text=None): 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, ) # Do not persist or send notification to the queue if it is a simulated recipient simulated = simulated_recipient(send_to, notification_type) personalisation = process_document_uploads(form.get("personalisation"), service, simulated, template.id) notification = { "id": create_uuid(), "template": str(template.id), "template_version": str(template.version), "to": form_send_to, "personalisation": personalisation, "simulated": simulated, "api_key": str(api_key.id), "key_type": str(api_key.key_type), "client_reference": form.get("reference", None), } encrypted_notification_data = encryption.encrypt(notification) scheduled_for = form.get("scheduled_for", None) if scheduled_for: notification = persist_notification( template_id=template.id, template_version=template.version, recipient=form_send_to, service=service, personalisation=personalisation, notification_type=notification_type, api_key_id=api_key.id, key_type=api_key.key_type, client_reference=form.get("reference", None), simulated=simulated, reply_to_text=reply_to_text, ) persist_scheduled_notification(notification.id, form["scheduled_for"]) elif current_app.config["FF_NOTIFICATION_CELERY_PERSISTENCE"] and not simulated: # depending on the type route to the appropriate save task if notification_type == EMAIL_TYPE: current_app.logger.info("calling save email task") save_email.apply_async( (authenticated_service.id, create_uuid(), encrypted_notification_data), queue=QueueNames.DATABASE if not authenticated_service.research_mode else QueueNames.RESEARCH_MODE, ) elif notification_type == SMS_TYPE: save_sms.apply_async( (authenticated_service.id, create_uuid(), encrypted_notification_data), queue=QueueNames.DATABASE if not authenticated_service.research_mode else QueueNames.RESEARCH_MODE, ) else: notification = persist_notification( template_id=template.id, template_version=template.version, recipient=form_send_to, service=service, personalisation=personalisation, notification_type=notification_type, api_key_id=api_key.id, key_type=api_key.key_type, client_reference=form.get("reference", None), simulated=simulated, reply_to_text=reply_to_text, ) if not simulated: send_notification_to_queue( notification=notification, research_mode=service.research_mode, queue=template.queue_to_use(), ) else: current_app.logger.debug("POST simulated notification for id: {}".format(notification.id)) if not isinstance(notification, Notification): notification["template_id"] = notification["template"] notification["api_key_id"] = notification["api_key"] notification["template_version"] = template.version notification["service"] = service notification["service_id"] = service.id notification["reply_to_text"] = reply_to_text del notification["template"] del notification["api_key"] del notification["simulated"] notification = Notification(**notification) return notification
def send_one_off_notification(service_id, post_data): service = dao_fetch_service_by_id(service_id) template = dao_get_template_by_id_and_service_id( template_id=post_data['template_id'], service_id=service_id ) personalisation = post_data.get('personalisation', None) validate_template(template.id, personalisation, service, template.template_type) check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service) validate_and_format_recipient( send_to=post_data['to'], key_type=KEY_TYPE_NORMAL, service=service, notification_type=template.template_type, allow_guest_list_recipients=False, ) postage = None client_reference = None if template.template_type == LETTER_TYPE: # Validate address and set postage to europe|rest-of-world if international letter, # otherwise persist_notification with use template postage postage = validate_address(service, personalisation) if not postage: postage = template.postage from app.utils import get_reference_from_personalisation client_reference = get_reference_from_personalisation(personalisation) validate_created_by(service, post_data['created_by']) sender_id = post_data.get('sender_id', None) reply_to = get_reply_to_text( notification_type=template.template_type, sender_id=sender_id, service=service, template=template ) notification = persist_notification( template_id=template.id, template_version=template.version, recipient=post_data['to'], service=service, personalisation=personalisation, notification_type=template.template_type, api_key_id=None, key_type=KEY_TYPE_NORMAL, created_by_id=post_data['created_by'], reply_to_text=reply_to, reference=create_one_off_reference(template.template_type), postage=postage, client_reference=client_reference ) queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None if template.template_type == LETTER_TYPE and service.research_mode: _update_notification_status( notification, NOTIFICATION_DELIVERED, ) else: send_notification_to_queue( notification=notification, research_mode=service.research_mode, queue=queue_name, ) return {'id': str(notification.id)}
def process_sms_or_email_notification(*, form, notification_type, api_key, template, service, reply_to_text=None): notification_id = None 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) # Do not persist or send notification to the queue if it is a simulated recipient simulated = simulated_recipient(send_to, notification_type) personalisation, document_download_count = process_document_uploads( form.get('personalisation'), service, simulated=simulated) if str(service.id) in current_app.config.get('HIGH_VOLUME_SERVICE') and api_key.key_type == KEY_TYPE_NORMAL \ and notification_type == EMAIL_TYPE: # Put GOV.UK Email notifications onto a queue # To take the pressure off the db for API requests put the notification for our high volume service onto a queue # the task will then save the notification, then call send_notification_to_queue. # We know that this team does not use the GET request, but relies on callbacks to get the status updates. try: notification_id = uuid.uuid4() notification = save_email_to_queue( form=form, notification_id=str(notification_id), notification_type=notification_type, api_key=api_key, template=template, service_id=service.id, personalisation=personalisation, document_download_count=document_download_count, reply_to_text=reply_to_text) return notification except SQSError: # if SQS cannot put the task on the queue, it's probably because the notification body was too long and it # went over SQS's 256kb message limit. If so, we current_app.logger.info( f'Notification {notification_id} failed to save to high volume queue. Using normal flow instead' ) notification = persist_notification( notification_id=notification_id, template_id=template.id, template_version=template.version, recipient=form_send_to, service=service, personalisation=personalisation, notification_type=notification_type, api_key_id=api_key.id, key_type=api_key.key_type, client_reference=form.get('reference', None), simulated=simulated, reply_to_text=reply_to_text, document_download_count=document_download_count) scheduled_for = form.get("scheduled_for", None) if scheduled_for: persist_scheduled_notification(notification.id, form["scheduled_for"]) else: if not simulated: queue_name = QueueNames.PRIORITY if template.process_type == PRIORITY else None send_notification_to_queue(notification=notification, research_mode=service.research_mode, queue=queue_name) else: current_app.logger.debug( "POST simulated notification for id: {}".format( notification.id)) return notification
def process_sms_or_email_notification( *, form, notification_type, template, template_with_content, template_process_type, service, reply_to_text=None, ): notification_id = uuid.uuid4() 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_user.key_type, service=service, notification_type=notification_type) # Do not persist or send notification to the queue if it is a simulated recipient simulated = simulated_recipient(send_to, notification_type) personalisation, document_download_count = process_document_uploads( form.get('personalisation'), service, simulated=simulated) if document_download_count: # We changed personalisation which means we need to update the content template_with_content.values = personalisation # validate content length after url is replaced in personalisation. check_is_message_too_long(template_with_content) resp = create_response_for_post_notification( notification_id=notification_id, client_reference=form.get('reference', None), template_id=template.id, template_version=template.version, service_id=service.id, notification_type=notification_type, reply_to=reply_to_text, template_with_content=template_with_content) if service.id in current_app.config.get('HIGH_VOLUME_SERVICE') \ and api_user.key_type == KEY_TYPE_NORMAL \ and notification_type in [EMAIL_TYPE, SMS_TYPE]: # Put service with high volumes of notifications onto a queue # To take the pressure off the db for API requests put the notification for our high volume service onto a queue # the task will then save the notification, then call send_notification_to_queue. # NOTE: The high volume service should be aware that the notification is not immediately # available by a GET request, it is recommend they use callbacks to keep track of status updates. try: save_email_or_sms_to_queue( form=form, notification_id=str(notification_id), notification_type=notification_type, api_key=api_user, template=template, service_id=service.id, personalisation=personalisation, document_download_count=document_download_count, reply_to_text=reply_to_text) return resp except SQSError: # if SQS cannot put the task on the queue, it's probably because the notification body was too long and it # went over SQS's 256kb message limit. If so, we current_app.logger.info( f'Notification {notification_id} failed to save to high volume queue. Using normal flow instead' ) persist_notification(notification_id=notification_id, template_id=template.id, template_version=template.version, recipient=form_send_to, service=service, personalisation=personalisation, notification_type=notification_type, api_key_id=api_user.id, key_type=api_user.key_type, client_reference=form.get('reference', None), simulated=simulated, reply_to_text=reply_to_text, document_download_count=document_download_count) if not simulated: queue_name = QueueNames.PRIORITY if template_process_type == PRIORITY else None send_notification_to_queue_detached( key_type=api_user.key_type, notification_type=notification_type, notification_id=notification_id, research_mode=service.research_mode, # research_mode is deprecated queue=queue_name) else: current_app.logger.debug( "POST simulated notification for id: {}".format(notification_id)) return resp
def test_allows_api_calls_with_international_numbers_if_service_does_allow_int_sms( key_type, sample_service_full_permissions): service_model = SerialisedService.from_id(sample_service_full_permissions.id) result = validate_and_format_recipient('20-12-1234-1234', key_type, service_model, SMS_TYPE) assert result == '201212341234'
def test_allows_api_calls_with_international_numbers_if_service_does_allow_int_sms( key_type, sample_service_full_permissions): result = validate_and_format_recipient('+20-12-1234-1234', key_type, sample_service_full_permissions, SMS_TYPE) assert result == '+201212341234'