diff --git a/api/producer/createDocumentReference/tests/test_create_document_reference.py b/api/producer/createDocumentReference/tests/test_create_document_reference.py index 56daacfc6..f6a6238db 100644 --- a/api/producer/createDocumentReference/tests/test_create_document_reference.py +++ b/api/producer/createDocumentReference/tests/test_create_document_reference.py @@ -239,6 +239,36 @@ def test_create_document_reference_invalid_body(): "diagnostics": "Request body could not be parsed (status: Field required)", "expression": ["status"], }, + { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed", + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + } + ], + }, + "diagnostics": "Request body could not be parsed (type: Field required)", + "expression": ["type"], + }, + { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed", + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + } + ], + }, + "diagnostics": "Request body could not be parsed (category: Field required)", + "expression": ["category"], + }, { "severity": "error", "code": "invalid", diff --git a/api/producer/swagger.yaml b/api/producer/swagger.yaml index c927f4871..76d1f4284 100644 --- a/api/producer/swagger.yaml +++ b/api/producer/swagger.yaml @@ -1354,12 +1354,12 @@ components: enum: ["entered-in-error", "amended", "preliminary", "final"] description: The status of the underlying document. type: - $ref: "#/components/schemas/CodeableConcept" + $ref: "#/components/schemas/NRLCodeableConcept" description: Specifies the particular kind of document referenced (e.g. History and Physical, Discharge Summary, Progress Note). This usually equates to the purpose of making the document referenced. category: type: array items: - $ref: "#/components/schemas/CodeableConcept" + $ref: "#/components/schemas/NRLCodeableConcept" description: A categorization for the type of document referenced – helps for indexing and searching. This may be implied by or derived from the code specified in the DocumentReference.type. subject: $ref: "#/components/schemas/Reference" @@ -1386,7 +1386,7 @@ components: description: Relationships that this document has with other document references that already exist. description: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: Human–readable description of the source document. securityLabel: type: array @@ -1408,6 +1408,8 @@ components: - content - author - context + - type + - category Bundle: type: object properties: @@ -1607,7 +1609,7 @@ components: $ref: "#/components/schemas/CodeableConcept" description: The kind of facility where the patient was seen. practiceSetting: - $ref: "#/components/schemas/CodeableConcept" + $ref: "#/components/schemas/NRLCodeableConcept" description: This property may convey specifics about the practice setting where the content was created, often reflecting the clinical specialty. sourcePatientInfo: $ref: "#/components/schemas/Reference" @@ -1693,7 +1695,7 @@ components: description: The calculated hash of the data using SHA–1. Represented using base64. title: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: A label or set of text to display in place of the data. creation: type: string @@ -1716,8 +1718,29 @@ components: description: A reference to a code defined by a terminology system. text: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" + description: A human language representation of the concept as seen/selected/uttered by the user who entered the data and/or which represents the intended meaning of the user. + NRLCodeableConcept: + type: object + properties: + id: + type: string + pattern: "[A-Za-z0-9\\-\\.]{1,64}" + description: Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces. + coding: + type: array + items: + $ref: "#/components/schemas/NRLCoding" + description: A reference to a code defined by a terminology system. + minItems: 1 + maxItems: 1 + text: + type: string + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: A human language representation of the concept as seen/selected/uttered by the user who entered the data and/or which represents the intended meaning of the user. + required: + - coding + Coding: type: object properties: @@ -1727,11 +1750,11 @@ components: description: Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces. system: type: string - pattern: \S* + pattern: \S+ description: The identification of the code system that defines the meaning of the symbol in the code. version: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: The version of the code system which was used when choosing this code. Note that a well–maintained code system does not need the version reported, because the meaning of codes is consistent across versions. However this cannot consistently be assured, and when the meaning is not guaranteed to be consistent, the version SHOULD be exchanged. code: type: string @@ -1739,11 +1762,41 @@ components: description: A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post–coordination). display: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: A representation of the meaning of the code in the system, following the rules of the system. userSelected: type: boolean description: Indicates that this coding was chosen by a user directly – e.g. off a pick list of available items (codes or displays). + NRLCoding: + type: object + properties: + id: + type: string + pattern: "[A-Za-z0-9\\-\\.]{1,64}" + description: Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces. + system: + type: string + pattern: \S+ + description: The identification of the code system that defines the meaning of the symbol in the code. + version: + type: string + pattern: "[\\S]+[ \\r\\n\\t\\S]*" + description: The version of the code system which was used when choosing this code. Note that a well–maintained code system does not need the version reported, because the meaning of codes is consistent across versions. However this cannot consistently be assured, and when the meaning is not guaranteed to be consistent, the version SHOULD be exchanged. + code: + type: string + pattern: "[^\\s]+(\\s[^\\s]+)*" + description: A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post–coordination). + display: + type: string + pattern: "[\\S]+[ \\r\\n\\t\\S]*" + description: A representation of the meaning of the code in the system, following the rules of the system. + userSelected: + type: boolean + description: Indicates that this coding was chosen by a user directly – e.g. off a pick list of available items (codes or displays). + required: + - system + - code + - display Extension: type: object properties: @@ -1842,11 +1895,11 @@ components: description: A coded type for the identifier that can be used to determine which identifier to use for a specific purpose. system: type: string - pattern: \S* + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: Establishes the namespace for the value – that is, a URL that describes a set values that are unique. value: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: The portion of the identifier typically relevant to the user and which is unique within the context of the system. period: $ref: "#/components/schemas/Period" @@ -1885,11 +1938,11 @@ components: description: How the value should be understood and represented – whether the actual value is greater or less than the stated value due to measurement issues; e.g. if the comparator is "<" , then the real value is < stated value. unit: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: A human–readable form of the unit. system: type: string - pattern: \S* + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: The identification of the system that provides the coded form of the unit. code: type: string @@ -1904,11 +1957,11 @@ components: description: Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces. reference: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources. type: type: string - pattern: \S* + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: |- The expected type of the target of the reference. If both Reference.type and Reference.reference are populated and Reference.reference is a FHIR URL, both SHALL be consistent. The type is the Canonical URL of Resource Definition that is the type this reference refers to. References are URLs that are relative to http://hl7.org/fhir/StructureDefinition/ e.g. "Patient" is a reference to http://hl7.org/fhir/StructureDefinition/Patient. Absolute URLs are only allowed for logical models (and can only be used in references in logical models, not resources). @@ -1917,7 +1970,7 @@ components: description: An identifier for the target resource. This is used when there is no way to reference the other resource directly, either because the entity it represents is not available through a FHIR server, or because there is no way for the author of the resource to convert a known identifier to an actual location. There is no requirement that a Reference.identifier point to something that is actually exposed as a FHIR instance, but it SHALL point to a business concept that would be expected to be exposed as a FHIR instance, and that instance would need to be of a FHIR resource type allowed by the reference. display: type: string - pattern: "[ \\r\\n\\t\\S]+" + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: Plain text narrative that identifies the resource in addition to the resource reference. Signature: type: object @@ -1975,13 +2028,13 @@ components: description: When the resource last changed – e.g. when the version changed. source: type: string - pattern: \S* + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc. profile: type: array items: type: string - pattern: \S* + pattern: "[\\S]+[ \\r\\n\\t\\S]*" description: A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition–definitions.html#StructureDefinition.url). security: type: array diff --git a/api/producer/updateDocumentReference/tests/test_update_document_reference.py b/api/producer/updateDocumentReference/tests/test_update_document_reference.py index 97b32207a..d60cfcd57 100644 --- a/api/producer/updateDocumentReference/tests/test_update_document_reference.py +++ b/api/producer/updateDocumentReference/tests/test_update_document_reference.py @@ -195,7 +195,7 @@ def test_create_document_reference_no_body(): } -def test_create_document_reference_invalid_body(): +def test_update_document_reference_invalid_body(): event = create_test_api_gateway_event( headers=create_headers(), path_parameters={"id": "Y05868-99999-99999-999999"}, @@ -246,6 +246,36 @@ def test_create_document_reference_invalid_body(): "diagnostics": "Request body could not be parsed (status: Field required)", "expression": ["status"], }, + { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed", + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + } + ], + }, + "diagnostics": "Request body could not be parsed (type: Field required)", + "expression": ["type"], + }, + { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed", + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + } + ], + }, + "diagnostics": "Request body could not be parsed (category: Field required)", + "expression": ["category"], + }, { "severity": "error", "code": "invalid", diff --git a/api/producer/upsertDocumentReference/tests/test_upsert_document_reference.py b/api/producer/upsertDocumentReference/tests/test_upsert_document_reference.py index 090542dae..35a087f55 100644 --- a/api/producer/upsertDocumentReference/tests/test_upsert_document_reference.py +++ b/api/producer/upsertDocumentReference/tests/test_upsert_document_reference.py @@ -324,6 +324,36 @@ def test_upsert_document_reference_invalid_body(): "diagnostics": "Request body could not be parsed (status: Field required)", "expression": ["status"], }, + { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed", + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + } + ], + }, + "diagnostics": "Request body could not be parsed (type: Field required)", + "expression": ["type"], + }, + { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed", + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + } + ], + }, + "diagnostics": "Request body could not be parsed (category: Field required)", + "expression": ["category"], + }, { "severity": "error", "code": "invalid", diff --git a/layer/nrlf/consumer/fhir/r4/model.py b/layer/nrlf/consumer/fhir/r4/model.py index 2fd597b42..ffc1667f8 100644 --- a/layer/nrlf/consumer/fhir/r4/model.py +++ b/layer/nrlf/consumer/fhir/r4/model.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: swagger.yaml -# timestamp: 2024-12-13T11:19:30+00:00 +# timestamp: 2025-01-27T09:26:33+00:00 from __future__ import annotations diff --git a/layer/nrlf/core/dynamodb/tests/test_model.py b/layer/nrlf/core/dynamodb/tests/test_model.py index 196d2ed17..951a8cf32 100644 --- a/layer/nrlf/core/dynamodb/tests/test_model.py +++ b/layer/nrlf/core/dynamodb/tests/test_model.py @@ -6,8 +6,7 @@ from nrlf.core.constants import PointerTypes from nrlf.core.dynamodb.model import DocumentPointer, DynamoDBModel from nrlf.core.utils import create_fhir_instant -from nrlf.producer.fhir.r4.model import DocumentReference -from nrlf.tests.data import load_document_reference, load_document_reference_json +from nrlf.tests.data import load_document_reference def test_dynamodb_model_init(): @@ -159,28 +158,6 @@ def test_document_pointer_from_document_reference_invalid(): assert str(error.value) == "'NoneType' object has no attribute 'coding'" -def test_document_pointer_from_document_reference_multiple_types(): - doc_ref_data = load_document_reference_json("Y05868-736253002-Valid") - doc_ref = DocumentReference.model_validate( - { - **doc_ref_data, - "type": { - "coding": [ - {"system": "http://snomed.info/sct", "code": "123456789"}, - {"system": "http://snomed.info/sct", "code": "987654321"}, - ] - }, - } - ) - - with pytest.raises(ValueError) as error: - DocumentPointer.from_document_reference(doc_ref) - - assert ( - str(error.value) == "DocumentReference.type.coding must have exactly one item" - ) - - def test_document_pointer_extract_custodian_suffix_no_suffix(): values = {"custodian": "X26", "custodian_suffix": None} diff --git a/layer/nrlf/core/tests/test_pydantic_errors.py b/layer/nrlf/core/tests/test_pydantic_errors.py index 7ff69e6be..033961446 100644 --- a/layer/nrlf/core/tests/test_pydantic_errors.py +++ b/layer/nrlf/core/tests/test_pydantic_errors.py @@ -1,5 +1,6 @@ import pytest +from nrlf.core.constants import SNOMED_SYSTEM_URL from nrlf.core.errors import ParseError from nrlf.core.validators import DocumentReferenceValidator from nrlf.tests.data import load_document_reference_json @@ -301,3 +302,297 @@ def test_validate_content_missing_content_stability_coding(): "diagnostics": "Failed to parse DocumentReference resource (content[0].extension[0].valueCodeableConcept.coding: Field required. See ValueSet: https://fhir.nhs.uk/England/CodeSystem/England-NRLContentStability)", "expression": ["content[0].extension[0].valueCodeableConcept.coding"], } + + +def test_validate_multiple_codings(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["category"][0] = { + "coding": [ + { + "system": SNOMED_SYSTEM_URL, + "code": "734163000", + "display": "Care plan", + }, + { + "system": SNOMED_SYSTEM_URL, + "code": "734163000", + "display": "Care plan", + }, + { + "system": SNOMED_SYSTEM_URL, + "code": "734163000", + "display": "Care plan", + }, + ] + } + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (category[0].coding: List should have at most 1 item after validation, not 3)", + "expression": ["category[0].coding"], + } + + +def test_validate_missing_coding(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["category"][0] = {"coding": []} + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (category[0].coding: List should have at least 1 item after validation, not 0)", + "expression": ["category[0].coding"], + } + + +def test_validate_empty_strings(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["category"][0] = { + "coding": [ + { + "system": SNOMED_SYSTEM_URL, + "code": "734163000", + "display": "", + } + ] + } + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (category[0].coding[0].display: String should match pattern '[\\S]+[ \\r\\n\\t\\S]*')", + "expression": ["category[0].coding[0].display"], + } + + +def test_validate_whitespace_strings(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["category"][0] = { + "coding": [ + { + "system": SNOMED_SYSTEM_URL, + "code": "734163000", + "display": " ", + } + ] + } + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (category[0].coding[0].display: String should match pattern '[\\S]+[ \\r\\n\\t\\S]*')", + "expression": ["category[0].coding[0].display"], + } + + +def test_validate_no_coding_where_mandatory(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["context"]["practiceSetting"] = { + "text": "Description of the clinic in text" + } + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (context.practiceSetting.coding: Field required)", + "expression": ["context.practiceSetting.coding"], + } + + +def test_validate_no_coding_where_optional(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["context"]["facilityType"] = { + "text": "Description of the facility type in text" + } + + result = validator.validate(document_ref_data) + + assert result.is_valid + + +def test_validate_missing_system_from_coding_where_mandatory(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["context"]["practiceSetting"] = { + "coding": [ + { + "code": "734163000", + "display": "Valid display string", + } + ] + } + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (context.practiceSetting.coding[0].system: Field required)", + "expression": ["context.practiceSetting.coding[0].system"], + } + + +def test_validate_missing_code_from_coding_where_mandatory(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["context"]["practiceSetting"] = { + "coding": [ + { + "system": SNOMED_SYSTEM_URL, + "display": "Valid display string", + } + ] + } + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (context.practiceSetting.coding[0].code: Field required)", + "expression": ["context.practiceSetting.coding[0].code"], + } + + +def test_validate_missing_display_from_coding_where_mandatory(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + document_ref_data["context"]["practiceSetting"] = { + "coding": [ + { + "system": SNOMED_SYSTEM_URL, + "code": "788002001", + } + ] + } + + with pytest.raises(ParseError) as error: + validator.validate(document_ref_data) + + exc = error.value + assert len(exc.issues) == 1 + assert exc.issues[0].model_dump(exclude_none=True) == { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "INVALID_RESOURCE", + "display": "Invalid validation of resource", + } + ] + }, + "diagnostics": "Failed to parse DocumentReference resource (context.practiceSetting.coding[0].display: Field required)", + "expression": ["context.practiceSetting.coding[0].display"], + } diff --git a/layer/nrlf/core/tests/test_request.py b/layer/nrlf/core/tests/test_request.py index e9d8706a5..726d1f520 100644 --- a/layer/nrlf/core/tests/test_request.py +++ b/layer/nrlf/core/tests/test_request.py @@ -310,6 +310,23 @@ def test_parse_body_invalid_json(): "diagnostics": "Request body could not be parsed (type: Input should be an object)", "expression": ["type"], }, + { + "code": "invalid", + "details": { + "coding": [ + { + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed", + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + }, + ], + }, + "diagnostics": "Request body could not be parsed (category: Field required)", + "expression": [ + "category", + ], + "severity": "error", + }, { "code": "invalid", "details": { diff --git a/layer/nrlf/core/tests/test_validators.py b/layer/nrlf/core/tests/test_validators.py index 4b13e5d56..e28653576 100644 --- a/layer/nrlf/core/tests/test_validators.py +++ b/layer/nrlf/core/tests/test_validators.py @@ -204,7 +204,7 @@ def test_document_reference_validator_parse_invalid(): } ] }, - "diagnostics": "Failed to parse DocumentReference resource (type: Input should be a valid dictionary or instance of CodeableConcept)", + "diagnostics": "Failed to parse DocumentReference resource (type: Input should be a valid dictionary or instance of NRLCodeableConcept)", "expression": ["type"], } @@ -225,16 +225,14 @@ def test_validate_document_reference_missing_fields(): document_ref_data = load_document_reference_json("Y05868-736253002-Valid") del document_ref_data["id"] - del document_ref_data["type"] del document_ref_data["custodian"] del document_ref_data["subject"] - del document_ref_data["category"] result = validator.validate(document_ref_data) assert result.is_valid is False assert result.resource.id is None - assert len(result.issues) == 5 + assert len(result.issues) == 3 assert result.issues[0].model_dump(exclude_none=True) == { "severity": "error", "code": "required", @@ -255,9 +253,7 @@ def test_validate_document_reference_missing_fields(): assert diagnostics == [ "The required field 'custodian' is missing", "The required field 'id' is missing", - "The required field 'type' is missing", "The required field 'subject' is missing", - "The required field 'category' is missing", ] @@ -362,34 +358,6 @@ def test_validate_identifiers_no_subject_identifier(): } -def test_validate_category_no_category(): - validator = DocumentReferenceValidator() - document_ref_data = load_document_reference_json("Y05868-736253002-Valid") - - del document_ref_data["category"] - - result = validator.validate(document_ref_data) - - assert result.is_valid is False - assert result.resource.id == "Y05868-99999-99999-999999" - assert len(result.issues) == 1 - assert result.issues[0].model_dump(exclude_none=True) == { - "severity": "error", - "code": "required", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_RESOURCE", - "display": "Invalid validation of resource", - } - ] - }, - "diagnostics": "The required field 'category' is missing", - "expression": ["category"], - } - - def test_validate_category_too_many_category(): validator = DocumentReferenceValidator() document_ref_data = load_document_reference_json("Y05868-736253002-Valid") @@ -497,52 +465,6 @@ def test_validate_category_coding_display_mismatch( } -def test_validate_category_coding_multiple_codings(): - validator = DocumentReferenceValidator() - document_ref_data = load_document_reference_json("Y05868-736253002-Valid") - - document_ref_data["category"][0] = { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "734163000", - "display": "Care plan", - }, - { - "system": "http://snomed.info/sct", - "code": "734163000", - "display": "Care plan", - }, - { - "system": "http://snomed.info/sct", - "code": "734163000", - "display": "Care plan", - }, - ] - } - - result = validator.validate(document_ref_data) - - assert result.is_valid is False - assert result.resource.id == "Y05868-99999-99999-999999" - assert len(result.issues) == 1 - assert result.issues[0].model_dump(exclude_none=True) == { - "severity": "error", - "code": "invalid", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_RESOURCE", - "display": "Invalid validation of resource", - } - ] - }, - "diagnostics": "Invalid category coding length: 3 Category Coding must only contain a single value", - "expression": ["category[0].coding"], - } - - def test_validate_category_coding_invalid_code(): validator = DocumentReferenceValidator() document_ref_data = load_document_reference_json("Y05868-736253002-Valid") @@ -647,52 +569,6 @@ def test_validate_type_coding_invalid_code(): } -def test_validate_type_coding_multiple_codings(): - validator = DocumentReferenceValidator() - document_ref_data = load_document_reference_json("Y05868-736253002-Valid") - - document_ref_data["type"] = { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "736253002", - "display": "Mental health crisis plan", - }, - { - "system": "http://snomed.info/sct", - "code": "736253002", - "display": "Mental health crisis plan", - }, - { - "system": "http://snomed.info/sct", - "code": "736253002", - "display": "Mental health crisis plan", - }, - ] - } - - result = validator.validate(document_ref_data) - - assert result.is_valid is False - assert result.resource.id == "Y05868-99999-99999-999999" - assert len(result.issues) == 1 - assert result.issues[0].model_dump(exclude_none=True) == { - "severity": "error", - "code": "invalid", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_RESOURCE", - "display": "Invalid validation of resource", - } - ] - }, - "diagnostics": "Invalid type coding length: 3 Type Coding must only contain a single value", - "expression": ["type.coding"], - } - - def test_validate_type_coding_invalid_system(): validator = DocumentReferenceValidator() document_ref_data = load_document_reference_json("Y05868-736253002-Valid") @@ -1363,35 +1239,6 @@ def test_validate_content_format_invalid_code_for_contact_details(): } -def test_validate_practiceSetting_no_coding(): - validator = DocumentReferenceValidator() - document_ref_data = load_document_reference_json("Y05868-736253002-Valid") - - document_ref_data["context"]["practiceSetting"] = { - "text": "Description of the clinic" - } - - result = validator.validate(document_ref_data) - - assert result.is_valid is False - assert len(result.issues) == 1 - assert result.issues[0].model_dump(exclude_none=True) == { - "severity": "error", - "code": "value", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_RESOURCE", - "display": "Invalid validation of resource", - } - ] - }, - "diagnostics": "Invalid practice setting: must contain a Coding", - "expression": ["context.practiceSetting.coding"], - } - - def test_validate_practiceSetting_coding_invalid_system(): validator = DocumentReferenceValidator() document_ref_data = load_document_reference_json("Y05868-736253002-Valid") @@ -1462,74 +1309,6 @@ def test_validate_practiceSetting_coding_invalid_code(): } -def test_validate_practiceSetting_coding_missing_code(): - validator = DocumentReferenceValidator() - document_ref_data = load_document_reference_json("Y05868-736253002-Valid") - - document_ref_data["context"]["practiceSetting"] = { - "coding": [ - { - "system": "http://snomed.info/sct", - "display": "Adult mental health service", - } - ] - } - - result = validator.validate(document_ref_data) - - assert result.is_valid is False - assert len(result.issues) == 1 - assert result.issues[0].model_dump(exclude_none=True) == { - "severity": "error", - "code": "value", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_RESOURCE", - "display": "Invalid validation of resource", - } - ] - }, - "diagnostics": "Invalid practice setting code: None Practice Setting coding must be a member of value set https://fhir.nhs.uk/England/ValueSet/England-PracticeSetting", - "expression": ["context.practiceSetting.coding[0].code"], - } - - -def test_validate_practiceSetting_coding_missing_display(): - validator = DocumentReferenceValidator() - document_ref_data = load_document_reference_json("Y05868-736253002-Valid") - - document_ref_data["context"]["practiceSetting"] = { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "788002001", - } - ] - } - - result = validator.validate(document_ref_data) - - assert result.is_valid is False - assert len(result.issues) == 1 - assert result.issues[0].model_dump(exclude_none=True) == { - "severity": "error", - "code": "value", - "details": { - "coding": [ - { - "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", - "code": "INVALID_RESOURCE", - "display": "Invalid validation of resource", - } - ] - }, - "diagnostics": "Invalid practice setting coding: display None does not match the expected display for 788002001 Practice Setting coding is bound to value set https://fhir.nhs.uk/England/ValueSet/England-PracticeSetting", - "expression": ["context.practiceSetting.coding[0]"], - } - - def test_validate_practiceSetting_coding_mismatch_code_and_display(): validator = DocumentReferenceValidator() document_ref_data = load_document_reference_json("Y05868-736253002-Valid") diff --git a/layer/nrlf/producer/fhir/r4/model.py b/layer/nrlf/producer/fhir/r4/model.py index 945b220ec..d2f9e3c06 100644 --- a/layer/nrlf/producer/fhir/r4/model.py +++ b/layer/nrlf/producer/fhir/r4/model.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: swagger.yaml -# timestamp: 2024-12-13T11:19:26+00:00 +# timestamp: 2025-01-27T09:26:28+00:00 from __future__ import annotations @@ -174,7 +174,7 @@ class Attachment(BaseModel): Optional[str], Field( description="A label or set of text to display in place of the data.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None creation: Annotated[ @@ -198,14 +198,14 @@ class Coding(BaseModel): Optional[str], Field( description="The identification of the code system that defines the meaning of the symbol in the code.", - pattern="\\S*", + pattern="\\S+", ), ] = None version: Annotated[ Optional[str], Field( description="The version of the code system which was used when choosing this code. Note that a well–maintained code system does not need the version reported, because the meaning of codes is consistent across versions. However this cannot consistently be assured, and when the meaning is not guaranteed to be consistent, the version SHOULD be exchanged.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None code: Annotated[ @@ -219,7 +219,7 @@ class Coding(BaseModel): Optional[str], Field( description="A representation of the meaning of the code in the system, following the rules of the system.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None userSelected: Annotated[ @@ -230,6 +230,50 @@ class Coding(BaseModel): ] = None +class NRLCoding(BaseModel): + id: Annotated[ + Optional[str], + Field( + description="Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.", + pattern="[A-Za-z0-9\\-\\.]{1,64}", + ), + ] = None + system: Annotated[ + str, + Field( + description="The identification of the code system that defines the meaning of the symbol in the code.", + pattern="\\S+", + ), + ] + version: Annotated[ + Optional[str], + Field( + description="The version of the code system which was used when choosing this code. Note that a well–maintained code system does not need the version reported, because the meaning of codes is consistent across versions. However this cannot consistently be assured, and when the meaning is not guaranteed to be consistent, the version SHOULD be exchanged.", + pattern="[\\S]+[ \\r\\n\\t\\S]*", + ), + ] = None + code: Annotated[ + str, + Field( + description="A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post–coordination).", + pattern="[^\\s]+(\\s[^\\s]+)*", + ), + ] + display: Annotated[ + str, + Field( + description="A representation of the meaning of the code in the system, following the rules of the system.", + pattern="[\\S]+[ \\r\\n\\t\\S]*", + ), + ] + userSelected: Annotated[ + Optional[bool], + Field( + description="Indicates that this coding was chosen by a user directly – e.g. off a pick list of available items (codes or displays)." + ), + ] = None + + class ContentStabilityExtensionCoding(Coding): system: Literal[ "https://fhir.nhs.uk/England/CodeSystem/England-NRLContentStability" @@ -302,14 +346,14 @@ class Quantity(BaseModel): Optional[str], Field( description="A human–readable form of the unit.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None system: Annotated[ Optional[str], Field( description="The identification of the system that provides the coded form of the unit.", - pattern="\\S*", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None code: Annotated[ @@ -326,7 +370,7 @@ class ProfileItem(RootModel[str]): str, Field( description="A list of profiles (references to [StructureDefinition](structuredefinition.html#) resources) that this resource claims to conform to. The URL is a reference to [StructureDefinition.url](structuredefinition–definitions.html#StructureDefinition.url).", - pattern="\\S*", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] @@ -357,7 +401,7 @@ class Meta(BaseModel): Optional[str], Field( description="A uri that identifies the source system of the resource. This provides a minimal amount of [Provenance](provenance.html#) information that can be used to track or differentiate the source of information in the resource. The source may identify another FHIR server, document, message, database, etc.", - pattern="\\S*", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None profile: Optional[List[ProfileItem]] = None @@ -453,7 +497,25 @@ class CodeableConcept(BaseModel): Optional[str], Field( description="A human language representation of the concept as seen/selected/uttered by the user who entered the data and/or which represents the intended meaning of the user.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", + ), + ] = None + + +class NRLCodeableConcept(BaseModel): + id: Annotated[ + Optional[str], + Field( + description="Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.", + pattern="[A-Za-z0-9\\-\\.]{1,64}", + ), + ] = None + coding: Annotated[List[NRLCoding], Field(max_length=1, min_length=1)] + text: Annotated[ + Optional[str], + Field( + description="A human language representation of the concept as seen/selected/uttered by the user who entered the data and/or which represents the intended meaning of the user.", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None @@ -657,12 +719,12 @@ class DocumentReference(BaseModel): Field(description="The status of the underlying document."), ] = None type: Annotated[ - Optional[CodeableConcept], + NRLCodeableConcept, Field( description="Specifies the particular kind of document referenced (e.g. History and Physical, Discharge Summary, Progress Note). This usually equates to the purpose of making the document referenced." ), - ] = None - category: Optional[List[CodeableConcept]] = None + ] + category: List[NRLCodeableConcept] subject: Annotated[ Optional[Reference], Field( @@ -694,7 +756,7 @@ class DocumentReference(BaseModel): Optional[str], Field( description="Human–readable description of the source document.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None securityLabel: Optional[List[CodeableConcept]] = None @@ -877,7 +939,7 @@ class DocumentReferenceContext(BaseModel): Field(description="The kind of facility where the patient was seen."), ] = None practiceSetting: Annotated[ - CodeableConcept, + NRLCodeableConcept, Field( description="This property may convey specifics about the practice setting where the content was created, often reflecting the clinical specialty." ), @@ -936,14 +998,14 @@ class Identifier(BaseModel): Optional[str], Field( description="Establishes the namespace for the value – that is, a URL that describes a set values that are unique.", - pattern="\\S*", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None value: Annotated[ Optional[str], Field( description="The portion of the identifier typically relevant to the user and which is unique within the context of the system.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None period: Annotated[ @@ -968,14 +1030,14 @@ class Reference(BaseModel): Optional[str], Field( description="A reference to a location at which the other resource is found. The reference may be a relative reference, in which case it is relative to the service base URL, or an absolute URL that resolves to the location where the resource is found. The reference may be version specific or not. If the reference is not to a FHIR RESTful server, then it should be assumed to be version specific. Internal fragment references (start with '#') refer to contained resources.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None type: Annotated[ Optional[str], Field( description='The expected type of the target of the reference. If both Reference.type and Reference.reference are populated and Reference.reference is a FHIR URL, both SHALL be consistent.\nThe type is the Canonical URL of Resource Definition that is the type this reference refers to. References are URLs that are relative to http://hl7.org/fhir/StructureDefinition/ e.g. "Patient" is a reference to http://hl7.org/fhir/StructureDefinition/Patient. Absolute URLs are only allowed for logical models (and can only be used in references in logical models, not resources).', - pattern="\\S*", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None identifier: Annotated[ @@ -988,7 +1050,7 @@ class Reference(BaseModel): Optional[str], Field( description="Plain text narrative that identifies the resource in addition to the resource reference.", - pattern="[ \\r\\n\\t\\S]+", + pattern="[\\S]+[ \\r\\n\\t\\S]*", ), ] = None diff --git a/layer/nrlf/producer/fhir/r4/strict_model.py b/layer/nrlf/producer/fhir/r4/strict_model.py index 0344821fc..c283c66a4 100644 --- a/layer/nrlf/producer/fhir/r4/strict_model.py +++ b/layer/nrlf/producer/fhir/r4/strict_model.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: swagger.yaml -# timestamp: 2024-12-13T11:19:28+00:00 +# timestamp: 2025-01-27T09:26:31+00:00 from __future__ import annotations @@ -207,6 +207,45 @@ class Coding(BaseModel): ] = None +class NRLCoding(BaseModel): + id: Annotated[ + Optional[StrictStr], + Field( + description="Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces." + ), + ] = None + system: Annotated[ + StrictStr, + Field( + description="The identification of the code system that defines the meaning of the symbol in the code." + ), + ] + version: Annotated[ + Optional[StrictStr], + Field( + description="The version of the code system which was used when choosing this code. Note that a well–maintained code system does not need the version reported, because the meaning of codes is consistent across versions. However this cannot consistently be assured, and when the meaning is not guaranteed to be consistent, the version SHOULD be exchanged." + ), + ] = None + code: Annotated[ + StrictStr, + Field( + description="A symbol in syntax defined by the system. The symbol may be a predefined code or an expression in a syntax defined by the coding system (e.g. post–coordination)." + ), + ] + display: Annotated[ + StrictStr, + Field( + description="A representation of the meaning of the code in the system, following the rules of the system." + ), + ] + userSelected: Annotated[ + Optional[StrictBool], + Field( + description="Indicates that this coding was chosen by a user directly – e.g. off a pick list of available items (codes or displays)." + ), + ] = None + + class ContentStabilityExtensionCoding(Coding): system: Literal[ "https://fhir.nhs.uk/England/CodeSystem/England-NRLContentStability" @@ -404,6 +443,22 @@ class CodeableConcept(BaseModel): ] = None +class NRLCodeableConcept(BaseModel): + id: Annotated[ + Optional[StrictStr], + Field( + description="Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces." + ), + ] = None + coding: Annotated[List[NRLCoding], Field(max_length=1, min_length=1)] + text: Annotated[ + Optional[StrictStr], + Field( + description="A human language representation of the concept as seen/selected/uttered by the user who entered the data and/or which represents the intended meaning of the user." + ), + ] = None + + class Extension(BaseModel): valueCodeableConcept: Annotated[ Optional[CodeableConcept], @@ -582,12 +637,12 @@ class DocumentReference(BaseModel): Field(description="The status of the underlying document."), ] = None type: Annotated[ - Optional[CodeableConcept], + NRLCodeableConcept, Field( description="Specifies the particular kind of document referenced (e.g. History and Physical, Discharge Summary, Progress Note). This usually equates to the purpose of making the document referenced." ), - ] = None - category: Optional[List[CodeableConcept]] = None + ] + category: List[NRLCodeableConcept] subject: Annotated[ Optional[Reference], Field( @@ -781,7 +836,7 @@ class DocumentReferenceContext(BaseModel): Field(description="The kind of facility where the patient was seen."), ] = None practiceSetting: Annotated[ - CodeableConcept, + NRLCodeableConcept, Field( description="This property may convey specifics about the practice setting where the content was created, often reflecting the clinical specialty." ), diff --git a/tests/features/producer/createDocumentReference-failure.feature b/tests/features/producer/createDocumentReference-failure.feature index af22ae1bc..61bd0db46 100644 --- a/tests/features/producer/createDocumentReference-failure.feature +++ b/tests/features/producer/createDocumentReference-failure.feature @@ -545,15 +545,16 @@ Feature: Producer - createDocumentReference - Failure Scenarios | system | value | | http://snomed.info/sct | 736253002 | When producer 'ANGY1' creates a DocumentReference with values: - | property | value | - | subject | 9278693472 | - | status | current | - | type_system | http://invalidsystem.info/sct | - | type | 736253002 | - | category | 734163000 | - | custodian | ANGY1 | - | author | HAR1 | - | url | https://example.org/my-doc.pdf | + | property | value | + | subject | 9278693472 | + | status | current | + | type_system | http://invalidsystem.info/sct | + | type_display | Mental health crisis plan | + | type | 736253002 | + | category | 734163000 | + | custodian | ANGY1 | + | author | HAR1 | + | url | https://example.org/my-doc.pdf | Then the response status code is 400 And the response is an OperationOutcome with 1 issue And the OperationOutcome contains the issue: @@ -581,14 +582,15 @@ Feature: Producer - createDocumentReference - Failure Scenarios | system | value | | http://snomed.info/sct | 736253002 | When producer 'ANGY1' creates a DocumentReference with values: - | property | value | - | subject | 9999999999 | - | status | current | - | type | invalid | - | category | 734163000 | - | custodian | ANGY1 | - | author | HAR1 | - | url | https://example.org/my-doc.pdf | + | property | value | + | subject | 9999999999 | + | status | current | + | type | invalid | + | type_display | Mental health crisis plan | + | category | 734163000 | + | custodian | ANGY1 | + | author | HAR1 | + | url | https://example.org/my-doc.pdf | Then the response status code is 400 And the response is an OperationOutcome with 1 issue And the OperationOutcome contains the issue: @@ -714,6 +716,8 @@ Feature: Producer - createDocumentReference - Failure Scenarios } } """ + Then the response status code is 400 + And the response is an OperationOutcome with 1 issue Scenario: Missing content Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API @@ -900,3 +904,53 @@ Feature: Producer - createDocumentReference - Failure Scenarios ] } """ + + Scenario: codings with empty string or leading whitespace + Given the application 'DataShare' (ID 'z00z-y11y-x22x') is registered to access the API + And the organisation 'TSTCUS' is authorised to access pointer types: + | system | value | + | http://snomed.info/sct | 736253002 | + When producer 'TSTCUS' requests creation of a DocumentReference with default test values except 'context' is: + """ + "context": { + "practiceSetting": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "788002001", + "display": "" + } + ] + }, + "facilityType": { + "coding": [ + { + "system": " system", + "code": "1234" + } + ] + } + } + """ + Then the response status code is 400 + And the response is an OperationOutcome with 1 issue + And the OperationOutcome contains the issue: + """ + { + "severity": "error", + "code": "invalid", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/Spine-ErrorOrWarningCode-1", + "code": "MESSAGE_NOT_WELL_FORMED", + "display": "Message not well formed" + } + ] + }, + "diagnostics": "Request body could not be parsed (context.practiceSetting.coding[0].display: String should match pattern '[\\S]+[ \\r\\n\\t\\S]*')", + "expression": [ + "context.practiceSetting.coding[0].display" + ] + } + """ diff --git a/tests/features/producer/upsertDocumentReference-failure.feature b/tests/features/producer/upsertDocumentReference-failure.feature index 3855b1b34..b86cdf60c 100644 --- a/tests/features/producer/upsertDocumentReference-failure.feature +++ b/tests/features/producer/upsertDocumentReference-failure.feature @@ -83,16 +83,17 @@ Feature: Producer - upsertDocumentReference - Failure Scenarios | system | value | | http://snomed.info/sct | 736253002 | When producer 'ANGY1' upserts a DocumentReference with values: - | property | value | - | id | X26-testid-upsert-0001-0001 | - | subject | 9278693472 | - | status | current | - | type_system | http://invalidsystem.info/sct | - | type | 736253002 | - | category | 734163000 | - | custodian | ANGY1 | - | author | HAR1 | - | url | https://example.org/my-doc.pdf | + | property | value | + | id | X26-testid-upsert-0001-0001 | + | subject | 9278693472 | + | status | current | + | type_system | http://invalidsystem.info/sct | + | type_display | Mental health crisis plan | + | type | 736253002 | + | category | 734163000 | + | custodian | ANGY1 | + | author | HAR1 | + | url | https://example.org/my-doc.pdf | Then the response status code is 400 And the response is an OperationOutcome with 1 issue And the OperationOutcome contains the issue: @@ -120,15 +121,17 @@ Feature: Producer - upsertDocumentReference - Failure Scenarios | system | value | | http://snomed.info/sct | 736253002 | When producer 'ANGY1' upserts a DocumentReference with values: - | property | value | - | id | X26-testid-upsert-0001-0001 | - | subject | 9999999999 | - | status | current | - | type | invalid | - | category | 734163000 | - | custodian | ANGY1 | - | author | HAR1 | - | url | https://example.org/my-doc.pdf | + | property | value | + | id | X26-testid-upsert-0001-0001 | + | subject | 9999999999 | + | status | current | + | type | invalid | + | type_system | http://snomed.info/sct | + | type_display | Mental health crisis plan | + | category | 734163000 | + | custodian | ANGY1 | + | author | HAR1 | + | url | https://example.org/my-doc.pdf | Then the response status code is 400 And the response is an OperationOutcome with 1 issue And the OperationOutcome contains the issue: diff --git a/tests/features/utils/data.py b/tests/features/utils/data.py index 35c3eb393..cd6a7262d 100644 --- a/tests/features/utils/data.py +++ b/tests/features/utils/data.py @@ -10,7 +10,6 @@ from nrlf.producer.fhir.r4.model import ( Attachment, CodeableConcept, - Coding, ContentStabilityExtension, ContentStabilityExtensionCoding, ContentStabilityExtensionValueCodeableConcept, @@ -19,6 +18,8 @@ DocumentReferenceContext, DocumentReferenceRelatesTo, Identifier, + NRLCodeableConcept, + NRLCoding, NRLFormatCode, Reference, ) @@ -80,9 +81,9 @@ def create_test_document_reference(items: dict) -> DocumentReference: ], ), context=DocumentReferenceContext( - practiceSetting=CodeableConcept( + practiceSetting=NRLCodeableConcept( coding=[ - Coding( + NRLCoding( system=SNOMED_SYSTEM_URL, code=str(practice_setting_code), display=practice_setting_display, @@ -102,8 +103,8 @@ def create_test_document_reference(items: dict) -> DocumentReference: "type_display", TYPE_ATTRIBUTES.get(type_str, {}).get("display") ) - base_doc_ref.type = CodeableConcept( - coding=[Coding(system=type_system, code=type_code, display=type_display)] + base_doc_ref.type = NRLCodeableConcept( + coding=[NRLCoding(system=type_system, code=type_code, display=type_display)] ) if items.get("subject"): @@ -136,9 +137,9 @@ def create_test_document_reference(items: dict) -> DocumentReference: f"{SNOMED_SYSTEM_URL}|{items['category']}", {} ).get("display") base_doc_ref.category = [ - CodeableConcept( + NRLCodeableConcept( coding=[ - Coding( + NRLCoding( system=SNOMED_SYSTEM_URL, code=items["category"], display=category_display, diff --git a/tests/smoke/setup.py b/tests/smoke/setup.py index f639e80f8..9bceec028 100644 --- a/tests/smoke/setup.py +++ b/tests/smoke/setup.py @@ -8,8 +8,6 @@ ) from nrlf.producer.fhir.r4.model import ( Attachment, - CodeableConcept, - Coding, ContentStabilityExtension, ContentStabilityExtensionCoding, ContentStabilityExtensionValueCodeableConcept, @@ -18,6 +16,8 @@ DocumentReferenceContext, DocumentReferenceRelatesTo, Identifier, + NRLCodeableConcept, + NRLCoding, NRLFormatCode, Reference, ) @@ -65,9 +65,9 @@ def build_document_reference( ], ) ], - type=CodeableConcept( + type=NRLCodeableConcept( coding=[ - Coding( + NRLCoding( system="http://snomed.info/sct", code=type, display=TYPE_ATTRIBUTES.get(f"http://snomed.info/sct|{type}").get( @@ -94,9 +94,9 @@ def build_document_reference( ) ], category=[ - CodeableConcept( + NRLCodeableConcept( coding=[ - Coding( + NRLCoding( system="http://snomed.info/sct", code=category, display=( @@ -107,9 +107,9 @@ def build_document_reference( ) ], context=DocumentReferenceContext( - practiceSetting=CodeableConcept( + practiceSetting=NRLCodeableConcept( coding=[ - Coding( + NRLCoding( system="http://snomed.info/sct", code="224891009", display="Healthcare services",