def test_generate_letter_pdf_filename_returns_tomorrows_filename(
        notify_api, mocker):
    created_at = datetime(2017, 12, 4, 17, 31)
    filename = generate_letter_pdf_filename(reference='foo',
                                            created_at=created_at)

    assert filename == '2017-12-05/NOTIFY.FOO.D.2.C.20171204173100.PDF'
def test_generate_letter_pdf_filename_returns_correct_filename_for_test_letters(
        notify_api, mocker):
    created_at = datetime(2017, 12, 4, 17, 29)
    filename = generate_letter_pdf_filename(reference='foo',
                                            created_at=created_at,
                                            ignore_folder=True)

    assert filename == 'NOTIFY.FOO.D.2.C.20171204172900.PDF'
Beispiel #3
0
def test_generate_letter_pdf_filename_returns_correct_filename(
        notify_api, mocker, crown_flag, expected_crown_text):
    created_at = datetime(2017, 12, 4, 17, 29)
    filename = generate_letter_pdf_filename(reference='foo',
                                            crown=crown_flag,
                                            created_at=created_at)

    assert filename == '2017-12-04/NOTIFY.FOO.D.2.C.{}.20171204172900.PDF'.format(
        expected_crown_text)
def test_generate_letter_pdf_filename_returns_correct_postage_for_filename(
        notify_api, postage, expected_postage):
    created_at = datetime(2017, 12, 4, 17, 29)
    filename = generate_letter_pdf_filename(reference='foo',
                                            created_at=created_at,
                                            postage=postage)

    assert filename == '2017-12-04/NOTIFY.FOO.D.{}.C.20171204172900.PDF'.format(
        expected_postage)
Beispiel #5
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,
            crown=notification.service.crown,
            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)
def test_upload_letter_pdf_uses_postage_from_notification(
        sample_letter_template, mocker, postage, expected_postage):
    letter_notification = create_notification(template=sample_letter_template,
                                              postage=postage)
    mock_s3 = mocker.patch('app.letters.utils.s3upload')

    filename = generate_letter_pdf_filename(
        reference=letter_notification.reference,
        created_at=letter_notification.created_at,
        ignore_folder=False,
        postage=letter_notification.postage)

    upload_letter_pdf(letter_notification, b'\x00\x01', precompiled=False)

    mock_s3.assert_called_once_with(
        bucket_name=current_app.config['LETTERS_PDF_BUCKET_NAME'],
        file_location=filename,
        filedata=b'\x00\x01',
        region=current_app.config['AWS_REGION'])
def test_upload_letter_pdf_to_correct_bucket(sample_letter_notification,
                                             mocker, is_precompiled_letter,
                                             bucket_config_name):
    if is_precompiled_letter:
        sample_letter_notification.template.hidden = True
        sample_letter_notification.template.name = PRECOMPILED_TEMPLATE_NAME

    mock_s3 = mocker.patch('app.letters.utils.s3upload')

    filename = generate_letter_pdf_filename(
        reference=sample_letter_notification.reference,
        created_at=sample_letter_notification.created_at,
        ignore_folder=is_precompiled_letter)

    upload_letter_pdf(sample_letter_notification,
                      b'\x00\x01',
                      precompiled=is_precompiled_letter)

    mock_s3.assert_called_once_with(
        bucket_name=current_app.config[bucket_config_name],
        file_location=filename,
        filedata=b'\x00\x01',
        region=current_app.config['AWS_REGION'])
Beispiel #8
0
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 = generate_letter_pdf_filename(
        reference=notification.reference,
        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)}
Beispiel #9
0
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 = generate_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)