Skip to content

Conversation

@Vox-Ben
Copy link
Contributor

@Vox-Ben Vox-Ben commented Jan 20, 2026

Description

Creates the controller class that orchestrates calls to the other gateway components and to the GP provider

Context

This module is needed so that we have something that actually calls all the components that we need to call and manages their responses.

Type of changes

  • Refactoring (non-breaking change)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would change existing functionality)
  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have followed the code style of the project
  • I have added tests to cover my changes
  • I have updated the documentation accordingly
  • This PR is a result of pair or mob programming
  • Exceptions/Exclusions to coding standards (e.g. #noqa or #NOSONAR) are included within this Pull Request.

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.

  • I confirm that neither PII/PID nor sensitive data are included in this PR and the codebase changes.

@Vox-Ben Vox-Ben requested a review from a team as a code owner January 20, 2026 16:03
@Vox-Ben Vox-Ben force-pushed the feature/GPCAPIM-255_controller_module branch from 1ab29e7 to fc5b194 Compare January 20, 2026 16:04
@github-actions
Copy link

ALB Target: arn:aws:elasticloadbalancing:eu-west-2:900119715266:targetgroup/gpcapim-255-controller-module/b4084f2f62453ce7
Preview URL: https://feature-gpcapim-255-controller-module.dev.endpoints.clinical-data-gateway.national.nhs.uk

1 similar comment
@github-actions
Copy link

ALB Target: arn:aws:elasticloadbalancing:eu-west-2:900119715266:targetgroup/gpcapim-255-controller-module/b4084f2f62453ce7
Preview URL: https://feature-gpcapim-255-controller-module.dev.endpoints.clinical-data-gateway.national.nhs.uk

@github-actions
Copy link

ALB Target: arn:aws:elasticloadbalancing:eu-west-2:900119715266:targetgroup/gpcapim-255-controller-module/b4084f2f62453ce7
Preview URL: https://feature-gpcapim-255-controller-module.dev.endpoints.clinical-data-gateway.national.nhs.uk

2 similar comments
@github-actions
Copy link

ALB Target: arn:aws:elasticloadbalancing:eu-west-2:900119715266:targetgroup/gpcapim-255-controller-module/b4084f2f62453ce7
Preview URL: https://feature-gpcapim-255-controller-module.dev.endpoints.clinical-data-gateway.national.nhs.uk

@github-actions
Copy link

ALB Target: arn:aws:elasticloadbalancing:eu-west-2:900119715266:targetgroup/gpcapim-255-controller-module/b4084f2f62453ce7
Preview URL: https://feature-gpcapim-255-controller-module.dev.endpoints.clinical-data-gateway.national.nhs.uk


return consumer_asid, provider_asid, provider_endpoint

def call_gp_provider(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this does more than call the provider (the provider module does only that): perhaps orchestrate_gp_provider-call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That feels a bit verbose for what is a main in all but name. How about just run? So using the controller looks like:

controller = Controller(pds_url, sds_url, nhsd_urid, timeout)
patient_details = controller.run(body, headers, auth_token)

Copy link
Contributor

@davidhamill1-nhs davidhamill1-nhs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1329 lines of code is not a small PR.

Comment on lines 314 to 323
c = _make_controller()

# PDS returns None by default
body = make_request_body("9434765919")
headers = make_headers()

r = c.call_gp_provider(body, headers, "token-abc")

assert r.status_code == 404
assert "No PDS patient found for NHS number" in (r.data or "")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given how much is mocked, it feels like this is testing the mocks more than the actual controller.

It is not clear from the test setup why this should return a 404/No patient found.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mocking out PDS, SDS and the GP provider. I don't want to call any of those in the unit test for the controller - that's for the integration tests (which don't exist, because we weren't going to stand up the server to run them against for steel thread). In this case, no patient with the given NHS number has been added to the PDS stub, and therefore when the controller calls the PDS stub it gets nothing back and returns a 404 with the given error message.

Does that make sense? I'll add a comment to explain that a bit more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These feel less like unit tests, testing a unit of code, and more like integration tests.

You could break call_gp_prodvier in to multiple private method calls, eg
Put

try:
            nhs_number = self._get_details_from_body(request_body)
        except RequestError as err:
            return FlaskResponse(
                status_code=err.status_code,
                data=str(err),
            )

        # Extract consumer ODS from headers
        consumer_ods = headers.get("Ods-from", "").strip()
        if not consumer_ods:
            return FlaskResponse(
                status_code=400,
                data='Missing required header "Ods-from"',
            )

        trace_id = headers.get("X-Request-ID")
        if trace_id is None:
            return FlaskResponse(
                status_code=400, data="Missing required header: X-Request-ID"
            )

        try:
            provider_ods = self._get_pds_details(auth_token, consumer_ods, nhs_number)
        except RequestError as err:
            return FlaskResponse(status_code=err.status_code, data=str(err))

in its own method and unit test that. And similarly with the calls to ODS for the provider and the consumer.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are lots of tests for handling error conditions here, but I couldn't actually see a 'happy path' 200 with expected response test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Um. Whoops. Failing to see the wood for the trees there. I've added a happy-path test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the point about breaking it up, I actually did break it up, because Ruff was complaining about cognitive complexity. So what was call_gp_provider (now run) now calls _get_details_from_body, _get_pds_details and _get_sds_details, but overall control is still within that public function. I left the unit tests just testing run though because a) they were already written and b) it seemed to make more sense to me to test the public function, which is where the functionality that we care about (ie. that it calls each in turn and returns the result) lives, than to test the private functions, which exist only for purposes of abstracting out some of the complexity and could be easily changed in future.

@Vox-Ben Vox-Ben force-pushed the feature/GPCAPIM-255_controller_module branch from c39e48c to 58c7e73 Compare January 27, 2026 09:54
@Vox-Ben Vox-Ben force-pushed the feature/GPCAPIM-255_controller_module branch from 58c7e73 to 5966ab0 Compare January 28, 2026 17:01
@sonarqubecloud
Copy link

@github-actions
Copy link

Deployment Complete

@Vox-Ben Vox-Ben mentioned this pull request Jan 28, 2026
10 tasks
@Vox-Ben
Copy link
Contributor Author

Vox-Ben commented Jan 28, 2026

This PR superseded by #59

@Vox-Ben Vox-Ben closed this Jan 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants