[GPCAPIM-305]: Create common Pydantic FHIR types#109
[GPCAPIM-305]: Create common Pydantic FHIR types#109davidhamill1-nhs wants to merge 33 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a shared, strongly-typed FHIR model layer (Pydantic-based) for Gateway API and refactors request/response handling for the $gpc.getstructuredrecord flow to reduce TypedDict usage and duplication.
Changes:
- Replace legacy
TypedDictFHIR structures with Pydantic models (FHIR R4 + STU3Parameters) and add extensive unit tests for validation/serialization. - Refactor
$gpc.getstructuredrecordrequest parsing to validate STU3Parameters, and introduce aGetStructuredRecordResponsehelper for building Flask responses. - Update stubs, tests, lint config, and OpenAPI constraints to align with canonical FHIR identifier system URIs.
Reviewed changes
Copilot reviewed 60 out of 67 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/config/vale/styles/config/vocabularies/words/accept.txt | Allow “Pydantic” / “validators” vocabulary in Vale. |
| ruff.toml | Expand per-file ignores for tests (allow SLF001). |
| gateway-api/tests/integration/test_get_structured_record.py | Update tests to use dict payloads instead of old Parameters type. |
| gateway-api/tests/conftest.py | Update shared test fixture types to dict[str, Any]. |
| gateway-api/tests/acceptance/steps/happy_path.py | Update BDD steps to use dict payloads. |
| gateway-api/stubs/stubs/data/patients/alice_jones_9999999999.json | Add identifier.system for org identifiers in stub data. |
| gateway-api/stubs/stubs/data/patients/blank_asid_sds_result_9000000011.json | Add identifier.system for org identifiers in stub data. |
| gateway-api/stubs/stubs/data/patients/blank_endpoint_sds_result_9000000013.json | Add identifier.system for org identifiers in stub data. |
| gateway-api/stubs/stubs/data/patients/induce_provider_error_9000000012.json | Add identifier.system for org identifiers in stub data. |
| gateway-api/stubs/stubs/data/patients/no_sds_result_9000000010.json | Add identifier.system for org identifiers in stub data. |
| gateway-api/stubs/stubs/data/patients/none_consumer_sds_result_9000000014.json | Add identifier.system for org identifiers in stub data. |
| gateway-api/src/gateway_api/test_controller.py | Update controller tests to use new fhir.r4.Patient model and response .json(). |
| gateway-api/src/gateway_api/test_app.py | Update app tests to mock controller returning a requests-like response and use dict payloads. |
| gateway-api/src/gateway_api/sds/client.py | Parse SDS responses into typed Bundle/Device/Endpoint models and adjust extraction logic. |
| gateway-api/src/gateway_api/provider/test_client.py | Update provider client tests to use dict request payloads. |
| gateway-api/src/gateway_api/pds/test_client.py | Update PDS client tests to expect fhir.r4.Patient and validate error handling via Pydantic. |
| gateway-api/src/gateway_api/pds/search_results.py | Remove legacy PdsSearchResults dataclass. |
| gateway-api/src/gateway_api/pds/client.py | Change PDS client to return typed Patient via model_validate(). |
| gateway-api/src/gateway_api/pds/init.py | Remove export of deleted PdsSearchResults. |
| gateway-api/src/gateway_api/get_structured_record/test_response.py | Add unit tests for new GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/get_structured_record/test_request.py | Update request tests to use dict fixtures; remove old FlaskResponse-based response tests. |
| gateway-api/src/gateway_api/get_structured_record/response.py | New response builder helper for $gpc.getstructuredrecord. |
| gateway-api/src/gateway_api/get_structured_record/request.py | Validate inbound JSON against STU3 Parameters model; expose headers/request_body via typed model. |
| gateway-api/src/gateway_api/get_structured_record/init.py | Export GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/controller.py | Return the provider requests.Response directly and use typed PDS Patient. |
| gateway-api/src/gateway_api/conftest.py | Update fixtures/types and align identifier system URIs. |
| gateway-api/src/gateway_api/common/error.py | Build OperationOutcome via typed R4 models and expose operation_outcome property. |
| gateway-api/src/gateway_api/app.py | Use GetStructuredRecordResponse for response construction and centralize error handling. |
| gateway-api/src/fhir/stu3/resources/parameters.py | Add STU3 Parameters resource model. |
| gateway-api/src/fhir/stu3/resources/init.py | Package marker (no functional changes shown). |
| gateway-api/src/fhir/stu3/elements/test_elements.py | Add tests for STU3 Parameters validation/immutability. |
| gateway-api/src/fhir/stu3/elements/init.py | Package marker (no functional changes shown). |
| gateway-api/src/fhir/stu3/init.py | Export STU3 Parameters. |
| gateway-api/src/fhir/resources/resource.py | Introduce polymorphic base Resource with resourceType dispatch and alias serialization. |
| gateway-api/src/fhir/resources/test_resource.py | Add unit tests for Resource dispatch/create/dump semantics. |
| gateway-api/src/fhir/resources/init.py | Export shared Resource base. |
| gateway-api/src/fhir/r4/resources/test_resources.py | Add comprehensive tests for new R4 resources (Bundle/Patient/Device/Endpoint/OperationOutcome). |
| gateway-api/src/fhir/r4/resources/patient.py | Add typed R4 Patient model + helpers (nhs_number, gp_ods_code). |
| gateway-api/src/fhir/r4/resources/operation_outcome.py | Add typed R4 OperationOutcome model. |
| gateway-api/src/fhir/r4/resources/endpoint.py | Add typed R4 Endpoint model. |
| gateway-api/src/fhir/r4/resources/device.py | Add typed R4 Device model. |
| gateway-api/src/fhir/r4/resources/bundle.py | Add typed R4 Bundle model + find_resources and empty(). |
| gateway-api/src/fhir/r4/resources/init.py | Package marker (no functional changes shown). |
| gateway-api/src/fhir/r4/resources/py.typed | Mark package as typed. |
| gateway-api/src/fhir/r4/elements/test_elements.py | Add tests for R4 elements (Identifier/Reference/Issue/Meta). |
| gateway-api/src/fhir/r4/elements/reference.py | Add R4 Reference base with reference type validation. |
| gateway-api/src/fhir/r4/elements/meta.py | Add R4 Meta element. |
| gateway-api/src/fhir/r4/elements/issue.py | Add R4 Issue, IssueCode, IssueSeverity. |
| gateway-api/src/fhir/r4/elements/identifier.py | Add R4 Identifier types (UUID/NHS Number). |
| gateway-api/src/fhir/r4/elements/init.py | Package marker (no functional changes shown). |
| gateway-api/src/fhir/r4/elements/py.typed | Mark package as typed. |
| gateway-api/src/fhir/r4/init.py | Export R4 elements/resources as a public entry point. |
| gateway-api/src/fhir/period.py | Remove legacy TypedDict Period. |
| gateway-api/src/fhir/patient.py | Remove legacy TypedDict Patient. |
| gateway-api/src/fhir/parameters.py | Remove legacy TypedDict Parameters. |
| gateway-api/src/fhir/operation_outcome.py | Remove legacy TypedDict OperationOutcome. |
| gateway-api/src/fhir/identifier.py | Remove legacy TypedDict Identifier. |
| gateway-api/src/fhir/human_name.py | Remove legacy TypedDict HumanName. |
| gateway-api/src/fhir/general_practitioner.py | Remove legacy TypedDict GeneralPractitioner. |
| gateway-api/src/fhir/bundle.py | Remove legacy TypedDict Bundle. |
| gateway-api/src/fhir/init.py | Remove legacy fhir re-export module (now namespace-style structure). |
| gateway-api/src/fhir/README.md | Add documentation for the new FHIR typing approach and examples. |
| gateway-api/pyproject.toml | Add pydantic dependency. |
| gateway-api/openapi.yaml | Constrain identifier.system via enum to the NHS number URI. |
| Makefile | Adjust wheel install platform targets. |
| .vscode/cspell-dictionary.txt | Add “searchset” to cspell dictionary. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
There was a problem hiding this comment.
Pull request overview
This PR replaces the existing FHIR TypedDict approach with a shared Pydantic-based FHIR model layer (R4 + STU3) and refactors request/response handling for /$gpc.getstructuredrecord to use these typed models across PDS/SDS/controller/app, updating tests and stubs accordingly.
Changes:
- Introduces a new
fhirPydantic model package (sharedResourcebase + selected R4 resources/elements + STU3Parameters) and removes oldTypedDictFHIR types. - Refactors Gateway API flow to validate inbound STU3
Parameters, parse upstream PDS/SDS responses as typed R4 models, and centralises HTTP response building inGetStructuredRecordResponse. - Updates test suites, stub payloads, OpenAPI schema, and tooling configs to align with the new typing/validation.
Reviewed changes
Copilot reviewed 62 out of 69 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/config/vale/styles/config/vocabularies/words/accept.txt | Allows “Pydantic” and “validators” in Vale vocabulary. |
| ruff.toml | Expands per-file ignores for tests (incl. SLF001). |
| gateway-api/tests/integration/test_get_structured_record.py | Updates test typing away from legacy Parameters type. |
| gateway-api/tests/contract/conftest.py | Removes verify=False from proxying requests. |
| gateway-api/tests/conftest.py | Updates simple_request_payload fixture typing to dict. |
| gateway-api/tests/acceptance/steps/happy_path.py | Updates step typing away from legacy Parameters type. |
| gateway-api/stubs/stubs/data/patients/none_consumer_sds_result_9000000014.json | Adds ODS identifier system to stub payload. |
| gateway-api/stubs/stubs/data/patients/no_sds_result_9000000010.json | Adds ODS identifier system to stub payload. |
| gateway-api/stubs/stubs/data/patients/induce_provider_error_9000000012.json | Adds ODS identifier system to stub payload. |
| gateway-api/stubs/stubs/data/patients/blank_endpoint_sds_result_9000000013.json | Adds ODS identifier system to stub payload. |
| gateway-api/stubs/stubs/data/patients/blank_asid_sds_result_9000000011.json | Adds ODS identifier system to stub payload. |
| gateway-api/stubs/stubs/data/patients/alice_jones_9999999999.json | Adds ODS identifier system to stub payload. |
| gateway-api/src/gateway_api/test_controller.py | Updates controller unit tests to use typed Patient and response .json(). |
| gateway-api/src/gateway_api/test_app.py | Updates app tests to use dict payloads and mocked response .json(). |
| gateway-api/src/gateway_api/sds/client.py | Changes SDS client to validate responses as typed R4 Bundle/Device/Endpoint. |
| gateway-api/src/gateway_api/provider/test_client.py | Updates provider client tests to use dict request payloads. |
| gateway-api/src/gateway_api/pds/test_client.py | Updates PDS tests to assert typed Patient properties. |
| gateway-api/src/gateway_api/pds/search_results.py | Removes PdsSearchResults dataclass (no longer used). |
| gateway-api/src/gateway_api/pds/client.py | Returns typed Patient via Pydantic validation (replacing extraction logic). |
| gateway-api/src/gateway_api/pds/init.py | Removes PdsSearchResults export. |
| gateway-api/src/gateway_api/get_structured_record/test_response.py | Adds tests for new GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/get_structured_record/test_request.py | Updates request tests for STU3 typed Parameters validation path. |
| gateway-api/src/gateway_api/get_structured_record/response.py | Adds GetStructuredRecordResponse response builder. |
| gateway-api/src/gateway_api/get_structured_record/request.py | Validates inbound body as STU3 Parameters and exposes typed accessors. |
| gateway-api/src/gateway_api/get_structured_record/init.py | Exports GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/controller.py | Returns provider requests.Response directly and consumes typed Patient from PDS. |
| gateway-api/src/gateway_api/conftest.py | Updates fixtures and request factory to use dict payloads and NHS number system URI. |
| gateway-api/src/gateway_api/common/error.py | Refactors errors to build typed R4 OperationOutcome via Pydantic models. |
| gateway-api/src/gateway_api/app.py | Refactors route to use GetStructuredRecordResponse and typed error outcomes. |
| gateway-api/src/fhir/stu3/resources/parameters.py | Adds STU3 Parameters resource model for inbound validation. |
| gateway-api/src/fhir/stu3/resources/init.py | Adds STU3 resources package marker. |
| gateway-api/src/fhir/stu3/elements/test_elements.py | Adds tests for STU3 Parameters validation/serialization. |
| gateway-api/src/fhir/stu3/elements/init.py | Adds STU3 elements package marker. |
| gateway-api/src/fhir/stu3/init.py | Exposes STU3 Parameters. |
| gateway-api/src/fhir/resources/test_resource.py | Adds tests for polymorphic Resource dispatch and dump behaviour. |
| gateway-api/src/fhir/resources/resource.py | Adds shared polymorphic Resource base with subtype dispatch + resourceType validation. |
| gateway-api/src/fhir/resources/init.py | Exports shared Resource. |
| gateway-api/src/fhir/r4/resources/test_resources.py | Adds tests for R4 resources (Bundle, Patient, Device, Endpoint, OperationOutcome). |
| gateway-api/src/fhir/r4/resources/py.typed | Marks R4 resources package as typed. |
| gateway-api/src/fhir/r4/resources/patient.py | Adds typed R4 Patient resource with nhs_number/gp_ods_code helpers. |
| gateway-api/src/fhir/r4/resources/operation_outcome.py | Adds typed R4 OperationOutcome resource. |
| gateway-api/src/fhir/r4/resources/endpoint.py | Adds typed R4 Endpoint resource. |
| gateway-api/src/fhir/r4/resources/device.py | Adds typed R4 Device resource with constrained identifier types. |
| gateway-api/src/fhir/r4/resources/bundle.py | Adds typed R4 Bundle resource with find_resources. |
| gateway-api/src/fhir/r4/resources/init.py | Adds R4 resources package marker. |
| gateway-api/src/fhir/r4/elements/test_elements.py | Adds tests for R4 elements (Identifier, Reference, Meta, Issue). |
| gateway-api/src/fhir/r4/elements/reference.py | Adds typed R4 Reference base with reference-type validation. |
| gateway-api/src/fhir/r4/elements/py.typed | Marks R4 elements package as typed. |
| gateway-api/src/fhir/r4/elements/meta.py | Adds typed R4 Meta element. |
| gateway-api/src/fhir/r4/elements/issue.py | Adds typed R4 Issue/IssueCode/IssueSeverity. |
| gateway-api/src/fhir/r4/elements/identifier.py | Adds typed R4 Identifier + constrained subclasses (UUID/NHS number). |
| gateway-api/src/fhir/r4/elements/init.py | Adds R4 elements package marker. |
| gateway-api/src/fhir/r4/init.py | Exposes R4 elements/resources via fhir.r4. |
| gateway-api/src/fhir/period.py | Removes legacy TypedDict Period. |
| gateway-api/src/fhir/patient.py | Removes legacy TypedDict Patient. |
| gateway-api/src/fhir/parameters.py | Removes legacy TypedDict Parameters. |
| gateway-api/src/fhir/operation_outcome.py | Removes legacy TypedDict OperationOutcome. |
| gateway-api/src/fhir/identifier.py | Removes legacy TypedDict Identifier. |
| gateway-api/src/fhir/human_name.py | Removes legacy TypedDict HumanName. |
| gateway-api/src/fhir/general_practitioner.py | Removes legacy TypedDict GeneralPractitioner. |
| gateway-api/src/fhir/bundle.py | Removes legacy TypedDict Bundle. |
| gateway-api/src/fhir/init.py | Clears legacy fhir re-exports (now empty). |
| gateway-api/src/fhir/README.md | Adds documentation for new typed FHIR approach and usage. |
| gateway-api/pyproject.toml | Adds pydantic dependency and includes fhir package. |
| gateway-api/openapi.yaml | Constrains valueIdentifier.system to the NHS number URI via enum. |
| Makefile | Adds additional musllinux platform target for wheel install. |
| .vscode/cspell-dictionary.txt | Adds “searchset” to spelling dictionary. |
| .github/workflows/preview-env.yml | Bumps Trivy action SHAs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
✅ Trivy gate: no Critical/High issues. Trivy IaC (Terraform) Summary
Findings (top 50)
|
|
✅ Trivy gate: no Critical/High vulnerabilities. Trivy Image Scan SummaryImage: 900119715266.dkr.ecr.eu-west-2.amazonaws.com/whoami:feature-gpcapim-305-common-fhir-package
Findings (top 50)
|
There was a problem hiding this comment.
Pull request overview
This PR introduces a shared, Pydantic-based set of FHIR types (STU3 + R4) and refactors Gateway API request/response handling and upstream client parsing to use these typed models instead of TypedDicts.
Changes:
- Added Pydantic FHIR models (R4 + STU3) including a polymorphic
Resourcebase for validation/serialization. - Refactored PDS/SDS client parsing and controller/test code to use typed FHIR models.
- Introduced
GetStructuredRecordResponseto centralize response construction and (currently) mirror request headers.
Reviewed changes
Copilot reviewed 63 out of 70 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/config/vale/styles/config/vocabularies/words/accept.txt | Allow-list new terms for Vale linting. |
| ruff.toml | Adjust test per-file ignores (incl. private member access in tests). |
| infrastructure/images/gateway-api/Dockerfile | apk upgrade during image build before creating non-root user. |
| gateway-api/tests/integration/test_get_structured_record.py | Update request payload typing and add header-mirroring integration test. |
| gateway-api/tests/contract/conftest.py | Remove verify=False from proxy request forwarding. |
| gateway-api/tests/conftest.py | Update request payload fixture typing away from old FHIR TypedDict. |
| gateway-api/tests/acceptance/steps/happy_path.py | Update payload typing/imports for acceptance steps. |
| gateway-api/stubs/stubs/data/patients/alice_jones_9999999999.json | Add ODS identifier system to stub patient payload. |
| gateway-api/stubs/stubs/data/patients/no_sds_result_9000000010.json | Add ODS identifier system to stub patient payload. |
| gateway-api/stubs/stubs/data/patients/blank_asid_sds_result_9000000011.json | Add ODS identifier system to stub patient payload. |
| gateway-api/stubs/stubs/data/patients/induce_provider_error_9000000012.json | Add ODS identifier system to stub patient payload. |
| gateway-api/stubs/stubs/data/patients/blank_endpoint_sds_result_9000000013.json | Add ODS identifier system to stub patient payload. |
| gateway-api/stubs/stubs/data/patients/none_consumer_sds_result_9000000014.json | Add ODS identifier system to stub patient payload. |
| gateway-api/src/gateway_api/test_controller.py | Update unit tests to use typed FHIR Patient and requests-style responses. |
| gateway-api/src/gateway_api/test_app.py | Update app tests to mock controller responses and simplify typing. |
| gateway-api/src/gateway_api/sds/client.py | Parse SDS responses into typed Bundle/Device/Endpoint models and simplify extraction. |
| gateway-api/src/gateway_api/provider/test_client.py | Update provider client tests to use dict payload typing. |
| gateway-api/src/gateway_api/pds/test_client.py | Update PDS tests for typed Patient parsing and add validation failure test. |
| gateway-api/src/gateway_api/pds/search_results.py | Remove old PdsSearchResults dataclass. |
| gateway-api/src/gateway_api/pds/client.py | Refactor PDS client to return typed Patient via Pydantic validation. |
| gateway-api/src/gateway_api/pds/init.py | Update exports after removing PdsSearchResults. |
| gateway-api/src/gateway_api/get_structured_record/test_response.py | Add unit tests for new response builder. |
| gateway-api/src/gateway_api/get_structured_record/test_request.py | Update request tests for new typed request parsing and remove old response-building tests. |
| gateway-api/src/gateway_api/get_structured_record/response.py | Introduce GetStructuredRecordResponse response builder. |
| gateway-api/src/gateway_api/get_structured_record/request.py | Parse/validate STU3 Parameters via Pydantic and expose typed accessors. |
| gateway-api/src/gateway_api/get_structured_record/init.py | Export GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/controller.py | Return provider HTTP response directly; refactor PDS handling to use typed Patient. |
| gateway-api/src/gateway_api/conftest.py | Update fixtures to dict payloads and align identifier system URIs. |
| gateway-api/src/gateway_api/common/error.py | Refactor error handling to build typed R4 OperationOutcome. |
| gateway-api/src/gateway_api/app.py | Use GetStructuredRecordResponse for consistent response building and error handling. |
| gateway-api/src/fhir/stu3/resources/parameters.py | Add STU3 Parameters resource model for inbound request validation. |
| gateway-api/src/fhir/stu3/elements/test_elements.py | Add tests for STU3 Parameters validation/immutability. |
| gateway-api/src/fhir/stu3/init.py | Add STU3 package exports. |
| gateway-api/src/fhir/resources/resource.py | Add polymorphic Resource base with resourceType dispatch + exclude-none serialization. |
| gateway-api/src/fhir/resources/test_resource.py | Add tests for polymorphic Resource dispatch and serialization. |
| gateway-api/src/fhir/resources/init.py | Export shared Resource base. |
| gateway-api/src/fhir/r4/init.py | Export R4 elements/resources for typed usage. |
| gateway-api/src/fhir/r4/elements/identifier.py | Add Identifier/UUID/NHS-number identifier elements. |
| gateway-api/src/fhir/r4/elements/issue.py | Add OperationOutcome Issue element + enums. |
| gateway-api/src/fhir/r4/elements/meta.py | Add Meta element with aliasing helpers. |
| gateway-api/src/fhir/r4/elements/reference.py | Add typed Reference base with reference-type validation. |
| gateway-api/src/fhir/r4/elements/test_elements.py | Add tests for R4 elements validation/immutability. |
| gateway-api/src/fhir/r4/resources/bundle.py | Add typed Bundle resource + find_resources. |
| gateway-api/src/fhir/r4/resources/device.py | Add typed Device resource for SDS parsing. |
| gateway-api/src/fhir/r4/resources/endpoint.py | Add typed Endpoint resource for SDS parsing. |
| gateway-api/src/fhir/r4/resources/operation_outcome.py | Add typed OperationOutcome resource. |
| gateway-api/src/fhir/r4/resources/patient.py | Add typed Patient resource with convenient NHS number / GP ODS accessors. |
| gateway-api/src/fhir/r4/resources/test_resources.py | Add tests for R4 resources creation/validation and helpers. |
| gateway-api/src/fhir/README.md | New documentation describing typed FHIR approach and usage. |
| gateway-api/src/fhir/bundle.py | Remove old TypedDict bundle. |
| gateway-api/src/fhir/general_practitioner.py | Remove old TypedDict generalPractitioner. |
| gateway-api/src/fhir/human_name.py | Remove old TypedDict humanName. |
| gateway-api/src/fhir/identifier.py | Remove old TypedDict identifier. |
| gateway-api/src/fhir/operation_outcome.py | Remove old TypedDict operation outcome. |
| gateway-api/src/fhir/parameters.py | Remove old TypedDict parameters. |
| gateway-api/src/fhir/patient.py | Remove old TypedDict patient. |
| gateway-api/src/fhir/period.py | Remove old TypedDict period. |
| gateway-api/src/fhir/init.py | Remove old umbrella exports for TypedDict-based FHIR types. |
| gateway-api/pyproject.toml | Add pydantic dependency. |
| gateway-api/openapi.yaml | Constrain identifier system to NHS number URI via enum. |
| Makefile | Add additional musllinux platform tag to packaging install step. |
| .vscode/cspell-dictionary.txt | Add “searchset” to dictionary. |
| .github/workflows/preview-env.yml | Update Trivy action pins. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
Deployment Complete
|
There was a problem hiding this comment.
Pull request overview
This PR begins the migration from ad-hoc TypedDict FHIR structures to a shared, typed Pydantic-based FHIR model layer (STU3 + R4) within gateway-api, and refactors the /patient/$gpc.getstructuredrecord flow to use a dedicated response builder that mirrors selected headers.
Changes:
- Introduces a new Pydantic-based
fhirpackage structure (R4 + STU3) with a polymorphicResourcebase and typed resources/elements. - Refactors PDS and SDS clients (and related tests/stubs) to parse/validate upstream FHIR JSON into typed models.
- Adds
GetStructuredRecordResponseto centralize response construction (including header mirroring) and updates app/controller/tests accordingly.
Reviewed changes
Copilot reviewed 63 out of 70 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/config/vale/styles/config/vocabularies/words/accept.txt | Allows “Pydantic”/“validators” in Vale vocabulary. |
| ruff.toml | Expands per-file ignores for tests (incl. SLF001). |
| infrastructure/images/gateway-api/Dockerfile | Adds apk upgrade during image build. |
| gateway-api/tests/integration/test_get_structured_record.py | Updates request payload typing; adds header-mirroring integration test. |
| gateway-api/tests/contract/conftest.py | Removes verify=False from proxying requests. |
| gateway-api/tests/conftest.py | Updates request fixture typing to plain dict. |
| gateway-api/tests/acceptance/steps/happy_path.py | Updates request payload typing in BDD steps. |
| gateway-api/stubs/stubs/data/patients/alice_jones_9999999999.json | Adds ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/no_sds_result_9000000010.json | Adds ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/blank_asid_sds_result_9000000011.json | Adds ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/induce_provider_error_9000000012.json | Adds ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/blank_endpoint_sds_result_9000000013.json | Adds ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/none_consumer_sds_result_9000000014.json | Adds ODS identifier system to stub data. |
| gateway-api/src/gateway_api/test_controller.py | Updates controller unit tests to use typed Patient model. |
| gateway-api/src/gateway_api/test_app.py | Updates app tests to expect .json()-style controller responses. |
| gateway-api/src/gateway_api/sds/client.py | Switches SDS parsing to typed Bundle/Device/Endpoint models. |
| gateway-api/src/gateway_api/provider/test_client.py | Updates provider client tests’ request payload typing. |
| gateway-api/src/gateway_api/pds/test_client.py | Refactors PDS tests to assert typed Patient behavior/errors. |
| gateway-api/src/gateway_api/pds/search_results.py | Removes legacy PdsSearchResults dataclass. |
| gateway-api/src/gateway_api/pds/client.py | Refactors PDS client to return typed Patient (Pydantic validation). |
| gateway-api/src/gateway_api/pds/init.py | Updates exports to remove PdsSearchResults. |
| gateway-api/src/gateway_api/get_structured_record/test_response.py | Adds tests for new GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/get_structured_record/test_request.py | Updates request tests to dict payload typing; removes old response-building tests. |
| gateway-api/src/gateway_api/get_structured_record/response.py | Introduces response builder with header mirroring + provider/error handling. |
| gateway-api/src/gateway_api/get_structured_record/request.py | Validates inbound STU3 Parameters with Pydantic; exposes typed fields/JSON. |
| gateway-api/src/gateway_api/get_structured_record/init.py | Exposes GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/controller.py | Returns provider requests.Response directly; uses typed Patient from PDS. |
| gateway-api/src/gateway_api/conftest.py | Updates shared test fixtures for request/response JSON systems/typing. |
| gateway-api/src/gateway_api/common/error.py | Switches errors to typed R4 OperationOutcome construction. |
| gateway-api/src/gateway_api/app.py | Uses GetStructuredRecordResponse for consistent output + header mirroring. |
| gateway-api/src/fhir/stu3/resources/parameters.py | Adds STU3 Parameters model for request validation. |
| gateway-api/src/fhir/stu3/elements/test_elements.py | Adds STU3 Parameters validation/unit tests. |
| gateway-api/src/fhir/stu3/init.py | Exposes STU3 Parameters. |
| gateway-api/src/fhir/resources/test_resource.py | Adds tests for polymorphic Resource dispatch + serialization behavior. |
| gateway-api/src/fhir/resources/resource.py | Adds polymorphic Resource base and create()/serialization defaults. |
| gateway-api/src/fhir/resources/init.py | Exposes shared Resource. |
| gateway-api/src/fhir/r4/resources/test_resources.py | Adds comprehensive R4 resource tests (Bundle/Patient/Device/Endpoint/Outcome). |
| gateway-api/src/fhir/r4/resources/patient.py | Adds typed R4 Patient model + helpers/properties. |
| gateway-api/src/fhir/r4/resources/operation_outcome.py | Adds typed R4 OperationOutcome model. |
| gateway-api/src/fhir/r4/resources/endpoint.py | Adds typed R4 Endpoint model. |
| gateway-api/src/fhir/r4/resources/device.py | Adds typed R4 Device model. |
| gateway-api/src/fhir/r4/resources/bundle.py | Adds typed R4 Bundle model + resource-finding helpers. |
| gateway-api/src/fhir/r4/elements/test_elements.py | Adds comprehensive R4 element tests (Identifier/Reference/Meta/Issue). |
| gateway-api/src/fhir/r4/elements/reference.py | Adds typed R4 Reference base model. |
| gateway-api/src/fhir/r4/elements/meta.py | Adds typed R4 Meta element. |
| gateway-api/src/fhir/r4/elements/issue.py | Adds typed R4 Issue element + enums. |
| gateway-api/src/fhir/r4/elements/identifier.py | Adds typed R4 Identifier element + concrete identifier types. |
| gateway-api/src/fhir/r4/init.py | Exposes R4 resources/elements as a public module surface. |
| gateway-api/src/fhir/period.py | Removes legacy TypedDict Period. |
| gateway-api/src/fhir/patient.py | Removes legacy TypedDict Patient. |
| gateway-api/src/fhir/parameters.py | Removes legacy TypedDict Parameters. |
| gateway-api/src/fhir/operation_outcome.py | Removes legacy TypedDict OperationOutcome. |
| gateway-api/src/fhir/identifier.py | Removes legacy TypedDict Identifier. |
| gateway-api/src/fhir/human_name.py | Removes legacy TypedDict HumanName. |
| gateway-api/src/fhir/general_practitioner.py | Removes legacy TypedDict GeneralPractitioner. |
| gateway-api/src/fhir/bundle.py | Removes legacy TypedDict Bundle. |
| gateway-api/src/fhir/init.py | Removes legacy fhir module re-exports. |
| gateway-api/src/fhir/README.md | Adds module-level documentation for the new typed FHIR approach. |
| gateway-api/pyproject.toml | Adds pydantic dependency. |
| gateway-api/openapi.yaml | Constrains NHS number identifier system via enum. |
| Makefile | Adds additional musllinux platform in packaging step. |
| .vscode/cspell-dictionary.txt | Adds “searchset” spelling entry. |
| .github/workflows/preview-env.yml | Updates pinned Trivy action SHAs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| @property | ||
| def request_body(self) -> str: | ||
| return json.dumps(self._request_body) | ||
| return self.parameters.model_dump_json() |
… of request class.
This reverts commit 49409de.
7e1f496 to
6270716
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new internal FHIR modelling layer based on Pydantic (replacing prior TypedDict-based FHIR types) and refactors request/response handling for the /$gpc.getstructuredrecord operation to centralise response construction and header mirroring.
Changes:
- Add Pydantic-based FHIR Resource/element models (R4 + STU3 Parameters) and supporting tests/docs.
- Refactor Gateway API request/response flow: validate inbound STU3
Parameters, return provider responses via a dedicatedGetStructuredRecordResponse. - Update PDS/SDS clients, stubs, and test suites to use the new typed FHIR models and URI systems.
Reviewed changes
Copilot reviewed 60 out of 67 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/config/vale/styles/config/vocabularies/words/accept.txt | Allow “Pydantic” and “validators” in Vale vocab. |
| ruff.toml | Extend test file ignores to allow private member access in tests. |
| gateway-api/tests/integration/test_get_structured_record.py | Update tests to use dict payloads and add header-mirroring assertion. |
| gateway-api/tests/conftest.py | Update fixtures to return dict payloads rather than old FHIR TypedDict types. |
| gateway-api/tests/acceptance/steps/happy_path.py | Update step typing to dict payloads (drop old Parameters import). |
| gateway-api/stubs/stubs/data/patients/none_consumer_sds_result_9000000014.json | Add ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/no_sds_result_9000000010.json | Add ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/induce_provider_error_9000000012.json | Add ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/blank_endpoint_sds_result_9000000013.json | Add ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/blank_asid_sds_result_9000000011.json | Add ODS identifier system to stub data. |
| gateway-api/stubs/stubs/data/patients/alice_jones_9999999999.json | Add ODS identifier system to stub data. |
| gateway-api/src/gateway_api/test_controller.py | Update controller tests to use new typed Patient and response json handling. |
| gateway-api/src/gateway_api/test_app.py | Update app tests to mock controller responses and expect JSON payloads as dicts. |
| gateway-api/src/gateway_api/sds/client.py | Switch SDS parsing to typed Bundle/Device/Endpoint models. |
| gateway-api/src/gateway_api/provider/test_client.py | Update provider client tests to use dict payloads. |
| gateway-api/src/gateway_api/pds/test_client.py | Update PDS client tests to assert typed Patient behaviour; simplify negative test coverage. |
| gateway-api/src/gateway_api/pds/search_results.py | Remove legacy PdsSearchResults dataclass. |
| gateway-api/src/gateway_api/pds/client.py | Refactor PDS client to return typed Patient via Pydantic validation. |
| gateway-api/src/gateway_api/pds/init.py | Stop exporting removed PdsSearchResults. |
| gateway-api/src/gateway_api/get_structured_record/test_response.py | Add unit tests for new GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/get_structured_record/test_request.py | Update request tests; remove tests for deleted FlaskResponse-based response-building. |
| gateway-api/src/gateway_api/get_structured_record/response.py | Introduce response builder to mirror headers and construct FHIR+json responses. |
| gateway-api/src/gateway_api/get_structured_record/request.py | Validate inbound payload as STU3 Parameters model and expose typed accessors. |
| gateway-api/src/gateway_api/get_structured_record/init.py | Export new GetStructuredRecordResponse. |
| gateway-api/src/gateway_api/controller.py | Update controller to return raw requests.Response from provider instead of custom FlaskResponse. |
| gateway-api/src/gateway_api/conftest.py | Update shared fixtures and request helpers to dict payloads and updated identifier system URIs. |
| gateway-api/src/gateway_api/common/error.py | Rework error modelling to build typed R4 OperationOutcome instances. |
| gateway-api/src/gateway_api/clinical_jwt/jwt.py | Remove mypy ignore around pyjwt.encode call. |
| gateway-api/src/gateway_api/app.py | Use GetStructuredRecordResponse to build final Flask response and mirror headers. |
| gateway-api/src/fhir/stu3/elements/test_elements.py | Add tests for STU3 Parameters model validation/immutability. |
| gateway-api/src/fhir/stu3/elements/parameters.py | Add STU3 Parameters resource model. |
| gateway-api/src/fhir/stu3/elements/init.py | Package init placeholder for STU3 elements. |
| gateway-api/src/fhir/stu3/init.py | Export STU3 Parameters. |
| gateway-api/src/fhir/resources/test_resource.py | Add tests for Resource dispatch, Meta, and dump behaviour. |
| gateway-api/src/fhir/resources/resource.py | Introduce base Resource and Meta implementation with subtype dispatch. |
| gateway-api/src/fhir/resources/init.py | Package init placeholder for shared resource infrastructure. |
| gateway-api/src/fhir/r4/resources/test_resources.py | Add tests for new R4 resources and helper behaviours. |
| gateway-api/src/fhir/r4/resources/py.typed | Mark fhir.r4.resources as typed for type checkers. |
| gateway-api/src/fhir/r4/resources/patient.py | Add typed R4 Patient model with NHS number / GP ODS helpers. |
| gateway-api/src/fhir/r4/resources/operation_outcome.py | Add typed R4 OperationOutcome model. |
| gateway-api/src/fhir/r4/resources/endpoint.py | Add typed R4 Endpoint model. |
| gateway-api/src/fhir/r4/resources/device.py | Add typed R4 Device model. |
| gateway-api/src/fhir/r4/resources/bundle.py | Add typed R4 Bundle model with find_resources helper. |
| gateway-api/src/fhir/r4/resources/init.py | Package init placeholder for R4 resources. |
| gateway-api/src/fhir/r4/elements/test_elements.py | Add tests for new R4 element types and validation expectations. |
| gateway-api/src/fhir/r4/elements/reference.py | Add typed R4 Reference base model with reference-type validation. |
| gateway-api/src/fhir/r4/elements/py.typed | Mark fhir.r4.elements as typed for type checkers. |
| gateway-api/src/fhir/r4/elements/issue.py | Add issue severity/code enums and Issue element dataclass. |
| gateway-api/src/fhir/r4/elements/identifier.py | Add Identifier/UUID/NHS-number identifier element types. |
| gateway-api/src/fhir/r4/elements/init.py | Package init placeholder for R4 elements. |
| gateway-api/src/fhir/r4/init.py | Export R4 elements/resources for simplified imports. |
| gateway-api/src/fhir/period.py | Remove legacy TypedDict Period type. |
| gateway-api/src/fhir/patient.py | Remove legacy TypedDict Patient type. |
| gateway-api/src/fhir/parameters.py | Remove legacy TypedDict Parameters type. |
| gateway-api/src/fhir/operation_outcome.py | Remove legacy TypedDict OperationOutcome type. |
| gateway-api/src/fhir/identifier.py | Remove legacy TypedDict Identifier type. |
| gateway-api/src/fhir/human_name.py | Remove legacy TypedDict HumanName type. |
| gateway-api/src/fhir/general_practitioner.py | Remove legacy TypedDict GeneralPractitioner type. |
| gateway-api/src/fhir/bundle.py | Remove legacy TypedDict Bundle type. |
| gateway-api/src/fhir/init.py | Repoint fhir package to export the new Resource base. |
| gateway-api/src/fhir/README.md | Document the new typed FHIR approach and version split (STU3 inbound vs R4 internal). |
| gateway-api/openapi.yaml | Constrain request identifier.system to the NHS-number URI via enum. |
| gateway-api/pyproject.toml | Add Pydantic dependency; remove pytest ini options section and a dev dependency. |
| Makefile | Adjust wheel install platforms for build target. |
| .vscode/cspell-dictionary.txt | Add “searchset” to workspace spell dictionary. |
| .github/workflows/preview-env.yml | Attempt to add SBOM generation step to preview workflow. |
Comments suppressed due to low confidence (2)
.github/workflows/preview-env.yml:443
- The “Generate SBOM” step has two
uses:keys (sbom-scan and image-scan). GitHub Actions steps can only have a singleuses(orrun), so this will make the workflow YAML invalid. Split this into two separate steps (one for sbom-scan, one for image-scan) or remove the unintendedusesline.
- name: Generate SBOM
uses: nhs-england-tools/trivy-action/sbom-scan@289984b2f03034233a347d6dbadecd5ca9ea9634
if: github.event.action != 'closed'
uses: nhs-england-tools/trivy-action/image-scan@289984b2f03034233a347d6dbadecd5ca9ea9634
with:
image-ref: ${{steps.meta.outputs.ecr_url}}:${{steps.meta.outputs.branch_name}}
artifact-name: trivy-sbom-${{ steps.meta.outputs.branch_name }}
gateway-api/pyproject.toml:65
[tool.pytest.ini_options]has been removed, but acceptance tests use pytest-bdd scenarios referencing feature files by name only (e.g.@scenario("happy_path.feature", ...)) while the feature lives undertests/acceptance/features/. Withoutbdd_features_base_dir, pytest-bdd won’t resolve these feature files and the acceptance suite will fail. Reintroduce the pytest ini options (at leastbdd_features_base_dir = "tests/acceptance/features", and ideally the marker registrations that were removed).
[tool.mypy]
strict = true
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| class PatientIdentifier( | ||
| Identifier, expected_system="https://fhir.nhs.uk/Id/nhs-number" | ||
| ): | ||
| """A FHIR R4 Patient Identifier utilising the NHS Number system.""" | ||
|
|
||
| def __init__(self, value: str): | ||
| super().__init__(value=value, system=self._expected_system) | ||
|
|
||
| @classmethod | ||
| def from_nhs_number(cls, nhs_number: str) -> "Patient.PatientIdentifier": | ||
| """Create a PatientIdentifier from an NHS number.""" | ||
| return cls(value=nhs_number) | ||
|
|
| # provided a generic dictonary object, delegate to the normal handler. | ||
| if cls != Resource or not isinstance(value, dict): |
| def encode(self) -> str: | ||
| return pyjwt.encode( | ||
| self.payload(), | ||
| key=None, # type: ignore[arg-type] | ||
| key=None, | ||
| algorithm=self.algorithm, | ||
| headers={"typ": self.type}, | ||
| ) |
| @dataclass(frozen=True) | ||
| class Identifier(ABC): | ||
| """ | ||
| A FHIR R4 Identifier element. See https://hl7.org/fhir/R4/datatypes.html#Identifier. | ||
| Attributes: | ||
| system: The namespace for the identifier value. | ||
| value: The value that is unique within the system. | ||
| """ | ||
|
|
||
| _expected_system: ClassVar[str] = "__unknown__" | ||
|
|
||
| value: str | ||
| system: str | ||
|
|
||
| @model_validator(mode="after") | ||
| def validate_system(self) -> "Identifier": | ||
| if self.system != self._expected_system: | ||
| raise ValueError( | ||
| f"Identifier system '{self.system}' does not match expected " | ||
| f"system '{self._expected_system}'." | ||
| ) | ||
| return self | ||
|
|
||
| @classmethod | ||
| def __init_subclass__(cls, expected_system: str) -> None: | ||
| cls._expected_system = expected_system | ||
|
|
| def __init__(self, value: uuid.UUID | None = None): | ||
| super().__init__( | ||
| value=str(value or uuid.uuid4()), | ||
| system=self._expected_system, |
There was a problem hiding this comment.
Using the Patient model enabled logic to be removed from this file:
_get_gp_ods_codeimplemented finding the first GP practice. For a PDS FHIR response there will only be at most one GP practice, the current one. As such,Patient.gp_ods_codewill return the current GP practice orNone, mimicking this behaviour with less code..- Using
Patientremoves the need to usePDSSearchResults. - Given we will only ever be retrieving a single PDS record with
GET /Patient/<nhs_number>, I have also removed thePdsClient._extract_single_search_result()method and havePdsClient.search_patient_by_nhs_number()simply return the Patient model-validated body.
|



Description
Pathology have created a lightweight FHIR package using Pydantic. Given we are using similar FHIR resources/elements, we will create a common package. This is the first step towards that: moving away from TypedDicts and copy-pastaing Pathology's package, before moving it in to the common repo.
Alongside this change I have actioned a PR comment from @nhsd-jack-wainwright that fitted well with this change - namely, having a response class,
GetStructuredRecordResponsethat handles all the response logic.Context
To reduce duplication of code across the products/teams, and to save reinventing the wheel multiple times.
Type of changes
Checklist
Sensitive Information Declaration
To ensure the utmost confidentiality and protect your and others privacy, we kindly ask you to NOT including PII (Personal Identifiable Information) / PID (Personal Identifiable Data) or any other sensitive data in this PR (Pull Request) and the codebase changes. We will remove any PR that do contain any sensitive information. We really appreciate your cooperation in this matter.