コード例 #1
0
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 both permission to send letters and permission to send pre-compiled PDFs
    check_service_has_permission(LETTER_TYPE, authenticated_service.permissions)
    check_service_has_permission(PRECOMPILED_LETTER, 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,
        precompiled=True
    )

    resp = {
        'id': notification.id,
        'reference': notification.client_reference
    }

    return jsonify(resp), 201
コード例 #2
0
def process_document_uploads(personalisation_data, service, simulated=False):
    file_keys = [
        k for k, v in (personalisation_data or {}).items()
        if isinstance(v, dict) and 'file' in v
    ]
    if not file_keys:
        return personalisation_data

    personalisation_data = personalisation_data.copy()

    check_service_has_permission(UPLOAD_DOCUMENT,
                                 authenticated_service.permissions)

    for key in file_keys:
        if simulated:
            personalisation_data[
                key] = document_download_client.get_upload_url(
                    service.id) + '/test-document'
        else:
            try:
                personalisation_data[
                    key] = document_download_client.upload_document(
                        service.id, personalisation_data[key]['file'])
            except DocumentDownloadError as e:
                raise BadRequestError(message=e.message,
                                      status_code=e.status_code)

    return personalisation_data
コード例 #3
0
def process_document_uploads(personalisation_data, service, simulated, template_id):
    file_keys = [k for k, v in (personalisation_data or {}).items() if isinstance(v, dict) and "file" in v]
    if not file_keys:
        return personalisation_data

    personalisation_data = personalisation_data.copy()

    check_service_has_permission(UPLOAD_DOCUMENT, authenticated_service.permissions)

    for key in file_keys:
        if simulated:
            personalisation_data[key] = document_download_client.get_upload_url(service.id) + "/test-document"
        else:
            try:
                personalisation_data[key] = document_download_client.upload_document(service.id, personalisation_data[key])
            except DocumentDownloadError as e:
                raise BadRequestError(message=e.message, status_code=e.status_code)

    if not simulated:
        save_stats_for_attachments(
            [v for k, v in personalisation_data.items() if k in file_keys],
            service.id,
            template_id,
        )

    return personalisation_data
コード例 #4
0
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 permission to send letters
    check_service_has_permission(LETTER_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,
        precompiled=True,
    )

    resp = {
        "id": notification.id,
        "reference": notification.client_reference,
        "postage": notification.postage,
    }

    return jsonify(resp), 201
コード例 #5
0
def post_precompiled_letter_notification():
    request_json = get_valid_json()
    if 'content' not in (request_json or {}):
        return post_notification(LETTER_TYPE)

    form = validate(request_json, post_precompiled_letter_request)

    # Check permission to send letters
    check_service_has_permission(LETTER_TYPE,
                                 authenticated_service.permissions)

    check_rate_limiting(authenticated_service, api_user)

    template = get_precompiled_letter_template(authenticated_service.id)

    # For precompiled letters the to field will be set to Provided as PDF until the validation passes,
    # then the address of the letter will be set as the to field
    form['personalisation'] = {'address_line_1': 'Provided as PDF'}

    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,
                                               precompiled=True)

    resp = {
        'id': notification.id,
        'reference': notification.client_reference,
        'postage': notification.postage
    }

    return jsonify(resp), 201
コード例 #6
0
def post_precompiled_letter_notification():
    request_json = get_valid_json()
    if 'content' not in (request_json or {}):
        return post_notification(LETTER_TYPE)

    form = validate(request_json, post_precompiled_letter_request)

    # Check permission to send letters
    check_service_has_permission(LETTER_TYPE,
                                 authenticated_service.permissions)

    check_rate_limiting(authenticated_service, api_user)

    template = get_precompiled_letter_template(authenticated_service.id)

    # For precompiled letters the to field will be set to Provided as PDF until the validation passes,
    # then the address of the letter will be set as the to field
    form['personalisation'] = {'address_line_1': 'Provided as PDF'}

    notification = process_letter_notification(
        letter_data=form,
        api_key=api_user,
        service=authenticated_service,
        template=template,
        template_with_content=None,  # not required for precompiled
        reply_to_text='',  # not required for precompiled
        precompiled=True)

    return jsonify(notification), 201
コード例 #7
0
def create_broadcast():

    check_service_has_permission(
        BROADCAST_TYPE,
        authenticated_service.permissions,
    )

    if request.content_type != 'application/cap+xml':
        raise BadRequestError(
            message=f'Content type {request.content_type} not supported',
            status_code=415,
        )

    cap_xml = request.get_data()

    if not validate_xml(cap_xml, 'CAP-v1.2.xsd'):
        raise BadRequestError(
            message='Request data is not valid CAP XML',
            status_code=400,
        )

    broadcast_json = cap_xml_to_dict(cap_xml)

    validate(broadcast_json, post_broadcast_schema)

    polygons = Polygons(
        list(
            chain.from_iterable(
                (area['polygons'] for area in broadcast_json['areas']))))

    broadcast_message = BroadcastMessage(
        service_id=authenticated_service.id,
        content=broadcast_json['content'],
        reference=broadcast_json['reference'],
        areas={
            'areas': [area['name'] for area in broadcast_json['areas']],
            'simple_polygons':
            polygons.smooth.simplify.as_coordinate_pairs_long_lat,
        },
        status=BroadcastStatusType.PENDING_APPROVAL,
        api_key_id=api_user.id,
        stubbed=authenticated_service.restricted
        # The client may pass in broadcast_json['expires'] but it’s
        # simpler for now to ignore it and have the rules around expiry
        # for broadcasts created with the API match those created from
        # the admin app
    )

    dao_save_object(broadcast_message)

    current_app.logger.info(
        f'Broadcast message {broadcast_message.id} created for service '
        f'{authenticated_service.id} with reference {broadcast_json["reference"]}'
    )

    return jsonify(broadcast_message.serialize()), 201
コード例 #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, service.permissions)
    check_service_has_permission(UPLOAD_LETTERS, service.permissions)
    check_service_over_daily_message_limit(KEY_TYPE_NORMAL, service)
    validate_created_by(service, post_data["created_by"])

    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
    billable_units = get_page_count(letter.read())

    personalisation = {"address_line_1": post_data["filename"]}

    # TODO: stop hard-coding postage as 'second' once we get postage from the admin
    notification = persist_notification(
        notification_id=post_data["file_id"],
        template_id=template.id,
        template_version=template.version,
        template_postage=template.postage,
        recipient=post_data["filename"],
        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="second",
    )

    upload_filename = get_letter_pdf_filename(
        notification.reference,
        notification.service.crown,
        is_scan_letter=False,
        postage=notification.postage,
    )

    move_uploaded_pdf_to_letters_bucket(file_location, upload_filename)

    return {"id": str(notification.id)}
コード例 #9
0
def post_notification(notification_type):
    with POST_NOTIFICATION_JSON_PARSE_DURATION_SECONDS.time():
        request_json = get_valid_json()

        if notification_type == EMAIL_TYPE:
            form = validate(request_json, post_email_request)
        elif notification_type == SMS_TYPE:
            form = validate(request_json, post_sms_request)
        elif notification_type == LETTER_TYPE:
            form = validate(request_json, post_letter_request)
        else:
            abort(404)

    check_service_has_permission(notification_type,
                                 authenticated_service.permissions)

    check_rate_limiting(authenticated_service, api_user)

    template, template_with_content = validate_template(form['template_id'],
                                                        form.get(
                                                            'personalisation',
                                                            {}),
                                                        authenticated_service,
                                                        notification_type,
                                                        check_char_count=False)

    reply_to = get_reply_to_text(notification_type, form, template)

    if notification_type == LETTER_TYPE:
        notification = process_letter_notification(
            letter_data=form,
            api_key=api_user,
            service=authenticated_service,
            template=template,
            template_with_content=template_with_content,
            reply_to_text=reply_to)
    else:
        notification = process_sms_or_email_notification(
            form=form,
            notification_type=notification_type,
            template=template,
            template_with_content=template_with_content,
            template_process_type=template.process_type,
            service=authenticated_service,
            reply_to_text=reply_to)

    return jsonify(notification), 201
コード例 #10
0
def post_bulk():
    try:
        request_json = request.get_json()
    except werkzeug.exceptions.BadRequest as e:
        raise BadRequestError(message=f"Error decoding arguments: {e.description}", status_code=400)

    max_rows = current_app.config["CSV_MAX_ROWS"]
    form = validate(request_json, post_bulk_request(max_rows))

    if len([source for source in [form.get("rows"), form.get("csv")] if source]) != 1:
        raise BadRequestError(message="You should specify either rows or csv", status_code=400)
    template = validate_template_exists(form["template_id"], authenticated_service)
    check_service_has_permission(template.template_type, authenticated_service.permissions)

    remaining_messages = authenticated_service.message_limit - fetch_todays_total_message_count(authenticated_service.id)

    form["validated_sender_id"] = validate_sender_id(template, form.get("reply_to_id"))

    try:
        if form.get("rows"):
            output = StringIO()
            writer = csv.writer(output)
            writer.writerows(form["rows"])
            file_data = output.getvalue()
        else:
            file_data = form["csv"]

        recipient_csv = RecipientCSV(
            file_data,
            template_type=template.template_type,
            placeholders=template._as_utils_template().placeholders,
            max_rows=max_rows,
            safelist=safelisted_members(authenticated_service, api_user.key_type),
            remaining_messages=remaining_messages,
        )
    except csv.Error as e:
        raise BadRequestError(message=f"Error converting to CSV: {str(e)}", status_code=400)

    check_for_csv_errors(recipient_csv, max_rows, remaining_messages)
    job = create_bulk_job(authenticated_service, api_user, template, form, recipient_csv)

    return jsonify(data=job_schema.dump(job).data), 201
コード例 #11
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 = 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)}
コード例 #12
0
def post_notification(notification_type):
    try:
        request_json = request.get_json()
    except werkzeug.exceptions.BadRequest as e:
        raise BadRequestError(message="Error decoding arguments: {}".format(e.description),
                              status_code=400)

    if notification_type == EMAIL_TYPE:
        form = validate(request_json, post_email_request)
    elif notification_type == SMS_TYPE:
        form = validate(request_json, post_sms_request)
    elif notification_type == LETTER_TYPE:
        form = validate(request_json, post_letter_request)
    else:
        abort(404)

    check_service_has_permission(notification_type, authenticated_service.permissions)

    scheduled_for = form.get("scheduled_for", None)

    check_service_can_schedule_notification(authenticated_service.permissions, scheduled_for)

    check_rate_limiting(authenticated_service, api_user)

    template, template_with_content = validate_template(
        form['template_id'],
        form.get('personalisation', {}),
        authenticated_service,
        notification_type,
    )

    reply_to = get_reply_to_text(notification_type, form, template)

    if notification_type == LETTER_TYPE:
        notification = process_letter_notification(
            letter_data=form,
            api_key=api_user,
            template=template,
            reply_to_text=reply_to
        )
    else:
        notification = process_sms_or_email_notification(
            form=form,
            notification_type=notification_type,
            api_key=api_user,
            template=template,
            service=authenticated_service,
            reply_to_text=reply_to
        )

        template_with_content.values = notification.personalisation

    if notification_type == SMS_TYPE:
        create_resp_partial = functools.partial(
            create_post_sms_response_from_notification,
            from_number=reply_to
        )
    elif notification_type == EMAIL_TYPE:
        create_resp_partial = functools.partial(
            create_post_email_response_from_notification,
            subject=template_with_content.subject,
            email_from='{}@{}'.format(authenticated_service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN'])
        )
    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
コード例 #13
0
def post_notification(notification_type):
    request_json = get_valid_json()

    if notification_type == EMAIL_TYPE:
        form = validate(request_json, post_email_request)
    elif notification_type == SMS_TYPE:
        form = validate(request_json, post_sms_request)
    elif notification_type == LETTER_TYPE:
        form = validate(request_json, post_letter_request)
    else:
        abort(404)

    check_service_has_permission(notification_type,
                                 authenticated_service.permissions)

    scheduled_for = form.get("scheduled_for", None)

    check_service_can_schedule_notification(authenticated_service.permissions,
                                            scheduled_for)

    check_rate_limiting(authenticated_service, api_user)

    template, template_with_content = validate_template(
        form['template_id'],
        form.get('personalisation', {}),
        authenticated_service,
        notification_type,
    )

    reply_to = get_reply_to_text(notification_type, form, template)

    if notification_type == LETTER_TYPE:
        notification = process_letter_notification(letter_data=form,
                                                   api_key=api_user,
                                                   template=template,
                                                   reply_to_text=reply_to)
    else:
        notification = process_sms_or_email_notification(
            form=form,
            notification_type=notification_type,
            api_key=api_user,
            template=template,
            service=authenticated_service,
            reply_to_text=reply_to)

        template_with_content.values = notification.personalisation

    if notification_type == SMS_TYPE:
        create_resp_partial = functools.partial(
            create_post_sms_response_from_notification,
            from_number=reply_to,
        )
    elif notification_type == EMAIL_TYPE:
        create_resp_partial = functools.partial(
            create_post_email_response_from_notification,
            subject=template_with_content.subject,
            email_from='{}@{}'.format(
                authenticated_service.email_from,
                current_app.config['NOTIFY_EMAIL_DOMAIN']),
        )
    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,
        url_root=request.url_root,
        scheduled_for=scheduled_for,
        content=template_with_content.content_with_placeholders_filled_in,
    )
    return jsonify(resp), 201
def post_notification(notification_type):
    try:
        request_json = request.get_json()
    except werkzeug.exceptions.BadRequest as e:
        raise BadRequestError(message="Error decoding arguments: {}".format(
            e.description),
                              status_code=400)

    if notification_type == EMAIL_TYPE:
        form = validate(request_json, post_email_request)
    elif notification_type == SMS_TYPE:
        form = validate(request_json, post_sms_request)
    elif notification_type == LETTER_TYPE:
        form = validate(request_json, post_letter_request)
    else:
        abort(404)

    check_service_has_permission(notification_type,
                                 authenticated_service.permissions)

    scheduled_for = form.get("scheduled_for", None)

    check_service_can_schedule_notification(authenticated_service.permissions,
                                            scheduled_for)

    check_rate_limiting(authenticated_service, api_user)

    template, template_with_content = validate_template(
        form['template_id'],
        form.get('personalisation', {}),
        authenticated_service,
        notification_type,
    )

    reply_to = get_reply_to_text(notification_type, form, template)

    if notification_type == LETTER_TYPE:
        notification = process_letter_notification(letter_data=form,
                                                   api_key=api_user,
                                                   template=template,
                                                   reply_to_text=reply_to)
    else:
        if 'email_address' in form or 'phone_number' in form:
            notification = process_sms_or_email_notification(
                form=form,
                notification_type=notification_type,
                api_key=api_user,
                template=template,
                service=authenticated_service,
                reply_to_text=reply_to)
        else:
            if accept_recipient_identifiers_enabled():
                notification = process_notification_with_recipient_identifier(
                    form=form,
                    notification_type=notification_type,
                    api_key=api_user,
                    template=template,
                    service=authenticated_service,
                    reply_to_text=reply_to)
            else:
                current_app.logger.debug(
                    "Sending a notification without contact information is not implemented."
                )
                return jsonify(result='error', message="Not Implemented"), 501

        template_with_content.values = notification.personalisation

    if notification_type == SMS_TYPE:
        create_resp_partial = functools.partial(
            create_post_sms_response_from_notification, from_number=reply_to)
    elif notification_type == EMAIL_TYPE:
        create_resp_partial = functools.partial(
            create_post_email_response_from_notification,
            subject=template_with_content.subject)
    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