Beispiel #1
0
def get_pdf_for_templated_letter(self, notification_id):
    try:
        notification = get_notification_by_id(notification_id, _raise=True)
        letter_filename = generate_letter_pdf_filename(
            reference=notification.reference,
            created_at=notification.created_at,
            ignore_folder=notification.key_type == KEY_TYPE_TEST,
            postage=notification.postage)
        letter_data = {
            'letter_contact_block':
            notification.reply_to_text,
            'template': {
                "subject": notification.template.subject,
                "content": notification.template.content,
                "template_type": notification.template.template_type
            },
            'values':
            notification.personalisation,
            'logo_filename':
            notification.service.letter_branding
            and notification.service.letter_branding.filename,
            'letter_filename':
            letter_filename,
            "notification_id":
            str(notification_id),
            'key_type':
            notification.key_type
        }

        encrypted_data = encryption.encrypt(letter_data)

        notify_celery.send_task(name=TaskNames.CREATE_PDF_FOR_TEMPLATED_LETTER,
                                args=(encrypted_data, ),
                                queue=QueueNames.SANITISE_LETTERS)
    except Exception:
        try:
            current_app.logger.exception(
                f"RETRY: calling create-letter-pdf task for notification {notification_id} failed"
            )
            self.retry(queue=QueueNames.RETRY)
        except self.MaxRetriesExceededError:
            message = f"RETRY FAILED: Max retries reached. " \
                      f"The task create-letter-pdf failed for notification id {notification_id}. " \
                      f"Notification has been updated to technical-failure"
            update_notification_status_by_id(notification_id,
                                             NOTIFICATION_TECHNICAL_FAILURE)
            raise NotificationTechnicalFailureException(message)
Beispiel #2
0
def deliver_sms(self, notification_id):
    try:
        current_app.logger.info("Start sending SMS for notification id: {}".format(notification_id))
        notification = notifications_dao.get_notification_by_id(notification_id)
        if not notification:
            raise NoResultFound()
        send_to_providers.send_sms_to_provider(notification)
    except Exception as e:
        try:
            current_app.logger.exception(
                "SMS notification delivery for id: {} failed".format(notification_id)
            )
            self.retry(queue=QueueNames.RETRY)
        except self.MaxRetriesExceededError:
            message = "RETRY FAILED: Max retries reached. The task send_sms_to_provider failed for notification {}. " \
                      "Notification has been updated to technical-failure".format(notification_id)
            update_notification_status_by_id(notification_id, NOTIFICATION_TECHNICAL_FAILURE)
            raise NotificationTechnicalFailureException(message)
def timeout_notifications():
    technical_failure_notifications, temporary_failure_notifications = \
        dao_timeout_notifications(current_app.config.get('SENDING_NOTIFICATIONS_TIMEOUT_PERIOD'))

    notifications = technical_failure_notifications + temporary_failure_notifications
    for notification in notifications:
        # queue callback task only if the service_callback_api exists
        service_callback_api = get_service_delivery_status_callback_api_for_service(service_id=notification.service_id)
        if service_callback_api:
            encrypted_notification = create_delivery_status_callback_data(notification, service_callback_api)
            send_delivery_status_to_service.apply_async([str(notification.id), encrypted_notification],
                                                        queue=QueueNames.CALLBACKS)

    current_app.logger.info(
        "Timeout period reached for {} notifications, status has been updated.".format(len(notifications)))
    if technical_failure_notifications:
        message = "{} notifications have been updated to technical-failure because they " \
                  "have timed out and are still in created.Notification ids: {}".format(
                      len(technical_failure_notifications), [str(x.id) for x in technical_failure_notifications])
        raise NotificationTechnicalFailureException(message)
def sanitise_letter(self, filename):
    try:
        reference = get_reference_from_filename(filename)
        notification = dao_get_notification_by_reference(reference)

        current_app.logger.info(
            'Notification ID {} Virus scan passed: {}'.format(
                notification.id, filename))

        if notification.status != NOTIFICATION_PENDING_VIRUS_CHECK:
            current_app.logger.info(
                'Sanitise letter called for notification {} which is in {} state'
                .format(notification.id, notification.status))
            return

        notify_celery.send_task(
            name=TaskNames.SANITISE_LETTER,
            kwargs={
                'notification_id':
                str(notification.id),
                'filename':
                filename,
                'allow_international_letters':
                notification.service.has_permission(INTERNATIONAL_LETTERS),
            },
            queue=QueueNames.SANITISE_LETTERS,
        )
    except Exception:
        try:
            current_app.logger.exception(
                "RETRY: calling sanitise_letter task for notification {} failed"
                .format(notification.id))
            self.retry(queue=QueueNames.RETRY)
        except self.MaxRetriesExceededError:
            message = "RETRY FAILED: Max retries reached. " \
                      "The task sanitise_letter failed for notification {}. " \
                      "Notification has been updated to technical-failure".format(notification.id)
            update_notification_status_by_id(notification.id,
                                             NOTIFICATION_TECHNICAL_FAILURE)
            raise NotificationTechnicalFailureException(message)
def process_sanitised_letter(self, sanitise_data):
    letter_details = encryption.decrypt(sanitise_data)

    filename = letter_details['filename']
    notification_id = letter_details['notification_id']

    current_app.logger.info('Processing sanitised letter with id {}'.format(notification_id))
    notification = get_notification_by_id(notification_id, _raise=True)

    if notification.status != NOTIFICATION_PENDING_VIRUS_CHECK:
        current_app.logger.info(
            'process-sanitised-letter task called for notification {} which is in {} state'.format(
                notification.id, notification.status)
        )
        return

    try:
        original_pdf_object = s3.get_s3_object(current_app.config['LETTERS_SCAN_BUCKET_NAME'], filename)

        if letter_details['validation_status'] == 'failed':
            current_app.logger.info('Processing invalid precompiled pdf with id {} (file {})'.format(
                notification_id, filename))

            _move_invalid_letter_and_update_status(
                notification=notification,
                filename=filename,
                scan_pdf_object=original_pdf_object,
                message=letter_details['message'],
                invalid_pages=letter_details['invalid_pages'],
                page_count=letter_details['page_count'],
            )
            return

        current_app.logger.info('Processing valid precompiled pdf with id {} (file {})'.format(
            notification_id, filename))

        billable_units = get_billable_units_for_letter_page_count(letter_details['page_count'])
        is_test_key = notification.key_type == KEY_TYPE_TEST

        # Updating the notification needs to happen before the file is moved. This is so that if updating the
        # notification fails, the task can retry because the file is in the same place.
        update_letter_pdf_status(
            reference=notification.reference,
            status=NOTIFICATION_DELIVERED if is_test_key else NOTIFICATION_CREATED,
            billable_units=billable_units,
            recipient_address=letter_details['address']
        )

        # The original filename could be wrong because we didn't know the postage.
        # Now we know if the letter is international, we can check what the filename should be.
        upload_file_name = get_letter_pdf_filename(
            reference=notification.reference,
            crown=notification.service.crown,
            created_at=notification.created_at,
            ignore_folder=True,
            postage=notification.postage
        )

        move_sanitised_letter_to_test_or_live_pdf_bucket(
            filename,
            is_test_key,
            notification.created_at,
            upload_file_name,
        )
        # We've moved the sanitised PDF from the sanitise bucket, but still need to delete the original file:
        original_pdf_object.delete()

    except BotoClientError:
        # Boto exceptions are likely to be caused by the file(s) being in the wrong place, so retrying won't help -
        # we'll need to manually investigate
        current_app.logger.exception(
            f"Boto error when processing sanitised letter for notification {notification.id} (file {filename})"
        )
        update_notification_status_by_id(notification.id, NOTIFICATION_TECHNICAL_FAILURE)
        raise NotificationTechnicalFailureException
    except Exception:
        try:
            current_app.logger.exception(
                "RETRY: calling process_sanitised_letter task for notification {} failed".format(notification.id)
            )
            self.retry(queue=QueueNames.RETRY)
        except self.MaxRetriesExceededError:
            message = "RETRY FAILED: Max retries reached. " \
                      "The task process_sanitised_letter failed for notification {}. " \
                      "Notification has been updated to technical-failure".format(notification.id)
            update_notification_status_by_id(notification.id, NOTIFICATION_TECHNICAL_FAILURE)
            raise NotificationTechnicalFailureException(message)
def fail_pii(notification, pii_type):
    notification.status = NOTIFICATION_CONTAINS_PII
    dao_update_notification(notification)
    raise NotificationTechnicalFailureException(
        "Send {} for notification id {} to provider is not allowed. Notification contains PII: {}"
        .format(notification.notification_type, notification.id, pii_type))
def malware_failure(notification):
    notification.status = NOTIFICATION_VIRUS_SCAN_FAILED
    dao_update_notification(notification)
    raise NotificationTechnicalFailureException(
        "Send {} for notification id {} to provider is not allowed. Notification contains malware"
        .format(notification.notification_type, notification.id))
Beispiel #8
0
def lookup_contact_info(self, notification_id):
    current_app.logger.info(
        f"Looking up contact information for notification_id:{notification_id}."
    )

    notification = get_notification_by_id(notification_id)

    va_profile_id = notification.recipient_identifiers[
        IdentifierType.VA_PROFILE_ID.value].id_value

    try:
        if EMAIL_TYPE == notification.notification_type:
            recipient = va_profile_client.get_email(va_profile_id)
        elif SMS_TYPE == notification.notification_type:
            recipient = va_profile_client.get_telephone(va_profile_id)
        else:
            raise NotImplementedError(
                f"The task lookup_contact_info failed for notification {notification_id}. "
                f"{notification.notification_type} is not supported")

    except VAProfileRetryableException as e:
        current_app.logger.exception(e)
        try:
            self.retry(queue=QueueNames.RETRY)
        except self.MaxRetriesExceededError:
            message = (
                'RETRY FAILED: Max retries reached. '
                f'The task lookup_contact_info failed for notification {notification_id}. '
                'Notification has been updated to technical-failure')

            update_notification_status_by_id(notification_id,
                                             NOTIFICATION_TECHNICAL_FAILURE,
                                             status_reason=e.failure_reason)
            raise NotificationTechnicalFailureException(message) from e

    except NoContactInfoException as e:
        message = (
            f'Can\'t proceed after querying VA Profile for contact information for {notification_id}. '
            'Stopping execution of following tasks. Notification has been updated to permanent-failure.'
        )
        current_app.logger.warning(f'{e.__class__.__name__} - {str(e)}: ' +
                                   message)
        self.request.chain = None

        update_notification_status_by_id(notification_id,
                                         NOTIFICATION_PERMANENT_FAILURE,
                                         status_reason=e.failure_reason)

    except VAProfileNonRetryableException as e:
        current_app.logger.exception(e)
        message = (
            f'The task lookup_contact_info failed for notification {notification_id}. '
            'Notification has been updated to technical-failure')
        update_notification_status_by_id(notification_id,
                                         NOTIFICATION_TECHNICAL_FAILURE,
                                         status_reason=e.failure_reason)
        raise NotificationTechnicalFailureException(message) from e

    else:
        notification.to = recipient
        dao_update_notification(notification)