Skip to content

Feature/cdapi 71 jw#68

Open
nhsd-jack-wainwright wants to merge 1 commit intomainfrom
feature/CDAPI-71-JW
Open

Feature/cdapi 71 jw#68
nhsd-jack-wainwright wants to merge 1 commit intomainfrom
feature/CDAPI-71-JW

Conversation

@nhsd-jack-wainwright
Copy link
Copy Markdown
Collaborator

@nhsd-jack-wainwright nhsd-jack-wainwright commented Mar 17, 2026

Description

Context

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.

Base automatically changed from feature/CDAPI-85 to main March 19, 2026 18:46
@MohammadPatelNHS MohammadPatelNHS force-pushed the feature/CDAPI-71-JW branch 4 times, most recently from 89ed95e to 75d0e29 Compare March 27, 2026 09:42
@MohammadPatelNHS MohammadPatelNHS marked this pull request as ready for review March 27, 2026 09:43
@MohammadPatelNHS MohammadPatelNHS requested a review from a team as a code owner March 27, 2026 09:43
Copy link
Copy Markdown
Collaborator Author

@nhsd-jack-wainwright nhsd-jack-wainwright left a comment

Choose a reason for hiding this comment

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

LGTM 👍. Just a few minor comments / suggestions 🙂


COPY /resources/build/mocks ${LAMBDA_TASK_ROOT}

# RUN mkdir -p /.aws
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Should this commented out logic be removed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

removed this,I think we will have a proper way of including or grabbing credentials in the future

KeyConditionExpression=Key("ddb_index").eq(BRANCH_NAME),
FilterExpression=Attr("access_token").eq(token)
& Attr("expiresAt").gt(int(time())),
def check_authenticated(token: str) -> bool:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Rather than returning a boolean here, I wonder if it would be nicer to return some sort of typed exception that's declared within the mock logic (maybe something like AuthenticatorError for example). That way we could add in an exception handler to provide the standard APIM response within the top level handler, rather than each mock endpoint needing to define this response?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This function no longer returns anything but will raise an exception when not authenticated, which gets caught by a handler exception at the lambda handler level

pass


class DynamoHelper:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Very minor, but I wonder if its worth renaming this helper just to abstract away that we're using dynamo for storage? Maybe it could just be something like StorageHelper?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this have been renamed now


return cast("BaseMockItem", response.get("Item"))

def query_by_ddb_index(self, filter_expression: Any) -> list[BaseMockItem]:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

As above with the class name, is it worth renaming this method to be something that's more generic? Maybe this method could just be called find for example that accepts an expression?

I wonder if it's worth always appending the TTL check here also as I think we'd always want to apply that condition as part of looking something up?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have renamed the function and put a ttl check inside the function to be appended to any filter expression we use

self.branch_name = branch_name
self.table_resource_created = False

def _set_up_resource(self) -> None:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Could this logic not be included as part of the constructor call above? That way other methods within this class don't need to worry about making sure this method is called?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have changed the way we create the resource now

Comment on lines +150 to +152
auth_token = pdm_routes.current_event.headers.get("Authorization", "").replace(
"Bearer ", ""
)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

As this logic will be common across all authentication, I think it's worth including this logic as part of the check_authenticated method.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I have moved this logic into check_authenticated

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I don't think this module should be required, as I think the same logging module should be used across all mocks within the lambda. Could the existing logging module as part of the apim_mock instead be moved up a level so it's at the root of the mock? That way each mock can just depend on the same module.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the logging setup is now part of the common module

}

# @patch("boto3.resource")
# def test_get_request_no_document_found(
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think this test should be included?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I originally thought this test was a bit redundant but its different from the other one I've written from the lambda_handler level. Ive reinstated this test

packages = [
{include = "apim_mock", from = "src"},
{include = "pdm_mock", from = "src"},
{include = "aws_helpers", from = "src"}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Maybe instead of having a aws_helpers package here we could instead have a common package which then includes the following modules:

  • storage - Includes the current DynamoHelper class
  • logging - Includes the logger configuration so it can be reused across all mocks

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

package has been renamed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Now that we've separated out the endpoints for individual mocks out of the top level lambda_handler I wonder if these tests can be a bit simpler? I think these tests only really need to verify the behaviour housed within the lambda_handler.py file itself, perhaps also verifying that the routers from the individual mock modules are registered?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the tests in this file have been moved to the relevant handler files

@MohammadPatelNHS MohammadPatelNHS requested a review from a team as a code owner April 10, 2026 09:24
Copy link
Copy Markdown
Collaborator Author

@nhsd-jack-wainwright nhsd-jack-wainwright left a comment

Choose a reason for hiding this comment

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

Looks good to me 👍, just a couple extra minor comments.


class TestStorageHelper:
# @patch("boto3.resource")
# def test_resource_set_up_once(self, mock_boto_resource: MagicMock) -> None:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Should this commented out test be included?


self.table = self.dynamodb.Table(self.table_name)

def get_item_by_session_id(self, session_id: str) -> BaseMockItem:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think this method could still be implemented with a generic?

Suggested change
def get_item_by_session_id(self, session_id: str) -> BaseMockItem:
def get_item_by_session_id[T: BaseMockItem](self, session_id: str, required_type: type[T]) -> T:
...
item = response.get("Item")
if not isinstance(item, required_type):
raise ItemNotFoundException(f"Item found but was not expected type. item={item}")
return item

@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown

Deployment Complete

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.

2 participants