From cfe25658a5b6a0823eaa8cdb1b24f5ad6fdb8051 Mon Sep 17 00:00:00 2001 From: Ben Taylor <59649826+Vox-Ben@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:11:44 +0000 Subject: [PATCH 1/3] Generate X-Request-Id header --- gateway-api/src/gateway_api/pds_search.py | 13 +++++-------- .../src/gateway_api/test_pds_search.py | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/gateway-api/src/gateway_api/pds_search.py b/gateway-api/src/gateway_api/pds_search.py index b21b6ecf..75abad86 100644 --- a/gateway-api/src/gateway_api/pds_search.py +++ b/gateway-api/src/gateway_api/pds_search.py @@ -138,19 +138,18 @@ def __init__( def _build_headers( self, - request_id: str | None = None, correlation_id: str | None = None, ) -> dict[str, str]: """ Build mandatory and optional headers for a PDS request. - :param request_id: Optional ``X-Request-ID``. If not supplied a new UUID is - generated. + A new UUID is generated for ``X-Request-ID`` with each call. + :param correlation_id: Optional ``X-Correlation-ID`` for cross-system tracing. :return: Dictionary of HTTP headers for the outbound request. """ headers = { - "X-Request-ID": request_id or str(uuid.uuid4()), + "X-Request-ID": str(uuid.uuid4()), "NHSD-End-User-Organisation-ODS": self.end_user_org_ods, "Accept": "application/fhir+json", } @@ -173,7 +172,6 @@ def _build_headers( def search_patient_by_nhs_number( self, nhs_number: str, - request_id: str | None = None, correlation_id: str | None = None, timeout: int | None = None, ) -> PdsSearchResults | None: @@ -183,9 +181,9 @@ def search_patient_by_nhs_number( Calls ``GET /Patient/{nhs_number}``, which returns a single FHIR Patient resource on success, then extracts a single :class:`PdsSearchResults`. + A new UUID is generated for the ``X-Request-ID`` header with each call. + :param nhs_number: NHS number to search for. - :param request_id: Optional request ID to reuse for retries; if not supplied a - UUID is generated. :param correlation_id: Optional correlation ID for tracing. :param timeout: Optional per-call timeout in seconds. If not provided, :attr:`timeout` is used. @@ -195,7 +193,6 @@ def search_patient_by_nhs_number( ``raise_for_status()`` raises :class:`requests.HTTPError`. """ headers = self._build_headers( - request_id=request_id, correlation_id=correlation_id, ) diff --git a/gateway-api/src/gateway_api/test_pds_search.py b/gateway-api/src/gateway_api/test_pds_search.py index a433c9a1..b0e98dd7 100644 --- a/gateway-api/src/gateway_api/test_pds_search.py +++ b/gateway-api/src/gateway_api/test_pds_search.py @@ -7,7 +7,6 @@ from dataclasses import dataclass from datetime import date from typing import TYPE_CHECKING, Any, cast -from uuid import uuid4 import pytest import requests @@ -270,7 +269,8 @@ def test_search_patient_by_nhs_number_sends_expected_headers( * Authorization header * NHSD-End-User-Organisation-ODS header * Accept header - * caller-provided X-Request-ID and X-Correlation-ID headers + * auto-generated X-Request-ID (UUID format) + * caller-provided X-Correlation-ID header :param stub: Stub backend fixture. :param mock_requests_get: Patched ``requests.get`` fixture capturing outbound @@ -291,12 +291,10 @@ def test_search_patient_by_nhs_number_sends_expected_headers( base_url="https://example.test/personal-demographics/FHIR/R4", ) - req_id = str(uuid4()) corr_id = "corr-123" result = client.search_patient_by_nhs_number( "9000000009", - request_id=req_id, correlation_id=corr_id, ) assert result is not None @@ -305,7 +303,14 @@ def test_search_patient_by_nhs_number_sends_expected_headers( assert headers["Authorization"] == "Bearer test-token" assert headers["NHSD-End-User-Organisation-ODS"] == "A12345" assert headers["Accept"] == "application/fhir+json" - assert headers["X-Request-ID"] == req_id + # X-Request-ID should be auto-generated as a UUID + assert "X-Request-ID" in headers + assert isinstance(headers["X-Request-ID"], str) + assert len(headers["X-Request-ID"]) >= 32 + # Verify it's a valid UUID by trying to parse it + from uuid import UUID + + UUID(headers["X-Request-ID"]) # Should not raise assert headers["X-Correlation-ID"] == corr_id @@ -314,10 +319,10 @@ def test_search_patient_by_nhs_number_generates_request_id( mock_requests_get: dict[str, Any], ) -> None: """ - Verify that the client generates an X-Request-ID when not provided. + Verify that the client generates a new X-Request-ID for each request. The stub is in strict mode, so a missing or invalid X-Request-ID would cause a 400. - This test confirms a request ID is present and looks UUID-like. + This test confirms a request ID is present and is a valid UUID. :param stub: Stub backend fixture. :param mock_requests_get: Patched ``requests.get`` fixture capturing outbound From 0178c42705465bda3c7e127a13a50dec465c561b Mon Sep 17 00:00:00 2001 From: Ben Taylor <59649826+Vox-Ben@users.noreply.github.com> Date: Thu, 19 Feb 2026 14:49:30 +0000 Subject: [PATCH 2/3] Review comments --- gateway-api/src/gateway_api/pds_search.py | 16 ---------------- gateway-api/src/gateway_api/test_pds_search.py | 10 ++++++---- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/gateway-api/src/gateway_api/pds_search.py b/gateway-api/src/gateway_api/pds_search.py index 75abad86..78564b3d 100644 --- a/gateway-api/src/gateway_api/pds_search.py +++ b/gateway-api/src/gateway_api/pds_search.py @@ -142,11 +142,6 @@ def _build_headers( ) -> dict[str, str]: """ Build mandatory and optional headers for a PDS request. - - A new UUID is generated for ``X-Request-ID`` with each call. - - :param correlation_id: Optional ``X-Correlation-ID`` for cross-system tracing. - :return: Dictionary of HTTP headers for the outbound request. """ headers = { "X-Request-ID": str(uuid.uuid4()), @@ -180,17 +175,6 @@ def search_patient_by_nhs_number( Calls ``GET /Patient/{nhs_number}``, which returns a single FHIR Patient resource on success, then extracts a single :class:`PdsSearchResults`. - - A new UUID is generated for the ``X-Request-ID`` header with each call. - - :param nhs_number: NHS number to search for. - :param correlation_id: Optional correlation ID for tracing. - :param timeout: Optional per-call timeout in seconds. If not provided, - :attr:`timeout` is used. - :return: A :class:`PdsSearchResults` instance if a patient can be extracted, - otherwise ``None``. - :raises ExternalServiceError: If the HTTP request returns an error status and - ``raise_for_status()`` raises :class:`requests.HTTPError`. """ headers = self._build_headers( correlation_id=correlation_id, diff --git a/gateway-api/src/gateway_api/test_pds_search.py b/gateway-api/src/gateway_api/test_pds_search.py index b0e98dd7..3a6d110e 100644 --- a/gateway-api/src/gateway_api/test_pds_search.py +++ b/gateway-api/src/gateway_api/test_pds_search.py @@ -7,6 +7,7 @@ from dataclasses import dataclass from datetime import date from typing import TYPE_CHECKING, Any, cast +from uuid import UUID import pytest import requests @@ -305,12 +306,13 @@ def test_search_patient_by_nhs_number_sends_expected_headers( assert headers["Accept"] == "application/fhir+json" # X-Request-ID should be auto-generated as a UUID assert "X-Request-ID" in headers - assert isinstance(headers["X-Request-ID"], str) - assert len(headers["X-Request-ID"]) >= 32 + # Verify it's a valid UUID by trying to parse it - from uuid import UUID + try: + UUID(headers["X-Request-ID"]) # Should not raise + except ValueError: + pytest.fail("X-Request-ID header was not a valid UUID") - UUID(headers["X-Request-ID"]) # Should not raise assert headers["X-Correlation-ID"] == corr_id From f7bd7a59654641d4535e62470b636467e15710c2 Mon Sep 17 00:00:00 2001 From: Ben Taylor <59649826+Vox-Ben@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:48:43 +0000 Subject: [PATCH 3/3] Comment change to kick pipeline --- gateway-api/src/gateway_api/test_pds_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gateway-api/src/gateway_api/test_pds_search.py b/gateway-api/src/gateway_api/test_pds_search.py index 3a6d110e..72d4e4e1 100644 --- a/gateway-api/src/gateway_api/test_pds_search.py +++ b/gateway-api/src/gateway_api/test_pds_search.py @@ -304,7 +304,7 @@ def test_search_patient_by_nhs_number_sends_expected_headers( assert headers["Authorization"] == "Bearer test-token" assert headers["NHSD-End-User-Organisation-ODS"] == "A12345" assert headers["Accept"] == "application/fhir+json" - # X-Request-ID should be auto-generated as a UUID + # X-Request-ID should be an auto-generated UUID assert "X-Request-ID" in headers # Verify it's a valid UUID by trying to parse it