Skip to content

Commit 39645c0

Browse files
Merge branch 'develop' into feature/kabo5-NRL519-validate-practice-setting
2 parents 2aef83d + 8ab94e2 commit 39645c0

16 files changed

+653
-24
lines changed

.github/workflows/pr-env-deploy.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,53 @@ jobs:
264264
- name: Run Integration Tests
265265
run: make test-features-integration TF_WORKSPACE_NAME=${{ needs.set-environment-id.outputs.environment_id }}
266266

267+
smoke-test:
268+
name: Run Smoke Tests
269+
needs: [set-environment-id, integration-test]
270+
environment: pull-request
271+
runs-on: [self-hosted, ci]
272+
steps:
273+
- name: Git Clone - ${{ github.event.pull_request.head.ref }}
274+
uses: actions/checkout@v4
275+
with:
276+
ref: ${{ github.event.pull_request.head.ref }}
277+
278+
- name: Setup asdf cache
279+
uses: actions/cache@v4
280+
with:
281+
path: ~/.asdf
282+
key: ${{ runner.os }}-asdf-${{ hashFiles('**/.tool-versions') }}
283+
restore-keys: |
284+
${{ runner.os }}-asdf-
285+
286+
- name: Install asdf and tools
287+
uses: asdf-vm/actions/[email protected]
288+
with:
289+
asdf_branch: v0.13.1
290+
291+
- name: Setup Python environment
292+
run: |
293+
poetry install --no-root
294+
source $(poetry env info --path)/bin/activate
295+
296+
- name: Configure Management Credentials
297+
uses: aws-actions/configure-aws-credentials@v4
298+
with:
299+
aws-region: eu-west-2
300+
role-to-assume: ${{ secrets.MGMT_ROLE_ARN }}
301+
role-session-name: github-actions-ci-${{ needs.set-environment-id.outputs.environment_id }}
302+
303+
- name: Terraform Init
304+
run: |
305+
terraform -chdir=terraform/infrastructure init
306+
terraform -chdir=terraform/infrastructure workspace new ${{ needs.set-environment-id.outputs.environment_id }} || \
307+
terraform -chdir=terraform/infrastructure workspace select ${{ needs.set-environment-id.outputs.environment_id }}
308+
309+
- name: Smoke Test
310+
run: |
311+
make ENV=dev truststore-pull-client
312+
make ENV=dev TF_WORKSPACE_NAME=${{ needs.set-environment-id.outputs.environment_id }} test-smoke-internal
313+
267314
performance-test:
268315
name: Run Performance Tests
269316
needs: [set-environment-id, integration-test]

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Crown Copyright
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

api/consumer/searchDocumentReference/search_document_reference.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ def handler(
5858
expression="type",
5959
)
6060

61-
if not validate_category(params.category):
61+
categories = params.category.root.split(",") if params.category else []
62+
if not validate_category(categories):
6263
logger.log(
6364
LogReference.CONSEARCH002b,
6465
category=params.category,
@@ -102,7 +103,7 @@ def handler(
102103
nhs_number=params.nhs_number,
103104
custodian=custodian_id,
104105
pointer_types=pointer_types,
105-
categories=[params.category.root] if params.category else [],
106+
categories=categories,
106107
):
107108
try:
108109
document_reference = DocumentReference.model_validate_json(result.document)

api/consumer/searchDocumentReference/tests/test_search_document_reference_consumer.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
from moto import mock_aws
44

55
from api.consumer.searchDocumentReference.search_document_reference import handler
6+
from nrlf.core.constants import (
7+
CATEGORY_ATTRIBUTES,
8+
TYPE_ATTRIBUTES,
9+
Categories,
10+
PointerTypes,
11+
)
612
from nrlf.core.dynamodb.repository import DocumentPointer, DocumentPointerRepository
713
from nrlf.tests.data import load_document_reference
814
from nrlf.tests.dynamodb import mock_repository
@@ -144,6 +150,19 @@ def test_search_document_reference_happy_path_with_category(
144150
doc_pointer = DocumentPointer.from_document_reference(doc_ref)
145151
repository.create(doc_pointer)
146152

153+
# Second pointer different category
154+
doc_ref2 = load_document_reference("Y05868-736253002-Valid")
155+
doc_ref2.id = "Y05868-736253002-Valid2"
156+
doc_ref2.type.coding[0].code = PointerTypes.NEWS2_CHART.coding_value()
157+
doc_ref2.type.coding[0].display = TYPE_ATTRIBUTES.get(
158+
PointerTypes.NEWS2_CHART.value
159+
).get("display")
160+
doc_ref2.category[0].coding[0].code = Categories.OBSERVATIONS.coding_value()
161+
doc_ref2.category[0].coding[0].display = CATEGORY_ATTRIBUTES.get(
162+
Categories.OBSERVATIONS.value
163+
).get("display")
164+
repository.create(DocumentPointer.from_document_reference(doc_ref2))
165+
147166
event = create_test_api_gateway_event(
148167
headers=create_headers(),
149168
query_string_parameters={
@@ -160,7 +179,6 @@ def test_search_document_reference_happy_path_with_category(
160179
"headers": default_response_headers(),
161180
"isBase64Encoded": False,
162181
}
163-
164182
parsed_body = json.loads(body)
165183
assert parsed_body == {
166184
"resourceType": "Bundle",
@@ -176,6 +194,63 @@ def test_search_document_reference_happy_path_with_category(
176194
}
177195

178196

197+
@mock_aws
198+
@mock_repository
199+
def test_search_document_reference_happy_path_with_multiple_categories(
200+
repository: DocumentPointerRepository,
201+
):
202+
doc_ref = load_document_reference("Y05868-736253002-Valid")
203+
doc_pointer = DocumentPointer.from_document_reference(doc_ref)
204+
repository.create(doc_pointer)
205+
206+
# Second pointer different category
207+
doc_ref2 = load_document_reference("Y05868-736253002-Valid")
208+
doc_ref2.id = "Y05868-736253002-Valid2"
209+
doc_ref2.type.coding[0].code = PointerTypes.NEWS2_CHART.coding_value()
210+
doc_ref2.type.coding[0].display = TYPE_ATTRIBUTES.get(
211+
PointerTypes.NEWS2_CHART.value
212+
).get("display")
213+
doc_ref2.category[0].coding[0].code = Categories.OBSERVATIONS.coding_value()
214+
doc_ref2.category[0].coding[0].display = CATEGORY_ATTRIBUTES.get(
215+
Categories.OBSERVATIONS.value
216+
).get("display")
217+
repository.create(DocumentPointer.from_document_reference(doc_ref2))
218+
219+
event = create_test_api_gateway_event(
220+
headers=create_headers(),
221+
query_string_parameters={
222+
"subject:identifier": "https://fhir.nhs.uk/Id/nhs-number|6700028191",
223+
"category": "http://snomed.info/sct|734163000,http://snomed.info/sct|1102421000000108",
224+
},
225+
)
226+
227+
result = handler(event, create_mock_context())
228+
body = result.pop("body")
229+
230+
assert result == {
231+
"statusCode": "200",
232+
"headers": default_response_headers(),
233+
"isBase64Encoded": False,
234+
}
235+
236+
parsed_body = json.loads(body)
237+
assert parsed_body == {
238+
"resourceType": "Bundle",
239+
"type": "searchset",
240+
"link": [
241+
{
242+
"relation": "self",
243+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191&category=http://snomed.info/sct|734163000,http://snomed.info/sct|1102421000000108",
244+
}
245+
],
246+
"total": 2,
247+
"entry": [
248+
{"resource": doc_ref2.model_dump(exclude_none=True)},
249+
{"resource": doc_ref.model_dump(exclude_none=True)},
250+
],
251+
}
252+
253+
179254
@mock_aws
180255
@mock_repository
181256
def test_search_document_reference_happy_path_with_nicip_type(

api/consumer/searchPostDocumentReference/search_post_document_reference.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ def handler(
6161
expression="type",
6262
)
6363

64-
if not validate_category(body.category):
64+
categories = body.category.root.split(",") if body.category else []
65+
if not validate_category(categories):
6566
logger.log(
6667
LogReference.CONPOSTSEARCH002b,
67-
type=body.category,
68+
category=body.category,
6869
) # TODO - Should update error message once permissioning by category is implemented
6970
return SpineErrorResponse.INVALID_CODE_SYSTEM(
7071
diagnostics="The provided category is not valid",
@@ -105,7 +106,7 @@ def handler(
105106
nhs_number=body.nhs_number,
106107
custodian=custodian_id,
107108
pointer_types=pointer_types,
108-
categories=[body.category.root] if body.category else [],
109+
categories=categories,
109110
):
110111
try:
111112
document_reference = DocumentReference.model_validate_json(result.document)

api/consumer/searchPostDocumentReference/tests/test_search_post_document_reference_consumer.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
from api.consumer.searchPostDocumentReference.search_post_document_reference import (
66
handler,
77
)
8+
from nrlf.core.constants import (
9+
CATEGORY_ATTRIBUTES,
10+
TYPE_ATTRIBUTES,
11+
Categories,
12+
PointerTypes,
13+
)
814
from nrlf.core.dynamodb.repository import DocumentPointer, DocumentPointerRepository
915
from nrlf.tests.data import load_document_reference
1016
from nrlf.tests.dynamodb import mock_repository
@@ -153,6 +159,19 @@ def test_search_post_document_reference_happy_path_with_category(
153159
doc_pointer = DocumentPointer.from_document_reference(doc_ref)
154160
repository.create(doc_pointer)
155161

162+
# Second pointer different category
163+
doc_ref2 = load_document_reference("Y05868-736253002-Valid")
164+
doc_ref2.id = "Y05868-736253002-Valid2"
165+
doc_ref2.type.coding[0].code = PointerTypes.NEWS2_CHART.coding_value()
166+
doc_ref2.type.coding[0].display = TYPE_ATTRIBUTES.get(
167+
PointerTypes.NEWS2_CHART.value
168+
).get("display")
169+
doc_ref2.category[0].coding[0].code = Categories.OBSERVATIONS.coding_value()
170+
doc_ref2.category[0].coding[0].display = CATEGORY_ATTRIBUTES.get(
171+
Categories.OBSERVATIONS.value
172+
).get("display")
173+
repository.create(DocumentPointer.from_document_reference(doc_ref2))
174+
156175
event = create_test_api_gateway_event(
157176
headers=create_headers(),
158177
body=json.dumps(
@@ -187,6 +206,65 @@ def test_search_post_document_reference_happy_path_with_category(
187206
}
188207

189208

209+
@mock_aws
210+
@mock_repository
211+
def test_search_post_document_reference_happy_path_with_multiple_categories(
212+
repository: DocumentPointerRepository,
213+
):
214+
doc_ref = load_document_reference("Y05868-736253002-Valid")
215+
doc_pointer = DocumentPointer.from_document_reference(doc_ref)
216+
repository.create(doc_pointer)
217+
218+
# Second pointer different category
219+
doc_ref2 = load_document_reference("Y05868-736253002-Valid")
220+
doc_ref2.id = "Y05868-736253002-Valid2"
221+
doc_ref2.type.coding[0].code = PointerTypes.NEWS2_CHART.coding_value()
222+
doc_ref2.type.coding[0].display = TYPE_ATTRIBUTES.get(
223+
PointerTypes.NEWS2_CHART.value
224+
).get("display")
225+
doc_ref2.category[0].coding[0].code = Categories.OBSERVATIONS.coding_value()
226+
doc_ref2.category[0].coding[0].display = CATEGORY_ATTRIBUTES.get(
227+
Categories.OBSERVATIONS.value
228+
).get("display")
229+
repository.create(DocumentPointer.from_document_reference(doc_ref2))
230+
231+
event = create_test_api_gateway_event(
232+
headers=create_headers(),
233+
body=json.dumps(
234+
{
235+
"subject:identifier": "https://fhir.nhs.uk/Id/nhs-number|6700028191",
236+
"category": "http://snomed.info/sct|734163000,http://snomed.info/sct|1102421000000108",
237+
}
238+
),
239+
)
240+
241+
result = handler(event, create_mock_context())
242+
body = result.pop("body")
243+
244+
assert result == {
245+
"statusCode": "200",
246+
"headers": default_response_headers(),
247+
"isBase64Encoded": False,
248+
}
249+
250+
parsed_body = json.loads(body)
251+
assert parsed_body == {
252+
"resourceType": "Bundle",
253+
"type": "searchset",
254+
"link": [
255+
{
256+
"relation": "self",
257+
"url": "https://pytest.api.service.nhs.uk/record-locator/consumer/FHIR/R4/DocumentReference?subject:identifier=https://fhir.nhs.uk/Id/nhs-number|6700028191&category=http://snomed.info/sct|734163000,http://snomed.info/sct|1102421000000108",
258+
}
259+
],
260+
"total": 2,
261+
"entry": [
262+
{"resource": doc_ref2.model_dump(exclude_none=True)},
263+
{"resource": doc_ref.model_dump(exclude_none=True)},
264+
],
265+
}
266+
267+
190268
@mock_aws
191269
@mock_repository
192270
def test_search_document_reference_no_results(repository: DocumentPointerRepository):

api/producer/searchDocumentReference/search_document_reference.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@ def handler(
5959
expression="type",
6060
)
6161

62-
if not validate_category(params.category):
62+
categories = params.category.root.split(",") if params.category else []
63+
if not validate_category(categories):
6364
logger.log(
6465
LogReference.PROSEARCH002b,
65-
type=params.category,
66+
category=params.category,
6667
) # TODO - Should update error message once permissioning by category is implemented
6768
return SpineErrorResponse.INVALID_CODE_SYSTEM(
6869
diagnostics="Invalid query parameter (The provided category is not valid)",
@@ -78,15 +79,15 @@ def handler(
7879
custodian_suffix=metadata.ods_code_extension,
7980
nhs_number=params.nhs_number,
8081
pointer_types=pointer_types,
81-
categories=[params.category.root] if params.category else [],
82+
categories=params.category.root.split(",") if params.category else [],
8283
)
8384

8485
for result in repository.search(
8586
custodian=metadata.ods_code,
8687
custodian_suffix=metadata.ods_code_extension,
8788
nhs_number=params.nhs_number,
8889
pointer_types=pointer_types,
89-
categories=[params.category.root] if params.category else [],
90+
categories=params.category.root.split(",") if params.category else [],
9091
):
9192
try:
9293
document_reference = DocumentReference.model_validate_json(result.document)

0 commit comments

Comments
 (0)