Skip to content

Commit 30709fd

Browse files
committed
Merge branch 'VED-887-Slack-alerting-API-errors' of https://github.com/NHSDigital/immunisation-fhir-api into VED-887-Slack-alerting-API-errors
2 parents 749ac3b + 23fcef4 commit 30709fd

File tree

17 files changed

+2861
-3614
lines changed

17 files changed

+2861
-3614
lines changed

.github/workflows/deploy-backend.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ jobs:
8383
run: make plan-ci
8484

8585
- name: Save Terraform Plan
86-
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4
86+
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
8787
with:
8888
name: ${{ env.ENVIRONMENT }}-${{ env.SUB_ENVIRONMENT }}-tfplan
8989
path: infrastructure/instance/tfplan

.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/environments/prod/blue/variables.tfvars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pds_environment = "prod"
55
error_alarm_notifications_enabled = true
66
create_mesh_processor = true
77
has_sub_environment_scope = false
8+
dspp_kms_key_alias = "nhsd-dspp-core-prod-extended-attributes-gdp-key"

infrastructure/instance/environments/prod/green/variables.tfvars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pds_environment = "prod"
55
error_alarm_notifications_enabled = true
66
create_mesh_processor = true
77
has_sub_environment_scope = false
8+
dspp_kms_key_alias = "nhsd-dspp-core-prod-extended-attributes-gdp-key"

infrastructure/instance/file_name_processor.tf

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,44 @@ resource "aws_iam_policy" "filenameprocessor_dynamo_access_policy" {
253253
})
254254
}
255255

256+
# Kms policy setup on filenameprocessor lambda for dps cross account bucket access
257+
resource "aws_iam_policy" "filenameprocessor_dps_extended_attribute_kms_policy" {
258+
name = "${local.short_prefix}-filenameproc-dps-kms-policy"
259+
description = "Allow Lambda to use DPS KMS key for SSE-KMS encrypted S3 bucket access"
260+
261+
policy = jsonencode({
262+
Version = "2012-10-17"
263+
Statement = [
264+
{
265+
Effect = "Allow",
266+
Action = [
267+
"kms:Decrypt",
268+
"kms:GenerateDataKey",
269+
"kms:DescribeKey"
270+
],
271+
Resource = "arn:aws:kms:eu-west-2:${var.dspp_core_account_id}:key/*",
272+
"Condition" = {
273+
"ForAnyValue:StringEquals" = {
274+
"kms:ResourceAliases" = "alias/${var.dspp_kms_key_alias}"
275+
}
276+
}
277+
}
278+
]
279+
})
280+
}
256281

257282
# Attach the execution policy to the Lambda role
258283
resource "aws_iam_role_policy_attachment" "filenameprocessor_lambda_exec_policy_attachment" {
259284
role = aws_iam_role.filenameprocessor_lambda_exec_role.name
260285
policy_arn = aws_iam_policy.filenameprocessor_lambda_exec_policy.arn
261286
}
262287

288+
#Attach the dps kms policy to the Lambda role
289+
resource "aws_iam_role_policy_attachment" "filenameprocessor_lambda_dps_kms_ea_policy_attachment" {
290+
role = aws_iam_role.filenameprocessor_lambda_exec_role.name
291+
policy_arn = aws_iam_policy.filenameprocessor_dps_extended_attribute_kms_policy.arn
292+
}
293+
263294
# Attach the SQS policy to the Lambda role
264295
resource "aws_iam_role_policy_attachment" "filenameprocessor_lambda_sqs_policy_attachment" {
265296
role = aws_iam_role.filenameprocessor_lambda_exec_role.name

infrastructure/instance/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ variable "csoc_account_id" {
1010
default = "693466633220"
1111
}
1212

13+
variable "dspp_kms_key_alias" {
14+
description = "Alias name of the DPS KMS key allowed for SSE-KMS encryption"
15+
type = string
16+
default = "nhsd-dspp-core-ref-extended-attributes-gdp-key"
17+
}
18+
1319
variable "create_mesh_processor" {
1420
default = false
1521
}

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}"

0 commit comments

Comments
 (0)