diff --git a/lambdas/services/create_document_reference_service.py b/lambdas/services/create_document_reference_service.py index 28d45813f..68e9137fe 100644 --- a/lambdas/services/create_document_reference_service.py +++ b/lambdas/services/create_document_reference_service.py @@ -10,13 +10,13 @@ from models.fhir.R4.fhir_document_reference import Attachment, DocumentReferenceInfo from pydantic import ValidationError from services.base.ssm_service import SSMService +from services.feature_flags_service import FeatureFlagService from services.post_fhir_document_reference_service import ( PostFhirDocumentReferenceService, ) from utils import upload_file_configs from utils.audit_logging_setup import LoggingService from utils.common_query_filters import get_document_type_filter -from utils.constants.ssm import UPLOAD_PILOT_ODS_ALLOWED_LIST from utils.dynamo_query_filter_builder import DynamoQueryFilterBuilder from utils.exceptions import ( ConfigNotFoundException, @@ -49,13 +49,16 @@ class CreateDocumentReferenceService: def __init__(self): self.post_fhir_doc_ref_service = PostFhirDocumentReferenceService() self.ssm_service = SSMService() + self.feature_flag_service = FeatureFlagService() self.lg_dynamo_table = os.getenv("LLOYD_GEORGE_DYNAMODB_NAME") self.staging_bucket_name = os.getenv("STAGING_STORE_BUCKET_NAME") self.upload_sub_folder = "user_upload" def create_document_reference_request( - self, nhs_number: str, documents_list: list[dict] + self, + nhs_number: str, + documents_list: list[dict], ): upload_document_names = [] url_responses = {} @@ -73,18 +76,19 @@ def create_document_reference_request( for validated_doc in upload_request_documents: snomed_code = validated_doc.doc_type - config = upload_file_configs.get_config_by_snomed_code( - snomed_code - ) + config = upload_file_configs.get_config_by_snomed_code(snomed_code) if config.single_file_only: self.check_existing_records_and_remove_failed_upload( nhs_number, - snomed_code + snomed_code, ) document_reference = self.create_document_reference( - nhs_number, user_ods_code, validated_doc, snomed_code + nhs_number, + user_ods_code, + validated_doc, + snomed_code, ) self.validate_document_file_type(validated_doc, config) @@ -126,13 +130,21 @@ def create_document_reference_request( raise DocumentRefException(400, LambdaError.DocRefInvalidFiles) def validate_document_file_type(self, validated_doc, document_config): - if not is_file_type_allowed(validated_doc.file_name, document_config.accepted_file_types): + if not is_file_type_allowed( + validated_doc.file_name, + document_config.accepted_file_types, + ): raise LGInvalidFilesException( - f"Unsupported file type for file: {validated_doc.file_name}" + f"Unsupported file type for file: {validated_doc.file_name}", ) def build_and_process_fhir_doc_ref( - self, nhs_number, user_ods_code, validated_doc, snomed_code, document_reference + self, + nhs_number, + user_ods_code, + validated_doc, + snomed_code, + document_reference, ): doc_ref_info = self.build_doc_ref_info( validated_doc, @@ -142,11 +154,11 @@ def build_and_process_fhir_doc_ref( ) fhir_doc_ref = doc_ref_info.create_fhir_document_reference_object( - document_reference + document_reference, ) fhir_response = self.post_fhir_doc_ref_service.process_fhir_document_reference( - fhir_doc_ref.model_dump_json() + fhir_doc_ref.model_dump_json(), ) return fhir_response @@ -159,7 +171,11 @@ def validate_patient_user_ods_codes_match(self, user_ods_code, patient_ods_code) raise DocumentRefException(401, LambdaError.DocRefUnauthorizedOdsCode) def build_doc_ref_info( - self, validated_doc, nhs_number, snomed_code, user_ods_code + self, + validated_doc, + nhs_number, + snomed_code, + user_ods_code, ) -> DocumentReferenceInfo: attachment_details = Attachment( title=validated_doc.file_name, @@ -176,14 +192,17 @@ def build_doc_ref_info( return doc_ref_info def check_if_user_ods_code_is_in_pilot(self, ods_code) -> bool: - pilot_ods_codes = self.get_allowed_list_of_ods_codes_for_upload_pilot() - if ods_code in pilot_ods_codes: + pilot_ods_codes = ( + self.feature_flag_service.get_allowed_list_of_ods_codes_for_upload_pilot() + ) + if ods_code in pilot_ods_codes or pilot_ods_codes == []: return True else: raise OdsErrorException() def parse_documents_list( - self, document_list: list[dict] + self, + document_list: list[dict], ) -> list[UploadRequestDocument]: upload_request_document_list = [] for document in document_list: @@ -250,7 +269,7 @@ def check_existing_records_and_remove_failed_upload( ) if not previous_records: logger.info( - "No record was found for this patient. Will continue to create doc ref." + "No record was found for this patient. Will continue to create doc ref.", ) return @@ -261,7 +280,7 @@ def check_existing_records_and_remove_failed_upload( def stop_if_upload_is_in_process(self, previous_records: list[DocumentReference]): if any( self.post_fhir_doc_ref_service.document_service.is_upload_in_process( - document + document, ) for document in previous_records ): @@ -276,7 +295,7 @@ def stop_if_all_records_uploaded(self, previous_records: list[DocumentReference] if all_records_uploaded: logger.info( "The patient already has a full set of record. " - "We should not be processing the new Lloyd George record upload." + "We should not be processing the new Lloyd George record upload.", ) logger.error( f"{LambdaError.DocRefRecordAlreadyInPlace.to_str()}", @@ -291,30 +310,23 @@ def remove_records_of_failed_upload( ): logger.info( "Found previous records of failed upload. " - "Will delete those records before creating new document references." + "Will delete those records before creating new document references.", ) logger.info("Deleting files from s3...") for record in failed_upload_records: s3_bucket_name, s3_file_key = record._parse_s3_location( - record.file_location + record.file_location, ) self.post_fhir_doc_ref_service.s3_service.delete_object( - s3_bucket_name, s3_file_key + s3_bucket_name, + s3_file_key, ) logger.info("Deleting dynamodb record...") self.post_fhir_doc_ref_service.document_service.hard_delete_metadata_records( - table_name=table_name, document_references=failed_upload_records + table_name=table_name, + document_references=failed_upload_records, ) logger.info("Previous failed records are deleted.") - - def get_allowed_list_of_ods_codes_for_upload_pilot(self) -> list[str]: - logger.info( - "Starting ssm request to retrieve allowed list of ODS codes for Upload Pilot" - ) - response = self.ssm_service.get_ssm_parameter(UPLOAD_PILOT_ODS_ALLOWED_LIST) - if not response: - logger.warning("No ODS codes found in allowed list for Upload Pilot") - return response diff --git a/lambdas/services/feature_flags_service.py b/lambdas/services/feature_flags_service.py index dba5ef671..f592108d0 100644 --- a/lambdas/services/feature_flags_service.py +++ b/lambdas/services/feature_flags_service.py @@ -130,33 +130,36 @@ def get_feature_flags_by_flag(self, flag: str): def get_allowed_list_of_ods_codes_for_upload_pilot(self) -> list[str]: logger.info( - "Starting ssm request to retrieve allowed list of ODS codes for Upload Pilot" + "Starting ssm request to retrieve allowed list of ODS codes for Upload Pilot", ) - response = self.ssm_service.get_ssm_parameter(UPLOAD_PILOT_ODS_ALLOWED_LIST) - if not response: + response = self.ssm_service.get_ssm_parameter( + UPLOAD_PILOT_ODS_ALLOWED_LIST, + ).split(",") + if not response or response == ["*"]: logger.warning("No ODS codes found in allowed list for Upload Pilot") return [] - return response.split(",") + return response def check_if_ods_code_is_in_pilot(self) -> bool: ods_code = "" if isinstance(request_context.authorization, dict): ods_code = request_context.authorization.get( - "selected_organisation", {} + "selected_organisation", + {}, ).get("org_ods_code", "") if not ods_code: return False pilot_ods_codes = self.get_allowed_list_of_ods_codes_for_upload_pilot() - return ods_code in pilot_ods_codes + return ods_code in pilot_ods_codes or pilot_ods_codes == [] def validate_feature_flag(self, flag_name: str): flag_object = self.get_feature_flags_by_flag(flag_name) if not flag_object.get(flag_name, False): logger.info( - f"Feature flag '{flag_name}' not enabled, event will not be processed" + f"Feature flag '{flag_name}' not enabled, event will not be processed", ) raise FeatureFlagsException(404, LambdaError.FeatureFlagDisabled) diff --git a/lambdas/services/update_document_reference_service.py b/lambdas/services/update_document_reference_service.py index ed0be0c95..db30f3659 100644 --- a/lambdas/services/update_document_reference_service.py +++ b/lambdas/services/update_document_reference_service.py @@ -8,10 +8,10 @@ from pydantic import ValidationError from services.base.ssm_service import SSMService from services.document_service import DocumentService +from services.feature_flags_service import FeatureFlagService from services.put_fhir_document_reference_service import PutFhirDocumentReferenceService from utils.audit_logging_setup import LoggingService from utils.common_query_filters import CurrentStatusFile, NotDeleted -from utils.constants.ssm import UPLOAD_PILOT_ODS_ALLOWED_LIST from utils.dynamo_utils import DocTypeTableRouter from utils.exceptions import ( InvalidNhsNumberException, @@ -38,10 +38,14 @@ def __init__(self): self.fhir_doc_ref_service = PutFhirDocumentReferenceService() self.document_service = DocumentService() self.ssm_service = SSMService() + self.feature_flag_service = FeatureFlagService() self.doctype_table_router = DocTypeTableRouter() def update_document_reference_request( - self, nhs_number: str, document: dict, doc_ref_id: str + self, + nhs_number: str, + document: dict, + doc_ref_id: str, ): self.validate_doc_ref_exists(doc_ref_id) @@ -64,12 +68,16 @@ def update_document_reference_request( self.validate_user_patient_ods_match(patient_ods_code, user_ods_code) validate_files_for_access_and_store( - [update_request_document], pds_patient_details + [update_request_document], + pds_patient_details, ) self.stop_if_upload_is_in_progress(nhs_number) fhir_response = self.build_and_process_fhir_doc_ref( - nhs_number, doc_ref_id, update_request_document, user_ods_code + nhs_number, + doc_ref_id, + update_request_document, + user_ods_code, ) fhir_response_data = json.loads(fhir_response) @@ -97,12 +105,19 @@ def update_document_reference_request( raise DocumentRefException(400, LambdaError.DocRefInvalidFiles) def build_and_process_fhir_doc_ref( - self, nhs_number, doc_ref_id, update_request_document, user_ods_code + self, + nhs_number, + doc_ref_id, + update_request_document, + user_ods_code, ): snomed_code_type = self.get_snomed_code_from_doc(update_request_document) doc_ref_info = self.build_doc_ref_info( - nhs_number, update_request_document, snomed_code_type, user_ods_code + nhs_number, + update_request_document, + snomed_code_type, + user_ods_code, ) logger.info(f"Updating document reference for client id: {doc_ref_id}") @@ -110,11 +125,12 @@ def build_and_process_fhir_doc_ref( validate_doc_version = update_request_document.version_id fhir_doc_ref = doc_ref_info.create_fhir_document_reference_object_basic( - doc_ref_id, validate_doc_version + doc_ref_id, + validate_doc_version, ) fhir_response = self.fhir_doc_ref_service.process_fhir_document_reference( - fhir_doc_ref.model_dump_json() + fhir_doc_ref.model_dump_json(), ) return fhir_response @@ -153,7 +169,11 @@ def get_snomed_code_from_doc(self, update_request_document): return snomed_code_type def build_doc_ref_info( - self, nhs_number, update_request_document, snomed_code_type, user_ods_code + self, + nhs_number, + update_request_document, + snomed_code_type, + user_ods_code, ): attachment_details = Attachment( title=update_request_document.file_name, @@ -169,8 +189,10 @@ def build_doc_ref_info( return doc_ref_info def check_if_ods_code_is_in_pilot(self, ods_code) -> bool: - pilot_ods_codes = self.get_allowed_list_of_ods_codes_for_upload_pilot() - if ods_code in pilot_ods_codes: + pilot_ods_codes = ( + self.feature_flag_service.get_allowed_list_of_ods_codes_for_upload_pilot() + ) + if ods_code in pilot_ods_codes or pilot_ods_codes == []: return True else: raise OdsErrorException() @@ -178,7 +200,7 @@ def check_if_ods_code_is_in_pilot(self, ods_code) -> bool: def parse_document(self, document: dict) -> UploadRequestDocument: try: validated_doc: UploadRequestDocument = UploadRequestDocument.model_validate( - document + document, ) except ValidationError as e: logger.error( @@ -207,12 +229,3 @@ def stop_if_upload_is_in_progress(self, nhs_number: str): {"Result": UPDATE_REFERENCE_FAILED_MESSAGE}, ) raise DocumentRefException(423, LambdaError.UploadInProgressError) - - def get_allowed_list_of_ods_codes_for_upload_pilot(self) -> list[str]: - logger.info( - "Starting ssm request to retrieve allowed list of ODS codes for Upload Pilot" - ) - response = self.ssm_service.get_ssm_parameter(UPLOAD_PILOT_ODS_ALLOWED_LIST) - if not response: - logger.warning("No ODS codes found in allowed list for Upload Pilot") - return response diff --git a/lambdas/tests/unit/services/test_create_document_reference_service.py b/lambdas/tests/unit/services/test_create_document_reference_service.py index 8a1332e03..3f8cd77ea 100644 --- a/lambdas/tests/unit/services/test_create_document_reference_service.py +++ b/lambdas/tests/unit/services/test_create_document_reference_service.py @@ -65,13 +65,13 @@ def mock_create_doc_ref_service(set_env, mocker): @pytest.fixture def mock_fhir_doc_ref_base_service(mocker, setup_request_context): mock_document_service = mocker.patch( - "services.fhir_document_reference_service_base.DocumentService" + "services.fhir_document_reference_service_base.DocumentService", ) mock_s3_service = mocker.patch( - "services.fhir_document_reference_service_base.S3Service" + "services.fhir_document_reference_service_base.S3Service", ) mock_dynamo_service = mocker.patch( - "services.fhir_document_reference_service_base.DynamoDBService" + "services.fhir_document_reference_service_base.DynamoDBService", ) service = FhirDocumentReferenceServiceBase() service.document_service = mock_document_service.return_value @@ -98,9 +98,9 @@ def mock_process_fhir_document_reference(mocker): return_value=json.dumps( { "content": [ - {"attachment": {"url": "https://test-bucket.s3.amazonaws.com/"}} - ] - } + {"attachment": {"url": "https://test-bucket.s3.amazonaws.com/"}}, + ], + }, ), ) @@ -119,13 +119,15 @@ def mock_create_document_reference(mock_create_doc_ref_service, mocker): @pytest.fixture() def mock_remove_records(mock_create_doc_ref_service, mocker): yield mocker.patch.object( - mock_create_doc_ref_service, "remove_records_of_failed_upload" + mock_create_doc_ref_service, + "remove_records_of_failed_upload", ) @pytest.fixture() def mock_check_existing_records_and_remove_failed_upload( - mock_create_doc_ref_service, mocker + mock_create_doc_ref_service, + mocker, ): yield mocker.patch.object( mock_create_doc_ref_service, @@ -136,7 +138,7 @@ def mock_check_existing_records_and_remove_failed_upload( @pytest.fixture() def mock_check_for_duplicate_files(mocker): yield mocker.patch( - "services.create_document_reference_service.check_for_duplicate_files" + "services.create_document_reference_service.check_for_duplicate_files", ) @@ -150,7 +152,8 @@ def mock_getting_patient_info_from_pds(mocker, mock_pds_patient): @pytest.fixture def mock_fetch_available_document_references_by_type( - mocker, mock_fhir_doc_ref_base_service + mocker, + mock_fhir_doc_ref_base_service, ): mock = mocker.patch.object( mock_fhir_doc_ref_base_service.document_service, @@ -169,10 +172,12 @@ def undo_mocking_for_is_upload_in_process(mock_fhir_doc_ref_base_service): @pytest.fixture def mock_get_allowed_list_of_ods_codes_for_upload_pilot( - mock_create_doc_ref_service, mocker + mock_create_doc_ref_service, + mocker, ): return mocker.patch.object( - mock_create_doc_ref_service, "get_allowed_list_of_ods_codes_for_upload_pilot" + mock_create_doc_ref_service.feature_flag_service, + "get_allowed_list_of_ods_codes_for_upload_pilot", ) @@ -186,7 +191,8 @@ def test_create_document_reference_request_empty_list( ): with pytest.raises(DocumentRefException) as e: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, [] + TEST_NHS_NUMBER, + [], ) assert e.value == DocumentRefException(400, LambdaError.DocRefInvalidFiles) @@ -206,12 +212,13 @@ def test_create_document_reference_request_with_lg_list_happy_path( mock_check_for_duplicate_files, ): mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] mock_presigned_url_response = "https://test-bucket.s3.amazonaws.com/" url_references = mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) expected_response = { "uuid1": mock_presigned_url_response, @@ -221,7 +228,8 @@ def test_create_document_reference_request_with_lg_list_happy_path( assert url_references == expected_response mock_check_existing_records_and_remove_failed_upload.assert_called_with( - TEST_NHS_NUMBER, LG_FILE_LIST[0]["docType"] + TEST_NHS_NUMBER, + LG_FILE_LIST[0]["docType"], ) mock_check_for_duplicate_files.assert_called_once() @@ -253,19 +261,20 @@ def test_create_document_reference_request_raise_error_when_invalid_lg( file_name=file["fileName"], doc_type=SupportedDocumentTypes.LG, document_snomed_code_type=SnomedCodes.LLOYD_GEORGE.value.code, - ) + ), ) side_effects.append(document_references[index]) mock_create_document_reference.side_effect = side_effects mock_check_for_duplicate_files.side_effect = LGInvalidFilesException("test") mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] with pytest.raises(DocumentRefException): mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) mock_create_document_reference.assert_has_calls( @@ -293,7 +302,8 @@ def test_create_document_reference_failed_to_parse_pds_response( with pytest.raises(Exception) as exc_info: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) exception = exc_info.value @@ -315,7 +325,8 @@ def test_cdr_nhs_number_not_found_raises_search_patient_exception( with pytest.raises(Exception) as exc_info: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) exception = exc_info.value @@ -337,12 +348,13 @@ def test_cdr_non_pdf_file_raises_exception( ): mock_check_for_duplicate_files.side_effect = LGInvalidFilesException mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] with pytest.raises(Exception) as exc_info: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) exception = exc_info.value @@ -363,18 +375,23 @@ def test_create_document_reference_request_lg_upload_throw_lambda_error_if_uploa ): two_minutes_ago = 1698661380 # 2023-10-30T10:23:00 mock_records_upload_in_process = create_test_lloyd_george_doc_store_refs( - override={"uploaded": False, "uploading": True, "last_updated": two_minutes_ago} + override={ + "uploaded": False, + "uploading": True, + "last_updated": two_minutes_ago, + }, ) mock_fetch_available_document_references_by_type.return_value = ( mock_records_upload_in_process ) mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] with pytest.raises(DocumentRefException) as e: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) assert e.value == DocumentRefException(423, LambdaError.UploadInProgressError) @@ -392,12 +409,13 @@ def test_create_document_reference_request_lg_upload_throw_lambda_error_if_got_a create_test_lloyd_george_doc_store_refs() ) mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] with pytest.raises(DocumentRefException) as e: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) assert e.value == DocumentRefException(422, LambdaError.DocRefRecordAlreadyInPlace) @@ -411,7 +429,7 @@ def test_check_existing_records_remove_previous_failed_upload_and_continue( mocker, ): mock_doc_refs_of_failed_upload = create_test_lloyd_george_doc_store_refs( - override={"uploaded": False} + override={"uploaded": False}, ) mock_fetch_available_document_references_by_type.return_value = ( mock_doc_refs_of_failed_upload @@ -420,15 +438,18 @@ def test_check_existing_records_remove_previous_failed_upload_and_continue( mock_create_doc_ref_service.stop_if_upload_is_in_process = mocker.MagicMock() mock_create_doc_ref_service.check_existing_records_and_remove_failed_upload( - TEST_NHS_NUMBER, mock_doc_refs_of_failed_upload[0].document_snomed_code_type + TEST_NHS_NUMBER, + mock_doc_refs_of_failed_upload[0].document_snomed_code_type, ) mock_remove_records.assert_called_with( - MOCK_LG_TABLE_NAME, mock_doc_refs_of_failed_upload + MOCK_LG_TABLE_NAME, + mock_doc_refs_of_failed_upload, ) def test_parse_documents_list_for_valid_input( - mock_fhir_doc_ref_base_service, mock_create_doc_ref_service + mock_fhir_doc_ref_base_service, + mock_create_doc_ref_service, ): mock_input = LG_FILE_LIST expected = PARSED_LG_FILE_LIST @@ -446,7 +467,7 @@ def test_parse_documents_list_raise_lambda_error_when_no_type( { "fileName": "test1.txt", "contentType": "text/plain", - } + }, ] with pytest.raises(DocumentRefException): @@ -462,7 +483,7 @@ def test_parse_documents_list_raise_lambda_error_when_doc_type_is_invalid( "fileName": "test1.txt", "contentType": "text/plain", "docType": "banana", - } + }, ] with pytest.raises(DocumentRefException): @@ -470,7 +491,9 @@ def test_parse_documents_list_raise_lambda_error_when_doc_type_is_invalid( def test_prepare_doc_object_lg_happy_path( - mocker, mock_fhir_doc_ref_base_service, mock_create_doc_ref_service + mocker, + mock_fhir_doc_ref_base_service, + mock_create_doc_ref_service, ): validated_document = UploadRequestDocument.model_validate(LG_FILE_LIST[0]) nhs_number = "1234567890" @@ -488,7 +511,10 @@ def test_prepare_doc_object_lg_happy_path( ) actual_document_reference = mock_create_doc_ref_service.create_document_reference( - nhs_number, current_gp_ods, validated_document, snomed_code_type="SNOMED" + nhs_number, + current_gp_ods, + validated_document, + snomed_code_type="SNOMED", ) assert actual_document_reference == mocked_doc @@ -521,7 +547,7 @@ def test_check_existing_records_does_nothing_if_no_record_exist( assert ( mock_create_doc_ref_service.check_existing_records_and_remove_failed_upload( TEST_NHS_NUMBER, - SupportedDocumentTypes.LG + SupportedDocumentTypes.LG, ) is None ) @@ -542,14 +568,14 @@ def test_check_existing_records_throw_error_if_upload_in_progress( "uploaded": False, "uploading": True, "last_updated": two_minutes_ago, - } + }, ) ) with pytest.raises(Exception) as e: mock_create_doc_ref_service.check_existing_records_and_remove_failed_upload( TEST_NHS_NUMBER, - SupportedDocumentTypes.LG + SupportedDocumentTypes.LG, ) ex = e.value assert isinstance(ex, DocumentRefException) @@ -572,7 +598,7 @@ def test_check_existing_records_throw_error_if_got_a_full_set_of_uploaded_record with pytest.raises(Exception) as e: mock_create_doc_ref_service.check_existing_records_and_remove_failed_upload( TEST_NHS_NUMBER, - SupportedDocumentTypes.LG + SupportedDocumentTypes.LG, ) ex = e.value @@ -584,10 +610,12 @@ def test_check_existing_records_throw_error_if_got_a_full_set_of_uploaded_record def test_remove_records_of_failed_upload( - mock_fhir_doc_ref_base_service, mock_create_doc_ref_service, mocker + mock_fhir_doc_ref_base_service, + mock_create_doc_ref_service, + mocker, ): mock_doc_refs_of_failed_upload = create_test_lloyd_george_doc_store_refs( - override={"uploaded": False} + override={"uploaded": False}, ) mock_create_doc_ref_service.post_fhir_doc_ref_service.s3_service = ( @@ -623,7 +651,8 @@ def test_ods_code_not_in_pilot_raises_exception( with pytest.raises(DocumentRefException) as exc_info: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) mock_create_document_reference.assert_not_called() @@ -634,23 +663,6 @@ def test_ods_code_not_in_pilot_raises_exception( assert exception.message == "ODS code does not match any of the allowed." -def test_get_allowed_list_of_ods_codes_for_upload_pilot( - mock_fhir_doc_ref_base_service, mock_create_doc_ref_service, mock_ssm -): - mock_ssm.get_ssm_parameter.return_value = MOCK_ALLOWED_ODS_CODES_LIST_PILOT[ - "Parameter" - ]["Value"] - expected = "PI001,PI002,PI003" - - actual = ( - mock_create_doc_ref_service.get_allowed_list_of_ods_codes_for_upload_pilot() - ) - - mock_ssm.get_ssm_parameter.assert_called_once() - - assert actual == expected - - def test_patient_ods_does_not_match_user_ods_and_raises_exception( mock_fhir_doc_ref_base_service, mock_create_doc_ref_service, @@ -660,7 +672,8 @@ def test_patient_ods_does_not_match_user_ods_and_raises_exception( with pytest.raises(DocumentRefException) as exc_info: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, LG_FILE_LIST + TEST_NHS_NUMBER, + LG_FILE_LIST, ) mock_create_document_reference.assert_not_called() @@ -683,49 +696,53 @@ def test_unable_to_find_config_raises_exception( mock_process_fhir_document_reference, ): mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] with pytest.raises(DocumentRefException) as exc_info: mock_create_doc_ref_service.create_document_reference_request( - TEST_NHS_NUMBER, ARF_FILE_LIST + TEST_NHS_NUMBER, + ARF_FILE_LIST, ) exception = exc_info.value assert isinstance(exception, DocumentRefException) assert exception.status_code == 400 - assert ( - exception.message - == "Invalid files or id" - ) + assert exception.message == "Invalid files or id" mock_process_fhir_document_reference.assert_not_called() + def test_check_existing_records_fetches_previous_records_for_doc_type( mock_fhir_doc_ref_base_service, mock_create_doc_ref_service, mock_fetch_available_document_references_by_type, mock_remove_records, - mocker + mocker, ): doc_type = SupportedDocumentTypes.LG - expected_query_filter = NotDeleted & DynamoQueryFilterBuilder().add_condition( - DocumentReferenceMetadataFields.DOCUMENT_SNOMED_CODE_TYPE, - AttributeOperator.EQUAL, - doc_type - ).build() + expected_query_filter = ( + NotDeleted + & DynamoQueryFilterBuilder() + .add_condition( + DocumentReferenceMetadataFields.DOCUMENT_SNOMED_CODE_TYPE, + AttributeOperator.EQUAL, + doc_type, + ) + .build() + ) mocker.patch( - "services.create_document_reference_service.get_document_type_filter" + "services.create_document_reference_service.get_document_type_filter", ).return_value = expected_query_filter mock_create_doc_ref_service.check_existing_records_and_remove_failed_upload( TEST_NHS_NUMBER, - doc_type + doc_type, ) mock_fetch_available_document_references_by_type.assert_called_with( nhs_number=TEST_NHS_NUMBER, doc_type=doc_type, - query_filter=expected_query_filter - ) \ No newline at end of file + query_filter=expected_query_filter, + ) diff --git a/lambdas/tests/unit/services/test_feature_flags_service.py b/lambdas/tests/unit/services/test_feature_flags_service.py index c04c9218c..b2e98b47f 100644 --- a/lambdas/tests/unit/services/test_feature_flags_service.py +++ b/lambdas/tests/unit/services/test_feature_flags_service.py @@ -53,7 +53,8 @@ def mock_feature_flag_service(set_env, mocker, setup_request_context): def test_request_app_config_data_valid_response_returns_data( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=success_200_all_response, status_code=200) @@ -64,7 +65,8 @@ def test_request_app_config_data_valid_response_returns_data( def test_request_app_config_data_invalid_json_raises_exception( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): invalid_json = "invalid:" mock_requests.get(test_url, text=invalid_json, status_code=500) @@ -78,7 +80,8 @@ def test_request_app_config_data_invalid_json_raises_exception( def test_request_app_config_data_400_raises_not_found_exception( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=err_response, status_code=400) @@ -91,7 +94,8 @@ def test_request_app_config_data_400_raises_not_found_exception( def test_request_app_config_data_catch_all_raises_failure_exception( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=err_response, status_code=500) @@ -117,7 +121,8 @@ def test_get_feature_flags_returns_all_flags(mock_requests, mock_feature_flag_se def test_get_feature_flags_no_flags_returns_empty( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=empty_response, status_code=200) mock_feature_flag_service.request_app_config_data.return_value = empty_response @@ -130,7 +135,8 @@ def test_get_feature_flags_no_flags_returns_empty( def test_get_feature_flags_invalid_raises_exception( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=err_response, status_code=200) mock_feature_flag_service.request_app_config_data.return_value = err_response @@ -143,7 +149,8 @@ def test_get_feature_flags_invalid_raises_exception( def test_get_feature_flags_by_flag_returns_single_flag( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=success_200_with_filter_reponse, status_code=200) mock_feature_flag_service.request_app_config_data.return_value = ( @@ -158,7 +165,8 @@ def test_get_feature_flags_by_flag_returns_single_flag( def test_get_feature_flags_by_flag_no_flag_raises_exception( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=empty_response, status_code=200) mock_feature_flag_service.request_app_config_data.return_value = empty_response @@ -172,7 +180,8 @@ def test_get_feature_flags_by_flag_no_flag_raises_exception( def test_get_feature_flags_by_flag_invalid_raises_exception( - mock_requests, mock_feature_flag_service + mock_requests, + mock_feature_flag_service, ): mock_requests.get(test_url, json=err_response, status_code=200) mock_feature_flag_service.request_app_config_data.return_value = err_response @@ -195,14 +204,15 @@ def test_get_allowed_list_of_ods_codes_for_upload_pilot(mock_feature_flag_servic assert actual_codes == expected_codes mock_feature_flag_service.ssm_service.get_ssm_parameter.assert_called_with( - UPLOAD_PILOT_ODS_ALLOWED_LIST + UPLOAD_PILOT_ODS_ALLOWED_LIST, ) def test_get_allowed_list_of_ods_codes_for_upload_pilot_no_codes_found( - mock_feature_flag_service, caplog + mock_feature_flag_service, + caplog, ): - mock_feature_flag_service.ssm_service.get_ssm_parameter.return_value = [] + mock_feature_flag_service.ssm_service.get_ssm_parameter.return_value = "*" result = mock_feature_flag_service.get_allowed_list_of_ods_codes_for_upload_pilot() @@ -221,7 +231,10 @@ def test_get_allowed_list_of_ods_codes_for_upload_pilot_no_codes_found( ], ) def test_check_if_ods_code_is_in_pilot( - mocker, mock_feature_flag_service, auth_context, expected_result + mocker, + mock_feature_flag_service, + auth_context, + expected_result, ): mock_context = mocker.MagicMock() mock_context.authorization = auth_context @@ -309,7 +322,8 @@ def test_get_feature_flags_by_flag_overwrites_upload_flag( def test_get_feature_flags_by_flag_for_non_upload_flag( - mocker, mock_feature_flag_service + mocker, + mock_feature_flag_service, ): flag_name = "some_other_flag" mocker.patch.object(mock_feature_flag_service, "check_if_ods_code_is_in_pilot") diff --git a/lambdas/tests/unit/services/test_update_document_reference_service.py b/lambdas/tests/unit/services/test_update_document_reference_service.py index 1a09166a4..05eedf133 100644 --- a/lambdas/tests/unit/services/test_update_document_reference_service.py +++ b/lambdas/tests/unit/services/test_update_document_reference_service.py @@ -28,15 +28,15 @@ def mock_update_doc_ref_service(mocker): @pytest.fixture -def mock_fhir_doc_ref_base_service(mocker, setup_request_context): +def mock_fhir_doc_ref_base_service(mocker, setup_request_context, set_env): mock_document_service = mocker.patch( - "services.fhir_document_reference_service_base.DocumentService" + "services.fhir_document_reference_service_base.DocumentService", ) mock_s3_service = mocker.patch( - "services.fhir_document_reference_service_base.S3Service" + "services.fhir_document_reference_service_base.S3Service", ) mock_dynamo_service = mocker.patch( - "services.fhir_document_reference_service_base.DynamoDBService" + "services.fhir_document_reference_service_base.DynamoDBService", ) service = FhirDocumentReferenceServiceBase() service.document_service = mock_document_service.return_value @@ -66,10 +66,11 @@ def mock_stop_if_upload_is_in_progress(mock_update_doc_ref_service, mocker): @pytest.fixture() def mock_validate_files_for_access_and_store( - mocker, mock_getting_patient_info_from_pds + mocker, + mock_getting_patient_info_from_pds, ): yield mocker.patch( - "services.update_document_reference_service.validate_files_for_access_and_store" + "services.update_document_reference_service.validate_files_for_access_and_store", ) @@ -92,26 +93,29 @@ def mock_process_fhir_document_reference(mocker): return_value=json.dumps( { "content": [ - {"attachment": {"url": "https://test-bucket.s3.amazonaws.com/"}} - ] - } + {"attachment": {"url": "https://test-bucket.s3.amazonaws.com/"}}, + ], + }, ), ) @pytest.fixture def mock_get_allowed_list_of_ods_codes_for_upload_pilot( - mock_update_doc_ref_service, mocker + mock_update_doc_ref_service, + mocker, ): return mocker.patch.object( - mock_update_doc_ref_service, "get_allowed_list_of_ods_codes_for_upload_pilot" + mock_update_doc_ref_service.feature_flag_service, + "get_allowed_list_of_ods_codes_for_upload_pilot", ) @pytest.fixture def mock_check_if_ods_code_is_in_pilot(mock_update_doc_ref_service, mocker): return mocker.patch.object( - mock_update_doc_ref_service, "check_if_ods_code_is_in_pilot" + mock_update_doc_ref_service, + "check_if_ods_code_is_in_pilot", ) @@ -134,8 +138,8 @@ def mock_fetch_documents_from_table(mocker, mock_update_doc_ref_service): def test_update_document_reference_request_with_lg_list_happy_path( - mock_update_doc_ref_service, mock_fhir_doc_ref_base_service, + mock_update_doc_ref_service, mock_getting_patient_info_from_pds, mock_stop_if_upload_is_in_progress, mock_get_allowed_list_of_ods_codes_for_upload_pilot, @@ -145,7 +149,7 @@ def test_update_document_reference_request_with_lg_list_happy_path( mock_fetch_documents_from_table, ): mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] mock_getting_patient_info_from_pds.return_value = mock_pds_patient mock_fetch_documents_from_table.return_value = create_test_doc_store_refs() @@ -157,7 +161,9 @@ def test_update_document_reference_request_with_lg_list_happy_path( ) url_references = mock_update_doc_ref_service.update_document_reference_request( - TEST_NHS_NUMBER, LG_FILE, TEST_UUID + TEST_NHS_NUMBER, + LG_FILE, + TEST_UUID, ) expected_response = {"uuid1": mock_presigned_url_response} @@ -183,7 +189,9 @@ def test_ods_code_not_in_pilot_raises_exception( with pytest.raises(DocumentRefException) as exc_info: mock_update_doc_ref_service.update_document_reference_request( - TEST_NHS_NUMBER, LG_FILE, TEST_UUID + TEST_NHS_NUMBER, + LG_FILE, + TEST_UUID, ) mock_process_fhir_document_reference.assert_not_called() @@ -209,7 +217,9 @@ def test_nhs_number_not_found_raises_exception( with pytest.raises(DocumentRefException) as exc_info: mock_update_doc_ref_service.update_document_reference_request( - TEST_NHS_NUMBER, LG_FILE, TEST_UUID + TEST_NHS_NUMBER, + LG_FILE, + TEST_UUID, ) exception = exc_info.value @@ -223,26 +233,28 @@ def test_nhs_number_not_found_raises_exception( # covers for number of files expected, non-pdf files, incorrect file name format, duplicate files def test_invalid_files_raises_exception( + mock_fhir_doc_ref_base_service, mock_update_doc_ref_service, mock_validate_files_for_access_and_store, mock_getting_patient_info_from_pds, mock_pds_patient, mock_get_allowed_list_of_ods_codes_for_upload_pilot, - mock_fhir_doc_ref_base_service, mock_process_fhir_document_reference, mock_stop_if_upload_is_in_progress, mock_fetch_documents_from_table, ): mock_getting_patient_info_from_pds.return_value = mock_pds_patient mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] mock_validate_files_for_access_and_store.side_effect = LGInvalidFilesException mock_fetch_documents_from_table.return_value = create_test_doc_store_refs() with pytest.raises(DocumentRefException) as exc_info: mock_update_doc_ref_service.update_document_reference_request( - TEST_NHS_NUMBER, LG_FILE, TEST_UUID + TEST_NHS_NUMBER, + LG_FILE, + TEST_UUID, ) exception = exc_info.value @@ -255,29 +267,35 @@ def test_invalid_files_raises_exception( @freeze_time("2023-10-30T10:25:00") def test_upload_already_in_progress_raises_exception( + mock_fhir_doc_ref_base_service, mock_update_doc_ref_service, mock_fetch_document_by_type, mock_get_allowed_list_of_ods_codes_for_upload_pilot, mock_getting_patient_info_from_pds, mock_pds_patient, - mock_fhir_doc_ref_base_service, mock_process_fhir_document_reference, mock_validate_files_for_access_and_store, mock_fetch_documents_from_table, ): mock_getting_patient_info_from_pds.return_value = mock_pds_patient mock_get_allowed_list_of_ods_codes_for_upload_pilot.return_value = [ - TEST_CURRENT_GP_ODS + TEST_CURRENT_GP_ODS, ] mock_fetch_documents_from_table.return_value = create_test_doc_store_refs() two_minutes_ago = 1698661380 # 2023-10-30T10:23:00 mock_records_upload_in_process = create_test_lloyd_george_doc_store_refs( - override={"uploaded": False, "uploading": True, "last_updated": two_minutes_ago} + override={ + "uploaded": False, + "uploading": True, + "last_updated": two_minutes_ago, + }, ) mock_fetch_document_by_type.return_value = mock_records_upload_in_process with pytest.raises(DocumentRefException) as exc_info: mock_update_doc_ref_service.update_document_reference_request( - TEST_NHS_NUMBER, LG_FILE, TEST_UUID + TEST_NHS_NUMBER, + LG_FILE, + TEST_UUID, ) exception = exc_info.value @@ -287,6 +305,7 @@ def test_upload_already_in_progress_raises_exception( def test_fail_early_if_there_is_no_document_reference_to_update( + mock_fhir_doc_ref_base_service, mock_update_doc_ref_service, mock_fetch_documents_from_table, mock_process_fhir_document_reference, @@ -295,7 +314,9 @@ def test_fail_early_if_there_is_no_document_reference_to_update( mock_fetch_documents_from_table.return_value = [] with pytest.raises(DocumentRefException) as exc_info: mock_update_doc_ref_service.update_document_reference_request( - TEST_NHS_NUMBER, LG_FILE, TEST_UUID + TEST_NHS_NUMBER, + LG_FILE, + TEST_UUID, ) exception = exc_info.value