From c264c70f0e983ad80fe27260ed45ea14b644b52c Mon Sep 17 00:00:00 2001 From: Matt Potter Date: Wed, 11 Feb 2026 16:25:12 +0000 Subject: [PATCH] NDR-377 adding Location header to POST FHIR DocumentReference --- .../post_fhir_document_reference_handler.py | 8 ++++++-- .../document_reference_search_service.py | 4 ++-- .../fhir_document_reference_service_base.py | 6 ++---- .../post_fhir_document_reference_service.py | 9 ++++++--- ..._pdm_post_fhir_document_reference_handler.py | 16 +++++++++++++--- ...test_post_fhir_document_reference_handler.py | 17 ++++++++++++++--- lambdas/utils/constants/api.py | 3 +++ 7 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 lambdas/utils/constants/api.py diff --git a/lambdas/handlers/post_fhir_document_reference_handler.py b/lambdas/handlers/post_fhir_document_reference_handler.py index 9281b4ec79..f1b4b63f87 100644 --- a/lambdas/handlers/post_fhir_document_reference_handler.py +++ b/lambdas/handlers/post_fhir_document_reference_handler.py @@ -2,6 +2,7 @@ PostFhirDocumentReferenceService, ) from utils.audit_logging_setup import LoggingService +from utils.constants.api import DOCUMENT_RETRIEVE_ENDPOINT from utils.decorators.handle_lambda_exceptions import handle_lambda_exceptions_fhir from utils.lambda_exceptions import DocumentRefException from utils.lambda_response import ApiGatewayResponse @@ -30,13 +31,16 @@ def lambda_handler(event, context): fhir_doc_ref_service = PostFhirDocumentReferenceService() - fhir_response = fhir_doc_ref_service.process_fhir_document_reference( + fhir_response, document_id = fhir_doc_ref_service.process_fhir_document_reference( event.get("body"), event.get("requestContext") ) + # Construct the Location header for FHIR compliance + location_url = f"{DOCUMENT_RETRIEVE_ENDPOINT}/{document_id}" + return ApiGatewayResponse( status_code=201, body=fhir_response, methods="POST" - ).create_api_gateway_response() + ).create_api_gateway_response(headers={"Location": location_url}) except DocumentRefException as exception: logger.error(f"Error processing FHIR document reference: {str(exception)}") diff --git a/lambdas/services/document_reference_search_service.py b/lambdas/services/document_reference_search_service.py index a4046bb45e..025fb6a6f0 100644 --- a/lambdas/services/document_reference_search_service.py +++ b/lambdas/services/document_reference_search_service.py @@ -16,6 +16,7 @@ from services.document_service import DocumentService from utils.audit_logging_setup import LoggingService from utils.common_query_filters import NotDeleted, UploadCompleted +from utils.constants.api import DOCUMENT_RETRIEVE_ENDPOINT from utils.dynamo_query_filter_builder import DynamoQueryFilterBuilder from utils.exceptions import DynamoServiceException from utils.lambda_exceptions import DocumentRefSearchException @@ -225,12 +226,11 @@ def create_document_reference_fhir_response( self, document_reference: DocumentReference, ) -> dict: - document_retrieve_endpoint = os.getenv("DOCUMENT_RETRIEVE_ENDPOINT_APIM", "") document_details = Attachment( title=document_reference.file_name, creation=document_reference.document_scan_creation or document_reference.created, - url=document_retrieve_endpoint + url=DOCUMENT_RETRIEVE_ENDPOINT + "/" + document_reference.document_snomed_code_type + "~" diff --git a/lambdas/services/fhir_document_reference_service_base.py b/lambdas/services/fhir_document_reference_service_base.py index 1ae935e126..441fe61dbd 100644 --- a/lambdas/services/fhir_document_reference_service_base.py +++ b/lambdas/services/fhir_document_reference_service_base.py @@ -19,6 +19,7 @@ from services.document_service import DocumentService from utils.audit_logging_setup import LoggingService from utils.common_query_filters import CurrentStatusFile +from utils.constants.api import DOCUMENT_RETRIEVE_ENDPOINT from utils.dynamo_utils import DocTypeTableRouter from utils.exceptions import ( FhirDocumentReferenceException, @@ -194,11 +195,8 @@ def _create_fhir_response( if presigned_url: attachment_url = presigned_url else: - document_retrieve_endpoint = os.getenv( - "DOCUMENT_RETRIEVE_ENDPOINT_APIM", "" - ) attachment_url = ( - document_retrieve_endpoint + DOCUMENT_RETRIEVE_ENDPOINT + "/" + document_reference_ndr.document_snomed_code_type + "~" diff --git a/lambdas/services/post_fhir_document_reference_service.py b/lambdas/services/post_fhir_document_reference_service.py index 255c1c6bde..4d8ca0d064 100644 --- a/lambdas/services/post_fhir_document_reference_service.py +++ b/lambdas/services/post_fhir_document_reference_service.py @@ -26,7 +26,7 @@ def __init__(self): def process_fhir_document_reference( self, fhir_document: str, api_request_context: dict = {} - ) -> str: + ) -> tuple[str, str]: """ Process a FHIR Document Reference request @@ -34,7 +34,7 @@ def process_fhir_document_reference( fhir_document: FHIR Document Reference object Returns: - FHIR Document Reference response JSON object + Tuple of (FHIR Document Reference response JSON object, document ID) """ try: common_name = validate_common_name_in_mtls(api_request_context) @@ -75,7 +75,10 @@ def process_fhir_document_reference( document_reference, validated_fhir_doc, dynamo_table ) - return self._create_fhir_response(document_reference, presigned_url) + fhir_response = self._create_fhir_response(document_reference, presigned_url) + document_id = f"{document_reference.document_snomed_code_type}~{document_reference.id}" + + return fhir_response, document_id except (ValidationError, InvalidNhsNumberException) as e: logger.error(f"FHIR document validation error: {str(e)}") diff --git a/lambdas/tests/unit/handlers/test_pdm_post_fhir_document_reference_handler.py b/lambdas/tests/unit/handlers/test_pdm_post_fhir_document_reference_handler.py index 91c1b156a8..143354415f 100644 --- a/lambdas/tests/unit/handlers/test_pdm_post_fhir_document_reference_handler.py +++ b/lambdas/tests/unit/handlers/test_pdm_post_fhir_document_reference_handler.py @@ -57,18 +57,28 @@ def mock_service(mocker): return mock_service_instance -def test_mtls_lambda_handler_success(valid_mtls_event, context, mock_service): +def test_mtls_lambda_handler_success(valid_mtls_event, context, mock_service, mocker): """Test successful lambda execution.""" mock_response = {"resourceType": "DocumentReference", "id": "test-id"} + mock_document_id = "717391000000106~pdm-doc-456" - mock_service.process_fhir_document_reference.return_value = json.dumps( - mock_response + # Mock environment variable + mocker.patch.dict( + "os.environ", + {"DOCUMENT_RETRIEVE_ENDPOINT_APIM": "https://api.example.com/DocumentReference"}, + ) + + mock_service.process_fhir_document_reference.return_value = ( + json.dumps(mock_response), + mock_document_id, ) result = lambda_handler(valid_mtls_event, context) assert result["statusCode"] == 201 assert json.loads(result["body"]) == mock_response + assert "Location" in result["headers"] + assert result["headers"]["Location"] == f"https://api.example.com/DocumentReference/{mock_document_id}" mock_service.process_fhir_document_reference.assert_called_once_with( valid_mtls_event["body"], valid_mtls_event["requestContext"] diff --git a/lambdas/tests/unit/handlers/test_post_fhir_document_reference_handler.py b/lambdas/tests/unit/handlers/test_post_fhir_document_reference_handler.py index 7bf728c982..35221f6817 100644 --- a/lambdas/tests/unit/handlers/test_post_fhir_document_reference_handler.py +++ b/lambdas/tests/unit/handlers/test_post_fhir_document_reference_handler.py @@ -49,18 +49,28 @@ def mock_service(mocker): return mock_service_instance -def test_lambda_handler_success(valid_event, context, mock_service): +def test_lambda_handler_success(valid_event, context, mock_service, mocker): """Test successful lambda execution.""" mock_response = {"resourceType": "DocumentReference", "id": "test-id"} + mock_document_id = "16521000000101~test-doc-123" - mock_service.process_fhir_document_reference.return_value = json.dumps( - mock_response + # Mock environment variable + mocker.patch.dict( + "os.environ", + {"DOCUMENT_RETRIEVE_ENDPOINT_APIM": "https://api.example.com/DocumentReference"}, + ) + + mock_service.process_fhir_document_reference.return_value = ( + json.dumps(mock_response), + mock_document_id, ) result = lambda_handler(valid_event, context) assert result["statusCode"] == 201 assert json.loads(result["body"]) == mock_response + assert "Location" in result["headers"] + assert result["headers"]["Location"] == f"https://api.example.com/DocumentReference/{mock_document_id}" mock_service.process_fhir_document_reference.assert_called_once_with( valid_event["body"], valid_event["requestContext"] @@ -77,6 +87,7 @@ def test_lambda_handler_exception_handling(valid_event, context, mock_service): assert result["statusCode"] == 400 assert "resourceType" in json.loads(result["body"]) + assert "Location" not in result["headers"] mock_service.process_fhir_document_reference.assert_called_once_with( valid_event["body"], valid_event["requestContext"] diff --git a/lambdas/utils/constants/api.py b/lambdas/utils/constants/api.py new file mode 100644 index 0000000000..1d8adb7da9 --- /dev/null +++ b/lambdas/utils/constants/api.py @@ -0,0 +1,3 @@ +import os + +DOCUMENT_RETRIEVE_ENDPOINT = os.getenv("DOCUMENT_RETRIEVE_ENDPOINT_APIM", "")