Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from nrlf.core.constants import (
PERMISSION_AUDIT_DATES_FROM_PAYLOAD,
PERMISSION_SUPERSEDE_IGNORE_DELETE_FAIL,
TYPE_CATEGORIES,
)
from nrlf.core.decorators import request_handler
from nrlf.core.dynamodb.repository import DocumentPointer, DocumentPointerRepository
Expand Down Expand Up @@ -91,19 +90,6 @@ def _check_permissions(
expression="type.coding[0].code",
)

type_category = TYPE_CATEGORIES.get(core_model.type)
if type_category != core_model.category:
logger.log(
LogReference.PROCREATE005a,
ods_code=metadata.ods_code,
type=core_model.type,
category=core_model.category,
)
return SpineErrorResponse.BAD_REQUEST(
diagnostics=f"The Category code of the provided document '{core_model.category}' must match the allowed category for pointer type '{core_model.type}' with a category value of '{type_category}'",
expression="category.coding[0].code",
)


def _get_document_ids_to_supersede(
resource: DocumentReference,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from unittest.mock import patch

from freeze_uuid import freeze_uuid
from freezegun import freeze_time
Expand Down Expand Up @@ -412,6 +413,55 @@ def test_create_document_reference_invalid_pointer_type():
result = handler(event, create_mock_context())
body = result.pop("body")

assert result == {
"statusCode": "400",
"headers": default_response_headers(),
"isBase64Encoded": False,
}

parsed_body = json.loads(body)

assert parsed_body == {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "value",
"details": {
"coding": [
{
"code": "INVALID_RESOURCE",
"display": "Invalid validation of resource",
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
}
]
},
"diagnostics": "Invalid type code: invalid Type must be a member of the England-NRLRecordType value set (https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType)",
"expression": ["type.coding[0].code"],
}
],
}


@mock_aws
@mock_repository
@patch("nrlf.core.decorators.parse_permissions_file")
def test_create_document_reference_pointer_type_not_allowed(
parse_permissions_mock, repository: DocumentPointerRepository
):
doc_ref = load_document_reference("Y05868-736253002-Valid")

assert doc_ref.type and doc_ref.type.coding

event = create_test_api_gateway_event(
headers=create_headers(),
body=doc_ref.model_dump_json(exclude_none=True),
)

parse_permissions_mock.return_value = ["invalid"]
result = handler(event, create_mock_context())
body = result.pop("body")

assert result == {
"statusCode": "403",
"headers": default_response_headers(),
Expand Down Expand Up @@ -470,12 +520,12 @@ def test_create_document_reference_invalid_category_type():
"issue": [
{
"severity": "error",
"code": "invalid",
"code": "value",
"details": {
"coding": [
{
"code": "BAD_REQUEST",
"display": "Bad request",
"code": "INVALID_RESOURCE",
"display": "Invalid validation of resource",
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
}
]
Expand Down Expand Up @@ -757,6 +807,7 @@ def test_create_document_reference_invalid_relatesto_type(

assert doc_ref.type and doc_ref.type.coding
doc_ref.type.coding[0].code = "861421000000109"
doc_ref.type.coding[0].display = "End of life care coordination summary"
doc_ref.relatesTo = [
DocumentReferenceRelatesTo(
code="transforms",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,8 @@ def test_update_document_reference_immutable_fields(repository):
id=None,
system="http://snomed.info/sct",
version=None,
code="1213324",
display="Some Code",
code="861421000000109",
display="End of life care coordination summary",
userSelected=None,
)
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from unittest.mock import patch

from freezegun import freeze_time
from moto import mock_aws
Expand Down Expand Up @@ -221,12 +222,12 @@ def test_upsert_document_reference_invalid_category_type():
"issue": [
{
"severity": "error",
"code": "invalid",
"code": "value",
"details": {
"coding": [
{
"code": "BAD_REQUEST",
"display": "Bad request",
"code": "INVALID_RESOURCE",
"display": "Invalid validation of resource",
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
}
]
Expand Down Expand Up @@ -538,6 +539,55 @@ def test_upsert_document_reference_invalid_pointer_type():
result = handler(event, create_mock_context())
body = result.pop("body")

assert result == {
"statusCode": "400",
"headers": default_response_headers(),
"isBase64Encoded": False,
}

parsed_body = json.loads(body)

assert parsed_body == {
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "error",
"code": "value",
"details": {
"coding": [
{
"code": "INVALID_RESOURCE",
"display": "Invalid validation of resource",
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
}
]
},
"diagnostics": "Invalid type code: invalid Type must be a member of the England-NRLRecordType value set (https://fhir.nhs.uk/England/CodeSystem/England-NRLRecordType)",
"expression": ["type.coding[0].code"],
},
],
}


@mock_aws
@mock_repository
@patch("nrlf.core.decorators.parse_permissions_file")
def test_upsert_document_reference_pointer_type_not_allowed(
parse_permissions_mock, repository: DocumentPointerRepository
):
doc_ref = load_document_reference("Y05868-736253002-Valid")

assert doc_ref.type and doc_ref.type.coding

event = create_test_api_gateway_event(
headers=create_headers(),
body=doc_ref.model_dump_json(exclude_none=True),
)

parse_permissions_mock.return_value = ["invalid"]
result = handler(event, create_mock_context())
body = result.pop("body")

assert result == {
"statusCode": "403",
"headers": default_response_headers(),
Expand Down Expand Up @@ -848,6 +898,7 @@ def test_upsert_document_reference_invalid_relatesto_type(

assert doc_ref.type and doc_ref.type.coding
doc_ref.type.coding[0].code = "861421000000109"
doc_ref.type.coding[0].display = "End of life care coordination summary"
doc_ref.relatesTo = [
DocumentReferenceRelatesTo(
code="transforms",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from nrlf.core.constants import (
PERMISSION_AUDIT_DATES_FROM_PAYLOAD,
PERMISSION_SUPERSEDE_IGNORE_DELETE_FAIL,
TYPE_CATEGORIES,
)
from nrlf.core.decorators import request_handler
from nrlf.core.dynamodb.repository import DocumentPointer, DocumentPointerRepository
Expand Down Expand Up @@ -89,19 +88,6 @@ def _check_permissions(
expression="type.coding[0].code",
)

type_category = TYPE_CATEGORIES.get(core_model.type)
if type_category != core_model.category:
logger.log(
LogReference.PROUPSERT005a,
ods_code=metadata.ods_code,
type=core_model.type,
category=core_model.category,
)
return SpineErrorResponse.BAD_REQUEST(
diagnostics=f"The Category code of the provided document '{core_model.category}' must match the allowed category for pointer type '{core_model.type}' with a category value of '{type_category}'",
expression="category.coding[0].code",
)


def _get_document_ids_to_supersede(
resource: DocumentReference,
Expand Down
45 changes: 45 additions & 0 deletions layer/nrlf/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,51 @@ def coding_value(self):
},
}

TYPE_ATTRIBUTES = {
PointerTypes.MENTAL_HEALTH_PLAN.value: {
"display": "Mental health crisis plan",
},
PointerTypes.EMERGENCY_HEALTHCARE_PLAN.value: {
"display": "Emergency health care plan",
},
PointerTypes.EOL_COORDINATION_SUMMARY.value: {
"display": "End of life care coordination summary",
},
PointerTypes.RESPECT_FORM.value: {
"display": "ReSPECT (Recommended Summary Plan for Emergency Care and Treatment) form",
},
PointerTypes.NEWS2_CHART.value: {
"display": "Royal College of Physicians NEWS2 (National Early Warning Score 2) chart",
},
PointerTypes.CONTINGENCY_PLAN.value: {
"display": "Contingency plan",
},
PointerTypes.EOL_CARE_PLAN.value: {
"display": "End of life care plan",
},
PointerTypes.LLOYD_GEORGE_FOLDER.value: {
"display": "Lloyd George record folder",
},
PointerTypes.ADVANCED_CARE_PLAN.value: {
"display": "Advanced care plan",
},
PointerTypes.TREATMENT_ESCALATION_PLAN.value: {
"display": "Treatment escalation plan",
},
PointerTypes.SUMMARY_RECORD.value: {
"display": "Summary record",
},
PointerTypes.PERSONALISED_CARE_AND_SUPPORT_PLAN.value: {
"display": "Personalised Care and Support Plan",
},
PointerTypes.MRA_UPPER_LIMB_ARTERY.value: {
"display": "MRA Upper Limb Rt",
},
PointerTypes.MRI_AXILLA_BOTH.value: {
"display": "MRI Axilla Both",
},
}

TYPE_CATEGORIES = {
#
# Care plans
Expand Down
6 changes: 1 addition & 5 deletions layer/nrlf/core/log_references.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,7 @@ class LogReference(Enum):
)
PROCREATE005 = _Reference(
"WARN", "Organisation is not allowed to create pointer type"
) #
PROCREATE005a = _Reference(
"WARN",
"Organisation is not allowed to create pointer type with incorrect category",
) #
)
PROCREATE006 = _Reference("DEBUG", "Performing relatesTo validation on resource")
PROCREATE007a = _Reference(
"WARN", "RelatesTo validation failed - no target identifier value"
Expand Down
Loading