Skip to content

Commit 6a0cfd9

Browse files
committed
NRL-474 validate author system, length and value format
1 parent fc29146 commit 6a0cfd9

File tree

4 files changed

+266
-17
lines changed

4 files changed

+266
-17
lines changed

layer/nrlf/core/constants.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@ class Source(Enum):
88

99
VALID_SOURCES = frozenset(item.value for item in Source.__members__.values())
1010
EMPTY_VALUES = ("", None, [], {})
11-
REQUIRED_CREATE_FIELDS = ["custodian", "id", "type", "status", "subject", "category"]
11+
REQUIRED_CREATE_FIELDS = [
12+
"custodian",
13+
"id",
14+
"type",
15+
"status",
16+
"subject",
17+
"category",
18+
"author",
19+
]
1220
JSON_TYPES = {dict, list}
1321
NHS_NUMBER_INDEX = "idx_nhs_number_by_id"
1422
ID_SEPARATOR = "-"

layer/nrlf/core/tests/test_validators.py

Lines changed: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from nrlf.core.constants import PointerTypes
5+
from nrlf.core.constants import ODS_SYSTEM, PointerTypes
66
from nrlf.core.errors import ParseError
77
from nrlf.core.validators import (
88
DocumentReferenceValidator,
@@ -599,22 +599,44 @@ def test_validate_category_coding_invalid_system():
599599
}
600600

601601

602-
def test_validate_content_extension_too_many_extensions():
602+
def test_validate_author_no_author():
603603
validator = DocumentReferenceValidator()
604604
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
605605

606-
document_ref_data["content"][0]["extension"].append(
606+
del document_ref_data["author"]
607+
608+
result = validator.validate(document_ref_data)
609+
610+
assert result.is_valid is False
611+
assert result.resource.id == "Y05868-99999-99999-999999"
612+
assert len(result.issues) == 1
613+
assert result.issues[0].model_dump(exclude_none=True) == {
614+
"severity": "error",
615+
"code": "required",
616+
"details": {
617+
"coding": [
618+
{
619+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
620+
"code": "INVALID_RESOURCE",
621+
"display": "Invalid validation of resource",
622+
}
623+
]
624+
},
625+
"diagnostics": "The required field 'author' is missing",
626+
"expression": ["author"],
627+
}
628+
629+
630+
def test_validate_author_too_many_authors():
631+
validator = DocumentReferenceValidator()
632+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
633+
634+
document_ref_data["author"].append(
607635
{
608-
"url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability",
609-
"valueCodeableConcept": {
610-
"coding": [
611-
{
612-
"system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLContentStability",
613-
"code": "static",
614-
"display": "static",
615-
}
616-
]
617-
},
636+
"identifier": {
637+
"system": ODS_SYSTEM,
638+
"value": "someODSCode",
639+
}
618640
}
619641
)
620642

@@ -635,8 +657,107 @@ def test_validate_content_extension_too_many_extensions():
635657
}
636658
]
637659
},
638-
"diagnostics": "Invalid content extension length: 2 Extension must only contain a single value",
639-
"expression": ["content[0].extension"],
660+
"diagnostics": "Invalid author length: 2 Author must only contain a single value",
661+
"expression": ["author"],
662+
}
663+
664+
665+
def test_validate_author_system_invalid():
666+
validator = DocumentReferenceValidator()
667+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
668+
669+
document_ref_data["author"][0] = {
670+
"identifier": {
671+
"system": "some system",
672+
"value": "someODSCode",
673+
}
674+
}
675+
676+
result = validator.validate(document_ref_data)
677+
678+
assert result.is_valid is False
679+
assert result.resource.id == "Y05868-99999-99999-999999"
680+
assert len(result.issues) == 1
681+
assert result.issues[0].model_dump(exclude_none=True) == {
682+
"severity": "error",
683+
"code": "invalid",
684+
"details": {
685+
"coding": [
686+
{
687+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
688+
"code": "INVALID_RESOURCE",
689+
"display": "Invalid validation of resource",
690+
}
691+
]
692+
},
693+
"diagnostics": f"Invalid author system: 'some system' Author system must be 'https://fhir.nhs.uk/Id/ods-organization-code'",
694+
"expression": ["author[0].identifier.system"],
695+
}
696+
697+
698+
def test_validate_author_value_invalid():
699+
validator = DocumentReferenceValidator()
700+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
701+
702+
document_ref_data["author"][0] = {
703+
"identifier": {
704+
"system": ODS_SYSTEM,
705+
"value": "!!!!!!12sd",
706+
}
707+
}
708+
709+
result = validator.validate(document_ref_data)
710+
711+
assert result.is_valid is False
712+
assert result.resource.id == "Y05868-99999-99999-999999"
713+
assert len(result.issues) == 1
714+
assert result.issues[0].model_dump(exclude_none=True) == {
715+
"severity": "error",
716+
"code": "value",
717+
"details": {
718+
"coding": [
719+
{
720+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
721+
"code": "INVALID_RESOURCE",
722+
"display": "Invalid validation of resource",
723+
}
724+
]
725+
},
726+
"diagnostics": f"Invalid author value: '!!!!!!12sd' Author value must be alphanumeric",
727+
"expression": ["author[0].identifier.value"],
728+
}
729+
730+
731+
def test_validate_author_value_too_long():
732+
validator = DocumentReferenceValidator()
733+
document_ref_data = load_document_reference_json("Y05868-736253002-Valid")
734+
735+
document_ref_data["author"][0] = {
736+
"identifier": {
737+
"system": ODS_SYSTEM,
738+
"value": "d1111111111111111111111111111111111111111111111",
739+
}
740+
}
741+
742+
result = validator.validate(document_ref_data)
743+
744+
assert result.is_valid is False
745+
assert result.resource.id == "Y05868-99999-99999-999999"
746+
assert len(result.issues) == 1
747+
assert result.issues[0].model_dump(exclude_none=True) == {
748+
"severity": "error",
749+
"code": "value",
750+
"details": {
751+
"coding": [
752+
{
753+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
754+
"code": "INVALID_RESOURCE",
755+
"display": "Invalid validation of resource",
756+
}
757+
]
758+
},
759+
"diagnostics": f"Invalid author value: 'd1111111111111111111111111111111111111111111111' Author value must be less than 13 characters",
760+
"expression": ["author[0].identifier.value"],
640761
}
641762

642763

layer/nrlf/core/validators.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pydantic import ValidationError
66

77
from nrlf.core.codes import SpineErrorConcept
8-
from nrlf.core.constants import CATEGORY_ATTRIBUTES, REQUIRED_CREATE_FIELDS
8+
from nrlf.core.constants import CATEGORY_ATTRIBUTES, ODS_SYSTEM, REQUIRED_CREATE_FIELDS
99
from nrlf.core.errors import ParseError
1010
from nrlf.core.logger import LogReference, logger
1111
from nrlf.core.types import DocumentReference, OperationOutcomeIssue, RequestQueryType
@@ -118,6 +118,7 @@ def validate(self, data: Dict[str, Any] | DocumentReference):
118118
self._validate_relates_to(resource)
119119
self._validate_ssp_asid(resource)
120120
self._validate_category(resource)
121+
self._validate_author(resource)
121122
if resource.content[0].extension:
122123
self._validate_content_extension(resource)
123124

@@ -464,3 +465,48 @@ def _validate_content_extension(self, model: DocumentReference):
464465
field=f"content[{i}].extension[0].url",
465466
)
466467
return
468+
469+
def _validate_author(self, model: DocumentReference):
470+
"""
471+
Validate the author field contains an appropriate coding system and code.
472+
"""
473+
logger.log(LogReference.VALIDATOR001, step="author")
474+
475+
if len(model.author) > 1:
476+
self.result.add_error(
477+
issue_code="invalid",
478+
error_code="INVALID_RESOURCE",
479+
diagnostics=f"Invalid author length: {len(model.author)} Author must only contain a single value",
480+
field=f"author",
481+
)
482+
return
483+
484+
logger.debug("Validating author")
485+
identifier = model.author[0].identifier
486+
487+
if identifier.system != ODS_SYSTEM:
488+
self.result.add_error(
489+
issue_code="invalid",
490+
error_code="INVALID_IDENTIFIER_SYSTEM",
491+
diagnostics=f"Invalid author system: '{identifier.system}' Author system must be '{ODS_SYSTEM}'",
492+
field=f"author[0].identifier.system",
493+
)
494+
return
495+
496+
if not identifier.value.isalnum():
497+
self.result.add_error(
498+
issue_code="value",
499+
error_code="INVALID_RESOURCE",
500+
diagnostics=f"Invalid author value: '{identifier.value}' Author value must be alphanumeric",
501+
field=f"author[0].identifier.value",
502+
)
503+
return
504+
505+
if len(identifier.value) > 12:
506+
self.result.add_error(
507+
issue_code="value",
508+
error_code="INVALID_RESOURCE",
509+
diagnostics=f"Invalid author value: '{identifier.value}' Author value must be less than 13 characters",
510+
field=f"author[0].identifier.value",
511+
)
512+
return

tests/features/producer/createDocumentReference-failure.feature

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,80 @@ Feature: Producer - createDocumentReference - Failure Scenarios
120120
}
121121
"""
122122

123+
Scenario: Invalid Author
124+
Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API
125+
And the organisation 'TSTCUS' is authorised to access pointer types:
126+
| system | value |
127+
| http://snomed.info/sct | 736253002 |
128+
When producer 'TSTCUS' requests creation of a DocumentReference with default test values except 'author' is:
129+
"""
130+
"author":[{
131+
"identifier": {
132+
"system": "https://fhir.nhs.uk/Id/ods-organization-code",
133+
"value": "!!!!!"
134+
}
135+
}]
136+
"""
137+
Then the response status code is 400
138+
And the response is an OperationOutcome with 1 issue
139+
And the OperationOutcome contains the issue:
140+
"""
141+
{
142+
"severity": "error",
143+
"code": "value",
144+
"details": {
145+
"coding": [
146+
{
147+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
148+
"code": "INVALID_RESOURCE",
149+
"display": "Invalid validation of resource"
150+
}
151+
]
152+
},
153+
"diagnostics": "Invalid author value: '!!!!!' Author value must be alphanumeric",
154+
"expression": [
155+
"author[0].identifier.value"
156+
]
157+
}
158+
"""
159+
160+
Scenario: Invalid Author System
161+
Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API
162+
And the organisation 'TSTCUS' is authorised to access pointer types:
163+
| system | value |
164+
| http://snomed.info/sct | 736253002 |
165+
When producer 'TSTCUS' requests creation of a DocumentReference with default test values except 'author' is:
166+
"""
167+
"author":[{
168+
"identifier": {
169+
"system": "ddddd",
170+
"value": "123"
171+
}
172+
}]
173+
"""
174+
Then the response status code is 400
175+
And the response is an OperationOutcome with 1 issue
176+
And the OperationOutcome contains the issue:
177+
"""
178+
{
179+
"severity": "error",
180+
"code": "invalid",
181+
"details": {
182+
"coding": [
183+
{
184+
"system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1",
185+
"code": "INVALID_IDENTIFIER_SYSTEM",
186+
"display": "Invalid identifier system"
187+
}
188+
]
189+
},
190+
"diagnostics": "Invalid author system: 'ddddd' Author system must be 'https://fhir.nhs.uk/Id/ods-organization-code'",
191+
"expression": [
192+
"author[0].identifier.system"
193+
]
194+
}
195+
"""
196+
123197
# Invalid document reference - invalid custodian ID
124198
# Invalid document reference - invalid relatesTo target
125199
# Invalid document reference - invalid producer ID in relatesTo target

0 commit comments

Comments
 (0)