Skip to content

Commit 74fd7e3

Browse files
[PRMP-841] Create logic for new endpoint for review status (#890)
1 parent 1b242eb commit 74fd7e3

19 files changed

+712
-23
lines changed

.github/workflows/base-lambdas-reusable-deploy-all.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,3 +738,17 @@ jobs:
738738
lambda_layer_names: "core_lambda_layer"
739739
secrets:
740740
AWS_ASSUME_ROLE: ${{ secrets.AWS_ASSUME_ROLE }}
741+
742+
deploy_review_document_status_check_lambda:
743+
name: Deploy Search Document Review
744+
uses: ./.github/workflows/base-lambdas-reusable-deploy.yml
745+
with:
746+
environment: ${{ inputs.environment }}
747+
python_version: ${{ inputs.python_version }}
748+
build_branch: ${{ inputs.build_branch }}
749+
sandbox: ${{ inputs.sandbox }}
750+
lambda_handler_name: review_document_status_check_handler
751+
lambda_aws_name: ReviewDocumentStatusCheck
752+
lambda_layer_names: "core_lambda_layer"
753+
secrets:
754+
AWS_ASSUME_ROLE: ${{ secrets.AWS_ASSUME_ROLE }}

lambdas/enums/lambda_error.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ def create_error_body(self, params: Optional[dict] = None, **kwargs) -> str:
658658
}
659659

660660
"""
661-
Errors for SearchDocumentReviewReference exceptions
661+
Errors for DocumentReview exceptions
662662
"""
663663
DocumentReviewDB = {
664664
"err_code": "SDR_5001",
@@ -670,12 +670,23 @@ def create_error_body(self, params: Optional[dict] = None, **kwargs) -> str:
670670
"message": "Review document model error",
671671
}
672672

673-
SearchDocumentReviewMissingODS = {
674-
"err_code": "SDR_4001",
673+
DocumentReviewMissingODS = {
674+
"err_code": "SDR_4011",
675675
"message": "Missing ODS code in request context",
676676
}
677677

678678
SearchDocumentInvalidQuerystring = {
679679
"err_code": "SDR_4002",
680680
"message": "Invalid query string passed",
681681
}
682+
683+
DocumentReviewStatusMissingId = {
684+
"err_code": "SDR_4003",
685+
"message": "Missing path parameters"
686+
}
687+
688+
DocumentReviewForbidden = {
689+
"err_code": "SDR_4031",
690+
"message": "User is not permitted to review document"
691+
}
692+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import json
2+
3+
from enums.feature_flags import FeatureFlags
4+
from enums.lambda_error import LambdaError
5+
from services.feature_flags_service import FeatureFlagService
6+
from services.review_document_status_check_service import (
7+
ReviewDocumentStatusCheckService,
8+
)
9+
from utils.audit_logging_setup import LoggingService
10+
from utils.decorators.ensure_env_var import ensure_environment_variables
11+
from utils.decorators.handle_lambda_exceptions import handle_lambda_exceptions
12+
from utils.decorators.set_audit_arg import set_request_context_for_logging
13+
from utils.exceptions import OdsErrorException
14+
from utils.lambda_exceptions import DocumentReviewException
15+
from utils.lambda_handler_utils import validate_review_path_parameters
16+
from utils.lambda_response import ApiGatewayResponse
17+
from utils.ods_utils import extract_ods_code_from_request_context
18+
19+
logger = LoggingService(__name__)
20+
21+
22+
@ensure_environment_variables(
23+
names=["DOCUMENT_REVIEW_DYNAMODB_NAME"]
24+
)
25+
@set_request_context_for_logging
26+
@handle_lambda_exceptions
27+
def lambda_handler(event, context):
28+
"""
29+
Lambda handler for checking the review status of a document in the review table
30+
Trigger by GET request to /DocumentReview/{id}/{version}/Status
31+
32+
Args:
33+
event: API Gateway event containing path parameters {id} and {version}
34+
context: Lambda Context
35+
36+
Returns:
37+
ApiGatewayResponse, body contains id, version and review status of reference searched for.
38+
401 - No ODS code or auth token provided in request.
39+
403 - User is not author of review entry.
40+
500 - Document Review Error, Internal Server Error.
41+
42+
"""
43+
try:
44+
45+
feature_flag_service = FeatureFlagService()
46+
feature_flag_service.validate_feature_flag(
47+
FeatureFlags.UPLOAD_DOCUMENT_ITERATION_3_ENABLED
48+
)
49+
50+
ods_code = extract_ods_code_from_request_context()
51+
52+
logger.info("Initialising Review Document Status Check service.")
53+
status_check_service = ReviewDocumentStatusCheckService()
54+
55+
document_id, document_version = validate_review_path_parameters(event)
56+
body = status_check_service.get_document_review_status(
57+
ods_code=ods_code,
58+
document_id=document_id,
59+
document_version=document_version,
60+
)
61+
62+
logger.info("Returning document review status.")
63+
return ApiGatewayResponse(
64+
status_code=200,
65+
body=json.dumps(body),
66+
methods="GET",
67+
).create_api_gateway_response()
68+
69+
except OdsErrorException:
70+
logger.error("Missing ODS code in request context.")
71+
raise DocumentReviewException(401, LambdaError.DocumentReviewMissingODS)

lambdas/handlers/search_document_review_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def lambda_handler(event, context):
8181
logger.error(e)
8282
return ApiGatewayResponse(
8383
status_code=401,
84-
body=LambdaError.SearchDocumentReviewMissingODS.create_error_body(),
84+
body=LambdaError.DocumentReviewMissingODS.create_error_body(),
8585
methods="GET",
8686
).create_api_gateway_response()
8787

@@ -99,7 +99,7 @@ def get_ods_code_from_request_context():
9999

100100
except AttributeError as e:
101101
logger.error(e)
102-
raise DocumentReviewException(401, LambdaError.SearchDocumentReviewMissingODS)
102+
raise DocumentReviewException(401, LambdaError.DocumentReviewMissingODS)
103103

104104

105105
def parse_querystring_parameters(event):

lambdas/models/document_review.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ def model_dump_camel_case(self, *args, **kwargs):
5353

5454
return camel_case_model_dump_results
5555

56-
5756
def camelize(self, model: dict) -> dict:
5857
camel_case_dict = {}
5958
for key, value in model.items():
@@ -68,6 +67,7 @@ def camelize(self, model: dict) -> dict:
6867

6968
return camel_case_dict
7069

70+
7171
class PatchDocumentReviewRequest(BaseModel):
7272
model_config = ConfigDict(
7373
validate_by_alias=True,

lambdas/services/authoriser_service.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,16 +119,20 @@ def deny_access_policy(self, path, user_role, nhs_number: str = None):
119119
case "/Feedback":
120120
deny_resource = False
121121

122-
case "/DocumentReview":
123-
deny_resource = False
124-
125122
case "/DocumentStatus":
126123
deny_resource = (
127124
not patient_access_is_allowed or is_user_gp_clinical or is_user_pcse
128125
)
129-
case path if path.startswith("/DocumentReview/"):
126+
127+
case path if re.match(r"^/DocumentReview/[^/]+/[^/]+/Status$", path):
128+
deny_resource = False
129+
130+
case path if re.match(r"^/DocumentReview/[^/]+/[^/]+$", path):
130131
deny_resource = not patient_access_is_allowed
131132

133+
case "/DocumentReview":
134+
deny_resource = False
135+
132136
case "/UploadState":
133137
deny_resource = (
134138
not patient_access_is_allowed or is_user_gp_clinical or is_user_pcse

lambdas/services/document_service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ def fetch_documents_from_table(
106106
return documents
107107

108108
def get_item(
109-
self,
110-
document_id: str,
111-
sort_key: dict = None,
112-
table_name: str = None,
113-
model_class: type[BaseModel] = None,
109+
self,
110+
document_id: str,
111+
sort_key: dict = None,
112+
table_name: str = None,
113+
model_class: type[BaseModel] = None,
114114
) -> Optional[BaseModel]:
115115
"""Fetch a single document by ID from a specified or configured table.
116116

lambdas/services/document_upload_review_service.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,20 @@ def _validate_review_references(
8989
"Failed to validate document review references"
9090
)
9191

92+
def get_document(
93+
self, document_id: str, version: int | None
94+
) -> DocumentUploadReviewReference | None:
95+
try:
96+
sort_key = {"Version": version}
97+
response = self.get_item(
98+
table_name=self.table_name, document_id=document_id, sort_key=sort_key
99+
)
100+
101+
return response
102+
except ClientError as e:
103+
logger.error(e)
104+
raise DocumentReviewException("500, LambdaError.DocumentReviewDB")
105+
92106
def update_document_review_custodian(
93107
self,
94108
patient_documents: list[DocumentUploadReviewReference],
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from enums.lambda_error import LambdaError
2+
from models.document_review import DocumentUploadReviewReference
3+
from services.document_upload_review_service import DocumentUploadReviewService
4+
from utils.audit_logging_setup import LoggingService
5+
from utils.exceptions import OdsErrorException
6+
from utils.lambda_exceptions import DocumentReviewException
7+
8+
logger = LoggingService(__name__)
9+
10+
11+
class ReviewDocumentStatusCheckService:
12+
def __init__(self):
13+
self.review_document_service = DocumentUploadReviewService()
14+
15+
def get_document_review_status(
16+
self, ods_code: str, document_id: str, document_version: int
17+
):
18+
try:
19+
20+
logger.info("Extracting ODS code from request context.")
21+
22+
logger.info("Retrieving document review reference from DynamoDB.")
23+
review_document_reference = self.review_document_service.get_document(
24+
document_id=document_id, version=document_version
25+
)
26+
if not review_document_reference:
27+
logger.info("No document review references found.")
28+
raise DocumentReviewException(404, LambdaError.DocumentReviewNotFound)
29+
30+
logger.info("Checking user is author of review document.")
31+
if not self.user_is_author(ods_code, review_document_reference):
32+
raise DocumentReviewException(403, LambdaError.DocumentReviewForbidden)
33+
34+
return review_document_reference.model_dump_camel_case(
35+
mode="json", include={"id", "version", "review_status"}
36+
)
37+
38+
except OdsErrorException:
39+
logger.info("Failed to obtain ODS code from request context.")
40+
raise DocumentReviewException(401, LambdaError.DocumentReviewMissingODS)
41+
42+
def user_is_author(
43+
self,
44+
user_ods_code: str,
45+
review_document_reference: DocumentUploadReviewReference,
46+
) -> bool:
47+
return user_ods_code == review_document_reference.author

0 commit comments

Comments
 (0)