Skip to content

Commit 13b7a44

Browse files
committed
PDS
cpd
1 parent 207e3b5 commit 13b7a44

File tree

16 files changed

+817
-75
lines changed

16 files changed

+817
-75
lines changed
Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
11
{
22
"folders": [
3-
{
4-
"path": "."
5-
},
6-
{
7-
"path": "backend"
8-
},
9-
{
10-
"path": "filenameprocessor"
11-
},
12-
{
13-
"path": "recordprocessor"
14-
},
15-
{
16-
"path": "ack_backend"
17-
},
18-
{
19-
"path": "delta_backend"
20-
},
21-
{
22-
"path": "mesh_processor"
23-
},
24-
{
25-
"path": "e2e"
26-
},
27-
{
28-
"path": "e2e_batch"
29-
},
30-
{
31-
"path": "redis_sync"
32-
},
33-
{
34-
"path": "lambdas/id_sync"
35-
},
36-
{
37-
"path": "lambdas/shared"
38-
}
39-
],
40-
"settings": {},
3+
{
4+
"path": "."
5+
},
6+
{
7+
"path": "backend"
8+
},
9+
{
10+
"path": "filenameprocessor"
11+
},
12+
{
13+
"path": "recordprocessor"
14+
},
15+
{
16+
"path": "ack_backend"
17+
},
18+
{
19+
"path": "delta_backend"
20+
},
21+
{
22+
"path": "mesh_processor"
23+
},
24+
{
25+
"path": "e2e"
26+
},
27+
{
28+
"path": "e2e_batch"
29+
},
30+
{
31+
"path": "redis_sync"
32+
},
33+
{
34+
"path": "lambdas/id_sync"
35+
},
36+
{
37+
"path": "lambdas/shared"
38+
}
39+
],
40+
"settings": {}
4141
}

lambdas/id_sync/src/clients.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import os
2+
3+
4+
pds_env: str = os.getenv("PDS_ENV", "int")

lambdas/id_sync/src/pds_details.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'''
2+
record Processor
3+
'''
4+
from common.clients import logger, secrets_manager_client
5+
from clients import pds_env
6+
from cache import Cache
7+
from common.pds_service import PdsService
8+
from common.authentication import AppRestrictedAuth, Service
9+
10+
11+
def get_pds_patient_details(id: str) -> dict:
12+
try:
13+
logger.info(f"Get PDS patient details for {id}")
14+
15+
cache = Cache(directory="/tmp")
16+
authenticator = AppRestrictedAuth(
17+
service=Service.PDS,
18+
secret_manager_client=secrets_manager_client,
19+
environment=pds_env,
20+
cache=cache,
21+
)
22+
pds_service = PdsService(authenticator, pds_env)
23+
24+
details = pds_service.get_patient_details(id)
25+
26+
return details.get("id")
27+
except Exception:
28+
logger.exception(f"Error getting PDS patient details for {id}")
29+
return None

lambdas/id_sync/src/record_processor.py

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import json
77
from typing import Optional
88

9+
910
def process_record(event_record: AwsLambdaSqsEventRecord):
1011
record = AwsLambdaSqsEventRecord(event_record) if isinstance(event_record, dict) else event_record
1112
logger.info("Processing record: %s", record)
@@ -14,7 +15,47 @@ def process_record(event_record: AwsLambdaSqsEventRecord):
1415

1516
exists = check_records_exist(id)
1617

17-
return f"hello world {record}"
18+
if exists:
19+
# get patient details from PDS
20+
patient_details = get_pds_patient_details(id)
21+
22+
patient_details_id = patient_details.get("id")
23+
24+
# if patient NHS != id, update patient index of vax events to new number
25+
if patient_details_id != id:
26+
return update_patient_index(id, patient_details_id)
27+
else:
28+
return {"status": "success", "message": "No update required"}
29+
else:
30+
return {"status": "error", "message": f"No records found for ID: {id}"}
31+
32+
33+
def check_records_exist(id: str) -> bool:
34+
# TODO: Implement logic to check if records exist in the database
35+
logger.info(f"TODO Check if records exist for {id}")
36+
return False
37+
38+
39+
def get_pds_patient_details(id: str) -> dict:
40+
# TODO: Implement logic to retrieve patient details from PDS
41+
logger.info(f"TODO Get patient details for {id}")
42+
43+
authenticator = AppRestrictedAuth(
44+
service=Service.PDS,
45+
secret_manager_client=boto3.client("secretsmanager", config=boto_config),
46+
environment=pds_env,
47+
cache=cache,
48+
)
49+
pds_service = PdsService(authenticator, pds_env)
50+
51+
52+
return {"id": id, "name": "Mr Man"}
53+
54+
55+
def update_patient_index(old_id: str, new_id: str):
56+
# TODO: Implement logic to update patient index in Redis or other data store
57+
logger.info(f"TODO Update patient index from {old_id} to {new_id}")
58+
return {"status": "success", "message": f"Updated patient idx from {old_id} to {new_id}", "TODO": "Implement logic"}
1859

1960

2061
def get_id(event_body) -> Optional[str]:
@@ -26,34 +67,8 @@ def get_id(event_body) -> Optional[str]:
2667
else:
2768
data = event_body
2869
# Navigate through the nested structure
29-
entries = data.get("entry", [])
30-
if not entries:
31-
logger.warning("No entries found in bundle")
32-
return None
33-
34-
# Get the first entry's resource
35-
first_entry = entries[0]
36-
resource = first_entry.get("resource", {})
37-
parameters = resource.get("parameter", [])
38-
39-
# Find the "additional-context" parameter
40-
for param in parameters:
41-
if param.get("name") == "additional-context":
42-
parts = param.get("part", [])
43-
44-
# Find the "subject" part within additional-context
45-
for part in parts:
46-
if part.get("name") == "subject":
47-
value_ref = part.get("valueReference", {})
48-
identifier = value_ref.get("identifier", {})
49-
subject_id = identifier.get("value")
50-
51-
if subject_id:
52-
logger.info("Found subject identifier: %s", subject_id)
53-
return subject_id
54-
55-
logger.warning("Subject identifier not found in notification event")
56-
return None
70+
subject = data.get("subject")
71+
return subject
5772

5873
except (json.JSONDecodeError, KeyError, AttributeError) as e:
5974
logger.error("Error extracting subject identifier: %s", e)
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock
3+
from pds_details import get_pds_patient_details
4+
5+
6+
class TestGetPdsPatientDetails(unittest.TestCase):
7+
8+
def setUp(self):
9+
"""Set up test fixtures and mocks"""
10+
# Mock the dependencies
11+
self.mock_boto3_client = MagicMock()
12+
self.test_patient_id = "9912003888"
13+
14+
# Patch all external dependencies
15+
self.logger_patcher = patch('pds_details.logger')
16+
self.mock_logger = self.logger_patcher.start()
17+
18+
self.secrets_manager_patcher = patch('pds_details.secrets_manager_client')
19+
self.mock_secrets_manager = self.secrets_manager_patcher.start()
20+
21+
self.pds_env_patcher = patch('pds_details.pds_env')
22+
self.mock_pds_env = self.pds_env_patcher.start()
23+
24+
self.cache_patcher = patch('pds_details.Cache')
25+
self.mock_cache_class = self.cache_patcher.start()
26+
self.mock_cache_instance = MagicMock()
27+
self.mock_cache_class.return_value = self.mock_cache_instance
28+
29+
self.auth_patcher = patch('pds_details.AppRestrictedAuth')
30+
self.mock_auth_class = self.auth_patcher.start()
31+
self.mock_auth_instance = MagicMock()
32+
self.mock_auth_class.return_value = self.mock_auth_instance
33+
34+
self.pds_service_patcher = patch('pds_details.PdsService')
35+
self.mock_pds_service_class = self.pds_service_patcher.start()
36+
self.mock_pds_service_instance = MagicMock()
37+
self.mock_pds_service_class.return_value = self.mock_pds_service_instance
38+
39+
def tearDown(self):
40+
"""Clean up patches"""
41+
patch.stopall()
42+
43+
def test_get_pds_patient_details_success(self):
44+
"""Test successful retrieval of patient details"""
45+
# Arrange
46+
expected_patient_data = {
47+
"id": "9912003888",
48+
"name": "John Doe",
49+
"birthDate": "1990-01-01",
50+
"gender": "male"
51+
}
52+
self.mock_pds_service_instance.get_patient_details.return_value = expected_patient_data
53+
54+
# Act
55+
result = get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
56+
57+
# Assert
58+
self.assertEqual(result, "9912003888")
59+
60+
# Verify Cache was initialized correctly
61+
self.mock_cache_class.assert_called_once_with(directory="/tmp")
62+
63+
# Verify AppRestrictedAuth was initialized correctly
64+
from common.authentication import Service
65+
self.mock_auth_class.assert_called_once_with(
66+
service=Service.PDS,
67+
secret_manager_client=self.mock_secrets_manager,
68+
environment=self.mock_pds_env,
69+
cache=self.mock_cache_instance
70+
)
71+
72+
# Verify PdsService was initialized correctly
73+
self.mock_pds_service_class.assert_called_once_with(
74+
self.mock_auth_instance,
75+
self.mock_pds_env
76+
)
77+
78+
# Verify get_patient_details was called
79+
self.mock_pds_service_instance.get_patient_details.assert_called_once_with(self.test_patient_id)
80+
81+
def test_get_pds_patient_details_missing_id_in_response(self):
82+
"""Test when PDS response doesn't contain 'id' field"""
83+
# Arrange
84+
patient_data_without_id = {
85+
"name": "John Doe",
86+
"birthDate": "1990-01-01",
87+
"gender": "male"
88+
# Missing 'id' field
89+
}
90+
self.mock_pds_service_instance.get_patient_details.return_value = patient_data_without_id
91+
92+
# Act
93+
result = get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
94+
95+
# Assert
96+
self.assertIsNone(result)
97+
self.mock_pds_service_instance.get_patient_details.assert_called_once_with(self.test_patient_id)
98+
99+
def test_get_pds_patient_details_empty_response(self):
100+
"""Test when PDS returns empty response"""
101+
# Arrange
102+
self.mock_pds_service_instance.get_patient_details.return_value = {}
103+
104+
# Act
105+
result = get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
106+
107+
# Assert
108+
self.assertIsNone(result)
109+
self.mock_pds_service_instance.get_patient_details.assert_called_once_with(self.test_patient_id)
110+
111+
def test_get_pds_patient_details_none_response(self):
112+
"""Test when PDS returns None"""
113+
# Arrange
114+
self.mock_pds_service_instance.get_patient_details.return_value = None
115+
116+
# Act
117+
with self.assertRaises(AttributeError):
118+
get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
119+
120+
# Assert
121+
self.mock_pds_service_instance.get_patient_details.assert_called_once_with(self.test_patient_id)
122+
123+
def test_get_pds_patient_details_pds_service_exception(self):
124+
"""Test when PdsService.get_patient_details raises an exception"""
125+
# Arrange
126+
self.mock_pds_service_instance.get_patient_details.side_effect = Exception("PDS API error")
127+
128+
# Act & Assert
129+
with self.assertRaises(Exception) as context:
130+
get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
131+
132+
self.assertEqual(str(context.exception), "PDS API error")
133+
self.mock_pds_service_instance.get_patient_details.assert_called_once_with(self.test_patient_id)
134+
135+
def test_get_pds_patient_details_cache_initialization_error(self):
136+
"""Test when Cache initialization fails"""
137+
# Arrange
138+
self.mock_cache_class.side_effect = OSError("Cannot write to /tmp")
139+
140+
# Act & Assert
141+
with self.assertRaises(OSError) as context:
142+
get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
143+
144+
self.assertEqual(str(context.exception), "Cannot write to /tmp")
145+
self.mock_cache_class.assert_called_once_with(directory="/tmp")
146+
147+
def test_get_pds_patient_details_auth_initialization_error(self):
148+
"""Test when AppRestrictedAuth initialization fails"""
149+
# Arrange
150+
self.mock_auth_class.side_effect = ValueError("Invalid authentication parameters")
151+
152+
# Act & Assert
153+
with self.assertRaises(ValueError) as context:
154+
get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
155+
156+
self.assertEqual(str(context.exception), "Invalid authentication parameters")
157+
158+
def test_get_pds_patient_details_exception(self):
159+
"""Test when an exception occurs"""
160+
# Arrange
161+
self.mock_logger.info.side_effect = Exception("Logging system failure")
162+
163+
# Act
164+
result = get_pds_patient_details(self.test_patient_id, self.mock_boto3_client)
165+
166+
# Assert
167+
self.assertIsNone(result)
168+
169+
# Verify logger.exception was called due to the caught exception
170+
self.mock_logger.exception.assert_called_once_with(
171+
f"Error getting PDS patient details for {self.test_patient_id}")

0 commit comments

Comments
 (0)