Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
40b42cf
setup
Akol125 Jun 24, 2025
b4b540d
replace vaccineTypePermissions
Akol125 Jun 24, 2025
eed4049
remove imms-batch-app
Akol125 Jun 24, 2025
f06eeeb
test for new perms and imms batch removel
Akol125 Jun 24, 2025
029d78a
test still
Akol125 Jun 25, 2025
b4eb8d4
test3
Akol125 Jun 25, 2025
5969962
test_for_search
Akol125 Jun 25, 2025
e1ed4e1
test for update
Akol125 Jun 25, 2025
fd7b5bb
test for update2
Akol125 Jun 26, 2025
f0f9e77
test suite complete
Akol125 Jun 26, 2025
d81473c
Merge branch 'master' into VED-26-permissions-api
Akol125 Jun 26, 2025
49fc7c7
remove perm config in test
Akol125 Jun 26, 2025
e2c3f11
poetry
Akol125 Jun 26, 2025
25c0dd0
Merge branch 'master' into VED-26-permissions-api
Akol125 Jun 26, 2025
6444801
remove unused files
Akol125 Jun 26, 2025
57e8cb1
Merge branch 'VED-26-permissions-api' of https://github.com/NHSDigita…
Akol125 Jun 26, 2025
a402488
env vars
Akol125 Jun 26, 2025
4107f35
VED-26: Attach endpoint Lambda functions to the VPC so they can conne…
mfjarvis Jun 26, 2025
99cf3a7
VED-26: Give endpoint Lambdas the required permissions to attach to t…
mfjarvis Jun 26, 2025
fef9552
VED-26: Try creating a fresh client for every invocation.
mfjarvis Jun 27, 2025
41cb0b1
VED-26: Src again :(
mfjarvis Jun 27, 2025
f794777
VED-26: Revert Redis client change. Temporarily skip PDS callout.
mfjarvis Jun 27, 2025
8208760
VED-26: Revert PDS callout removal.
mfjarvis Jun 27, 2025
025104f
integrating the new supplier permissions
Akol125 Jun 28, 2025
829e1cd
Merge branch 'VED-26-permissions-api' of https://github.com/NHSDigita…
Akol125 Jun 28, 2025
2afa02c
clean up
Akol125 Jun 30, 2025
0fba593
clean up2
Akol125 Jun 30, 2025
152f621
test suite
Akol125 Jul 1, 2025
ac7fcd0
lint issues
Akol125 Jul 1, 2025
4fe0ae7
refactor control flow and duplication error
Akol125 Jul 1, 2025
f3586c6
e2e fix
Akol125 Jul 1, 2025
c6f4b85
e2e lint
Akol125 Jul 1, 2025
7c15343
setup
Akol125 Jun 24, 2025
6bff469
replace vaccineTypePermissions
Akol125 Jun 24, 2025
bb3f8f9
remove imms-batch-app
Akol125 Jun 24, 2025
fbb7dac
test for new perms and imms batch removel
Akol125 Jun 24, 2025
d9ecd63
test still
Akol125 Jun 25, 2025
46bfa26
test3
Akol125 Jun 25, 2025
a43e4e1
test_for_search
Akol125 Jun 25, 2025
5a0d56f
test for update
Akol125 Jun 25, 2025
81b0acb
test for update2
Akol125 Jun 26, 2025
8db4a49
test suite complete
Akol125 Jun 26, 2025
bff3c84
remove perm config in test
Akol125 Jun 26, 2025
02688c7
remove unused files
Akol125 Jun 26, 2025
a17b018
env vars
Akol125 Jun 26, 2025
41168f8
VED-26: Attach endpoint Lambda functions to the VPC so they can conne…
mfjarvis Jun 26, 2025
7735ff2
VED-26: Give endpoint Lambdas the required permissions to attach to t…
mfjarvis Jun 26, 2025
05f131c
VED-382: Use configurable disease mappings in backend.
mfjarvis Jun 27, 2025
4200ef6
VED-382: Revert test change.
mfjarvis Jun 27, 2025
10337fc
tests ok
nhsdevws Jun 27, 2025
9a72960
tests
nhsdevws Jun 27, 2025
9af164a
debug
nhsdevws Jun 30, 2025
a220b64
67 fails
nhsdevws Jul 1, 2025
f6de61a
search imms & post validator
nhsdevws Jul 1, 2025
dd92d51
20 errors
nhsdevws Jul 1, 2025
4fdd789
18 errors
nhsdevws Jul 1, 2025
b36a820
17 errors
nhsdevws Jul 1, 2025
2876973
18 errors
nhsdevws Jul 1, 2025
f544028
15 errors
nhsdevws Jul 1, 2025
698a7ca
14 errors
nhsdevws Jul 1, 2025
a0c6301
tests pass
nhsdevws Jul 1, 2025
79e32d4
path & tidy
nhsdevws Jul 1, 2025
3d50cfa
remock controller
nhsdevws Jul 2, 2025
adaf028
clean up test_utils
nhsdevws Jul 2, 2025
cbdd8be
remocked batch service
nhsdevws Jul 2, 2025
4f756cc
remock test imm post valid
nhsdevws Jul 2, 2025
9056a2d
remock test_fhir_repo
nhsdevws Jul 2, 2025
f4a57b9
remock
nhsdevws Jul 2, 2025
022f527
tidy
nhsdevws Jul 2, 2025
e48863d
remock & tidy
nhsdevws Jul 2, 2025
36278b3
tidy
nhsdevws Jul 2, 2025
36de023
tidy
nhsdevws Jul 2, 2025
b246f81
tidy
nhsdevws Jul 2, 2025
0df8b14
tidy
nhsdevws Jul 2, 2025
d533221
tidy
nhsdevws Jul 2, 2025
128e61e
tidy
nhsdevws Jul 2, 2025
59c0b25
refactor some logic from review sections
Akol125 Jul 3, 2025
0a7748a
refactor from review section
Akol125 Jul 3, 2025
c10fccd
lint issues
Akol125 Jul 3, 2025
e49362c
Merge remote-tracking branch 'origin/VED-26-permissions-api' into VED…
nhsdevws Jul 3, 2025
dfb2314
update
Akol125 Jul 3, 2025
4a08db9
Resolve merge tests
nhsdevws Jul 3, 2025
377e48c
tidy
nhsdevws Jul 3, 2025
3d175f0
Merge remote-tracking branch 'origin/VED-26-permissions-api' into VED…
nhsdevws Jul 3, 2025
108e72b
CRD
nhsdevws Jul 3, 2025
3a6a304
Merge branch 'master' into VED-382-backend-configurable-disease-mapping
nhsdevws Jul 4, 2025
c09f878
comments addressed
nhsdevws Jul 4, 2025
e01e798
Merge branch 'master' into VED-382-backend-configurable-disease-mapping
nhsdevws Jul 7, 2025
1fe630a
test sqs
nhsdevws Jul 7, 2025
9aa584e
e2e test
nhsdevws Jul 7, 2025
148880e
"Skipping test for now"
nhsdevws Jul 7, 2025
0233b38
comments
nhsdevws Jul 7, 2025
091a81e
test_convert_disease_codes_to_vaccine_type
nhsdevws Jul 7, 2025
5b03767
create_immunization_test_logic
nhsdevws Jul 7, 2025
70e3f90
remove redundant redis_patcher.stop()
nhsdevws Jul 7, 2025
4d1893a
mock hkeys
nhsdevws Jul 7, 2025
9d7d4c8
remove mappings
nhsdevws Jul 7, 2025
bca9c57
tidy
nhsdevws Jul 7, 2025
ef2afe1
break mock for test_post_validation_failed_get_by_all
nhsdevws Jul 7, 2025
e217a24
break in 2 test_post_validation_failed_get_by_all
nhsdevws Jul 7, 2025
46e096a
break up test_post_validation_failed
nhsdevws Jul 7, 2025
edd29e8
redis hget
nhsdevws Jul 7, 2025
07b1a15
refactor test_post_validation_failed_get
nhsdevws Jul 7, 2025
444d287
remove suplouous methods
nhsdevws Jul 7, 2025
be15970
supluous teardowns
nhsdevws Jul 7, 2025
6b96bde
teardown gone
nhsdevws Jul 7, 2025
d28c89a
super().tearDown() removed
nhsdevws Jul 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
- name: Run unittest with recordforwarder-coverage
working-directory: backend
id: recordforwarder
env:
PYTHONPATH: ${{ github.workspace }}/backend/src:${{ github.workspace }}/backend/tests
continue-on-error: true
run: |
poetry env use 3.11
Expand Down Expand Up @@ -90,6 +92,8 @@ jobs:

- name: Run unittest with coverage-fhir-api
working-directory: backend
env:
PYTHONPATH: ${{ github.workspace }}/backend/src:${{ github.workspace }}/backend/tests
id: fhirapi
continue-on-error: true
run: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ openapi.json
!**/.vscode/settings.json.default

devtools/volume/
backend/tests/.coverage
2 changes: 1 addition & 1 deletion ack_backend/tests/test_splunk_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from tests.utils_for_ack_backend_tests.utils_for_ack_backend_tests import generate_event

with patch.dict("os.environ", MOCK_ENVIRONMENT_DICT):
from src.ack_processor import lambda_handler
from ack_processor import lambda_handler

s3_client = boto3_client("s3")

Expand Down
2 changes: 1 addition & 1 deletion backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ package: build
docker run --rm -v $(shell pwd)/build:/build imms-lambda-build

test:
python -m unittest
@PYTHONPATH=src:tests python -m unittest

.PHONY: build package test
57 changes: 0 additions & 57 deletions backend/src/mappings.py

This file was deleted.

4 changes: 4 additions & 0 deletions backend/src/models/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ class Constants:
}

ALLOWED_CONTAINED_RESOURCES = {"Practitioner", "Patient"}

SUPPLIER_PERMISSIONS_KEY = "supplier_permissions"
VACCINE_TYPE_TO_DISEASES_HASH_KEY = "vacc_to_diseases"
DISEASES_TO_VACCINE_TYPE_HASH_KEY = "diseases_to_vacc"
9 changes: 1 addition & 8 deletions backend/src/models/fhir_immunization_post_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,8 @@ def validate(self):
# Initialise the mandation validation functions
mandation_functions = MandationFunctions(self.imms, self.vaccine_type)

# Identify whether to use the vaccine_type_agnostic, or else a vaccine type specific, validation rule set
validation_set_to_use = (
"vaccine_type_agnostic"
if self.vaccine_type in ValidationSets.vaccine_types_which_use_agnostic_set
else self.vaccine_type.lower()
)

# Obtain the relevant validation set
validation_set = getattr(ValidationSets, validation_set_to_use)
validation_set = getattr(ValidationSets, self.vaccine_type.lower(), ValidationSets.vaccine_type_agnostic)

# Create an instance of FieldLocations and set dynamic fields
field_locations = FieldLocations()
Expand Down
18 changes: 8 additions & 10 deletions backend/src/models/utils/validation_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import json


from typing import Union
from mappings import vaccine_type_mappings
from .generic_utils import create_diagnostics_error
from base_utils.base_utils import obtain_field_location
from models.obtain_field_value import ObtainFieldValue
from models.field_names import FieldNames
from models.errors import MandatoryError
from constants import Urls
from models.constants import Constants
from clients import redis_client


def get_target_disease_codes(immunization: dict):
Expand Down Expand Up @@ -52,17 +52,15 @@ def convert_disease_codes_to_vaccine_type(disease_codes_input: list) -> Union[st
Takes a list of disease codes and returns the corresponding vaccine type if found,
otherwise raises a value error
"""
try:
return next(
vaccine_type
for disease_codes, vaccine_type in vaccine_type_mappings
if sorted(disease_codes_input) == disease_codes
)
except Exception as e:
key = ":".join(sorted(disease_codes_input))
vaccine_type = redis_client.hget(Constants.DISEASES_TO_VACCINE_TYPE_HASH_KEY, key)

if not vaccine_type:
raise ValueError(
f"Validation errors: protocolApplied[0].targetDisease[*].coding[?(@.system=='http://snomed.info/sct')].code - "
f"{disease_codes_input} is not a valid combination of disease codes for this service"
) from e
)
return vaccine_type


def get_vaccine_type(immunization: dict):
Expand Down
5 changes: 1 addition & 4 deletions backend/src/models/validation_sets.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Validation sets for each vaccine type"""

from models.mandation_functions import MandationRules
from mappings import VaccineTypes


class ValidationSets:
Expand All @@ -11,7 +10,7 @@ class ValidationSets:

TO ADD A NEW VACCINE TYPE:
* If the mandation rules for the new vaccine type are identical to the vaccine_type_agnostic rules, then
add the vaccine type to the vaccine_types_which_use_agnostic_set list.
no action is required.
* If some of the mandation rules for the new vaccine type are different than the agnostic rules, then create a
new validation set, with the same name as the vaccine type. This can be done by copying and pasting the
vaccine_type_agnostic set, and amending any rules as required.
Expand All @@ -21,8 +20,6 @@ class ValidationSets:
def __init__(self) -> None:
pass

vaccine_types_which_use_agnostic_set = [VaccineTypes.covid_19, VaccineTypes.flu, VaccineTypes.hpv, VaccineTypes.mmr,VaccineTypes.rsv]

vaccine_type_agnostic = {
"patient_identifier_value": MandationRules.required,
"patient_name_given": MandationRules.mandatory,
Expand Down
9 changes: 6 additions & 3 deletions backend/src/parameter_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
from typing import Optional
from urllib.parse import parse_qs, urlencode, quote

from mappings import VaccineTypes
from clients import redis_client
from models.errors import ParameterException
from models.constants import Constants

ParamValue = list[str]
ParamContainer = dict[str, ParamValue]
Expand Down Expand Up @@ -107,9 +108,11 @@ def process_search_params(params: ParamContainer) -> SearchParams:
vaccine_type is not None]
if len(vaccine_types) < 1:
raise ParameterException(f"Search parameter {immunization_target_key} must have one or more values.")
if any([x not in VaccineTypes().all for x in vaccine_types]):

valid_vaccine_types = redis_client.hkeys(Constants.VACCINE_TYPE_TO_DISEASES_HASH_KEY)
if any(x not in valid_vaccine_types for x in vaccine_types):
raise ParameterException(
f"immunization-target must be one or more of the following: {','.join(VaccineTypes().all)}")
f"immunization-target must be one or more of the following: {', '.join(valid_vaccine_types)}")

# date.from
date_froms = params.get(date_from_key, [])
Expand Down
4 changes: 0 additions & 4 deletions backend/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +0,0 @@
import os
import sys

sys.path.append(f"{os.path.dirname(os.path.abspath(__file__))}/../src")
12 changes: 9 additions & 3 deletions backend/tests/test_fhir_batch_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from models.errors import IdentifierDuplicationError, ResourceNotFoundError, UnhandledResponseError, ResourceFoundError
from fhir_batch_repository import ImmunizationBatchRepository, create_table
from tests.utils.immunization_utils import create_covid_19_immunization_dict

imms_id = str(uuid4())


Expand All @@ -28,6 +29,12 @@ def setUp(self):
self.table.query = MagicMock(return_value={})
self.immunization = create_covid_19_immunization_dict(imms_id)
self.table.update_item = MagicMock(return_value = {"ResponseMetadata": {"HTTPStatusCode": 200}})
self.redis_patcher = patch("models.utils.validation_utils.redis_client")
self.mock_redis_client = self.redis_patcher.start()

def tearDown(self):
self.redis_patcher.stop()
return super().tearDown()

class TestCreateImmunization(TestImmunizationBatchRepository):

Expand All @@ -41,6 +48,7 @@ def modify_immunization(self, remove_nhs):

def create_immunization_test_logic(self, is_present, remove_nhs):
"""Common logic for testing immunization creation."""
self.mock_redis_client.hget.side_effect = ['COVID19']
self.modify_immunization(remove_nhs)

self.repository.create_immunization(
Expand All @@ -65,18 +73,16 @@ def create_immunization_test_logic(self, is_present, remove_nhs):

def test_create_immunization_with_nhs_number(self):
"""Test creating Immunization with NHS number."""

self.create_immunization_test_logic(is_present=True, remove_nhs=False)

def test_create_immunization_without_nhs_number(self):
"""Test creating Immunization without NHS number."""

self.create_immunization_test_logic(is_present=False, remove_nhs=True)


def test_create_immunization_duplicate(self):
"""it should not create Immunization since the request is duplicate"""

self.table.query = MagicMock(return_value={
"id": imms_id,
"identifier": [{"system": "test-system", "value": "12345"}],
Expand Down
44 changes: 41 additions & 3 deletions backend/tests/test_fhir_batch_service.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
import unittest
import uuid
from copy import deepcopy
from unittest.mock import Mock, create_autospec
from unittest.mock import Mock, create_autospec, patch
from tests.utils.immunization_utils import create_covid_19_immunization_dict_no_id
from models.errors import CustomValidationError
from models.fhir_immunization import ImmunizationValidator
from fhir_batch_repository import ImmunizationBatchRepository
from fhir_batch_service import ImmunizationBatchService

class TestCreateImmunizationBatchService(unittest.TestCase):

class TestFhirBatchServiceBase(unittest.TestCase):
"""Base class for all tests to set up common fixtures"""

def setUp(self):
super().setUp()
self.redis_patcher = patch("models.utils.validation_utils.redis_client")
self.mock_redis_client = self.redis_patcher.start()
self.logger_info_patcher = patch("logging.Logger.info")
self.mock_logger_info = self.logger_info_patcher.start()

def tearDown(self):
self.redis_patcher.stop()
self.logger_info_patcher.stop()
super().tearDown()


class TestCreateImmunizationBatchService(TestFhirBatchServiceBase):

def setUp(self):
super().setUp()
self.mock_repo = create_autospec(ImmunizationBatchRepository)
self.mock_validator = create_autospec(ImmunizationValidator)
self.mock_table = Mock()
Expand All @@ -19,6 +37,14 @@ def setUp(self):
self.mock_repo, ImmunizationValidator(add_post_validators=False)
)

def tearDown(self):
super().tearDown()
self.mock_repo.reset_mock()
self.mock_validator.reset_mock()
self.mock_table.reset_mock()
self.service = None
self.pre_validate_fhir_service = None

def test_create_immunization_valid(self):
"""it should create Immunization and return imms id location"""

Expand Down Expand Up @@ -57,6 +83,7 @@ def test_create_immunization_post_validation_error(self):
bad_target_disease_imms = deepcopy(valid_imms)
bad_target_disease_imms["protocolApplied"][0]["targetDisease"][0]["coding"][0]["code"] = "bad-code"
expected_msg = "protocolApplied[0].targetDisease[*].coding[?(@.system=='http://snomed.info/sct')].code - ['bad-code'] is not a valid combination of disease codes for this service"
self.mock_redis_client.hget.return_value = None # Reset mock for invalid cases
with self.assertRaises(CustomValidationError) as error:
self.pre_validate_fhir_service.create_immunization(
immunization=bad_target_disease_imms,
Expand All @@ -69,9 +96,10 @@ def test_create_immunization_post_validation_error(self):
self.mock_repo.create_immunization.assert_not_called()


class TestUpdateImmunizationBatchService(unittest.TestCase):
class TestUpdateImmunizationBatchService(TestFhirBatchServiceBase):

def setUp(self):
super().setUp()
self.mock_repo = create_autospec(ImmunizationBatchRepository)
self.mock_validator = create_autospec(ImmunizationValidator)
self.mock_table = Mock()
Expand All @@ -80,6 +108,14 @@ def setUp(self):
self.mock_repo, ImmunizationValidator(add_post_validators=False)
)

def tearDown(self):
super().tearDown()
self.mock_repo.reset_mock()
self.mock_validator.reset_mock()
self.mock_table.reset_mock()
self.service = None
self.pre_validate_fhir_service = None

def test_update_immunization_valid(self):
"""it should update Immunization and return imms id"""

Expand Down Expand Up @@ -114,6 +150,8 @@ def test_update_immunization_pre_validation_error(self):
def test_update_immunization_post_validation_error(self):
"""it should return error since it got failed in initial validation"""

self.mock_redis_client.hget.return_value = None # Reset mock for invalid cases

valid_imms = create_covid_19_immunization_dict_no_id()
bad_target_disease_imms = deepcopy(valid_imms)
bad_target_disease_imms["protocolApplied"][0]["targetDisease"][0]["coding"][0]["code"] = "bad-code"
Expand Down
Loading
Loading