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
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)
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)
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)
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)
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)
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()
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()
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)
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
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()
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()
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)
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)
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)
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)