From 2818d67549ff144cd3806a09913431b712646ad9 Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Wed, 22 Jul 2026 15:20:05 +0100 Subject: [PATCH 01/22] New results endpoints for supplier API for acute pattern --- docs/test-types/README.md | 29 + docs/test-types/hiv.md | 119 ++++ docs/test-types/psa.md | 134 ++++ schemas/changelog.md | 32 + schemas/home-test-supplier-api.yaml | 250 +++++-- schemas/supplier-api-spec-v2.yaml | 976 ++++++++++++++++++++++++++++ 6 files changed, 1502 insertions(+), 38 deletions(-) create mode 100644 docs/test-types/README.md create mode 100644 docs/test-types/hiv.md create mode 100644 docs/test-types/psa.md create mode 100644 schemas/supplier-api-spec-v2.yaml diff --git a/docs/test-types/README.md b/docs/test-types/README.md new file mode 100644 index 0000000..098bce9 --- /dev/null +++ b/docs/test-types/README.md @@ -0,0 +1,29 @@ +# Test-Type Guidance + +The [supplier API spec](../../schemas/supplier-api-spec-v2.yaml) defines the base integration contract. Not every field is meaningful for every test type, and some fields that are optional in the spec are **mandatory in practice** for specific test types. + +This directory contains per-test-type guidance that suppliers **must read alongside the spec** before implementing. + +## How to use these documents + +Each file covers one test type and specifies: + +- The applicable SNOMED CT code(s) +- Which optional spec fields become mandatory for that test type +- Supplier responsibilities (clinical management, patient contact, dispatch) +- Relevant `Extension-UKCore-SpecimenCollectionMethod` values +- Result and observation requirements + +## Test types + +| File | Test | Order path | +|---|---|---| +| [hiv.md](hiv.md) | HIV antigen test | Consumer-initiated (patient self-order) | +| [psa.md](psa.md) | PSA (Prostate-Specific Antigen) | Clinician-initiated (acute/EPR) | + +## Key conventions + +- **`telecom`** is optional in the base spec. Whether it is required depends on who carries clinical management responsibility for the test type. See each test-type file for the specific rule. +- **`Extension-UKCore-SpecimenCollectionMethod`** will be present on orders where the collection method must be specified. Suppliers must use this value to determine which kit variant to dispatch. +- **`gender`** will be present on clinician-initiated orders (sourced from the EPR). It may be absent on consumer-initiated orders. +- Fields not listed in a test-type file follow the base spec rules (optional unless spec marks them required). diff --git a/docs/test-types/hiv.md b/docs/test-types/hiv.md new file mode 100644 index 0000000..e5e0f01 --- /dev/null +++ b/docs/test-types/hiv.md @@ -0,0 +1,119 @@ +# HIV Antigen Test — Supplier Guidance + +## Overview + +| | | +|---|---| +| **Test name** | HIV antigen test | +| **SNOMED CT code** | `31676001` — HIV antigen test | +| **Order path** | Consumer-initiated (patient self-order via HomeTest platform) | +| **Clinical management** | Supplier responsibility | + +--- + +## Mandatory fields (beyond base spec) + +The following fields are **optional** in the base supplier spec but are **mandatory** for HIV orders. + +### `contained[Patient].telecom` + +Suppliers are responsible for all clinical management of HIV test orders. This includes contacting the patient to deliver results, providing appropriate clinical advice, and referring where required. Patient contact details are therefore essential. + +**Requirements:** +- `telecom` **must** be present with a minimum of two entries +- One entry **must** have `system: "phone"` with a valid UK phone number +- One entry **must** have `system: "email"` with a valid email address + +```json +"telecom": [ + { + "system": "phone", + "value": "+447700900123", + "use": "mobile" + }, + { + "system": "email", + "value": "patient@example.com", + "use": "home" + } +] +``` + +--- + +## Specimen collection method + +The `Extension-UKCore-SpecimenCollectionMethod` extension on the `ServiceRequest` specifies how the sample is to be collected. Suppliers must use this value to determine which kit variant to dispatch. + +### Supported values + +| SNOMED code | Display | Kit variant | +|---|---|---| +| `122554006` | Capillary blood specimen | Finger-prick blood spot card | + +> **Note:** Additional collection methods (oral swab, transdermal) may be added in future. Suppliers should handle unknown `SpecimenCollectionMethod` values gracefully and contact the HomeTest team before rejecting an order on this basis. + +If the extension is absent, assume capillary blood (current default). + +--- + +## Supplier responsibilities + +| Responsibility | Supplier | HomeTest platform | Requesting clinician | +|---|---|---|---| +| Kit dispatch | ✅ | | | +| Sample receipt and processing | ✅ | | | +| Result submission (`/results`) | ✅ | | | +| Patient notification of result | ✅ | | | +| Clinical advice and onward referral | ✅ | | | +| Eligibility / quota checking | ✅ (via 409 response) | | | + +--- + +## Order flow + +1. HomeTest platform sends `POST /order` with a `FHIRServiceRequest` containing patient demographics (including `telecom`) +2. Supplier validates eligibility and quota rules; return `409` with appropriate `OperationOutcome` if the order cannot proceed +3. On acceptance, supplier dispatches the kit to the address in `contained[Patient].address` +4. Supplier posts status updates via the task endpoint as the order progresses +5. On result availability, supplier submits an `Observation` via `POST /results` +6. Supplier contacts the patient directly to deliver results and provide clinical support + +--- + +## Result requirements + +Results must be submitted as a `FHIRObservation` and must include: + +- `basedOn` referencing the originating `ServiceRequest` +- `status: "final"` for confirmed results +- `code` using SNOMED CT (`31676001` — HIV antigen test) +- `valueCodeableConcept` with one of the following result values: + +| SNOMED code | Display | Meaning | +|---|---|---| +| `260415000` | Not detected | Negative result | +| `260373001` | Detected | Positive result — clinical follow-up required | +| `419984006` | Inconclusive | Indeterminate — retest may be required | + +- `interpretation` using `http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation`: + - `N` (Normal) for negative + - `POS` (Positive) for detected + - `IND` (Indeterminate) for inconclusive + +--- + +## Business rule errors (409 responses) + +Suppliers must return a `409 Conflict` with a `FHIROperationOutcome` for the following conditions: + +| `details.text` | Condition | +|---|---| +| `individual_quota_used` | Patient has already used their test allocation for this period | +| `local_quota_exceeded` | Local authority quota has been exhausted | +| `out_of_stock` | Kit variant required is not currently available | +| `not_eligible` | Patient does not meet local eligibility criteria | +| `test_not_available` | Test type not available from this supplier | +| `suspected_fraud` | Order flagged for manual review | + +See [supplier-api-spec-v2.yaml](../../schemas/supplier-api-spec-v2.yaml) for the full `409` response schema and examples. diff --git a/docs/test-types/psa.md b/docs/test-types/psa.md new file mode 100644 index 0000000..e04149b --- /dev/null +++ b/docs/test-types/psa.md @@ -0,0 +1,134 @@ +# PSA (Prostate-Specific Antigen) — Supplier Guidance + +> **Status: Work in progress.** This document provides a structural skeleton. Sections marked **[TBD]** require further clinical and operational input before supplier onboarding for PSA tests. + +## Overview + +| | | +|---|---| +| **Test name** | Measurement of prostate-specific antigen | +| **SNOMED CT code** | `63476009` — Measurement of prostate specific antigen | +| **Order path** | Clinician-initiated (acute/EPR via HomeTest acute consumer API) | +| **Clinical management** | Requesting clinician's responsibility — **not the supplier** | + +--- + +## Key difference from consumer-initiated tests + +PSA orders originate from a clinician in an acute or primary care setting via their EPR system. The requesting clinician retains full clinical management responsibility, including result interpretation, patient notification, and onward referral. + +**Suppliers do not contact the patient.** Suppliers dispatch the kit, process the sample, and submit results. All clinical interaction is handled outside of the supplier's scope. + +--- + +## Mandatory fields (beyond base spec) + +### `contained[Patient].telecom` + +**Not required.** Suppliers must not attempt to contact the patient directly for PSA orders. `telecom` will be absent from these orders. Supplier implementations must tolerate a `FHIRServiceRequest` where `telecom` is not present on the contained `Patient`. + +### `contained[Patient].gender` + +`gender` will be present on PSA orders (sourced from the EPR). Suppliers should use this where relevant to test processing or result interpretation. **[TBD: confirm whether gender is required for PSA processing or informational only]** + +### `ServiceRequest.extension` — SpecimenCollectionMethod + +The `Extension-UKCore-SpecimenCollectionMethod` extension will be present on PSA orders and must be used to determine the kit variant to dispatch. + +**[TBD: confirm supported collection method SNOMED codes for PSA]** + +| SNOMED code | Display | Kit variant | +|---|---|---| +| `122554006` | Capillary blood specimen | Finger-prick blood spot card | +| **[TBD]** | **[TBD]** | **[TBD]** | + +--- + +## Supplier responsibilities + +| Responsibility | Supplier | HomeTest platform | Requesting clinician | +|---|---|---|---| +| Kit dispatch | ✅ | | | +| Sample receipt and processing | ✅ | | | +| Result submission (`/results`) | ✅ | | | +| Patient notification of result | | | ✅ | +| Clinical advice and onward referral | | | ✅ | +| Eligibility / quota checking | **[TBD]** | | | + +--- + +## Order flow + +**[TBD: confirm end-to-end flow for clinician-initiated PSA orders, including any differences in status update expectations]** + +1. HomeTest platform receives order from EPR, validates, schedules dispatch, then sends `POST /order` to supplier +2. Supplier dispatches kit to address in `contained[Patient].address` +3. Supplier posts status updates via the task endpoint as the order progresses +4. On result availability, supplier submits an `Observation` via `POST /results` +5. HomeTest platform routes result back to the requesting clinician's EPR — **supplier does not contact the patient** + +--- + +## Result requirements + +PSA is a **quantitative** test — the result is a numeric concentration value, not a coded qualitative finding. Suppliers must use `valueQuantity`, not `valueCodeableConcept`. + +Results must be submitted as a `FHIRObservation` and must include: + +- `basedOn` referencing the originating `ServiceRequest` +- `status: "final"` for confirmed results +- `code` using SNOMED CT (`63476009` — Measurement of prostate specific antigen) +- `valueQuantity` with the measured PSA concentration in ng/mL (see below) +- `referenceRange` (see below) +- `interpretation` indicating whether the value is within or outside the normal range + +### `valueQuantity` + +```json +"valueQuantity": { + "value": 4.2, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" +} +``` + +- `system` must be `http://unitsofmeasure.org` (UCUM) +- `code` must be `ng/mL` + +### `referenceRange` + +PSA reference ranges are age-dependent. Suppliers must provide at least one `referenceRange` entry. Where age-adjusted ranges are applied, each band should be a separate entry with the applicable age described in the `text` field. + +```json +"referenceRange": [ + { + "low": { "value": 0, "unit": "ng/mL", "system": "http://unitsofmeasure.org", "code": "ng/mL" }, + "high": { "value": 4.0, "unit": "ng/mL", "system": "http://unitsofmeasure.org", "code": "ng/mL" }, + "type": { + "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", "code": "normal", "display": "Normal Range" }] + }, + "text": "Normal range for adult males (age-adjusted ranges may apply)" + } +] +``` + +**[TBD: confirm whether a standard NHS age-adjusted PSA reference range table should be mandated, or whether suppliers use their own laboratory reference ranges.]** + +### `interpretation` + +Use codes from `http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation`: + +| Code | Display | Meaning | +|---|---|---| +| `N` | Normal | Value within reference range | +| `H` | High | Value above upper reference range | +| `HH` | Critical high | Value significantly above upper reference range — urgent clinical review required | + +--- + +## Business rule errors (409 responses) + +**[TBD: confirm which 409 business rule conditions apply to clinician-initiated PSA orders. Individual quota and local quota rules may not apply in the same way as consumer-initiated tests.]** + +See [supplier-api-spec-v2.yaml](../../schemas/supplier-api-spec-v2.yaml) for the full `409` response schema and examples. diff --git a/schemas/changelog.md b/schemas/changelog.md index 6357f3b..e228aef 100644 --- a/schemas/changelog.md +++ b/schemas/changelog.md @@ -1,3 +1,35 @@ +**Version 2.0.0 - July 20, 2026 - Acute Consumer Order Support** + +Changes to supplier-api-spec-v2.yaml (new file, v1 remains unchanged): + +1. Relaxed `telecom` from required to optional on contained Patient + - **Reason:** Clinician-initiated (acute) orders do not require supplier-to-patient contact — clinical management is the requesting clinician's responsibility. Telecom is still mandatory for test types where suppliers manage clinical follow-up (e.g. HIV); this is now enforced via test-type guidance in `docs/test-types/` rather than the base spec. + - **Impact on existing suppliers:** Non-breaking. Consumer-initiated HIV orders continue to include telecom as before. Existing suppliers implementing HIV tests are unaffected. + +2. Added `extension` (optional array) to `FHIRServiceRequest` + - Carries `Extension-UKCore-SpecimenCollectionMethod` to specify the sample collection method (e.g. capillary blood, oral swab). + - Supports current HIV capillary blood path and future variants (oral swab, transdermal). + - **Impact:** Non-breaking additive field. Suppliers not yet reading this extension should handle it gracefully and dispatch the default kit variant; see per-test-type docs for when this field becomes mandatory. + +3. Added `authoredOn` (optional, date-time) to `FHIRServiceRequest` + - Set by the HomeTest platform at order creation time. Useful for supplier audit trails and order traceability. + - **Impact:** Non-breaking additive field. + +4. Added `gender` (optional enum: male/female/other/unknown) to contained Patient + - Present on clinician-initiated orders (sourced from EPR). May be absent on consumer-initiated orders. + - **Impact:** Non-breaking additive field. + +5. Added `meta` (optional) to contained Patient + - Allows UK Core Patient profile URL to flow through where present on the source resource. + - **Impact:** Non-breaking additive field. + +6. Added `docs/test-types/` guidance directory + - `docs/test-types/README.md` — explains the convention: spec is the base contract; test-type files define per-test mandatory fields and supplier responsibilities. + - `docs/test-types/hiv.md` — specifies that `telecom` is mandatory for HIV, documents clinical management responsibilities, specimen collection method values, and result codes. + - `docs/test-types/psa.md` — skeleton for PSA (clinician-initiated); documents that `telecom` is not required, clinical management is by the requesting clinician. Sections marked [TBD] pending clinical/operational input. + +--- + **Version 1.0.1** --- diff --git a/schemas/home-test-supplier-api.yaml b/schemas/home-test-supplier-api.yaml index 43979a1..53b167b 100644 --- a/schemas/home-test-supplier-api.yaml +++ b/schemas/home-test-supplier-api.yaml @@ -33,36 +33,89 @@ paths: application/fhir+json: schema: $ref: '#/components/schemas/FHIRObservation' - example: - resourceType: "Observation" - id: "550e8400-e29b-41d4-a716-446655440001" - basedOn: - - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" - status: "final" - code: - coding: - - system: "http://snomed.info/sct" - code: "31676001" - display: "HIV antigen test" - text: "HIV antigen test" - subject: - reference: "Patient/123e4567-e89b-12d3-a456-426614174000" - effectiveDateTime: "2025-11-04T15:45:00Z" - issued: "2025-11-04T16:00:00Z" - performer: - - reference: "Organization/SUPP001" - display: "Test Supplier Ltd" - valueCodeableConcept: - coding: - - system: "http://snomed.info/sct" - code: "260415000" - display: "Not detected" - interpretation: - - coding: - - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" - code: "N" - display: "Normal" - text: "Normal" + examples: + hiv_negative: + summary: HIV antigen test — negative result (valueCodeableConcept) + value: + resourceType: "Observation" + id: "550e8400-e29b-41d4-a716-446655440001" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + code: + coding: + - system: "http://snomed.info/sct" + code: "31676001" + display: "HIV antigen test" + text: "HIV antigen test" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2025-11-04T15:45:00Z" + issued: "2025-11-04T16:00:00Z" + performer: + - reference: "Organization/SUPP001" + display: "Test Supplier Ltd" + valueCodeableConcept: + coding: + - system: "http://snomed.info/sct" + code: "260415000" + display: "Not detected" + interpretation: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" + code: "N" + display: "Normal" + text: "Normal" + psa_result: + summary: PSA test — quantitative result with reference range (valueQuantity) + value: + resourceType: "Observation" + id: "550e8400-e29b-41d4-a716-446655440002" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + code: + coding: + - system: "http://snomed.info/sct" + code: "63476009" + display: "Measurement of prostate specific antigen" + text: "Measurement of prostate specific antigen" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2026-07-21T10:30:00Z" + issued: "2026-07-21T11:00:00Z" + performer: + - reference: "Organization/SUPP002" + display: "PSA Test Lab Ltd" + valueQuantity: + value: 4.2 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + interpretation: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" + code: "H" + display: "High" + text: "Above reference range" + referenceRange: + - low: + value: 0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + high: + value: 4.0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + type: + coding: + - system: "http://terminology.hl7.org/CodeSystem/referencerange-meaning" + code: "normal" + display: "Normal Range" + text: "Normal Range" + text: "Normal range for adult males (age-adjusted ranges may apply)" '400': $ref: '#/components/responses/BadRequest' '401': @@ -112,7 +165,6 @@ components: - code - subject - basedOn - - valueCodeableConcept properties: resourceType: type: string @@ -122,6 +174,19 @@ components: type: string description: Logical id of this artifact example: "550e8400-e29b-41d4-a716-446655440001" + meta: + type: object + description: >- + Metadata about this resource. SHOULD include the UK Core profile URL to declare + conformance, enabling profile validation by receiving systems. + properties: + profile: + type: array + description: UK Core profile URL(s) this resource conforms to + minItems: 1 + items: + type: string + example: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Observation"] basedOn: type: array description: Reference to the ServiceRequest this observation fulfills @@ -135,6 +200,22 @@ components: description: Status of the observation result enum: [registered, preliminary, final, amended, corrected, cancelled, entered-in-error, unknown] example: "final" + category: + type: array + description: >- + Classification of the observation. SHOULD be present for UK Core conformance + (UKCore-ObservationCategory preferred binding). All home test results (HIV, PSA) + are laboratory tests — use code 'laboratory'. + System: http://terminology.hl7.org/CodeSystem/observation-category + minItems: 1 + items: + $ref: '#/components/schemas/FHIRCodeableConcept' + example: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: "Laboratory" code: allOf: - $ref: '#/components/schemas/FHIRCodeableConcept' @@ -180,12 +261,73 @@ components: valueCodeableConcept: allOf: - $ref: '#/components/schemas/FHIRCodeableConcept' - - description: Actual result (for coded results) + - description: >- + Actual result as a coded concept (for qualitative results, e.g. HIV detected/not detected). + Exactly one of valueCodeableConcept or valueQuantity must be present — + see docs/test-types/ for which applies to each test type. example: coding: - system: "http://snomed.info/sct" code: "260415000" display: "Not detected" + valueQuantity: + allOf: + - $ref: '#/components/schemas/FHIRQuantity' + - description: >- + Actual result as a measured quantity (for quantitative results, e.g. PSA level in ng/mL). + Exactly one of valueQuantity or valueCodeableConcept must be present — + see docs/test-types/ for which applies to each test type. + example: + value: 4.2 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + referenceRange: + type: array + description: >- + Guide for interpreting the result value. Must be present when valueQuantity is used. + Age-adjusted ranges should be expressed as separate entries with the text or appliesTo + fields indicating the applicable population. + items: + type: object + properties: + low: + allOf: + - $ref: '#/components/schemas/FHIRQuantity' + - description: Lower bound of the reference range (inclusive) + example: + value: 0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + high: + allOf: + - $ref: '#/components/schemas/FHIRQuantity' + - description: Upper bound of the reference range (inclusive) + example: + value: 4.0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + type: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: Reference range qualifier (normal, therapeutic, etc.) + example: + coding: + - system: "http://terminology.hl7.org/CodeSystem/referencerange-meaning" + code: "normal" + display: "Normal Range" + text: "Normal Range" + appliesTo: + type: array + description: Population this range applies to (e.g. age group, sex) + items: + $ref: '#/components/schemas/FHIRCodeableConcept' + text: + type: string + description: Free text description of the reference range + example: "Normal range for adult males (age-adjusted ranges may apply)" FHIRTask: type: object @@ -207,6 +349,19 @@ components: type: string description: Logical id of this artifact example: "task-550e8400-e29b-41d4-a716-446655440000" + meta: + type: object + description: >- + Metadata about this resource. SHOULD include the UK Core profile URL to declare + conformance, enabling profile validation by receiving systems. + properties: + profile: + type: array + description: UK Core profile URL(s) this resource conforms to + minItems: 1 + items: + type: string + example: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Task"] identifier: type: array description: Task Instance Identifier @@ -252,12 +407,6 @@ components: format: date-time description: Task Last Modified Date example: "2025-11-04T10:35:00Z" - requester: - allOf: - - $ref: '#/components/schemas/FHIRReference' - - description: Who is asking for task to be done - example: - reference: "Organization/ORG001" owner: allOf: - $ref: '#/components/schemas/FHIRReference' @@ -376,6 +525,31 @@ components: type: string description: Representation defined by the system example: "HIV antigen test" + FHIRQuantity: + type: object + description: A measured or measurable amount (FHIR Quantity datatype) + properties: + value: + type: number + description: Numerical value + example: 4.2 + comparator: + type: string + description: How to understand the value — used when the value is a bound (e.g. '<4.0') + enum: ["<", "<=", ">=", ">"] + example: "<" + unit: + type: string + description: Unit representation (human-readable) + example: "ng/mL" + system: + type: string + description: System that defines coded unit form — use UCUM (http://unitsofmeasure.org) + example: "http://unitsofmeasure.org" + code: + type: string + description: Coded form of the unit (UCUM code) + example: "ng/mL" FHIRIdentifier: type: object description: An identifier intended for computation (FHIR Identifier datatype) diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml new file mode 100644 index 0000000..31b70b1 --- /dev/null +++ b/schemas/supplier-api-spec-v2.yaml @@ -0,0 +1,976 @@ +openapi: 3.0.3 +info: + title: Supplier API + description: API for medical test suppliers to provide in order to allow Home Test platform integration + version: 2.0.0 + +paths: + /oauth/token: + post: + summary: OAuth 2.0 Token Endpoint + description: Exchange client credentials for an access token + tags: + - Authentication + security: [] + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + required: + - grant_type + - client_id + - client_secret + properties: + grant_type: + type: string + enum: [client_credentials] + description: OAuth 2.0 grant type - must be 'client_credentials' + example: "client_credentials" + client_id: + type: string + description: Client identifier assigned during registration + example: "supplier_client_abc123" + client_secret: + type: string + description: Client secret assigned during registration + example: "client_secret_xyz789" + scope: + type: string + description: Optional - requested scope (space-separated) + example: "orders results" + responses: + '200': + description: Access token issued successfully + content: + application/json: + schema: + type: object + properties: + access_token: + type: string + description: The access token for API requests + example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + token_type: + type: string + description: Token type - always 'Bearer' + example: "Bearer" + expires_in: + type: integer + description: Token lifetime in seconds + example: 3600 + scope: + type: string + description: Granted scopes (space-separated) + example: "orders results" + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + error: + type: string + enum: [invalid_request, invalid_client, invalid_grant, unsupported_grant_type] + example: "invalid_request" + error_description: + type: string + example: "The request is missing a required parameter" + '401': + description: Invalid client credentials + content: + application/json: + schema: + type: object + properties: + error: + type: string + example: "invalid_client" + error_description: + type: string + example: "Client authentication failed" + + /order: + post: + summary: Receive Test Order + description: Receive a new test order from the home test platform + tags: + - Order Management + parameters: + - name: X-Correlation-ID + in: header + required: true + description: Unique identifier for request tracking and idempotency + schema: + type: string + format: uuid + example: "550e8400-e29b-41d4-a716-446655440000" + requestBody: + required: true + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIRServiceRequest' + responses: + '201': + description: Order received and processed successfully + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIRServiceRequest' + '400': + $ref: '#/components/responses/BadRequest' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '409': + description: Business rule conflict - order cannot be processed + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIROperationOutcome' + examples: + test_not_available: + summary: Test not available + value: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "business-rule" + details: + text: "Test not available" + diagnostics: "The requested test is not available from this supplier" + individual_quota_used: + summary: Individual quota already used + value: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "business-rule" + details: + text: "Individual quota already used" + diagnostics: "Patient has already used their allocated quota for this test type" + local_quota_exceeded: + summary: Local test quota exceeded + value: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "business-rule" + details: + text: "Local test quota exceeded" + diagnostics: "Local authority test quota has been exceeded for this area" + suspected_fraud: + summary: Suspected fraudulent order + value: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "security" + details: + text: "Suspected fraudulent order" + diagnostics: "Order flagged for potential fraudulent activity and requires manual review" + out_of_stock: + summary: Out of stock + value: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "business-rule" + details: + text: "Out of stock" + diagnostics: "Test kit is currently out of stock at the requested location" + not_eligible: + summary: Individual not eligible due to local authority rules + value: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "business-rule" + details: + text: "Individual not eligible due to local authority rules" + diagnostics: "Patient does not meet local authority eligibility criteria for this test" + + /results: + get: + summary: Get Test Results + description: Retrieve test results from the home test platform for completed orders + tags: + - Results Management + parameters: + - name: X-Correlation-ID + in: header + required: true + description: Unique identifier for request tracking and idempotency + schema: + type: string + format: uuid + example: "550e8400-e29b-41d4-a716-446655440000" + - name: order_uid + in: query + required: true + description: Unique identifier of the order to get results for + schema: + type: string + format: uuid + example: "550e8400-e29b-41d4-a716-446655440000" + responses: + '200': + description: Results retrieved successfully + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIRBundleSearchsetObservations' + examples: + hiv_negative: + summary: HIV antigen test — negative result (valueCodeableConcept) + value: + resourceType: "Bundle" + type: "searchset" + total: 1 + entry: + - fullUrl: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" + resource: + resourceType: "Observation" + id: "550e8400-e29b-41d4-a716-446655440001" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + code: + coding: + - system: "http://snomed.info/sct" + code: "31676001" + display: "HIV antigen test" + text: "HIV antigen test" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2025-11-04T15:45:00Z" + issued: "2025-11-04T16:00:00Z" + performer: + - reference: "Organization/SUPP001" + display: "Test Supplier Ltd" + valueCodeableConcept: + coding: + - system: "http://snomed.info/sct" + code: "260415000" + display: "Not detected" + interpretation: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" + code: "N" + display: "Normal" + text: "Normal" + psa_result: + summary: PSA test — quantitative result with reference range (valueQuantity) + value: + resourceType: "Bundle" + type: "searchset" + total: 1 + entry: + - fullUrl: "urn:uuid:550e8400-e29b-41d4-a716-446655440002" + resource: + resourceType: "Observation" + id: "550e8400-e29b-41d4-a716-446655440002" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + code: + coding: + - system: "http://snomed.info/sct" + code: "63476009" + display: "Measurement of prostate specific antigen" + text: "Measurement of prostate specific antigen" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2026-07-21T10:30:00Z" + issued: "2026-07-21T11:00:00Z" + performer: + - reference: "Organization/SUPP002" + display: "PSA Test Lab Ltd" + valueQuantity: + value: 4.2 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + interpretation: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" + code: "H" + display: "High" + text: "Above reference range" + referenceRange: + - low: + value: 0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + high: + value: 4.0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + type: + coding: + - system: "http://terminology.hl7.org/CodeSystem/referencerange-meaning" + code: "normal" + display: "Normal Range" + text: "Normal Range" + text: "Normal range for adult males (age-adjusted ranges may apply)" + '400': + description: Invalid request - FHIR OperationOutcome + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIROperationOutcome' + '404': + description: No results found for the specified order + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIROperationOutcome' + +components: + schemas: + FHIRServiceRequest: + type: object + description: FHIR ServiceRequest resource for test orders + required: + - resourceType + - status + - intent + - code + - subject + - contained + properties: + resourceType: + type: string + enum: [ServiceRequest] + example: "ServiceRequest" + id: + type: string + description: Logical id of this artifact + example: "550e8400-e29b-41d4-a716-446655440000" + meta: + type: object + description: >- + Metadata about this resource. SHOULD include the UK Core profile URL to declare + conformance, enabling profile validation by receiving systems. + properties: + profile: + type: array + description: UK Core profile URL(s) this resource conforms to + minItems: 1 + items: + type: string + example: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-ServiceRequest"] + status: + type: string + description: Status of the service request + enum: [draft, active, on-hold, revoked, completed, entered-in-error, unknown] + example: "active" + intent: + type: string + description: Intent of the service request + enum: [proposal, plan, directive, order, original-order, reflex-order, filler-order, instance-order, option] + example: "order" + code: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: What is being requested/ordered (SNOMED CT) + example: + coding: + - system: "http://snomed.info/sct" + code: "31676001" + display: "HIV antigen test" + text: "HIV antigen test" + contained: + type: array + description: Contained resources including Patient demographics + minItems: 1 + items: + type: object + required: + - resourceType + - id + - name + - address + properties: + resourceType: + type: string + enum: [Patient] + example: "Patient" + id: + type: string + description: Local identifier for contained resource + example: "patient-1" + meta: + type: object + properties: + profile: + type: array + description: UK Core profile URL(s) this resource conforms to + items: + type: string + example: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient"] + name: + type: array + description: Patient name(s) + items: + $ref: '#/components/schemas/FHIRHumanName' + gender: + type: string + description: Administrative gender of the patient + enum: [male, female, other, unknown] + example: "male" + telecom: + type: array + description: >- + Contact details for the patient. Required for test types where the supplier + is responsible for clinical management (e.g. HIV). Must include at least one + phone and one email entry. See test-type guidance in docs/test-types/. + minItems: 2 + items: + $ref: '#/components/schemas/FHIRContactPoint' + address: + type: array + description: Patient address(es) - FHIR compliant + items: + $ref: '#/components/schemas/FHIRAddress' + birthDate: + type: string + format: date + description: Patient's date of birth (FHIR standard field name) + example: "1985-03-15" + subject: + allOf: + - $ref: '#/components/schemas/FHIRReference' + - description: Reference to Patient - use contained resource reference + example: + reference: "#patient-1" + performer: + type: array + description: Requested performer (this supplier) + items: + $ref: '#/components/schemas/FHIRReference' + authoredOn: + type: string + format: date-time + description: Date and time the order was created on the HomeTest platform. Set by the server. + example: "2026-07-20T09:00:00Z" + extension: + type: array + description: >- + Extensions on this resource. Used to carry additional clinical context. + Supported extensions: + - Extension-UKCore-SpecimenCollectionMethod: conveys the sample collection method + (e.g. capillary blood, oral swab). Required for applicable test types — see + docs/test-types/ for per-test guidance. + items: + type: object + required: + - url + - valueCodeableConcept + properties: + url: + type: string + description: Extension URL identifier + example: "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-SpecimenCollectionMethod" + valueCodeableConcept: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: Coded value for the extension + example: + coding: + - system: "http://snomed.info/sct" + code: "122554006" + display: "Capillary blood specimen" + text: "capillary" + + FHIRObservation: + type: object + description: FHIR Observation resource for test results + required: + - resourceType + - status + - code + - subject + - basedOn + properties: + resourceType: + type: string + enum: [Observation] + example: "Observation" + id: + type: string + description: Logical id of this artifact + example: "550e8400-e29b-41d4-a716-446655440001" + meta: + type: object + description: >- + Metadata about this resource. SHOULD include the UK Core profile URL to declare + conformance, enabling profile validation by receiving systems. + properties: + profile: + type: array + description: UK Core profile URL(s) this resource conforms to + minItems: 1 + items: + type: string + example: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Observation"] + basedOn: + type: array + description: Reference to the ServiceRequest this observation fulfills + items: + $ref: '#/components/schemas/FHIRReference' + status: + type: string + description: Status of the observation result + enum: [registered, preliminary, final, amended, corrected, cancelled, entered-in-error, unknown] + example: "final" + category: + type: array + description: >- + Classification of the observation. SHOULD be present for UK Core conformance + (UKCore-ObservationCategory preferred binding). All home test results (HIV, PSA) + are laboratory tests — use code 'laboratory'. + System: http://terminology.hl7.org/CodeSystem/observation-category + minItems: 1 + items: + $ref: '#/components/schemas/FHIRCodeableConcept' + example: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: "Laboratory" + code: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: Type of observation (SNOMED CT or LOINC) + example: + coding: + - system: "http://snomed.info/sct" + code: "31676001" + display: "HIV antigen test" + text: "HIV antigen test" + subject: + allOf: + - $ref: '#/components/schemas/FHIRReference' + - description: Patient reference for matching with service request - PII removed for results + example: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: + type: string + format: date-time + description: When the observation was made + example: "2025-11-04T15:45:00Z" + issued: + type: string + format: date-time + description: Date/Time this version was made available + example: "2025-11-04T16:00:00Z" + performer: + type: array + description: Who is responsible for the observation + items: + $ref: '#/components/schemas/FHIRReference' + interpretation: + type: array + description: Clinical interpretation of the observation (e.g., Normal, Abnormal, Positive, Negative) + items: + $ref: '#/components/schemas/FHIRCodeableConcept' + valueCodeableConcept: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: >- + Actual result as a coded concept (for qualitative results, e.g. HIV detected/not detected). + Exactly one of valueCodeableConcept or valueQuantity must be present — + see docs/test-types/ for which applies to each test type. + example: + coding: + - system: "http://snomed.info/sct" + code: "260415000" + display: "Not detected" + valueQuantity: + allOf: + - $ref: '#/components/schemas/FHIRQuantity' + - description: >- + Actual result as a measured quantity (for quantitative results, e.g. PSA level in ng/mL). + Exactly one of valueQuantity or valueCodeableConcept must be present — + see docs/test-types/ for which applies to each test type. + example: + value: 4.2 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + referenceRange: + type: array + description: >- + Guide for interpreting the result value. Must be present when valueQuantity is used. + Age-adjusted ranges should be expressed as separate entries with the text or appliesTo + fields indicating the applicable population. + items: + type: object + properties: + low: + allOf: + - $ref: '#/components/schemas/FHIRQuantity' + - description: Lower bound of the reference range (inclusive) + example: + value: 0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + high: + allOf: + - $ref: '#/components/schemas/FHIRQuantity' + - description: Upper bound of the reference range (inclusive) + example: + value: 4.0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + type: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: Reference range qualifier (normal, therapeutic, etc.) + example: + coding: + - system: "http://terminology.hl7.org/CodeSystem/referencerange-meaning" + code: "normal" + display: "Normal Range" + text: "Normal Range" + appliesTo: + type: array + description: Population this range applies to (e.g. age group, sex) + items: + $ref: '#/components/schemas/FHIRCodeableConcept' + text: + type: string + description: Free text description of the reference range + example: "Normal range for adult males (age-adjusted ranges may apply)" + + FHIROperationOutcome: + type: object + description: FHIR OperationOutcome resource for reporting errors and warnings + required: + - resourceType + - issue + properties: + resourceType: + type: string + enum: [OperationOutcome] + example: "OperationOutcome" + id: + type: string + description: Logical id of this artifact + example: "operation-outcome-001" + issue: + type: array + description: Issues with the request or response + minItems: 1 + items: + type: object + required: + - severity + - code + properties: + severity: + type: string + description: Severity of the issue + enum: [fatal, error, warning, information] + example: "error" + code: + type: string + description: Error or warning code + enum: [invalid, structure, required, value, invariant, security, login, unknown, expired, forbidden, suppressed, processing, not-supported, duplicate, multiple-matches, not-found, deleted, too-long, code-invalid, extension, too-costly, business-rule, conflict, transient, lock-error, no-store, exception, timeout, incomplete, throttled, informational] + example: "invalid" + details: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: Additional details about the error + example: + text: "Invalid postcode format provided" + diagnostics: + type: string + description: Additional diagnostic information + example: "Postcode must be in UK format (e.g., SW1A 1AA)" + location: + type: array + description: Xpath or JSONPath of element with error + items: + type: string + example: ["postcode"] + expression: + type: array + description: FHIRPath of element with error + items: + type: string + example: ["postcode"] + FHIRReference: + type: object + description: A reference from one resource to another (FHIR Reference datatype) + required: + - reference + properties: + reference: + type: string + description: Literal reference, Relative, internal or absolute URL + example: "Organization/SUP001" + type: + type: string + description: Type the reference refers to (e.g., "Organization") + example: "Organization" + display: + type: string + description: Text alternative for the resource + example: "Supplier Organization Name" + FHIRCodeableConcept: + type: object + description: A CodeableConcept represents a value that is usually supplied by providing a reference to one or more terminologies or ontologies (FHIR CodeableConcept datatype) + properties: + coding: + type: array + description: Code defined by a terminology system + items: + $ref: '#/components/schemas/FHIRCoding' + text: + type: string + description: Plain text representation of the concept + example: "HIV antigen test" + FHIRCoding: + type: object + description: A Coding is a representation of a defined concept using a symbol from a defined code system (FHIR Coding datatype) + properties: + system: + type: string + description: Identity of the terminology system + example: "http://snomed.info/sct" + code: + type: string + description: Symbol in syntax defined by the system + example: "31676001" + display: + type: string + description: Representation defined by the system + example: "HIV antigen test" + FHIRQuantity: + type: object + description: A measured or measurable amount (FHIR Quantity datatype) + properties: + value: + type: number + description: Numerical value + example: 4.2 + comparator: + type: string + description: How to understand the value — used when the value is a bound (e.g. '<4.0') + enum: ["<", "<=", ">=", ">"] + example: "<" + unit: + type: string + description: Unit representation (human-readable) + example: "ng/mL" + system: + type: string + description: System that defines coded unit form — use UCUM (http://unitsofmeasure.org) + example: "http://unitsofmeasure.org" + code: + type: string + description: Coded form of the unit (UCUM code) + example: "ng/mL" + FHIRIdentifier: + type: object + description: An identifier intended for computation (FHIR Identifier datatype) + properties: + system: + type: string + description: The namespace for the identifier value + example: "https://fhir.hometest.nhs.uk/Id/order-uid" + value: + type: string + description: The value that is unique within the system + example: "550e8400-e29b-41d4-a716-446655440000" + use: + type: string + description: The purpose of this identifier + enum: [usual, official, temp, secondary, old] + example: "official" + FHIRHumanName: + type: object + description: A name of a human with text, parts and usage information (FHIR HumanName datatype) + required: + - family + properties: + use: + type: string + description: Purpose of this name + enum: [usual, official, temp, nickname, anonymous, old, maiden] + example: "official" + family: + type: string + description: Family/last name + example: "Smith" + given: + type: array + description: Given/first names + items: + type: string + example: ["John"] + text: + type: string + description: Full name as a single string + example: "John Smith" + FHIRContactPoint: + type: object + description: Details for all kinds of technology-mediated contact points (FHIR ContactPoint datatype) + required: + - value + properties: + system: + type: string + description: Type of contact point + enum: [phone, fax, email, pager, url, sms, other] + example: "phone" + value: + type: string + description: The actual contact point details + example: "+447700900123" + use: + type: string + description: Purpose of this contact point + enum: [home, work, temp, old, mobile] + example: "mobile" + FHIRAddress: + type: object + description: An address expressed using postal conventions (FHIR Address datatype) + required: + - line + - postalCode + properties: + use: + type: string + description: Purpose of this address + enum: [home, work, temp, old, billing] + example: "home" + type: + type: string + description: Distinguishes between physical and postal addresses + enum: [postal, physical, both] + example: "both" + line: + type: array + description: Street address lines + items: + type: string + example: ["123 Main Street", "Apartment 2B"] + city: + type: string + description: City name + example: "London" + postalCode: + type: string + description: Postal code + example: "SW1A 1AA" + country: + type: string + description: Country + example: "United Kingdom" + FHIRBundleSearchsetObservations: + type: object + description: FHIR Bundle of type 'searchset' containing Observation resources + required: + - resourceType + - type + - entry + properties: + resourceType: + type: string + enum: [Bundle] + example: "Bundle" + type: + type: string + enum: [searchset] + example: "searchset" + total: + type: integer + description: Total number of matching results + example: 1 + link: + type: array + description: Links related to this result set + items: + type: object + properties: + relation: + type: string + example: "self" + url: + type: string + format: uri + example: "/results?order_uid=550e8400-e29b-41d4-a716-446655440000" + entry: + type: array + description: Bundle entries containing Observation resources + items: + type: object + properties: + fullUrl: + type: string + format: uri + example: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" + resource: + $ref: '#/components/schemas/FHIRObservation' + responses: + BadRequest: + description: Bad request - invalid parameters + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIROperationOutcome' + example: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "invalid" + details: + text: "Validation Error" + diagnostics: "The request contains invalid data that cannot be processed" + + UnprocessableEntity: + description: Unprocessable entity - FHIR validation errors + content: + application/fhir+json: + schema: + $ref: '#/components/schemas/FHIROperationOutcome' + example: + resourceType: "OperationOutcome" + issue: + - severity: "error" + code: "structure" + details: + text: "FHIR Validation Error" + diagnostics: "The FHIR resource contains validation errors and cannot be processed" + expression: + - "ServiceRequest.subject.identifier.value" + + securitySchemes: + OAuth2ClientCredentials: + type: oauth2 + description: OAuth 2.0 Client Credentials flow for supplier authentication + flows: + clientCredentials: + tokenUrl: /oauth/token + scopes: + orders: Access to order management endpoints + results: Access to results endpoints + SupplierAuth: + type: http + scheme: bearer + bearerFormat: JWT + description: JWT token issued to verified suppliers via OAuth 2.0 + +security: + - OAuth2ClientCredentials: [orders, results] + - SupplierAuth: [] \ No newline at end of file From a0c3d5ffbb85c5966cd63a8a2032dea39ec3b745 Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Thu, 23 Jul 2026 16:53:07 +0100 Subject: [PATCH 02/22] Updating specs following merge --- schemas/changelog.md | 3 +- schemas/home-test-supplier-api.yaml | 130 +++++++++++++++++++++++++++- schemas/supplier-api-spec-v2.yaml | 78 ++++++++++++----- 3 files changed, 183 insertions(+), 28 deletions(-) diff --git a/schemas/changelog.md b/schemas/changelog.md index fd2c421..705b50e 100644 --- a/schemas/changelog.md +++ b/schemas/changelog.md @@ -26,10 +26,11 @@ All notable changes to the NHS Home Test Supplier Integration Framework API sche - [Version 1.1.6 - June 22, 2026 - Add order cancellation\*\*](#version-116---june-22-2026---add-order-cancellation) - [Version 1.1.7 - July 7, 2026 - Aligned API spec for APIM publication](#version-117---july-7-2026---aligned-api-spec-for-apim-publication) - [Version 1.1.8 - July 22, 2026 - Typed CodeableConcept schemas for category and businessStatus](#version-118---july-22-2026---typed-codeableconcept-schemas) + - [Version 2.0.0 - July 20, 2026 - Acute Consumer Order Support](#version-200---july-20-2026---acute-consumer-order-support) --- -**Version 2.0.0 - July 20, 2026 - Acute Consumer Order Support** +## Version 2.0.0 - July 20, 2026 - Acute Consumer Order Support Changes to supplier-api-spec-v2.yaml (new file, v1 remains unchanged): diff --git a/schemas/home-test-supplier-api.yaml b/schemas/home-test-supplier-api.yaml index 6671d56..e490331 100644 --- a/schemas/home-test-supplier-api.yaml +++ b/schemas/home-test-supplier-api.yaml @@ -156,6 +156,8 @@ paths: examples: FHIRBundleResultsExample: $ref: "#/components/examples/FHIRBundleResultsExample" + FHIRBundleResultsPSAExample: + $ref: "#/components/examples/FHIRBundleResultsPSAExample" responses: "201": description: Results posted successfully @@ -166,6 +168,8 @@ paths: examples: FHIRBundleResultsExample: $ref: "#/components/examples/FHIRBundleResultsExample" + FHIRBundleResultsPSAExample: + $ref: "#/components/examples/FHIRBundleResultsPSAExample" "400": $ref: "#/components/responses/BadRequest" "401": @@ -316,6 +320,19 @@ components: type: string description: Logical id of this artifact example: "36716694-601b-4ad2-99e3-befb78d846ef" + meta: + type: object + description: >- + Metadata about this resource. SHOULD include the UK Core profile URL to declare + conformance, enabling profile validation by receiving systems. + properties: + profile: + type: array + description: UK Core profile URL(s) this resource conforms to + minItems: 1 + items: + type: string + example: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-DiagnosticReport"] basedOn: type: array description: Reference to the ServiceRequest this DiagnosticReport fulfills @@ -386,13 +403,13 @@ components: required: - resourceType - id + - basedOn - status - category - code - subject - effectiveDateTime - issued - - valueCodeableConcept properties: resourceType: type: string @@ -542,6 +559,13 @@ components: type: string description: Free text description of the reference range example: "Normal range for adult males (age-adjusted ranges may apply)" + interpretation: + type: array + description: >- + Categorical assessment of the observation value (e.g. high, low, normal). + Use the v3 ObservationInterpretation code system for coded interpretations. + items: + $ref: '#/components/schemas/FHIRCodeableConcept' FHIRCommunication: type: object description: FHIR Communication resource for sending messages to patients @@ -970,6 +994,7 @@ components: - system: "http://terminology.hl7.org/CodeSystem/v2-0074" code: "LAB" display: "Laboratory" + text: "Laboratory" code: coding: - system: "http://snomed.info/sct" @@ -989,12 +1014,15 @@ components: resource: resourceType: "Observation" id: "550e8400-e29b-41d4-a716-446655440001" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" status: "final" category: - coding: - system: "http://terminology.hl7.org/CodeSystem/observation-category" code: "laboratory" display: "Laboratory" + text: "Laboratory" code: coding: - extension: @@ -1022,6 +1050,103 @@ components: status: "not-done" reasonReference: - reference: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" + FHIRBundleResultsPSAExample: + summary: Example FHIR Bundle for PSA test results submission (valueQuantity with reference range) + value: + resourceType: "Bundle" + id: "72831cc0-7ca8-4571-b0e3-99c6d06a0ace" + type: "collection" + entry: + - fullUrl: "urn:uuid:b2c3d4e5-f6a7-4901-bcde-f12345678901" + resource: + resourceType: "DiagnosticReport" + id: "b2c3d4e5-f6a7-4901-bcde-f12345678901" + basedOn: + - reference: "ServiceRequest/660f9511-f3ac-42e5-b827-557766551111" + status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v2-0074" + code: "LAB" + display: "Laboratory" + text: "Laboratory" + code: + coding: + - system: "http://snomed.info/sct" + code: "721981007" + display: "Diagnostic studies report" + text: "Diagnostic studies report" + subject: + reference: "Patient/234f5678-f9a0-43e4-b567-537725285111" + effectiveDateTime: "2026-07-21T10:30:00Z" + issued: "2026-07-21T11:00:00Z" + performer: + - reference: "Organization/SUPP002" + display: "PSA Test Lab Ltd" + result: + - reference: "urn:uuid:660f9511-f3ac-42e5-b827-557766551112" + - fullUrl: "urn:uuid:660f9511-f3ac-42e5-b827-557766551112" + resource: + resourceType: "Observation" + id: "660f9511-f3ac-42e5-b827-557766551112" + basedOn: + - reference: "ServiceRequest/660f9511-f3ac-42e5-b827-557766551111" + status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: "Laboratory" + code: + coding: + - system: "http://snomed.info/sct" + code: "63476009" + display: "Measurement of prostate specific antigen" + text: "Measurement of prostate specific antigen" + subject: + reference: "Patient/234f5678-f9a0-43e4-b567-537725285111" + effectiveDateTime: "2026-07-21T10:30:00Z" + issued: "2026-07-21T11:00:00Z" + performer: + - reference: "Organization/SUPP002" + display: "PSA Test Lab Ltd" + valueQuantity: + value: 4.2 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + interpretation: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" + code: "H" + display: "High" + text: "Above reference range" + referenceRange: + - low: + value: 0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + high: + value: 4.0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + type: + coding: + - system: "http://terminology.hl7.org/CodeSystem/referencerange-meaning" + code: "normal" + display: "Normal Range" + text: "Normal Range" + text: "Normal range for adult males (age-adjusted ranges may apply)" + - fullUrl: "urn:uuid:f58bd21c-69dd-4483-a678-668877662223" + resource: + resourceType: "Communication" + id: "f58bd21c-69dd-4483-a678-668877662223" + status: "on-hold" + reasonReference: + - reference: "urn:uuid:660f9511-f3ac-42e5-b827-557766551112" FHIRTaskExample: summary: Example FHIR Task for a 'dispatched' update, see examples folder for further examples. value: @@ -1040,9 +1165,6 @@ components: reference: "Patient/123e4567-e89b-12d3-a456-426614174000" authoredOn: "2025-11-04T10:30:00Z" lastModified: "2025-11-04T10:35:00Z" - requester: - reference: "Organization/ORG001" - display: "HomeTest" owner: reference: "Organization/SUP001" display: "Test Supplier Ltd" diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 31b70b1..4a2ea17 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -237,6 +237,12 @@ paths: basedOn: - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: "Laboratory" code: coding: - system: "http://snomed.info/sct" @@ -275,6 +281,12 @@ paths: basedOn: - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: "Laboratory" code: coding: - system: "http://snomed.info/sct" @@ -492,10 +504,14 @@ components: description: FHIR Observation resource for test results required: - resourceType + - id - status + - category - code - subject - basedOn + - effectiveDateTime + - issued properties: resourceType: type: string @@ -530,14 +546,10 @@ components: example: "final" category: type: array - description: >- - Classification of the observation. SHOULD be present for UK Core conformance - (UKCore-ObservationCategory preferred binding). All home test results (HIV, PSA) - are laboratory tests — use code 'laboratory'. - System: http://terminology.hl7.org/CodeSystem/observation-category + description: Category of the observation (should be laboratory/Laboratory for observation category) minItems: 1 items: - $ref: '#/components/schemas/FHIRCodeableConcept' + $ref: '#/components/schemas/FHIRObservationCoding' example: - coding: - system: "http://terminology.hl7.org/CodeSystem/observation-category" @@ -743,6 +755,20 @@ components: type: object description: A Coding is a representation of a defined concept using a symbol from a defined code system (FHIR Coding datatype) properties: + extension: + type: array + description: Optional extensions for the coding (e.g., to indicate which value set the code is from) + items: + type: object + properties: + url: + type: string + description: URL of the extension + example: "http://hl7.org/fhir/StructureDefinition/valueset-reference" + valueUri: + type: string + description: URI of the value set this code is from + example: "https://fhir.hl7.org.uk/ValueSet/UKCore-PathologyBoundedCodeListObservables" system: type: string description: Identity of the terminology system @@ -780,23 +806,6 @@ components: type: string description: Coded form of the unit (UCUM code) example: "ng/mL" - FHIRIdentifier: - type: object - description: An identifier intended for computation (FHIR Identifier datatype) - properties: - system: - type: string - description: The namespace for the identifier value - example: "https://fhir.hometest.nhs.uk/Id/order-uid" - value: - type: string - description: The value that is unique within the system - example: "550e8400-e29b-41d4-a716-446655440000" - use: - type: string - description: The purpose of this identifier - enum: [usual, official, temp, secondary, old] - example: "official" FHIRHumanName: type: object description: A name of a human with text, parts and usage information (FHIR HumanName datatype) @@ -877,6 +886,29 @@ components: type: string description: Country example: "United Kingdom" + FHIRObservationCoding: + type: object + description: A specific example of a CodeableConcept for Observation category. + required: + - text + properties: + coding: + type: array + description: Code defined by a terminology system + items: + $ref: '#/components/schemas/FHIRCoding' + text: + $ref: '#/components/schemas/FHIRObservationCodingText' + example: + coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: Laboratory + FHIRObservationCodingText: + type: string + description: Observation category status values. + enum: [Laboratory] FHIRBundleSearchsetObservations: type: object description: FHIR Bundle of type 'searchset' containing Observation resources From 3b6cc39b52b490f4b76c5c95d0eb31a01e0df93c Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Fri, 24 Jul 2026 10:06:26 +0100 Subject: [PATCH 03/22] Fix pre-commit errors --- schemas/supplier-api-spec-v2.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 4a2ea17..1e182e3 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -1,3 +1,4 @@ +--- openapi: 3.0.3 info: title: Supplier API From e07bc75a7d44bb2feb203b8cabd747fe139d08e5 Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Fri, 24 Jul 2026 10:27:03 +0100 Subject: [PATCH 04/22] Missing new line --- schemas/supplier-api-spec-v2.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 1e182e3..7f7865f 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -1006,4 +1006,5 @@ components: security: - OAuth2ClientCredentials: [orders, results] - - SupplierAuth: [] \ No newline at end of file + - SupplierAuth: [] + \ No newline at end of file From d492a86696f2539cec2c156f62d319e6adbdf7ad Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Fri, 24 Jul 2026 11:47:12 +0100 Subject: [PATCH 05/22] Updates following pre-commit checks --- docs/test-types/hiv.md | 1 + schemas/supplier-api-spec-v2.yaml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/test-types/hiv.md b/docs/test-types/hiv.md index e5e0f01..4bc0c1d 100644 --- a/docs/test-types/hiv.md +++ b/docs/test-types/hiv.md @@ -20,6 +20,7 @@ The following fields are **optional** in the base supplier spec but are **mandat Suppliers are responsible for all clinical management of HIV test orders. This includes contacting the patient to deliver results, providing appropriate clinical advice, and referring where required. Patient contact details are therefore essential. **Requirements:** + - `telecom` **must** be present with a minimum of two entries - One entry **must** have `system: "phone"` with a valid UK phone number - One entry **must** have `system: "email"` with a valid email address diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 7f7865f..14feaa9 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -1007,4 +1007,3 @@ components: security: - OAuth2ClientCredentials: [orders, results] - SupplierAuth: [] - \ No newline at end of file From a29abfca6ba78b6b5c69afb34ef417c0d6bbe0fc Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Fri, 24 Jul 2026 16:24:11 +0100 Subject: [PATCH 06/22] Changes following code review most importantly reestablishing the Result Bundle inc DiagnosticReport --- docs/test-types/hiv.md | 23 +- schemas/supplier-api-spec-v2.yaml | 459 +++++++++++++++++++++++------- 2 files changed, 366 insertions(+), 116 deletions(-) diff --git a/docs/test-types/hiv.md b/docs/test-types/hiv.md index 4bc0c1d..1e26212 100644 --- a/docs/test-types/hiv.md +++ b/docs/test-types/hiv.md @@ -21,9 +21,9 @@ Suppliers are responsible for all clinical management of HIV test orders. This i **Requirements:** -- `telecom` **must** be present with a minimum of two entries +- `telecom` **must** be present with a minimum of one entry - One entry **must** have `system: "phone"` with a valid UK phone number -- One entry **must** have `system: "email"` with a valid email address +- One entry **may** have `system: "email"` with a valid email address (optional) ```json "telecom": [ @@ -31,11 +31,6 @@ Suppliers are responsible for all clinical management of HIV test orders. This i "system": "phone", "value": "+447700900123", "use": "mobile" - }, - { - "system": "email", - "value": "patient@example.com", - "use": "home" } ] ``` @@ -62,29 +57,29 @@ If the extension is absent, assume capillary blood (current default). | Responsibility | Supplier | HomeTest platform | Requesting clinician | |---|---|---|---| +| Eligibility / quota checking | ✅ (via 409 response) | | | | Kit dispatch | ✅ | | | | Sample receipt and processing | ✅ | | | -| Result submission (`/results`) | ✅ | | | | Patient notification of result | ✅ | | | | Clinical advice and onward referral | ✅ | | | -| Eligibility / quota checking | ✅ (via 409 response) | | | +| Result submission (`/results`) | ✅ | | | --- ## Order flow -1. HomeTest platform sends `POST /order` with a `FHIRServiceRequest` containing patient demographics (including `telecom`) -2. Supplier validates eligibility and quota rules; return `409` with appropriate `OperationOutcome` if the order cannot proceed +1. HomeTest platform sends a pre-submission eligibility check with a `draft` `FHIRServiceRequest`; supplier validates eligibility and quota rules and returns `409` with an appropriate `OperationOutcome` if the order cannot proceed +2. HomeTest platform sends `POST /order` with an `active` `FHIRServiceRequest` containing patient demographics (including `telecom`); if a pre-submission check was performed and the order matches, the supplier must accept it — `400`/`409` responses are not expected at this stage unless no pre-submission check was performed or a technical error occurs 3. On acceptance, supplier dispatches the kit to the address in `contained[Patient].address` 4. Supplier posts status updates via the task endpoint as the order progresses -5. On result availability, supplier submits an `Observation` via `POST /results` -6. Supplier contacts the patient directly to deliver results and provide clinical support +5. Supplier contacts the patient directly to deliver results and provide clinical support +6. After notifying the patient, supplier submits an `Observation` via `POST /results` to make results available to HomeTest --- ## Result requirements -Results must be submitted as a `FHIRObservation` and must include: +Results must be submitted as a FHIR `Bundle` containing a `DiagnosticReport` and an `Observation`. The `Observation` must include: - `basedOn` referencing the originating `ServiceRequest` - `status: "final"` for confirmed results diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 14feaa9..2cf1984 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -222,114 +222,204 @@ paths: content: application/fhir+json: schema: - $ref: '#/components/schemas/FHIRBundleSearchsetObservations' + $ref: '#/components/schemas/FHIRBundleSearchsetResultsBundle' examples: hiv_negative: - summary: HIV antigen test — negative result (valueCodeableConcept) + summary: HIV antigen test — negative result value: resourceType: "Bundle" + id: "d95bc671-03c6-4f0c-9cd5-793ab0da08b3" type: "searchset" total: 1 + link: + - relation: "self" + url: "/results?order_uid=550e8400-e29b-41d4-a716-446655440000" entry: - - fullUrl: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" + - fullUrl: "urn:uuid:42631cc0-6ca7-4460-a9d2-88b5c95f9f89" resource: - resourceType: "Observation" - id: "550e8400-e29b-41d4-a716-446655440001" - basedOn: - - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" - status: "final" - category: - - coding: - - system: "http://terminology.hl7.org/CodeSystem/observation-category" - code: "laboratory" - display: "Laboratory" - text: "Laboratory" - code: - coding: - - system: "http://snomed.info/sct" - code: "31676001" - display: "HIV antigen test" - text: "HIV antigen test" - subject: - reference: "Patient/123e4567-e89b-12d3-a456-426614174000" - effectiveDateTime: "2025-11-04T15:45:00Z" - issued: "2025-11-04T16:00:00Z" - performer: - - reference: "Organization/SUPP001" - display: "Test Supplier Ltd" - valueCodeableConcept: - coding: - - system: "http://snomed.info/sct" - code: "260415000" - display: "Not detected" - interpretation: - - coding: - - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" - code: "N" - display: "Normal" - text: "Normal" + resourceType: "Bundle" + id: "42631cc0-6ca7-4460-a9d2-88b5c95f9f89" + type: "collection" + entry: + - fullUrl: "urn:uuid:36716694-601b-4ad2-99e3-befb78d846ef" + resource: + resourceType: "DiagnosticReport" + id: "36716694-601b-4ad2-99e3-befb78d846ef" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v2-0074" + code: "LAB" + display: "Laboratory" + text: "Laboratory" + code: + coding: + - system: "http://snomed.info/sct" + code: "721981007" + display: "Diagnostic studies report" + text: "Diagnostic studies report" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2025-11-04T15:45:00Z" + issued: "2025-11-04T16:00:00Z" + performer: + - reference: "Organization/SUPP001" + display: "Test Supplier Ltd" + result: + - reference: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" + - fullUrl: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" + resource: + resourceType: "Observation" + id: "550e8400-e29b-41d4-a716-446655440001" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: "Laboratory" + code: + coding: + - system: "http://snomed.info/sct" + code: "31676001" + display: "HIV antigen test" + text: "HIV antigen test" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2025-11-04T15:45:00Z" + issued: "2025-11-04T16:00:00Z" + performer: + - reference: "Organization/SUPP001" + display: "Test Supplier Ltd" + valueCodeableConcept: + coding: + - system: "http://snomed.info/sct" + code: "260415000" + display: "Not detected" + interpretation: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" + code: "N" + display: "Normal" + text: "Normal" + - fullUrl: "urn:uuid:f47ac10b-58cc-4372-a567-0e02b2c3d479" + resource: + resourceType: "Communication" + id: "f47ac10b-58cc-4372-a567-0e02b2c3d479" + status: "not-done" + reasonReference: + - reference: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" psa_result: - summary: PSA test — quantitative result with reference range (valueQuantity) + summary: PSA test — quantitative result with reference range value: resourceType: "Bundle" + id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" type: "searchset" total: 1 + link: + - relation: "self" + url: "/results?order_uid=550e8400-e29b-41d4-a716-446655440000" entry: - - fullUrl: "urn:uuid:550e8400-e29b-41d4-a716-446655440002" + - fullUrl: "urn:uuid:b2c3d4e5-f6a7-8901-bcde-f12345678901" resource: - resourceType: "Observation" - id: "550e8400-e29b-41d4-a716-446655440002" - basedOn: - - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" - status: "final" - category: - - coding: - - system: "http://terminology.hl7.org/CodeSystem/observation-category" - code: "laboratory" - display: "Laboratory" - text: "Laboratory" - code: - coding: - - system: "http://snomed.info/sct" - code: "63476009" - display: "Measurement of prostate specific antigen" - text: "Measurement of prostate specific antigen" - subject: - reference: "Patient/123e4567-e89b-12d3-a456-426614174000" - effectiveDateTime: "2026-07-21T10:30:00Z" - issued: "2026-07-21T11:00:00Z" - performer: - - reference: "Organization/SUPP002" - display: "PSA Test Lab Ltd" - valueQuantity: - value: 4.2 - unit: "ng/mL" - system: "http://unitsofmeasure.org" - code: "ng/mL" - interpretation: - - coding: - - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" - code: "H" - display: "High" - text: "Above reference range" - referenceRange: - - low: - value: 0 - unit: "ng/mL" - system: "http://unitsofmeasure.org" - code: "ng/mL" - high: - value: 4.0 - unit: "ng/mL" - system: "http://unitsofmeasure.org" - code: "ng/mL" - type: - coding: - - system: "http://terminology.hl7.org/CodeSystem/referencerange-meaning" - code: "normal" - display: "Normal Range" - text: "Normal Range" - text: "Normal range for adult males (age-adjusted ranges may apply)" + resourceType: "Bundle" + id: "b2c3d4e5-f6a7-8901-bcde-f12345678901" + type: "collection" + entry: + - fullUrl: "urn:uuid:c3d4e5f6-a7b8-9012-cdef-123456789012" + resource: + resourceType: "DiagnosticReport" + id: "c3d4e5f6-a7b8-9012-cdef-123456789012" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v2-0074" + code: "LAB" + display: "Laboratory" + text: "Laboratory" + code: + coding: + - system: "http://snomed.info/sct" + code: "721981007" + display: "Diagnostic studies report" + text: "Diagnostic studies report" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2026-07-21T10:30:00Z" + issued: "2026-07-21T11:00:00Z" + performer: + - reference: "Organization/SUPP002" + display: "PSA Test Lab Ltd" + result: + - reference: "urn:uuid:550e8400-e29b-41d4-a716-446655440002" + - fullUrl: "urn:uuid:550e8400-e29b-41d4-a716-446655440002" + resource: + resourceType: "Observation" + id: "550e8400-e29b-41d4-a716-446655440002" + basedOn: + - reference: "ServiceRequest/550e8400-e29b-41d4-a716-446655440000" + status: "final" + category: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/observation-category" + code: "laboratory" + display: "Laboratory" + text: "Laboratory" + code: + coding: + - system: "http://snomed.info/sct" + code: "63476009" + display: "Measurement of prostate specific antigen" + text: "Measurement of prostate specific antigen" + subject: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: "2026-07-21T10:30:00Z" + issued: "2026-07-21T11:00:00Z" + performer: + - reference: "Organization/SUPP002" + display: "PSA Test Lab Ltd" + valueQuantity: + value: 4.2 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + interpretation: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation" + code: "H" + display: "High" + text: "Above reference range" + referenceRange: + - low: + value: 0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + high: + value: 4.0 + unit: "ng/mL" + system: "http://unitsofmeasure.org" + code: "ng/mL" + type: + coding: + - system: "http://terminology.hl7.org/CodeSystem/referencerange-meaning" + code: "normal" + display: "Normal Range" + text: "Normal Range" + text: "Normal range for adult males (age-adjusted ranges may apply)" + - fullUrl: "urn:uuid:d4e5f6a7-b8c9-0123-defa-234567890123" + resource: + resourceType: "Communication" + id: "d4e5f6a7-b8c9-0123-defa-234567890123" + status: "completed" + reasonReference: + - reference: "urn:uuid:550e8400-e29b-41d4-a716-446655440002" '400': description: Invalid request - FHIR OperationOutcome content: @@ -441,8 +531,8 @@ components: description: >- Contact details for the patient. Required for test types where the supplier is responsible for clinical management (e.g. HIV). Must include at least one - phone and one email entry. See test-type guidance in docs/test-types/. - minItems: 2 + phone entry. Email is optional. See test-type guidance in docs/test-types/. + minItems: 1 items: $ref: '#/components/schemas/FHIRContactPoint' address: @@ -910,18 +1000,24 @@ components: type: string description: Observation category status values. enum: [Laboratory] - FHIRBundleSearchsetObservations: + FHIRBundleSearchsetResultsBundle: type: object - description: FHIR Bundle of type 'searchset' containing Observation resources + description: FHIR Bundle of type 'searchset' containing a single results Bundle per order required: - resourceType - type + - total + - link - entry properties: resourceType: type: string enum: [Bundle] example: "Bundle" + id: + type: string + description: Logical id of this artifact + example: "d95bc671-03c6-4f0c-9cd5-793ab0da08b3" type: type: string enum: [searchset] @@ -932,7 +1028,7 @@ components: example: 1 link: type: array - description: Links related to this result set + description: Must contain at least a 'self' link to the search that produced this searchset items: type: object properties: @@ -941,20 +1037,179 @@ components: example: "self" url: type: string - format: uri + format: uri-reference example: "/results?order_uid=550e8400-e29b-41d4-a716-446655440000" entry: type: array - description: Bundle entries containing Observation resources + description: Bundle entries, each containing a results Bundle (DiagnosticReport, Observation, and Communication) items: type: object properties: fullUrl: type: string format: uri - example: "urn:uuid:550e8400-e29b-41d4-a716-446655440001" + example: "urn:uuid:42631cc0-6ca7-4460-a9d2-88b5c95f9f89" + resource: + $ref: '#/components/schemas/FHIRBundleResults' + FHIRBundleResults: + type: object + description: FHIR Bundle of type 'collection' containing DiagnosticReport, Observation, and Communication resources for a single test result + required: + - resourceType + - id + - type + - entry + properties: + resourceType: + type: string + enum: [Bundle] + example: "Bundle" + id: + type: string + description: Logical id of this artifact + example: "42631cc0-6ca7-4460-a9d2-88b5c95f9f89" + type: + type: string + enum: [collection] + example: "collection" + entry: + type: array + description: Must contain exactly three entries — a DiagnosticReport, an Observation, and a Communication + minItems: 3 + maxItems: 3 + items: + type: object + required: + - fullUrl + - resource + properties: + fullUrl: + type: string + description: Absolute URL for the resource entry (urn:uuid followed by the resource id) + example: "urn:uuid:36716694-601b-4ad2-99e3-befb78d846ef" resource: - $ref: '#/components/schemas/FHIRObservation' + oneOf: + - $ref: '#/components/schemas/FHIRDiagnosticReport' + - $ref: '#/components/schemas/FHIRObservation' + - $ref: '#/components/schemas/FHIRCommunication' + discriminator: + propertyName: resourceType + mapping: + DiagnosticReport: '#/components/schemas/FHIRDiagnosticReport' + Observation: '#/components/schemas/FHIRObservation' + Communication: '#/components/schemas/FHIRCommunication' + FHIRDiagnosticReport: + type: object + description: FHIR DiagnosticReport resource grouping observations for a test result + required: + - resourceType + - id + - basedOn + - status + - category + - code + - subject + - issued + - performer + - result + properties: + resourceType: + type: string + enum: [DiagnosticReport] + example: "DiagnosticReport" + id: + type: string + description: Logical id of this artifact + example: "36716694-601b-4ad2-99e3-befb78d846ef" + basedOn: + type: array + description: Reference to the ServiceRequest this DiagnosticReport fulfills + items: + $ref: '#/components/schemas/FHIRReference' + status: + type: string + description: Status of the diagnostic report + enum: [final] + example: "final" + category: + type: array + description: Category of the report — use LAB/Laboratory for test results + minItems: 1 + items: + $ref: '#/components/schemas/FHIRCodeableConcept' + example: + - coding: + - system: "http://terminology.hl7.org/CodeSystem/v2-0074" + code: "LAB" + display: "Laboratory" + text: "Laboratory" + code: + allOf: + - $ref: '#/components/schemas/FHIRCodeableConcept' + - description: Always use SNOMED CT 721981007 — Diagnostic studies report + example: + coding: + - system: "http://snomed.info/sct" + code: "721981007" + display: "Diagnostic studies report" + text: "Diagnostic studies report" + subject: + allOf: + - $ref: '#/components/schemas/FHIRReference' + - description: Patient reference — PII removed for results + example: + reference: "Patient/123e4567-e89b-12d3-a456-426614174000" + effectiveDateTime: + type: string + format: date-time + description: When the test was performed + example: "2025-11-04T15:45:00Z" + issued: + type: string + format: date-time + description: When this report was made available + example: "2025-11-04T16:00:00Z" + performer: + type: array + description: Organisation responsible for the report + items: + $ref: '#/components/schemas/FHIRReference' + result: + type: array + description: References to the Observation resources that are part of this report + items: + $ref: '#/components/schemas/FHIRReference' + FHIRCommunication: + type: object + description: FHIR Communication resource recording whether clinical contact was made with the patient + required: + - resourceType + - id + - status + - reasonReference + properties: + resourceType: + type: string + enum: [Communication] + example: "Communication" + id: + type: string + description: Logical id of this artifact + example: "f47ac10b-58cc-4372-a567-0e02b2c3d479" + status: + type: string + description: >- + Status of the communication event. + Use 'completed' when clinical contact has been made. + Use 'on-hold' when clinical contact is required but not yet made. + Use 'not-done' when clinical contact is not required (e.g. non-reactive results). + enum: [completed, on-hold, not-done] + example: "not-done" + reasonReference: + type: array + description: Reference to the Observation this communication relates to + items: + $ref: '#/components/schemas/FHIRReference' responses: BadRequest: description: Bad request - invalid parameters From 3b2553c70f72da22456806899f0647aea221848c Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Thu, 30 Jul 2026 16:53:42 +0100 Subject: [PATCH 07/22] added new psa examples --- CONTRIBUTING.md | 2 +- README.md | 2 +- ...get_test_results_non_reactive.example.json | 0 ...rvation_non_definitive_result.example.json | 0 .../observation_non_reactive.example.json | 0 ...rvation_reactive_with_contact.example.json | 0 ...tion_reactive_without_contact.example.json | 0 ...perationoutcome_business_rule.example.json | 0 .../{ => hiv}/order_cancelled.example.json | 0 .../order_servicerequest.example.json | 0 ...y_ineligible_operationoutcome.example.json | 0 ...dereligibility_servicerequest.example.json | 0 .../task_update_dispatched.example.json | 0 .../task_update_received-at-lab.example.json | 0 .../task_update_test-processed.example.json | 0 .../psa/get_test_results_high.example.json | 200 ++++++++++++++++++ .../psa/get_test_results_normal.example.json | 200 ++++++++++++++++++ .../fhir/psa/observation_high.example.json | 180 ++++++++++++++++ .../fhir/psa/observation_normal.example.json | 180 ++++++++++++++++ .../psa/order_servicerequest.example.json | 79 +++++++ tests/fhir-examples/README.md | 2 +- 21 files changed, 842 insertions(+), 3 deletions(-) rename examples/fhir/{ => hiv}/get_test_results_non_reactive.example.json (100%) rename examples/fhir/{ => hiv}/observation_non_definitive_result.example.json (100%) rename examples/fhir/{ => hiv}/observation_non_reactive.example.json (100%) rename examples/fhir/{ => hiv}/observation_reactive_with_contact.example.json (100%) rename examples/fhir/{ => hiv}/observation_reactive_without_contact.example.json (100%) rename examples/fhir/{ => hiv}/operationoutcome_business_rule.example.json (100%) rename examples/fhir/{ => hiv}/order_cancelled.example.json (100%) rename examples/fhir/{ => hiv}/order_servicerequest.example.json (100%) rename examples/fhir/{ => hiv}/ordereligibility_ineligible_operationoutcome.example.json (100%) rename examples/fhir/{ => hiv}/ordereligibility_servicerequest.example.json (100%) rename examples/fhir/{ => hiv}/task_update_dispatched.example.json (100%) rename examples/fhir/{ => hiv}/task_update_received-at-lab.example.json (100%) rename examples/fhir/{ => hiv}/task_update_test-processed.example.json (100%) create mode 100644 examples/fhir/psa/get_test_results_high.example.json create mode 100644 examples/fhir/psa/get_test_results_normal.example.json create mode 100644 examples/fhir/psa/observation_high.example.json create mode 100644 examples/fhir/psa/observation_normal.example.json create mode 100644 examples/fhir/psa/order_servicerequest.example.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ace54b..57d74d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -234,7 +234,7 @@ To avoid repeating git config steps across repositories or having it declared gl ### FHIR example validation -If you are adding or modifying FHIR example resources in [`examples/fhir/`](./examples/fhir), run the FHIR validator locally before opening a PR to catch issues early. See [`tests/fhir-examples/README.md`](./tests/fhir-examples/README.md) for prerequisites, usage, and how to interpret the output. +If you are adding or modifying FHIR example resources in [`examples/fhir/`](./examples/fhir) (organised into [`hiv/`](./examples/fhir/hiv) and [`psa/`](./examples/fhir/psa) subdirectories), run the FHIR validator locally before opening a PR to catch issues early. See [`tests/fhir-examples/README.md`](./tests/fhir-examples/README.md) for prerequisites, usage, and how to interpret the output. Validation also runs automatically in CI and results are posted as a PR comment. diff --git a/README.md b/README.md index 3070e2e..6150938 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ cd hometest-supplier-integration-framework ### FHIR example validation -The FHIR example resources in [`examples/fhir/`](./examples/fhir) are validated against the FHIR specification using the [HL7 FHIR Validator CLI](https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator). +The FHIR example resources in [`examples/fhir/`](./examples/fhir) (organised into [`hiv/`](./examples/fhir/hiv) and [`psa/`](./examples/fhir/psa) subdirectories) are validated against the FHIR specification using the [HL7 FHIR Validator CLI](https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator). Validation runs automatically in CI on every push and pull request via [`.github/workflows/validate-fhir-examples.yaml`](./.github/workflows/validate-fhir-examples.yaml). Results are posted as a comment on pull requests. diff --git a/examples/fhir/get_test_results_non_reactive.example.json b/examples/fhir/hiv/get_test_results_non_reactive.example.json similarity index 100% rename from examples/fhir/get_test_results_non_reactive.example.json rename to examples/fhir/hiv/get_test_results_non_reactive.example.json diff --git a/examples/fhir/observation_non_definitive_result.example.json b/examples/fhir/hiv/observation_non_definitive_result.example.json similarity index 100% rename from examples/fhir/observation_non_definitive_result.example.json rename to examples/fhir/hiv/observation_non_definitive_result.example.json diff --git a/examples/fhir/observation_non_reactive.example.json b/examples/fhir/hiv/observation_non_reactive.example.json similarity index 100% rename from examples/fhir/observation_non_reactive.example.json rename to examples/fhir/hiv/observation_non_reactive.example.json diff --git a/examples/fhir/observation_reactive_with_contact.example.json b/examples/fhir/hiv/observation_reactive_with_contact.example.json similarity index 100% rename from examples/fhir/observation_reactive_with_contact.example.json rename to examples/fhir/hiv/observation_reactive_with_contact.example.json diff --git a/examples/fhir/observation_reactive_without_contact.example.json b/examples/fhir/hiv/observation_reactive_without_contact.example.json similarity index 100% rename from examples/fhir/observation_reactive_without_contact.example.json rename to examples/fhir/hiv/observation_reactive_without_contact.example.json diff --git a/examples/fhir/operationoutcome_business_rule.example.json b/examples/fhir/hiv/operationoutcome_business_rule.example.json similarity index 100% rename from examples/fhir/operationoutcome_business_rule.example.json rename to examples/fhir/hiv/operationoutcome_business_rule.example.json diff --git a/examples/fhir/order_cancelled.example.json b/examples/fhir/hiv/order_cancelled.example.json similarity index 100% rename from examples/fhir/order_cancelled.example.json rename to examples/fhir/hiv/order_cancelled.example.json diff --git a/examples/fhir/order_servicerequest.example.json b/examples/fhir/hiv/order_servicerequest.example.json similarity index 100% rename from examples/fhir/order_servicerequest.example.json rename to examples/fhir/hiv/order_servicerequest.example.json diff --git a/examples/fhir/ordereligibility_ineligible_operationoutcome.example.json b/examples/fhir/hiv/ordereligibility_ineligible_operationoutcome.example.json similarity index 100% rename from examples/fhir/ordereligibility_ineligible_operationoutcome.example.json rename to examples/fhir/hiv/ordereligibility_ineligible_operationoutcome.example.json diff --git a/examples/fhir/ordereligibility_servicerequest.example.json b/examples/fhir/hiv/ordereligibility_servicerequest.example.json similarity index 100% rename from examples/fhir/ordereligibility_servicerequest.example.json rename to examples/fhir/hiv/ordereligibility_servicerequest.example.json diff --git a/examples/fhir/task_update_dispatched.example.json b/examples/fhir/hiv/task_update_dispatched.example.json similarity index 100% rename from examples/fhir/task_update_dispatched.example.json rename to examples/fhir/hiv/task_update_dispatched.example.json diff --git a/examples/fhir/task_update_received-at-lab.example.json b/examples/fhir/hiv/task_update_received-at-lab.example.json similarity index 100% rename from examples/fhir/task_update_received-at-lab.example.json rename to examples/fhir/hiv/task_update_received-at-lab.example.json diff --git a/examples/fhir/task_update_test-processed.example.json b/examples/fhir/hiv/task_update_test-processed.example.json similarity index 100% rename from examples/fhir/task_update_test-processed.example.json rename to examples/fhir/hiv/task_update_test-processed.example.json diff --git a/examples/fhir/psa/get_test_results_high.example.json b/examples/fhir/psa/get_test_results_high.example.json new file mode 100644 index 0000000..d98d88b --- /dev/null +++ b/examples/fhir/psa/get_test_results_high.example.json @@ -0,0 +1,200 @@ +{ + "resourceType": "Bundle", + "id": "8f4e0c3b-2a5d-4f9e-b6c8-3d7a9f1e2b4c", + "type": "searchset", + "total": 1, + "link": [ + { + "relation": "self", + "url": "/results?order_uid=a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "entry": [ + { + "search": { + "mode": "match" + }, + "fullUrl": "urn:uuid:a2b3c4d5-e6f7-4a8b-9c0d-e1f2a3b4c5d6", + "resource": { + "resourceType": "Bundle", + "id": "a2b3c4d5-e6f7-4a8b-9c0d-e1f2a3b4c5d6", + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:b3c4d5e6-f7a8-4b9c-0d1e-f2a3b4c5d6e7", + "resource": { + "resourceType": "DiagnosticReport", + "id": "b3c4d5e6-f7a8-4b9c-0d1e-f2a3b4c5d6e7", + "text": { + "status": "generated", + "div": "
DiagnosticReport: PSA measurement — result above reference range
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB", + "display": "Laboratory" + } + ], + "text": "Laboratory" + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ], + "text": "Diagnostic studies report" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "result": [ + { + "reference": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8" + } + ] + } + }, + { + "fullUrl": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8", + "resource": { + "resourceType": "Observation", + "id": "c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8", + "text": { + "status": "generated", + "div": "
Observation: PSA measurement — 6.8 ng/mL (above reference range)
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ], + "text": "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": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "valueQuantity": { + "value": 6.8, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "H", + "display": "High" + } + ], + "text": "Above reference range" + } + ], + "referenceRange": [ + { + "low": { + "value": 0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "high": { + "value": 4.0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", + "code": "normal", + "display": "Normal Range" + } + ], + "text": "Normal Range" + }, + "text": "Normal range for adult males (age-adjusted ranges may apply)" + } + ] + } + }, + { + "fullUrl": "urn:uuid:d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", + "resource": { + "resourceType": "Communication", + "id": "d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", + "text": { + "status": "generated", + "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician for review and patient notification
" + }, + "status": "not-done", + "reasonReference": [ + { + "reference": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8" + } + ] + } + } + ] + } + } + ] +} diff --git a/examples/fhir/psa/get_test_results_normal.example.json b/examples/fhir/psa/get_test_results_normal.example.json new file mode 100644 index 0000000..264b1eb --- /dev/null +++ b/examples/fhir/psa/get_test_results_normal.example.json @@ -0,0 +1,200 @@ +{ + "resourceType": "Bundle", + "id": "7e3d9b2a-1f4c-4e8d-a5b7-2c6f8e0d1a3b", + "type": "searchset", + "total": 1, + "link": [ + { + "relation": "self", + "url": "/results?order_uid=a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "entry": [ + { + "search": { + "mode": "match" + }, + "fullUrl": "urn:uuid:c7b4e8f1-2d3a-4e5b-8f1c-3b5a7d9e2f4c", + "resource": { + "resourceType": "Bundle", + "id": "c7b4e8f1-2d3a-4e5b-8f1c-3b5a7d9e2f4c", + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:d8c5f9a2-3e4b-5f6c-9a2d-4c6b8e0f3a5d", + "resource": { + "resourceType": "DiagnosticReport", + "id": "d8c5f9a2-3e4b-5f6c-9a2d-4c6b8e0f3a5d", + "text": { + "status": "generated", + "div": "
DiagnosticReport: PSA measurement — result within normal range
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB", + "display": "Laboratory" + } + ], + "text": "Laboratory" + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ], + "text": "Diagnostic studies report" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "result": [ + { + "reference": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e" + } + ] + } + }, + { + "fullUrl": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e", + "resource": { + "resourceType": "Observation", + "id": "e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e", + "text": { + "status": "generated", + "div": "
Observation: PSA measurement — 2.1 ng/mL (normal)
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ], + "text": "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": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "valueQuantity": { + "value": 2.1, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "Normal" + } + ], + "text": "Within reference range" + } + ], + "referenceRange": [ + { + "low": { + "value": 0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "high": { + "value": 4.0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", + "code": "normal", + "display": "Normal Range" + } + ], + "text": "Normal Range" + }, + "text": "Normal range for adult males (age-adjusted ranges may apply)" + } + ] + } + }, + { + "fullUrl": "urn:uuid:f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", + "resource": { + "resourceType": "Communication", + "id": "f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", + "text": { + "status": "generated", + "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician
" + }, + "status": "not-done", + "reasonReference": [ + { + "reference": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e" + } + ] + } + } + ] + } + } + ] +} diff --git a/examples/fhir/psa/observation_high.example.json b/examples/fhir/psa/observation_high.example.json new file mode 100644 index 0000000..2558e99 --- /dev/null +++ b/examples/fhir/psa/observation_high.example.json @@ -0,0 +1,180 @@ +{ + "resourceType": "Bundle", + "id": "a2b3c4d5-e6f7-4a8b-9c0d-e1f2a3b4c5d6", + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:b3c4d5e6-f7a8-4b9c-0d1e-f2a3b4c5d6e7", + "resource": { + "resourceType": "DiagnosticReport", + "id": "b3c4d5e6-f7a8-4b9c-0d1e-f2a3b4c5d6e7", + "text": { + "status": "generated", + "div": "
DiagnosticReport: PSA measurement — result above reference range
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB", + "display": "Laboratory" + } + ], + "text": "Laboratory" + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ], + "text": "Diagnostic studies report" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "result": [ + { + "reference": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8" + } + ] + } + }, + { + "fullUrl": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8", + "resource": { + "resourceType": "Observation", + "id": "c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8", + "text": { + "status": "generated", + "div": "
Observation: PSA measurement — 6.8 ng/mL (above reference range)
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ], + "text": "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": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "valueQuantity": { + "value": 6.8, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "H", + "display": "High" + } + ], + "text": "Above reference range" + } + ], + "referenceRange": [ + { + "low": { + "value": 0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "high": { + "value": 4.0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", + "code": "normal", + "display": "Normal Range" + } + ], + "text": "Normal Range" + }, + "text": "Normal range for adult males (age-adjusted ranges may apply)" + } + ] + } + }, + { + "fullUrl": "urn:uuid:d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", + "resource": { + "resourceType": "Communication", + "id": "d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", + "text": { + "status": "generated", + "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician for review and patient notification
" + }, + "status": "not-done", + "reasonReference": [ + { + "reference": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8" + } + ] + } + } + ] +} diff --git a/examples/fhir/psa/observation_normal.example.json b/examples/fhir/psa/observation_normal.example.json new file mode 100644 index 0000000..af5c8f8 --- /dev/null +++ b/examples/fhir/psa/observation_normal.example.json @@ -0,0 +1,180 @@ +{ + "resourceType": "Bundle", + "id": "c7b4e8f1-2d3a-4e5b-8f1c-3b5a7d9e2f4c", + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:d8c5f9a2-3e4b-5f6c-9a2d-4c6b8e0f3a5d", + "resource": { + "resourceType": "DiagnosticReport", + "id": "d8c5f9a2-3e4b-5f6c-9a2d-4c6b8e0f3a5d", + "text": { + "status": "generated", + "div": "
DiagnosticReport: PSA measurement — result within normal range
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0074", + "code": "LAB", + "display": "Laboratory" + } + ], + "text": "Laboratory" + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "721981007", + "display": "Diagnostic studies report" + } + ], + "text": "Diagnostic studies report" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "result": [ + { + "reference": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e" + } + ] + } + }, + { + "fullUrl": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e", + "resource": { + "resourceType": "Observation", + "id": "e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e", + "text": { + "status": "generated", + "div": "
Observation: PSA measurement — 2.1 ng/mL (normal)
" + }, + "basedOn": [ + { + "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" + } + ], + "status": "final", + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/observation-category", + "code": "laboratory", + "display": "Laboratory" + } + ], + "text": "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": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "subject": { + "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" + }, + "effectiveDateTime": "2026-07-29T10:30:00Z", + "issued": "2026-07-29T11:00:00Z", + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "valueQuantity": { + "value": 2.1, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "interpretation": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", + "code": "N", + "display": "Normal" + } + ], + "text": "Within reference range" + } + ], + "referenceRange": [ + { + "low": { + "value": 0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "high": { + "value": 4.0, + "unit": "ng/mL", + "system": "http://unitsofmeasure.org", + "code": "ng/mL" + }, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", + "code": "normal", + "display": "Normal Range" + } + ], + "text": "Normal Range" + }, + "text": "Normal range for adult males (age-adjusted ranges may apply)" + } + ] + } + }, + { + "fullUrl": "urn:uuid:f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", + "resource": { + "resourceType": "Communication", + "id": "f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", + "text": { + "status": "generated", + "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician
" + }, + "status": "not-done", + "reasonReference": [ + { + "reference": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e" + } + ] + } + } + ] +} diff --git a/examples/fhir/psa/order_servicerequest.example.json b/examples/fhir/psa/order_servicerequest.example.json new file mode 100644 index 0000000..ecdf56f --- /dev/null +++ b/examples/fhir/psa/order_servicerequest.example.json @@ -0,0 +1,79 @@ +{ + "resourceType": "ServiceRequest", + "id": "a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94", + "text": { + "status": "generated", + "div": "
ServiceRequest: PSA measurement order (clinician-initiated)
" + }, + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-SpecimenCollectionMethod", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "122554006", + "display": "Capillary blood specimen" + } + ], + "text": "Capillary blood specimen" + } + } + ], + "status": "active", + "intent": "order", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "contained": [ + { + "resourceType": "Patient", + "id": "b5e9d3c2-8a1f-4b6e-ad3c-2f9e4g7b1e05", + "name": [ + { + "use": "official", + "family": "Doe", + "given": [ + "Hohn" + ], + "text": "John Doe" + } + ], + "gender": "male", + "address": [ + { + "use": "home", + "type": "both", + "line": [ + "123 Evergreen Terrace" + ], + "city": "Birmingham", + "postalCode": "B15 XXX", + "country": "United Kingdom" + } + ], + "birthDate": "1970-01-01" + } + ], + "subject": { + "reference": "#b5e9d3c2-8a1f-4b6e-ad3c-2f9e4g7b1e05" + }, + "requester": { + "reference": "Organization/GP-PRAC-001", + "display": "Birchwood Road GP Practice" + }, + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "authoredOn": "2026-07-28T09:15:00Z" +} diff --git a/tests/fhir-examples/README.md b/tests/fhir-examples/README.md index 03b2ce5..6934d43 100644 --- a/tests/fhir-examples/README.md +++ b/tests/fhir-examples/README.md @@ -1,7 +1,7 @@ # FHIR Example Validation This directory contains tooling to validate the FHIR example resources in -[`examples/fhir/`](../../examples/fhir) against the FHIR specification using +[`examples/fhir/`](../../examples/fhir) (organised into [`hiv/`](../../examples/fhir/hiv) and [`psa/`](../../examples/fhir/psa) subdirectories) against the FHIR specification using the [HL7 FHIR Validator CLI](https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator). Validation is also run automatically in CI on every push and pull request — see From 82916a2a4495d522779230e06f496579f492dcd1 Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Tue, 25 Aug 2026 14:14:10 +0100 Subject: [PATCH 08/22] Post review changes of supplier specs --- .../psa/get_test_results_high.example.json | 200 ------------------ .../psa/get_test_results_normal.example.json | 200 ------------------ .../fhir/psa/observation_high.example.json | 23 -- .../fhir/psa/observation_normal.example.json | 35 --- .../psa/order_servicerequest.example.json | 6 +- ...rder_servicerequest_with_note.example.json | 80 +++++++ schemas/changelog.md | 19 ++ schemas/home-test-supplier-api.yaml | 8 +- schemas/supplier-api-spec-v2.yaml | 89 ++++++++ 9 files changed, 193 insertions(+), 467 deletions(-) delete mode 100644 examples/fhir/psa/get_test_results_high.example.json delete mode 100644 examples/fhir/psa/get_test_results_normal.example.json create mode 100644 examples/fhir/psa/order_servicerequest_with_note.example.json diff --git a/examples/fhir/psa/get_test_results_high.example.json b/examples/fhir/psa/get_test_results_high.example.json deleted file mode 100644 index d98d88b..0000000 --- a/examples/fhir/psa/get_test_results_high.example.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "resourceType": "Bundle", - "id": "8f4e0c3b-2a5d-4f9e-b6c8-3d7a9f1e2b4c", - "type": "searchset", - "total": 1, - "link": [ - { - "relation": "self", - "url": "/results?order_uid=a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" - } - ], - "entry": [ - { - "search": { - "mode": "match" - }, - "fullUrl": "urn:uuid:a2b3c4d5-e6f7-4a8b-9c0d-e1f2a3b4c5d6", - "resource": { - "resourceType": "Bundle", - "id": "a2b3c4d5-e6f7-4a8b-9c0d-e1f2a3b4c5d6", - "type": "collection", - "entry": [ - { - "fullUrl": "urn:uuid:b3c4d5e6-f7a8-4b9c-0d1e-f2a3b4c5d6e7", - "resource": { - "resourceType": "DiagnosticReport", - "id": "b3c4d5e6-f7a8-4b9c-0d1e-f2a3b4c5d6e7", - "text": { - "status": "generated", - "div": "
DiagnosticReport: PSA measurement — result above reference range
" - }, - "basedOn": [ - { - "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0074", - "code": "LAB", - "display": "Laboratory" - } - ], - "text": "Laboratory" - } - ], - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "721981007", - "display": "Diagnostic studies report" - } - ], - "text": "Diagnostic studies report" - }, - "subject": { - "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" - }, - "effectiveDateTime": "2026-07-29T10:30:00Z", - "issued": "2026-07-29T11:00:00Z", - "performer": [ - { - "reference": "Organization/SUPP002", - "display": "PSA Test Lab Ltd" - } - ], - "result": [ - { - "reference": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8" - } - ] - } - }, - { - "fullUrl": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8", - "resource": { - "resourceType": "Observation", - "id": "c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8", - "text": { - "status": "generated", - "div": "
Observation: PSA measurement — 6.8 ng/mL (above reference range)
" - }, - "basedOn": [ - { - "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "laboratory", - "display": "Laboratory" - } - ], - "text": "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": "63476009", - "display": "Measurement of prostate specific antigen" - } - ], - "text": "Measurement of prostate specific antigen" - }, - "subject": { - "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" - }, - "effectiveDateTime": "2026-07-29T10:30:00Z", - "issued": "2026-07-29T11:00:00Z", - "performer": [ - { - "reference": "Organization/SUPP002", - "display": "PSA Test Lab Ltd" - } - ], - "valueQuantity": { - "value": 6.8, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, - "interpretation": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", - "code": "H", - "display": "High" - } - ], - "text": "Above reference range" - } - ], - "referenceRange": [ - { - "low": { - "value": 0, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, - "high": { - "value": 4.0, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", - "code": "normal", - "display": "Normal Range" - } - ], - "text": "Normal Range" - }, - "text": "Normal range for adult males (age-adjusted ranges may apply)" - } - ] - } - }, - { - "fullUrl": "urn:uuid:d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", - "resource": { - "resourceType": "Communication", - "id": "d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", - "text": { - "status": "generated", - "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician for review and patient notification
" - }, - "status": "not-done", - "reasonReference": [ - { - "reference": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8" - } - ] - } - } - ] - } - } - ] -} diff --git a/examples/fhir/psa/get_test_results_normal.example.json b/examples/fhir/psa/get_test_results_normal.example.json deleted file mode 100644 index 264b1eb..0000000 --- a/examples/fhir/psa/get_test_results_normal.example.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "resourceType": "Bundle", - "id": "7e3d9b2a-1f4c-4e8d-a5b7-2c6f8e0d1a3b", - "type": "searchset", - "total": 1, - "link": [ - { - "relation": "self", - "url": "/results?order_uid=a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" - } - ], - "entry": [ - { - "search": { - "mode": "match" - }, - "fullUrl": "urn:uuid:c7b4e8f1-2d3a-4e5b-8f1c-3b5a7d9e2f4c", - "resource": { - "resourceType": "Bundle", - "id": "c7b4e8f1-2d3a-4e5b-8f1c-3b5a7d9e2f4c", - "type": "collection", - "entry": [ - { - "fullUrl": "urn:uuid:d8c5f9a2-3e4b-5f6c-9a2d-4c6b8e0f3a5d", - "resource": { - "resourceType": "DiagnosticReport", - "id": "d8c5f9a2-3e4b-5f6c-9a2d-4c6b8e0f3a5d", - "text": { - "status": "generated", - "div": "
DiagnosticReport: PSA measurement — result within normal range
" - }, - "basedOn": [ - { - "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0074", - "code": "LAB", - "display": "Laboratory" - } - ], - "text": "Laboratory" - } - ], - "code": { - "coding": [ - { - "system": "http://snomed.info/sct", - "code": "721981007", - "display": "Diagnostic studies report" - } - ], - "text": "Diagnostic studies report" - }, - "subject": { - "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" - }, - "effectiveDateTime": "2026-07-29T10:30:00Z", - "issued": "2026-07-29T11:00:00Z", - "performer": [ - { - "reference": "Organization/SUPP002", - "display": "PSA Test Lab Ltd" - } - ], - "result": [ - { - "reference": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e" - } - ] - } - }, - { - "fullUrl": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e", - "resource": { - "resourceType": "Observation", - "id": "e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e", - "text": { - "status": "generated", - "div": "
Observation: PSA measurement — 2.1 ng/mL (normal)
" - }, - "basedOn": [ - { - "reference": "ServiceRequest/a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d94" - } - ], - "status": "final", - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/observation-category", - "code": "laboratory", - "display": "Laboratory" - } - ], - "text": "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": "63476009", - "display": "Measurement of prostate specific antigen" - } - ], - "text": "Measurement of prostate specific antigen" - }, - "subject": { - "reference": "Patient/123e4567-e89b-12d3-a456-426614174001" - }, - "effectiveDateTime": "2026-07-29T10:30:00Z", - "issued": "2026-07-29T11:00:00Z", - "performer": [ - { - "reference": "Organization/SUPP002", - "display": "PSA Test Lab Ltd" - } - ], - "valueQuantity": { - "value": 2.1, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, - "interpretation": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", - "code": "N", - "display": "Normal" - } - ], - "text": "Within reference range" - } - ], - "referenceRange": [ - { - "low": { - "value": 0, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, - "high": { - "value": 4.0, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/referencerange-meaning", - "code": "normal", - "display": "Normal Range" - } - ], - "text": "Normal Range" - }, - "text": "Normal range for adult males (age-adjusted ranges may apply)" - } - ] - } - }, - { - "fullUrl": "urn:uuid:f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", - "resource": { - "resourceType": "Communication", - "id": "f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", - "text": { - "status": "generated", - "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician
" - }, - "status": "not-done", - "reasonReference": [ - { - "reference": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e" - } - ] - } - } - ] - } - } - ] -} diff --git a/examples/fhir/psa/observation_high.example.json b/examples/fhir/psa/observation_high.example.json index 2558e99..2203322 100644 --- a/examples/fhir/psa/observation_high.example.json +++ b/examples/fhir/psa/observation_high.example.json @@ -132,12 +132,6 @@ ], "referenceRange": [ { - "low": { - "value": 0, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, "high": { "value": 4.0, "unit": "ng/mL", @@ -158,23 +152,6 @@ } ] } - }, - { - "fullUrl": "urn:uuid:d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", - "resource": { - "resourceType": "Communication", - "id": "d5e6f7a8-b9c0-4d1e-2f3a-b4c5d6e7f8a9", - "text": { - "status": "generated", - "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician for review and patient notification
" - }, - "status": "not-done", - "reasonReference": [ - { - "reference": "urn:uuid:c4d5e6f7-a8b9-4c0d-1e2f-a3b4c5d6e7f8" - } - ] - } } ] } diff --git a/examples/fhir/psa/observation_normal.example.json b/examples/fhir/psa/observation_normal.example.json index af5c8f8..6af1608 100644 --- a/examples/fhir/psa/observation_normal.example.json +++ b/examples/fhir/psa/observation_normal.example.json @@ -118,26 +118,8 @@ "system": "http://unitsofmeasure.org", "code": "ng/mL" }, - "interpretation": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation", - "code": "N", - "display": "Normal" - } - ], - "text": "Within reference range" - } - ], "referenceRange": [ { - "low": { - "value": 0, - "unit": "ng/mL", - "system": "http://unitsofmeasure.org", - "code": "ng/mL" - }, "high": { "value": 4.0, "unit": "ng/mL", @@ -158,23 +140,6 @@ } ] } - }, - { - "fullUrl": "urn:uuid:f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", - "resource": { - "resourceType": "Communication", - "id": "f0e7b1c4-5a6d-7b8e-1c4f-6e8d0a2b5c7f", - "text": { - "status": "generated", - "div": "
Communication: no supplier-initiated clinical contact — result routed to requesting clinician
" - }, - "status": "not-done", - "reasonReference": [ - { - "reference": "urn:uuid:e9d6a0b3-4f5c-6a7d-0b3e-5d7c9f1a4b6e" - } - ] - } } ] } diff --git a/examples/fhir/psa/order_servicerequest.example.json b/examples/fhir/psa/order_servicerequest.example.json index ecdf56f..c69f35a 100644 --- a/examples/fhir/psa/order_servicerequest.example.json +++ b/examples/fhir/psa/order_servicerequest.example.json @@ -41,7 +41,7 @@ "use": "official", "family": "Doe", "given": [ - "Hohn" + "John" ], "text": "John Doe" } @@ -65,10 +65,6 @@ "subject": { "reference": "#b5e9d3c2-8a1f-4b6e-ad3c-2f9e4g7b1e05" }, - "requester": { - "reference": "Organization/GP-PRAC-001", - "display": "Birchwood Road GP Practice" - }, "performer": [ { "reference": "Organization/SUPP002", diff --git a/examples/fhir/psa/order_servicerequest_with_note.example.json b/examples/fhir/psa/order_servicerequest_with_note.example.json new file mode 100644 index 0000000..31718cf --- /dev/null +++ b/examples/fhir/psa/order_servicerequest_with_note.example.json @@ -0,0 +1,80 @@ +{ + "resourceType": "ServiceRequest", + "id": "a3f8c2d1-7b4e-4a5f-9c2d-1e8b3f6a0d95", + "text": { + "status": "generated", + "div": "
ServiceRequest: PSA measurement order (clinician-initiated) with clinical note
" + }, + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-SpecimenCollectionMethod", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "122554006", + "display": "Capillary blood specimen" + } + ], + "text": "Capillary blood specimen" + } + } + ], + "status": "active", + "intent": "order", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "contained": [ + { + "resourceType": "Patient", + "id": "b5e9d3c2-8a1f-4b6e-ad3c-2f9e4g7b1e05", + "name": [ + { + "use": "official", + "family": "Doe", + "given": [ + "John" + ], + "text": "John Doe" + } + ], + "gender": "male", + "address": [ + { + "use": "home", + "type": "both", + "line": [ + "123 Evergreen Terrace" + ], + "city": "Birmingham", + "postalCode": "B15 XXX", + "country": "United Kingdom" + } + ], + "birthDate": "1970-01-01" + } + ], + "subject": { + "reference": "#b5e9d3c2-8a1f-4b6e-ad3c-2f9e4g7b1e05" + }, + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "authoredOn": "2026-08-21T09:00:00Z", + "note": [ + { + "text": "PSA test required for Active Surveillance" + } + ] +} diff --git a/schemas/changelog.md b/schemas/changelog.md index 705b50e..c4f2ea6 100644 --- a/schemas/changelog.md +++ b/schemas/changelog.md @@ -27,6 +27,25 @@ All notable changes to the NHS Home Test Supplier Integration Framework API sche - [Version 1.1.7 - July 7, 2026 - Aligned API spec for APIM publication](#version-117---july-7-2026---aligned-api-spec-for-apim-publication) - [Version 1.1.8 - July 22, 2026 - Typed CodeableConcept schemas for category and businessStatus](#version-118---july-22-2026---typed-codeableconcept-schemas) - [Version 2.0.0 - July 20, 2026 - Acute Consumer Order Support](#version-200---july-20-2026---acute-consumer-order-support) + - [Version 2.0.1 - August 21, 2026 - Add ServiceRequest.note for clinical context](#version-201---august-21-2026---add-servicerequestrequestnote-for-clinical-context) + +--- + +## Version 2.0.1 - August 21, 2026 - Add ServiceRequest.note for clinical context + +Changes to `supplier-api-spec-v2.yaml` and `examples/fhir/psa/`: + +1. Added `note` (optional array of `FHIRAnnotation`) to `FHIRServiceRequest` + - Allows clinicians to include free-text notes explaining why the test is being ordered (e.g. "PSA test required for Active Surveillance"). + - Uses the FHIR `Annotation` datatype: `text` is required; `authorString` and `time` are optional. + - **Impact:** Non-breaking additive field. + +2. Added `FHIRAnnotation` schema to `components/schemas` + - New reusable datatype schema supporting the `note` field above. + +3. Added PSA order example with clinical note + - New inline example `psa_order` added to `/order` request and `201` response. + - New example file: `examples/fhir/psa/order_servicerequest_with_note.example.json` --- diff --git a/schemas/home-test-supplier-api.yaml b/schemas/home-test-supplier-api.yaml index e490331..91ae71e 100644 --- a/schemas/home-test-supplier-api.yaml +++ b/schemas/home-test-supplier-api.yaml @@ -377,12 +377,12 @@ components: effectiveDateTime: type: string format: date-time - description: Date/Time the observation was made (when the test was performed) + description: Date/time the specimen was collected from the patient example: "2025-11-04T15:45:00Z" issued: type: string format: date-time - description: Date/Time this diagnostic report was made available + description: Date/time the result was finalised and made available example: "2025-11-04T16:00:00Z" performer: type: array @@ -477,12 +477,12 @@ components: effectiveDateTime: type: string format: date-time - description: Date/Time the observation was made (when the test was performed) + description: Date/time the specimen was collected from the patient example: "2025-11-04T15:45:00Z" issued: type: string format: date-time - description: Date/Time this Observation was made available + description: Date/time the result was finalised and made available example: "2025-11-04T16:00:00Z" performer: type: array diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 2cf1984..36dd416 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -114,6 +114,38 @@ paths: application/fhir+json: schema: $ref: '#/components/schemas/FHIRServiceRequest' + examples: + psa_order: + summary: PSA test order with clinical note + value: + resourceType: "ServiceRequest" + id: "660f9511-f3ac-42e5-b827-557766551111" + status: "active" + intent: "order" + code: + coding: + - system: "http://snomed.info/sct" + code: "63476009" + display: "Measurement of prostate specific antigen" + text: "Measurement of prostate specific antigen" + contained: + - resourceType: "Patient" + id: "patient-1" + name: + - use: "official" + family: "Smith" + given: ["John"] + gender: "male" + birthDate: "1965-03-15" + address: + - use: "home" + line: ["123 Main Street"] + postalCode: "SW1A 1AA" + subject: + reference: "#patient-1" + authoredOn: "2026-08-21T09:00:00Z" + note: + - text: "PSA test required for Active Surveillance" responses: '201': description: Order received and processed successfully @@ -121,6 +153,38 @@ paths: application/fhir+json: schema: $ref: '#/components/schemas/FHIRServiceRequest' + examples: + psa_order: + summary: PSA test order with clinical note + value: + resourceType: "ServiceRequest" + id: "660f9511-f3ac-42e5-b827-557766551111" + status: "active" + intent: "order" + code: + coding: + - system: "http://snomed.info/sct" + code: "63476009" + display: "Measurement of prostate specific antigen" + text: "Measurement of prostate specific antigen" + contained: + - resourceType: "Patient" + id: "patient-1" + name: + - use: "official" + family: "Smith" + given: ["John"] + gender: "male" + birthDate: "1965-03-15" + address: + - use: "home" + line: ["123 Main Street"] + postalCode: "SW1A 1AA" + subject: + reference: "#patient-1" + authoredOn: "2026-08-21T09:00:00Z" + note: + - text: "PSA test required for Active Surveillance" '400': $ref: '#/components/responses/BadRequest' '422': @@ -589,6 +653,11 @@ components: code: "122554006" display: "Capillary blood specimen" text: "capillary" + note: + type: array + description: Free-text clinical notes from the requesting clinician + items: + $ref: '#/components/schemas/FHIRAnnotation' FHIRObservation: type: object @@ -1000,6 +1069,26 @@ components: type: string description: Observation category status values. enum: [Laboratory] + FHIRAnnotation: + type: object + description: A text note with optional author and timestamp (FHIR Annotation datatype) + required: + - text + properties: + authorString: + type: string + # use authorReference instead when the author is a known FHIR resource + description: Name of the annotation author as a plain string + example: "Dr. Jane Smith" + time: + type: string + format: date-time + description: When the annotation was made + example: "2026-08-21T09:00:00Z" + text: + type: string + description: Free-text clinical note + example: "PSA test required for Active Surveillance" FHIRBundleSearchsetResultsBundle: type: object description: FHIR Bundle of type 'searchset' containing a single results Bundle per order From 596d7a76d8df03b8f23a32d13cfaf4e1fac35d9f Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Tue, 25 Aug 2026 14:56:21 +0100 Subject: [PATCH 09/22] Fix up changelog --- schemas/changelog.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/schemas/changelog.md b/schemas/changelog.md index e138968..62adead 100644 --- a/schemas/changelog.md +++ b/schemas/changelog.md @@ -8,7 +8,6 @@ All notable changes to the NHS Home Test Supplier Integration Framework API sche - [Changelog](#changelog) - [Table of Contents](#table-of-contents) - - [Version 2.0.0 - July 20, 2026 - Acute Consumer Order Support](#version-200---july-20-2026---acute-consumer-order-support) - [Version 1.0.1](#version-101) - [Version 1.0.2 - January 26, 2026 - Additional FHIR Compliance Updates](#version-102---january-26-2026---additional-fhir-compliance-updates) - [Version 1.0.3 - January 27, 2026 - FHIR R4 Validation and UUID Corrections](#version-103---january-27-2026---fhir-r4-validation-and-uuid-corrections) @@ -28,8 +27,8 @@ All notable changes to the NHS Home Test Supplier Integration Framework API sche - [Version 1.1.7 - July 7, 2026 - Aligned API spec for APIM publication](#version-117---july-7-2026---aligned-api-spec-for-apim-publication) - [Version 1.1.8 - July 22, 2026 - Typed CodeableConcept schemas](#version-118---july-22-2026---typed-codeableconcept-schemas) - [Version 2.0.1 - August 10, 2026 - Make patient email optional](#version-201----august-10-2026---make-patient-email-optional) - - [Version 3.0.0 - July 20, 2026 - Acute Consumer Order Support](#version-200---july-20-2026---acute-consumer-order-support) - - [Version 3.0.1 - August 21, 2026 - Add ServiceRequest.note for clinical context](#version-201---august-21-2026---add-servicerequestrequestnote-for-clinical-context) + - [Version 3.0.0 - July 20, 2026 - Acute Consumer Order Support](#version-300---july-20-2026---acute-consumer-order-support) + - [Version 3.0.1 - August 21, 2026 - Add ServiceRequest.note for clinical context](#version-301---august-21-2026---add-servicerequestrequestnote-for-clinical-context) --- From c7407ef47cd1bf9d8084c1b8eb1c4ad30e7781af Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Tue, 25 Aug 2026 15:15:55 +0100 Subject: [PATCH 10/22] Fix linting errors --- schemas/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/changelog.md b/schemas/changelog.md index 62adead..6dac983 100644 --- a/schemas/changelog.md +++ b/schemas/changelog.md @@ -28,7 +28,7 @@ All notable changes to the NHS Home Test Supplier Integration Framework API sche - [Version 1.1.8 - July 22, 2026 - Typed CodeableConcept schemas](#version-118---july-22-2026---typed-codeableconcept-schemas) - [Version 2.0.1 - August 10, 2026 - Make patient email optional](#version-201----august-10-2026---make-patient-email-optional) - [Version 3.0.0 - July 20, 2026 - Acute Consumer Order Support](#version-300---july-20-2026---acute-consumer-order-support) - - [Version 3.0.1 - August 21, 2026 - Add ServiceRequest.note for clinical context](#version-301---august-21-2026---add-servicerequestrequestnote-for-clinical-context) + - [Version 3.0.1 - August 21, 2026 - Add ServiceRequest.note for clinical context](#version-301---august-21-2026---add-servicerequestnote-for-clinical-context) --- From 054c84577ffce6307e64367d0430211ac0ed14aa Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Thu, 17 Sep 2026 16:43:49 +0100 Subject: [PATCH 11/22] Added new files based on rduh structure changes --- ...rvicerequest_with_note_bundle.example.json | 145 ++++++++++++++++++ .../psa/rduh-service-request-example.json | 143 +++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 examples/fhir/psa/order_servicerequest_with_note_bundle.example.json create mode 100644 examples/fhir/psa/rduh-service-request-example.json diff --git a/examples/fhir/psa/order_servicerequest_with_note_bundle.example.json b/examples/fhir/psa/order_servicerequest_with_note_bundle.example.json new file mode 100644 index 0000000..77b6b50 --- /dev/null +++ b/examples/fhir/psa/order_servicerequest_with_note_bundle.example.json @@ -0,0 +1,145 @@ +{ + "resourceType": "Bundle", + "id": "psa-order-bundle-with-note", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Bundle" + ] + }, + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e90", + "resource": { + "resourceType": "ServiceRequest", + "id": "psa-order-with-note", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-ServiceRequest" + ] + }, + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-SpecimenCollectionMethod", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "122554006", + "display": "Capillary blood specimen" + } + ], + "text": "Capillary blood specimen" + } + } + ], + "status": "active", + "intent": "order", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "subject": { + "reference": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e91", + "display": "John Doe" + }, + "authoredOn": "2026-09-15T09:00:00Z", + "requester": { + "reference": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e92", + "display": "Dr Sarah Jenkins" + }, + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "note": [ + { + "text": "PSA test required for Active Surveillance" + } + ] + } + }, + { + "fullUrl": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e91", + "resource": { + "resourceType": "Patient", + "id": "psa-patient-example", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient" + ] + }, + "active": true, + "identifier": [ + { + "system": "https://fhir.hl7.org.uk/Id/nhs-number", + "value": "9443211234" + } + ], + "name": [ + { + "use": "official", + "family": "Doe", + "given": [ + "John" + ], + "text": "John Doe" + } + ], + "gender": "male", + "birthDate": "1970-01-01", + "address": [ + { + "use": "home", + "type": "both", + "line": [ + "123 Evergreen Terrace" + ], + "city": "Birmingham", + "postalCode": "B15 XXX", + "country": "United Kingdom" + } + ] + } + }, + { + "fullUrl": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e92", + "resource": { + "resourceType": "Practitioner", + "id": "psa-practitioner-example", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Practitioner" + ] + }, + "identifier": [ + { + "system": "https://fhir.hl7.org.uk/Id/gmc-number", + "value": "7011234" + } + ], + "name": [ + { + "use": "official", + "family": "Jenkins", + "given": [ + "Sarah" + ], + "prefix": [ + "Dr" + ], + "text": "Dr Sarah Jenkins" + } + ] + } + } + ] +} diff --git a/examples/fhir/psa/rduh-service-request-example.json b/examples/fhir/psa/rduh-service-request-example.json new file mode 100644 index 0000000..b48e3b9 --- /dev/null +++ b/examples/fhir/psa/rduh-service-request-example.json @@ -0,0 +1,143 @@ +{ + "resourceType": "Bundle", + "id": "ukcore-pathology-order-bundle", + "meta": { + "profile": [ + "https://hl7.org.uk" + ] + }, + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:6f6634d0-997d-4171-be17-1f4153096001", + "resource": { + "resourceType": "ServiceRequest", + "id": "ukcore-pathology-order-fbc", + "meta": { + "profile": [ + "https://hl7.org.uk" + ] + }, + "status": "active", + "intent": "order", + "category": [ + { + "coding": [ + { + "system": "http://snomed.info", + "code": "108252007", + "display": "Laboratory procedure" + } + ] + } + ], + "priority": "routine", + "code": { + "coding": [ + { + "system": "http://snomed.info", + "code": "444158007", + "display": "Full blood count" + } + ], + "text": "Full Blood Count (FBC)" + }, + "subject": { + "reference": "urn:uuid:6f6634d0-997d-4171-be17-1f4153096002", + "display": "Richard Jones" + }, + "authoredOn": "2026-09-15T14:30:00Z", + "requester": { + "reference": "urn:uuid:6f6634d0-997d-4171-be17-1f4153096003", + "display": "Dr. Sarah Jenkins" + }, + "reasonCode": [ + { + "coding": [ + { + "system": "http://snomed.info", + "code": "84229001", + "display": "Fatigue" + } + ], + "text": "Lethargy and tiredness" + } + ] + } + }, + { + "fullUrl": "urn:uuid:6f6634d0-997d-4171-be17-1f4153096002", + "resource": { + "resourceType": "Patient", + "id": "ukcore-patient-example", + "meta": { + "profile": [ + "https://hl7.org.uk" + ] + }, + "active": true, + "identifier": [ + { + "extension": [ + { + "url": "https://hl7.org.uk", + "valueCodeableConcept": { + "coding": [ + { + "system": "https://hl7.org.uk", + "code": "01", + "display": "Number present and verified" + } + ] + } + } + ], + "system": "https://fhir.nhs.uk", + "value": "9443211234" + } + ], + "name": [ + { + "use": "official", + "family": "Jones", + "given": [ + "Richard" + ] + } + ], + "gender": "male", + "birthDate": "1974-04-12" + } + }, + { + "fullUrl": "urn:uuid:6f6634d0-997d-4171-be17-1f4153096003", + "resource": { + "resourceType": "Practitioner", + "id": "ukcore-practitioner-example", + "meta": { + "profile": [ + "https://hl7.org.uk" + ] + }, + "identifier": [ + { + "system": "https://fhir.nhs.uk", + "value": "7011234" + } + ], + "name": [ + { + "use": "official", + "family": "Jenkins", + "given": [ + "Sarah" + ], + "prefix": [ + "Dr" + ] + } + ] + } + } + ] +} \ No newline at end of file From 2b9c80ca780cf8f17bf2539221b3cf20e032dd05 Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Mon, 28 Sep 2026 16:26:19 +0100 Subject: [PATCH 12/22] Updated specs in line with RDUH investigation --- examples/fhir/psa/epic-order-hl7-2.txt | 5 + .../fhir/psa/epic-order-hl7-generated.txt | 7 + examples/fhir/psa/generated-order-request.txt | 4 + ...est_with_note_specimen_bundle.example.json | 179 ++++++++ .../rduh_order_servicerequest_example.json | 171 +++++++ ...h_order_servicerequest_ukcore_example.json | 231 ++++++++++ schemas/supplier-api-spec-v2.yaml | 421 +++++++++++++++++- 7 files changed, 1016 insertions(+), 2 deletions(-) create mode 100644 examples/fhir/psa/epic-order-hl7-2.txt create mode 100644 examples/fhir/psa/epic-order-hl7-generated.txt create mode 100644 examples/fhir/psa/generated-order-request.txt create mode 100644 examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json create mode 100644 examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json create mode 100644 examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json diff --git a/examples/fhir/psa/epic-order-hl7-2.txt b/examples/fhir/psa/epic-order-hl7-2.txt new file mode 100644 index 0000000..9045042 --- /dev/null +++ b/examples/fhir/psa/epic-order-hl7-2.txt @@ -0,0 +1,5 @@ +MSH|^~\&|Epic|RH8|HomeTest|X26|20260904153627||ORM^O01|4128|P|2.5.1|||AL|NE +PID|1||9443211234^^^https://fhir.hl7.org.uk/Id/nhs-number^||MOUSE^MINNIE^^^^^D||19871005|F|||113 BEAUMONT ROAD^^PLYMOUTH^^PL4 9EE^ENG^P^^DEVON|||||||||||||||||||N +ORC|NW|2812455^EPC|||||^||||||||||||||||||||||LAB +OBR|1|2812455^EPC||63476009^Measurement of prostate specific antigen^SCT|||||||||||122554006&Capillary blood specimen&SCT^^^20982000&Capillary&SCT|||||||||||||||| +SPM|1|^||122554006^Capillary blood specimen^SCT^^^^|||^^^^^^|20982000^Capillary^SCT^^^^|||||||||| \ No newline at end of file diff --git a/examples/fhir/psa/epic-order-hl7-generated.txt b/examples/fhir/psa/epic-order-hl7-generated.txt new file mode 100644 index 0000000..6828e1f --- /dev/null +++ b/examples/fhir/psa/epic-order-hl7-generated.txt @@ -0,0 +1,7 @@ +MSH|^~\&|EPIC|RAA1|HOMETEST|SUPP002|20260821090000||ORM^O01|EPC202608210900001234|P|2.3|||AL|NE|GB +PID|1||9876543210^^^NHS^NH~MRN00445566^^^EPIC^MR||DOE^JOHN^^^^^L||19700101|M|||123 EVERGREEN TERRACE^^BIRMINGHAM^^B15 XXX^GBR^H +PV1|1|O|||||G1234567^SMITH^JANE^^^^^^NPI^^^^ORDERING +ORC|NW|ORD-A3F8C2D1-7B4E^EPIC|||||||20260821090000|||G1234567^SMITH^JANE^^^^^^NPI +OBR|1|ORD-A3F8C2D1-7B4E^EPIC||63476009^Measurement of prostate specific antigen^SCT|||20260821090000||||||PSA test required for Active Surveillance|||G1234567^SMITH^JANE^^^^^^NPI|||||||||L +NTE|1|OBR|PSA test required for Active Surveillance +SPM|1|SPEC-A3F8C2D1^EPIC||122554006^Capillary blood specimen^SCT|||||||P \ No newline at end of file diff --git a/examples/fhir/psa/generated-order-request.txt b/examples/fhir/psa/generated-order-request.txt new file mode 100644 index 0000000..6086d5f --- /dev/null +++ b/examples/fhir/psa/generated-order-request.txt @@ -0,0 +1,4 @@ +MSH|^~\&#|HomeTest|X26|Epic|RH8|20260923161613+0000||ORM^O01|8880C7BF-8CD7-446F-ACF7-2A87C3F1BB8B|P|2.5.1|||AL|NE +PID|1||9443211234^^^HC^PI~944 321 1234^^^NHS^NH||MOUSE^MINNIE^^^^^B||19871005|F|||113 BEAUMONT ROAD^^PLYMOUTH^^PL4 9EE^ENG^H^^DEVON||||||||944 321 1234|||||||||||N||||RH8^^ISO +ORC|NW|2812455^|^|||||||||||||||||||XAD^^^^^^L||XAD^^^^^^B +OBR|1|2812455^|^|63476009^Measurement of prostate specific antigen^SCT^^^^|||||||||||122554006&Capillary blood specimen&SCT^^^20982000&Capillary&SCT||||||||||||||||^^^^^^ \ No newline at end of file diff --git a/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json b/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json new file mode 100644 index 0000000..f8c5e46 --- /dev/null +++ b/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json @@ -0,0 +1,179 @@ +{ + "resourceType": "Bundle", + "id": "psa-order-bundle-with-note-and-specimen", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Bundle" + ] + }, + "type": "collection", + "entry": [ + { + "fullUrl": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e90", + "resource": { + "resourceType": "ServiceRequest", + "id": "psa-order-with-note", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-ServiceRequest" + ] + }, + "status": "active", + "intent": "order", + "category": [ + { + "coding": [ + { + "system": "https://fhir.hometest.nhs.uk/CodeSystem/service-request-category", + "code": "home-test", + "display": "Home test" + } + ], + "text": "Home test" + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "63476009", + "display": "Measurement of prostate specific antigen" + } + ], + "text": "Measurement of prostate specific antigen" + }, + "subject": { + "reference": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e91", + "display": "John Doe" + }, + "specimen": [ + { + "reference": "urn:uuid:specimen-001" + } + ], + "authoredOn": "2026-09-15T09:00:00Z", + "requester": { + "reference": "urn:uuid:requesting-hospital-001", + "display": "Example NHS Foundation Trust" + }, + "performer": [ + { + "reference": "Organization/SUPP002", + "display": "PSA Test Lab Ltd" + } + ], + "note": [ + { + "text": "PSA test required for Active Surveillance" + } + ] + } + }, + { + "fullUrl": "urn:uuid:specimen-001", + "resource": { + "resourceType": "Specimen", + "id": "specimen-001", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Specimen" + ] + }, + "status": "available", + "subject": { + "reference": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e91", + "display": "John Doe" + }, + "request": [ + { + "reference": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e90" + } + ], + "type": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "122554006", + "display": "Capillary blood specimen" + } + ], + "text": "Capillary blood specimen" + }, + "collection": { + "method": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "122554006", + "display": "Capillary blood specimen" + } + ], + "text": "Capillary blood specimen" + } + } + } + }, + { + "fullUrl": "urn:uuid:7d2a4f1e-0a7a-4b67-9c2f-8f3c6a1d5e91", + "resource": { + "resourceType": "Patient", + "id": "psa-patient-example", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient" + ] + }, + "active": true, + "identifier": [ + { + "system": "https://fhir.hl7.org.uk/Id/nhs-number", + "value": "9443211234" + } + ], + "name": [ + { + "use": "official", + "family": "Doe", + "given": [ + "John" + ], + "text": "John Doe" + } + ], + "gender": "male", + "birthDate": "1970-01-01", + "address": [ + { + "use": "home", + "type": "both", + "line": [ + "123 Evergreen Terrace" + ], + "city": "Birmingham", + "postalCode": "B15 XXX", + "country": "United Kingdom" + } + ] + } + }, + { + "fullUrl": "urn:uuid:requesting-hospital-001", + "resource": { + "resourceType": "Organization", + "id": "requesting-hospital-001", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization" + ] + }, + "identifier": [ + { + "system": "https://fhir.hl7.org.uk/Id/ods-organization-code", + "value": "RGT" + } + ], + "name": "Example NHS Foundation Trust" + } + } + ] +} \ No newline at end of file diff --git a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json new file mode 100644 index 0000000..3b51cd8 --- /dev/null +++ b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json @@ -0,0 +1,171 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "request": { + "method": "POST", + "url": "Organization" + }, + "fullUrl": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "value": "RH8" + } + ] + } + }, + { + "request": { + "method": "POST", + "url": "Organization" + }, + "fullUrl": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "value": "https://fhir.hl7.org.uk/Id/nhs-number" + } + ] + } + }, + { + "request": { + "method": "POST", + "url": "Patient" + }, + "fullUrl": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53", + "resource": { + "resourceType": "Patient", + "address": [ + { + "city": "PLYMOUTH", + "country": "ENG", + "district": "DEVON", + "line": [ + "113 BEAUMONT ROAD" + ], + "postalCode": "PL4 9EE" + } + ], + "birthDate": "1987-10-05", + "gender": "female", + "identifier": [ + { + "assigner": { + "reference": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5" + }, + "system": "https://fhir.hl7.org.uk/Id/nhs-number", + "value": "9443211234" + } + ], + "managingOrganization": { + "reference": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012" + }, + "name": [ + { + "family": "MOUSE", + "given": [ + "MINNIE" + ], + "text": "MINNIE MOUSE", + "use": "official" + } + ] + } + }, + { + "request": { + "method": "POST", + "url": "Specimen" + }, + "fullUrl": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135", + "resource": { + "resourceType": "Specimen", + "collection": { + "bodySite": { + "coding": [ + { + "code": "20982000", + "display": "Capillary", + "system": "http://snomed.info/sct" + } + ] + } + }, + "subject": { + "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + }, + "type": { + "coding": [ + { + "code": "122554006", + "display": "Capillary blood specimen", + "system": "http://snomed.info/sct" + } + ] + } + } + }, + { + "request": { + "method": "POST", + "url": "ServiceRequest" + }, + "fullUrl": "urn:uuid:685c4a3a-5ac3-498d-9703-c81da7a5d9f4", + "resource": { + "resourceType": "ServiceRequest", + "category": [ + { + "coding": [ + { + "code": "103693007", + "display": "Diagnostic procedure", + "system": "http://snomed.info/sct" + } + ], + "text": "Diagnostic procedure" + } + ], + "code": { + "coding": [ + { + "code": "63476009", + "display": "Measurement of prostate specific antigen", + "system": "http://snomed.info/sct" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "PLAC", + "display": "Placer Identifier", + "system": "http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text": "PlacerId" + }, + "use": "official", + "value": "2812455" + } + ], + "intent": "order", + "specimen": [ + { + "reference": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135" + } + ], + "status": "unknown", + "subject": { + "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + } + } + } + ] +} \ No newline at end of file diff --git a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json new file mode 100644 index 0000000..e92af4b --- /dev/null +++ b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json @@ -0,0 +1,231 @@ +{ + "resourceType": "Bundle", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Bundle" + ] + }, + "type": "transaction", + "entry": [ + { + "request": { + "method": "POST", + "url": "Organization" + }, + "fullUrl": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012", + "resource": { + "resourceType": "Organization", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization" + ] + }, + "identifier": [ + { + "value": "RH8" + } + ] + } + }, + { + "request": { + "method": "POST", + "url": "Organization" + }, + "fullUrl": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5", + "resource": { + "resourceType": "Organization", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization" + ] + }, + "identifier": [ + { + "value": "https://fhir.hl7.org.uk/Id/nhs-number" + } + ] + } + }, + { + "request": { + "method": "POST", + "url": "Organization" + }, + "fullUrl": "urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193", + "resource": { + "resourceType": "Organization", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization" + ] + }, + "identifier": [ + { + "value": "RH8" + } + ] + } + }, + { + "request": { + "method": "POST", + "url": "Patient" + }, + "fullUrl": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53", + "resource": { + "resourceType": "Patient", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient" + ] + }, + "address": [ + { + "city": "PLYMOUTH", + "country": "ENG", + "district": "DEVON", + "line": [ + "113 BEAUMONT ROAD" + ], + "postalCode": "PL4 9EE" + } + ], + "birthDate": "1987-10-05", + "gender": "female", + "identifier": [ + { + "assigner": { + "reference": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5" + }, + "system": "https://fhir.hl7.org.uk/Id/nhs-number", + "value": "9443211234" + } + ], + "managingOrganization": { + "reference": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012" + }, + "name": [ + { + "family": "MOUSE", + "given": [ + "MINNIE" + ], + "text": "MINNIE MOUSE", + "use": "official" + } + ] + } + }, + { + "request": { + "method": "POST", + "url": "Specimen" + }, + "fullUrl": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135", + "resource": { + "resourceType": "Specimen", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Specimen" + ] + }, + "status": "available", + "type": { + "coding": [ + { + "code": "122554006", + "display": "Capillary blood specimen", + "system": "http://snomed.info/sct" + } + ] + } + } + }, + { + "request": { + "method": "POST", + "url": "ServiceRequest" + }, + "fullUrl": "urn:uuid:685c4a3a-5ac3-498d-9703-c81da7a5d9f4", + "resource": { + "resourceType": "ServiceRequest", + "meta": { + "profile": [ + "https://fhir.hl7.org.uk/StructureDefinition/UKCore-ServiceRequest" + ] + }, + "category": [ + { + "coding": [ + { + "code": "103693007", + "display": "Diagnostic procedure", + "system": "http://snomed.info/sct" + } + ], + "text": "Diagnostic procedure" + } + ], + "code": { + "coding": [ + { + "code": "63476009", + "display": "Measurement of prostate specific antigen", + "system": "http://snomed.info/sct" + } + ] + }, + "extension": [ + { + "url": "https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-SpecimenCollectionMethod", + "valueCodeableConcept": { + "coding": [ + { + "code": "122554006", + "display": "Capillary blood specimen", + "system": "http://snomed.info/sct" + } + ], + "text": "Capillary blood specimen" + } + } + ], + "identifier": [ + { + "type": { + "coding": [ + { + "code": "PLAC", + "display": "Placer Identifier", + "system": "http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text": "PlacerId" + }, + "use": "official", + "value": "2812455" + } + ], + "intent": "order", + "performer": [ + { + "reference": "urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193" + } + ], + "requester": { + "reference": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012" + }, + "specimen": [ + { + "reference": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135" + } + ], + "status": "active", + "subject": { + "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + } + } + } + ] +} diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 36dd416..4b6673c 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -96,7 +96,10 @@ paths: /order: post: summary: Receive Test Order - description: Receive a new test order from the home test platform + description: >- + Receive a new test order from the home test platform. Accepts the legacy standalone + ServiceRequest format and the UK Core transaction Bundle format. Existing suppliers + may continue sending standalone ServiceRequests; Bundle support is additive. tags: - Order Management parameters: @@ -113,7 +116,9 @@ paths: content: application/fhir+json: schema: - $ref: '#/components/schemas/FHIRServiceRequest' + oneOf: + - $ref: '#/components/schemas/FHIRServiceRequest' + - $ref: '#/components/schemas/FHIRTransactionBundle' examples: psa_order: summary: PSA test order with clinical note @@ -146,6 +151,139 @@ paths: authoredOn: "2026-08-21T09:00:00Z" note: - text: "PSA test required for Active Surveillance" + psa_ukcore_transaction_bundle: + summary: UK Core PSA transaction Bundle with patient, organizations and specimen + value: + resourceType: Bundle + meta: + profile: + - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Bundle + type: transaction + entry: + - fullUrl: urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012 + request: + method: POST + url: Organization + resource: + resourceType: Organization + meta: + profile: + - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization + identifier: + - value: RH8 + - fullUrl: urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5 + request: + method: POST + url: Organization + resource: + resourceType: Organization + meta: + profile: + - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization + identifier: + - value: https://fhir.hl7.org.uk/Id/nhs-number + - fullUrl: urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193 + request: + method: POST + url: Organization + resource: + resourceType: Organization + meta: + profile: + - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization + identifier: + - value: RH8 + - fullUrl: urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53 + request: + method: POST + url: Patient + resource: + resourceType: Patient + meta: + profile: + - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient + address: + - city: PLYMOUTH + country: ENG + district: DEVON + line: [113 BEAUMONT ROAD] + postalCode: PL4 9EE + birthDate: "1987-10-05" + gender: female + identifier: + - assigner: + reference: urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5 + system: https://fhir.hl7.org.uk/Id/nhs-number + value: "9443211234" + managingOrganization: + reference: urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012 + name: + - family: MOUSE + given: [MINNIE] + text: MINNIE MOUSE + use: official + - fullUrl: urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135 + request: + method: POST + url: Specimen + resource: + resourceType: Specimen + meta: + profile: + - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Specimen + status: available + type: + coding: + - code: "122554006" + display: Capillary blood specimen + system: http://snomed.info/sct + - fullUrl: urn:uuid:685c4a3a-5ac3-498d-9703-c81da7a5d9f4 + request: + method: POST + url: ServiceRequest + resource: + resourceType: ServiceRequest + meta: + profile: + - https://fhir.hl7.org.uk/StructureDefinition/UKCore-ServiceRequest + category: + - coding: + - code: "103693007" + display: Diagnostic procedure + system: http://snomed.info/sct + text: Diagnostic procedure + code: + coding: + - code: "63476009" + display: Measurement of prostate specific antigen + system: http://snomed.info/sct + extension: + - url: https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-SpecimenCollectionMethod + valueCodeableConcept: + coding: + - code: "122554006" + display: Capillary blood specimen + system: http://snomed.info/sct + text: Capillary blood specimen + identifier: + - type: + coding: + - code: PLAC + display: Placer Identifier + system: http://terminology.hl7.org/CodeSystem/v2-0203 + text: PlacerId + use: official + value: "2812455" + intent: order + performer: + - reference: urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193 + requester: + reference: urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012 + specimen: + - reference: urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135 + status: active + subject: + reference: urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53 responses: '201': description: Order received and processed successfully @@ -620,6 +758,10 @@ components: description: Requested performer (this supplier) items: $ref: '#/components/schemas/FHIRReference' + requester: + allOf: + - $ref: '#/components/schemas/FHIRReference' + - description: Organization or practitioner that initiated the request authoredOn: type: string format: date-time @@ -659,6 +801,281 @@ components: items: $ref: '#/components/schemas/FHIRAnnotation' + FHIRTransactionBundle: + type: object + description: >- + UK Core transaction Bundle order containing separately-addressed resources. This is + additive to the legacy standalone ServiceRequest format. References between Bundle + entries MUST resolve to an entry fullUrl; suppliers should accept urn:uuid references. + required: + - resourceType + - meta + - type + - entry + properties: + resourceType: + type: string + enum: [Bundle] + meta: + $ref: '#/components/schemas/UKCoreBundleMeta' + type: + type: string + enum: [transaction] + entry: + type: array + minItems: 1 + items: + oneOf: + - $ref: '#/components/schemas/FHIRTransactionOrganizationEntry' + - $ref: '#/components/schemas/FHIRTransactionPatientEntry' + - $ref: '#/components/schemas/FHIRTransactionSpecimenEntry' + - $ref: '#/components/schemas/FHIRTransactionServiceRequestEntry' + + UKCoreBundleMeta: + type: object + required: [profile] + properties: + profile: + type: array + minItems: 1 + items: + type: string + enum: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Bundle"] + + FHIRTransactionEntryRequest: + type: object + required: [method, url] + properties: + method: + type: string + enum: [POST] + url: + type: string + enum: [Organization, Patient, Specimen, ServiceRequest] + + FHIRTransactionOrganizationEntry: + type: object + required: [fullUrl, request, resource] + properties: + fullUrl: + type: string + format: uri + request: + allOf: + - $ref: '#/components/schemas/FHIRTransactionEntryRequest' + - properties: + url: + type: string + enum: [Organization] + resource: + type: object + required: [resourceType, meta, identifier] + properties: + resourceType: + type: string + enum: [Organization] + meta: + type: object + required: [profile] + properties: + profile: + type: array + minItems: 1 + items: + type: string + enum: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization"] + identifier: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/FHIRBundleIdentifier' + + FHIRTransactionPatientEntry: + type: object + required: [fullUrl, request, resource] + properties: + fullUrl: + type: string + format: uri + request: + allOf: + - $ref: '#/components/schemas/FHIRTransactionEntryRequest' + - properties: + url: + type: string + enum: [Patient] + resource: + type: object + required: [resourceType, meta, address, birthDate, gender, identifier, managingOrganization, name] + properties: + resourceType: + type: string + enum: [Patient] + meta: + type: object + required: [profile] + properties: + profile: + type: array + minItems: 1 + items: + type: string + enum: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Patient"] + address: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/FHIRAddress' + birthDate: + type: string + format: date + gender: + type: string + enum: [male, female, other, unknown] + identifier: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/FHIRBundleIdentifier' + managingOrganization: + $ref: '#/components/schemas/FHIRReference' + name: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/FHIRHumanName' + + FHIRTransactionSpecimenEntry: + type: object + required: [fullUrl, request, resource] + properties: + fullUrl: + type: string + format: uri + request: + allOf: + - $ref: '#/components/schemas/FHIRTransactionEntryRequest' + - properties: + url: + type: string + enum: [Specimen] + resource: + type: object + required: [resourceType, meta, status, type] + properties: + resourceType: + type: string + enum: [Specimen] + meta: + type: object + required: [profile] + properties: + profile: + type: array + minItems: 1 + items: + type: string + enum: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-Specimen"] + status: + type: string + enum: [available, unavailable, unsatisfactory, entered-in-error] + type: + $ref: '#/components/schemas/FHIRCodeableConcept' + + FHIRTransactionServiceRequestEntry: + type: object + required: [fullUrl, request, resource] + properties: + fullUrl: + type: string + format: uri + request: + allOf: + - $ref: '#/components/schemas/FHIRTransactionEntryRequest' + - properties: + url: + type: string + enum: [ServiceRequest] + resource: + type: object + required: [resourceType, meta, status, intent, category, code, subject, specimen] + properties: + resourceType: + type: string + enum: [ServiceRequest] + meta: + type: object + required: [profile] + properties: + profile: + type: array + minItems: 1 + items: + type: string + enum: ["https://fhir.hl7.org.uk/StructureDefinition/UKCore-ServiceRequest"] + status: + type: string + enum: [draft, active, on-hold, revoked, completed, entered-in-error, unknown] + intent: + type: string + enum: [proposal, plan, directive, order, original-order, reflex-order, filler-order, instance-order, option] + category: + type: array + items: + $ref: '#/components/schemas/FHIRCodeableConcept' + code: + $ref: '#/components/schemas/FHIRCodeableConcept' + extension: + type: array + description: >- + Include Extension-UKCore-SpecimenCollectionMethod when required by the ordered + test type. For PSA orders, use the SNOMED CT specimen collection method. + items: + $ref: '#/components/schemas/UKCoreSpecimenCollectionMethodExtension' + identifier: + type: array + items: + $ref: '#/components/schemas/FHIRBundleIdentifier' + performer: + type: array + items: + $ref: '#/components/schemas/FHIRReference' + requester: + $ref: '#/components/schemas/FHIRReference' + specimen: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/FHIRReference' + subject: + $ref: '#/components/schemas/FHIRReference' + + FHIRBundleIdentifier: + type: object + required: [value] + properties: + assigner: + $ref: '#/components/schemas/FHIRReference' + system: + type: string + type: + $ref: '#/components/schemas/FHIRCodeableConcept' + use: + type: string + enum: [usual, official, temp, secondary, old] + value: + type: string + + UKCoreSpecimenCollectionMethodExtension: + type: object + required: [url, valueCodeableConcept] + properties: + url: + type: string + enum: ["https://fhir.hl7.org.uk/StructureDefinition/Extension-UKCore-SpecimenCollectionMethod"] + valueCodeableConcept: + $ref: '#/components/schemas/FHIRCodeableConcept' + FHIRObservation: type: object description: FHIR Observation resource for test results From 978e95841bcdd1c14a9bf833ed8fc555e4cb8782 Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Thu, 1 Oct 2026 16:31:03 +0100 Subject: [PATCH 13/22] Post code review updates --- ...h_order_servicerequest_ukcore_example.json | 57 +------- schemas/supplier-api-spec-v2.yaml | 134 +++++------------- 2 files changed, 38 insertions(+), 153 deletions(-) diff --git a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json index e92af4b..75934ad 100644 --- a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json +++ b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json @@ -22,47 +22,7 @@ }, "identifier": [ { - "value": "RH8" - } - ] - } - }, - { - "request": { - "method": "POST", - "url": "Organization" - }, - "fullUrl": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5", - "resource": { - "resourceType": "Organization", - "meta": { - "profile": [ - "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization" - ] - }, - "identifier": [ - { - "value": "https://fhir.hl7.org.uk/Id/nhs-number" - } - ] - } - }, - { - "request": { - "method": "POST", - "url": "Organization" - }, - "fullUrl": "urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193", - "resource": { - "resourceType": "Organization", - "meta": { - "profile": [ - "https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization" - ] - }, - "identifier": [ - { - "value": "RH8" + "value": "REQUESTER_ODS_CODE" } ] } @@ -93,15 +53,6 @@ ], "birthDate": "1987-10-05", "gender": "female", - "identifier": [ - { - "assigner": { - "reference": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5" - }, - "system": "https://fhir.hl7.org.uk/Id/nhs-number", - "value": "9443211234" - } - ], "managingOrganization": { "reference": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012" }, @@ -131,6 +82,9 @@ ] }, "status": "available", + "subject": { + "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + }, "type": { "coding": [ { @@ -210,7 +164,8 @@ "intent": "order", "performer": [ { - "reference": "urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193" + "reference": "Organization/ODS_CODE", + "type": "Organization" } ], "requester": { diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index 4b6673c..db1ee03 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -120,39 +120,8 @@ paths: - $ref: '#/components/schemas/FHIRServiceRequest' - $ref: '#/components/schemas/FHIRTransactionBundle' examples: - psa_order: - summary: PSA test order with clinical note - value: - resourceType: "ServiceRequest" - id: "660f9511-f3ac-42e5-b827-557766551111" - status: "active" - intent: "order" - code: - coding: - - system: "http://snomed.info/sct" - code: "63476009" - display: "Measurement of prostate specific antigen" - text: "Measurement of prostate specific antigen" - contained: - - resourceType: "Patient" - id: "patient-1" - name: - - use: "official" - family: "Smith" - given: ["John"] - gender: "male" - birthDate: "1965-03-15" - address: - - use: "home" - line: ["123 Main Street"] - postalCode: "SW1A 1AA" - subject: - reference: "#patient-1" - authoredOn: "2026-08-21T09:00:00Z" - note: - - text: "PSA test required for Active Surveillance" psa_ukcore_transaction_bundle: - summary: UK Core PSA transaction Bundle with patient, organizations and specimen + summary: UK Core PSA transaction Bundle without an NHS number value: resourceType: Bundle meta: @@ -170,29 +139,7 @@ paths: profile: - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization identifier: - - value: RH8 - - fullUrl: urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5 - request: - method: POST - url: Organization - resource: - resourceType: Organization - meta: - profile: - - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization - identifier: - - value: https://fhir.hl7.org.uk/Id/nhs-number - - fullUrl: urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193 - request: - method: POST - url: Organization - resource: - resourceType: Organization - meta: - profile: - - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Organization - identifier: - - value: RH8 + - value: REQUESTER_ODS_CODE - fullUrl: urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53 request: method: POST @@ -210,11 +157,6 @@ paths: postalCode: PL4 9EE birthDate: "1987-10-05" gender: female - identifier: - - assigner: - reference: urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5 - system: https://fhir.hl7.org.uk/Id/nhs-number - value: "9443211234" managingOrganization: reference: urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012 name: @@ -232,6 +174,8 @@ paths: profile: - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Specimen status: available + subject: + reference: urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53 type: coding: - code: "122554006" @@ -276,7 +220,8 @@ paths: value: "2812455" intent: order performer: - - reference: urn:uuid:8d3e15c4-2b71-4c59-9a84-0f6e27b8d193 + - reference: Organization/ODS_CODE + type: Organization requester: reference: urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012 specimen: @@ -291,38 +236,6 @@ paths: application/fhir+json: schema: $ref: '#/components/schemas/FHIRServiceRequest' - examples: - psa_order: - summary: PSA test order with clinical note - value: - resourceType: "ServiceRequest" - id: "660f9511-f3ac-42e5-b827-557766551111" - status: "active" - intent: "order" - code: - coding: - - system: "http://snomed.info/sct" - code: "63476009" - display: "Measurement of prostate specific antigen" - text: "Measurement of prostate specific antigen" - contained: - - resourceType: "Patient" - id: "patient-1" - name: - - use: "official" - family: "Smith" - given: ["John"] - gender: "male" - birthDate: "1965-03-15" - address: - - use: "home" - line: ["123 Main Street"] - postalCode: "SW1A 1AA" - subject: - reference: "#patient-1" - authoredOn: "2026-08-21T09:00:00Z" - note: - - text: "PSA test required for Active Surveillance" '400': $ref: '#/components/responses/BadRequest' '422': @@ -804,9 +717,11 @@ components: FHIRTransactionBundle: type: object description: >- - UK Core transaction Bundle order containing separately-addressed resources. This is - additive to the legacy standalone ServiceRequest format. References between Bundle - entries MUST resolve to an entry fullUrl; suppliers should accept urn:uuid references. + UK Core transaction Bundle order containing Organization, Patient, Specimen, and + ServiceRequest entries in that order. This is additive to the legacy standalone + ServiceRequest format. An NHS number is not required or expected in this Bundle. + References between Bundle entries MUST resolve to an entry fullUrl; suppliers should + accept urn:uuid references. required: - resourceType - meta @@ -823,7 +738,9 @@ components: enum: [transaction] entry: type: array - minItems: 1 + minItems: 4 + maxItems: 4 + description: Organization, Patient, Specimen, and ServiceRequest transaction entries, in that order items: oneOf: - $ref: '#/components/schemas/FHIRTransactionOrganizationEntry' @@ -906,7 +823,7 @@ components: enum: [Patient] resource: type: object - required: [resourceType, meta, address, birthDate, gender, identifier, managingOrganization, name] + required: [resourceType, meta, address, birthDate, gender, managingOrganization, name] properties: resourceType: type: string @@ -934,7 +851,7 @@ components: enum: [male, female, other, unknown] identifier: type: array - minItems: 1 + description: Optional non-NHS identifier; do not include an NHS number in this order Bundle. items: $ref: '#/components/schemas/FHIRBundleIdentifier' managingOrganization: @@ -961,7 +878,7 @@ components: enum: [Specimen] resource: type: object - required: [resourceType, meta, status, type] + required: [resourceType, meta, status, subject, type] properties: resourceType: type: string @@ -979,6 +896,8 @@ components: status: type: string enum: [available, unavailable, unsatisfactory, entered-in-error] + subject: + $ref: '#/components/schemas/FHIRReference' type: $ref: '#/components/schemas/FHIRCodeableConcept' @@ -1038,8 +957,19 @@ components: $ref: '#/components/schemas/FHIRBundleIdentifier' performer: type: array + description: >- + Requested performing Organization, identified by its ODS code using the + relative reference form Organization/{ODS code}. items: - $ref: '#/components/schemas/FHIRReference' + allOf: + - $ref: '#/components/schemas/FHIRReference' + - properties: + reference: + type: string + example: Organization/ODS_CODE + type: + type: string + enum: [Organization] requester: $ref: '#/components/schemas/FHIRReference' specimen: @@ -1505,7 +1435,7 @@ components: text: type: string description: Free-text clinical note - example: "PSA test required for Active Surveillance" + example: "Clinical note text" FHIRBundleSearchsetResultsBundle: type: object description: FHIR Bundle of type 'searchset' containing a single results Bundle per order From eaabf52b1c7e3542f1e064f41e10fb6d50d54d29 Mon Sep 17 00:00:00 2001 From: lewisbirks Date: Thu, 1 Oct 2026 16:43:04 +0100 Subject: [PATCH 14/22] chore: linting Signed-off-by: lewisbirks --- examples/fhir/psa/epic-order-hl7-2.txt | 2 +- .../fhir/psa/epic-order-hl7-generated.txt | 2 +- examples/fhir/psa/generated-order-request.txt | 2 +- ...est_with_note_specimen_bundle.example.json | 2 +- .../rduh_order_servicerequest_example.json | 324 +++++++++--------- .../psa/rduh-service-request-example.json | 2 +- 6 files changed, 167 insertions(+), 167 deletions(-) diff --git a/examples/fhir/psa/epic-order-hl7-2.txt b/examples/fhir/psa/epic-order-hl7-2.txt index 9045042..9be95fa 100644 --- a/examples/fhir/psa/epic-order-hl7-2.txt +++ b/examples/fhir/psa/epic-order-hl7-2.txt @@ -2,4 +2,4 @@ MSH|^~\&|Epic|RH8|HomeTest|X26|20260904153627||ORM^O01|4128|P|2.5.1|||AL|NE PID|1||9443211234^^^https://fhir.hl7.org.uk/Id/nhs-number^||MOUSE^MINNIE^^^^^D||19871005|F|||113 BEAUMONT ROAD^^PLYMOUTH^^PL4 9EE^ENG^P^^DEVON|||||||||||||||||||N ORC|NW|2812455^EPC|||||^||||||||||||||||||||||LAB OBR|1|2812455^EPC||63476009^Measurement of prostate specific antigen^SCT|||||||||||122554006&Capillary blood specimen&SCT^^^20982000&Capillary&SCT|||||||||||||||| -SPM|1|^||122554006^Capillary blood specimen^SCT^^^^|||^^^^^^|20982000^Capillary^SCT^^^^|||||||||| \ No newline at end of file +SPM|1|^||122554006^Capillary blood specimen^SCT^^^^|||^^^^^^|20982000^Capillary^SCT^^^^|||||||||| diff --git a/examples/fhir/psa/epic-order-hl7-generated.txt b/examples/fhir/psa/epic-order-hl7-generated.txt index 6828e1f..e003ca1 100644 --- a/examples/fhir/psa/epic-order-hl7-generated.txt +++ b/examples/fhir/psa/epic-order-hl7-generated.txt @@ -4,4 +4,4 @@ PV1|1|O|||||G1234567^SMITH^JANE^^^^^^NPI^^^^ORDERING ORC|NW|ORD-A3F8C2D1-7B4E^EPIC|||||||20260821090000|||G1234567^SMITH^JANE^^^^^^NPI OBR|1|ORD-A3F8C2D1-7B4E^EPIC||63476009^Measurement of prostate specific antigen^SCT|||20260821090000||||||PSA test required for Active Surveillance|||G1234567^SMITH^JANE^^^^^^NPI|||||||||L NTE|1|OBR|PSA test required for Active Surveillance -SPM|1|SPEC-A3F8C2D1^EPIC||122554006^Capillary blood specimen^SCT|||||||P \ No newline at end of file +SPM|1|SPEC-A3F8C2D1^EPIC||122554006^Capillary blood specimen^SCT|||||||P diff --git a/examples/fhir/psa/generated-order-request.txt b/examples/fhir/psa/generated-order-request.txt index 6086d5f..aec684f 100644 --- a/examples/fhir/psa/generated-order-request.txt +++ b/examples/fhir/psa/generated-order-request.txt @@ -1,4 +1,4 @@ MSH|^~\&#|HomeTest|X26|Epic|RH8|20260923161613+0000||ORM^O01|8880C7BF-8CD7-446F-ACF7-2A87C3F1BB8B|P|2.5.1|||AL|NE PID|1||9443211234^^^HC^PI~944 321 1234^^^NHS^NH||MOUSE^MINNIE^^^^^B||19871005|F|||113 BEAUMONT ROAD^^PLYMOUTH^^PL4 9EE^ENG^H^^DEVON||||||||944 321 1234|||||||||||N||||RH8^^ISO ORC|NW|2812455^|^|||||||||||||||||||XAD^^^^^^L||XAD^^^^^^B -OBR|1|2812455^|^|63476009^Measurement of prostate specific antigen^SCT^^^^|||||||||||122554006&Capillary blood specimen&SCT^^^20982000&Capillary&SCT||||||||||||||||^^^^^^ \ No newline at end of file +OBR|1|2812455^|^|63476009^Measurement of prostate specific antigen^SCT^^^^|||||||||||122554006&Capillary blood specimen&SCT^^^20982000&Capillary&SCT||||||||||||||||^^^^^^ diff --git a/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json b/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json index f8c5e46..6071bfe 100644 --- a/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json +++ b/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json @@ -176,4 +176,4 @@ } } ] -} \ No newline at end of file +} diff --git a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json index 3b51cd8..2658395 100644 --- a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json +++ b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_example.json @@ -1,171 +1,171 @@ { - "resourceType": "Bundle", - "type": "transaction", - "entry": [ - { - "request": { - "method": "POST", - "url": "Organization" - }, - "fullUrl": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012", - "resource": { - "resourceType": "Organization", - "identifier": [ - { - "value": "RH8" - } - ] - } - }, - { - "request": { - "method": "POST", - "url": "Organization" - }, - "fullUrl": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5", - "resource": { - "resourceType": "Organization", - "identifier": [ - { - "value": "https://fhir.hl7.org.uk/Id/nhs-number" - } - ] - } - }, - { - "request": { - "method": "POST", - "url": "Patient" - }, - "fullUrl": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53", - "resource": { - "resourceType": "Patient", - "address": [ - { - "city": "PLYMOUTH", - "country": "ENG", - "district": "DEVON", - "line": [ - "113 BEAUMONT ROAD" - ], - "postalCode": "PL4 9EE" - } - ], - "birthDate": "1987-10-05", - "gender": "female", - "identifier": [ - { - "assigner": { - "reference": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5" + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "request": { + "method": "POST", + "url": "Organization" }, - "system": "https://fhir.hl7.org.uk/Id/nhs-number", - "value": "9443211234" - } - ], - "managingOrganization": { - "reference": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012" - }, - "name": [ - { - "family": "MOUSE", - "given": [ - "MINNIE" - ], - "text": "MINNIE MOUSE", - "use": "official" - } - ] - } - }, - { - "request": { - "method": "POST", - "url": "Specimen" - }, - "fullUrl": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135", - "resource": { - "resourceType": "Specimen", - "collection": { - "bodySite": { - "coding": [ - { - "code": "20982000", - "display": "Capillary", - "system": "http://snomed.info/sct" - } - ] - } - }, - "subject": { - "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + "fullUrl": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "value": "RH8" + } + ] + } }, - "type": { - "coding": [ - { - "code": "122554006", - "display": "Capillary blood specimen", - "system": "http://snomed.info/sct" + { + "request": { + "method": "POST", + "url": "Organization" + }, + "fullUrl": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "value": "https://fhir.hl7.org.uk/Id/nhs-number" + } + ] } - ] - } - } - }, - { - "request": { - "method": "POST", - "url": "ServiceRequest" - }, - "fullUrl": "urn:uuid:685c4a3a-5ac3-498d-9703-c81da7a5d9f4", - "resource": { - "resourceType": "ServiceRequest", - "category": [ - { - "coding": [ - { - "code": "103693007", - "display": "Diagnostic procedure", - "system": "http://snomed.info/sct" - } - ], - "text": "Diagnostic procedure" - } - ], - "code": { - "coding": [ - { - "code": "63476009", - "display": "Measurement of prostate specific antigen", - "system": "http://snomed.info/sct" + }, + { + "request": { + "method": "POST", + "url": "Patient" + }, + "fullUrl": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53", + "resource": { + "resourceType": "Patient", + "address": [ + { + "city": "PLYMOUTH", + "country": "ENG", + "district": "DEVON", + "line": [ + "113 BEAUMONT ROAD" + ], + "postalCode": "PL4 9EE" + } + ], + "birthDate": "1987-10-05", + "gender": "female", + "identifier": [ + { + "assigner": { + "reference": "urn:uuid:b9b174d9-aa4a-477a-acbd-f833eb6b70c5" + }, + "system": "https://fhir.hl7.org.uk/Id/nhs-number", + "value": "9443211234" + } + ], + "managingOrganization": { + "reference": "urn:uuid:528c4978-6d0d-45a5-9dda-40642b155012" + }, + "name": [ + { + "family": "MOUSE", + "given": [ + "MINNIE" + ], + "text": "MINNIE MOUSE", + "use": "official" + } + ] } - ] }, - "identifier": [ - { - "type": { - "coding": [ - { - "code": "PLAC", - "display": "Placer Identifier", - "system": "http://terminology.hl7.org/CodeSystem/v2-0203" + { + "request": { + "method": "POST", + "url": "Specimen" + }, + "fullUrl": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135", + "resource": { + "resourceType": "Specimen", + "collection": { + "bodySite": { + "coding": [ + { + "code": "20982000", + "display": "Capillary", + "system": "http://snomed.info/sct" + } + ] + } + }, + "subject": { + "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + }, + "type": { + "coding": [ + { + "code": "122554006", + "display": "Capillary blood specimen", + "system": "http://snomed.info/sct" + } + ] } - ], - "text": "PlacerId" + } + }, + { + "request": { + "method": "POST", + "url": "ServiceRequest" }, - "use": "official", - "value": "2812455" - } - ], - "intent": "order", - "specimen": [ - { - "reference": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135" - } - ], - "status": "unknown", - "subject": { - "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + "fullUrl": "urn:uuid:685c4a3a-5ac3-498d-9703-c81da7a5d9f4", + "resource": { + "resourceType": "ServiceRequest", + "category": [ + { + "coding": [ + { + "code": "103693007", + "display": "Diagnostic procedure", + "system": "http://snomed.info/sct" + } + ], + "text": "Diagnostic procedure" + } + ], + "code": { + "coding": [ + { + "code": "63476009", + "display": "Measurement of prostate specific antigen", + "system": "http://snomed.info/sct" + } + ] + }, + "identifier": [ + { + "type": { + "coding": [ + { + "code": "PLAC", + "display": "Placer Identifier", + "system": "http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text": "PlacerId" + }, + "use": "official", + "value": "2812455" + } + ], + "intent": "order", + "specimen": [ + { + "reference": "urn:uuid:9df086ae-598d-436d-b0c0-d87954d5d135" + } + ], + "status": "unknown", + "subject": { + "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" + } + } } - } - } - ] -} \ No newline at end of file + ] +} diff --git a/examples/fhir/psa/rduh-service-request-example.json b/examples/fhir/psa/rduh-service-request-example.json index b48e3b9..16c4941 100644 --- a/examples/fhir/psa/rduh-service-request-example.json +++ b/examples/fhir/psa/rduh-service-request-example.json @@ -140,4 +140,4 @@ } } ] -} \ No newline at end of file +} From 67b44cb4e692229540639ba6e91872bbfe97edf5 Mon Sep 17 00:00:00 2001 From: lewisbirks Date: Thu, 1 Oct 2026 17:47:48 +0100 Subject: [PATCH 15/22] chore: update validator to run against sub-dirs Signed-off-by: lewisbirks --- .../scripts/generate-validation-comment.js | 10 +- .github/workflows/report-fhir-validation.yaml | 15 +- .github/workflows/validate-fhir-examples.yaml | 9 +- tests/config/gitleaks.toml => .gitleaks.toml | 2 +- .mise.toml | 20 +- .pre-commit-config.yaml | 2 +- mise.lock | 401 ++++++++++++++++++ 7 files changed, 441 insertions(+), 18 deletions(-) rename tests/config/gitleaks.toml => .gitleaks.toml (91%) create mode 100644 mise.lock diff --git a/.github/scripts/generate-validation-comment.js b/.github/scripts/generate-validation-comment.js index 55eba88..7b50197 100644 --- a/.github/scripts/generate-validation-comment.js +++ b/.github/scripts/generate-validation-comment.js @@ -2,6 +2,7 @@ // Environment variables: // - RESULTS_PATH : path to the results.json file (optional, defaults to '.local/results/results.json') // - VALIDATION_OUTCOME : result of the validation job ('success' | 'failure' | 'cancelled'), optional +// - VALIDATION_TYPE : type of FHIR examples being reported (optional) // - RUN_URL : URL to the workflow run, for linking to artifacts // - OUTPUT_PATH : path for the generated comment body (optional, defaults to 'comment-body.md') // - GITHUB_SERVER_URL : GitHub server URL (set automatically by Actions) @@ -14,8 +15,11 @@ const path = require("node:path"); const resultsPath = process.env.RESULTS_PATH ?? ".local/results/results.json"; const validationOutcome = process.env.VALIDATION_OUTCOME; +const validationType = process.env.VALIDATION_TYPE; const runUrl = process.env.RUN_URL; const outputPath = process.env.OUTPUT_PATH ?? "comment-body.md"; +const heading = `## 🔬 FHIR Validation Results${validationType ? ` - ${validationType.toUpperCase()}` : ""}`; +const commentMarker = validationType ? `` : ""; const serverUrl = process.env.GITHUB_SERVER_URL; const repository = process.env.GITHUB_REPOSITORY; @@ -47,7 +51,7 @@ let body; if (!fs.existsSync(resultsPath)) { body = - `## 🔬 FHIR Validation Results\n\n` + + `${heading}\n\n` + `❌ Validation did not produce a results file. ` + `Check the [workflow run](${runUrl}) for details.`; } else { @@ -115,7 +119,7 @@ if (!fs.existsSync(resultsPath)) { } body = - `## 🔬 FHIR Validation Results\n\n` + + `${heading}\n\n` + `${overall} — ${summary}\n` + details + `\n> Full HTML report available in the [workflow run artifacts](${runUrl}).`; @@ -126,5 +130,5 @@ const warning = ? `> [!WARNING]\n> The FHIR validator exited with errors. This should be reviewed before merging.\n\n` : ""; -fs.writeFileSync(outputPath, `\n${warning}${body}`); +fs.writeFileSync(outputPath, `${commentMarker}\n${warning}${body}`); console.log(`Comment body written to ${outputPath}`); diff --git a/.github/workflows/report-fhir-validation.yaml b/.github/workflows/report-fhir-validation.yaml index 27b5b41..9fcbb33 100644 --- a/.github/workflows/report-fhir-validation.yaml +++ b/.github/workflows/report-fhir-validation.yaml @@ -12,6 +12,9 @@ jobs: name: Generate validation comment runs-on: ubuntu-latest if: github.event.workflow_run.event == 'pull_request' + strategy: + matrix: + example_type: ["hiv", "psa"] permissions: contents: read outputs: @@ -27,7 +30,7 @@ jobs: uses: actions/download-artifact@v8.0.1 continue-on-error: true with: - name: fhir-validation-results + name: fhir-validation-results-${{ matrix.example_type }} path: .local/results/ github-token: ${{ github.token }} run-id: ${{ github.event.workflow_run.id }} @@ -45,6 +48,7 @@ jobs: env: RESULTS_PATH: .local/results/results.json VALIDATION_OUTCOME: ${{ github.event.workflow_run.conclusion }} + VALIDATION_TYPE: ${{ matrix.example_type }} RUN_URL: ${{ github.event.workflow_run.html_url }} OUTPUT_PATH: .local/comment-body.md run: node .github/scripts/generate-validation-comment.js @@ -52,13 +56,16 @@ jobs: - name: Upload comment body uses: actions/upload-artifact@v7.0.1 with: - name: fhir-validation-comment + name: fhir-validation-comment-${{ matrix.example_type }} path: .local/comment-body.md report: name: Post validation report runs-on: ubuntu-latest needs: generate + strategy: + matrix: + example_type: ["hiv", "psa"] permissions: pull-requests: write issues: write @@ -67,7 +74,7 @@ jobs: - name: Download comment body uses: actions/download-artifact@v8.0.1 with: - name: fhir-validation-comment + name: fhir-validation-comment-${{ matrix.example_type }} path: .local/ github-token: ${{ github.token }} @@ -80,7 +87,7 @@ jobs: with: issue-number: ${{ needs.generate.outputs.pr-number }} comment-author: github-actions[bot] - body-includes: '' + body-includes: "" - name: Post or update PR comment uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 diff --git a/.github/workflows/validate-fhir-examples.yaml b/.github/workflows/validate-fhir-examples.yaml index 247dc90..874013c 100644 --- a/.github/workflows/validate-fhir-examples.yaml +++ b/.github/workflows/validate-fhir-examples.yaml @@ -30,6 +30,9 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + strategy: + matrix: + example_type: ["hiv", "psa"] steps: - name: Checkout repository uses: actions/checkout@v7 @@ -82,7 +85,7 @@ jobs: restore-keys: | fhir-validator-cache-${{ steps.vars.outputs.fhir_version }}- fhir-validator-cache- - - name: Run FHIR validation (FHIR R${{ steps.vars.outputs.fhir_version }}) + - name: Run FHIR validation on ${{ matrix.example_type }} (FHIR R${{ steps.vars.outputs.fhir_version }}) id: validate continue-on-error: true env: @@ -90,13 +93,13 @@ jobs: run: | tests/fhir-examples/fhir-validator.sh \ -j "$JAR" \ - -i examples/fhir \ + -i examples/fhir/${{ matrix.example_type }} \ -o .local/results \ -v "$FHIR_VERSION" \ -c .local/cache - name: Upload validation results uses: actions/upload-artifact@v7.0.1 with: - name: fhir-validation-results + name: fhir-validation-results-${{ matrix.example_type }} path: .local/results/ if-no-files-found: warn diff --git a/tests/config/gitleaks.toml b/.gitleaks.toml similarity index 91% rename from tests/config/gitleaks.toml rename to .gitleaks.toml index db1361c..3501e79 100644 --- a/tests/config/gitleaks.toml +++ b/.gitleaks.toml @@ -16,4 +16,4 @@ regexes = [ ] [allowlist] -paths = ['''.terraform.lock.hcl''', '''poetry.lock''', '''yarn.lock''', '''.pre-commit-config.yaml''', '''\.local'''] +paths = ['''.terraform.lock.hcl''', '''poetry.lock''', '''yarn.lock''', '''mise\.lock''', '''.pre-commit-config.yaml''', '''\.local'''] diff --git a/.mise.toml b/.mise.toml index c435170..398fa88 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,5 +1,7 @@ [settings] install_before = "7d" +lockfile = true +locked = true [settings.python] compile = false @@ -14,16 +16,19 @@ cosign = false # https://devguide.python.org/versions/ # Required by: pre-commit, yamllint, shellcheck-py, check-jsonschema, # conventional-pre-commit, sqlfluff -"core:python" = "3.14.5" +python = "3.14.6" # https://nodejs.org/en/about/previous-releases # Required by: markdownlint-cli, prettier, spectral, newman -"core:node" = "22.22.3" +node = "24.21.0" + +# Required by: local runs of validator +java = "temurin-25.0.4+101.0.LTS" # ─── Pre-commit ─────────────────────────────────────────────────────────────── # https://github.com/pre-commit/pre-commit/releases -pre-commit = "4.6.0" +pre-commit = "4.6.2" # ─── Linting & formatting ──────────────────────────────────────────────────── @@ -31,16 +36,19 @@ pre-commit = "4.6.0" # Required by: pre-commit actionlint hook (system fallback) actionlint = "1.7.12" +# Required by: pre-commit shellcheck hook (system fallback) +shellcheck = "0.11.0" + # https://github.com/crate-ci/typos/releases # Required by: pre-commit typos hook (system fallback) -typos = "1.47.0" +typos = "1.50.2" # https://github.com/mikefarah/yq/releases # Required by: .github/workflows/ (yq to read/update YAML values) -yq = "4.53.2" +yq = "4.53.6" # https://github.com/biomejs/biome -biome = "2.5.2" +biome = "2.5.14" # ─── Security scanning ─────────────────────────────────────────────────────── diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1eb278f..bd834fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -70,6 +70,6 @@ repos: hooks: - id: gitleaks name: gitleaks - entry: gitleaks detect --no-git --redact --verbose --config=tests/config/gitleaks.toml + entry: gitleaks detect --no-git --redact --verbose --config=.gitleaks.toml language: system pass_filenames: false diff --git a/mise.lock b/mise.lock new file mode 100644 index 0000000..ad46b00 --- /dev/null +++ b/mise.lock @@ -0,0 +1,401 @@ +# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html + +lockfile_version = 2 + +[[tools.actionlint]] +version = "1.7.12" +backend = "aqua:rhysd/actionlint" +specifiers = ["1.7.12"] + +[tools.actionlint."platforms.linux-arm64"] +checksum = "sha256:325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924897" +provenance = "github-attestations" + +[tools.actionlint."platforms.linux-arm64-musl"] +checksum = "sha256:325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924897" +provenance = "github-attestations" + +[tools.actionlint."platforms.linux-x64"] +checksum = "sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924896" +provenance = "github-attestations" + +[tools.actionlint."platforms.linux-x64-musl"] +checksum = "sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924896" +provenance = "github-attestations" + +[tools.actionlint."platforms.macos-arm64"] +checksum = "sha256:aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_darwin_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924893" +provenance = "github-attestations" + +[tools.actionlint."platforms.macos-x64"] +checksum = "sha256:5b44c3bc2255115c9b69e30efc0fecdf498fdb63c5d58e17084fd5f16324c644" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_darwin_amd64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924880" +provenance = "github-attestations" + +[tools.actionlint."platforms.windows-x64"] +checksum = "sha256:6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_windows_amd64.zip" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924919" +provenance = "github-attestations" + +[[tools.biome]] +version = "2.5.14" +backend = "aqua:biomejs/biome" +specifiers = ["2.5.14"] + +[tools.biome."platforms.linux-arm64"] +checksum = "sha256:50ac7f598985e21b15da57e5d87da0f7ab0c163e6f78eb6d4690309827d77ef8" +url = "https://github.com/biomejs/biome/releases/download/%40biomejs/biome%402.5.14/biome-linux-arm64" +url_api = "https://api.github.com/repos/biomejs/biome/releases/assets/567471272" +provenance = "github-attestations" + +[tools.biome."platforms.linux-arm64-musl"] +checksum = "sha256:e97669c6e998e4a160c71bd97757eae21fe8c8cb58a044311096779cd1d00454" +url = "https://github.com/biomejs/biome/releases/download/%40biomejs/biome%402.5.14/biome-linux-arm64-musl" +url_api = "https://api.github.com/repos/biomejs/biome/releases/assets/567471270" +provenance = "github-attestations" + +[tools.biome."platforms.linux-x64"] +checksum = "sha256:290c1c85deeaf01310d9187306060b53961574856a7e34f59d9662562e6f19fe" +url = "https://github.com/biomejs/biome/releases/download/%40biomejs/biome%402.5.14/biome-linux-x64" +url_api = "https://api.github.com/repos/biomejs/biome/releases/assets/567471264" +provenance = "github-attestations" + +[tools.biome."platforms.linux-x64-musl"] +checksum = "sha256:d15a39b256ca1294fe8f8817f9162f7c26229ca0cb8bd19d0369540c1a01d781" +url = "https://github.com/biomejs/biome/releases/download/%40biomejs/biome%402.5.14/biome-linux-x64-musl" +url_api = "https://api.github.com/repos/biomejs/biome/releases/assets/567471265" +provenance = "github-attestations" + +[tools.biome."platforms.macos-arm64"] +checksum = "sha256:3d1194d0a7b720315fb6f8cbafeb5b18ca7100c3e635e8ce82ef46b315de9c0e" +url = "https://github.com/biomejs/biome/releases/download/%40biomejs/biome%402.5.14/biome-darwin-arm64" +url_api = "https://api.github.com/repos/biomejs/biome/releases/assets/567471268" +provenance = "github-attestations" + +[tools.biome."platforms.macos-x64"] +checksum = "sha256:b331448d7afb592cc4674e9bd6905eae5e79f5dd9b34b61b44803a6b7a91b801" +url = "https://github.com/biomejs/biome/releases/download/%40biomejs/biome%402.5.14/biome-darwin-x64" +url_api = "https://api.github.com/repos/biomejs/biome/releases/assets/567471271" +provenance = "github-attestations" + +[tools.biome."platforms.windows-x64"] +checksum = "sha256:bef8f088617c8364314f55dffdc7b8ddabe12fee9e1e71f45f708a7027579280" +url = "https://github.com/biomejs/biome/releases/download/%40biomejs/biome%402.5.14/biome-win32-x64.exe" +url_api = "https://api.github.com/repos/biomejs/biome/releases/assets/567471269" +provenance = "github-attestations" + +[[tools.gitleaks]] +version = "8.30.1" +backend = "aqua:gitleaks/gitleaks" +specifiers = ["8.30.1"] + +[tools.gitleaks."platforms.linux-arm64"] +checksum = "sha256:e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080" +url = "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/gitleaks/gitleaks/releases/assets/378332057" + +[tools.gitleaks."platforms.linux-arm64-musl"] +checksum = "sha256:e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080" +url = "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/gitleaks/gitleaks/releases/assets/378332057" + +[tools.gitleaks."platforms.linux-x64"] +checksum = "sha256:551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb" +url = "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz" +url_api = "https://api.github.com/repos/gitleaks/gitleaks/releases/assets/378332058" + +[tools.gitleaks."platforms.linux-x64-musl"] +checksum = "sha256:551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb" +url = "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz" +url_api = "https://api.github.com/repos/gitleaks/gitleaks/releases/assets/378332058" + +[tools.gitleaks."platforms.macos-arm64"] +checksum = "sha256:b40ab0ae55c505963e365f271a8d3846efbc170aa17f2607f13df610a9aeb6a5" +url = "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_darwin_arm64.tar.gz" +url_api = "https://api.github.com/repos/gitleaks/gitleaks/releases/assets/378332059" + +[tools.gitleaks."platforms.macos-x64"] +checksum = "sha256:dfe101a4db2255fc85120ac7f3d25e4342c3c20cf749f2c20a18081af1952709" +url = "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_darwin_x64.tar.gz" +url_api = "https://api.github.com/repos/gitleaks/gitleaks/releases/assets/378332053" + +[tools.gitleaks."platforms.windows-x64"] +checksum = "sha256:d29144deff3a68aa93ced33dddf84b7fdc26070add4aa0f4513094c8332afc4e" +url = "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_windows_x64.zip" +url_api = "https://api.github.com/repos/gitleaks/gitleaks/releases/assets/378333178" + +[[tools.java]] +version = "temurin-25.0.4+101.0.LTS" +backend = "core:java" +specifiers = ["temurin-25.0.4+101.0.LTS"] + +[tools.java."platforms.linux-arm64"] +checksum = "sha256:69df11a02cfa3ef7d7ca645e03edce6778ec090e100f6ae2b42097865730ac52" +url = "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_aarch64_linux_hotspot_25.0.4.1_1.tar.gz" + +[tools.java."platforms.linux-arm64-musl"] +checksum = "sha256:8d19373d427017d86ac87cf6a47ba49746be864832f60b52b7e3b1531c6b2cb8" +url = "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_aarch64_alpine-linux_hotspot_25.0.4.1_1.tar.gz" + +[tools.java."platforms.linux-x64"] +checksum = "sha256:dbb698396d478e7fa2b1e50f4103324b2a99b90569ee27c33f2261f9215cf41e" +url = "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_x64_linux_hotspot_25.0.4.1_1.tar.gz" + +[tools.java."platforms.linux-x64-musl"] +checksum = "sha256:f18648ee5ce45261f50dc9493fdae7ddebaa2a7a857cafda3e2a448a79c03dee" +url = "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_x64_alpine-linux_hotspot_25.0.4.1_1.tar.gz" + +[tools.java."platforms.macos-arm64"] +checksum = "sha256:61979887f7506a24a57439ff99adb8b3a7fc89977d9cfe3b8984f58a981b7b9d" +url = "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_aarch64_mac_hotspot_25.0.4.1_1.tar.gz" + +[tools.java."platforms.macos-x64"] +checksum = "sha256:e6229d9504f7922053ab31821b9e6bee8761daf7b026a3476d1a027563009880" +url = "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_x64_mac_hotspot_25.0.4.1_1.tar.gz" + +[tools.java."platforms.windows-x64"] +checksum = "sha256:00c847d804f4a78e9f04f2683faf14fed898535b177b7fc704486cb0284e9283" +url = "https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.4.1%2B1/OpenJDK25U-jdk_x64_windows_hotspot_25.0.4.1_1.zip" + +[[tools.node]] +version = "24.21.0" +backend = "core:node" +specifiers = ["24.21.0"] + +[tools.node."platforms.linux-arm64"] +checksum = "sha256:724282c3b43aec998aa9527380465b45d229e021b58035f5f4f63095eabfe5d5" +url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-linux-arm64.tar.gz" + +[tools.node."platforms.linux-arm64-musl"] +checksum = "sha256:3048b0811e158ca0d8672b59c839861763e144492980d8d29b369dfee45747e4" +url = "https://unofficial-builds.nodejs.org/download/release/v24.21.0/node-v24.21.0-linux-arm64-musl.tar.gz" + +[tools.node."platforms.linux-x64"] +checksum = "sha256:6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff" +url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-linux-x64.tar.gz" + +[tools.node."platforms.linux-x64-musl"] +checksum = "sha256:01a713e34da98e7b4d2a3191ee7ca3f93dd0384d1a2ef23c379b2161ee6e133a" +url = "https://unofficial-builds.nodejs.org/download/release/v24.21.0/node-v24.21.0-linux-x64-musl.tar.gz" + +[tools.node."platforms.macos-arm64"] +checksum = "sha256:bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057" +url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-darwin-arm64.tar.gz" + +[tools.node."platforms.macos-x64"] +checksum = "sha256:1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097" +url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-darwin-x64.tar.gz" + +[tools.node."platforms.windows-x64"] +checksum = "sha256:158f7685b44de51f6c0df1d153526cbcd3e1bc739a8dfc607721cef75de9e541" +url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-win-x64.zip" + +[[tools.pre-commit]] +version = "4.6.2" +backend = "aqua:pre-commit/pre-commit" +specifiers = ["4.6.2"] + +[tools.pre-commit."platforms.linux-arm64"] +checksum = "sha256:43c09a73d82af8efff09900e60d76d67a2c3d990613c3c770b2b5c4418a16c4b" +url = "https://github.com/pre-commit/pre-commit/releases/download/v4.6.2/pre-commit-4.6.2.pyz" +url_api = "https://api.github.com/repos/pre-commit/pre-commit/releases/assets/509317714" + +[tools.pre-commit."platforms.linux-arm64-musl"] +checksum = "sha256:43c09a73d82af8efff09900e60d76d67a2c3d990613c3c770b2b5c4418a16c4b" +url = "https://github.com/pre-commit/pre-commit/releases/download/v4.6.2/pre-commit-4.6.2.pyz" +url_api = "https://api.github.com/repos/pre-commit/pre-commit/releases/assets/509317714" + +[tools.pre-commit."platforms.linux-x64"] +checksum = "sha256:43c09a73d82af8efff09900e60d76d67a2c3d990613c3c770b2b5c4418a16c4b" +url = "https://github.com/pre-commit/pre-commit/releases/download/v4.6.2/pre-commit-4.6.2.pyz" +url_api = "https://api.github.com/repos/pre-commit/pre-commit/releases/assets/509317714" + +[tools.pre-commit."platforms.linux-x64-musl"] +checksum = "sha256:43c09a73d82af8efff09900e60d76d67a2c3d990613c3c770b2b5c4418a16c4b" +url = "https://github.com/pre-commit/pre-commit/releases/download/v4.6.2/pre-commit-4.6.2.pyz" +url_api = "https://api.github.com/repos/pre-commit/pre-commit/releases/assets/509317714" + +[tools.pre-commit."platforms.macos-arm64"] +checksum = "sha256:43c09a73d82af8efff09900e60d76d67a2c3d990613c3c770b2b5c4418a16c4b" +url = "https://github.com/pre-commit/pre-commit/releases/download/v4.6.2/pre-commit-4.6.2.pyz" +url_api = "https://api.github.com/repos/pre-commit/pre-commit/releases/assets/509317714" + +[tools.pre-commit."platforms.macos-x64"] +checksum = "sha256:43c09a73d82af8efff09900e60d76d67a2c3d990613c3c770b2b5c4418a16c4b" +url = "https://github.com/pre-commit/pre-commit/releases/download/v4.6.2/pre-commit-4.6.2.pyz" +url_api = "https://api.github.com/repos/pre-commit/pre-commit/releases/assets/509317714" + +[[tools.python]] +version = "3.14.6" +backend = "core:python" +specifiers = ["3.14.6"] + +[tools.python."platforms.linux-arm64"] +checksum = "sha256:8ad1ec529cd1708245e8661a463245fc1479c43cbfd66e58b095456ebf77180a" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260804/cpython-3.14.6+20260804-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" +provenance = "github-attestations" + +[tools.python."platforms.linux-arm64-musl"] +checksum = "sha256:85dbb537dc5d6679edabfb61cefdf2511d26cb8669662c4563202b0990e79b4a" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260804/cpython-3.14.6+20260804-aarch64-unknown-linux-musl-install_only_stripped.tar.gz" +provenance = "github-attestations" + +[tools.python."platforms.linux-x64"] +checksum = "sha256:ba960817e542642021135db330d875f410f4e14c64a62e2ae5faf3697ada08b2" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260804/cpython-3.14.6+20260804-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" +provenance = "github-attestations" + +[tools.python."platforms.linux-x64-musl"] +checksum = "sha256:cbedbddcc6b2de99310015e629df67aeb732f622bbf258ec5d310757f85ab7ac" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260804/cpython-3.14.6+20260804-x86_64-unknown-linux-musl-install_only_stripped.tar.gz" +provenance = "github-attestations" + +[tools.python."platforms.macos-arm64"] +checksum = "blake3:e9164d7cebd580fa2d74b4fa9217ac4ec3e7579b905cd9202afd0b90d691ee54" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260804/cpython-3.14.6+20260804-aarch64-apple-darwin-install_only_stripped.tar.gz" +provenance = "github-attestations" + +[tools.python."platforms.macos-x64"] +checksum = "sha256:78ded4c4f0b6a931240f09bd20608533bb40dd71c1ff953c7267359075ca8b16" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260804/cpython-3.14.6+20260804-x86_64-apple-darwin-install_only_stripped.tar.gz" +provenance = "github-attestations" + +[tools.python."platforms.windows-x64"] +checksum = "sha256:ada613a2af32c04cf7c54726243c56d76f0e59f5f61d2f28371d3b9d8efad566" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260804/cpython-3.14.6+20260804-x86_64-pc-windows-msvc-install_only_stripped.tar.gz" +provenance = "github-attestations" + +[[tools.shellcheck]] +version = "0.11.0" +backend = "aqua:koalaman/shellcheck" +specifiers = [ + "0.11.0", + "latest", +] + +[tools.shellcheck."platforms.linux-arm64"] +checksum = "sha256:12b331c1d2db6b9eb13cfca64306b1b157a86eb69db83023e261eaa7e7c14588" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.aarch64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056934" + +[tools.shellcheck."platforms.linux-arm64-musl"] +checksum = "sha256:12b331c1d2db6b9eb13cfca64306b1b157a86eb69db83023e261eaa7e7c14588" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.aarch64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056934" + +[tools.shellcheck."platforms.linux-x64"] +checksum = "sha256:8c3be12b05d5c177a04c29e3c78ce89ac86f1595681cab149b65b97c4e227198" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.x86_64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056942" + +[tools.shellcheck."platforms.linux-x64-musl"] +checksum = "sha256:8c3be12b05d5c177a04c29e3c78ce89ac86f1595681cab149b65b97c4e227198" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.linux.x86_64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056942" + +[tools.shellcheck."platforms.macos-arm64"] +checksum = "sha256:56affdd8de5527894dca6dc3d7e0a99a873b0f004d7aabc30ae407d3f48b0a79" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.darwin.aarch64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056932" + +[tools.shellcheck."platforms.macos-x64"] +checksum = "sha256:3c89db4edcab7cf1c27bff178882e0f6f27f7afdf54e859fa041fca10febe4c6" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.darwin.x86_64.tar.xz" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056930" + +[tools.shellcheck."platforms.windows-x64"] +checksum = "sha256:8a4e35ab0b331c85d73567b12f2a444df187f483e5079ceffa6bda1faa2e740e" +url = "https://github.com/koalaman/shellcheck/releases/download/v0.11.0/shellcheck-v0.11.0.zip" +url_api = "https://api.github.com/repos/koalaman/shellcheck/releases/assets/279056944" + +[[tools.typos]] +version = "1.50.2" +backend = "aqua:crate-ci/typos" +specifiers = ["1.50.2"] + +[tools.typos."platforms.linux-arm64"] +checksum = "sha256:27c4b6d0ba6f37d120ce32117777d703fc9d57ba71f16e8bcde5b35c987cf9b8" +url = "https://github.com/crate-ci/typos/releases/download/v1.50.2/typos-v1.50.2-aarch64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/crate-ci/typos/releases/assets/565799446" + +[tools.typos."platforms.linux-arm64-musl"] +checksum = "sha256:27c4b6d0ba6f37d120ce32117777d703fc9d57ba71f16e8bcde5b35c987cf9b8" +url = "https://github.com/crate-ci/typos/releases/download/v1.50.2/typos-v1.50.2-aarch64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/crate-ci/typos/releases/assets/565799446" + +[tools.typos."platforms.linux-x64"] +checksum = "sha256:abcb3e257c7c2abeff4d903f7fe68071357637605bdb283ce2251f44bc70dc09" +url = "https://github.com/crate-ci/typos/releases/download/v1.50.2/typos-v1.50.2-x86_64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/crate-ci/typos/releases/assets/565795694" + +[tools.typos."platforms.linux-x64-musl"] +checksum = "sha256:abcb3e257c7c2abeff4d903f7fe68071357637605bdb283ce2251f44bc70dc09" +url = "https://github.com/crate-ci/typos/releases/download/v1.50.2/typos-v1.50.2-x86_64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/crate-ci/typos/releases/assets/565795694" + +[tools.typos."platforms.macos-arm64"] +checksum = "sha256:70d61e9d682034133f0aa56f5a02bae0de168495f3cce62a87054aa7186c1901" +url = "https://github.com/crate-ci/typos/releases/download/v1.50.2/typos-v1.50.2-aarch64-apple-darwin.tar.gz" +url_api = "https://api.github.com/repos/crate-ci/typos/releases/assets/565802016" + +[tools.typos."platforms.macos-x64"] +checksum = "sha256:7480a081e51d428d94f560ef8b855ae0decb7c35cc686925ed887c5f51b1ecf1" +url = "https://github.com/crate-ci/typos/releases/download/v1.50.2/typos-v1.50.2-x86_64-apple-darwin.tar.gz" +url_api = "https://api.github.com/repos/crate-ci/typos/releases/assets/565800683" + +[tools.typos."platforms.windows-x64"] +checksum = "sha256:8474ca8bd72df497b7dee2272345bdea0d4926b18b708faea492fc9bcfd0b0e9" +url = "https://github.com/crate-ci/typos/releases/download/v1.50.2/typos-v1.50.2-x86_64-pc-windows-msvc.zip" +url_api = "https://api.github.com/repos/crate-ci/typos/releases/assets/565798642" + +[[tools.yq]] +version = "4.53.6" +backend = "aqua:mikefarah/yq" +specifiers = ["4.53.6"] + +[tools.yq."platforms.linux-arm64"] +checksum = "sha256:88a1016bc1d657375a35864e4f44b6f333df8ff97b559f51bba0adcb2169df09" +url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_linux_arm64" +url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028007" + +[tools.yq."platforms.linux-arm64-musl"] +checksum = "sha256:88a1016bc1d657375a35864e4f44b6f333df8ff97b559f51bba0adcb2169df09" +url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_linux_arm64" +url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028007" + +[tools.yq."platforms.linux-x64"] +checksum = "sha256:c5f056448f973ae7d39b5401949648a78f2dc1947d6a8eb65be60d5c504b9385" +url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_linux_amd64" +url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028022" + +[tools.yq."platforms.linux-x64-musl"] +checksum = "sha256:c5f056448f973ae7d39b5401949648a78f2dc1947d6a8eb65be60d5c504b9385" +url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_linux_amd64" +url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028022" + +[tools.yq."platforms.macos-arm64"] +checksum = "sha256:cceb0b8d71ea5294334121f8429f33f92b920e7217d904a2f9f35443968ac424" +url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_darwin_arm64" +url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028033" + +[tools.yq."platforms.macos-x64"] +checksum = "sha256:caa513cb04f3804b34d4752f0e0d7904fecb9e7cf1d34081289f83259319a7f6" +url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_darwin_amd64" +url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522028031" + +[tools.yq."platforms.windows-x64"] +checksum = "sha256:ece3dd8bb50d39f93610506273ea262feb91e5c486bbddbb10abf91b2a6c0f14" +url = "https://github.com/mikefarah/yq/releases/download/v4.53.6/yq_windows_amd64.exe" +url_api = "https://api.github.com/repos/mikefarah/yq/releases/assets/522027974" From 8fbaf80c2fef5b9dcd052a372985e3ef3c974109 Mon Sep 17 00:00:00 2001 From: lewisbirks Date: Thu, 1 Oct 2026 17:58:18 +0100 Subject: [PATCH 16/22] chore: sonar happiness Signed-off-by: lewisbirks --- .github/scripts/generate-validation-comment.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/scripts/generate-validation-comment.js b/.github/scripts/generate-validation-comment.js index 7b50197..9632c77 100644 --- a/.github/scripts/generate-validation-comment.js +++ b/.github/scripts/generate-validation-comment.js @@ -18,8 +18,8 @@ const validationOutcome = process.env.VALIDATION_OUTCOME; const validationType = process.env.VALIDATION_TYPE; const runUrl = process.env.RUN_URL; const outputPath = process.env.OUTPUT_PATH ?? "comment-body.md"; -const heading = `## 🔬 FHIR Validation Results${validationType ? ` - ${validationType.toUpperCase()}` : ""}`; -const commentMarker = validationType ? `` : ""; +const heading = `## 🔬 FHIR Validation Results - ${validationType.toUpperCase()}`; +const commentMarker = ``; const serverUrl = process.env.GITHUB_SERVER_URL; const repository = process.env.GITHUB_REPOSITORY; @@ -99,8 +99,14 @@ if (!fs.existsSync(resultsPath)) { } } - const overall = - errors > 0 ? "❌ Validation failed" : warnings > 0 ? "⚠️ Validation passed with warnings" : "✅ Validation passed"; + let overall; + if (errors > 0) { + overall = "❌ Validation failed"; + } else if (warnings > 0) { + overall = "⚠️ Validation passed with warnings"; + } else { + overall = "✅ Validation passed"; + } const summary = `**${errors}** error(s) · **${warnings}** warning(s) · **${info}** info`; From fff5ecb9fc688e30bfe48288f9b83f9b24108997 Mon Sep 17 00:00:00 2001 From: lewisbirks Date: Thu, 1 Oct 2026 18:01:37 +0100 Subject: [PATCH 17/22] chore: simplify action Signed-off-by: lewisbirks --- .github/workflows/cicd-1-pull-request.yaml | 49 ++-------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/.github/workflows/cicd-1-pull-request.yaml b/.github/workflows/cicd-1-pull-request.yaml index e6a668c..88b8cf1 100644 --- a/.github/workflows/cicd-1-pull-request.yaml +++ b/.github/workflows/cicd-1-pull-request.yaml @@ -14,48 +14,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 - - - name: Restore cache pre-commit hooks - id: cache-pre-commit - uses: actions/cache/restore@v6 - with: - path: ~/.cache/pre-commit - key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} - restore-keys: | - pre-commit-${{ runner.os }}- - - - name: Install mise - shell: bash - run: | - echo "::group::Configure git" - git config --global --add safe.directory '*' - echo "::endgroup::" - - echo "::group::Install pre-commit hooks" - curl https://mise.run | sh - echo "$HOME/.local/share/mise/bin" >> "$GITHUB_PATH" - echo "$HOME/.local/share/mise/shims" >> "$GITHUB_PATH" - echo "::endgroup::" - - - name: Install pre-commit - shell: bash - run: | - echo "::group::Install pre-commit hooks" - mise exec -- pre-commit install --install-hooks - echo "::endgroup::" - - - name: Run pre-commit - shell: bash - run: | - echo "::group::Run pre-commit checks" - SKIP=no-commit-to-branch mise exec -- pre-commit run --all-files --show-diff-on-failure --color=always - env: - PRE_COMMIT_COLOR: always - - - name: "Save cache pre-commit hooks" - if: always() - continue-on-error: true - uses: actions/cache/save@v6 - with: - path: ~/.cache/pre-commit - key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + - name: Initialize mise + uses: NHSDigital/hometest-mgmt-terraform/.github/actions/install-mise@main + - name: Run pre-commit checks + uses: NHSDigital/hometest-mgmt-terraform/.github/actions/perform-pre-commit@main From c9827d03ffad22b2a47c9da8dc3a74e23c81c2a0 Mon Sep 17 00:00:00 2001 From: lewisbirks Date: Fri, 2 Oct 2026 09:42:32 +0100 Subject: [PATCH 18/22] chore: shared actions are currently in internal repo and can't work in public repos Signed-off-by: lewisbirks --- .github/actions/install-mise/action.yaml | 165 ++++++++++++++++++ .../actions/perform-pre-commit/action.yaml | 58 ++++++ .github/workflows/cicd-1-pull-request.yaml | 4 +- 3 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 .github/actions/install-mise/action.yaml create mode 100644 .github/actions/perform-pre-commit/action.yaml diff --git a/.github/actions/install-mise/action.yaml b/.github/actions/install-mise/action.yaml new file mode 100644 index 0000000..f8a10a6 --- /dev/null +++ b/.github/actions/install-mise/action.yaml @@ -0,0 +1,165 @@ +--- +name: "Install mise" +description: "Install mise and repo dependencies" +inputs: + working_directory: + description: "Directory which will be used for mise installation" + required: false + default: "." + github_token: + description: "GitHub token for API rate limits (use a PAT for higher limits)" + required: false + default: "" + remove_android_sdk: + description: "Remove the Android SDK during cleanup (frees ~9GB but is slow takes around 1m15s)" + required: false + default: "false" + +runs: + using: "composite" + steps: + - name: Configure git + shell: bash + run: | + echo "::group::Configure git" + git config --global --add safe.directory '*' + echo "::endgroup::" + + echo "::group::GitHub API rate limit info" + if [[ -n "${GITHUB_TOKEN}" ]]; then + echo "Using provided GitHub token for API requests" + curl -s -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/rate_limit | jq '.rate' + else + echo "No GitHub token provided, using unauthenticated requests with lower rate limits" + curl -s https://api.github.com/rate_limit | jq '.rate' + fi + echo "::endgroup::" + + echo "::group:: PWD and directory contents" + pwd + ls -la + echo "::endgroup::" + + # - name: "Analyze disk usage" + # shell: bash + # run: | + # echo "::group::Filesystem usage" + # df -h + # echo "::endgroup::" + + # # 11G /usr/local/lib/android + # # 5.6G /usr/share/dotnet + # # 4.3G /usr/share/dotnet/sdk + # # 3.7G /usr/local/.ghcup + # # 3.3G /usr/share/swift + # # 1.8G /opt/hostedtoolcache/CodeQL + # # 1.6G /opt/hostedtoolcache/Python + # # 1.5G /usr/lib/jvm + # # 1.3G /usr/local/share/powershell + # # 1.1G /usr/local/julia1.12.7 + + # echo "::group::Top 30 largest directories" + # # Depth-limited scan of common large roots; sorted biggest-first for cleanup decisions. + # sudo du -h -d 3 /opt /usr/share /usr/local /usr/lib /home 2>/dev/null \ + # | sort -rh | head -n 30 + # echo "::endgroup::" + + + - name: "Free up disk space" + shell: bash + env: + REMOVE_ANDROID_SDK: ${{ inputs.remove_android_sdk }} + run: | + echo "::group::Disk usage before cleanup" + df -h + echo "::endgroup::" + + echo "::group::Free up disk space" + if [[ "${REMOVE_ANDROID_SDK}" == "true" ]]; then + echo "Removing android sdk..." + sudo rm -rf /usr/local/lib/android + else + echo "Skipping android sdk removal (remove_android_sdk != true)" + fi + echo "Removing dotnet..." + sudo rm -rf /usr/share/dotnet + echo "Removing swift..." + sudo rm -rf /usr/share/swift + echo "Removing CodeQL..." + sudo rm -rf /opt/hostedtoolcache/CodeQL + echo "Removing go..." + sudo rm -rf /opt/hostedtoolcache/go + echo "::endgroup::" + + echo "::group::Disk usage after cleanup" + df -h + echo "::endgroup::" + + - name: "Determine repo root" + id: repo-root + shell: bash + env: + WORKING_DIR: ${{ inputs.working_directory }} + run: | + echo "::group::Configure repo root for mise installation" + REPO_ROOT=$(git -C "$WORKING_DIR" rev-parse --show-toplevel) + echo "path=${REPO_ROOT}" >> "$GITHUB_OUTPUT" + + ACTION_REPO_ROOT="${GITHUB_ACTION_PATH%%/.github/*}" + echo "action_repo_root=${ACTION_REPO_ROOT}" >> "$GITHUB_OUTPUT" + echo "::endgroup::" + + echo "::group::Print repo root, PWD, and GITHUB_ACTION_PATH" + echo "Repo root: ${REPO_ROOT}" + echo "Current directory: $(pwd)" + echo "GITHUB_ACTION_PATH: ${GITHUB_ACTION_PATH}" + echo " Action repo root: ${ACTION_REPO_ROOT}" + echo "::endgroup::" + + - name: Restore cache mise + id: cache-mise + uses: actions/cache/restore@v6 + with: + path: | + ~/.local/share/mise + ${{ steps.repo-root.outputs.path }}/.mise-cache + key: mise-${{ runner.os }}-${{ runner.arch }}-mgmt-${{ hashFiles(format('{0}/mise.lock', steps.repo-root.outputs.path)) }} + restore-keys: | + mise-${{ runner.os }}-${{ runner.arch }}-mgmt- + mise-${{ runner.os }}-${{ runner.arch }}- + + - name: Install mise + shell: bash + working-directory: ${{ steps.repo-root.outputs.path }} + env: + GITHUB_TOKEN: ${{ inputs.github_token || github.token }} + MISE_GITHUB_TOKEN: ${{ inputs.github_token || github.token }} + run: | + curl https://mise.run | sh + echo "$HOME/.local/share/mise/bin" >> $GITHUB_PATH + echo "$HOME/.local/share/mise/shims" >> $GITHUB_PATH + + - name: Mise install + shell: bash + working-directory: ${{ steps.repo-root.outputs.path }} + env: + GITHUB_TOKEN: ${{ inputs.github_token || github.token }} + MISE_GITHUB_TOKEN: ${{ inputs.github_token || github.token }} + run: | + mise install + echo "::group::mise doctor" + mise doctor || true + echo "::endgroup::" + echo "::group::Disk usage after mise install" + df -h + echo "::endgroup::" + + - name: "Save cache mise" + if: always() + continue-on-error: true + uses: actions/cache/save@v6 + with: + path: | + ~/.local/share/mise + ${{ steps.repo-root.outputs.path }}/.mise-cache + key: mise-${{ runner.os }}-${{ runner.arch }}-mgmt-${{ hashFiles(format('{0}/mise.lock', steps.repo-root.outputs.path)) }} diff --git a/.github/actions/perform-pre-commit/action.yaml b/.github/actions/perform-pre-commit/action.yaml new file mode 100644 index 0000000..eb39a93 --- /dev/null +++ b/.github/actions/perform-pre-commit/action.yaml @@ -0,0 +1,58 @@ +--- +name: "Perform pre-commit checks" +description: "Perform pre-commit checks" +inputs: + base_sha: + description: "Base commit SHA used to determine pre-commit diff range" + required: false + default: "" + all_files: + description: "Run pre-commit against all files instead of a diff range" + required: false + default: "true" +runs: + using: "composite" + steps: + - name: Restore cache pre-commit hooks + id: cache-pre-commit + uses: actions/cache/restore@v6 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} + restore-keys: | + pre-commit-${{ runner.os }}- + + - name: Install pre-commit + shell: bash + run: | + echo "::group::Install pre-commit hooks" + mise exec -- pre-commit install --install-hooks + echo "::endgroup::" + + - name: Run pre-commit + shell: bash + run: | + echo "::group::Run pre-commit checks" + if [[ "${ALL_FILES}" == "true" ]]; then + SKIP=no-commit-to-branch mise exec -- pre-commit run --all-files --show-diff-on-failure --color=always + else + if [[ -n "${BASE_SHA}" ]]; then + FROM_REF="${BASE_SHA}" + else + FROM_REF="origin/main" + fi + SKIP=no-commit-to-branch mise exec -- pre-commit run --from-ref "${FROM_REF}" --to-ref HEAD --show-diff-on-failure --color=always + fi + echo "::endgroup::" + env: + ALL_FILES: ${{ inputs.all_files }} + BASE_SHA: ${{ inputs.base_sha }} + PRE_COMMIT_COLOR: always + + - name: "Save cache pre-commit hooks" + if: always() + continue-on-error: true + uses: actions/cache/save@v6 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }} diff --git a/.github/workflows/cicd-1-pull-request.yaml b/.github/workflows/cicd-1-pull-request.yaml index 88b8cf1..8670732 100644 --- a/.github/workflows/cicd-1-pull-request.yaml +++ b/.github/workflows/cicd-1-pull-request.yaml @@ -15,6 +15,6 @@ jobs: - name: Checkout uses: actions/checkout@v7 - name: Initialize mise - uses: NHSDigital/hometest-mgmt-terraform/.github/actions/install-mise@main + uses: ./.github/actions/install-mise - name: Run pre-commit checks - uses: NHSDigital/hometest-mgmt-terraform/.github/actions/perform-pre-commit@main + uses: ./.github/actions/perform-pre-commit From 45209925a7166958015411e4e5abd732c69d0b7c Mon Sep 17 00:00:00 2001 From: lewisbirks Date: Fri, 2 Oct 2026 09:54:56 +0100 Subject: [PATCH 19/22] chore: move example file structure Signed-off-by: lewisbirks --- .github/scripts/generate-validation-comment.js | 2 +- examples/fhir/{ => hiv}/order_servicerequest_no_email.json | 0 examples/{fhir => hl7}/psa/epic-order-hl7-2.txt | 0 examples/{fhir => hl7}/psa/epic-order-hl7-generated.txt | 0 examples/{fhir => hl7}/psa/generated-order-request.txt | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename examples/fhir/{ => hiv}/order_servicerequest_no_email.json (100%) rename examples/{fhir => hl7}/psa/epic-order-hl7-2.txt (100%) rename examples/{fhir => hl7}/psa/epic-order-hl7-generated.txt (100%) rename examples/{fhir => hl7}/psa/generated-order-request.txt (100%) diff --git a/.github/scripts/generate-validation-comment.js b/.github/scripts/generate-validation-comment.js index 9632c77..09f7bae 100644 --- a/.github/scripts/generate-validation-comment.js +++ b/.github/scripts/generate-validation-comment.js @@ -2,7 +2,7 @@ // Environment variables: // - RESULTS_PATH : path to the results.json file (optional, defaults to '.local/results/results.json') // - VALIDATION_OUTCOME : result of the validation job ('success' | 'failure' | 'cancelled'), optional -// - VALIDATION_TYPE : type of FHIR examples being reported (optional) +// - VALIDATION_TYPE : type of FHIR examples being reported // - RUN_URL : URL to the workflow run, for linking to artifacts // - OUTPUT_PATH : path for the generated comment body (optional, defaults to 'comment-body.md') // - GITHUB_SERVER_URL : GitHub server URL (set automatically by Actions) diff --git a/examples/fhir/order_servicerequest_no_email.json b/examples/fhir/hiv/order_servicerequest_no_email.json similarity index 100% rename from examples/fhir/order_servicerequest_no_email.json rename to examples/fhir/hiv/order_servicerequest_no_email.json diff --git a/examples/fhir/psa/epic-order-hl7-2.txt b/examples/hl7/psa/epic-order-hl7-2.txt similarity index 100% rename from examples/fhir/psa/epic-order-hl7-2.txt rename to examples/hl7/psa/epic-order-hl7-2.txt diff --git a/examples/fhir/psa/epic-order-hl7-generated.txt b/examples/hl7/psa/epic-order-hl7-generated.txt similarity index 100% rename from examples/fhir/psa/epic-order-hl7-generated.txt rename to examples/hl7/psa/epic-order-hl7-generated.txt diff --git a/examples/fhir/psa/generated-order-request.txt b/examples/hl7/psa/generated-order-request.txt similarity index 100% rename from examples/fhir/psa/generated-order-request.txt rename to examples/hl7/psa/generated-order-request.txt From 317ce4bea8f21237dfdd6451e73f783221ac0084 Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Fri, 2 Oct 2026 15:32:57 +0100 Subject: [PATCH 20/22] Move working examples --- examples/fhir/psa/{ => hl7}/epic-order-hl7-2.txt | 0 examples/fhir/psa/{ => hl7}/epic-order-hl7-generated.txt | 0 examples/fhir/psa/{ => hl7}/generated-order-request.txt | 0 examples/fhir/psa/{ => reference}/observation_high.example.json | 0 examples/fhir/psa/{ => reference}/observation_normal.example.json | 0 .../fhir/psa/{ => reference}/order_servicerequest.example.json | 0 .../{ => reference}/order_servicerequest_with_note.example.json | 0 .../order_servicerequest_with_note_bundle.example.json | 0 .../order_servicerequest_with_note_specimen_bundle.example.json | 0 .../fhir/psa/{ => reference}/rduh-service-request-example.json | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename examples/fhir/psa/{ => hl7}/epic-order-hl7-2.txt (100%) rename examples/fhir/psa/{ => hl7}/epic-order-hl7-generated.txt (100%) rename examples/fhir/psa/{ => hl7}/generated-order-request.txt (100%) rename examples/fhir/psa/{ => reference}/observation_high.example.json (100%) rename examples/fhir/psa/{ => reference}/observation_normal.example.json (100%) rename examples/fhir/psa/{ => reference}/order_servicerequest.example.json (100%) rename examples/fhir/psa/{ => reference}/order_servicerequest_with_note.example.json (100%) rename examples/fhir/psa/{ => reference}/order_servicerequest_with_note_bundle.example.json (100%) rename examples/fhir/psa/{ => reference}/order_servicerequest_with_note_specimen_bundle.example.json (100%) rename examples/fhir/psa/{ => reference}/rduh-service-request-example.json (100%) diff --git a/examples/fhir/psa/epic-order-hl7-2.txt b/examples/fhir/psa/hl7/epic-order-hl7-2.txt similarity index 100% rename from examples/fhir/psa/epic-order-hl7-2.txt rename to examples/fhir/psa/hl7/epic-order-hl7-2.txt diff --git a/examples/fhir/psa/epic-order-hl7-generated.txt b/examples/fhir/psa/hl7/epic-order-hl7-generated.txt similarity index 100% rename from examples/fhir/psa/epic-order-hl7-generated.txt rename to examples/fhir/psa/hl7/epic-order-hl7-generated.txt diff --git a/examples/fhir/psa/generated-order-request.txt b/examples/fhir/psa/hl7/generated-order-request.txt similarity index 100% rename from examples/fhir/psa/generated-order-request.txt rename to examples/fhir/psa/hl7/generated-order-request.txt diff --git a/examples/fhir/psa/observation_high.example.json b/examples/fhir/psa/reference/observation_high.example.json similarity index 100% rename from examples/fhir/psa/observation_high.example.json rename to examples/fhir/psa/reference/observation_high.example.json diff --git a/examples/fhir/psa/observation_normal.example.json b/examples/fhir/psa/reference/observation_normal.example.json similarity index 100% rename from examples/fhir/psa/observation_normal.example.json rename to examples/fhir/psa/reference/observation_normal.example.json diff --git a/examples/fhir/psa/order_servicerequest.example.json b/examples/fhir/psa/reference/order_servicerequest.example.json similarity index 100% rename from examples/fhir/psa/order_servicerequest.example.json rename to examples/fhir/psa/reference/order_servicerequest.example.json diff --git a/examples/fhir/psa/order_servicerequest_with_note.example.json b/examples/fhir/psa/reference/order_servicerequest_with_note.example.json similarity index 100% rename from examples/fhir/psa/order_servicerequest_with_note.example.json rename to examples/fhir/psa/reference/order_servicerequest_with_note.example.json diff --git a/examples/fhir/psa/order_servicerequest_with_note_bundle.example.json b/examples/fhir/psa/reference/order_servicerequest_with_note_bundle.example.json similarity index 100% rename from examples/fhir/psa/order_servicerequest_with_note_bundle.example.json rename to examples/fhir/psa/reference/order_servicerequest_with_note_bundle.example.json diff --git a/examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json b/examples/fhir/psa/reference/order_servicerequest_with_note_specimen_bundle.example.json similarity index 100% rename from examples/fhir/psa/order_servicerequest_with_note_specimen_bundle.example.json rename to examples/fhir/psa/reference/order_servicerequest_with_note_specimen_bundle.example.json diff --git a/examples/fhir/psa/rduh-service-request-example.json b/examples/fhir/psa/reference/rduh-service-request-example.json similarity index 100% rename from examples/fhir/psa/rduh-service-request-example.json rename to examples/fhir/psa/reference/rduh-service-request-example.json From f11d031f0e50fbfbf988789657123a50b01115ed Mon Sep 17 00:00:00 2001 From: ronanoconnor1 Date: Fri, 9 Oct 2026 10:04:08 +0100 Subject: [PATCH 21/22] updates to spec for specimen body site --- .../rduh_order_servicerequest_0810.json | 274 ++++++++++++++++++ ...h_order_servicerequest_ukcore_example.json | 11 + schemas/supplier-api-spec-v2.yaml | 13 +- 3 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json diff --git a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json new file mode 100644 index 0000000..e9c7ea7 --- /dev/null +++ b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json @@ -0,0 +1,274 @@ +{ + "resourceType":"Bundle", + "type":"transaction", + "entry":[ + { + "request":{ + "method":"POST", + "url":"Organization" + }, + "fullUrl":"urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86", + "resource":{ + "resourceType":"Organization", + "identifier":[ + { + "value":"RDE" + } + ] + } + }, + { + "request":{ + "method":"POST", + "url":"Organization" + }, + "fullUrl":"urn:uuid:73889367-0349-4c7b-9f29-73bb1cf5cb22", + "resource":{ + "resourceType":"Organization", + "identifier":[ + { + "value":"NHS" + } + ] + } + }, + { + "request":{ + "method":"POST", + "url":"Patient" + }, + "fullUrl":"urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf", + "resource":{ + "resourceType":"Patient", + "address":[ + { + "country":"ENG" + } + ], + "birthDate":"1966-10-06", + "deceasedBoolean":false, + "gender":"male", + "identifier":[ + { + "assigner":{ + "reference":"urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86" + }, + "extension":[ + { + "url":"http://intersystems.com/fhir/extn/sda3/lib/patient-number-i-s-o-assigning-authority", + "valueString":"RDE" + } + ], + "type":{ + "coding":[ + { + "code":"MR", + "system":"http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text":"MRN" + }, + "value":"KB14733" + }, + { + "assigner":{ + "reference":"urn:uuid:73889367-0349-4c7b-9f29-73bb1cf5cb22" + }, + "extension":[ + { + "url":"http://intersystems.com/fhir/extn/sda3/lib/patient-number-i-s-o-assigning-authority", + "valueString":"NHS" + } + ], + "type":{ + "coding":[ + { + "code":"NH" + } + ], + "text":"NH" + }, + "value":"7496487841" + } + ], + "managingOrganization":{ + "reference":"urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86" + }, + "name":[ + { + "family":"HOMETEST", + "given":[ + "PSA" + ], + "text":"PSA HOMETEST", + "use":"official" + } + ] + } + }, + { + "request":{ + "method":"POST", + "url":"Practitioner" + }, + "fullUrl":"urn:uuid:fde1e02c-ca08-4a2c-8b2c-d3903f484332", + "resource":{ + "resourceType":"Practitioner", + "extension":[ + { + "url":"http://intersystems.com/fhir/extn/sda3/lib/code-table-detail-care-provider-description", + "valueString":"(ANMD), ANAESTHETIC" + } + ], + "identifier":[ + { + "extension":[ + { + "url":"http://intersystems.com/fhir/extn/sda3/lib/code-table-detail-care-provider-s-d-a-coding-standard", + "valueString":"PROVID" + } + ], + "type":{ + "coding":[ + { + "code":"PROVID" + } + ] + }, + "value":"107100" + } + ], + "name":[ + { + "family":"(ANMD)", + "given":[ + "ANAESTHETIC", + "CONSULTANT" + ], + "text":"ANAESTHETIC CONSULTANT (ANMD)" + } + ] + } + }, + { + "request":{ + "method":"POST", + "url":"Specimen" + }, + "fullUrl":"urn:uuid:19f45c72-1a46-4944-bdaf-846d20cbdd4b", + "resource":{ + "resourceType":"Specimen", + "collection":{ + "bodySite":{ + "coding":[ + { + "code":"122554006", + "display":"Blood, Capillary" + } + ] + }, + "quantity":{ + "system":"http://www.intersystems.com/fhir/system" + } + }, + "subject":{ + "reference":"urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf" + }, + "type":{ + "coding":[ + { + "code":"119297000", + "display":"Blood", + "system":"http://snomed.info/sct" + } + ] + } + } + }, + { + "request":{ + "method":"POST", + "url":"ServiceRequest" + }, + "fullUrl":"urn:uuid:e8395ba4-a624-40dc-9adf-d84f61e851a5", + "resource":{ + "resourceType":"ServiceRequest", + "authoredOn":"2026-10-08T13:53:24+01:00", + "category":[ + { + "coding":[ + { + "code":"103693007", + "display":"Diagnostic procedure", + "system":"http://snomed.info/sct" + } + ], + "text":"Diagnostic procedure" + } + ], + "code":{ + "coding":[ + { + "code":"63476009", + "display":"PSA HOMETEST", + "system":"http://snomed.info/sct" + } + ] + }, + "extension":[ + { + "url":"http://intersystems.com/fhir/extn/sda3/lib/lab-order-placer-assigning-authority", + "valueString":"EPC^^" + } + ], + "identifier":[ + { + "type":{ + "coding":[ + { + "code":"FILL", + "display":"Filler Identifier", + "system":"http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text":"FillerId" + }, + "use":"official" + }, + { + "type":{ + "coding":[ + { + "code":"PLAC", + "display":"Placer Identifier", + "system":"http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text":"PlacerId" + }, + "use":"official", + "value":"RH82822337" + } + ], + "intent":"order", + "note":[ + { + "text":"Test hometest interface" + } + ], + "requester":{ + "reference":"urn:uuid:fde1e02c-ca08-4a2c-8b2c-d3903f484332" + }, + "specimen":[ + { + "reference":"urn:uuid:19f45c72-1a46-4944-bdaf-846d20cbdd4b" + } + ], + "status":"active", + "subject":{ + "reference":"urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf" + } + } + } + ] +} \ No newline at end of file diff --git a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json index 75934ad..8f1b599 100644 --- a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json +++ b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_ukcore_example.json @@ -82,6 +82,17 @@ ] }, "status": "available", + "collection": { + "bodySite": { + "coding": [ + { + "code": "20982000", + "display": "Capillary", + "system": "http://snomed.info/sct" + } + ] + } + }, "subject": { "reference": "urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53" }, diff --git a/schemas/supplier-api-spec-v2.yaml b/schemas/supplier-api-spec-v2.yaml index db1ee03..14bd649 100644 --- a/schemas/supplier-api-spec-v2.yaml +++ b/schemas/supplier-api-spec-v2.yaml @@ -174,6 +174,12 @@ paths: profile: - https://fhir.hl7.org.uk/StructureDefinition/UKCore-Specimen status: available + collection: + bodySite: + coding: + - code: "20982000" + display: Capillary + system: http://snomed.info/sct subject: reference: urn:uuid:a9d98321-bfd1-4abd-8180-017228ad8b53 type: @@ -878,7 +884,7 @@ components: enum: [Specimen] resource: type: object - required: [resourceType, meta, status, subject, type] + required: [resourceType, meta, subject, type] properties: resourceType: type: string @@ -896,6 +902,11 @@ components: status: type: string enum: [available, unavailable, unsatisfactory, entered-in-error] + collection: + type: object + properties: + bodySite: + $ref: '#/components/schemas/FHIRCodeableConcept' subject: $ref: '#/components/schemas/FHIRReference' type: From f2a50ce5d88ccf116f57affcca1366ef0c8d8fcf Mon Sep 17 00:00:00 2001 From: lewisbirks Date: Fri, 9 Oct 2026 10:29:37 +0100 Subject: [PATCH 22/22] chore: pre-commit Signed-off-by: lewisbirks --- .typos.toml | 4 + .../rduh_order_servicerequest_0810.json | 524 +++++++++--------- 2 files changed, 266 insertions(+), 262 deletions(-) create mode 100644 .typos.toml diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000..23cb9ce --- /dev/null +++ b/.typos.toml @@ -0,0 +1,4 @@ +[files] +extend-exclude = [ + "examples/fhir/psa/poc_working_examples/*.json" +] diff --git a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json index e9c7ea7..d93580e 100644 --- a/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json +++ b/examples/fhir/psa/poc_working_examples/rduh_order_servicerequest_0810.json @@ -1,274 +1,274 @@ { - "resourceType":"Bundle", - "type":"transaction", - "entry":[ - { - "request":{ - "method":"POST", - "url":"Organization" - }, - "fullUrl":"urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86", - "resource":{ - "resourceType":"Organization", - "identifier":[ - { - "value":"RDE" - } - ] - } - }, - { - "request":{ - "method":"POST", - "url":"Organization" - }, - "fullUrl":"urn:uuid:73889367-0349-4c7b-9f29-73bb1cf5cb22", - "resource":{ - "resourceType":"Organization", - "identifier":[ - { - "value":"NHS" - } - ] - } - }, - { - "request":{ - "method":"POST", - "url":"Patient" - }, - "fullUrl":"urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf", - "resource":{ - "resourceType":"Patient", - "address":[ - { - "country":"ENG" - } - ], - "birthDate":"1966-10-06", - "deceasedBoolean":false, - "gender":"male", - "identifier":[ - { - "assigner":{ - "reference":"urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86" + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "request": { + "method": "POST", + "url": "Organization" }, - "extension":[ - { - "url":"http://intersystems.com/fhir/extn/sda3/lib/patient-number-i-s-o-assigning-authority", - "valueString":"RDE" - } - ], - "type":{ - "coding":[ - { - "code":"MR", - "system":"http://terminology.hl7.org/CodeSystem/v2-0203" - } - ], - "text":"MRN" - }, - "value":"KB14733" - }, - { - "assigner":{ - "reference":"urn:uuid:73889367-0349-4c7b-9f29-73bb1cf5cb22" - }, - "extension":[ - { - "url":"http://intersystems.com/fhir/extn/sda3/lib/patient-number-i-s-o-assigning-authority", - "valueString":"NHS" - } - ], - "type":{ - "coding":[ - { - "code":"NH" - } - ], - "text":"NH" - }, - "value":"7496487841" - } - ], - "managingOrganization":{ - "reference":"urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86" + "fullUrl": "urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "value": "RDE" + } + ] + } }, - "name":[ - { - "family":"HOMETEST", - "given":[ - "PSA" - ], - "text":"PSA HOMETEST", - "use":"official" - } - ] - } - }, - { - "request":{ - "method":"POST", - "url":"Practitioner" - }, - "fullUrl":"urn:uuid:fde1e02c-ca08-4a2c-8b2c-d3903f484332", - "resource":{ - "resourceType":"Practitioner", - "extension":[ - { - "url":"http://intersystems.com/fhir/extn/sda3/lib/code-table-detail-care-provider-description", - "valueString":"(ANMD), ANAESTHETIC" - } - ], - "identifier":[ - { - "extension":[ - { - "url":"http://intersystems.com/fhir/extn/sda3/lib/code-table-detail-care-provider-s-d-a-coding-standard", - "valueString":"PROVID" - } - ], - "type":{ - "coding":[ - { - "code":"PROVID" - } - ] + { + "request": { + "method": "POST", + "url": "Organization" }, - "value":"107100" - } - ], - "name":[ - { - "family":"(ANMD)", - "given":[ - "ANAESTHETIC", - "CONSULTANT" - ], - "text":"ANAESTHETIC CONSULTANT (ANMD)" - } - ] - } - }, - { - "request":{ - "method":"POST", - "url":"Specimen" - }, - "fullUrl":"urn:uuid:19f45c72-1a46-4944-bdaf-846d20cbdd4b", - "resource":{ - "resourceType":"Specimen", - "collection":{ - "bodySite":{ - "coding":[ - { - "code":"122554006", - "display":"Blood, Capillary" - } - ] - }, - "quantity":{ - "system":"http://www.intersystems.com/fhir/system" - } - }, - "subject":{ - "reference":"urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf" + "fullUrl": "urn:uuid:73889367-0349-4c7b-9f29-73bb1cf5cb22", + "resource": { + "resourceType": "Organization", + "identifier": [ + { + "value": "NHS" + } + ] + } }, - "type":{ - "coding":[ - { - "code":"119297000", - "display":"Blood", - "system":"http://snomed.info/sct" + { + "request": { + "method": "POST", + "url": "Patient" + }, + "fullUrl": "urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf", + "resource": { + "resourceType": "Patient", + "address": [ + { + "country": "ENG" + } + ], + "birthDate": "1966-10-06", + "deceasedBoolean": false, + "gender": "male", + "identifier": [ + { + "assigner": { + "reference": "urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86" + }, + "extension": [ + { + "url": "http://intersystems.com/fhir/extn/sda3/lib/patient-number-i-s-o-assigning-authority", + "valueString": "RDE" + } + ], + "type": { + "coding": [ + { + "code": "MR", + "system": "http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text": "MRN" + }, + "value": "KB14733" + }, + { + "assigner": { + "reference": "urn:uuid:73889367-0349-4c7b-9f29-73bb1cf5cb22" + }, + "extension": [ + { + "url": "http://intersystems.com/fhir/extn/sda3/lib/patient-number-i-s-o-assigning-authority", + "valueString": "NHS" + } + ], + "type": { + "coding": [ + { + "code": "NH" + } + ], + "text": "NH" + }, + "value": "7496487841" + } + ], + "managingOrganization": { + "reference": "urn:uuid:3be95115-cdb3-4093-964a-6ddd32528d86" + }, + "name": [ + { + "family": "HOMETEST", + "given": [ + "PSA" + ], + "text": "PSA HOMETEST", + "use": "official" + } + ] } - ] - } - } - }, - { - "request":{ - "method":"POST", - "url":"ServiceRequest" - }, - "fullUrl":"urn:uuid:e8395ba4-a624-40dc-9adf-d84f61e851a5", - "resource":{ - "resourceType":"ServiceRequest", - "authoredOn":"2026-10-08T13:53:24+01:00", - "category":[ - { - "coding":[ - { - "code":"103693007", - "display":"Diagnostic procedure", - "system":"http://snomed.info/sct" - } - ], - "text":"Diagnostic procedure" - } - ], - "code":{ - "coding":[ - { - "code":"63476009", - "display":"PSA HOMETEST", - "system":"http://snomed.info/sct" + }, + { + "request": { + "method": "POST", + "url": "Practitioner" + }, + "fullUrl": "urn:uuid:fde1e02c-ca08-4a2c-8b2c-d3903f484332", + "resource": { + "resourceType": "Practitioner", + "extension": [ + { + "url": "http://intersystems.com/fhir/extn/sda3/lib/code-table-detail-care-provider-description", + "valueString": "(ANMD), ANAESTHETIC" + } + ], + "identifier": [ + { + "extension": [ + { + "url": "http://intersystems.com/fhir/extn/sda3/lib/code-table-detail-care-provider-s-d-a-coding-standard", + "valueString": "PROVID" + } + ], + "type": { + "coding": [ + { + "code": "PROVID" + } + ] + }, + "value": "107100" + } + ], + "name": [ + { + "family": "(ANMD)", + "given": [ + "ANAESTHETIC", + "CONSULTANT" + ], + "text": "ANAESTHETIC CONSULTANT (ANMD)" + } + ] } - ] }, - "extension":[ - { - "url":"http://intersystems.com/fhir/extn/sda3/lib/lab-order-placer-assigning-authority", - "valueString":"EPC^^" - } - ], - "identifier":[ - { - "type":{ - "coding":[ - { - "code":"FILL", - "display":"Filler Identifier", - "system":"http://terminology.hl7.org/CodeSystem/v2-0203" - } - ], - "text":"FillerId" + { + "request": { + "method": "POST", + "url": "Specimen" }, - "use":"official" - }, - { - "type":{ - "coding":[ - { - "code":"PLAC", - "display":"Placer Identifier", - "system":"http://terminology.hl7.org/CodeSystem/v2-0203" + "fullUrl": "urn:uuid:19f45c72-1a46-4944-bdaf-846d20cbdd4b", + "resource": { + "resourceType": "Specimen", + "collection": { + "bodySite": { + "coding": [ + { + "code": "122554006", + "display": "Blood, Capillary" + } + ] + }, + "quantity": { + "system": "http://www.intersystems.com/fhir/system" + } + }, + "subject": { + "reference": "urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf" + }, + "type": { + "coding": [ + { + "code": "119297000", + "display": "Blood", + "system": "http://snomed.info/sct" + } + ] } - ], - "text":"PlacerId" - }, - "use":"official", - "value":"RH82822337" - } - ], - "intent":"order", - "note":[ - { - "text":"Test hometest interface" - } - ], - "requester":{ - "reference":"urn:uuid:fde1e02c-ca08-4a2c-8b2c-d3903f484332" + } }, - "specimen":[ - { - "reference":"urn:uuid:19f45c72-1a46-4944-bdaf-846d20cbdd4b" - } - ], - "status":"active", - "subject":{ - "reference":"urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf" + { + "request": { + "method": "POST", + "url": "ServiceRequest" + }, + "fullUrl": "urn:uuid:e8395ba4-a624-40dc-9adf-d84f61e851a5", + "resource": { + "resourceType": "ServiceRequest", + "authoredOn": "2026-10-08T13:53:24+01:00", + "category": [ + { + "coding": [ + { + "code": "103693007", + "display": "Diagnostic procedure", + "system": "http://snomed.info/sct" + } + ], + "text": "Diagnostic procedure" + } + ], + "code": { + "coding": [ + { + "code": "63476009", + "display": "PSA HOMETEST", + "system": "http://snomed.info/sct" + } + ] + }, + "extension": [ + { + "url": "http://intersystems.com/fhir/extn/sda3/lib/lab-order-placer-assigning-authority", + "valueString": "EPC^^" + } + ], + "identifier": [ + { + "type": { + "coding": [ + { + "code": "FILL", + "display": "Filler Identifier", + "system": "http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text": "FillerId" + }, + "use": "official" + }, + { + "type": { + "coding": [ + { + "code": "PLAC", + "display": "Placer Identifier", + "system": "http://terminology.hl7.org/CodeSystem/v2-0203" + } + ], + "text": "PlacerId" + }, + "use": "official", + "value": "RH82822337" + } + ], + "intent": "order", + "note": [ + { + "text": "Test hometest interface" + } + ], + "requester": { + "reference": "urn:uuid:fde1e02c-ca08-4a2c-8b2c-d3903f484332" + }, + "specimen": [ + { + "reference": "urn:uuid:19f45c72-1a46-4944-bdaf-846d20cbdd4b" + } + ], + "status": "active", + "subject": { + "reference": "urn:uuid:49d5b864-9a25-41f0-8011-926d12e9d9cf" + } + } } - } - } - ] -} \ No newline at end of file + ] +}