Skip to content

Commit cc13fa7

Browse files
committed
Sonarcloud and firehose mocking.
1 parent 73e5dcf commit cc13fa7

File tree

10 files changed

+456
-84
lines changed

10 files changed

+456
-84
lines changed

.github/workflows/sonarcloud.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ jobs:
6161
6262
- name: Run unittest with coverage-delta
6363
id: delta
64+
env:
65+
PYTHONPATH: delta_backend/src:delta_backend/tests
6466
continue-on-error: true
6567
run: |
66-
pip install poetry==1.8.4 mypy-boto3-dynamodb==1.35.54 boto3==1.26.165 coverage botocore==1.29.165 jmespath==1.0.1 python-dateutil==2.9.0 urllib3==1.26.20 s3transfer==0.6.2 typing-extensions==4.12.2
68+
pip install poetry==1.8.4 moto==4.2.11 mypy-boto3-dynamodb==1.35.54 boto3==1.26.165 coverage botocore==1.29.165 jmespath==1.0.1 python-dateutil==2.9.0 urllib3==1.26.20 s3transfer==0.6.2 typing-extensions==4.12.2
6769
poetry run coverage run --source=delta_backend -m unittest discover -s delta_backend || echo "delta tests failed" >> failed_tests.txt
6870
poetry run coverage xml -o sonarcloud-coverage-delta.xml
6971

delta_backend/pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ version = "0.1.0"
44
description = ""
55
authors = ["Your Name <[email protected]>"]
66
readme = "README.md"
7+
packages = [
8+
{include = "src"}
9+
]
710

811
[tool.poetry.dependencies]
912
python = "~3.10"

delta_backend/src/Converter.py

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,17 @@
1414
get_valid_address,
1515
)
1616

17-
# Converter variables
18-
FHIRData = ""
19-
SchemaFile = {}
20-
imms = []
21-
Converted = {}
22-
ErrorRecords = []
23-
2417

2518
# Converter
2619
class Converter:
2720

2821
def __init__(self, fhir_data):
29-
self.FHIRData = fhir_data # Store JSON data directly
30-
self.SchemaFile = ConversionLayout.ConvertLayout
22+
#Converter variables
23+
self.imms = []
24+
self.converted = {}
25+
self.error_records = []
26+
self.fhir_data = fhir_data # Store JSON data directly
27+
self.schema_file = ConversionLayout.ConvertLayout
3128

3229
# create a FHIR parser - uses fhir json data from delta
3330
# (helper methods to extract values from the nested FHIR structure)
@@ -56,7 +53,7 @@ def _convertData(self, ConversionValidate, expression, dataParser, json_data):
5653
except Exception as e:
5754
message = "Data get value Unexpected exception [%s]: %s" % (e.__class__.__name__, e)
5855
p = {"code": ExceptionMessages.PARSING_ERROR, "message": message}
59-
ErrorRecords.append(p)
56+
self.error_records.append(p)
6057
return p
6158

6259
for conversionValue in conversionValues:
@@ -66,26 +63,26 @@ def _convertData(self, ConversionValidate, expression, dataParser, json_data):
6663
if "address" in FHIRFieldName or "performer" in FHIRFieldName or "name" in FHIRFieldName:
6764
convertedData = self.extract_patient_details(json_data, FlatFieldName)
6865
if convertedData is not None:
69-
Converted[FlatFieldName] = convertedData
66+
self.converted[FlatFieldName] = convertedData
7067

7168
# run the conversion against the data
7269
def runConversion(self, json_data, summarise=False, report_unexpected_exception=True):
7370
try:
74-
dataParser = self._getFHIRParser(self.FHIRData)
71+
dataParser = self._getFHIRParser(self.fhir_data)
7572
except Exception as e:
7673
if report_unexpected_exception:
7774
message = "FHIR Parser Unexpected exception [%s]: %s" % (e.__class__.__name__, e)
7875
p = {"code": 0, "message": message}
79-
ErrorRecords.append(p)
76+
self.error_records.append(p)
8077
return p
8178

8279
try:
83-
schemaParser = self._getSchemaParser(self.SchemaFile)
80+
schemaParser = self._getSchemaParser(self.schema_file)
8481
except Exception as e:
8582
if report_unexpected_exception:
8683
message = "Schema Parser Unexpected exception [%s]: %s" % (e.__class__.__name__, e)
8784
p = {"code": 0, "message": message}
88-
ErrorRecords.append(p)
85+
self.error_records.append(p)
8986
return p
9087

9188
try:
@@ -94,7 +91,7 @@ def runConversion(self, json_data, summarise=False, report_unexpected_exception=
9491
if report_unexpected_exception:
9592
message = "Expression Checker Unexpected exception [%s]: %s" % (e.__class__.__name__, e)
9693
p = {"code": 0, "message": message}
97-
ErrorRecords.append(p)
94+
self.error_records.append(p)
9895
return p
9996

10097
# get list of expressions
@@ -104,17 +101,17 @@ def runConversion(self, json_data, summarise=False, report_unexpected_exception=
104101
if report_unexpected_exception:
105102
message = "Expression Getter Unexpected exception [%s]: %s" % (e.__class__.__name__, e)
106103
p = {"code": 0, "message": message}
107-
ErrorRecords.append(p)
104+
self.error_records.append(p)
108105
return p
109106

110107
for conversion in conversions:
111108
rows = self._convertData(ConversionValidate, conversion, dataParser, json_data)
112109

113-
imms.append(Converted)
114-
return imms
110+
self.imms.append(self.converted)
111+
return self.imms
115112

116113
def getErrorRecords(self):
117-
return ErrorRecords
114+
return self.error_records
118115

119116
def extract_patient_details(self, json_data, FlatFieldName):
120117
if not hasattr(self, "_cached_values"):

delta_backend/src/delta.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def send_message(record):
2828
sqs_client.send_message(QueueUrl=failure_queue_url, MessageBody=json.dumps(message_body))
2929
logger.info("Record saved successfully to the DLQ")
3030
except ClientError as e:
31-
logger.info(f"Error sending record to DLQ: {e}")
31+
logger.error(f"Error sending record to DLQ: {e}")
3232

3333

3434
def get_vaccine_type(patientsk) -> str:
@@ -156,4 +156,7 @@ def handler(event, context):
156156
log_data["operation_outcome"] = operation_outcome
157157
firehose_log["event"] = log_data
158158
firehose_logger.send_log(firehose_log)
159-
raise Exception(f"Delta Lambda failure: {e}")
159+
return {
160+
"statusCode": 500,
161+
"body": "Records not processed",
162+
}

delta_backend/tests/sample_data/__init__.py

Whitespace-only changes.
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""
2+
# This module provides a function to generate a sample FHIR Immunization resource
3+
"""
4+
def get_test_data_resource():
5+
"""
6+
The returned resource includes details about the practitioner, patient,
7+
vaccine code, location, and other relevant fields.
8+
"""
9+
return {
10+
"resourceType": "Immunization",
11+
"contained": [
12+
{
13+
"resourceType": "Practitioner",
14+
"id": "Pract1",
15+
"name": [
16+
{
17+
"family": "O'Reilly",
18+
"given": ["Ellena"]
19+
}
20+
]
21+
},
22+
{
23+
"resourceType": "Patient",
24+
"id": "Pat1",
25+
"identifier": [
26+
{
27+
"system": "https://fhir.nhs.uk/Id/nhs-number",
28+
"value": "9674963871"
29+
}
30+
],
31+
"name": [
32+
{
33+
"family": "GREIR",
34+
"given": ["SABINA"]
35+
}
36+
],
37+
"gender": "female",
38+
"birthDate": "2019-01-31",
39+
"address": [
40+
{
41+
"postalCode": "GU14 6TU"
42+
}
43+
]
44+
}
45+
],
46+
"extension": [
47+
{
48+
"url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-VaccinationProcedure",
49+
"valueCodeableConcept": {
50+
"coding": [
51+
{
52+
"system": "http://snomed.info/sct",
53+
"code": "1303503001",
54+
"display":
55+
"Administration of vaccine product containing only Human orthopneumovirus antigen (procedure)"
56+
}
57+
]
58+
}
59+
}
60+
],
61+
"identifier": [
62+
{
63+
"system": "https://www.ravs.england.nhs.uk/",
64+
"value": "0001_RSV_v5_RUN_2_CDFDPS-742_valid_dose_1"
65+
}
66+
],
67+
"status": "completed",
68+
"vaccineCode": {
69+
"coding": [
70+
{
71+
"system": "http://snomed.info/sct",
72+
"code": "42605811000001109",
73+
"display":
74+
"Abrysvo vaccine powder and solvent for solution for injection 0.5ml vials (Pfizer Ltd) (product)"
75+
}
76+
]
77+
},
78+
"patient": {
79+
"reference": "#Pat1"
80+
},
81+
"occurrenceDateTime": "2024-06-10T18:33:25+00:00",
82+
"recorded": "2024-06-10T18:33:25+00:00",
83+
"primarySource": True,
84+
"manufacturer": {
85+
"display": "Pfizer"
86+
},
87+
"location": {
88+
"type": "Location",
89+
"identifier": {
90+
"value": "J82067",
91+
"system": "https://fhir.nhs.uk/Id/ods-organization-code"
92+
}
93+
},
94+
"lotNumber": "RSVTEST",
95+
"expirationDate": "2024-12-31",
96+
"site": {
97+
"coding": [
98+
{
99+
"system": "http://snomed.info/sct",
100+
"code": "368208006",
101+
"display": "Left upper arm structure (body structure)"
102+
}
103+
]
104+
},
105+
"route": {
106+
"coding": [
107+
{
108+
"system": "http://snomed.info/sct",
109+
"code": "78421000",
110+
"display": "Intramuscular route (qualifier value)"
111+
}
112+
]
113+
},
114+
"doseQuantity": {
115+
"value": 0.5,
116+
"unit": "Milliliter (qualifier value)",
117+
"system": "http://unitsofmeasure.org",
118+
"code": "258773002"
119+
},
120+
"performer": [
121+
{
122+
"actor": {
123+
"reference": "#Pract1"
124+
}
125+
},
126+
{
127+
"actor": {
128+
"type": "Organization",
129+
"identifier": {
130+
"system": "https://fhir.nhs.uk/Id/ods-organization-code",
131+
"value": "X0X0X"
132+
}
133+
}
134+
}
135+
],
136+
"reasonCode": [
137+
{
138+
"coding": [
139+
{
140+
"code": "Test",
141+
"system": "http://snomed.info/sct"
142+
}
143+
]
144+
}
145+
],
146+
"protocolApplied": [
147+
{
148+
"targetDisease": [
149+
{
150+
"coding": [
151+
{
152+
"system": "http://snomed.info/sct",
153+
"code": "840539006",
154+
"display": "Disease caused by severe acute respiratory syndrome coronavirus 2"
155+
}
156+
]
157+
}
158+
],
159+
"doseNumberPositiveInt": 1
160+
}
161+
],
162+
"id": "ca8ba2c6-2383-4465-b456-c1174c21cf31"
163+
}
164+

delta_backend/tests/test_convert_to_flat_json.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from unittest.mock import patch, Mock
99
from moto import mock_dynamodb, mock_sqs
1010
from boto3 import resource as boto3_resource, client as boto3_client
11-
from tests.utils_for_converter_tests import ValuesForTests, ErrorValuesForTests
11+
from utils_for_converter_tests import ValuesForTests, ErrorValuesForTests
1212
from botocore.config import Config
1313
from pathlib import Path
1414
from SchemaParser import SchemaParser
@@ -24,7 +24,6 @@
2424
request_json_data = ValuesForTests.json_data
2525
with patch.dict("os.environ", MOCK_ENV_VARS):
2626
from delta import handler, Converter
27-
from Converter import imms, ErrorRecords
2827

2928

3029
@patch.dict("os.environ", MOCK_ENV_VARS, clear=True)
@@ -65,6 +64,16 @@ def setUp(self):
6564
},
6665
],
6766
)
67+
self.logger_info_patcher = patch("logging.Logger.info")
68+
self.mock_logger_info = self.logger_info_patcher.start()
69+
70+
self.logger_exception_patcher = patch("logging.Logger.exception")
71+
self.mock_logger_exception = self.logger_exception_patcher.start()
72+
73+
def tearDown(self):
74+
self.logger_exception_patcher.stop()
75+
self.logger_info_patcher.stop()
76+
6877

6978
@staticmethod
7079
def get_event(event_name="INSERT", operation="operation", supplier="EMIS"):
@@ -101,7 +110,6 @@ def assert_dynamodb_record(self, operation_flag, items, expected_values, expecte
101110

102111
def test_fhir_converter_json_direct_data(self):
103112
"""it should convert fhir json data to flat json"""
104-
imms.clear()
105113
json_data = json.dumps(ValuesForTests.json_data)
106114

107115
start = time.time()
@@ -130,7 +138,6 @@ def test_fhir_converter_json_error_scenario(self):
130138
error_test_cases = [ErrorValuesForTests.missing_json, ErrorValuesForTests.json_dob_error]
131139

132140
for test_case in error_test_cases:
133-
imms.clear()
134141
json_data = json.dumps(test_case)
135142

136143
start = time.time()
@@ -167,8 +174,6 @@ def test_handler_imms_convert_to_flat_json(self):
167174

168175
for test_case in expected_action_flags:
169176
with self.subTest(test_case["Operation"]):
170-
imms.clear()
171-
172177
event = self.get_event(operation=test_case["Operation"])
173178

174179
response = handler(event, None)
@@ -256,7 +261,7 @@ def test_get_conversions_exception(self, mock_get_conversions):
256261
response = converter.runConversion(ValuesForTests.json_data)
257262

258263
# Check if the error message was added to ErrorRecords
259-
self.assertEqual(len(converter.getErrorRecords()), 3)
264+
self.assertEqual(len(converter.getErrorRecords()), 1)
260265
self.assertIn(
261266
"FHIR Parser Unexpected exception [JSONDecodeError]: Expecting value: line 1 column 1 (char 0)",
262267
converter.getErrorRecords()[0]["message"],
@@ -268,7 +273,6 @@ def test_get_conversions_exception(self, mock_get_conversions):
268273
def test_conversion_exceptions(self, mock_get_key_value, mock_get_conversions):
269274
mock_get_conversions.side_effect = Exception("Error while getting conversions")
270275
mock_get_key_value.side_effect = Exception("Key value retrieval failed")
271-
ErrorRecords.clear()
272276
converter = Converter(fhir_data="some_data")
273277

274278
schema = {

0 commit comments

Comments
 (0)