Skip to content

Commit 39f6d46

Browse files
committed
Search Fails with unrecognised typo
1 parent 9f6b8a1 commit 39f6d46

File tree

7 files changed

+225
-126
lines changed

7 files changed

+225
-126
lines changed

backend/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ package: build
66
docker run --rm -v $(shell pwd)/build:/build imms-lambda-build
77

88
test:
9-
@PYTHONPATH=src:tests python -m unittest
9+
@PYTHONPATH=src:tests:tests/utils python -m unittest
1010

1111
coverage-run:
12-
@PYTHONPATH=src:tests coverage run -m unittest discover
12+
@PYTHONPATH=src:tests:tests/utils coverage run -m unittest discover
1313

1414
coverage-report:
1515
coverage report -m

backend/poetry.lock

Lines changed: 93 additions & 93 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/src/constants.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,16 @@ class Urls:
2323

2424

2525
GENERIC_SERVER_ERROR_DIAGNOSTICS_MESSAGE = "Unable to process request. Issue may be transient."
26+
27+
SEARCH_IMMUNIZATIONS_PARAMETERS = [
28+
"patient.identifier",
29+
"-immunization.target",
30+
"_include",
31+
"-date.from",
32+
"-date.to"
33+
]
34+
35+
SEARCH_IMMUNIZATION_BY_IDENTIFIER_PARAMETERS = [
36+
"identifier",
37+
"_elements"
38+
]

backend/src/search_imms_handler.py

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
from models.errors import Severity, Code, create_operation_outcome
1212
from constants import GENERIC_SERVER_ERROR_DIAGNOSTICS_MESSAGE
1313
from log_structure import function_info
14-
import base64
15-
import urllib.parse
14+
from search_parameter_validator import (
15+
is_immunization_by_identifier,
16+
get_parsed_body
17+
)
1618

1719
logging.basicConfig(level="INFO")
1820
logger = logging.getLogger()
@@ -24,33 +26,13 @@ def search_imms_handler(event: events.APIGatewayProxyEventV1, _context: context_
2426

2527
def search_imms(event: events.APIGatewayProxyEventV1, controller: FhirController):
2628
try:
29+
2730
query_params = event.get("queryStringParameters", {})
2831
body = event.get("body")
29-
body_has_immunization_identifier = False
30-
query_string_has_immunization_identifier = False
31-
query_string_has_element = False
32-
body_has_immunization_element = False
33-
if not (query_params == None and body == None):
34-
if query_params:
35-
query_string_has_immunization_identifier = "identifier" in event.get(
36-
"queryStringParameters", {}
37-
)
38-
query_string_has_element = "_elements" in event.get("queryStringParameters", {})
39-
# Decode body from base64
40-
if body:
41-
decoded_body = base64.b64decode(body).decode("utf-8")
42-
# Parse the URL encoded body
43-
parsed_body = urllib.parse.parse_qs(decoded_body)
44-
45-
# Check for 'identifier' in body
46-
body_has_immunization_identifier = "identifier" in parsed_body
47-
body_has_immunization_element = "_elements" in parsed_body
48-
if (
49-
query_string_has_immunization_identifier
50-
or body_has_immunization_identifier
51-
or query_string_has_element
52-
or body_has_immunization_element
53-
):
32+
has_body = body is not None
33+
has_query_params = query_params is not None and query_params != {}
34+
if has_query_params or has_body:
35+
if is_immunization_by_identifier(query_params, get_parsed_body(body)):
5436
return controller.get_immunization_by_identifier(event)
5537
response = controller.search_immunizations(event)
5638

@@ -66,6 +48,16 @@ def search_imms(event: events.APIGatewayProxyEventV1, controller: FhirController
6648
)
6749
return FhirController.create_response(400, exp_error)
6850
return response
51+
52+
except ValueError as ve:
53+
logger.exception("ValueError occurred")
54+
exp_error = create_operation_outcome(
55+
resource_id=str(uuid.uuid4()),
56+
severity=Severity.error,
57+
code=Code.invalid,
58+
diagnostics=str(ve)
59+
)
60+
return FhirController.create_response(400, exp_error)
6961
except Exception: # pylint: disable = broad-exception-caught
7062
logger.exception("Unhandled exception")
7163
exp_error = create_operation_outcome(
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import base64
2+
import urllib.parse
3+
from constants import SEARCH_IMMUNIZATION_BY_IDENTIFIER_PARAMETERS, SEARCH_IMMUNIZATIONS_PARAMETERS
4+
5+
6+
def body_to_dict(body):
7+
"""
8+
Converts a body array of {'key': ..., 'value': ...} to a dict.
9+
"""
10+
if isinstance(body, list):
11+
return {item['key']: item['value'] for item in body if 'key' in item and 'value' in item}
12+
return body if isinstance(body, dict) else {}
13+
14+
15+
def check_route_parameters(query_params, body, valid_params: list, second_list: list = None):
16+
try:
17+
18+
query_params = query_params or {}
19+
body_dict = body_to_dict(body)
20+
# merge query and body parameters
21+
all_params = {**query_params, **body_dict}
22+
23+
found = False
24+
for param in all_params:
25+
if param in valid_params:
26+
found = True
27+
break
28+
29+
for param in all_params:
30+
if param in valid_params or param in second_list:
31+
continue
32+
elif param == "id":
33+
continue
34+
else:
35+
raise ValueError(f"Invalid body parameter: {param}")
36+
return found
37+
except Exception as e:
38+
raise ValueError(f"Error checking route parameters: {e}")
39+
40+
41+
def is_immunization_by_identifier(query_params, body):
42+
# check the parameters indicate search by identifier
43+
return check_route_parameters(query_params, body, SEARCH_IMMUNIZATION_BY_IDENTIFIER_PARAMETERS,
44+
SEARCH_IMMUNIZATIONS_PARAMETERS)
45+
46+
# def is_search_immunizations(query_params, body):
47+
# # check the parameters indicate search for immunizations
48+
# return check_route_parameters(query_params, body, SEARCH_IMMUNIZATIONS_PARAMETERS)
49+
50+
def get_parsed_body(body):
51+
if body:
52+
decoded_body = base64.b64decode(body).decode("utf-8")
53+
# Parse the URL encoded body
54+
return urllib.parse.parse_qs(decoded_body)
55+
return None

backend/tests/test_search_imms.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ def test_search_immunizations_get_id_from_body_imms_identifer(self):
129129

130130
def test_search_immunizations_lambda_size_limit(self):
131131
"""it should return 400 as search returned too many results."""
132-
lambda_event = {"pathParameters": {"id": "an-id"}, "body": None}
132+
lambda_event = {
133+
"pathParameters": {"id": "an-id"},
134+
"body": None,
135+
}
133136
request_file = script_location / "sample_data" / "sample_input_search_imms.json"
134137
with open(request_file) as f:
135138
exp_res = json.load(f)
@@ -163,6 +166,42 @@ def test_search_handle_exception(self):
163166
# Then
164167
act_body = json.loads(act_res["body"])
165168

166-
self.assertEqual(exp_error["issue"][0]["code"], act_body["issue"][0]["code"])
167-
self.assertEqual(exp_error["issue"][0]["severity"], act_body["issue"][0]["severity"])
169+
exp_issue = exp_error["issue"][0]
170+
act_issue = act_body["issue"][0]
171+
self.assertEqual(exp_issue["code"], act_issue["code"])
172+
self.assertEqual(exp_issue["severity"], act_issue["severity"])
168173
self.assertEqual(act_res["statusCode"], 500)
174+
175+
def test_search_immunizations_invalid_params(self):
176+
"""it should return 400 if invalid parameters are provided"""
177+
lambda_event = {
178+
"pathParameters": {"id": "an-id"},
179+
"queryStringParameters": {
180+
"identifier": "https://supplierABC/identifiers/vacc|f10b59b3-fc73-4616-99c9-9e882ab31184",
181+
"_elephants": "id,meta",
182+
},
183+
"body": None,
184+
}
185+
# When
186+
act_res = search_imms(lambda_event, self.controller)
187+
188+
# Then
189+
self.assertEqual(act_res["statusCode"], 400)
190+
191+
192+
def test_search_immunizations_invalid_params(self):
193+
"""it should return 400 if only invalid parameters are provided"""
194+
lambda_event = {
195+
"pathParameters": {"id": "an-id"},
196+
"queryStringParameters": {
197+
"_elephants": "id,meta",
198+
},
199+
"body": None,
200+
}
201+
202+
# When
203+
act_res = search_imms(lambda_event, self.controller)
204+
205+
# Then
206+
self.assertEqual(act_res["statusCode"], 400)
207+

backend/tests/utils/test_permissions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22
from unittest.mock import patch
3-
from src.models.utils.permissions import get_supplier_permissions
3+
from models.utils.permissions import get_supplier_permissions
44

55

66
class TestPermissions(unittest.TestCase):

0 commit comments

Comments
 (0)