Skip to content

Commit acec615

Browse files
CCM-12875: Initial setup for APIM PDM Mock
1 parent bfe8272 commit acec615

21 files changed

Lines changed: 8643 additions & 2 deletions

.gitleaksignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ cd9c0efec38c5d63053dd865e5d4e207c0760d91:docs/guides/Perform_static_analysis.md:
99
0cbdddbf54927dfc4a226b88d2fed045dfc0cb92:docs/cloudevents/digital-letters/2025-10/example-events/file-deleted-event.md:ipv4:42
1010
0cbdddbf54927dfc4a226b88d2fed045dfc0cb92:docs/cloudevents/digital-letters/2025-10/example-events/file-deleted-event.json:ipv4:21
1111
52386bca9b88c0c8c7dee52ab0ec69d04fb72c46:src/TESTING_PLAN.md:ipv4:145
12+
973c8a1feb76f3cd8743ce27b14e4acc4252240c:src/TESTING_PLAN.md:ipv4:2507
13+
cc74128e4207833109339e96f3aaebf3cd40dd65:src/TESTING_PLAN.md:ipv4:2507
14+
791619daf5af4806da7266fa301c0e82145b6de8:src/TESTING_PLAN.md:ipv4:2507
15+
d1c0a37078cbed4fbedae044e5cbafac71717af0:utils/utils/src/__tests__/key-generation/validate-private-key.test.ts:private-key:7
16+
d1c0a37078cbed4fbedae044e5cbafac71717af0:utils/utils/src/__tests__/key-generation/get-private-key.test.ts:private-key:23
17+
d1c0a37078cbed4fbedae044e5cbafac71717af0:utils/utils/src/__tests__/key-generation/get-private-key.test.ts:private-key:30
18+
d1c0a37078cbed4fbedae044e5cbafac71717af0:utils/utils/src/__tests__/key-generation/get-private-key.test.ts:private-key:46

infrastructure/terraform/components/dl/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ No requirements.
2121
| <a name="input_log_retention_in_days"></a> [log\_retention\_in\_days](#input\_log\_retention\_in\_days) | The retention period in days for the Cloudwatch Logs events to be retained, default of 0 is indefinite | `number` | `0` | no |
2222
| <a name="input_mesh_poll_schedule"></a> [mesh\_poll\_schedule](#input\_mesh\_poll\_schedule) | Schedule to poll MESH for messages | `string` | `"cron(0,30 8-16 ? * MON-FRI *)"` | no |
2323
| <a name="input_parent_acct_environment"></a> [parent\_acct\_environment](#input\_parent\_acct\_environment) | Name of the environment responsible for the acct resources used, affects things like DNS zone. Useful for named dev environments | `string` | `"main"` | no |
24+
| <a name="input_pdm_access_token_ssm_path"></a> [pdm\_access\_token\_ssm\_path](#input\_pdm\_access\_token\_ssm\_path) | SSM Parameter path for the PDM API access token | `string` | `"/digital-letters/pdm/access-token"` | no |
25+
| <a name="input_pdm_mock_access_token"></a> [pdm\_mock\_access\_token](#input\_pdm\_mock\_access\_token) | Mock access token for PDM API authentication (used in local/dev environments) | `string` | `"mock-pdm-token"` | no |
26+
| <a name="input_pdm_use_non_mock_token"></a> [pdm\_use\_non\_mock\_token](#input\_pdm\_use\_non\_mock\_token) | Whether to use the SSM parameter token instead of the mock token | `bool` | `false` | no |
2427
| <a name="input_project"></a> [project](#input\_project) | The name of the tfscaffold project | `string` | n/a | yes |
2528
| <a name="input_queue_batch_size"></a> [queue\_batch\_size](#input\_queue\_batch\_size) | maximum number of queue items to process | `number` | `10` | no |
2629
| <a name="input_queue_batch_window_seconds"></a> [queue\_batch\_window\_seconds](#input\_queue\_batch\_window\_seconds) | maximum time in seconds between processing events | `number` | `10` | no |
@@ -33,6 +36,7 @@ No requirements.
3336
|------|--------|---------|
3437
| <a name="module_kms"></a> [kms](#module\_kms) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-kms.zip | n/a |
3538
| <a name="module_mesh_poll"></a> [mesh\_poll](#module\_mesh\_poll) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
39+
| <a name="module_pdm_mock_api"></a> [pdm\_mock\_api](#module\_pdm\_mock\_api) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
3640
| <a name="module_s3bucket_letters"></a> [s3bucket\_letters](#module\_s3bucket\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-s3bucket.zip | n/a |
3741
| <a name="module_sqs_event_publisher_errors"></a> [sqs\_event\_publisher\_errors](#module\_sqs\_event\_publisher\_errors) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-sqs.zip | n/a |
3842
| <a name="module_sqs_ttl"></a> [sqs\_ttl](#module\_sqs\_ttl) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-sqs.zip | n/a |
@@ -43,6 +47,8 @@ No requirements.
4347
| Name | Description |
4448
|------|-------------|
4549
| <a name="output_deployment"></a> [deployment](#output\_deployment) | Deployment details used for post-deployment scripts |
50+
| <a name="output_pdm_mock_api_endpoint"></a> [pdm\_mock\_api\_endpoint](#output\_pdm\_mock\_api\_endpoint) | The base URL of the PDM Mock API |
51+
| <a name="output_pdm_mock_api_id"></a> [pdm\_mock\_api\_id](#output\_pdm\_mock\_api\_id) | The ID of the PDM Mock API Gateway |
4652
<!-- vale on -->
4753
<!-- markdownlint-enable -->
4854
<!-- END_TF_DOCS -->
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# API Gateway REST API for PDM Mock
2+
resource "aws_api_gateway_rest_api" "pdm_mock" {
3+
name = "${var.project}-${var.environment}-pdm-mock-api"
4+
description = "PDM Mock API for testing integration with Patient Data Manager"
5+
6+
endpoint_configuration {
7+
types = ["REGIONAL"]
8+
}
9+
10+
tags = {
11+
Name = "${var.project}-${var.environment}-pdm-mock-api"
12+
Project = var.project
13+
Environment = var.environment
14+
Component = local.component
15+
}
16+
}
17+
18+
# /resource path
19+
resource "aws_api_gateway_resource" "resource" {
20+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
21+
parent_id = aws_api_gateway_rest_api.pdm_mock.root_resource_id
22+
path_part = "resource"
23+
}
24+
25+
# /resource/{id} path
26+
resource "aws_api_gateway_resource" "resource_id" {
27+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
28+
parent_id = aws_api_gateway_resource.resource.id
29+
path_part = "{id}"
30+
}
31+
32+
# POST /resource - Create resource
33+
resource "aws_api_gateway_method" "create_resource" {
34+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
35+
resource_id = aws_api_gateway_resource.resource.id
36+
http_method = "POST"
37+
authorization = "NONE"
38+
}
39+
40+
resource "aws_api_gateway_integration" "create_resource" {
41+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
42+
resource_id = aws_api_gateway_resource.resource.id
43+
http_method = aws_api_gateway_method.create_resource.http_method
44+
45+
integration_http_method = "POST"
46+
type = "AWS_PROXY"
47+
uri = module.pdm_mock_api.lambda_invoke_arn
48+
}
49+
50+
# GET /resource/{id} - Get resource
51+
resource "aws_api_gateway_method" "get_resource" {
52+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
53+
resource_id = aws_api_gateway_resource.resource_id.id
54+
http_method = "GET"
55+
authorization = "NONE"
56+
}
57+
58+
resource "aws_api_gateway_integration" "get_resource" {
59+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
60+
resource_id = aws_api_gateway_resource.resource_id.id
61+
http_method = aws_api_gateway_method.get_resource.http_method
62+
63+
integration_http_method = "POST"
64+
type = "AWS_PROXY"
65+
uri = module.pdm_mock_api.lambda_invoke_arn
66+
}
67+
68+
# Lambda permission for API Gateway
69+
resource "aws_lambda_permission" "pdm_mock_api_gateway" {
70+
statement_id = "AllowAPIGatewayInvoke"
71+
action = "lambda:InvokeFunction"
72+
function_name = module.pdm_mock_api.lambda_function_name
73+
principal = "apigateway.amazonaws.com"
74+
75+
# More specific source ARN for better security
76+
source_arn = "${aws_api_gateway_rest_api.pdm_mock.execution_arn}/*/*"
77+
}
78+
79+
# Deployment
80+
resource "aws_api_gateway_deployment" "pdm_mock" {
81+
depends_on = [
82+
aws_api_gateway_integration.create_resource,
83+
aws_api_gateway_integration.get_resource,
84+
]
85+
86+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
87+
88+
triggers = {
89+
redeployment = sha1(jsonencode([
90+
aws_api_gateway_resource.resource.id,
91+
aws_api_gateway_resource.resource_id.id,
92+
aws_api_gateway_method.create_resource.id,
93+
aws_api_gateway_method.get_resource.id,
94+
aws_api_gateway_integration.create_resource.id,
95+
aws_api_gateway_integration.get_resource.id,
96+
]))
97+
}
98+
99+
lifecycle {
100+
create_before_destroy = true
101+
}
102+
}
103+
104+
# Stage
105+
resource "aws_api_gateway_stage" "pdm_mock" {
106+
deployment_id = aws_api_gateway_deployment.pdm_mock.id
107+
rest_api_id = aws_api_gateway_rest_api.pdm_mock.id
108+
stage_name = var.environment
109+
110+
xray_tracing_enabled = true
111+
112+
access_log_settings {
113+
destination_arn = aws_cloudwatch_log_group.pdm_mock_api_gateway.arn
114+
format = jsonencode({
115+
requestId = "$context.requestId"
116+
ip = "$context.identity.sourceIp"
117+
caller = "$context.identity.caller"
118+
user = "$context.identity.user"
119+
requestTime = "$context.requestTime"
120+
httpMethod = "$context.httpMethod"
121+
resourcePath = "$context.resourcePath"
122+
status = "$context.status"
123+
protocol = "$context.protocol"
124+
responseLength = "$context.responseLength"
125+
})
126+
}
127+
128+
tags = {
129+
Name = "${var.project}-${var.environment}-pdm-mock-api-stage"
130+
Project = var.project
131+
Environment = var.environment
132+
Component = local.component
133+
}
134+
}
135+
136+
# CloudWatch Log Group for API Gateway
137+
resource "aws_cloudwatch_log_group" "pdm_mock_api_gateway" {
138+
name = "/aws/apigateway/${var.project}-${var.environment}-pdm-mock-api"
139+
retention_in_days = var.log_retention_in_days
140+
kms_key_id = module.kms.key_arn
141+
142+
tags = {
143+
Name = "${var.project}-${var.environment}-pdm-mock-api-logs"
144+
Project = var.project
145+
Environment = var.environment
146+
Component = local.component
147+
}
148+
}
149+
150+
# Outputs
151+
output "pdm_mock_api_endpoint" {
152+
description = "The base URL of the PDM Mock API"
153+
value = aws_api_gateway_stage.pdm_mock.invoke_url
154+
}
155+
156+
output "pdm_mock_api_id" {
157+
description = "The ID of the PDM Mock API Gateway"
158+
value = aws_api_gateway_rest_api.pdm_mock.id
159+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
module "pdm_mock_api" {
2+
source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip"
3+
4+
function_name = "pdm-mock-api"
5+
description = "A lambda function for mocking PDM (Patient Data Manager) API endpoints"
6+
7+
aws_account_id = var.aws_account_id
8+
component = local.component
9+
environment = var.environment
10+
project = var.project
11+
region = var.region
12+
group = var.group
13+
14+
log_retention_in_days = var.log_retention_in_days
15+
kms_key_arn = module.kms.key_arn
16+
17+
iam_policy_document = {
18+
body = data.aws_iam_policy_document.pdm_mock_api_lambda.json
19+
}
20+
21+
function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
22+
function_code_base_path = local.aws_lambda_functions_dir_path
23+
function_code_dir = "pdm-mock-api/dist"
24+
function_include_common = true
25+
handler_function_name = "handler"
26+
runtime = "nodejs22.x"
27+
memory = 256
28+
timeout = 30
29+
log_level = var.log_level
30+
31+
force_lambda_code_deploy = var.force_lambda_code_deploy
32+
enable_lambda_insights = false
33+
34+
send_to_firehose = true
35+
log_destination_arn = local.log_destination_arn
36+
log_subscription_role_arn = local.acct.log_subscription_role_arn
37+
38+
lambda_env_vars = {
39+
MOCK_ACCESS_TOKEN = var.pdm_mock_access_token
40+
ACCESS_TOKEN_SSM_PATH = var.pdm_access_token_ssm_path
41+
USE_NON_MOCK_TOKEN = var.pdm_use_non_mock_token
42+
}
43+
}
44+
45+
data "aws_iam_policy_document" "pdm_mock_api_lambda" {
46+
statement {
47+
sid = "KMSPermissions"
48+
effect = "Allow"
49+
50+
actions = [
51+
"kms:Decrypt",
52+
"kms:GenerateDataKey",
53+
]
54+
55+
resources = [
56+
module.kms.key_arn,
57+
]
58+
}
59+
60+
statement {
61+
sid = "SSMParameterAccess"
62+
effect = "Allow"
63+
64+
actions = [
65+
"ssm:GetParameter",
66+
"ssm:GetParameters",
67+
]
68+
69+
resources = [
70+
"arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter${var.pdm_access_token_ssm_path}",
71+
]
72+
}
73+
}

infrastructure/terraform/components/dl/variables.tf

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,21 @@ variable "ttl_poll_schedule" {
115115
description = "Schedule to poll for any overdue TTL records"
116116
default = "rate(10 minutes)" # Every 10 minutes
117117
}
118+
119+
variable "pdm_mock_access_token" {
120+
type = string
121+
description = "Mock access token for PDM API authentication (used in local/dev environments)"
122+
default = "mock-pdm-token"
123+
}
124+
125+
variable "pdm_access_token_ssm_path" {
126+
type = string
127+
description = "SSM Parameter path for the PDM API access token"
128+
default = "/digital-letters/pdm/access-token"
129+
}
130+
131+
variable "pdm_use_non_mock_token" {
132+
type = bool
133+
description = "Whether to use the SSM parameter token instead of the mock token"
134+
default = false
135+
}

lambdas/pdm-mock-api/.eslintignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist
2+
node_modules
3+
coverage

0 commit comments

Comments
 (0)