def test_create_service_callback_api_schema_validate_fails_with_misspelled_keys( key, wrong_key, value): under_test = { "url": "https://some_url.for_service", "bearer_token": "something_ten_chars", "notification_statuses": ["failed"], "updated_by_id": str(uuid.uuid4()) } del under_test[key] under_test[wrong_key] = value with pytest.raises(ValidationError) as e: validate(under_test, create_service_callback_api_request_schema) errors = json.loads(str(e.value)).get('errors') assert len(errors) == 1 assert errors[0]['error'] == 'ValidationError' assert errors[0]['message'] == f"{key} is a required property"
def update_service_letter_contact(service_id, letter_contact_id): # validate the service exists, throws ResultNotFound exception. dao_fetch_service_by_id(service_id) form = validate(request.get_json(), add_service_letter_contact_block_request) new_reply_to = update_letter_contact(service_id=service_id, letter_contact_id=letter_contact_id, contact_block=form['contact_block'], is_default=form.get('is_default', True)) return jsonify(data=new_reply_to.serialize()), 200
def send_user_2fa_code(user_id, code_type): user_to_send_to = get_user_by_id(user_id=user_id) if count_user_verify_codes(user_to_send_to) >= current_app.config.get( 'MAX_VERIFY_CODE_COUNT'): set_verify_codes_to_used(user_id) data = request.get_json() if code_type == SMS_TYPE: validate(data, post_send_user_sms_code_schema) send_user_sms_code(user_to_send_to, data) elif code_type == EMAIL_TYPE: validate(data, post_send_user_email_code_schema) send_user_email_code(user_to_send_to, data) else: abort(404) return '{}', 204
def test_post_email_schema_invalid_scheduled_for(invalid_datetime, schema): j = {"template_id": str(uuid.uuid4()), "scheduled_for": invalid_datetime} if schema == post_email_request_schema: j.update({"email_address": "*****@*****.**"}) else: j.update({"phone_number": "07515111111"}) with pytest.raises(ValidationError) as e: validate(j, schema) error = json.loads(str(e.value)) assert error['status_code'] == 400 assert error['errors'] == [{ 'error': 'ValidationError', 'message': "scheduled_for datetime format is invalid. " "It must be a valid ISO8601 date time format, " "https://en.wikipedia.org/wiki/ISO_8601" }]
def test_get_notifications_request_invalid_statuses(invalid_statuses, valid_statuses): partial_error_status = ( "is not one of " "[cancelled, created, sending, sent, delivered, pending, failed, " "technical-failure, temporary-failure, permanent-failure, pending-virus-check, " "validation-failed, virus-scan-failed, returned-letter, pii-check-failed, accepted, received]" ) with pytest.raises(ValidationError) as e: validate({"status": invalid_statuses + valid_statuses}, get_notifications_request) errors = json.loads(str(e.value)).get("errors") assert len(errors) == len(invalid_statuses) for index, value in enumerate(invalid_statuses): assert errors[index]["message"] == "status {} {}".format( value, partial_error_status)
def test_service_inbound_api_schema_validates(): under_test = { "url": "https://some_url.for_service", "bearer_token": "something_ten_chars", "updated_by_id": str(uuid.uuid4()) } validated = validate(under_test, update_service_inbound_api_schema) assert validated == under_test
def add_service_reply_to_email_address(service_id): # validate the service exists, throws ResultNotFound exception. dao_fetch_service_by_id(service_id) form = validate(request.get_json(), add_service_email_reply_to_request) new_reply_to = add_reply_to_email_address_for_service( service_id=service_id, email_address=form['email_address'], is_default=form.get('is_default', True)) return jsonify(data=new_reply_to.serialize()), 201
def move_to_template_folder(service_id, target_template_folder_id=None): data = request.get_json() validate(data, post_move_template_folder_schema) if target_template_folder_id: target_template_folder = dao_get_template_folder_by_id_and_service_id( target_template_folder_id, service_id) else: target_template_folder = None for template_folder_id in data["folders"]: try: template_folder = dao_get_template_folder_by_id_and_service_id( template_folder_id, service_id) except NoResultFound: msg = "No folder found with id {} for service {}".format( template_folder_id, service_id) raise InvalidRequest(msg, status_code=400) _validate_folder_move( target_template_folder, target_template_folder_id, template_folder, template_folder_id, ) template_folder.parent = target_template_folder for template_id in data["templates"]: try: template = dao_get_template_by_id_and_service_id( template_id, service_id) except NoResultFound: msg = "Could not move to folder: No template found with id {} for service {}".format( template_id, service_id) raise InvalidRequest(msg, status_code=400) if template.archived: current_app.logger.info( "Could not move to folder: Template {} is archived. (Skipping)" .format(template_id)) else: template.folder = target_template_folder return "", 204
def update_broadcast_message_status(service_id, broadcast_message_id): data = request.get_json() validate(data, update_broadcast_message_status_schema) broadcast_message = dao_get_broadcast_message_by_id_and_service_id( broadcast_message_id, service_id) new_status = data['status'] updating_user = get_user_by_id(data['created_by']) _update_broadcast_message(broadcast_message, new_status, updating_user) dao_save_object(broadcast_message) if new_status in { BroadcastStatusType.BROADCASTING, BroadcastStatusType.CANCELLED }: _create_broadcast_event(broadcast_message) return jsonify(broadcast_message.serialize()), 200
def test_post_email_json_schema_missing_recipient_identifier_required_fields( recipient_identifier, missing_key_name): j = { "recipient_identifier": recipient_identifier, "template_id": str(uuid.uuid4()) } with pytest.raises(ValidationError) as e: validate(j, post_email_request_schema) error = json.loads(str(e.value)) assert len(error.keys()) == 2 assert error.get('status_code') == 400 assert len(error.get('errors')) == len(missing_key_name) for key_name in missing_key_name: assert { 'error': 'ValidationError', 'message': "recipient_identifier " + key_name + " is a required property" } in error['errors']
def get_template_by_id(template_id, version=None): _data = {'id': template_id} if version: _data['version'] = version data = validate(_data, get_template_by_id_request) template = templates_dao.dao_get_template_by_id_and_service_id( template_id, authenticated_service.id, data.get('version')) return jsonify(template.serialize()), 200
def test_post_sms_schema_with_personalisation_that_is_not_a_dict(): j = { "phone_number": "6502532222", "template_id": str(uuid.uuid4()), "reference": "reference from caller", "personalisation": "not_a_dict" } with pytest.raises(ValidationError) as e: validate(j, post_sms_request_schema) error = json.loads(str(e.value)) assert len(error.get('errors')) == 1 assert error['errors'] == [{ 'error': 'ValidationError', 'message': "personalisation not_a_dict is not of type object" }] assert error.get('status_code') == 400 assert len(error.keys()) == 2
def create_contact_list(service_id): service_contact_list = validate(request.get_json(), create_service_contact_list_schema) service_contact_list['created_by_id'] = service_contact_list.pop('created_by') service_contact_list['created_at'] = datetime.utcnow() service_contact_list['service_id'] = str(service_id) list_to_save = ServiceContactList(**service_contact_list) save_service_contact_list(list_to_save) return jsonify(list_to_save.serialize()), 201
def test_post_email_schema_id_type_should_only_use_enum_values(id_type): id_type_as_parameter_json = { "recipient_identifier": { "id_type": id_type, "id_value": "bar" }, "template_id": str(uuid.uuid4()) } if id_type in RECIPIENT_IDENTIFIER_TYPES: assert validate(id_type_as_parameter_json, post_email_request_schema) == id_type_as_parameter_json else: with pytest.raises(ValidationError) as e: validate(id_type_as_parameter_json, post_email_request_schema) error = json.loads(str(e.value)) assert len(error.keys()) == 2 assert error.get('status_code') == 400 assert len(error.get('errors')) == 1 assert {'error': 'ValidationError', 'message': "recipient_identifier INVALID is not one of [VAPROFILEID, PID, ICN]"} in error['errors']
def import_speakers(): data = request.get_json(force=True) validate(data, post_import_speakers_schema) errors = [] speakers = [] for item in data: err = '' if item.get('parent_name'): parent_speaker = Speaker.query.filter(Speaker.name == item['parent_name']).first() if parent_speaker: if parent_speaker.parent_id: err = 'Parent speaker can`t have a parent' current_app.logger.error(err) errors.append(err) else: item['parent_id'] = str(parent_speaker.id) else: err = 'Can`t find speaker: {}'.format(item['parent_name']) current_app.logger.error(err) errors.append(err) del item['parent_name'] if not err: speaker = Speaker.query.filter(Speaker.name == item['name']).first() if not speaker: speaker = Speaker(**item) speakers.append(speaker) dao_create_speaker(speaker) else: err = u'speaker already exists: {}'.format(speaker.name) current_app.logger.error(err) errors.append(err) res = { "speakers": [s.serialize() for s in speakers] } if errors: res['errors'] = errors return jsonify(res), 201 if speakers else 400 if errors else 200
def send_message(): data = request.get_json(force=True) current_app.logger.info('send_message: %r', data) validate(data, post_send_message_schema) emails_to = [user.email for user in dao_get_admin_users()] status_code = send_smtp_email(emails_to, 'Web message: {}'.format(data['reason']), data['message'], from_email=data['email'], from_name=data['name']) return jsonify({ 'message': 'Your message was sent' if status_code == 200 else 'An error occurred sending your message' })
def test_post_sms_json_schema_bad_uuid_and_missing_phone_number_and_recipient_identifier( ): j = {"template_id": "notUUID"} with pytest.raises(ValidationError) as e: validate(j, post_sms_request_schema) error = json.loads(str(e.value)) assert len(error.keys()) == 2 assert error.get('status_code') == 400 assert len(error.get('errors')) == 2 assert { 'error': 'ValidationError', 'message': "{template_id: notUUID} is not valid under any of the given schemas" } in error['errors'] assert { 'error': 'ValidationError', 'message': "template_id is not a valid UUID" } in error['errors']
def create_or_update_free_sms_fragment_limit(service_id): req_args = request.get_json() form = validate(req_args, create_or_update_free_sms_fragment_limit_schema) update_free_sms_fragment_limit_data(service_id, free_sms_fragment_limit=form.get('free_sms_fragment_limit'), financial_year_start=form.get('financial_year_start')) return jsonify(form), 201
def create_magazine(): data = request.get_json(force=True) validate(data, post_create_magazine_schema) new_filename = get_magazine_filename(data['filename']) if new_filename: magazine = Magazine(title=data['title'], filename=new_filename) dao_create_record(magazine) upload_tasks.upload_magazine.apply_async( (str(magazine.id), data['pdf_data'])) return jsonify(magazine.serialize()), 201 raise InvalidRequest( 'Invalid filename for magazine: {}'.format(data['filename']), 400)
def add_user_to_service(service_id, user_id): service = dao_fetch_service_by_id(service_id) user = get_user_by_id(user_id=user_id) if user in service.users: error = 'User id: {} already part of service id: {}'.format(user_id, service_id) raise InvalidRequest(error, status_code=400) data = request.get_json() validate(data, post_set_permissions_schema) permissions = [ Permission(service_id=service_id, user_id=user_id, permission=p['permission']) for p in data['permissions'] ] folder_permissions = data.get('folder_permissions', []) dao_add_user_to_service(service, user, permissions, folder_permissions) data = service_schema.dump(service).data return jsonify(data=data), 201
def add_service_letter_contact(service_id): # validate the service exists, throws ResultNotFound exception. dao_fetch_service_by_id(service_id) form = validate(request.get_json(), add_service_letter_contact_block_request) new_letter_contact = add_letter_contact_for_service( service_id=service_id, contact_block=form["contact_block"], is_default=form.get("is_default", True), ) return jsonify(data=new_letter_contact.serialize()), 201
def update_service_reply_to_email_address(service_id, reply_to_email_id): # validate the service exists, throws ResultNotFound exception. dao_fetch_service_by_id(service_id) form = validate(request.get_json(), add_service_email_reply_to_request) new_reply_to = update_reply_to_email_address( service_id=service_id, reply_to_id=reply_to_email_id, email_address=form["email_address"], is_default=form.get("is_default", True), ) return jsonify(data=new_reply_to.serialize()), 200
def add_service_reply_to_email_address(service_id): # validate the service exists, throws ResultNotFound exception. dao_fetch_service_by_id(service_id) form = validate(request.get_json(), add_service_email_reply_to_request) check_if_reply_to_address_already_in_use(service_id, form["email_address"]) new_reply_to = add_reply_to_email_address_for_service( service_id=service_id, email_address=form["email_address"], is_default=form.get("is_default", True), ) return jsonify(data=new_reply_to.serialize()), 201
def test_post_notification_with_document_upload(client, notify_db_session, mocker, csv_param): service = create_service(service_permissions=[EMAIL_TYPE]) service.contact_link = '*****@*****.**' template = create_template( service=service, template_type='email', content="Document 1: ((first_link)). Document 2: ((second_link))") mocker.patch('app.celery.provider_tasks.deliver_email.apply_async') document_download_mock = mocker.patch( 'app.v2.notifications.post_notifications.document_download_client') document_download_mock.upload_document.side_effect = lambda service_id, content, is_csv: f'{content}-link' data = { "email_address": service.users[0].email_address, "template_id": template.id, "personalisation": { "first_link": { "file": "abababab", **csv_param }, "second_link": { "file": "cdcdcdcd", **csv_param } } } auth_header = create_authorization_header(service_id=service.id) response = client.post(path="v2/notifications/email", data=json.dumps(data), headers=[('Content-Type', 'application/json'), auth_header]) assert response.status_code == 201, response.get_data(as_text=True) resp_json = json.loads(response.get_data(as_text=True)) assert validate(resp_json, post_email_response) == resp_json assert document_download_mock.upload_document.call_args_list == [ call(str(service.id), 'abababab', csv_param.get('is_csv')), call(str(service.id), 'cdcdcdcd', csv_param.get('is_csv')) ] notification = Notification.query.one() assert notification.status == NOTIFICATION_CREATED assert notification.personalisation == { 'first_link': 'abababab-link', 'second_link': 'cdcdcdcd-link' } assert notification.document_download_count == 2 assert resp_json['content'][ 'body'] == 'Document 1: abababab-link. Document 2: cdcdcdcd-link'
def get_inbound_sms(): data = validate(request.args.to_dict(), get_inbound_sms_request) paginated_inbound_sms = inbound_sms_dao.dao_get_paginated_inbound_sms_for_service_for_public_api( authenticated_service.id, older_than=data.get('older_than', None), page_size=current_app.config.get('API_PAGE_SIZE')) return jsonify( received_text_messages=[i.serialize() for i in paginated_inbound_sms], links=_build_links(paginated_inbound_sms)), 200
def test_create_service_callback_api_schema_validate_succeeds(): under_test = { "url": "https://some_url.for_service", "bearer_token": "something_ten_chars", "notification_statuses": ["failed"], "updated_by_id": str(uuid.uuid4()) } validated = validate(under_test, create_service_callback_api_request_schema) assert validated == under_test
def invite_user_to_org(organisation_id): data = request.get_json() validate(data, post_create_invited_org_user_status_schema) invited_org_user = InvitedOrganisationUser( email_address=data["email_address"], invited_by_id=data["invited_by"], organisation_id=organisation_id, ) save_invited_org_user(invited_org_user) template = dao_get_template_by_id( current_app.config["ORGANISATION_INVITATION_EMAIL_TEMPLATE_ID"]) saved_notification = persist_notification( template_id=template.id, template_version=template.version, recipient=invited_org_user.email_address, service=template.service, personalisation={ "user_name": invited_org_user.invited_by.name, "organisation_name": invited_org_user.organisation.name, "url": invited_org_user_url( invited_org_user.id, data.get("invite_link_host"), ), }, notification_type=EMAIL_TYPE, api_key_id=None, key_type=KEY_TYPE_NORMAL, reply_to_text=invited_org_user.invited_by.email_address, ) send_notification_to_queue(saved_notification, research_mode=False, queue=QueueNames.NOTIFY) return jsonify(data=invited_org_user.serialize()), 201
def set_permissions(user_id, service_id): # TODO fix security hole, how do we verify that the user # who is making this request has permission to make the request. service_user = dao_get_service_user(user_id, service_id) user = service_user.user service = dao_fetch_service_by_id(service_id=service_id) data = request.get_json() validate(data, post_set_permissions_schema) permission_list = [ Permission(service_id=service_id, user_id=user_id, permission=p['permission']) for p in data['permissions'] ] service_key = "service_id_{}".format(service_id) change_dict = {service_key: service_id, "permissions": permission_list} try: _update_alert(user, update_dct_to_str(change_dict)) except Exception as e: current_app.logger.error(e) permission_dao.set_user_service_permission(user, service, permission_list, _commit=True, replace=True) if 'folder_permissions' in data: folders = [ dao_get_template_folder_by_id_and_service_id( folder_id, service_id) for folder_id in data['folder_permissions'] ] service_user.folders = folders dao_update_service_user(service_user) return jsonify({}), 204
def test_post_email_schema_id_type_should_only_use_enum_values(id_type): id_type_as_parameter_json = { "recipient_identifier": { "id_type": id_type, "id_value": "bar" }, "template_id": str(uuid.uuid4()) } if id_type in IdentifierType.values(): assert validate(id_type_as_parameter_json, post_email_request_schema) == id_type_as_parameter_json else: with pytest.raises(ValidationError) as e: validate(id_type_as_parameter_json, post_email_request_schema) error = json.loads(str(e.value)) assert len(error.keys()) == 2 assert error.get('status_code') == 400 assert len(error.get('errors')) == 1 assert 'ValidationError' in error['errors'][0]['error'] assert f"recipient_identifier {id_type} is not one of " in error[ 'errors'][0]['message']
def send_user_2fa_code(user_id, code_type): user_to_send_to = get_user_by_id(user_id=user_id) if(verify_within_time(user_to_send_to, age=timedelta(seconds=10)) >= 1): raise InvalidRequest("Code already sent, wait 10 seconds", status_code=400) if count_user_verify_codes(user_to_send_to) >= current_app.config.get('MAX_VERIFY_CODE_COUNT'): # Prevent more than `MAX_VERIFY_CODE_COUNT` active verify codes at a time current_app.logger.warning('Too many verify codes created for user {}'.format(user_to_send_to.id)) else: data = request.get_json() if code_type == SMS_TYPE: validate(data, post_send_user_sms_code_schema) send_user_sms_code(user_to_send_to, data) elif code_type == EMAIL_TYPE: validate(data, post_send_user_email_code_schema) send_user_email_code(user_to_send_to, data) else: abort(404) return '{}', 204