def get_conversation_list(params):
    logger.debug('Attempting to retrieve threads list')

    headers = _create_get_conversation_headers()
    url = f"{current_app.config['SECURE_MESSAGE_URL']}/threads"

    with _get_session() as session:
        response = session.get(url, headers=headers, params=params)
        try:
            response.raise_for_status()
        except HTTPError:
            raise ApiError(logger,
                           response,
                           log_level='error',
                           message='Threads retrieval failed')

    logger.debug('Threads retrieval successful')

    try:
        return response.json()['messages']
    except JSONDecodeError:
        raise ApiError(logger,
                       response,
                       log_level='error',
                       message='The threads response could not be decoded')
    except KeyError:
        logger.error(
            "Response was successful but didn't contain a 'messages' key")
        raise NoMessagesError
Beispiel #2
0
def get_conversation(thread_id):
    logger.info('Retrieving conversation thread', thread_id=thread_id)

    headers = _create_get_conversation_headers()
    url = f"{current_app.config['SECURE_MESSAGE_URL']}/threads/{thread_id}"

    with _get_session() as session:
        response = session.get(url, headers=headers)
        try:
            response.raise_for_status()
        except HTTPError as exception:
            if exception.response.status_code == 403:
                raise IncorrectAccountAccessError(
                    message='Access not granted for thread',
                    thread_id=thread_id)
            else:
                logger.error('Thread retrieval failed', thread_id=thread_id)
                raise ApiError(response)

    logger.info('Successfully retrieved conversation thread',
                thread_id=thread_id)

    try:
        return response.json()
    except JSONDecodeError:
        logger.error('The thread response could not be decoded',
                     thread_id=thread_id)
        raise ApiError(response)
Beispiel #3
0
def get_conversation_list(params):
    logger.info('Retrieving threads list')

    headers = _create_get_conversation_headers()
    url = f"{current_app.config['SECURE_MESSAGE_URL']}/threads"

    with _get_session() as session:
        response = session.get(url, headers=headers, params=params)
        try:
            response.raise_for_status()
        except HTTPError:
            logger.error('Threads retrieval failed')
            raise ApiError(response)

    logger.info('Successfully retrieved threads list')

    try:
        return response.json()['messages']
    except JSONDecodeError:
        logger.error('The threads response could not be decoded')
        raise ApiError(response)
    except KeyError:
        logger.error(
            "Request was successful but didn't contain a 'messages' key")
        raise NoMessagesError
def get_conversation(thread_id):
    logger.debug('Attempting to retrieve thread', thread_id=thread_id)

    headers = _create_get_conversation_headers()
    url = f"{current_app.config['SECURE_MESSAGE_URL']}/v2/threads/{thread_id}"

    with _get_session() as session:
        response = session.get(url, headers=headers)
        try:
            response.raise_for_status()
        except HTTPError:
            raise ApiError(logger,
                           response,
                           log_level='error',
                           message='Thread retrieval failed',
                           thread_id=thread_id)

    logger.debug('Thread retrieval successful', thread_id=thread_id)

    try:
        return response.json()
    except JSONDecodeError:
        raise ApiError(logger,
                       response,
                       log_level='error',
                       message='The thread response could not be decoded',
                       thread_id=thread_id)
Beispiel #5
0
    def test_add_survey_post_fail(self, mock_request, get_iac_by_enrolment_code):
        mock_request.get(url_banner_api, status_code=404)
        error_response = Response()
        error_response.status_code = 500
        error_response.url = url_validate_enrolment
        error = ApiError(logger, error_response)
        error.status_code = 500

        get_iac_by_enrolment_code.side_effect = error

        response = self.app.post('/surveys/add-survey', data={'enrolment_code': enrolment_code})

        self.assertEqual(response.status_code, 500)
        self.assertTrue('An error has occurred'.encode() in response.data)
Beispiel #6
0
    def test_add_survey_submit_fail(self, decrypt_enrolment_code, get_iac_from_enrolment):
        decrypt_enrolment_code.return_value = enrolment_code.encode()

        error_response = Response()
        error_response.status_code = 500
        error_response.url = url_validate_enrolment
        error = ApiError(logger, error_response)
        error.status_code = 500
        get_iac_from_enrolment.side_effect = error

        response = self.app.get(f'/surveys/add-survey/add-survey-submit?encrypted_enrolment_code={encrypted_enrolment_code}')

        self.assertEqual(response.status_code, 500)
        self.assertTrue('An error has occurred'.encode() in response.data)
Beispiel #7
0
    def test_add_survey_post_fail(self, get_iac_by_enrolment_code):
        error_response = Response()
        error_response.status_code = 500
        error_response.url = url_validate_enrolment
        error = ApiError(logger, error_response)
        error.status_code = 500

        get_iac_by_enrolment_code.side_effect = error

        response = self.app.post('/surveys/add-survey',
                                 data={'enrolment_code': enrolment_code})

        self.assertEqual(response.status_code, 500)
        self.assertTrue('Error 500 - Server error'.encode() in response.data)
Beispiel #8
0
    def test_survey_confirm_organisation_fail(self, decrypt_enrolment_code, _, get_case):
        decrypt_enrolment_code.return_value = enrolment_code.encode()

        error_response = Response()
        error_response.status_code = 500
        error_response.url = url_get_case
        error = ApiError(logger, error_response)
        error.status_code = 500
        get_case.side_effect = error

        url = f"/surveys/add-survey/confirm-organisation-survey?encrypted_enrolment_code={encrypted_enrolment_code}"
        response = self.app.get(url)

        self.assertEqual(response.status_code, 500)
        self.assertTrue('Error 500 - Server error'.encode() in response.data)
def get_iac_from_enrolment(enrolment_code):
    """
    Gets enrolment details from IAC service for a given enrolment code

    :param enrolment_code: An enrolment code
    :type enrolment_code: str
    :raises ApiError: Raised when IAC service returns a 401 status
    :return: A dict with the IAC details if it exists and is active, and None otherwise
    """
    logger.info("Attempting to retrieve IAC", enrolment_code=enrolment_code)
    url = f"{app.config['IAC_URL']}/iacs/{enrolment_code}"
    response = requests.get(url, auth=app.config["BASIC_AUTH"])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        if response.status_code == 404:
            logger.info("IAC not found", enrolment_code=enrolment_code, status_code=response.status_code)
            return
        # 401s may include error context in the JSON response
        if response.status_code != 401:
            logger.error("Failed to retrieve IAC", enrolment_code=enrolment_code)
            raise ApiError(logger, response)

    if response.json().get("active") is False:
        logger.info("IAC is not active", enrolment_code=enrolment_code)
        return

    logger.info("Successfully retrieved IAC", enrolment_code=enrolment_code)
    return response.json()
Beispiel #10
0
def get_cases_by_party_id(party_id,
                          case_url,
                          case_auth,
                          case_events=False,
                          iac=True):
    logger.info("Attempting to retrieve cases by party id", party_id=party_id)

    url = f"{case_url}/cases/partyid/{party_id}"
    if case_events:
        url = f"{url}?caseevents=true"
    if not iac:
        if case_events:
            url = f"{url}&"
        else:
            url = f"{url}?"
        url = f"{url}iac=false"
    response = requests.get(url, auth=case_auth)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.error("Failed to retrieve cases by party id", party_id=party_id)
        raise ApiError(logger, response)

    logger.info("Successfully retrieved cases by party id", party_id=party_id)
    return response.json()
Beispiel #11
0
def notify_party_and_respondent_account_locked(respondent_id,
                                               email_address,
                                               status=None):
    logger.debug(
        'Notifying respondent and party service that account is locked')
    url = f'{app.config["PARTY_URL"]}/party-api/v1/respondents/edit-account-status/{respondent_id}'

    data = {
        'respondent_id': respondent_id,
        'email_address': email_address,
        'status_change': status
    }

    response = requests.put(url, json=data, auth=app.config['PARTY_AUTH'])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.error('Failed to notify party',
                     respondent_id=respondent_id,
                     status=status)
        raise ApiError(logger, response)

    logger.info('Successfully notified party and respondent',
                respondent_id=respondent_id,
                status=status)
Beispiel #12
0
def get_collection_exercises_for_survey(survey_id, live_only=None):
    logger.debug('Retrieving collection exercises for survey',
                 survey_id=survey_id)

    if live_only is True:
        url = f"{app.config['COLLECTION_EXERCISE_URL']}/collectionexercises/survey/{survey_id}?liveOnly=true"
    else:
        url = f"{app.config['COLLECTION_EXERCISE_URL']}/collectionexercises/survey/{survey_id}"

    response = requests.get(url, auth=app.config['COLLECTION_EXERCISE_AUTH'])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        raise ApiError(
            logger,
            response,
            survey_id=survey_id,
            log_level='warning'
            if response.status_code == 404 else 'exception',
            message='Failed to retrieve collection exercises for survey')

    logger.debug("Successfully retrieved collection exercises for survey",
                 survey_id=survey_id)
    collection_exercises = response.json()

    for collection_exercise in collection_exercises:
        if collection_exercise['events']:
            collection_exercise['events'] = convert_events_to_new_format(
                collection_exercise['events'])

    return collection_exercises
Beispiel #13
0
def get_collection_exercise(collection_exercise_id):
    logger.debug('Attempting to retrieve collection exercise',
                 collection_exercise_id=collection_exercise_id)
    url = f"{app.config['COLLECTION_EXERCISE_URL']}/collectionexercises/{collection_exercise_id}"

    response = requests.get(url, auth=app.config['COLLECTION_EXERCISE_AUTH'])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        raise ApiError(logger,
                       response,
                       collection_exercise_id=collection_exercise_id,
                       log_level='warning'
                       if response.status_code == 404 else 'exception',
                       message='Failed to retrieve collection exercise')

    logger.debug('Successfully retrieved collection exercise',
                 collection_exercise_id=collection_exercise_id)
    collection_exercise = response.json()

    if collection_exercise['events']:
        collection_exercise['events'] = convert_events_to_new_format(
            collection_exercise['events'])

    return collection_exercise
def delete_verification_token(token):
    """
    Gives call to party service to delete a verification token for the respondent
    :param token: the verification token
    """
    email = decode_email_token(token)
    logger.info("Attempting to delete respondent verification token",
                email=obfuscate_email(email))

    party_id = get_respondent_by_email(email)["id"]
    url = f"{app.config['PARTY_URL']}/party-api/v1/respondents/{party_id}/password-verification-token/{token}"
    response = requests.delete(url, auth=app.config["BASIC_AUTH"])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        if response.status_code == 404:
            logger.error("Verification token not found")
            raise NotFound("Token not found")
        logger.error("Failed to delete respondent verification token",
                     email=obfuscate_email(email))
        raise ApiError(logger, response)

    logger.info("Successfully deleted respondent verification token",
                email=obfuscate_email(email))

    return response.json()
def post_verification_token(email, token):
    """
    Gives call to party service to add a verification token for the respondent and increase the password reset counter
    :param email: the respondent's email
    :param token: the verification token
    """
    logger.info(
        "Attempting to add respondent verification token and increase password reset counter",
        email=obfuscate_email(email),
    )

    party_id = get_respondent_by_email(email)["id"]
    url = f"{app.config['PARTY_URL']}/party-api/v1/respondents/{party_id}/password-verification-token"
    payload = {
        "token": token,
    }
    response = requests.post(url, auth=app.config["BASIC_AUTH"], json=payload)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.error(
            "Failed to add respondent verification token or increase password reset counter",
            email=obfuscate_email(email),
        )
        raise ApiError(logger, response)

    logger.info(
        "Successfully added respondent verification token and password reset counter",
        email=obfuscate_email(email))

    return response.json()
Beispiel #16
0
def get_collection_exercises_for_survey(survey_id,
                                        collex_url,
                                        collex_auth,
                                        live_only=None):
    logger.info('Retrieving collection exercises for survey',
                survey_id=survey_id)

    if live_only is True:
        url = f"{collex_url}/collectionexercises/survey/{survey_id}?liveOnly=true"
    else:
        url = f"{collex_url}/collectionexercises/survey/{survey_id}"

    response = requests.get(url, auth=collex_auth)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.error('Failed to retrieve collection exercises for survey',
                     survey_id=survey_id)
        raise ApiError(logger, response)

    logger.info("Successfully retrieved collection exercises for survey",
                survey_id=survey_id)
    collection_exercises = response.json()

    for collection_exercise in collection_exercises:
        if collection_exercise['events']:
            collection_exercise['events'] = convert_events_to_new_format(
                collection_exercise['events'])

    return collection_exercises
def create_pending_survey_account(registration_data):
    """
    Gives call to party service to create a new account and register the account against the email address of share
    surveys/ transfer surveys
    :param registration_data: respondent details
    :type registration_data: dict
    :raises ApiError: Raised when party returns api error
    """
    logger.info("Attempting to create new account against share survey")

    url = f"{app.config['PARTY_URL']}/party-api/v1/pending-survey-respondent"
    registration_data["status"] = "ACTIVE"
    response = requests.post(url,
                             auth=app.config["BASIC_AUTH"],
                             json=registration_data)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        if response.status_code == 400:
            logger.info("Email has already been used")
        else:
            logger.error("Failed to create account")
        raise ApiError(logger, response)

    logger.info("Successfully created account")
def create_account(registration_data: dict) -> None:
    obfuscated_email = obfuscate_email(registration_data["emailAddress"])
    enrolment_code = registration_data["enrolmentCode"]
    logger.info("Attempting to create account",
                email=obfuscated_email,
                enrolment_code=enrolment_code)

    url = f"{app.config['PARTY_URL']}/party-api/v1/respondents"
    registration_data["status"] = "CREATED"
    response = requests.post(url,
                             auth=app.config["BASIC_AUTH"],
                             json=registration_data)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        if response.status_code == 409:
            logger.info("Email has already been used",
                        email=obfuscated_email,
                        enrolment_code=enrolment_code)
        else:
            logger.error("Failed to create account",
                         email=obfuscated_email,
                         enrolment_code=enrolment_code)
        raise ApiError(logger, response, message=response.json())

    logger.info("Successfully created account",
                email=obfuscated_email,
                enrolment_code=enrolment_code)
def notify_party_and_respondent_account_locked(respondent_id,
                                               email_address,
                                               status=None):
    bound_logger = logger.bind(respondent_id=respondent_id,
                               email=obfuscate_email(email_address),
                               status=status)
    bound_logger.info(
        "Notifying respondent and party service that account is locked")
    url = f'{app.config["PARTY_URL"]}/party-api/v1/respondents/edit-account-status/{respondent_id}'

    data = {
        "respondent_id": respondent_id,
        "email_address": email_address,
        "status_change": status
    }

    response = requests.put(url, json=data, auth=app.config["BASIC_AUTH"])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        bound_logger.error("Failed to notify party")
        bound_logger.unbind("respondent_id", "email", "status")
        raise ApiError(logger, response)

    bound_logger.info(
        "Successfully notified respondent and party service that account is locked"
    )
    bound_logger.unbind("respondent_id", "email", "status")
def get_user_count_registered_against_business_and_survey(
        business_id: str, survey_id: str, is_transfer) -> int:
    """
    returns total number of users registered against a business and survey

    :param business_id: business id
    :param survey_id: The survey id
    :param is_transfer: True if the request is for transfer survey
    :return: total number of users
    """
    logger.info("Attempting to get user count",
                business_ids=business_id,
                survey_id=survey_id)
    url = f'{app.config["PARTY_URL"]}/party-api/v1/pending-survey-users-count'
    data = {
        "business_id": business_id,
        "survey_id": survey_id,
        "is_transfer": is_transfer
    }
    response = requests.get(url, params=data, auth=app.config["BASIC_AUTH"])
    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        raise ApiError(logger, response)
    return response.json()
Beispiel #21
0
def send_message(message_json):
    """
    Creates a message in the secure-message service

    :param message_json: A block of JSON representing a message
    :type message_json: dict
    :raises ApiError: Raised when secure-message returns a non-200 status code
    :return: A json response from secure-message
    """
    party_id = json.loads(message_json).get('msg_from')
    logger.info('Sending message', party_id=party_id)

    url = f"{current_app.config['SECURE_MESSAGE_URL']}/messages"
    headers = _create_send_message_headers()

    with _get_session() as session:
        response = session.post(url, headers=headers, data=message_json)
        try:
            response.raise_for_status()
        except HTTPError:
            logger.error('Message sending failed due to API Error',
                         party_id=party_id)
            raise ApiError(response)

    logger.info('Successfully sent message', party_id=party_id)
    return response.json()
def get_party_by_business_id(party_id,
                             party_url,
                             party_auth,
                             collection_exercise_id=None,
                             verbose=True):
    logger.info("Attempting to retrieve party by business",
                party_id=party_id,
                collection_exercise_id=collection_exercise_id)

    url = f"{party_url}/party-api/v1/businesses/id/{party_id}"
    params = {}
    if collection_exercise_id:
        params["collection_exercise_id"] = collection_exercise_id
    if verbose:
        params["verbose"] = True
    response = requests.get(url, params=params, auth=party_auth)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.error("Failed to retrieve party by business",
                     party_id=party_id,
                     collection_exercise_id=collection_exercise_id)
        raise ApiError(logger, response)

    logger.info("Successfully retrieved party by business",
                party_id=party_id,
                collection_exercise_id=collection_exercise_id)
    return response.json()
def download_collection_instrument(collection_instrument_id, case_id,
                                   party_id):
    logger.debug('Attempting to download collection instrument',
                 collection_instrument_id=collection_instrument_id,
                 party_id=party_id)

    url = f"{app.config['COLLECTION_INSTRUMENT_URL']}/collection-instrument-api/1.0.2/download/{collection_instrument_id}"
    response = requests.get(url, auth=app.config['COLLECTION_INSTRUMENT_AUTH'])

    # Post relevant download case event
    category = 'COLLECTION_INSTRUMENT_DOWNLOADED' if response.ok else 'COLLECTION_INSTRUMENT_ERROR'
    case_controller.post_case_event(
        case_id,
        party_id=party_id,
        category=category,
        description=
        f'Instrument {collection_instrument_id} downloaded by {party_id} for case {case_id}'
    )

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        raise ApiError(logger,
                       response,
                       collection_instrument_id=collection_instrument_id,
                       log_level='warning'
                       if response.status_code == 404 else 'exception',
                       message='Failed to download collection instrument',
                       party_id=party_id)

    logger.debug('Successfully downloaded collection instrument',
                 collection_instrument_id=collection_instrument_id,
                 party_id=party_id)
    return response.content, response.headers.items()
    def test_add_survey_enrolment_code_already_used(self, mock_request,
                                                    get_iac_by_enrolment_code):
        mock_request.get(url_banner_api, status_code=404)
        error_response = Response()
        error_response.status_code = 400
        error_response.url = url_validate_enrolment
        error = ApiError(logger, error_response)
        error.status_code = 400

        get_iac_by_enrolment_code.side_effect = error

        response = self.app.post("/surveys/add-survey",
                                 data={"enrolment_code": enrolment_code})

        self.assertEqual(response.status_code, 200)
        self.assertTrue(
            "Enter a valid enrolment code".encode() in response.data)
Beispiel #25
0
    def test_add_survey_enrolment_code_already_used(self,
                                                    get_iac_by_enrolment_code):
        error_response = Response()
        error_response.status_code = 400
        error_response.url = url_validate_enrolment
        error = ApiError(logger, error_response)
        error.status_code = 400

        get_iac_by_enrolment_code.side_effect = error

        response = self.app.post('/surveys/add-survey',
                                 data={'enrolment_code': enrolment_code})

        self.assertEqual(response.status_code, 200)
        self.assertTrue('Enrolment code not valid'.encode() in response.data)
        self.assertTrue(
            'Please re-enter the code and try again'.encode() in response.data)
Beispiel #26
0
    def test_survey_confirm_organisation_fail(self, mock_request,
                                              decrypt_enrolment_code, _,
                                              get_case):
        mock_request.get(url_banner_api, status_code=404)
        decrypt_enrolment_code.return_value = enrolment_code.encode()

        error_response = Response()
        error_response.status_code = 500
        error_response.url = url_get_case
        error = ApiError(logger, error_response)
        error.status_code = 500
        get_case.side_effect = error

        url = f"/surveys/add-survey/confirm-organisation-survey?encrypted_enrolment_code={encrypted_enrolment_code}"
        response = self.app.get(url)

        self.assertEqual(response.status_code, 500)
        self.assertTrue("An error has occurred".encode() in response.data)
    def test_api_error(self, mock_request):
        response_mock = MagicMock()
        logger_mock = MagicMock()
        mock_request.post(url_auth_token, exc=ApiError(logger_mock, response_mock))
        mock_request.get(url_banner_api, status_code=404)

        response = self.app.post("sign-in", data=self.sign_in_form, follow_redirects=True)

        self.assertEqual(response.status_code, 500)
        self.assertTrue("An error has occurred".encode() in response.data)
def resend_password_email_expired_token(token):
    logger.info("Re-sending password email", token=token)
    url = f'{app.config["PARTY_URL"]}/party-api/v1/resend-password-email-expired-token/{token}'
    response = requests.post(url, auth=app.config["BASIC_AUTH"])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.error("Re-sending of password email for expired token failed")
        raise ApiError(logger, response)
    logger.info("Sucessfully re-sent password email", token=token)
Beispiel #29
0
def resend_verification_email_expired_token(token):
    logger.info('Re-sending verification email', token=token)
    url = f'{app.config["PARTY_URL"]}/party-api/v1/resend-verification-email-expired-token/{token}'
    response = requests.post(url, auth=app.config['BASIC_AUTH'])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.error(
            'Re-sending of verification email for expired token failed')
        raise ApiError(logger, response)
    logger.info('Successfully re-sent verification email', token=token)
def resend_verification_email(party_id):
    logger.info("Re-sending verification email", party_id=party_id)
    url = f'{app.config["PARTY_URL"]}/party-api/v1/resend-verification-email/{party_id}'
    response = requests.post(url, auth=app.config["BASIC_AUTH"])

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError:
        logger.exception("Re-sending of verification email failed",
                         party_id=party_id)
        raise ApiError(logger, response)
    logger.info("Successfully re-sent verification email", party_id=party_id)