Skip to content

Commit ca20f72

Browse files
Merge pull request #624 from NHSDigital/feature/kabo5-NRL-525-self-links-serch
Feature/kabo5 nrl 525 self links serch
2 parents 3af1d02 + 9db38ba commit ca20f72

File tree

8 files changed

+187
-3
lines changed

8 files changed

+187
-3
lines changed

api/consumer/searchDocumentReference/search_document_reference.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from nrlf.consumer.fhir.r4.model import Bundle, DocumentReference
44
from nrlf.core.codes import SpineErrorConcept
5+
from nrlf.core.config import Config
56
from nrlf.core.decorators import request_handler
67
from nrlf.core.dynamodb.repository import DocumentPointerRepository
78
from nrlf.core.errors import OperationOutcomeError
@@ -41,6 +42,9 @@ def handler(
4142
diagnostics="A valid NHS number is required to search for document references",
4243
expression="subject:identifier",
4344
)
45+
config = Config()
46+
base_url = f"https://{config.ENVIRONMENT}.api.service.nhs.uk/"
47+
self_link = f"{base_url}record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|{params.nhs_number}"
4448

4549
if not validate_type_system(params.type, metadata.pointer_types):
4650
logger.log(
@@ -58,8 +62,20 @@ def handler(
5862
if params.custodian_identifier
5963
else None
6064
)
61-
bundle = {"resourceType": "Bundle", "type": "searchset", "total": 0, "entry": []}
65+
if custodian_id:
66+
self_link += f"&custodian:identifier=https://fhir.nhs.uk/Id/ods-organization-code|{custodian_id}"
67+
6268
pointer_types = [params.type.__root__] if params.type else metadata.pointer_types
69+
if params.type:
70+
self_link += f"&type={params.type.__root__}"
71+
72+
bundle = {
73+
"resourceType": "Bundle",
74+
"type": "searchset",
75+
"link": [{"relation": "self", "url": self_link}],
76+
"total": 0,
77+
"entry": [],
78+
}
6379

6480
logger.log(
6581
LogReference.CONSEARCH003,

api/consumer/searchDocumentReference/tests/test_search_document_reference_consumer.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,16 @@ def test_search_document_reference_happy_path(repository: DocumentPointerReposit
3333
assert result == {"statusCode": "200", "headers": {}, "isBase64Encoded": False}
3434

3535
parsed_body = json.loads(body)
36+
3637
assert parsed_body == {
3738
"resourceType": "Bundle",
3839
"type": "searchset",
40+
"link": [
41+
{
42+
"relation": "self",
43+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191",
44+
}
45+
],
3946
"total": 1,
4047
"entry": [{"resource": doc_ref.dict(exclude_none=True)}],
4148
}
@@ -67,6 +74,49 @@ def test_search_document_reference_happy_path_with_custodian(
6774
assert parsed_body == {
6875
"resourceType": "Bundle",
6976
"type": "searchset",
77+
"link": [
78+
{
79+
"relation": "self",
80+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191&custodian:identifier=https://fhir.nhs.uk/Id/ods-organization-code|Y05868",
81+
}
82+
],
83+
"total": 1,
84+
"entry": [{"resource": doc_ref.dict(exclude_none=True)}],
85+
}
86+
87+
88+
@mock_aws
89+
@mock_repository
90+
def test_search_document_reference_happy_path_with_type(
91+
repository: DocumentPointerRepository,
92+
):
93+
doc_ref = load_document_reference("Y05868-736253002-Valid")
94+
doc_pointer = DocumentPointer.from_document_reference(doc_ref)
95+
repository.create(doc_pointer)
96+
97+
event = create_test_api_gateway_event(
98+
headers=create_headers(),
99+
query_string_parameters={
100+
"subject:identifier": "https://fhir.nhs.uk/Id/nhs-number|6700028191",
101+
"type": "http://snomed.info/sct|736253002",
102+
},
103+
)
104+
105+
result = handler(event, create_mock_context())
106+
body = result.pop("body")
107+
108+
assert result == {"statusCode": "200", "headers": {}, "isBase64Encoded": False}
109+
110+
parsed_body = json.loads(body)
111+
assert parsed_body == {
112+
"resourceType": "Bundle",
113+
"type": "searchset",
114+
"link": [
115+
{
116+
"relation": "self",
117+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191&type=http://snomed.info/sct|736253002",
118+
}
119+
],
70120
"total": 1,
71121
"entry": [{"resource": doc_ref.dict(exclude_none=True)}],
72122
}
@@ -91,6 +141,12 @@ def test_search_document_reference_no_results(repository: DocumentPointerReposit
91141
assert parsed_body == {
92142
"resourceType": "Bundle",
93143
"type": "searchset",
144+
"link": [
145+
{
146+
"relation": "self",
147+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191",
148+
}
149+
],
94150
"total": 0,
95151
"entry": [],
96152
}

api/consumer/searchPostDocumentReference/search_post_document_reference.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from nrlf.consumer.fhir.r4.model import Bundle, DocumentReference
44
from nrlf.core.codes import SpineErrorConcept
5+
from nrlf.core.config import Config
56
from nrlf.core.decorators import request_handler
67
from nrlf.core.dynamodb.repository import DocumentPointerRepository
78
from nrlf.core.errors import OperationOutcomeError
@@ -45,6 +46,10 @@ def handler(
4546
expression="subject:identifier",
4647
)
4748

49+
config = Config()
50+
base_url = f"https://{config.ENVIRONMENT}.api.service.nhs.uk/"
51+
self_link = f"{base_url}record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|{body.nhs_number}"
52+
4853
if not validate_type_system(body.type, metadata.pointer_types):
4954
logger.log(
5055
LogReference.CONPOSTSEARCH002,
@@ -61,9 +66,20 @@ def handler(
6166
if body.custodian_identifier
6267
else None
6368
)
69+
if custodian_id:
70+
self_link += f"&custodian:identifier=https://fhir.nhs.uk/Id/ods-organization-code|{custodian_id}"
6471

65-
bundle = {"resourceType": "Bundle", "type": "searchset", "total": 0, "entry": []}
6672
pointer_types = [body.type.__root__] if body.type else metadata.pointer_types
73+
if body.type:
74+
self_link += f"&type={body.type.__root__}"
75+
76+
bundle = {
77+
"resourceType": "Bundle",
78+
"type": "searchset",
79+
"link": [{"relation": "self", "url": self_link}],
80+
"total": 0,
81+
"entry": [],
82+
}
6783

6884
logger.log(
6985
LogReference.CONPOSTSEARCH003,

api/consumer/searchPostDocumentReference/tests/test_search_post_document_reference_consumer.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ def test_search_post_document_reference_happy_path(
4242
assert parsed_body == {
4343
"resourceType": "Bundle",
4444
"type": "searchset",
45+
"link": [
46+
{
47+
"relation": "self",
48+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191",
49+
}
50+
],
4551
"total": 1,
4652
"entry": [{"resource": doc_ref.dict(exclude_none=True)}],
4753
}
@@ -75,6 +81,51 @@ def test_search_post_document_reference_happy_path_with_custodian(
7581
assert parsed_body == {
7682
"resourceType": "Bundle",
7783
"type": "searchset",
84+
"link": [
85+
{
86+
"relation": "self",
87+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191&custodian:identifier=https://fhir.nhs.uk/Id/ods-organization-code|Y05868",
88+
}
89+
],
90+
"total": 1,
91+
"entry": [{"resource": doc_ref.dict(exclude_none=True)}],
92+
}
93+
94+
95+
@mock_aws
96+
@mock_repository
97+
def test_search_post_document_reference_happy_path_with_type(
98+
repository: DocumentPointerRepository,
99+
):
100+
doc_ref = load_document_reference("Y05868-736253002-Valid")
101+
doc_pointer = DocumentPointer.from_document_reference(doc_ref)
102+
repository.create(doc_pointer)
103+
104+
event = create_test_api_gateway_event(
105+
headers=create_headers(),
106+
body=json.dumps(
107+
{
108+
"subject:identifier": "https://fhir.nhs.uk/Id/nhs-number|6700028191",
109+
"type": "http://snomed.info/sct|736253002",
110+
},
111+
),
112+
)
113+
114+
result = handler(event, create_mock_context())
115+
body = result.pop("body")
116+
117+
assert result == {"statusCode": "200", "headers": {}, "isBase64Encoded": False}
118+
119+
parsed_body = json.loads(body)
120+
assert parsed_body == {
121+
"resourceType": "Bundle",
122+
"type": "searchset",
123+
"link": [
124+
{
125+
"relation": "self",
126+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191&type=http://snomed.info/sct|736253002",
127+
}
128+
],
78129
"total": 1,
79130
"entry": [{"resource": doc_ref.dict(exclude_none=True)}],
80131
}
@@ -101,6 +152,12 @@ def test_search_document_reference_no_results(repository: DocumentPointerReposit
101152
assert parsed_body == {
102153
"resourceType": "Bundle",
103154
"type": "searchset",
155+
"link": [
156+
{
157+
"relation": "self",
158+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191",
159+
}
160+
],
104161
"total": 0,
105162
"entry": [],
106163
}

layer/nrlf/core/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Config(BaseSettings):
88
All Environment Variables are validated using pydantic, and will result in
99
a 500 Internal Server Error if validation fails.
1010
11-
To add a new Environment Variable simply a new pydantic compatible
11+
To add a new Environment Variable simply add a new pydantic compatible
1212
definition below, and pydantic should allow for even complex validation
1313
logic to be supported.
1414
"""

tests/features/consumer/searchDocumentReference-success.feature

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Feature: Consumer - searchDocumentReference - Success Scenarios
1919
| subject | 9278693472 |
2020
Then the response status code is 200
2121
And the response is a searchset Bundle
22+
And the Bundle has a self link matching 'DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|9278693472'
2223
And the Bundle has a total of 1
2324
And the Bundle has 1 entry
2425
And the Bundle contains an DocumentReference with values
@@ -68,6 +69,7 @@ Feature: Consumer - searchDocumentReference - Success Scenarios
6869
| subject | 9278693472 |
6970
Then the response status code is 200
7071
And the response is a searchset Bundle
72+
And the Bundle has a self link matching 'DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|9278693472'
7173
And the Bundle has a total of 2
7274
And the Bundle has 2 entries
7375
And the Bundle contains an DocumentReference with values

tests/features/consumer/searchPostDocumentReference-success.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Feature: Consumer - searchDocumentReference - Success Scenarios
1919
| subject | 9278693472 |
2020
Then the response status code is 200
2121
And the response is a searchset Bundle
22+
And the Bundle has a self link matching 'DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|9278693472'
2223
And the Bundle has a total of 1
2324
And the Bundle has 1 entry
2425
And the Bundle contains an DocumentReference with values

tests/features/steps/3_assert.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,42 @@ def assert_bundle_total_step(context: Context, total: str):
6969
)
7070

7171

72+
@then("the Bundle has a self link matching '{rel_url}'")
73+
def assert_bundle_self(context: Context, rel_url: str):
74+
assert (
75+
context.bundle is not None
76+
), "The Bundle has not yet been parsed from the response"
77+
assert context.bundle.link is not None, format_error(
78+
"No links present in the Bundle",
79+
f"{context.base_url}{rel_url}",
80+
"None",
81+
context.response.text,
82+
)
83+
84+
assert len(context.bundle.link) == 1, format_error(
85+
"The Bundle's link array should contain a single item if no pagination is used",
86+
"1 entry",
87+
f"{len(context.bundle.link)} entries",
88+
context.response.text,
89+
)
90+
91+
link_entry = context.bundle.link[0].dict(exclude_none=True)
92+
assert link_entry.get("relation") == "self", format_error(
93+
"Link should specify a 'self' type relation",
94+
"self",
95+
link_entry.get("relation"),
96+
context.response.text,
97+
)
98+
99+
actual_url_params = link_entry.get("url").split("consumer/FHIR/R4/")[-1]
100+
assert actual_url_params == rel_url, format_error(
101+
"Link url does not specify the search parameters expected",
102+
rel_url,
103+
actual_url_params,
104+
context.response.text,
105+
)
106+
107+
72108
@then("the Bundle has {num_entries} entry")
73109
@then("the Bundle has {num_entries} entries")
74110
def assert_bundle_entries_step(context: Context, num_entries: str):

0 commit comments

Comments
 (0)