Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 1 addition & 20 deletions gateway-api/src/gateway_api/pds_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,19 +138,13 @@ 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.
: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",
}
Expand All @@ -173,7 +167,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:
Expand All @@ -182,20 +175,8 @@ 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`.

: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.
: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(
request_id=request_id,
correlation_id=correlation_id,
)

Expand Down
21 changes: 14 additions & 7 deletions gateway-api/src/gateway_api/test_pds_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from dataclasses import dataclass
from datetime import date
from typing import TYPE_CHECKING, Any, cast
from uuid import uuid4
from uuid import UUID

import pytest
import requests
Expand Down Expand Up @@ -270,7 +270,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
Expand All @@ -291,12 +292,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
Expand All @@ -305,7 +304,15 @@ 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 an auto-generated UUID
assert "X-Request-ID" in headers

# Verify it's a valid UUID by trying to parse it
try:
UUID(headers["X-Request-ID"]) # Should not raise
except ValueError:
pytest.fail("X-Request-ID header was not a valid UUID")

assert headers["X-Correlation-ID"] == corr_id


Expand All @@ -314,10 +321,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
Expand Down