Skip to content

Commit 34d6156

Browse files
authored
Merge branch 'master' into VED-940-OAS
2 parents b5fc560 + 6237afa commit 34d6156

File tree

13 files changed

+314
-211
lines changed

13 files changed

+314
-211
lines changed

.github/workflows/quality-checks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ jobs:
247247
fi
248248
249249
- name: SonarCloud Scan
250-
uses: SonarSource/sonarqube-scan-action@fd88b7d7ccbaefd23d8f36f73b59db7a3d246602
250+
uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9
251251
env:
252252
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
253253
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

infrastructure/instance/mesh_processor.tf

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ resource "aws_ecr_repository" "mesh_file_converter_lambda_repository" {
3333
module "mesh_processor_docker_image" {
3434
count = var.create_mesh_processor ? 1 : 0
3535

36-
source = "terraform-aws-modules/lambda/aws//modules/docker-build"
37-
version = "8.1.2"
36+
source = "terraform-aws-modules/lambda/aws//modules/docker-build"
37+
version = "8.1.2"
38+
docker_file_path = "./mesh_processor/Dockerfile"
3839

3940
create_ecr_repo = false
4041
ecr_repo = aws_ecr_repository.mesh_file_converter_lambda_repository[0].name
@@ -57,9 +58,10 @@ module "mesh_processor_docker_image" {
5758

5859
platform = "linux/amd64"
5960
use_image_tag = false
60-
source_path = local.mesh_processor_lambda_dir
61+
source_path = abspath("${path.root}/../../lambdas")
6162
triggers = {
62-
dir_sha = local.mesh_processor_lambda_dir_sha
63+
dir_sha = local.mesh_processor_lambda_dir_sha
64+
shared_dir_sha = local.shared_dir_sha
6365
}
6466
}
6567

lambdas/backend/src/controller/fhir_controller.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from service.fhir_service import FhirService, get_service_url
3737

3838
IMMUNIZATION_ENV = os.getenv("IMMUNIZATION_ENV")
39+
IMMUNIZATION_BASE_PATH = os.getenv("IMMUNIZATION_BASE_PATH")
3940

4041

4142
def make_controller(
@@ -51,7 +52,7 @@ def make_controller(
5152

5253
class FhirController:
5354
_IMMUNIZATION_ID_PATTERN = r"^[A-Za-z0-9\-.]{1,64}$"
54-
_API_SERVICE_URL = get_service_url()
55+
_API_SERVICE_URL = get_service_url(IMMUNIZATION_ENV, IMMUNIZATION_BASE_PATH)
5556

5657
def __init__(
5758
self,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Constants for the fhir_service layer"""
2+
3+
DEFAULT_BASE_PATH = "immunisation-fhir-api/FHIR/R4"
4+
PR_ENV_PREFIX = "pr-"

lambdas/backend/src/service/fhir_service.py

Lines changed: 12 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import datetime
33
import logging
44
import os
5-
import urllib.parse
65
import uuid
76
from typing import Any, Optional, cast
87
from uuid import uuid4
@@ -43,11 +42,10 @@
4342
validate_identifiers_match,
4443
validate_resource_versions_match,
4544
)
46-
from controller.constants import IMMUNIZATION_TARGET_LEGACY_KEY_NAME, ImmunizationSearchParameterName
47-
from controller.parameter_parser import PATIENT_IDENTIFIER_SYSTEM
4845
from filter import Filter
4946
from models.errors import UnauthorizedVaxError
5047
from repository.fhir_repository import ImmunizationRepository
48+
from service.search_url_helper import create_url_for_bundle_link, get_service_url
5149

5250
logging.basicConfig(level="INFO")
5351
logger = logging.getLogger()
@@ -59,24 +57,6 @@
5957
IMMUNIZATION_VALIDATOR = ImmunizationValidator()
6058

6159

62-
def get_service_url(
63-
service_env: str = IMMUNIZATION_ENV,
64-
service_base_path: str = IMMUNIZATION_BASE_PATH,
65-
) -> str:
66-
if not service_base_path:
67-
service_base_path = "immunisation-fhir-api/FHIR/R4"
68-
69-
non_prod = ["internal-dev", "int", "sandbox"]
70-
if service_env in non_prod:
71-
subdomain = f"{service_env}."
72-
elif service_env == "prod":
73-
subdomain = ""
74-
else:
75-
subdomain = "internal-dev."
76-
77-
return f"https://{subdomain}api.service.nhs.uk/{service_base_path}"
78-
79-
8060
class FhirService:
8161
_DATA_MISSING_DATE_TIME_ERROR_MSG = (
8262
"Data quality issue - immunisation with ID %s was found containing no occurrenceDateTime"
@@ -98,7 +78,7 @@ def get_immunization_by_identifier(
9878
"""
9979
Get an Immunization by its ID. Returns a FHIR Bundle containing the search results.
10080
"""
101-
base_url = f"{get_service_url()}/Immunization"
81+
base_url = f"{get_service_url(IMMUNIZATION_ENV, IMMUNIZATION_BASE_PATH)}/Immunization"
10282
resource, resource_metadata = self.immunization_repo.get_immunization_by_identifier(identifier)
10383

10484
if not resource:
@@ -251,7 +231,7 @@ def search_immunizations(
251231
BundleEntry(
252232
resource=Immunization.parse_obj(imms),
253233
search=BundleEntrySearch(mode="match"),
254-
fullUrl=f"{get_service_url()}/Immunization/{imms['id']}",
234+
fullUrl=f"{get_service_url(IMMUNIZATION_ENV, IMMUNIZATION_BASE_PATH)}/Immunization/{imms['id']}",
255235
)
256236
for imms in processed_resources
257237
]
@@ -288,7 +268,15 @@ def search_immunizations(
288268
link=[
289269
BundleLink(
290270
relation="self",
291-
url=self.create_url_for_bundle_link(permitted_vacc_types, nhs_number, date_from, date_to, include),
271+
url=create_url_for_bundle_link(
272+
permitted_vacc_types,
273+
nhs_number,
274+
date_from,
275+
date_to,
276+
include,
277+
IMMUNIZATION_ENV,
278+
IMMUNIZATION_BASE_PATH,
279+
),
292280
)
293281
],
294282
total=len(processed_resources),
@@ -408,29 +396,3 @@ def process_patient_for_bundle(patient: dict):
408396
new_patient["id"] = new_patient["identifier"][0].get("value")
409397

410398
return new_patient
411-
412-
@staticmethod
413-
def create_url_for_bundle_link(
414-
immunization_targets: set[str],
415-
patient_nhs_number: str,
416-
date_from: Optional[datetime.date],
417-
date_to: Optional[datetime.date],
418-
include: Optional[str],
419-
) -> str:
420-
"""Creates url for the searchset Bundle Link."""
421-
params = {
422-
# Temporarily maintaining this for backwards compatibility with imms history, but we should remove it
423-
IMMUNIZATION_TARGET_LEGACY_KEY_NAME: ",".join(immunization_targets),
424-
ImmunizationSearchParameterName.IMMUNIZATION_TARGET: ",".join(immunization_targets),
425-
ImmunizationSearchParameterName.PATIENT_IDENTIFIER: f"{PATIENT_IDENTIFIER_SYSTEM}|{patient_nhs_number}",
426-
}
427-
428-
if date_from:
429-
params[ImmunizationSearchParameterName.DATE_FROM] = date_from.isoformat()
430-
if date_to:
431-
params[ImmunizationSearchParameterName.DATE_TO] = date_to.isoformat()
432-
if include:
433-
params[ImmunizationSearchParameterName.INCLUDE] = include
434-
435-
query = urllib.parse.urlencode(params)
436-
return f"{get_service_url()}/Immunization?{query}"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""Module containing helper functions for the constructions of Immunisation FHIR API search URLs"""
2+
3+
import datetime
4+
import urllib.parse
5+
from typing import Optional
6+
7+
from controller.constants import IMMUNIZATION_TARGET_LEGACY_KEY_NAME, ImmunizationSearchParameterName
8+
from controller.parameter_parser import PATIENT_IDENTIFIER_SYSTEM
9+
from service.constants import DEFAULT_BASE_PATH, PR_ENV_PREFIX
10+
11+
12+
def get_service_url(service_env: Optional[str], service_base_path: Optional[str]) -> str:
13+
"""Sets the service URL based on service parameters derived from env vars. PR environments use internal-dev while
14+
we also default to this environment. The only other exceptions are preprod which maps to the Apigee int environment
15+
and prod which does not have a subdomain."""
16+
if not service_base_path:
17+
service_base_path = DEFAULT_BASE_PATH
18+
19+
if service_env is None or is_pr_env(service_env):
20+
subdomain = "internal-dev."
21+
elif service_env == "preprod":
22+
subdomain = "int."
23+
elif service_env == "prod":
24+
subdomain = ""
25+
else:
26+
subdomain = f"{service_env}."
27+
28+
return f"https://{subdomain}api.service.nhs.uk/{service_base_path}"
29+
30+
31+
def is_pr_env(service_env: Optional[str]) -> bool:
32+
return service_env is not None and service_env.startswith(PR_ENV_PREFIX)
33+
34+
35+
def create_url_for_bundle_link(
36+
immunization_targets: set[str],
37+
patient_nhs_number: str,
38+
date_from: Optional[datetime.date],
39+
date_to: Optional[datetime.date],
40+
include: Optional[str],
41+
service_env: Optional[str],
42+
service_base_path: Optional[str],
43+
) -> str:
44+
"""Creates url for the searchset Bundle Link."""
45+
params = {
46+
# Temporarily maintaining this for backwards compatibility with imms history, but we should remove it
47+
IMMUNIZATION_TARGET_LEGACY_KEY_NAME: ",".join(immunization_targets),
48+
ImmunizationSearchParameterName.IMMUNIZATION_TARGET: ",".join(immunization_targets),
49+
ImmunizationSearchParameterName.PATIENT_IDENTIFIER: f"{PATIENT_IDENTIFIER_SYSTEM}|{patient_nhs_number}",
50+
}
51+
52+
if date_from:
53+
params[ImmunizationSearchParameterName.DATE_FROM] = date_from.isoformat()
54+
if date_to:
55+
params[ImmunizationSearchParameterName.DATE_TO] = date_to.isoformat()
56+
if include:
57+
params[ImmunizationSearchParameterName.INCLUDE] = include
58+
59+
query = urllib.parse.urlencode(params)
60+
return f"{get_service_url(service_env, service_base_path)}/Immunization?{query}"

lambdas/backend/tests/service/test_fhir_service.py

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from common.models.immunization_record_metadata import ImmunizationRecordMetadata
2323
from models.errors import UnauthorizedVaxError
2424
from repository.fhir_repository import ImmunizationRepository
25-
from service.fhir_service import FhirService, get_service_url
25+
from service.fhir_service import FhirService
2626
from test_common.testing_utils.generic_utils import load_json_data
2727
from test_common.testing_utils.immunization_utils import (
2828
VALID_NHS_NUMBER,
@@ -46,43 +46,14 @@ def setUp(self):
4646
self.mock_redis_getter = self.redis_getter_patcher.start()
4747
self.logger_info_patcher = patch("logging.Logger.info")
4848
self.mock_logger_info = self.logger_info_patcher.start()
49+
self.imms_env_patcher = patch("service.fhir_service.IMMUNIZATION_ENV", "internal-dev")
50+
self.imms_env_patcher.start()
4951

5052
def tearDown(self):
5153
super().tearDown()
5254
patch.stopall()
5355

5456

55-
class TestServiceUrl(unittest.TestCase):
56-
def setUp(self):
57-
self.logger_info_patcher = patch("logging.Logger.info")
58-
self.mock_logger_info = self.logger_info_patcher.start()
59-
60-
def tearDown(self):
61-
patch.stopall()
62-
63-
def test_get_service_url(self):
64-
"""it should create service url"""
65-
env = "int"
66-
base_path = "immunisation-fhir-api/FHIR/R4"
67-
url = get_service_url(env, base_path)
68-
self.assertEqual(url, f"https://{env}.api.service.nhs.uk/{base_path}")
69-
# default should be internal-dev
70-
env = "it-does-not-exist"
71-
base_path = "immunisation-fhir-api/FHIR/R4"
72-
url = get_service_url(env, base_path)
73-
self.assertEqual(url, f"https://internal-dev.api.service.nhs.uk/{base_path}")
74-
# prod should not have a subdomain
75-
env = "prod"
76-
base_path = "immunisation-fhir-api/FHIR/R4"
77-
url = get_service_url(env, base_path)
78-
self.assertEqual(url, f"https://api.service.nhs.uk/{base_path}")
79-
# any other env should fall back to internal-dev (like pr-xx or per-user)
80-
env = "pr-42"
81-
base_path = "immunisation-fhir-api/FHIR/R4"
82-
url = get_service_url(env, base_path)
83-
self.assertEqual(url, f"https://internal-dev.api.service.nhs.uk/{base_path}")
84-
85-
8657
class TestGetImmunization(TestFhirServiceBase):
8758
"""Tests for FhirService.get_immunization_by_id"""
8859

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Tests for the search_url_helper file"""
2+
3+
import unittest
4+
5+
from service.search_url_helper import get_service_url
6+
7+
8+
class TestServiceUrl(unittest.TestCase):
9+
def test_get_service_url(self):
10+
"""it should create service url"""
11+
test_cases = [
12+
("pr-123", "https://internal-dev.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"),
13+
(None, "https://internal-dev.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"),
14+
("preprod", "https://int.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"),
15+
("prod", "https://api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"),
16+
("ref", "https://ref.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"),
17+
("internal-dev", "https://internal-dev.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"),
18+
("internal-qa", "https://internal-qa.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"),
19+
]
20+
mock_base_path = "immunisation-fhir-api/FHIR/R4"
21+
22+
for mock_env, expected in test_cases:
23+
with self.subTest(mock_env=mock_env, expected=expected):
24+
self.assertEqual(get_service_url(mock_env, mock_base_path), expected)
25+
26+
def test_get_service_url_uses_default_path_when_not_provided(self):
27+
self.assertEqual(
28+
get_service_url(None, None), "https://internal-dev.api.service.nhs.uk/immunisation-fhir-api/FHIR/R4"
29+
)

lambdas/mesh_processor/Dockerfile

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,31 @@ RUN mkdir -p /home/appuser && \
66
echo 'appuser:x:1001:' >> /etc/group && \
77
chown -R 1001:1001 /home/appuser && pip install "poetry~=2.1.2"
88

9+
# Install Poetry dependencies
10+
# Copy mesh_processor Poetry files
11+
COPY ./mesh_processor/poetry.lock ./mesh_processor/pyproject.toml ./mesh_processor/README.md ./
912

10-
11-
COPY poetry.lock pyproject.toml README.md ./
13+
# Install mesh_processor dependencies
14+
WORKDIR /var/task
1215
RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi --no-root --only main
1316

14-
1517
# -----------------------------
1618
FROM base AS build
1719

18-
COPY src .
20+
# Set working directory back to Lambda task root
21+
WORKDIR /var/task
22+
23+
# Copy shared source code
24+
COPY ./shared/src/common ./common
25+
26+
# Copy mesh_processor source code
27+
COPY ./mesh_processor/src .
28+
29+
# Set correct permissions
1930
RUN chmod 644 $(find . -type f) && chmod 755 $(find . -type d)
31+
2032
# Switch to the non-root user for running the container
2133
USER 1001:1001
34+
35+
# Set the Lambda handler
2236
CMD ["converter.lambda_handler"]

lambdas/mesh_processor/pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ version = "0.1.0"
44
description = ""
55
authors = ["Your Name <[email protected]>"]
66
readme = "README.md"
7-
packages = [{include = "src"}]
7+
packages = [
8+
{include = "src"},
9+
{include = "common", from = "../shared/src"}
10+
]
811

912
[tool.poetry.dependencies]
1013
python = "~3.11"

0 commit comments

Comments
 (0)