Skip to content

Commit d3f26c6

Browse files
NRL-518 Add validator method for content and add tests
1 parent 84a5f10 commit d3f26c6

File tree

5 files changed

+515
-0
lines changed

5 files changed

+515
-0
lines changed

layer/nrlf/core/tests/test_validators.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,3 +1562,96 @@ def test_validate_content_extension_invalid_code_and_display_mismatch():
15621562
"content[0].extension[0].valueCodeableConcept.coding[0].display"
15631563
],
15641564
}
1565+
1566+
def test_validate_content_missing_attachment():
1567+
validator = DocumentReferenceValidator()
1568+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
1569+
1570+
document_ref_data["content"][0].pop("attachment")
1571+
1572+
result = validator.validate(document_ref_data)
1573+
1574+
assert result.is_valid is False
1575+
assert len(result.issues) == 1
1576+
assert result.issues[0].model_dump(exclude_none=True) == {
1577+
"severity": "error",
1578+
"code": "required",
1579+
"details": {
1580+
"coding": [
1581+
{
1582+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
1583+
"code": "INVALID_RESOURCE",
1584+
"display": "Invalid validation of resource",
1585+
}
1586+
]
1587+
},
1588+
"diagnostics": "Missing attachment in content",
1589+
"expression": ["content[0].attachment"],
1590+
}
1591+
1592+
def test_validate_content_missing_content_type():
1593+
validator = DocumentReferenceValidator()
1594+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
1595+
1596+
document_ref_data["content"][0]["attachment"].pop("contentType")
1597+
1598+
result = validator.validate(document_ref_data)
1599+
1600+
assert result.is_valid is False
1601+
assert len(result.issues) == 1
1602+
assert result.issues[0].model_dump(exclude_none=True) == {
1603+
"severity": "error",
1604+
"code": "required",
1605+
"details": {
1606+
"coding": [
1607+
{
1608+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
1609+
"code": "INVALID_RESOURCE",
1610+
"display": "Invalid validation of resource",
1611+
}
1612+
]
1613+
},
1614+
"diagnostics": "Missing contentType in content.attachment",
1615+
"expression": ["content[0].attachment.contentType"],
1616+
}
1617+
1618+
def test_validate_content_invalid_content_type():
1619+
validator = DocumentReferenceValidator()
1620+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
1621+
1622+
document_ref_data["content"][0]["attachment"][
1623+
"contentType"
1624+
] = "invalid/type"
1625+
1626+
result = validator.validate(document_ref_data)
1627+
1628+
assert result.is_valid is False
1629+
assert len(result.issues) == 1
1630+
assert result.issues[0].model_dump(exclude_none=True) == {
1631+
"severity": "error",
1632+
"code": "value",
1633+
"details": {
1634+
"coding": [
1635+
{
1636+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
1637+
"code": "INVALID_RESOURCE",
1638+
"display": "Invalid validation of resource",
1639+
}
1640+
]
1641+
},
1642+
"diagnostics": "Invalid contentType: invalid/type. Must be 'application/pdf' or 'text/html'",
1643+
"expression": ["content[0].attachment.contentType"],
1644+
}
1645+
1646+
def test_validate_content_valid_content_type():
1647+
validator = DocumentReferenceValidator()
1648+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
1649+
1650+
document_ref_data["content"][0]["attachment"][
1651+
"contentType"
1652+
] = "application/pdf"
1653+
1654+
result = validator.validate(document_ref_data)
1655+
1656+
assert result.is_valid is True
1657+
assert result.issues == []

layer/nrlf/core/validators.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ def validate(self, data: Dict[str, Any] | DocumentReference):
141141
self._validate_category(resource)
142142
self._validate_author(resource)
143143
self._validate_type_category_mapping(resource)
144+
self._validate_content(resource)
144145
if resource.content[0].extension:
145146
self._validate_content_extension(resource)
146147

@@ -603,3 +604,36 @@ def _validate_author(self, model: DocumentReference):
603604
field=f"author[0].identifier.value",
604605
)
605606
return
607+
608+
def _validate_content(self, model: DocumentReference):
609+
"""
610+
Validate that the contentType is present and is either 'application/pdf' or 'text/html'.
611+
"""
612+
logger.log(LogReference.VALIDATOR001, step="content")
613+
614+
for i, content in enumerate(model.content):
615+
if not content.attachment:
616+
self.result.add_error(
617+
issue_code="required",
618+
error_code="INVALID_RESOURCE",
619+
diagnostics="Missing attachment in content",
620+
field=f"content[{i}].attachment",
621+
)
622+
continue
623+
624+
if not content.attachment.contentType:
625+
self.result.add_error(
626+
issue_code="required",
627+
error_code="INVALID_RESOURCE",
628+
diagnostics="Missing contentType in content.attachment",
629+
field=f"content[{i}].attachment.contentType",
630+
)
631+
continue
632+
633+
if content.attachment.contentType not in ["application/pdf", "text/html"]:
634+
self.result.add_error(
635+
issue_code="value",
636+
error_code="INVALID_RESOURCE",
637+
diagnostics=f"Invalid contentType: {content.attachment.contentType}. Must be 'application/pdf' or 'text/html'",
638+
field=f"content[{i}].attachment.contentType",
639+
)

tests/features/producer/createDocumentReference-failure.feature

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,3 +655,117 @@ Feature: Producer - createDocumentReference - Failure Scenarios
655655
| type-system | type-code | category-code | type-display | correct-display |
656656
| https://nicip.nhs.uk | MAULR | 721981007 | "Nonsense display" | MRA Upper Limb Rt |
657657
| https://nicip.nhs.uk | MAXIB | 103693007 | "Nonsense display" | MRI Axilla Both |
658+
659+
Scenario: Missing content
660+
Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API
661+
And the organisation 'TSTCUS' is authorised to access pointer types:
662+
| system | value |
663+
| http://snomed.info/sct | 736253002 |
664+
When producer 'TSTCUS' requests creation of a DocumentReference with default test values except 'content' is:
665+
"""
666+
"content": []
667+
"""
668+
Then the response status code is 400
669+
And the response is an OperationOutcome with 1 issue
670+
And the OperationOutcome contains the issue:
671+
"""
672+
{
673+
"severity": "error",
674+
"code": "invalid",
675+
"details": {
676+
"coding": [
677+
{
678+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
679+
"code": "MESSAGE_NOT_WELL_FORMED",
680+
"display": "Message not well formed"
681+
}
682+
]
683+
},
684+
"diagnostics": "Request body could not be parsed (content: List should have at least 1 item after validation, not 0)",
685+
"expression": [
686+
"content"
687+
]
688+
}
689+
"""
690+
691+
Scenario: Missing contentType
692+
Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API
693+
And the organisation 'TSTCUS' is authorised to access pointer types:
694+
| system | value |
695+
| http://snomed.info/sct | 736253002 |
696+
When producer 'TSTCUS' requests creation of a DocumentReference with default test values except 'content' is:
697+
"""
698+
"content": [
699+
{
700+
"attachment": {
701+
"contentType": "",
702+
"url": "https://spine-proxy.national.ncrs.nhs.uk/https%3A%2F%2Fp1.nhs.uk%2FMentalhealthCrisisPlanReport.pdf"
703+
},
704+
"format": {
705+
"system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode",
706+
"code": "urn:nhs-ic:unstructured",
707+
"display": "Unstructured document"
708+
}
709+
}
710+
]
711+
"""
712+
Then the response status code is 400
713+
And the response is an OperationOutcome with 1 issue
714+
And the OperationOutcome contains the issue:
715+
"""
716+
{
717+
"severity": "error",
718+
"code": "invalid",
719+
"details": {
720+
"coding": [
721+
{
722+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
723+
"code": "MESSAGE_NOT_WELL_FORMED",
724+
"display": "Message not well formed"
725+
}
726+
]
727+
},
728+
"diagnostics": "Request body could not be parsed (content.0.attachment.contentType: String should match pattern '^(application|audio|image|message|model|multipart|text|video)/[a-zA-Z0-9!#$&^_+.-]+(;[a-zA-Z0-9!#$&^_+.-]+=[a-zA-Z0-9!#$&^_+.-]+)*$')",
729+
"expression": [
730+
"content.0.attachment.contentType"
731+
]
732+
}
733+
"""
734+
735+
Scenario: Invalid contentType
736+
Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API
737+
And the organisation 'ANGY1' is authorised to access pointer types:
738+
| system | value |
739+
| http://snomed.info/sct | 736253002 |
740+
When producer 'ANGY1' creates a DocumentReference with values:
741+
| property | value |
742+
| subject | 9999999999 |
743+
| status | current |
744+
| type | 736253002 |
745+
| category | 734163000 |
746+
| custodian | ANGY1 |
747+
| author | HAR1 |
748+
| url | https://example.org/my-doc.pdf |
749+
| contentType | application/invalid |
750+
Then the response status code is 400
751+
And the response is an OperationOutcome with 1 issue
752+
And the OperationOutcome contains the issue:
753+
"""
754+
{
755+
"severity": "error",
756+
"code": "value",
757+
"details": {
758+
"coding": [
759+
{
760+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
761+
"code": "INVALID_RESOURCE",
762+
"display": "Invalid validation of resource"
763+
}
764+
]
765+
},
766+
"diagnostics": "Invalid contentType: application/invalid. Must be 'application/pdf' or 'text/html'",
767+
"expression": [
768+
"content[0].attachment.contentType"
769+
]
770+
}
771+
"""

0 commit comments

Comments
 (0)