diff --git a/mocks/src/pdm_mock/handler.py b/mocks/src/pdm_mock/handler.py index c9355fe2..b14945f3 100644 --- a/mocks/src/pdm_mock/handler.py +++ b/mocks/src/pdm_mock/handler.py @@ -29,7 +29,7 @@ class PDMResponse(TypedDict): class DocumentItem(BaseMockItem): - document: dict[str, Any] + document: str type RequestHandler = Callable[[], PDMResponse] @@ -80,7 +80,8 @@ def _fetch_patient_from_payload(payload: dict[str, Any]) -> str | None: if (resource := entry.get("resource")) and resource.get("resourceType") == "Patient" and "identifier" in resource - and (patient := resource.get("identifier", {}).get("value")) + and len(resource["identifier"]) > 0 + and (patient := resource.get("identifier")[0].get("value")) ] if not patient_values: @@ -113,7 +114,7 @@ def handle_post_request(payload: dict[str, Any]) -> PDMResponse: item: DocumentItem = { "sessionId": document_id, "expiresAt": int(time()) + 600, - "document": created_document, + "document": json.dumps(created_document), "type": "pdm_document", } @@ -125,7 +126,7 @@ def handle_post_request(payload: dict[str, Any]) -> PDMResponse: def handle_get_request(document_id: str) -> PDMResponse: table_item = _get_document_from_table(document_id) - document = table_item["document"] + document = json.loads(table_item["document"]) return {"status_code": 200, "response": document} diff --git a/mocks/src/pdm_mock/test_handler.py b/mocks/src/pdm_mock/test_handler.py index eb119f0b..ce022c99 100644 --- a/mocks/src/pdm_mock/test_handler.py +++ b/mocks/src/pdm_mock/test_handler.py @@ -11,10 +11,6 @@ ) from aws_lambda_powertools.utilities.typing import LambdaContext -with patch("boto3.resource"): - from apim_mock.auth_check import AuthenticationError - from common.storage_helper import ItemNotFoundException - os.environ["PDM_TABLE_NAME"] = "test_table" os.environ["DDB_INDEX_TAG"] = "test_branch" os.environ["AUTH_URL"] = "auth_url" @@ -22,6 +18,10 @@ os.environ["API_KEY"] = "api_key" os.environ["TOKEN_TABLE_NAME"] = "token_table" # noqa: S105 - Dummy value +with patch("boto3.resource"): + from apim_mock.auth_check import AuthenticationError + from common.storage_helper import ItemNotFoundException + @pytest.fixture def basic_document_payload() -> dict[str, Any]: @@ -38,13 +38,13 @@ def multiple_patient_document_payload( { "resource": { "resourceType": "Patient", - "identifier": {"value": "patient1"}, + "identifier": [{"value": "patient1"}], } }, { "resource": { "resourceType": "Patient", - "identifier": {"value": "patient2"}, + "identifier": [{"value": "patient2"}], } }, ], @@ -61,7 +61,7 @@ def validation_error_patient_payload( { "resource": { "resourceType": "Patient", - "identifier": {"value": "PDM_VALIDATION_ERROR"}, + "identifier": [{"value": "PDM_VALIDATION_ERROR"}], } }, ], @@ -78,7 +78,7 @@ def server_error_patient_payload( { "resource": { "resourceType": "Patient", - "identifier": {"value": "PDM_SERVER_ERROR"}, + "identifier": [{"value": "PDM_SERVER_ERROR"}], } }, ], @@ -271,7 +271,7 @@ def test_handle_get_request( mock_dynamodb_client.Table.return_value.get_item.return_value = { "Item": { "sessionId": "document_id", - "document": basic_saved_document, + "document": json.dumps(basic_saved_document), "type": "document", "expiresAt": 1, "ddb_index": "test_branch", @@ -481,7 +481,7 @@ def test_get_document( check_authenticated_mock.return_value = None mock_storage_helper.return_value = { "sessionId": "document_id", - "document": {"test_key": "test_data"}, + "document": json.dumps({"test_key": "test_data"}), } event = self._create_test_event( diff --git a/pathology-api/conftest.py b/pathology-api/conftest.py index f886d044..0d9c28f2 100644 --- a/pathology-api/conftest.py +++ b/pathology-api/conftest.py @@ -21,7 +21,7 @@ def build_valid_test_result() -> Callable[[str, str], Bundle]: def builder_function(patient: str, ods_code: str) -> Bundle: return BundleBuilder.with_defaults( composition_func=lambda service_request_url: Composition.create( - subject=LogicalReference(PatientIdentifier.from_nhs_number(patient)), + subject=LogicalReference(PatientIdentifier.create_with(patient)), extension=[ # Using HTTP to match profile required by implementation guide. ReferenceExtension( diff --git a/pathology-api/src/pathology_api/fhir/r4/elements.py b/pathology-api/src/pathology_api/fhir/r4/elements.py index 969f1097..6b2417f4 100644 --- a/pathology-api/src/pathology_api/fhir/r4/elements.py +++ b/pathology-api/src/pathology_api/fhir/r4/elements.py @@ -1,5 +1,4 @@ import datetime -import uuid from abc import ABC from dataclasses import dataclass from typing import Annotated, Any, ClassVar @@ -107,25 +106,13 @@ class UnknownIdentifier(Identifier, validate_system=False): """Provides a fallback Identifier type for an unknown system.""" -class UUIDIdentifier(Identifier, expected_system="https://tools.ietf.org/html/rfc4122"): - """A UUID identifier utilising the standard RFC 4122 system.""" - - @classmethod - def create_with_uuid(cls, value: uuid.UUID | None = None) -> "UUIDIdentifier": - """Create a UUIDIdentifier with the provided UUID value.""" - return cls( - value=str(value or uuid.uuid4()), - system=cls.expected_system, - ) - - class PatientIdentifier( Identifier, expected_system="https://fhir.nhs.uk/Id/nhs-number" ): """A FHIR R4 Patient Identifier utilising the NHS Number system.""" @classmethod - def from_nhs_number(cls, nhs_number: str) -> "PatientIdentifier": + def create_with(cls, nhs_number: str) -> "PatientIdentifier": """Create a PatientIdentifier from an NHS number.""" return cls(value=nhs_number, system=cls.expected_system) @@ -144,6 +131,7 @@ def from_ods_code(cls, ods_code: str) -> "OrganizationIdentifier": @dataclass(frozen=True) class LogicalReference[T: Identifier]: identifier: T + reference: str | None = None @dataclass(frozen=True) diff --git a/pathology-api/src/pathology_api/fhir/r4/resources.py b/pathology-api/src/pathology_api/fhir/r4/resources.py index 00b4d3a4..f900420c 100644 --- a/pathology-api/src/pathology_api/fhir/r4/resources.py +++ b/pathology-api/src/pathology_api/fhir/r4/resources.py @@ -20,7 +20,6 @@ LogicalReference, Meta, PatientIdentifier, - UUIDIdentifier, ) @@ -128,7 +127,6 @@ class Bundle(Resource, resource_type="Bundle"): """A FHIR R4 Bundle resource.""" bundle_type: BundleType = Field(alias="type", frozen=True) - identifier: Annotated[UUIDIdentifier | None, Field(frozen=True)] = None entries: list["Bundle.Entry"] | None = Field(None, frozen=True, alias="entry") class Entry(BaseModel): diff --git a/pathology-api/src/pathology_api/fhir/r4/test_elements.py b/pathology-api/src/pathology_api/fhir/r4/test_elements.py index e06773b5..4ac6c7d6 100644 --- a/pathology-api/src/pathology_api/fhir/r4/test_elements.py +++ b/pathology-api/src/pathology_api/fhir/r4/test_elements.py @@ -1,5 +1,4 @@ import datetime -import uuid import pydantic import pytest @@ -16,7 +15,6 @@ PatientIdentifier, ReferenceExtension, UnknownIdentifier, - UUIDIdentifier, ) @@ -66,23 +64,6 @@ def test_with_last_updated_defaults_to_now(self) -> None: assert meta.last_updated <= after_create -class TestUUIDIdentifier: - def test_create_with_value(self) -> None: - expected_uuid = uuid.UUID("12345678-1234-5678-1234-567812345678") - identifier = UUIDIdentifier.create_with_uuid(expected_uuid) - - assert identifier.system == "https://tools.ietf.org/html/rfc4122" - assert identifier.value == str(expected_uuid) - - def test_create_without_value(self) -> None: - identifier = UUIDIdentifier.create_with_uuid() - - assert identifier.system == "https://tools.ietf.org/html/rfc4122" - # Validates that value is a valid UUID v4 - parsed_uuid = uuid.UUID(identifier.value) - assert parsed_uuid.version == 4 - - class _TestIdentifierContainer(BaseModel): identifier: "IdentifierStub" @@ -159,7 +140,7 @@ class TestPatientIdentifier: def test_create_from_nhs_number(self) -> None: """Test creating a PatientIdentifier from an NHS number.""" nhs_number = "1234567890" - identifier = PatientIdentifier.from_nhs_number(nhs_number) + identifier = PatientIdentifier.create_with(nhs_number) assert identifier.system == "https://fhir.nhs.uk/Id/nhs-number" assert identifier.value == nhs_number @@ -180,7 +161,7 @@ class _TestContainer(BaseModel): def test_create_with_patient_identifier(self) -> None: nhs_number = "nhs_number" - patient_id = PatientIdentifier.from_nhs_number(nhs_number) + patient_id = PatientIdentifier.create_with(nhs_number) reference = LogicalReference(identifier=patient_id) @@ -190,11 +171,11 @@ def test_create_with_patient_identifier(self) -> None: def test_serialization(self) -> None: nhs_number = "nhs_number" - patient_id = PatientIdentifier.from_nhs_number(nhs_number) + patient_id = PatientIdentifier.create_with(nhs_number) reference = LogicalReference(identifier=patient_id) container = self._TestContainer(reference=reference) - serialized = container.model_dump(by_alias=True) + serialized = container.model_dump(by_alias=True, exclude_none=True) expected = { "reference": { @@ -206,6 +187,25 @@ def test_serialization(self) -> None: } assert serialized == expected + def test_serialization_with_reference(self) -> None: + nhs_number = "nhs_number" + patient_id = PatientIdentifier.create_with(nhs_number) + reference = LogicalReference(identifier=patient_id, reference=nhs_number) + + container = self._TestContainer(reference=reference) + serialized = container.model_dump(by_alias=True) + + expected = { + "reference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "nhs_number", + }, + "reference": nhs_number, + } + } + assert serialized == expected + def test_deserialization(self) -> None: data = { "reference": { @@ -218,7 +218,30 @@ def test_deserialization(self) -> None: container = self._TestContainer.model_validate(data) + assert container.reference.reference is None + created_identifier = container.reference.identifier + + assert isinstance(created_identifier, PatientIdentifier) + assert created_identifier.system == "https://fhir.nhs.uk/Id/nhs-number" + assert created_identifier.value == "nhs_number" + + def test_deserialization_with_reference(self) -> None: + expected_reference = "expected-reference" + data = { + "reference": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "nhs_number", + }, + "reference": expected_reference, + } + } + + container = self._TestContainer.model_validate(data) + + assert container.reference.reference == expected_reference created_identifier = container.reference.identifier + assert isinstance(created_identifier, PatientIdentifier) assert created_identifier.system == "https://fhir.nhs.uk/Id/nhs-number" assert created_identifier.value == "nhs_number" diff --git a/pathology-api/src/pathology_api/fhir/r4/test_resources.py b/pathology-api/src/pathology_api/fhir/r4/test_resources.py index bb5e9da1..06149738 100644 --- a/pathology-api/src/pathology_api/fhir/r4/test_resources.py +++ b/pathology-api/src/pathology_api/fhir/r4/test_resources.py @@ -1,4 +1,6 @@ import json +import uuid +from datetime import datetime, timezone from typing import Any import pydantic @@ -208,9 +210,7 @@ def test_create(self) -> None: expected_entry = Bundle.Entry( fullUrl="full", resource=Composition.create( - subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") - ) + subject=LogicalReference(PatientIdentifier.create_with("nhs_number")) ), ) @@ -220,20 +220,16 @@ def test_create(self) -> None: ) assert bundle.bundle_type == "document" - assert bundle.identifier is None assert bundle.entries == [expected_entry] def test_create_without_entries(self) -> None: bundle = Bundle.empty("document") assert bundle.bundle_type == "document" - assert bundle.identifier is None assert bundle.entries is None expected_composition = Composition.create( - subject=LogicalReference( - identifier=PatientIdentifier.from_nhs_number("nhs_number") - ) + subject=LogicalReference(identifier=PatientIdentifier.create_with("nhs_number")) ) @pytest.mark.parametrize( @@ -309,7 +305,7 @@ def test_deserialise_without_type(self) -> None: def test_has_resource(self) -> None: expected_resource = Patient.create( - identifier=PatientIdentifier.from_nhs_number("nhs_number") + identifier=PatientIdentifier.create_with("nhs_number") ) bundle = Bundle.create( @@ -327,7 +323,7 @@ def test_has_resource_no_resources(self) -> None: assert bundle.has_resource(Resource) is False expected_patient = Patient.create( - identifier=PatientIdentifier.from_nhs_number("nhs_number") + identifier=PatientIdentifier.create_with("nhs_number") ) @pytest.mark.parametrize( @@ -357,7 +353,7 @@ def test_has_resource_no_resources(self) -> None: Bundle.Entry( fullUrl="secondUrl", resource=Patient.create( - identifier=PatientIdentifier.from_nhs_number( + identifier=PatientIdentifier.create_with( "second_nhs_number" ) ), @@ -380,7 +376,7 @@ def test_get_resource_no_resources(self) -> None: def test_get_resource_wrong_type(self) -> None: expected_resource = Patient.create( - identifier=PatientIdentifier.from_nhs_number("nhs_number") + identifier=PatientIdentifier.create_with("nhs_number") ) bundle = Bundle.create( @@ -392,7 +388,7 @@ def test_get_resource_wrong_type(self) -> None: def test_get_resource_wrong_url(self) -> None: expected_resource = Patient.create( - identifier=PatientIdentifier.from_nhs_number("nhs_number") + identifier=PatientIdentifier.create_with("nhs_number") ) bundle = Bundle.create( @@ -404,7 +400,7 @@ def test_get_resource_wrong_url(self) -> None: def test_get_resource_multiple_resources_same_url(self) -> None: expected_resource = Patient.create( - identifier=PatientIdentifier.from_nhs_number("nhs_number") + identifier=PatientIdentifier.create_with("nhs_number") ) bundle = Bundle.create( @@ -421,6 +417,37 @@ def test_get_resource_multiple_resources_same_url(self) -> None: ): bundle.get_resource(url="fullUrl", t=Patient) + def test_create_with_identifier(self) -> None: + data: dict[str, Any] = { + "resourceType": "Bundle", + "type": "document", + "identifier": {"system": "urn:ietf:rfc:3986", "value": str(uuid.uuid4())}, + } + + bundle = Bundle.model_validate(data) + assert bundle.resource_type == "Bundle" + assert bundle.bundle_type == "document" + + serialised = bundle.model_dump(by_alias=True) + assert serialised["identifier"] == { + "system": "urn:ietf:rfc:3986", + "value": data["identifier"]["value"], + } + + def test_create_with_unexpected_field(self) -> None: + data: dict[str, Any] = { + "resourceType": "Bundle", + "type": "document", + "timestamp": datetime.now(tz=timezone.utc).isoformat(), + } + + bundle = Bundle.model_validate(data) + assert bundle.resource_type == "Bundle" + assert bundle.bundle_type == "document" + + serialised = bundle.model_dump(by_alias=True) + assert serialised["timestamp"] == data["timestamp"] + class TestOperationOutcome: def test_create_validation_error(self) -> None: diff --git a/pathology-api/src/pathology_api/test_handler.py b/pathology-api/src/pathology_api/test_handler.py index 039c77a4..71233cf9 100644 --- a/pathology-api/src/pathology_api/test_handler.py +++ b/pathology-api/src/pathology_api/test_handler.py @@ -81,9 +81,7 @@ class _InvalidExtension(Extension, type_name="invalid_extension"): ), pytest.param( lambda _: Composition.create( - subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") - ), + subject=LogicalReference(PatientIdentifier.create_with("nhs_number")), extension=None, ), "Composition does not define a valid basedOn-order-or-requisition " @@ -92,9 +90,7 @@ class _InvalidExtension(Extension, type_name="invalid_extension"): ), pytest.param( lambda service_request_url: Composition.create( - subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") - ), + subject=LogicalReference(PatientIdentifier.create_with("nhs_number")), extension=[ _InvalidExtension( url="http://hl7.eu/fhir/StructureDefinition/composition-basedOn-order-or-requisition", @@ -109,9 +105,7 @@ class _InvalidExtension(Extension, type_name="invalid_extension"): ), pytest.param( lambda service_request_url: Composition.create( - subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") - ), + subject=LogicalReference(PatientIdentifier.create_with("nhs_number")), extension=[ ReferenceExtension( url="invalid", @@ -125,9 +119,7 @@ class _InvalidExtension(Extension, type_name="invalid_extension"): ), pytest.param( lambda _: Composition.create( - subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") - ), + subject=LogicalReference(PatientIdentifier.create_with("nhs_number")), extension=[ ReferenceExtension( url="http://hl7.eu/fhir/StructureDefinition/composition-basedOn-order-or-requisition", @@ -195,7 +187,7 @@ def _invalid_organization_scenarios() -> list[Any]: ), pytest.param( Organization.create( - identifier=[PatientIdentifier.from_nhs_number("nhs_number")] + identifier=[PatientIdentifier.create_with("nhs_number")] ), r"Organization \(organisation\) does not define a supported identifier\. " r"Supported system 'https://fhir\.nhs\.uk/Id/ods-organization-code'", @@ -225,51 +217,6 @@ def set_correlation_id_for_logger(self) -> Generator[None, None, None]: yield reset_correlation_id() - def _build_valid_test_result(self) -> Bundle: - organisation_entry = Bundle.Entry( - fullUrl="organisation", - resource=Organization.create( - identifier=[OrganizationIdentifier.from_ods_code("ods_code")] - ), - ) - - practitioner_role_entry = Bundle.Entry( - fullUrl="practitioner_role", - resource=PractitionerRole.create( - organization=LiteralReference(reference=organisation_entry.full_url) - ), - ) - - service_request_entry = Bundle.Entry( - fullUrl="service_request", - resource=ServiceRequest.create( - requester=LiteralReference(reference=practitioner_role_entry.full_url) - ), - ) - - composition = Composition.create( - subject=LogicalReference(PatientIdentifier.from_nhs_number("nhs_number_1")), - extension=[ - ReferenceExtension( - url="http://hl7.eu/fhir/StructureDefinition/composition-basedOn-order-or-requisition", - valueReference=LiteralReference(service_request_entry.full_url), - ) - ], - ) - - return Bundle.create( - type="document", - entry=[ - organisation_entry, - practitioner_role_entry, - service_request_entry, - Bundle.Entry( - fullUrl="composition", - resource=composition, - ), - ], - ) - def test_handle_request( self, build_valid_test_result: Callable[[str, str], Bundle], @@ -357,7 +304,7 @@ def test_handle_request_raises_error_when_multiple_composition_resources( self, ) -> None: composition = Composition.create( - subject=LogicalReference(PatientIdentifier.from_nhs_number("nhs_number_1")) + subject=LogicalReference(PatientIdentifier.create_with("nhs_number_1")) ) bundle = ( @@ -451,7 +398,7 @@ def test_handle_request_raises_error_when_bundle_includes_id( self, ) -> None: composition = Composition.create( - subject=LogicalReference(PatientIdentifier.from_nhs_number("nhs_number_1")) + subject=LogicalReference(PatientIdentifier.create_with("nhs_number_1")) ) bundle = Bundle.create( diff --git a/pathology-api/src/pathology_api/test_pdm.py b/pathology-api/src/pathology_api/test_pdm.py index 9ea8036f..3ac4e1a2 100644 --- a/pathology-api/src/pathology_api/test_pdm.py +++ b/pathology-api/src/pathology_api/test_pdm.py @@ -94,7 +94,7 @@ def test_post_document_success( fullUrl="patient", resource=Composition.create( subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") + PatientIdentifier.create_with("nhs_number") ) ), ) @@ -140,7 +140,7 @@ def test_post_document_401(self) -> None: fullUrl="patient", resource=Composition.create( subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") + PatientIdentifier.create_with("nhs_number") ) ), ) @@ -164,7 +164,7 @@ def test_post_document_4xx(self) -> None: fullUrl="patient", resource=Composition.create( subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") + PatientIdentifier.create_with("nhs_number") ) ), ) @@ -187,7 +187,7 @@ def test_post_document_5xx(self) -> None: fullUrl="patient", resource=Composition.create( subject=LogicalReference( - PatientIdentifier.from_nhs_number("nhs_number") + PatientIdentifier.create_with("nhs_number") ) ), ) diff --git a/pathology-api/src/pathology_api/test_utils.py b/pathology-api/src/pathology_api/test_utils.py index 26ad8602..4787e11e 100644 --- a/pathology-api/src/pathology_api/test_utils.py +++ b/pathology-api/src/pathology_api/test_utils.py @@ -20,7 +20,7 @@ def _build_composition(service_request_url: str) -> Composition: return Composition.create( - subject=LogicalReference(PatientIdentifier.from_nhs_number("nhs_number")), + subject=LogicalReference(PatientIdentifier.create_with("nhs_number")), extension=[ # Using HTTP to match profile required by implementation guide. ReferenceExtension( diff --git a/pathology-api/tests/acceptance/conftest.py b/pathology-api/tests/acceptance/conftest.py index 6fd4da3c..ed88e801 100644 --- a/pathology-api/tests/acceptance/conftest.py +++ b/pathology-api/tests/acceptance/conftest.py @@ -1,3 +1,7 @@ +from collections.abc import Callable +from pathlib import Path +from typing import Any + import pytest import requests @@ -19,3 +23,35 @@ def response(self, value: requests.Response) -> None: @pytest.fixture def response_context() -> ResponseContext: return ResponseContext() + + +class TestContext: + _sent_request: str | None = None + + @property + def sent_request(self) -> str | None: + return self._sent_request + + @sent_request.setter + def sent_request(self, value: str) -> None: + if self._sent_request: + raise RuntimeError("Request has already been sent.") + self._sent_request = value + + +@pytest.fixture +def test_context() -> TestContext: + return TestContext() + + +@pytest.fixture +def build_full_test_result() -> Callable[[str, str], Any]: + def _build_full_test_result(subject: str, requesting_ods_code: str) -> str: + with open(Path(__file__).parent / "full_test_result_template.json") as template: + return ( + template.read() + .replace("$subject", subject) + .replace("$requesting_ods_code", requesting_ods_code) + ) + + return _build_full_test_result diff --git a/pathology-api/tests/acceptance/features/bundle_endpoint.feature b/pathology-api/tests/acceptance/features/bundle_endpoint.feature index 9b2cb2ff..f654240e 100644 --- a/pathology-api/tests/acceptance/features/bundle_endpoint.feature +++ b/pathology-api/tests/acceptance/features/bundle_endpoint.feature @@ -12,6 +12,7 @@ Feature: pathology Bundle API When I send a valid Bundle to the Pathology API Then the response status code should be 200 And the response should contain a valid "document" Bundle + And the response should include the created test result Scenario: Sending an invalid bundle When I send an invalid Bundle to the Pathology API diff --git a/pathology-api/tests/acceptance/full_test_result_template.json b/pathology-api/tests/acceptance/full_test_result_template.json new file mode 100644 index 00000000..46e4e22f --- /dev/null +++ b/pathology-api/tests/acceptance/full_test_result_template.json @@ -0,0 +1,1582 @@ +{ + "resourceType": "Bundle", + "identifier": { + "system": "urn:ietf:rfc:3986", + "value": "urn:uuid:866e3e7a-2142-4af2-8461-2d2e876e8521" + }, + "type": "document", + "entry": [ + { + "fullUrl": "urn:uuid:604765c4-0e8a-4cd6-96a0-a2651fb5ab19", + "resource": { + "resourceType": "Composition", + "id": "604765c4-0e8a-4cd6-96a0-a2651fb5ab19", + "extension": [ + { + "url": "http://hl7.eu/fhir/StructureDefinition/composition-basedOn-order-or-requisition", + "valueReference": { + "reference": "urn:uuid:1c38d507-9ad7-4b49-ba91-7da204842cac" + } + }, + { + "url": "http://hl7.eu/fhir/laboratory/StructureDefinition/composition-diagnosticReportReference", + "valueReference": { + "reference": "urn:uuid:35d46ca1-f253-4c97-b7ee-fb5fccdf6c20" + } + } + ], + "identifier": { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "c3d5e6f7-8901-2345-6789-0abcdef12345" + }, + "status": "final", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "394915009", + "display": "General pathology (specialty)" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "date": "2022-03-08T12:09:00+00:00", + "author": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "title": "Laboratory Report", + "section": [ + { + "title": "FBC - full blood count", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "1022441000000101", + "display": "FBC - full blood count" + } + ] + }, + "entry": [ + { + "reference": "urn:uuid:ad717ae8-cb35-4d66-ba51-e22a76b1d158" + } + ] + } + ] + } + }, + { + "fullUrl": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338", + "resource": { + "resourceType": "Organization", + "id": "8a6d85b8-9837-4fed-a257-4cf207988338", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "Y8J7D" + } + ], + "name": "TD008362 PATH LAB 001", + "address": [ + { + "line": [ + "PATHOLOGY LAB", + "7-8 WELLINGTON PLACE" + ], + "city": "LEEDS", + "district": "WEST YORKSHIRE", + "postalCode": "LS1 4AP" + } + ] + } + }, + { + "fullUrl": "urn:uuid:3c43b5b3-06d6-445f-ae9a-48d5f05df434", + "resource": { + "resourceType": "Organization", + "id": "3c43b5b3-06d6-445f-ae9a-48d5f05df434", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "$requesting_ods_code" + } + ], + "name": "PICKERING MEDICAL PRACTICE", + "address": [ + { + "line": [ + "SOUTHGATE" + ], + "city": "PICKERING", + "district": "NORTH YORKSHIRE", + "postalCode": "YO18 8BL" + } + ] + } + }, + { + "fullUrl": "urn:uuid:9a835acf-d715-4d84-8dcf-a8435f6417fe", + "resource": { + "resourceType": "Practitioner", + "id": "9a835acf-d715-4d84-8dcf-a8435f6417fe", + "identifier": [ + { + "system": "https://fhir.hl7.org.uk/Id/gmc-number", + "value": "C1008215" + } + ], + "name": [ + { + "use": "official", + "family": "TEST", + "given": [ + "User" + ], + "prefix": [ + "Dr" + ] + } + ] + } + }, + { + "fullUrl": "urn:uuid:e1f4d3c2-5b6a-4f7e-9d8c-0b1a2c3d4e5f", + "resource": { + "resourceType": "PractitionerRole", + "id": "e1f4d3c2-5b6a-4f7e-9d8c-0b1a2c3d4e5f", + "practitioner": { + "reference": "urn:uuid:9a835acf-d715-4d84-8dcf-a8435f6417fe" + }, + "organization": { + "reference": "urn:uuid:3c43b5b3-06d6-445f-ae9a-48d5f05df434" + }, + "code": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "62247001", + "display": "General practitioner" + } + ] + } + ], + "specialty": [ + { + "coding": [ + { + "system": "https://fhir.hl7.org.uk/CodeSystem/UKCore-PracticeSettingCode", + "code": "600", + "display": "General Medical Practice" + } + ] + } + ] + } + }, + { + "fullUrl": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "resource": { + "resourceType": "Patient", + "id": "ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": [ + { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + ] + } + }, + { + "fullUrl": "urn:uuid:1c38d507-9ad7-4b49-ba91-7da204842cac", + "resource": { + "resourceType": "ServiceRequest", + "id": "1c38d507-9ad7-4b49-ba91-7da204842cac", + "identifier": [ + { + "system": "http://B82033-pickeringmedicalpractice.com/labrequest", + "value": "REQ-20220129-000012" + } + ], + "status": "active", + "intent": "order", + "category": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "108252007", + "display": "Laboratory procedure" + } + ] + } + ], + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "authoredOn": "2022-02-07T09:17:00+00:00", + "requester": { + "reference": "urn:uuid:e1f4d3c2-5b6a-4f7e-9d8c-0b1a2c3d4e5f" + }, + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "reasonCode": [ + { + "text": "Tired all the time, shortness of breath, arrhythmia" + } + ] + } + }, + { + "fullUrl": "urn:uuid:35d46ca1-f253-4c97-b7ee-fb5fccdf6c20", + "resource": { + "resourceType": "DiagnosticReport", + "id": "35d46ca1-f253-4c97-b7ee-fb5fccdf6c20", + "identifier": [ + { + "system": "http://Y8J7D-pathlab001.com/report", + "value": "REP-20220209-001369" + } + ], + "basedOn": [ + { + "reference": "urn:uuid:1c38d507-9ad7-4b49-ba91-7da204842cac" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "specimen": [ + { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + } + ], + "result": [ + { + "reference": "urn:uuid:ad717ae8-cb35-4d66-ba51-e22a76b1d158" + } + ] + } + }, + { + "fullUrl": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d", + "resource": { + "resourceType": "Specimen", + "id": "bab0eaec-1ec5-4598-b660-90bb38a1030d", + "identifier": [ + { + "system": "http://B82033-pickeringmedicalpractice.com/specimen", + "value": "SPC-REQ-20220208-000016" + } + ], + "accessionIdentifier": { + "system": "http://Y8J7D-pathlab001.com/specimen", + "value": "SPC-Lab-20220208-001397" + }, + "status": "available", + "type": { + "text": "Venous blood specimen" + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "receivedTime": "2022-02-08T16:03:00+00:00", + "request": [ + { + "reference": "urn:uuid:1c38d507-9ad7-4b49-ba91-7da204842cac" + } + ], + "collection": { + "collectedDateTime": "2022-02-08T10:23:00+00:00" + } + } + }, + { + "fullUrl": "urn:uuid:ad717ae8-cb35-4d66-ba51-e22a76b1d158", + "resource": { + "resourceType": "Observation", + "id": "ad717ae8-cb35-4d66-ba51-e22a76b1d158", + "extension": [ + { + "url": "http://hl7.org/fhir/6.0/StructureDefinition/extension-Observation.organizer", + "valueBoolean": true + } + ], + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "1022441000000101", + "display": "FBC - full blood count" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "hasMember": [ + { + "reference": "urn:uuid:f8c185fc-0993-4b80-9d33-4c9ae11c3afe" + }, + { + "reference": "urn:uuid:78afe41f-4689-41a2-b10d-351a991b261a" + }, + { + "reference": "urn:uuid:b7849f25-3c44-4542-805f-ba769b7cfe04" + }, + { + "reference": "urn:uuid:b483a0bf-c332-4dde-b606-ec29aab33583" + }, + { + "reference": "urn:uuid:9e44a75d-e676-4497-aeb3-da72a08a3641" + }, + { + "reference": "urn:uuid:ba423cce-bf8c-440c-8061-086055a1799b" + }, + { + "reference": "urn:uuid:af52170f-763a-40a6-9363-dc58cd72602e" + }, + { + "reference": "urn:uuid:298306aa-4a5e-4442-8f60-9153c19c6333" + }, + { + "reference": "urn:uuid:69d08add-5ab8-46a3-be08-fb15e550da75" + }, + { + "reference": "urn:uuid:d5995ac0-55d8-4f2f-9058-6235a13fc1fe" + }, + { + "reference": "urn:uuid:42d1c079-777a-4878-b568-af6f297dbecb" + }, + { + "reference": "urn:uuid:56f9a606-221b-462b-b134-154c72db9c66" + }, + { + "reference": "urn:uuid:d719524b-993c-455c-9c18-36e3dfe6f71e" + } + ] + } + }, + { + "fullUrl": "urn:uuid:f8c185fc-0993-4b80-9d33-4c9ae11c3afe", + "resource": { + "resourceType": "Observation", + "id": "f8c185fc-0993-4b80-9d33-4c9ae11c3afe", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022541000000102", + "display": "Total white cell count" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1110441000000100", + "display": "White blood cell count in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 4.76, + "unit": "10*9/L" + }, + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 4, + "unit": "10*9/L" + }, + "high": { + "value": 11, + "unit": "10*9/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:78afe41f-4689-41a2-b10d-351a991b261a", + "resource": { + "resourceType": "Observation", + "id": "78afe41f-4689-41a2-b10d-351a991b261a", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "1022451000000103", + "display": "Red blood cell count" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 3.63, + "unit": "10*12/L" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "L", + "display": "Low" + } + ] + } + ], + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 4.1, + "unit": "10*12/L" + }, + "high": { + "value": 5.1, + "unit": "10*12/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:b7849f25-3c44-4542-805f-ba769b7cfe04", + "resource": { + "resourceType": "Observation", + "id": "b7849f25-3c44-4542-805f-ba769b7cfe04", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022431000000105", + "display": "Haemoglobin estimation" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1107511000000100", + "display": "Haemoglobin mass concentration in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 108, + "unit": "g/L" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "L", + "display": "Low" + } + ] + } + ], + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 120, + "unit": "g/L" + }, + "high": { + "value": 150, + "unit": "g/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:b483a0bf-c332-4dde-b606-ec29aab33583", + "resource": { + "resourceType": "Observation", + "id": "b483a0bf-c332-4dde-b606-ec29aab33583", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022291000000105", + "display": "Haematocrit" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1111571000000101", + "display": "Haematocrit volume fraction of blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 0.352, + "unit": "l/l" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "L", + "display": "Low" + } + ] + } + ], + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 0.36, + "unit": "l/l" + }, + "high": { + "value": 0.46, + "unit": "l/l" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:9e44a75d-e676-4497-aeb3-da72a08a3641", + "resource": { + "resourceType": "Observation", + "id": "9e44a75d-e676-4497-aeb3-da72a08a3641", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022491000000106", + "display": "MCV - Mean corpuscular volume" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1491000237105", + "display": "Erythrocytes MCV (mean corpuscular volume) in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 97, + "unit": "fL" + }, + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 80, + "unit": "fL" + }, + "high": { + "value": 100, + "unit": "fL" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:ba423cce-bf8c-440c-8061-086055a1799b", + "resource": { + "resourceType": "Observation", + "id": "ba423cce-bf8c-440c-8061-086055a1799b", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022471000000107", + "display": "MCH - Mean corpuscular haemoglobin" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022471000000107", + "display": "MCH - Mean corpuscular haemoglobin" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 29.8, + "unit": "pg" + }, + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 27, + "unit": "pg" + }, + "high": { + "value": 32, + "unit": "pg" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:af52170f-763a-40a6-9363-dc58cd72602e", + "resource": { + "resourceType": "Observation", + "id": "af52170f-763a-40a6-9363-dc58cd72602e", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022481000000109", + "display": "MCHC - Mean corpuscular haemoglobin concentration" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022481000000109", + "display": "MCHC - Mean corpuscular haemoglobin concentration" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 307, + "unit": "g/L" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "L", + "display": "Low" + } + ] + } + ], + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 315, + "unit": "g/L" + }, + "high": { + "value": 345, + "unit": "g/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:298306aa-4a5e-4442-8f60-9153c19c6333", + "resource": { + "resourceType": "Observation", + "id": "298306aa-4a5e-4442-8f60-9153c19c6333", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022651000000100", + "display": "Platelet count" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1108041000000107", + "display": "Platelet count in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 143, + "unit": "10*9/L" + }, + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 140, + "unit": "10*9/L" + }, + "high": { + "value": 400, + "unit": "10*9/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:69d08add-5ab8-46a3-be08-fb15e550da75", + "resource": { + "resourceType": "Observation", + "id": "69d08add-5ab8-46a3-be08-fb15e550da75", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022551000000104", + "display": "Neutrophil count" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1108071000000101", + "display": "Neutrophil count in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 2.66, + "unit": "10*9/L" + }, + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 2, + "unit": "10*9/L" + }, + "high": { + "value": 7, + "unit": "10*9/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:d5995ac0-55d8-4f2f-9058-6235a13fc1fe", + "resource": { + "resourceType": "Observation", + "id": "d5995ac0-55d8-4f2f-9058-6235a13fc1fe", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022581000000105", + "display": "Lymphocyte count" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "67541000237108", + "display": "Lymphocyte count in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 0.7, + "unit": "10*9/L" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "L", + "display": "Low" + } + ] + } + ], + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 1, + "unit": "10*9/L" + }, + "high": { + "value": 3, + "unit": "10*9/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:42d1c079-777a-4878-b568-af6f297dbecb", + "resource": { + "resourceType": "Observation", + "id": "42d1c079-777a-4878-b568-af6f297dbecb", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022591000000107", + "display": "Monocyte count" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1107991000000100", + "display": "Monocyte count in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 1.3, + "unit": "10*9/L" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "H", + "display": "High" + } + ] + } + ], + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 0.2, + "unit": "10*9/L" + }, + "high": { + "value": 1, + "unit": "10*9/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:56f9a606-221b-462b-b134-154c72db9c66", + "resource": { + "resourceType": "Observation", + "id": "56f9a606-221b-462b-b134-154c72db9c66", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022561000000101", + "display": "Eosinophil count" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1107391000000104", + "display": "Eosinophil count in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 0.07, + "unit": "10*9/L" + }, + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 0.02, + "unit": "10*9/L" + }, + "high": { + "value": 0.5, + "unit": "10*9/L" + } + } + ] + } + }, + { + "fullUrl": "urn:uuid:d719524b-993c-455c-9c18-36e3dfe6f71e", + "resource": { + "resourceType": "Observation", + "id": "d719524b-993c-455c-9c18-36e3dfe6f71e", + "identifier": [ + { + "system": "https://tools.ietf.org/html/rfc4122", + "value": "2af46949-4938-4c57-bad4-c4363e1965d5" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ] + } + ], + "code": { + "coding": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1022571000000108", + "display": "Basophil count" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/valueset-reference", + "valueUri": "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyAndLaboratoryMedicineObservables" + } + ], + "system": "http://snomed.info/sct", + "code": "1106091000000103", + "display": "Basophil count in blood" + } + ] + }, + "subject": { + "reference": "urn:uuid:ab87a3f8-1d37-44a9-804e-5e962598a6e4", + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "$subject" + } + }, + "issued": "2022-02-09T11:17:00+00:00", + "performer": [ + { + "reference": "urn:uuid:8a6d85b8-9837-4fed-a257-4cf207988338" + } + ], + "valueQuantity": { + "value": 0.03, + "unit": "10*9/L" + }, + "specimen": { + "reference": "urn:uuid:bab0eaec-1ec5-4598-b660-90bb38a1030d" + }, + "referenceRange": [ + { + "low": { + "value": 0.01, + "unit": "10*9/L" + }, + "high": { + "value": 0.1, + "unit": "10*9/L" + } + } + ] + } + } + ] +} diff --git a/pathology-api/tests/acceptance/steps/bundle_endpoint_steps.py b/pathology-api/tests/acceptance/steps/bundle_endpoint_steps.py index 4bf3ed1c..15dcf810 100644 --- a/pathology-api/tests/acceptance/steps/bundle_endpoint_steps.py +++ b/pathology-api/tests/acceptance/steps/bundle_endpoint_steps.py @@ -1,6 +1,8 @@ """Step definitions for pathology API bundle endpoint feature.""" +import json from collections.abc import Callable +from typing import Any import requests from pathology_api.fhir.r4.resources import ( @@ -9,7 +11,7 @@ ) from pytest_bdd import given, parsers, then, when -from tests.acceptance.conftest import ResponseContext +from tests.acceptance.conftest import ResponseContext, TestContext from tests.conftest import Client BUNDLE_ENDPOINT = "FHIR/R4/Bundle" @@ -32,7 +34,8 @@ def step_api_is_running(client: Client) -> None: def step_send_valid_bundle( client: Client, response_context: ResponseContext, - build_valid_test_result: Callable[[str, str], Bundle], + test_context: TestContext, + build_full_test_result: Callable[[str, str], str], ) -> None: """ Send a valid Bundle to the API. @@ -40,17 +43,19 @@ def step_send_valid_bundle( Args: client: Test client response_context: Context to store the response - build_valid_test_result: Function to build a valid test result + test_context: Context to store test data + build_full_test_result: Function to build a full test result """ + test_result = build_full_test_result("nhs_number_1", "ods_code") response_context.response = client.send( path=BUNDLE_ENDPOINT, request_method="POST", - data=build_valid_test_result("nhs_number_1", "ods_code").model_dump_json( - by_alias=True, exclude_none=True - ), + data=test_result, ) + test_context.sent_request = test_result + @when("I send an invalid Bundle to the Pathology API") def step_send_invalid_bundle(client: Client, response_context: ResponseContext) -> None: @@ -123,6 +128,30 @@ def step_check_status_code( f"got {response.status_code}" ) +@then("the response should include the created test result") +def step_check_response_includes_created_result( + response_context: ResponseContext, test_context: TestContext +) -> None: + """Verify the response includes the created test result. + + Args: + response_context: Context containing the response + test_context: Context containing the sent request data + """ + response = _validate_response_set(response_context) + + assert test_context.sent_request is not None, "Sent request has not been set." + + expected_data = json.loads(test_context.sent_request) + response_data = response.json() + + print(f"Expected data: {expected_data}") + print(f"Response data: {response_data}") + + _assert_content(expected_data, response_data) + + assert response_data.get("id") is not None + assert response_data.get("meta", {}).get("lastUpdated") is not None @then(parsers.cfparse('the response should contain "{expected_text}"')) def step_check_response_contains( @@ -165,3 +194,13 @@ def step_check_response_contains_valid_bundle( def _validate_response_set(response_context: ResponseContext) -> requests.Response: assert response_context.response is not None, "Response has not been set." return response_context.response + +def _assert_content(expected: Any, actual: Any) -> None: + if isinstance(expected, dict): + for k, v in expected.items(): + _assert_content(v, actual.get(k)) + elif isinstance(expected, list): + for i, item in enumerate(expected): + _assert_content(item, actual[i]) + else: + assert expected == actual, f"Expected {expected}, got {actual}" diff --git a/pathology-api/tests/integration/test_endpoints.py b/pathology-api/tests/integration/test_endpoints.py index 08b76a76..66141796 100644 --- a/pathology-api/tests/integration/test_endpoints.py +++ b/pathology-api/tests/integration/test_endpoints.py @@ -45,6 +45,9 @@ def test_bundle_returns_200( assert response_bundle.meta is not None response_meta = response_bundle.meta + + print(f"Response meta: {response_meta}") + assert response_meta.last_updated is not None assert response_meta.version_id == "1"