From fd335babc0aac3779ee1b3e12adec45e8f5d5c65 Mon Sep 17 00:00:00 2001 From: "Axel Garcia K." Date: Mon, 11 Aug 2025 13:10:13 +0100 Subject: [PATCH 1/4] NRL-1491 Add in context retrieval mechanism example --- resources/fhir/NRLF-Retrieval-CodeSystem.json | 5 + .../NRLF-RetrievalMechanism-ValueSet.json | 4 + .../INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json | 105 ++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 tests/data/samples/INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json diff --git a/resources/fhir/NRLF-Retrieval-CodeSystem.json b/resources/fhir/NRLF-Retrieval-CodeSystem.json index 9d28dc3d2..b447dcb6d 100644 --- a/resources/fhir/NRLF-Retrieval-CodeSystem.json +++ b/resources/fhir/NRLF-Retrieval-CodeSystem.json @@ -58,6 +58,11 @@ "definition": "This document can be retrieved via the Large Document Retrieval proxy service." } ] + }, + { + "code": "Direct_InContext", + "display": "Direct using In-Context", + "definition": "This document can be retrieved in-context via the In-Context launch mechanism." } ] } diff --git a/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json b/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json index 6af55f053..d69122141 100644 --- a/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json +++ b/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json @@ -33,6 +33,10 @@ { "code": "LDR", "display": "Large Document Retrieval" + }, + { + "code": "Direct_InContext", + "display": "Direct using In-Context" } ] } diff --git a/tests/data/samples/INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json b/tests/data/samples/INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json new file mode 100644 index 000000000..410f8bba2 --- /dev/null +++ b/tests/data/samples/INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json @@ -0,0 +1,105 @@ +{ + "resourceType": "DocumentReference", + "id": "INCTX-197ca0ca-f547-42b6-a68c-99b75a3a2df5", + "meta": { + "lastUpdated": "2025-02-10T16:48:27.326Z" + }, + "masterIdentifier": { + "system": "urn:ietf:rfc:3986", + "value": "mid_197ca0ca-f547-42b6-a68c-99b75a3a2df5" + }, + "status": "current", + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "736253002", + "display": "Mental health crisis plan" + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "734163000", + "display": "Care plan" + } + ] + } + ], + "subject": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/nhs-number", + "value": "9999999999" + } + }, + "date": "2025-02-10T16:48:27.326Z", + "author": [ + { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "L85012" + } + } + ], + "custodian": { + "identifier": { + "system": "https://fhir.nhs.uk/Id/ods-organization-code", + "value": "INCTX" + } + }, + "content": [ + { + "attachment": { + "contentType": "text/html", + "url": "https://supplier.inctxlaunch.example/content", + "creation": "2025-02-10T16:48:27.326Z" + }, + "format": { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode", + "code": "urn:nhs-ic:structured", + "display": "Structured Document" + }, + "extension": [ + { + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLContentStability", + "code": "dynamic", + "display": "Dynamic" + } + ] + }, + "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability" + }, + { + "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-RetrievalMechanism", + "valueCodeableConcept": [ + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism", + "code": "Direct_InContext", + "display": "Direct using In-Context" + } + ] + } + ] + } + ], + "context": { + "period": { + "start": "2025-02-10T16:48:27.326Z" + }, + "practiceSetting": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "1060971000000108", + "display": "General practice service" + } + ] + } + } +} From a3c9760956987a2fab84418c39b9187a5b7d713b Mon Sep 17 00:00:00 2001 From: "Axel Garcia K." Date: Thu, 21 Aug 2025 15:29:25 +0100 Subject: [PATCH 2/4] NRL-1491 Add InContext retrieval mechanism and allow text/html with structured format --- api/consumer/swagger.yaml | 10 ++++++++-- api/producer/swagger.yaml | 10 ++++++++-- layer/nrlf/consumer/fhir/r4/model.py | 9 +++++++-- layer/nrlf/core/constants.py | 3 ++- layer/nrlf/core/validators.py | 3 ++- layer/nrlf/producer/fhir/r4/model.py | 9 +++++++-- layer/nrlf/producer/fhir/r4/strict_model.py | 9 +++++++-- resources/fhir/NRLF-Retrieval-CodeSystem.json | 16 +++++++++------- .../fhir/NRLF-RetrievalMechanism-ValueSet.json | 4 ++-- ...> SYNTHETIC_IN_CONTEXT_LAUNCH_EXAMPLE.json} | 18 ++++++++++-------- 10 files changed, 62 insertions(+), 29 deletions(-) rename tests/data/samples/{INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json => SYNTHETIC_IN_CONTEXT_LAUNCH_EXAMPLE.json} (87%) diff --git a/api/consumer/swagger.yaml b/api/consumer/swagger.yaml index a6fb5141e..4652ddba5 100644 --- a/api/consumer/swagger.yaml +++ b/api/consumer/swagger.yaml @@ -1015,10 +1015,16 @@ components: - "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism" code: type: string - enum: ["SSP", "Direct", "LDR"] + enum: ["SSP", "Direct", "LDR", "InContext"] display: type: string - enum: ["Spine Secure Proxy", "Direct", "Large Document Retrieval"] + enum: + [ + "Spine Secure Proxy", + "Direct", + "Large Document Retrieval", + "Direct using In-Context", + ] required: - system - code diff --git a/api/producer/swagger.yaml b/api/producer/swagger.yaml index d513cec19..14f6f052f 100644 --- a/api/producer/swagger.yaml +++ b/api/producer/swagger.yaml @@ -1671,10 +1671,16 @@ components: - "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism" code: type: string - enum: ["SSP", "Direct", "LDR"] + enum: ["SSP", "Direct", "LDR", "InContext"] display: type: string - enum: ["Spine Secure Proxy", "Direct", "Large Document Retrieval"] + enum: + [ + "Spine Secure Proxy", + "Direct", + "Large Document Retrieval", + "Direct using In-Context", + ] required: - system - code diff --git a/layer/nrlf/consumer/fhir/r4/model.py b/layer/nrlf/consumer/fhir/r4/model.py index 36cbefce2..d008c8015 100644 --- a/layer/nrlf/consumer/fhir/r4/model.py +++ b/layer/nrlf/consumer/fhir/r4/model.py @@ -242,8 +242,13 @@ class ContentStabilityExtensionCoding(Coding): class RetrievalMechanismExtensionCoding(Coding): system: Literal["https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism"] - code: Literal["SSP", "Direct", "LDR"] - display: Literal["Spine Secure Proxy", "Direct", "Large Document Retrieval"] + code: Literal["SSP", "Direct", "LDR", "InContext"] + display: Literal[ + "Spine Secure Proxy", + "Direct", + "Large Document Retrieval", + "Direct using In-Context", + ] class NRLFormatCode(Coding): diff --git a/layer/nrlf/core/constants.py b/layer/nrlf/core/constants.py index d72162829..0152986f9 100644 --- a/layer/nrlf/core/constants.py +++ b/layer/nrlf/core/constants.py @@ -687,7 +687,8 @@ def coding_value(self): CONTENT_RETRIEVAL_CODE_MAP = { "Direct": "Direct", "SSP": "Spine Secure Proxy", - "LDR": "Large Document Retrieval", + "NDR": "Large Document Retrieval", + "InContext": "Direct using In-Context", } CONTENT_FORMAT_CODE_URL = "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode" CONTENT_FORMAT_CODE_MAP = { diff --git a/layer/nrlf/core/validators.py b/layer/nrlf/core/validators.py index 6c906cf50..768a47dc4 100644 --- a/layer/nrlf/core/validators.py +++ b/layer/nrlf/core/validators.py @@ -472,7 +472,8 @@ def _validate_content_format(self, model: DocumentReference): for i, content in enumerate(model.content): if ( content.attachment.contentType == "text/html" - and content.format.code != "urn:nhs-ic:record-contact" + and content.format.code + not in ["urn:nhs-ic:record-contact", "urn:nhs-ic:structured"] ): self.result.add_error( issue_code="business-rule", diff --git a/layer/nrlf/producer/fhir/r4/model.py b/layer/nrlf/producer/fhir/r4/model.py index 8b0e3fc64..714cc39f2 100644 --- a/layer/nrlf/producer/fhir/r4/model.py +++ b/layer/nrlf/producer/fhir/r4/model.py @@ -286,8 +286,13 @@ class ContentStabilityExtensionCoding(Coding): class RetrievalMechanismExtensionCoding(Coding): system: Literal["https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism"] - code: Literal["SSP", "Direct", "LDR"] - display: Literal["Spine Secure Proxy", "Direct", "Large Document Retrieval"] + code: Literal["SSP", "Direct", "LDR", "InContext"] + display: Literal[ + "Spine Secure Proxy", + "Direct", + "Large Document Retrieval", + "Direct using In-Context", + ] class NRLFormatCode(Coding): diff --git a/layer/nrlf/producer/fhir/r4/strict_model.py b/layer/nrlf/producer/fhir/r4/strict_model.py index 87cdefcdc..6e3886bcf 100644 --- a/layer/nrlf/producer/fhir/r4/strict_model.py +++ b/layer/nrlf/producer/fhir/r4/strict_model.py @@ -257,8 +257,13 @@ class ContentStabilityExtensionCoding(Coding): class RetrievalMechanismExtensionCoding(Coding): system: Literal["https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism"] - code: Literal["SSP", "Direct", "LDR"] - display: Literal["Spine Secure Proxy", "Direct", "Large Document Retrieval"] + code: Literal["SSP", "Direct", "LDR", "InContext"] + display: Literal[ + "Spine Secure Proxy", + "Direct", + "Large Document Retrieval", + "Direct using In-Context", + ] class NRLFormatCode(Coding): diff --git a/resources/fhir/NRLF-Retrieval-CodeSystem.json b/resources/fhir/NRLF-Retrieval-CodeSystem.json index b447dcb6d..406c02677 100644 --- a/resources/fhir/NRLF-Retrieval-CodeSystem.json +++ b/resources/fhir/NRLF-Retrieval-CodeSystem.json @@ -2,7 +2,7 @@ "resourceType": "CodeSystem", "id": "England-RetrievalMechanismNRL", "url": "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanismNRL", - "version": "1.0.1", + "version": "1.1.0", "name": "EnglandRetrievalMechanismNRL", "title": "England Retrieval MechanismNRL", "status": "draft", @@ -40,7 +40,14 @@ { "code": "Direct", "display": "Direct", - "definition": "This document can be directly retrieved via HTTP(s) at its public URL." + "definition": "This document can be directly retrieved via HTTP(s) at its public URL.", + "concept": [ + { + "code": "InContext", + "display": "Direct using In-Context", + "definition": "This document can be retrieved in-context via the In-Context launch mechanism." + } + ] }, { "code": "Proxy", @@ -58,11 +65,6 @@ "definition": "This document can be retrieved via the Large Document Retrieval proxy service." } ] - }, - { - "code": "Direct_InContext", - "display": "Direct using In-Context", - "definition": "This document can be retrieved in-context via the In-Context launch mechanism." } ] } diff --git a/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json b/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json index d69122141..e680a09d2 100644 --- a/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json +++ b/resources/fhir/NRLF-RetrievalMechanism-ValueSet.json @@ -2,7 +2,7 @@ "resourceType": "ValueSet", "id": "England-RetrievalMechanism", "url": "https://fhir.nhs.uk/England/ValueSet/England-RetrievalMechanism", - "version": "1.0.1", + "version": "1.1.0", "name": "EnglandRetrievalMechanism", "status": "draft", "date": "2025-02-28", @@ -35,7 +35,7 @@ "display": "Large Document Retrieval" }, { - "code": "Direct_InContext", + "code": "InContext", "display": "Direct using In-Context" } ] diff --git a/tests/data/samples/INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json b/tests/data/samples/SYNTHETIC_IN_CONTEXT_LAUNCH_EXAMPLE.json similarity index 87% rename from tests/data/samples/INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json rename to tests/data/samples/SYNTHETIC_IN_CONTEXT_LAUNCH_EXAMPLE.json index 410f8bba2..94b54f179 100644 --- a/tests/data/samples/INCTX_IN_CONTEXT_LAUNCH_EXAMPLE.json +++ b/tests/data/samples/SYNTHETIC_IN_CONTEXT_LAUNCH_EXAMPLE.json @@ -76,14 +76,16 @@ "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability" }, { - "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-RetrievalMechanism", - "valueCodeableConcept": [ - { - "system": "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism", - "code": "Direct_InContext", - "display": "Direct using In-Context" - } - ] + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism", + "code": "InContext", + "display": "Direct using In-Context" + } + ] + }, + "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-RetrievalMechanism" } ] } From 289501e92b38584115b1041e88806cdd341b4338 Mon Sep 17 00:00:00 2001 From: "Axel Garcia K." Date: Wed, 27 Aug 2025 14:46:55 +0100 Subject: [PATCH 3/4] NRL-1491 Expand integration tests to include in-context --- .../features/producer/createDocumentReference-failure.feature | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/features/producer/createDocumentReference-failure.feature b/tests/features/producer/createDocumentReference-failure.feature index 099ae18c1..6651b90ac 100644 --- a/tests/features/producer/createDocumentReference-failure.feature +++ b/tests/features/producer/createDocumentReference-failure.feature @@ -1369,7 +1369,7 @@ Feature: Producer - createDocumentReference - Failure Scenarios } ] }, - "diagnostics": "Invalid content retrieval extension (content[0].extension[0].valueCodeableConcept.coding[0].code: Input should be 'SSP', 'Direct' or 'LDR', see: https://fhir.nhs.uk/England/ValueSet/England-RetrievalMechanism)", + "diagnostics": "Invalid content retrieval extension (content[0].extension[0].valueCodeableConcept.coding[0].code: Input should be 'SSP', 'Direct', 'LDR' or 'InContext', see: https://fhir.nhs.uk/England/ValueSet/England-RetrievalMechanism)", "expression": ["content[0].extension[0].valueCodeableConcept.coding[0].code"] } """ @@ -1436,7 +1436,7 @@ Feature: Producer - createDocumentReference - Failure Scenarios } ] }, - "diagnostics": "Invalid content retrieval extension (content[0].extension[0].valueCodeableConcept.coding[0].display: Input should be 'Spine Secure Proxy', 'Direct' or 'Large Document Retrieval', see: https://fhir.nhs.uk/England/ValueSet/England-RetrievalMechanism)", + "diagnostics": "Invalid content retrieval extension (content[0].extension[0].valueCodeableConcept.coding[0].display: Input should be 'Spine Secure Proxy', 'Direct', 'Large Document Retrieval' or 'Direct using In-Context', see: https://fhir.nhs.uk/England/ValueSet/England-RetrievalMechanism)", "expression": ["content[0].extension[0].valueCodeableConcept.coding[0].display"] } """ From 4fad9db7394ab98619b8d038a3f21ce37517b576 Mon Sep 17 00:00:00 2001 From: "Axel Garcia K." Date: Wed, 27 Aug 2025 15:18:33 +0100 Subject: [PATCH 4/4] NRL-1491 Add tests for in context launch document pointers --- layer/nrlf/core/constants.py | 2 +- layer/nrlf/core/tests/test_validators.py | 43 +++++++++++ .../createDocumentReference-success.feature | 77 +++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/layer/nrlf/core/constants.py b/layer/nrlf/core/constants.py index 0152986f9..a3ccb4583 100644 --- a/layer/nrlf/core/constants.py +++ b/layer/nrlf/core/constants.py @@ -687,7 +687,7 @@ def coding_value(self): CONTENT_RETRIEVAL_CODE_MAP = { "Direct": "Direct", "SSP": "Spine Secure Proxy", - "NDR": "Large Document Retrieval", + "LDR": "Large Document Retrieval", "InContext": "Direct using In-Context", } CONTENT_FORMAT_CODE_URL = "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode" diff --git a/layer/nrlf/core/tests/test_validators.py b/layer/nrlf/core/tests/test_validators.py index 03c458ca7..40ab6a84a 100644 --- a/layer/nrlf/core/tests/test_validators.py +++ b/layer/nrlf/core/tests/test_validators.py @@ -1884,6 +1884,7 @@ def test_validate_content_stability_extension_display_mismatch(code, display): ("SSP", "Spine Secure Proxy"), ("Direct", "Direct"), ("LDR", "Large Document Retrieval"), + ("InContext", "Direct using In-Context"), ], ) def test_validate_retrieval_mechanism_extension_valid(code, display): @@ -1911,3 +1912,45 @@ def test_validate_retrieval_mechanism_extension_display_mismatch(code, display): in issue.diagnostics for issue in validator.result.issues ) + + +def test_validate_structured_format_with_text_html_for_incontext_launch(): + validator = DocumentReferenceValidator() + document_ref_data = load_document_reference_json("Y05868-736253002-Valid") + + # Set up for direct in-context launch + document_ref_data["content"][0]["attachment"]["contentType"] = "text/html" + document_ref_data["content"][0]["format"] = { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode", + "code": "urn:nhs-ic:structured", + "display": "Structured Document", + } + document_ref_data["content"][0]["extension"] = [ + { + "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-RetrievalMechanism", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism", + "code": "InContext", + "display": "Direct using In-Context", + } + ] + }, + }, + { + "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLContentStability", + "code": "dynamic", + "display": "Dynamic", + } + ] + }, + }, + ] + + result = validator.validate(document_ref_data) + assert result.is_valid is True diff --git a/tests/features/producer/createDocumentReference-success.feature b/tests/features/producer/createDocumentReference-success.feature index cc7186b12..fa654a1fd 100644 --- a/tests/features/producer/createDocumentReference-success.feature +++ b/tests/features/producer/createDocumentReference-success.feature @@ -491,3 +491,80 @@ Feature: Producer - createDocumentReference - Success Scenarios | content[1].attachment.url | https://example.org/doc2.pdf | | content[1].extension[1].valueCodeableConcept.coding[0].code | SSP | | content[1].extension[1].valueCodeableConcept.coding[0].display | Spine Secure Proxy | + + Scenario: Successfully create a Document Pointer with InContext retrieval mechanism and structured format + 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 | 1363501000000100 | + When producer 'TSTCUS' requests creation of a DocumentReference with default test values except 'content' is: + """ + "content": [ + { + "attachment": { + "contentType": "text/html", + "url": "https://example.org/incontext-launch.html" + }, + "format": { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLFormatCode", + "code": "urn:nhs-ic:structured", + "display": "Structured Document" + }, + "extension": [ + { + "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-ContentStability", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-NRLContentStability", + "code": "dynamic", + "display": "Dynamic" + } + ] + } + }, + { + "url": "https://fhir.nhs.uk/England/StructureDefinition/Extension-England-RetrievalMechanism", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://fhir.nhs.uk/England/CodeSystem/England-RetrievalMechanism", + "code": "InContext", + "display": "Direct using In-Context" + } + ] + } + } + ] + } + ] + """ + Then the response status code is 201 + And the response is an OperationOutcome with 1 issue + And the OperationOutcome contains the issue: + """ + { + "severity": "information", + "code": "informational", + "details": { + "coding": [ + { + "system": "https://fhir.nhs.uk/ValueSet/NRL-ResponseCode", + "code": "RESOURCE_CREATED", + "display": "Resource created" + } + ] + }, + "diagnostics": "The document has been created" + } + """ + And the response has a Location header + And the Location header starts with '/producer/FHIR/R4/DocumentReference/TSTCUS-' + And the resource in the Location header exists with values: + | property | value | + | content[0].attachment.url | https://example.org/incontext-launch.html | + | content[0].attachment.contentType | text/html | + | content[0].format.code | urn:nhs-ic:structured | + | content[0].format.display | Structured Document | + | content[0].extension[1].valueCodeableConcept.coding[0].code | InContext | + | content[0].extension[1].valueCodeableConcept.coding[0].display | Direct using In-Context |