Skip to content

Commit b7d90a8

Browse files
CCM-13308: Send reports lambda via MESH
1 parent 5bf915c commit b7d90a8

32 files changed

+1658
-7
lines changed

Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ quick-start: config clean build serve-docs # Quick start target to setup, build
1212
dependencies:: # Install dependencies needed to build and test the project @Pipeline
1313
$(MAKE) -C src/cloudevents install
1414
$(MAKE) -C src/eventcatalogasyncapiimporter install
15-
$(MAKE) -C lambdas/mesh-acknowledge install
1615
$(MAKE) -C utils/py-utils install
16+
$(MAKE) -C utils/py-mock-mesh install
17+
$(MAKE) -C lambdas/mesh-acknowledge install
1718
$(MAKE) -C lambdas/mesh-poll install
1819
$(MAKE) -C lambdas/mesh-download install
19-
$(MAKE) -C utils/py-mock-mesh install
20+
$(MAKE) -C lambdas/report-sender install
2021
./scripts/set-github-token.sh
2122
npm install --workspaces
2223
$(MAKE) generate
@@ -48,6 +49,7 @@ clean:: # Clean-up project resources (main) @Operations
4849
$(MAKE) -C utils/py-utils clean && \
4950
$(MAKE) -C lambdas/mesh-poll clean && \
5051
$(MAKE) -C lambdas/mesh-download clean && \
52+
$(MAKE) -C lambdas/report-sender clean && \
5153
$(MAKE) -C utils/py-mock-mesh clean && \
5254
$(MAKE) -C src/python-schema-generator clean && \
5355
rm -f .version

infrastructure/terraform/components/dl/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ No requirements.
6666
| <a name="module_print_status_handler"></a> [print\_status\_handler](#module\_print\_status\_handler) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
6767
| <a name="module_report_event_transformer"></a> [report\_event\_transformer](#module\_report\_event\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
6868
| <a name="module_report_scheduler"></a> [report\_scheduler](#module\_report\_scheduler) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
69+
| <a name="module_report_sender"></a> [report\_sender](#module\_report\_sender) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
6970
| <a name="module_s3bucket_cf_logs"></a> [s3bucket\_cf\_logs](#module\_s3bucket\_cf\_logs) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-s3bucket.zip | n/a |
7071
| <a name="module_s3bucket_file_quarantine"></a> [s3bucket\_file\_quarantine](#module\_s3bucket\_file\_quarantine) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-s3bucket.zip | n/a |
7172
| <a name="module_s3bucket_file_safe"></a> [s3bucket\_file\_safe](#module\_s3bucket\_file\_safe) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-s3bucket.zip | n/a |
@@ -84,6 +85,7 @@ No requirements.
8485
| <a name="module_sqs_print_analyser"></a> [sqs\_print\_analyser](#module\_sqs\_print\_analyser) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip | n/a |
8586
| <a name="module_sqs_print_sender"></a> [sqs\_print\_sender](#module\_sqs\_print\_sender) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip | n/a |
8687
| <a name="module_sqs_print_status_handler"></a> [sqs\_print\_status\_handler](#module\_sqs\_print\_status\_handler) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip | n/a |
88+
| <a name="module_sqs_report_sender"></a> [sqs\_report\_sender](#module\_sqs\_report\_sender) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip | n/a |
8789
| <a name="module_sqs_scanner"></a> [sqs\_scanner](#module\_sqs\_scanner) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip | n/a |
8890
| <a name="module_sqs_ttl"></a> [sqs\_ttl](#module\_sqs\_ttl) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip | n/a |
8991
| <a name="module_sqs_ttl_handle_expiry_errors"></a> [sqs\_ttl\_handle\_expiry\_errors](#module\_sqs\_ttl\_handle\_expiry\_errors) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip | n/a |
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
resource "aws_cloudwatch_event_rule" "report_generated" {
2+
name = "${local.csi}-report-generated"
3+
description = "Route ReportGenerated events from report-generation lambda to report-sender queue"
4+
event_bus_name = aws_cloudwatch_event_bus.main.name
5+
6+
event_pattern = jsonencode({
7+
"detail" : {
8+
"type" : [
9+
"uk.nhs.notify.digital.letters.reporting.report.generated.v1"
10+
]
11+
}
12+
})
13+
}
14+
15+
# EventBridge target to send events to SQS queue
16+
resource "aws_cloudwatch_event_target" "report_sender_sqs" {
17+
rule = aws_cloudwatch_event_rule.report_generated.name
18+
arn = module.sqs_report_sender.sqs_queue_arn
19+
event_bus_name = aws_cloudwatch_event_bus.main.name
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
resource "aws_lambda_event_source_mapping" "report_sender" {
2+
event_source_arn = module.sqs_report_sender.sqs_queue_arn
3+
function_name = module.report_sender.function_name
4+
batch_size = var.queue_batch_size
5+
maximum_batching_window_in_seconds = var.queue_batch_window_seconds
6+
7+
function_response_types = [
8+
"ReportBatchItemFailures"
9+
]
10+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
module "report_sender" {
2+
source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip"
3+
4+
function_name = "report-sender"
5+
description = "A lambda function for sending reports to Trusts via MESH messages"
6+
aws_account_id = var.aws_account_id
7+
component = local.component
8+
environment = var.environment
9+
project = var.project
10+
region = var.region
11+
group = var.group
12+
13+
log_retention_in_days = var.log_retention_in_days
14+
kms_key_arn = module.kms.key_arn
15+
16+
iam_policy_document = {
17+
body = data.aws_iam_policy_document.report_sender_lambda.json
18+
}
19+
20+
function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
21+
function_code_base_path = local.aws_lambda_functions_dir_path
22+
function_code_dir = "report-sender/target/dist"
23+
function_include_common = true
24+
function_module_name = "report_sender"
25+
handler_function_name = "handler.handler"
26+
runtime = "python3.14"
27+
memory = 128
28+
timeout = 5
29+
log_level = var.log_level
30+
31+
force_lambda_code_deploy = var.force_lambda_code_deploy
32+
enable_lambda_insights = false
33+
34+
log_destination_arn = local.log_destination_arn
35+
log_subscription_role_arn = local.acct.log_subscription_role_arn
36+
37+
lambda_env_vars = {
38+
REPORT_SENDER_METRIC_NAME = "report-sender-successful-sends"
39+
REPORT_SENDER_METRIC_NAMESPACE = "dl-report-sender"
40+
DLQ_URL = module.sqs_report_sender.sqs_dlq_url
41+
ENVIRONMENT = var.environment
42+
EVENT_PUBLISHER_DLQ_URL = module.sqs_event_publisher_errors.sqs_queue_url
43+
EVENT_PUBLISHER_EVENT_BUS_ARN = aws_cloudwatch_event_bus.main.arn
44+
MOCK_MESH_BUCKET = module.s3bucket_non_pii_data.bucket
45+
SSM_MESH_PREFIX = "${local.ssm_mesh_prefix}"
46+
SSM_SENDERS_PREFIX = "${local.ssm_senders_prefix}"
47+
USE_MESH_MOCK = var.enable_mock_mesh ? "true" : "false"
48+
}
49+
50+
}
51+
52+
data "aws_iam_policy_document" "report_sender_lambda" {
53+
statement {
54+
sid = "KMSPermissions"
55+
effect = "Allow"
56+
57+
actions = [
58+
"kms:Decrypt",
59+
"kms:GenerateDataKey",
60+
]
61+
62+
resources = [
63+
module.kms.key_arn,
64+
]
65+
}
66+
67+
statement {
68+
sid = "SQSPermissions"
69+
effect = "Allow"
70+
71+
actions = [
72+
"sqs:ReceiveMessage",
73+
"sqs:DeleteMessage",
74+
"sqs:GetQueueAttributes",
75+
]
76+
77+
resources = [
78+
module.sqs_report_sender.sqs_queue_arn,
79+
]
80+
}
81+
82+
statement {
83+
sid = "SQSDLQPermissions"
84+
effect = "Allow"
85+
86+
actions = [
87+
"sqs:SendMessage",
88+
]
89+
90+
resources = [
91+
module.sqs_report_sender.sqs_dlq_arn,
92+
]
93+
}
94+
95+
statement {
96+
sid = "EventBridgePermissions"
97+
effect = "Allow"
98+
99+
actions = [
100+
"events:PutEvents",
101+
]
102+
103+
resources = [
104+
aws_cloudwatch_event_bus.main.arn,
105+
]
106+
}
107+
108+
statement {
109+
sid = "DLQPermissions"
110+
effect = "Allow"
111+
112+
actions = [
113+
"sqs:SendMessage",
114+
"sqs:SendMessageBatch",
115+
]
116+
117+
resources = [
118+
module.sqs_event_publisher_errors.sqs_queue_arn,
119+
]
120+
}
121+
122+
statement {
123+
sid = "SSMPermissions"
124+
effect = "Allow"
125+
126+
actions = [
127+
"ssm:GetParameter",
128+
"ssm:GetParametersByPath",
129+
]
130+
131+
resources = [
132+
"arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter${local.ssm_prefix}/*"
133+
]
134+
}
135+
136+
statement {
137+
sid = "S3BucketPermissions"
138+
effect = "Allow"
139+
140+
actions = [
141+
"s3:GetObject",
142+
]
143+
144+
resources = [
145+
"${module.s3bucket_reporting.arn}/*",
146+
]
147+
}
148+
149+
# Grant S3 PutObject permissions for the mock-mesh directory only when the mock is enabled
150+
dynamic "statement" {
151+
for_each = var.enable_mock_mesh ? [1] : []
152+
content {
153+
sid = "MockMeshPutObject"
154+
effect = "Allow"
155+
156+
actions = [
157+
"s3:PutObject",
158+
]
159+
160+
resources = [
161+
"${module.s3bucket_non_pii_data.arn}/mock-mesh/*"
162+
]
163+
}
164+
}
165+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module "sqs_report_sender" {
2+
source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.30/terraform-sqs.zip"
3+
4+
aws_account_id = var.aws_account_id
5+
component = local.component
6+
environment = var.environment
7+
project = var.project
8+
region = var.region
9+
name = "report-sender"
10+
11+
sqs_kms_key_arn = module.kms.key_arn
12+
13+
visibility_timeout_seconds = 60
14+
15+
create_dlq = true
16+
17+
sqs_policy_overload = data.aws_iam_policy_document.sqs_report_sender.json
18+
}
19+
20+
data "aws_iam_policy_document" "sqs_report_sender" {
21+
statement {
22+
sid = "AllowEventBridgeToSendMessage"
23+
effect = "Allow"
24+
25+
principals {
26+
type = "Service"
27+
identifiers = ["events.amazonaws.com"]
28+
}
29+
30+
actions = [
31+
"sqs:SendMessage"
32+
]
33+
34+
resources = [
35+
"arn:aws:sqs:${var.region}:${var.aws_account_id}:${local.csi}-report-sender-queue"
36+
]
37+
38+
condition {
39+
test = "ArnLike"
40+
variable = "aws:SourceArn"
41+
values = [aws_cloudwatch_event_rule.report_generated.arn]
42+
}
43+
}
44+
}

infrastructure/terraform/components/dl/pre.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
2626
make -C "$ROOT/lambdas/mesh-acknowledge" package
2727
make -C "$ROOT/lambdas/mesh-poll" package
2828
make -C "$ROOT/lambdas/mesh-download" package
29+
make -C "$ROOT/lambdas/report-sender" package

lambdas/report-sender/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__
2+
.venv

lambdas/report-sender/Makefile

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
PACKAGE=report_sender
2+
VERSION=0.1.0
3+
4+
install:
5+
pip install -r requirements.txt
6+
7+
install-dev:
8+
pip install -r requirements-dev.txt
9+
10+
test:
11+
cd ../.. && PYTHONPATH=lambdas/report-sender:$$PYTHONPATH pytest lambdas/report-sender/report_sender/__tests__/ -v
12+
13+
coverage:
14+
cd ../.. && PYTHONPATH=lambdas/report-sender:$$PYTHONPATH pytest lambdas/report-sender/report_sender/__tests__/ \
15+
--cov=lambdas/report-sender/report_sender \
16+
--cov-config=lambdas/report-sender/pytest.ini \
17+
--cov-report=html:lambdas/report-sender/htmlcov \
18+
--cov-report=term-missing \
19+
--cov-report=xml:lambdas/report-sender/coverage.xml \
20+
--cov-branch
21+
22+
lint:
23+
pylint report_sender
24+
25+
format:
26+
autopep8 -ri .
27+
28+
package:
29+
./package_python_lambda.sh report_sender
30+
31+
clean:
32+
rm -rf target
33+
34+
.PHONY: install install-dev test coverage lint format package clean
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
component_name="$1"
5+
6+
rootdir=$(realpath "$(dirname "$0")/../..")
7+
source ${rootdir}/utils/get_version.sh
8+
9+
dist_dir="${PWD}/target/dist"
10+
rm -rf "${dist_dir}"
11+
mkdir -p "${dist_dir}"
12+
13+
# Extract internal (file://) and external dependencies from requirements.txt
14+
grep -E '^-e ' requirements.txt | sed 's|^-e ||' > target/internal_requirements.txt || true
15+
grep -vE '^-e ' requirements.txt > target/external_requirements.txt || true
16+
17+
# Install external dependencies (from PyPI)
18+
pip install --platform manylinux2014_x86_64 --only-binary=:all: -r target/external_requirements.txt --target ${dist_dir} --python-version 3.14 --implementation cp
19+
20+
# Install internal dependencies (local packages)
21+
pip install -r target/internal_requirements.txt --target ${dist_dir}
22+
23+
# Bundle application code
24+
pip install . --no-deps --target ${dist_dir}

0 commit comments

Comments
 (0)