Skip to content

Commit 13a17db

Browse files
committed
Merge remote branch into VED-789-Schema-Validation
2 parents 16edf3c + 2d47be5 commit 13a17db

File tree

105 files changed

+6451
-5745
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+6451
-5745
lines changed

.github/dependabot.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ updates:
88
- package-ecosystem: "docker"
99
directories:
1010
- "/lambdas/ack_backend"
11-
- "/delta_backend"
11+
- "/lambdas/delta_backend"
1212
- "/filenameprocessor"
1313
- "/infrastructure/grafana/non-prod/docker"
1414
- "/mesh_processor"
@@ -50,13 +50,13 @@ updates:
5050
- "/"
5151
- "/backend"
5252
- "/batch_processor_filter"
53-
- "/delta_backend"
5453
- "/tests/e2e"
5554
- "/tests/e2e_batch"
5655
- "/filenameprocessor"
5756
- "/mesh_processor"
5857
- "/recordprocessor"
5958
- "/lambdas/ack_backend"
59+
- "/lambdas/delta_backend"
6060
- "/lambdas/redis_sync"
6161
- "/lambdas/id_sync"
6262
- "/lambdas/mns_subscription"

.github/workflows/quality-checks.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
steps:
2020
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
2121

22-
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444
22+
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903
2323
with:
2424
node-version: "23.11.0"
2525
cache: "npm"
@@ -140,17 +140,6 @@ jobs:
140140
poetry run coverage run -m unittest discover -p "*batch*.py" || echo "recordforwarder tests failed" >> ../failed_tests.txt
141141
poetry run coverage xml -o ../recordforwarder-coverage.xml
142142
143-
- name: Run unittest with coverage-delta
144-
working-directory: delta_backend
145-
id: delta
146-
env:
147-
PYTHONPATH: delta_backend/src:delta_backend/tests
148-
continue-on-error: true
149-
run: |
150-
poetry install
151-
poetry run coverage run -m unittest discover || echo "delta tests failed" >> ../failed_tests.txt
152-
poetry run coverage xml -o ../delta-coverage.xml
153-
154143
- name: Run unittest with coverage-fhir-api
155144
working-directory: backend
156145
env:
@@ -182,6 +171,17 @@ jobs:
182171
poetry run coverage run --source=src -m unittest discover || echo "ack-lambda tests failed" >> ../../failed_tests.txt
183172
poetry run coverage xml -o ../../ack-lambda-coverage.xml
184173
174+
- name: Run unittest with coverage-delta
175+
working-directory: lambdas/delta_backend
176+
id: delta
177+
env:
178+
PYTHONPATH: ${{ env.LAMBDA_PATH }}/delta_backend/src:${{ env.LAMBDA_PATH }}/delta_backend/tests:${{ env.SHARED_PATH }}/src
179+
continue-on-error: true
180+
run: |
181+
poetry install
182+
poetry run coverage run -m unittest discover || echo "delta tests failed" >> ../../failed_tests.txt
183+
poetry run coverage xml -o ../../delta-coverage.xml
184+
185185
- name: Run unittest with coverage-mns-subscription
186186
working-directory: lambdas/mns_subscription
187187
id: mns_subscription

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
SHELL=/usr/bin/env bash -euo pipefail
22

3-
PYTHON_PROJECT_DIRS_WITH_UNIT_TESTS = backend batch_processor_filter delta_backend filenameprocessor mesh_processor recordprocessor lambdas/ack_backend lambdas/redis_sync lambdas/id_sync lambdas/mns_subscription lambdas/shared
3+
PYTHON_PROJECT_DIRS_WITH_UNIT_TESTS = backend batch_processor_filter filenameprocessor mesh_processor recordprocessor lambdas/ack_backend lambdas/delta_backend lambdas/redis_sync lambdas/id_sync lambdas/mns_subscription lambdas/shared
44
PYTHON_PROJECT_DIRS = tests/e2e tests/e2e_batch quality_checks $(PYTHON_PROJECT_DIRS_WITH_UNIT_TESTS)
55

66
.PHONY: install lint format format-check clean publish build-proxy release initialise-all-python-venvs update-all-python-dependencies run-all-python-unit-tests build-all-docker-images

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,17 @@ Steps:
130130
```
131131
132132
7. Install poetry
133+
133134
```
134135
pip install poetry
135136
```
136137
138+
8. Install pre-commit hooks. Ensure you have installed nodejs at the same version or later as per .tool-versions and
139+
then, from the repo root, run:
140+
```
141+
npm install
142+
```
143+
137144
### Setting up a virtual environment with poetry
138145
139146
The steps below must be performed in each Lambda function folder and e2e folder to ensure the environment is correctly configured.

backend/poetry.lock

Lines changed: 412 additions & 359 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/src/controller/aws_apig_response_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Optional
55

66

7-
def create_response(status_code: int, body: Optional[dict | str] = None, headers: Optional[dict] = None):
7+
def create_response(status_code: int, body: Optional[dict | str] = None, headers: Optional[dict] = None) -> dict:
88
"""Creates response body as per Lambda -> API Gateway proxy integration"""
99
if body is not None:
1010
if isinstance(body, dict):

backend/src/controller/fhir_api_exception_handler.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from controller.aws_apig_response_utils import create_response
1010
from models.errors import (
1111
Code,
12+
InvalidImmunizationId,
1213
ResourceNotFoundError,
1314
Severity,
1415
UnauthorizedError,
@@ -17,6 +18,7 @@
1718
)
1819

1920
_CUSTOM_EXCEPTION_TO_STATUS_MAP: dict[Type[Exception], int] = {
21+
InvalidImmunizationId: 400,
2022
UnauthorizedError: 403,
2123
UnauthorizedVaxError: 403,
2224
ResourceNotFoundError: 404,

backend/src/controller/fhir_controller.py

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
from models.errors import (
2020
Code,
2121
IdentifierDuplicationError,
22+
InvalidImmunizationId,
2223
ParameterException,
23-
ResourceNotFoundError,
2424
Severity,
2525
UnauthorizedError,
2626
UnauthorizedVaxError,
@@ -96,8 +96,8 @@ def get_immunization_by_identifier(self, aws_event) -> dict:
9696
def get_immunization_by_id(self, aws_event: APIGatewayProxyEventV1) -> dict:
9797
imms_id = get_path_parameter(aws_event, "id")
9898

99-
if id_error := self._validate_id(imms_id):
100-
return create_response(400, id_error)
99+
if not self._is_valid_immunization_id(imms_id):
100+
raise InvalidImmunizationId()
101101

102102
supplier_system = get_supplier_system_header(aws_event)
103103

@@ -157,10 +157,20 @@ def update_immunization(self, aws_event):
157157

158158
supplier_system = self._identify_supplier_system(aws_event)
159159

160-
# Validate the imms id - start
161-
if id_error := self._validate_id(imms_id):
162-
return create_response(400, json.dumps(id_error))
163-
# Validate the imms id - end
160+
# Refactor to raise InvalidImmunizationId when working on VED-747
161+
if not self._is_valid_immunization_id(imms_id):
162+
return create_response(
163+
400,
164+
json.dumps(
165+
create_operation_outcome(
166+
resource_id=str(uuid.uuid4()),
167+
severity=Severity.error,
168+
code=Code.invalid,
169+
diagnostics="Validation errors: the provided event ID is either missing or not in the expected "
170+
"format.",
171+
)
172+
),
173+
)
164174

165175
# Validate the body of the request - start
166176
try:
@@ -320,31 +330,18 @@ def update_immunization(self, aws_event):
320330
except UnauthorizedVaxError as unauthorized:
321331
return create_response(403, unauthorized.to_operation_outcome())
322332

323-
def delete_immunization(self, aws_event):
324-
try:
325-
if aws_event.get("headers"):
326-
imms_id = aws_event["pathParameters"]["id"]
327-
else:
328-
raise UnauthorizedError()
329-
except UnauthorizedError as unauthorized:
330-
return create_response(403, unauthorized.to_operation_outcome())
333+
@fhir_api_exception_handler
334+
def delete_immunization(self, aws_event: APIGatewayProxyEventV1) -> dict:
335+
imms_id = get_path_parameter(aws_event, "id")
331336

332-
# Validate the imms id
333-
if id_error := self._validate_id(imms_id):
334-
return create_response(400, json.dumps(id_error))
337+
if not self._is_valid_immunization_id(imms_id):
338+
raise InvalidImmunizationId()
335339

336-
supplier_system = self._identify_supplier_system(aws_event)
340+
supplier_system = get_supplier_system_header(aws_event)
337341

338-
try:
339-
self.fhir_service.delete_immunization(imms_id, supplier_system)
340-
return create_response(204)
342+
self.fhir_service.delete_immunization(imms_id, supplier_system)
341343

342-
except ResourceNotFoundError as not_found:
343-
return create_response(404, not_found.to_operation_outcome())
344-
except UnhandledResponseError as unhandled_error:
345-
return create_response(500, unhandled_error.to_operation_outcome())
346-
except UnauthorizedVaxError as unauthorized:
347-
return create_response(403, unauthorized.to_operation_outcome())
344+
return create_response(204)
348345

349346
def search_immunizations(self, aws_event: APIGatewayProxyEventV1) -> dict:
350347
try:
@@ -406,17 +403,9 @@ def search_immunizations(self, aws_event: APIGatewayProxyEventV1) -> dict:
406403
result_json_dict["total"] = 0
407404
return create_response(200, json.dumps(result_json_dict))
408405

409-
def _validate_id(self, _id: str) -> Optional[dict]:
410-
if not re.match(self.immunization_id_pattern, _id):
411-
msg = "Validation errors: the provided event ID is either missing or not in the expected format."
412-
return create_operation_outcome(
413-
resource_id=str(uuid.uuid4()),
414-
severity=Severity.error,
415-
code=Code.invalid,
416-
diagnostics=msg,
417-
)
418-
419-
return None
406+
def _is_valid_immunization_id(self, immunization_id: str) -> bool:
407+
"""Validates if the given unique Immunization ID is valid."""
408+
return False if not re.match(self.immunization_id_pattern, immunization_id) else True
420409

421410
def _validate_identifier_system(self, _id: str, _elements: str) -> Optional[dict]:
422411
if not _id:

backend/src/delete_imms_handler.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import argparse
22
import logging
33
import pprint
4-
import uuid
54

6-
from constants import GENERIC_SERVER_ERROR_DIAGNOSTICS_MESSAGE
7-
from controller.aws_apig_response_utils import create_response
85
from controller.fhir_controller import FhirController, make_controller
96
from log_structure import function_info
10-
from models.errors import Code, Severity, create_operation_outcome
117

128
logging.basicConfig(level="INFO")
139
logger = logging.getLogger()
@@ -19,17 +15,7 @@ def delete_imms_handler(event, _context):
1915

2016

2117
def delete_immunization(event, controller: FhirController):
22-
try:
23-
return controller.delete_immunization(event)
24-
except Exception: # pylint: disable = broad-exception-caught
25-
logger.exception("Unhandled exception")
26-
exp_error = create_operation_outcome(
27-
resource_id=str(uuid.uuid4()),
28-
severity=Severity.error,
29-
code=Code.server_error,
30-
diagnostics=GENERIC_SERVER_ERROR_DIAGNOSTICS_MESSAGE,
31-
)
32-
return create_response(500, exp_error)
18+
return controller.delete_immunization(event)
3319

3420

3521
if __name__ == "__main__":

backend/src/models/errors.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,19 @@ def to_operation_outcome(self) -> dict:
127127
pass
128128

129129

130+
@dataclass
131+
class InvalidImmunizationId(ValidationError):
132+
"""Use this when the unique Immunization ID is invalid"""
133+
134+
def to_operation_outcome(self) -> dict:
135+
return create_operation_outcome(
136+
resource_id=str(uuid.uuid4()),
137+
severity=Severity.error,
138+
code=Code.invalid,
139+
diagnostics="Validation errors: the provided event ID is either missing or not in the expected format.",
140+
)
141+
142+
130143
@dataclass
131144
class InvalidPatientId(ValidationError):
132145
"""Use this when NHS Number is invalid or doesn't exist"""

0 commit comments

Comments
 (0)