Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2158076
feat: Introduce Mock PDS Lambda for MNS integration testing
amarauzoma Apr 17, 2026
1f5dfc5
refactor: Simplify MockPdsService and improve error handling
Thomas-Boyle Apr 17, 2026
d7421df
chore: Remove MNS performance testing plan for mocked PDS
Thomas-Boyle Apr 17, 2026
28cac67
Merge remote-tracking branch 'origin/master' into VED-1235-Lambda-to-…
Thomas-Boyle Apr 17, 2026
98dc17b
refactor: Optimize Mock PDS service initialization in lambda_handler
Thomas-Boyle Apr 17, 2026
d7692be
feat: Enhance Mock PDS service with additional coverage reporting and…
Thomas-Boyle Apr 17, 2026
ec65ea0
feat: Add Mock PDS Lambda and ECR repository configuration
Thomas-Boyle Apr 17, 2026
194e8e8
Merge branch 'master' into VED-1235-Lambda-to-mock-PDS-in-Ref
Thomas-Boyle Apr 17, 2026
e18d510
chore: Add module comment for ECR repository in mock PDS configuration
amarauzoma Apr 17, 2026
611e304
Add expected_commit_id input to E2E test workflow for improved commit…
amarauzoma Apr 20, 2026
87d68b3
Add expected_commit_id input to E2E test workflow for improved commit…
amarauzoma Apr 20, 2026
989d10f
Merge branch 'master' into VED-1235-Lambda-to-mock-PDS-in-Ref
Thomas-Boyle Apr 20, 2026
d4a04fa
Remove mock_pds entry from lambda_image_overrides example in deploy-b…
amarauzoma Apr 20, 2026
813a0e0
Add mock_pds_enabled variable to dev environment configuration
Thomas-Boyle Apr 20, 2026
d5d0318
Set ref mns_environment to int
amarauzoma Apr 21, 2026
1fd7c4c
Merge origin/master and resolve workflow/terraform conflicts
amarauzoma Apr 21, 2026
5f97f5f
Merge branch 'master' into VED-1235-Lambda-to-mock-PDS-in-Ref
amarauzoma Apr 21, 2026
b74a32c
Update expected_commit_id to use github.sha in E2E tests
amarauzoma Apr 22, 2026
bdf2fe4
Disable mock PDS in development environment
amarauzoma Apr 22, 2026
aae6283
Refactor PDS service caching logic and update coverage dependency
amarauzoma Apr 29, 2026
ba7d55b
Merge branch 'master' into VED-1235-Lambda-to-mock-PDS-in-Ref
amarauzoma Apr 29, 2026
0dff83c
Update Dockerfile to set environment variables for Poetry installation
amarauzoma Apr 29, 2026
708197e
Refactor Dockerfile to install Poetry using hashed requirements for s…
amarauzoma Apr 29, 2026
3aa57e0
Refactor Dockerfile to improve readability of Poetry installation com…
amarauzoma Apr 29, 2026
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
7 changes: 5 additions & 2 deletions .github/workflows/deploy-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
lambda_build_flags:
Comment thread
amarauzoma marked this conversation as resolved.
description: >
JSON map of lambda_name -> force-build flag.
e.g. {"backend":true,"recordprocessor":true,"ack-backend":false}
e.g. {"backend":true,"recordprocessor":true,"ack-backend":false,"mock_pds":false}
required: false
type: string
default: "{}"
Expand Down Expand Up @@ -73,7 +73,7 @@ on:
lambda_build_flags:
description: >
JSON map of lambda_name -> force-build flag.
e.g. {"backend":true,"recordprocessor":true,"ack-backend":false}
e.g. {"backend":true,"recordprocessor":true,"ack-backend":false,"mock_pds":false}
required: false
type: string
default: "{}"
Expand Down Expand Up @@ -151,6 +151,9 @@ jobs:
- lambda_name: ack-backend
ecr_repository: imms-ackbackend-repo
lambda_dir: ack_backend
- lambda_name: mock_pds
ecr_repository: imms-mock-pds-repo
lambda_dir: mock_pds
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.

does this mean the mock_pds lambda will be created in all envs (including ref)? Dont we only need it for ref?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, it won’t be created in all environments. mock_pds is in the deploy list, but Terraform only creates the Lambda when mock_pds_enabled=true.
That is only true in dev/ref, so it is only created in ref.

uses: ./.github/workflows/deploy-lambda-artifact.yml
with:
lambda_name: ${{ matrix.lambda_name }}
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/pr-deploy-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ jobs:
uses: ./.github/workflows/deploy-backend.yml
with:
apigee_environment: internal-dev
lambda_build_flags: >-
${{ (github.event.action == 'opened' || github.event.action == 'reopened')
&& '{"recordprocessor":true,"ack-backend":true,"mock_pds":true}'
|| '{}' }}
diff_base_sha: ${{ github.event.action == 'synchronize' && github.event.before || github.event.pull_request.base.sha }}
diff_head_sha: ${{ github.event.pull_request.head.sha }}
run_diff_check: ${{ github.event.action == 'synchronize' }}
Expand All @@ -35,15 +39,19 @@ jobs:
include:
- apigee_environment_name: internal-dev
required_test_suite: smoke
require_matching_commit_id: true
- apigee_environment_name: internal-dev-sandbox
required_test_suite: sandbox
require_matching_commit_id: false
uses: ./.github/workflows/run-e2e-automation-tests.yml
with:
apigee_environment: ${{ matrix.apigee_environment_name }}
environment: dev
sub_environment: pr-${{github.event.pull_request.number}}
service_under_test: all
suite_to_run: ${{ matrix.required_test_suite }}
expected_commit_id: ${{ github.sha }}
require_matching_commit_id: ${{ matrix.require_matching_commit_id }}
secrets:
APIGEE_PASSWORD: ${{ secrets.APIGEE_PASSWORD }}
APIGEE_BASIC_AUTH_TOKEN: ${{ secrets.APIGEE_BASIC_AUTH_TOKEN }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pr-teardown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
recordprocessor:imms-recordprocessor-repo
recordforwarder:imms-recordforwarder-repo
ack_backend:imms-ackbackend-repo
mock_pds:imms-mock-pds-repo
redis_sync:imms-redis-sync-repo
permissions:
id-token: write
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/quality-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
env:
SHARED_PATH: ${{ github.workspace }}/lambdas/shared
LAMBDA_PATH: ${{ github.workspace }}/lambdas
POETRY_INSTALLER_ONLY_BINARY: ":all:"

jobs:
lint-specification:
Expand Down Expand Up @@ -184,6 +185,17 @@ jobs:
poetry run coverage run --source=src -m unittest discover || echo "mns_publisher tests failed" >> ../../failed_tests.txt
poetry run coverage xml -o ../../mns_publisher-coverage.xml

- name: Run unittest with coverage-mock-pds
working-directory: lambdas/mock_pds
id: mock_pds
env:
PYTHONPATH: ${{ env.LAMBDA_PATH }}/mock_pds/src
continue-on-error: true
run: |
poetry install
poetry run coverage run --source=src -m unittest discover -s tests -v || echo "mock_pds tests failed" >> ../../failed_tests.txt
poetry run coverage xml -o ../../mock_pds-coverage.xml

- name: Run unittest with coverage-mns-subscription
working-directory: lambdas/mns_subscription
id: mns_subscription
Expand Down
27 changes: 25 additions & 2 deletions .github/workflows/run-e2e-automation-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ on:
suite_to_run:
required: true
type: string
expected_commit_id:
required: false
type: string
default: ""
require_matching_commit_id:
required: false
type: boolean
default: true
secrets:
APIGEE_PASSWORD:
required: true
Expand Down Expand Up @@ -72,6 +80,16 @@ on:
description: Set to true if you want the MNS validation to be performed as part of the tests. please keep in mind it will increase execution time.
default: false
type: boolean
expected_commit_id:
description: Optional commit SHA expected from the deployed _status endpoint.
required: false
type: string
default: ""
require_matching_commit_id:
description: Whether the _status endpoint commitId must match the expected commit SHA.
required: false
type: boolean
default: true

env:
APIGEE_AUTH_ENV: ${{ inputs.apigee_environment == 'int' && inputs.apigee_environment || 'internal-dev' }}
Expand All @@ -81,7 +99,8 @@ env:
SERVICE_BASE_PATH: ${{ startsWith(inputs.sub_environment, 'pr-') && format('immunisation-fhir-api/FHIR/R4-{0}', inputs.sub_environment) || 'immunisation-fhir-api/FHIR/R4' }}
PROXY_NAME: ${{ startsWith(inputs.sub_environment, 'pr-') && format('immunisation-fhir-api-{0}', inputs.sub_environment) || format('immunisation-fhir-api-{0}', inputs.apigee_environment) }}
STATUS_API_KEY: ${{ secrets.STATUS_API_KEY }}
SOURCE_COMMIT_ID: ${{ github.sha }}
SOURCE_COMMIT_ID: ${{ inputs.expected_commit_id || github.sha }}
REQUIRE_MATCHING_COMMIT_ID: ${{ inputs.require_matching_commit_id }}
MNS_VALIDATION_REQUIRED: ${{ inputs.mns_validation_required || startsWith(inputs.sub_environment, 'pr-') || inputs.apigee_environment == 'internal-dev' }}

jobs:
Expand Down Expand Up @@ -112,11 +131,15 @@ jobs:

if [[ "${response_code}" -eq 200 ]] && [[ "${response_body}" == "OK" ]] && [[ "${status}" == "pass" ]]; then
echo "Status test successful"
if [[ "${REQUIRE_MATCHING_COMMIT_ID}" != "true" ]]; then
echo "Skipping commit hash validation for ${APIGEE_ENVIRONMENT}"
break
fi
if [[ "${commitId}" == "${SOURCE_COMMIT_ID}" ]]; then
echo "Commit hash test successful"
break
else
echo "Waiting for ${endpoint} to return the correct commit hash..."
echo "Waiting for ${endpoint} to return the correct commit hash... expected ${SOURCE_COMMIT_ID}, got ${commitId}"
fi
else
echo "Waiting for ${endpoint} to return a 200 response with 'OK' body..."
Expand Down
33 changes: 33 additions & 0 deletions infrastructure/account/mock_pds_ecr_repo.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
resource "aws_ecr_repository" "mock_pds_repository" {
image_scanning_configuration {
scan_on_push = true
}
image_tag_mutability = "IMMUTABLE"
name = "imms-mock-pds-repo"
}
# Module for building and pushing Docker image to ECR
resource "aws_ecr_repository_policy" "mock_pds_repository_lambda_image_retrieval_policy" {
repository = aws_ecr_repository.mock_pds_repository.name

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "LambdaECRImageRetrievalPolicy"
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com"
}
Action = [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
]
Condition = {
StringLike = {
"aws:sourceArn" = "arn:aws:lambda:${var.aws_region}:${var.imms_account_id}:function:imms-*-mock-pds-lambda"
}
}
}
]
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ mns_environment = "dev"
error_alarm_notifications_enabled = false
create_mesh_processor = false
has_sub_environment_scope = true
mock_pds_enabled = false
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ environment = "dev"
immunisation_account_id = "345594581768"
dspp_core_account_id = "603871901111"
pds_environment = "ref"
mns_environment = "dev"
mns_environment = "int"
mock_pds_enabled = true
mock_pds_average_rate_limit = 125
mock_pds_spike_rate_limit = 450
error_alarm_notifications_enabled = true
create_mesh_processor = false
has_sub_environment_scope = true
1 change: 1 addition & 0 deletions infrastructure/instance/id_sync_lambda.tf
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ resource "aws_lambda_function" "id_sync_lambda" {
variables = {
IEDS_TABLE_NAME = aws_dynamodb_table.events-dynamodb-table.name
PDS_ENV = var.pds_environment
PDS_BASE_URL = local.mock_pds_base_url
SPLUNK_FIREHOSE_NAME = module.splunk.firehose_stream_name
}
}
Expand Down
1 change: 1 addition & 0 deletions infrastructure/instance/mns_publisher.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module "mns_publisher" {
secrets_manager_policy_path = "${local.policy_path}/secret_manager.json"
account_id = data.aws_caller_identity.current.account_id
pds_environment = var.pds_environment
pds_base_url = local.mock_pds_base_url
mns_environment = var.mns_environment

private_subnet_ids = local.private_subnet_ids
Expand Down
162 changes: 162 additions & 0 deletions infrastructure/instance/mock_pds.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
locals {
mock_pds_lambda_name = "${local.short_prefix}-mock-pds-lambda"
mock_pds_base_url = var.mock_pds_enabled ? "${aws_lambda_function_url.mock_pds_lambda_url[0].function_url}Patient" : ""
}

resource "aws_iam_role" "mock_pds_lambda_exec_role" {
count = var.mock_pds_enabled ? 1 : 0

name = "${local.mock_pds_lambda_name}-exec-role"
assume_role_policy = jsonencode({
Version = "2012-10-17",
Statement = [{
Effect = "Allow",
Sid = "",
Principal = {
Service = "lambda.amazonaws.com"
},
Action = "sts:AssumeRole"
}]
})
}

resource "aws_iam_policy" "mock_pds_lambda_exec_policy" {
count = var.mock_pds_enabled ? 1 : 0

name = "${local.mock_pds_lambda_name}-exec-policy"
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "arn:aws:logs:${var.aws_region}:${var.immunisation_account_id}:log-group:/aws/lambda/${local.mock_pds_lambda_name}:*"
},
{
Effect = "Allow",
Action = [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
],
Resource = "*"
}
]
})
}

resource "aws_iam_policy" "mock_pds_lambda_kms_access_policy" {
count = var.mock_pds_enabled ? 1 : 0

name = "${local.mock_pds_lambda_name}-kms-policy"
description = "Allow mock PDS Lambda to decrypt environment variables"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"kms:Decrypt"
]
Resource = data.aws_kms_key.existing_lambda_encryption_key.arn
}
]
})
}

resource "aws_iam_role_policy_attachment" "mock_pds_lambda_exec_policy_attachment" {
count = var.mock_pds_enabled ? 1 : 0

role = aws_iam_role.mock_pds_lambda_exec_role[0].name
policy_arn = aws_iam_policy.mock_pds_lambda_exec_policy[0].arn
}

resource "aws_iam_role_policy_attachment" "mock_pds_lambda_kms_policy_attachment" {
count = var.mock_pds_enabled ? 1 : 0

role = aws_iam_role.mock_pds_lambda_exec_role[0].name
policy_arn = aws_iam_policy.mock_pds_lambda_kms_access_policy[0].arn
}

resource "aws_cloudwatch_log_group" "mock_pds_lambda_log_group" {
count = var.mock_pds_enabled ? 1 : 0

name = "/aws/lambda/${local.mock_pds_lambda_name}"
retention_in_days = 30
}

resource "aws_lambda_function" "mock_pds_lambda" {
count = var.mock_pds_enabled ? 1 : 0

function_name = local.mock_pds_lambda_name
role = aws_iam_role.mock_pds_lambda_exec_role[0].arn
package_type = "Image"
image_uri = var.mock_pds_image_uri
architectures = ["x86_64"]
timeout = 30

vpc_config {
subnet_ids = local.private_subnet_ids
security_group_ids = [data.aws_security_group.existing_securitygroup.id]
}

environment {
variables = {
REDIS_HOST = data.aws_elasticache_cluster.existing_redis.cache_nodes[0].address
REDIS_PORT = tostring(data.aws_elasticache_cluster.existing_redis.port)
MOCK_PDS_AVERAGE_LIMIT = tostring(var.mock_pds_average_rate_limit)
MOCK_PDS_AVERAGE_WINDOW_SECONDS = tostring(var.mock_pds_average_window_seconds)
MOCK_PDS_SPIKE_LIMIT = tostring(var.mock_pds_spike_rate_limit)
MOCK_PDS_SPIKE_WINDOW_SECONDS = tostring(var.mock_pds_spike_window_seconds)
MOCK_PDS_GP_ODS_CODE = var.mock_pds_gp_ods_code
}
}

kms_key_arn = data.aws_kms_key.existing_lambda_encryption_key.arn

depends_on = [
aws_cloudwatch_log_group.mock_pds_lambda_log_group,
aws_iam_policy.mock_pds_lambda_exec_policy
]
}

resource "aws_lambda_function_url" "mock_pds_lambda_url" {
count = var.mock_pds_enabled ? 1 : 0

function_name = aws_lambda_function.mock_pds_lambda[0].function_name
authorization_type = "NONE"
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.

why are we doing authorization_type = NONE?
Does this mean this lambda is freely available from the internet?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, authorization_type = "NONE" makes the Function URL public. We used this to keep the mock PDS endpoint simple for lower-env integration/perf testing. It does not expose real PDS data, but you’re right that it is internet-reachable.

}

resource "aws_lambda_permission" "mock_pds_lambda_url_invoke" {
count = var.mock_pds_enabled ? 1 : 0

statement_id = "AllowPublicInvokeFunctionUrl"
action = "lambda:InvokeFunctionUrl"
function_name = aws_lambda_function.mock_pds_lambda[0].function_name
principal = "*"
function_url_auth_type = "NONE"
}

resource "aws_cloudwatch_log_metric_filter" "mock_pds_throttle_logs" {
count = var.mock_pds_enabled ? 1 : 0

name = "${local.short_prefix}-MockPdsThrottleLogs"
pattern = "Mock PDS rate limit exceeded"
log_group_name = aws_cloudwatch_log_group.mock_pds_lambda_log_group[0].name

metric_transformation {
name = "${local.short_prefix}-MockPdsThrottleRequests"
namespace = "${local.short_prefix}-MockPds"
value = "1"
}
}

output "mock_pds_function_url" {
value = var.mock_pds_enabled ? aws_lambda_function_url.mock_pds_lambda_url[0].function_url : null
description = "Function URL for the mock PDS endpoint."
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ resource "aws_lambda_function" "mns_publisher_lambda" {
IMMUNIZATION_ENV = var.resource_scope,
IMMUNIZATION_BASE_PATH = var.imms_base_path
PDS_ENV = var.pds_environment
PDS_BASE_URL = var.pds_base_url
MNS_ENV = var.mns_environment
}
}
Expand Down
Loading
Loading