Skip to content

Commit 9c867bd

Browse files
authored
Merge branch 'develop' into feature/eema1-NRL-474-authorBasicValidation
2 parents 9fef834 + 2cbeb7f commit 9c867bd

File tree

2 files changed

+210
-4
lines changed

2 files changed

+210
-4
lines changed

layer/nrlf/core/errors.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
from typing import List, Optional
22

33
from pydantic import ValidationError
4+
from pydantic_core import ErrorDetails
45

56
from nrlf.core.response import Response
67
from nrlf.core.types import CodeableConcept
78
from nrlf.producer.fhir.r4 import model as producer_model
89
from nrlf.producer.fhir.r4.model import OperationOutcome, OperationOutcomeIssue
910

1011

12+
def diag_for_error(error: ErrorDetails) -> str:
13+
if error["loc"]:
14+
return f"{error['loc'][0]}: {error['msg']}"
15+
else:
16+
return f"root: {error['msg']}"
17+
18+
19+
def expression_for_error(error: ErrorDetails) -> Optional[str]:
20+
return str(error["loc"][0] if error["loc"] else "root")
21+
22+
1123
class OperationOutcomeError(Exception):
1224
"""
1325
Will instantly trigger an OperationOutcome error response when raised
@@ -59,8 +71,8 @@ def from_validation_error(
5971
severity="error",
6072
code="invalid",
6173
details=details, # type: ignore
62-
diagnostics=f"{msg} ({error['loc'][0]}: {error['msg']})",
63-
expression=[str(error["loc"][0])], # type: ignore
74+
diagnostics=f"{msg} ({diag_for_error(error)})",
75+
expression=[expression_for_error(error)], # type: ignore
6476
)
6577
for error in exc.errors()
6678
]

layer/nrlf/core/tests/test_request.py

Lines changed: 196 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import pytest
44

5-
from nrlf.core.errors import OperationOutcomeError
6-
from nrlf.core.request import parse_headers
5+
from nrlf.core.errors import OperationOutcomeError, ParseError
6+
from nrlf.core.request import parse_body, parse_headers
7+
from nrlf.producer.fhir.r4.model import DocumentReference
8+
from nrlf.tests.data import load_document_reference_data
79

810

911
def test_parse_headers_empty_headers():
@@ -129,3 +131,195 @@ def test_parse_headers_case_insensitive():
129131
assert metadata.client_rp_details.developer_app_name == "TestApp"
130132
assert metadata.client_rp_details.developer_app_id == "12345"
131133
assert metadata.ods_code_parts == ("X26", "001")
134+
135+
136+
def test_parse_body_no_model_no_body():
137+
body = None
138+
model = None
139+
140+
result = parse_body(model, body)
141+
142+
assert result is None
143+
144+
145+
def test_parse_body_valid_docref():
146+
model = DocumentReference
147+
docref_body = load_document_reference_data("Y05868-736253002-Valid")
148+
149+
result = parse_body(model, docref_body)
150+
151+
assert isinstance(result, DocumentReference)
152+
153+
154+
def test_parse_body_no_body():
155+
model = DocumentReference
156+
body = None
157+
158+
with pytest.raises(OperationOutcomeError) as error:
159+
parse_body(model, body)
160+
161+
exc = error.value
162+
163+
assert exc.status_code == "400"
164+
assert exc.operation_outcome.model_dump(exclude_none=True) == {
165+
"resourceType": "OperationOutcome",
166+
"issue": [
167+
{
168+
"severity": "error",
169+
"code": "invalid",
170+
"details": {
171+
"coding": [
172+
{
173+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
174+
"code": "BAD_REQUEST",
175+
"display": "Bad request",
176+
}
177+
],
178+
},
179+
"diagnostics": "Request body is required",
180+
}
181+
],
182+
}
183+
184+
185+
def test_parse_body_invalid_docref_json():
186+
model = DocumentReference
187+
docref_body = load_document_reference_data("Y05868-736253002-Valid")
188+
189+
docref_body = docref_body.replace('unstructured"', "unstructured")
190+
191+
with pytest.raises(ParseError) as error:
192+
parse_body(model, docref_body)
193+
194+
response = error.value.response.model_dump()
195+
196+
assert response["statusCode"] == "400"
197+
assert json.loads(response["body"]) == {
198+
"resourceType": "OperationOutcome",
199+
"issue": [
200+
{
201+
"severity": "error",
202+
"code": "invalid",
203+
"details": {
204+
"coding": [
205+
{
206+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
207+
"code": "MESSAGE_NOT_WELL_FORMED",
208+
"display": "Message not well formed",
209+
}
210+
],
211+
},
212+
"diagnostics": "Request body could not be parsed (root: Invalid JSON: control character (\\u0000-\\u001F) found while parsing a string at line 72 column 0)",
213+
"expression": ["root"],
214+
}
215+
],
216+
}
217+
218+
219+
def test_parse_body_invalid_json():
220+
model = DocumentReference
221+
body = '{ "type": "is-not-a-docref" }'
222+
223+
with pytest.raises(ParseError) as error:
224+
parse_body(model, body)
225+
226+
response = error.value.response.model_dump()
227+
228+
assert response["statusCode"] == "400"
229+
assert json.loads(response["body"]) == {
230+
"resourceType": "OperationOutcome",
231+
"issue": [
232+
{
233+
"severity": "error",
234+
"code": "invalid",
235+
"details": {
236+
"coding": [
237+
{
238+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
239+
"code": "MESSAGE_NOT_WELL_FORMED",
240+
"display": "Message not well formed",
241+
}
242+
]
243+
},
244+
"diagnostics": "Request body could not be parsed (resourceType: Field required)",
245+
"expression": ["resourceType"],
246+
},
247+
{
248+
"severity": "error",
249+
"code": "invalid",
250+
"details": {
251+
"coding": [
252+
{
253+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
254+
"code": "MESSAGE_NOT_WELL_FORMED",
255+
"display": "Message not well formed",
256+
}
257+
]
258+
},
259+
"diagnostics": "Request body could not be parsed (status: Field required)",
260+
"expression": ["status"],
261+
},
262+
{
263+
"severity": "error",
264+
"code": "invalid",
265+
"details": {
266+
"coding": [
267+
{
268+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
269+
"code": "MESSAGE_NOT_WELL_FORMED",
270+
"display": "Message not well formed",
271+
}
272+
]
273+
},
274+
"diagnostics": "Request body could not be parsed (type: Input should be an object)",
275+
"expression": ["type"],
276+
},
277+
{
278+
"severity": "error",
279+
"code": "invalid",
280+
"details": {
281+
"coding": [
282+
{
283+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
284+
"code": "MESSAGE_NOT_WELL_FORMED",
285+
"display": "Message not well formed",
286+
}
287+
]
288+
},
289+
"diagnostics": "Request body could not be parsed (content: Field required)",
290+
"expression": ["content"],
291+
},
292+
],
293+
}
294+
295+
296+
def test_parse_body_not_json():
297+
model = DocumentReference
298+
body = "is not json"
299+
300+
with pytest.raises(ParseError) as error:
301+
parse_body(model, body)
302+
303+
response = error.value.response
304+
305+
assert response.statusCode == "400"
306+
assert json.loads(response.body) == {
307+
"resourceType": "OperationOutcome",
308+
"issue": [
309+
{
310+
"severity": "error",
311+
"code": "invalid",
312+
"details": {
313+
"coding": [
314+
{
315+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
316+
"code": "MESSAGE_NOT_WELL_FORMED",
317+
"display": "Message not well formed",
318+
}
319+
]
320+
},
321+
"diagnostics": "Request body could not be parsed (root: Invalid JSON: expected value at line 1 column 1)",
322+
"expression": ["root"],
323+
}
324+
],
325+
}

0 commit comments

Comments
 (0)