-
Notifications
You must be signed in to change notification settings - Fork 1
[PRMP-594] Get Document by ID #858
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
924dcaa
[PRMP-594] Handler & service for getting single document by ID
FoxMaltas-NHS 3ab6081
[PRMP-594] Completed unit tests for get_document_reference_service
FoxMaltas-NHS ed5aa5c
[PRMP-594] Removed unneeded environment variable check
FoxMaltas-NHS e9a5363
[PRMP-594] Changed document reference database filter conditions
FoxMaltas-NHS a621f8c
[PRMP-594] Removed unused lambda error
FoxMaltas-NHS bacd42f
[PRMP-594] Rename variable so it doesn't shadow a python builtin
FoxMaltas-NHS f405c22
Merge branch 'main' into PRMP-594
FoxMaltas-NHS d43fdd7
[PRMP-594] Switched to using feature flag validation function from PR…
FoxMaltas-NHS 5f96920
Merge branch 'main' into PRMP-594
FoxMaltas-NHS 219c438
[PRMP-594] Refactored checking expected parameters were passed
FoxMaltas-NHS 333e1de
Merge branch 'main' into PRMP-594
FoxMaltas-NHS 9ce22be
[PRMP-594] Removed unnecessary check and error raise
FoxMaltas-NHS d88ec64
Merge branch 'main' into PRMP-594
FoxMaltas-NHS 1b6ef4a
[PRMP-594] Added file content type to api return body
FoxMaltas-NHS 5fd401a
[PRMP-594] Added lambda handler to deploy workflow
FoxMaltas-NHS 3355bb5
Merge branch 'main' into PRMP-594
FoxMaltas-NHS f5f9d70
Merge branch 'main' into PRMP-594
FoxMaltas-NHS 5435eb4
[PRMP-594] Remove duplicated feature flag
FoxMaltas-NHS 90ba691
Merge branch 'main' into PRMP-594
FoxMaltas-NHS e411892
[PRMP-594] Added missing required environment variables
FoxMaltas-NHS 0d177bc
[PRMP-594] Refactored service to use s3, dynamo, and document service…
FoxMaltas-NHS 7710866
[PRMP-594] Fix for service unit tests
FoxMaltas-NHS 7bdfb87
Merge branch 'main' into PRMP-594
FoxMaltas-NHS cac91f0
[PRMP-594] Switched test_get_document_reference_handler to use confte…
FoxMaltas-NHS 977dfb6
[PRMP-594] Remove unused test fixture
FoxMaltas-NHS a80fdc3
Merge branch 'main' into PRMP-594
FoxMaltas-NHS bc92f7a
fix function call
adamwhitingnhs 87c3964
[PRMP-594] Fixed unit test
FoxMaltas-NHS bda5f24
Merge branch 'main' into PRMP-594
FoxMaltas-NHS c431b06
Merge branch 'main' into PRMP-594
adamwhitingnhs 3576577
Merge branch 'main' into PRMP-594
adamwhitingnhs 630b6a6
Merge branch 'main' into PRMP-594
DuncanSangsterNHS File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| from utils.decorators.handle_lambda_exceptions import handle_lambda_exceptions | ||
| from utils.decorators.override_error_check import override_error_check | ||
| from services.feature_flags_service import FeatureFlagService | ||
| from enums.feature_flags import FeatureFlags | ||
| from services.get_document_reference_service import GetDocumentReferenceService | ||
| from utils.decorators.validate_patient_id import validate_patient_id | ||
| from utils.lambda_exceptions import FeatureFlagsException | ||
| from enums.lambda_error import LambdaError | ||
| from utils.lambda_exceptions import GetDocumentRefException | ||
| from utils.lambda_response import ApiGatewayResponse | ||
| from utils.decorators.ensure_env_var import ensure_environment_variables | ||
| from utils.decorators.set_audit_arg import set_request_context_for_logging | ||
| from enums.logging_app_interaction import LoggingAppInteraction | ||
| from utils.audit_logging_setup import LoggingService | ||
| from utils.request_context import request_context | ||
| import json | ||
|
|
||
| logger = LoggingService(__name__) | ||
|
|
||
| @validate_patient_id | ||
| @handle_lambda_exceptions | ||
| @set_request_context_for_logging | ||
| @ensure_environment_variables( | ||
| names=[ | ||
| "LLOYD_GEORGE_DYNAMODB_NAME", | ||
| "PRESIGNED_ASSUME_ROLE", | ||
| "APPCONFIG_APPLICATION", | ||
| "APPCONFIG_ENVIRONMENT", | ||
| "APPCONFIG_CONFIGURATION", | ||
| "EDGE_REFERENCE_TABLE", | ||
| "CLOUDFRONT_URL", | ||
| ] | ||
| ) | ||
| @override_error_check | ||
| def lambda_handler(event: dict[str, any], context): | ||
| request_context.app_interaction = LoggingAppInteraction.VIEW_LG_RECORD.value | ||
|
|
||
| feature_flag_service = FeatureFlagService() | ||
| feature_flag_service.validate_feature_flag(FeatureFlags.UPLOAD_DOCUMENT_ITERATION_3_ENABLED) | ||
|
|
||
| logger.info("Starting document fetch by ID process") | ||
|
|
||
| try: | ||
| document_id = event["pathParameters"]["id"] | ||
| nhs_number = event["queryStringParameters"]["patientId"] | ||
| except KeyError: | ||
| raise GetDocumentRefException(400, LambdaError.DocumentReferenceMissingParameters) | ||
|
|
||
| service = GetDocumentReferenceService() | ||
|
|
||
| document_info = service.get_document_url_by_id(document_id, nhs_number) | ||
|
|
||
| return ApiGatewayResponse( | ||
| status_code=200, body=json.dumps(document_info), methods="GET" | ||
| ).create_api_gateway_response() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import os | ||
| import uuid | ||
| from datetime import datetime, timezone | ||
| from services.get_fhir_document_reference_service import ( | ||
| GetFhirDocumentReferenceService, | ||
| ) | ||
| from utils.audit_logging_setup import LoggingService | ||
| from utils.lambda_exceptions import GetDocumentRefException | ||
| from enums.lambda_error import LambdaError | ||
| from utils.utilities import format_cloudfront_url | ||
| from models.document_reference import DocumentReference | ||
| from utils.dynamo_query_filter_builder import DynamoQueryFilterBuilder | ||
| from enums.dynamo_filter import AttributeOperator | ||
| from utils.common_query_filters import NotDeleted | ||
|
|
||
| logger = LoggingService(__name__) | ||
|
|
||
| class GetDocumentReferenceService: | ||
| def __init__(self): | ||
| self.fhir_doc_service = GetFhirDocumentReferenceService() | ||
| self.document_service = self.fhir_doc_service.document_service | ||
| self.dynamo_service = self.document_service.dynamo_service | ||
| self.s3_service = self.fhir_doc_service.s3_service | ||
| self.lg_table = os.environ.get("LLOYD_GEORGE_DYNAMODB_NAME") | ||
| self.cloudfront_table_name = os.environ.get("EDGE_REFERENCE_TABLE") | ||
| self.cloudfront_url = os.environ.get("CLOUDFRONT_URL") | ||
|
|
||
| def get_document_url_by_id(self, document_id: str, nhs_number: str): | ||
| document_reference = self.get_document_reference(document_id, nhs_number) | ||
|
|
||
| presigned_s3_url = self.create_document_presigned_url( | ||
| document_reference.s3_bucket_name, | ||
| document_reference.s3_file_key | ||
| ) | ||
|
|
||
| return { | ||
| "url": presigned_s3_url, | ||
| "contentType": document_reference.content_type | ||
| } | ||
|
|
||
| def create_document_presigned_url(self, bucket_name, file_location): | ||
| presigned_url_response = self.s3_service.create_download_presigned_url( | ||
| s3_bucket_name=bucket_name, | ||
| file_key=file_location, | ||
| ) | ||
|
|
||
| presigned_id = str(uuid.uuid4()) | ||
| deletion_date = datetime.now(timezone.utc) | ||
|
|
||
| ttl_half_an_hour_in_seconds = self.s3_service.presigned_url_expiry | ||
| dynamo_item_ttl = int(deletion_date.timestamp() + ttl_half_an_hour_in_seconds) | ||
| self.dynamo_service.create_item( | ||
| self.cloudfront_table_name, | ||
| { | ||
| "ID": presigned_id, | ||
| "presignedUrl": presigned_url_response, | ||
| "TTL": dynamo_item_ttl, | ||
| }, | ||
| ) | ||
| return format_cloudfront_url(presigned_id, self.cloudfront_url) | ||
|
|
||
| def get_document_reference(self, document_id: str, nhs_number: str) -> DocumentReference: | ||
| filter_builder = DynamoQueryFilterBuilder() | ||
| filter_builder.add_condition("DocStatus", AttributeOperator.EQUAL, "final") | ||
| filter_builder.add_condition("NhsNumber", AttributeOperator.EQUAL, nhs_number) | ||
|
|
||
| table_filter = filter_builder.build() | ||
|
|
||
| table_filter = table_filter & NotDeleted | ||
|
|
||
| documents = self.document_service.fetch_documents_from_table( | ||
| table_name=self.lg_table, | ||
| search_condition=document_id, | ||
| search_key="ID", | ||
| query_filter=table_filter, | ||
| ) | ||
| if len(documents) > 0: | ||
| logger.info("Document found for given id") | ||
| return documents[0] | ||
| else: | ||
| raise GetDocumentRefException( | ||
| 404, LambdaError.DocumentReferenceNotFound | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
lambdas/tests/unit/handlers/test_get_document_reference_handler.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| import pytest | ||
| import json | ||
| import os | ||
| from unittest.mock import patch | ||
| from handlers.get_document_reference_handler import lambda_handler | ||
| from utils.lambda_exceptions import GetDocumentRefException | ||
| from enums.feature_flags import FeatureFlags | ||
| from utils.lambda_response import ApiGatewayResponse | ||
| from utils.lambda_exceptions import FeatureFlagsException | ||
| from enums.lambda_error import LambdaError | ||
| from utils.error_response import ErrorResponse | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def mock_feature_flag_service(mocker): | ||
| yield mocker.patch("handlers.get_document_reference_handler.FeatureFlagService").return_value | ||
|
|
||
| @pytest.fixture | ||
| def mock_get_document_service(mocker): | ||
| yield mocker.patch( | ||
| "handlers.get_document_reference_handler.GetDocumentReferenceService" | ||
| ).return_value | ||
|
|
||
| @pytest.fixture | ||
| def mock_valid_nhs_number(): | ||
| yield "4407064188" | ||
|
|
||
| @pytest.fixture | ||
| def feature_flag(): | ||
| yield FeatureFlags.UPLOAD_DOCUMENT_ITERATION_3_ENABLED | ||
|
|
||
| @pytest.fixture | ||
| def mock_interaction_id(): | ||
| yield "88888888-4444-4444-4444-121212121212" | ||
|
|
||
| @pytest.fixture | ||
| def mocked_bad_env_vars(): | ||
| env_vars = { | ||
| #"LLOYD_GEORGE_DYNAMODB_NAME": "mock_dynamodb_name", | ||
| "PRESIGNED_ASSUME_ROLE": "mock_presigned_role", | ||
| "APPCONFIG_APPLICATION": "mock_value", | ||
| "APPCONFIG_ENVIRONMENT": "mock_value", | ||
| "APPCONFIG_CONFIGURATION": "mock_value", | ||
| "EDGE_REFERENCE_TABLE": "mock_value", | ||
| "CLOUDFRONT_URL": "mock_value", | ||
| } | ||
|
|
||
| with patch.dict(os.environ, env_vars): | ||
| yield "LLOYD_GEORGE_DYNAMODB_NAME" | ||
|
|
||
|
|
||
|
|
||
| def test_handler_valid_request_returns_200( | ||
| valid_id_event_with_auth_header, | ||
| mock_feature_flag_service, | ||
| mock_get_document_service, | ||
| mock_valid_nhs_number, | ||
| context, | ||
| set_env, | ||
| feature_flag | ||
| ): | ||
| mock_document_id = "1" | ||
| valid_id_event_with_auth_header["pathParameters"] = {"id": mock_document_id} | ||
| valid_id_event_with_auth_header["queryStringParameters"]["patientId"] = mock_valid_nhs_number | ||
| mock_presigned_s3_url = "https://mock.url/" | ||
| mock_content_type = "application/pdf" | ||
|
|
||
| expected_body = { | ||
| "url": mock_presigned_s3_url, | ||
| "contentType": mock_content_type | ||
| } | ||
|
|
||
| expected_result = ApiGatewayResponse( | ||
| status_code=200, body=json.dumps(expected_body), methods="GET" | ||
| ).create_api_gateway_response() | ||
|
|
||
| mock_get_document_service.get_document_url_by_id.return_value = expected_body | ||
|
|
||
| result = lambda_handler(valid_id_event_with_auth_header, context) | ||
|
|
||
| assert result == expected_result | ||
| assert result["body"] == json.dumps(expected_body) | ||
|
|
||
| mock_feature_flag_service.validate_feature_flag.assert_called_once_with( | ||
| feature_flag | ||
| ) | ||
| mock_get_document_service.get_document_url_by_id.assert_called_once_with( | ||
| mock_document_id, | ||
| mock_valid_nhs_number) | ||
|
|
||
| def test_missing_nhs_number_errors( | ||
| valid_id_event_with_auth_header, | ||
| mock_feature_flag_service, | ||
| context, | ||
| set_env, | ||
| feature_flag, | ||
| ): | ||
| valid_id_event_with_auth_header["pathParameters"] = {"id": "1"} | ||
| valid_id_event_with_auth_header["queryStringParameters"].pop("patientId") | ||
|
|
||
| expected_result = ApiGatewayResponse( | ||
| status_code=400, | ||
| body=LambdaError.PatientIdNoKey.create_error_body(), | ||
| methods="GET", | ||
| ).create_api_gateway_response() | ||
|
|
||
| result = lambda_handler(valid_id_event_with_auth_header, context) | ||
|
|
||
| assert result == expected_result | ||
|
|
||
| def test_missing_document_id_errors( | ||
| valid_id_event_with_auth_header, | ||
| mock_feature_flag_service, | ||
| mock_valid_nhs_number, | ||
| context, | ||
| set_env, | ||
| feature_flag, | ||
| mock_interaction_id | ||
| ): | ||
| valid_id_event_with_auth_header["pathParameters"] = {} | ||
| valid_id_event_with_auth_header["queryStringParameters"]["patientId"] = mock_valid_nhs_number | ||
kamenbachvarov-nhs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| expected_error = GetDocumentRefException(400, LambdaError.DocumentReferenceMissingParameters) | ||
|
|
||
| expected_result = ApiGatewayResponse( | ||
| status_code=400, | ||
| body=ErrorResponse( | ||
| err_code=expected_error.err_code, | ||
| message=expected_error.message, | ||
| interaction_id=mock_interaction_id | ||
| ).create(), | ||
| methods="GET" | ||
| ).create_api_gateway_response() | ||
|
|
||
| mock_feature_flag_service.get_feature_flags_by_flag.return_value = {feature_flag: True} | ||
|
|
||
| result = lambda_handler(valid_id_event_with_auth_header, context) | ||
|
|
||
| assert result == expected_result | ||
|
|
||
| def test_env_vars_not_set_errors( | ||
| valid_id_event_with_auth_header, | ||
| context, | ||
| mocked_bad_env_vars | ||
| ): | ||
| expected_result = ApiGatewayResponse( | ||
| status_code=500, | ||
| body=LambdaError.EnvMissing.create_error_body({"name": mocked_bad_env_vars}), | ||
| methods="GET" | ||
| ).create_api_gateway_response() | ||
|
|
||
| result = lambda_handler(valid_id_event_with_auth_header, context) | ||
|
|
||
| assert result == expected_result | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.