Skip to content

Commit d7a5885

Browse files
authored
CCM-12222: list routing configs api endpoint (#710)
1 parent 92e5bc6 commit d7a5885

File tree

27 files changed

+1147
-28
lines changed

27 files changed

+1147
-28
lines changed

infrastructure/terraform/modules/backend-api/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ No requirements.
5151
| <a name="module_lambda_sftp_poll"></a> [lambda\_sftp\_poll](#module\_lambda\_sftp\_poll) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
5252
| <a name="module_lambda_sftp_request_proof"></a> [lambda\_sftp\_request\_proof](#module\_lambda\_sftp\_request\_proof) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
5353
| <a name="module_lambda_validate_letter_template_files"></a> [lambda\_validate\_letter\_template\_files](#module\_lambda\_validate\_letter\_template\_files) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
54+
| <a name="module_list_routing_configs_lambda"></a> [list\_routing\_configs\_lambda](#module\_list\_routing\_configs\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
5455
| <a name="module_list_template_lambda"></a> [list\_template\_lambda](#module\_list\_template\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
5556
| <a name="module_request_proof_lambda"></a> [request\_proof\_lambda](#module\_request\_proof\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
5657
| <a name="module_s3bucket_download"></a> [s3bucket\_download](#module\_s3bucket\_download) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.20/terraform-s3bucket.zip | n/a |

infrastructure/terraform/modules/backend-api/iam_role_api_gateway_execution_role.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ data "aws_iam_policy_document" "api_gateway_execution_policy" {
5353
module.create_template_lambda.function_arn,
5454
module.delete_template_lambda.function_arn,
5555
module.get_client_lambda.function_arn,
56-
module.get_template_lambda.function_arn,
5756
module.get_routing_config_lambda.function_arn,
57+
module.get_template_lambda.function_arn,
58+
module.list_routing_configs_lambda.function_arn,
5859
module.list_template_lambda.function_arn,
5960
module.request_proof_lambda.function_arn,
6061
module.submit_template_lambda.function_arn,

infrastructure/terraform/modules/backend-api/locals.tf

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,20 @@ locals {
1111
client_ssm_path_pattern = "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter${local.client_ssm_path_prefix}/*"
1212

1313
openapi_spec = templatefile("${path.module}/spec.tmpl.json", {
14-
APIG_EXECUTION_ROLE_ARN = aws_iam_role.api_gateway_execution_role.arn
15-
AUTHORIZER_LAMBDA_ARN = module.authorizer_lambda.function_arn
16-
AWS_REGION = var.region
17-
CREATE_LAMBDA_ARN = module.create_template_lambda.function_arn
18-
DELETE_LAMBDA_ARN = module.delete_template_lambda.function_arn
19-
GET_CLIENT_LAMBDA_ARN = module.get_client_lambda.function_arn
20-
GET_LAMBDA_ARN = module.get_template_lambda.function_arn
21-
GET_ROUTING_CONFIG_LAMBDA_ARN = module.get_routing_config_lambda.function_arn
22-
LIST_LAMBDA_ARN = module.list_template_lambda.function_arn
23-
REQUEST_PROOF_LAMBDA_ARN = module.request_proof_lambda.function_arn
24-
SUBMIT_LAMBDA_ARN = module.submit_template_lambda.function_arn
25-
UPDATE_LAMBDA_ARN = module.update_template_lambda.function_arn
26-
UPLOAD_LETTER_LAMBDA_ARN = module.upload_letter_template_lambda.function_arn
14+
APIG_EXECUTION_ROLE_ARN = aws_iam_role.api_gateway_execution_role.arn
15+
AUTHORIZER_LAMBDA_ARN = module.authorizer_lambda.function_arn
16+
AWS_REGION = var.region
17+
CREATE_LAMBDA_ARN = module.create_template_lambda.function_arn
18+
DELETE_LAMBDA_ARN = module.delete_template_lambda.function_arn
19+
GET_CLIENT_LAMBDA_ARN = module.get_client_lambda.function_arn
20+
GET_LAMBDA_ARN = module.get_template_lambda.function_arn
21+
GET_ROUTING_CONFIG_LAMBDA_ARN = module.get_routing_config_lambda.function_arn
22+
LIST_LAMBDA_ARN = module.list_template_lambda.function_arn
23+
LIST_ROUTING_CONFIGS_LAMBDA_ARN = module.list_routing_configs_lambda.function_arn
24+
REQUEST_PROOF_LAMBDA_ARN = module.request_proof_lambda.function_arn
25+
SUBMIT_LAMBDA_ARN = module.submit_template_lambda.function_arn
26+
UPDATE_LAMBDA_ARN = module.update_template_lambda.function_arn
27+
UPLOAD_LETTER_LAMBDA_ARN = module.upload_letter_template_lambda.function_arn
2728
})
2829

2930
backend_lambda_environment_variables = {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
module "list_routing_configs_lambda" {
2+
source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip"
3+
4+
project = var.project
5+
environment = var.environment
6+
component = var.component
7+
aws_account_id = var.aws_account_id
8+
region = var.region
9+
10+
kms_key_arn = var.kms_key_arn
11+
12+
function_name = "list-routing-configs"
13+
14+
function_module_name = "list-routing-configs"
15+
handler_function_name = "handler"
16+
description = "List Routing Configs API endpoint"
17+
18+
memory = 512
19+
timeout = 3
20+
runtime = "nodejs20.x"
21+
22+
log_retention_in_days = var.log_retention_in_days
23+
24+
iam_policy_document = {
25+
body = data.aws_iam_policy_document.list_routing_configs_lambda_policy.json
26+
}
27+
28+
lambda_env_vars = local.backend_lambda_environment_variables
29+
function_s3_bucket = var.function_s3_bucket
30+
function_code_base_path = local.lambdas_dir
31+
function_code_dir = "backend-api/dist/list-routing-configs"
32+
33+
send_to_firehose = var.send_to_firehose
34+
log_destination_arn = var.log_destination_arn
35+
log_subscription_role_arn = var.log_subscription_role_arn
36+
}
37+
38+
data "aws_iam_policy_document" "list_routing_configs_lambda_policy" {
39+
statement {
40+
sid = "AllowDynamoAccess"
41+
effect = "Allow"
42+
43+
actions = [
44+
"dynamodb:Query",
45+
]
46+
47+
resources = [
48+
aws_dynamodb_table.routing_configuration.arn,
49+
]
50+
}
51+
52+
statement {
53+
sid = "AllowKMSAccess"
54+
effect = "Allow"
55+
56+
actions = [
57+
"kms:Decrypt",
58+
"kms:DescribeKey",
59+
"kms:Encrypt",
60+
"kms:GenerateDataKey*",
61+
"kms:ReEncrypt*",
62+
]
63+
64+
resources = [
65+
var.kms_key_arn
66+
]
67+
}
68+
}

infrastructure/terraform/modules/backend-api/spec.tmpl.json

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,9 +636,21 @@
636636
"type": "object"
637637
},
638638
"RoutingConfigStatus": {
639+
"anyOf": [
640+
{
641+
"$ref": "#/components/schemas/RoutingConfigStatusActive"
642+
},
643+
{
644+
"enum": [
645+
"DELETED"
646+
],
647+
"type": "string"
648+
}
649+
]
650+
},
651+
"RoutingConfigStatusActive": {
639652
"enum": [
640653
"COMPLETED",
641-
"DELETED",
642654
"DRAFT"
643655
],
644656
"type": "string"
@@ -658,6 +670,24 @@
658670
],
659671
"type": "object"
660672
},
673+
"RoutingConfigSuccessList": {
674+
"properties": {
675+
"data": {
676+
"items": {
677+
"$ref": "#/components/schemas/RoutingConfig"
678+
},
679+
"type": "array"
680+
},
681+
"statusCode": {
682+
"type": "integer"
683+
}
684+
},
685+
"required": [
686+
"data",
687+
"statusCode"
688+
],
689+
"type": "object"
690+
},
661691
"SmsProperties": {
662692
"properties": {
663693
"message": {
@@ -1023,6 +1053,70 @@
10231053
}
10241054
}
10251055
},
1056+
"/v1/routing-configurations": {
1057+
"get": {
1058+
"description": "List all routing configs",
1059+
"parameters": [
1060+
{
1061+
"description": "Filter by a single active status",
1062+
"in": "query",
1063+
"name": "status",
1064+
"schema": {
1065+
"$ref": "#/components/schemas/RoutingConfigStatusActive"
1066+
}
1067+
}
1068+
],
1069+
"responses": {
1070+
"200": {
1071+
"content": {
1072+
"application/json": {
1073+
"schema": {
1074+
"$ref": "#/components/schemas/RoutingConfigSuccessList"
1075+
}
1076+
}
1077+
},
1078+
"description": "200 response",
1079+
"headers": {
1080+
"Content-Type": {
1081+
"schema": {
1082+
"type": "string"
1083+
}
1084+
}
1085+
}
1086+
},
1087+
"default": {
1088+
"content": {
1089+
"application/json": {
1090+
"schema": {
1091+
"$ref": "#/components/schemas/Failure"
1092+
}
1093+
}
1094+
},
1095+
"description": "Error"
1096+
}
1097+
},
1098+
"security": [
1099+
{
1100+
"authorizer": []
1101+
}
1102+
],
1103+
"summary": "List all routing configs",
1104+
"x-amazon-apigateway-integration": {
1105+
"contentHandling": "CONVERT_TO_TEXT",
1106+
"credentials": "${APIG_EXECUTION_ROLE_ARN}",
1107+
"httpMethod": "POST",
1108+
"passthroughBehavior": "WHEN_NO_TEMPLATES",
1109+
"responses": {
1110+
".*": {
1111+
"statusCode": "200"
1112+
}
1113+
},
1114+
"timeoutInMillis": 29000,
1115+
"type": "AWS_PROXY",
1116+
"uri": "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${LIST_ROUTING_CONFIGS_LAMBDA_ARN}/invocations"
1117+
}
1118+
}
1119+
},
10261120
"/v1/template": {
10271121
"post": {
10281122
"description": "Create a template",

lambdas/backend-api/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,11 @@ curl --location "${APIG_STAGE}/v1/routing-configuration/${ROUTING_CONFIG_ID}" \
149149
--header 'Accept: application/json' \
150150
--header "Authorization: $SANDBOX_TOKEN"
151151
```
152+
153+
### GET - /v1/routing-configurations - List routing configurations
154+
155+
```bash
156+
curl --location "${APIG_STAGE}/v1/routing-configurations \
157+
--header 'Accept: application/json' \
158+
--header "Authorization: $SANDBOX_TOKEN"
159+
```

lambdas/backend-api/build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ npx esbuild \
2323
src/templates/get.ts \
2424
src/templates/get-routing-config.ts \
2525
src/templates/list.ts \
26+
src/templates/list-routing-configs.ts \
2627
src/templates/process-proof.ts \
2728
src/templates/proof.ts \
2829
src/templates/set-letter-upload-virus-scan-status.ts \
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import type { APIGatewayProxyEvent, Context } from 'aws-lambda';
2+
import { mock } from 'jest-mock-extended';
3+
import type { Logger } from 'nhs-notify-web-template-management-utils/logger';
4+
import { createHandler } from '@backend-api/templates/api/list-routing-configs';
5+
import { RoutingConfigClient } from '@backend-api/templates/app/routing-config-client';
6+
import { makeRoutingConfig } from '../fixtures/routing-config';
7+
8+
jest.mock('nhs-notify-web-template-management-utils/logger', () => ({
9+
logger: mock<Logger>({
10+
child: jest.fn().mockReturnThis(),
11+
}),
12+
}));
13+
14+
const setup = () => {
15+
const routingConfigClient = mock<RoutingConfigClient>();
16+
17+
const handler = createHandler({ routingConfigClient });
18+
19+
return { handler, mocks: { routingConfigClient } };
20+
};
21+
22+
describe('ListRoutingConfig handler', () => {
23+
test.each([
24+
['undefined', undefined],
25+
['missing user', { clientId: 'client-id', user: undefined }],
26+
['missing client', { clientId: undefined, user: 'user-id' }],
27+
])(
28+
'should return 400 - Invalid request when requestContext is %s',
29+
async (_, ctx) => {
30+
const { handler, mocks } = setup();
31+
32+
const event = mock<APIGatewayProxyEvent>({
33+
requestContext: { authorizer: ctx },
34+
});
35+
36+
const result = await handler(event, mock<Context>(), jest.fn());
37+
38+
expect(result).toEqual({
39+
statusCode: 400,
40+
body: JSON.stringify({
41+
statusCode: 400,
42+
technicalMessage: 'Invalid request',
43+
}),
44+
});
45+
46+
expect(
47+
mocks.routingConfigClient.listRoutingConfigs
48+
).not.toHaveBeenCalled();
49+
}
50+
);
51+
52+
test('should return error when listing routing configs fails', async () => {
53+
const { handler, mocks } = setup();
54+
55+
mocks.routingConfigClient.listRoutingConfigs.mockResolvedValueOnce({
56+
error: {
57+
errorMeta: {
58+
code: 500,
59+
description: 'Internal server error',
60+
},
61+
},
62+
});
63+
64+
const event = mock<APIGatewayProxyEvent>();
65+
event.requestContext.authorizer = {
66+
user: 'sub',
67+
clientId: 'nhs-notify-client-id',
68+
};
69+
70+
event.queryStringParameters = {
71+
status: 'DRAFT',
72+
};
73+
74+
const result = await handler(event, mock<Context>(), jest.fn());
75+
76+
expect(result).toEqual({
77+
statusCode: 500,
78+
body: JSON.stringify({
79+
statusCode: 500,
80+
technicalMessage: 'Internal server error',
81+
}),
82+
});
83+
84+
expect(mocks.routingConfigClient.listRoutingConfigs).toHaveBeenCalledWith(
85+
'nhs-notify-client-id',
86+
{ status: 'DRAFT' }
87+
);
88+
});
89+
90+
test('should return list of routing configs', async () => {
91+
const { handler, mocks } = setup();
92+
93+
const list = [
94+
makeRoutingConfig({
95+
owner: 'nhs-notify-client-id',
96+
clientId: 'nhs-notify-client-id',
97+
status: 'COMPLETED',
98+
}),
99+
makeRoutingConfig({
100+
owner: 'nhs-notify-client-id',
101+
clientId: 'nhs-notify-client-id',
102+
status: 'COMPLETED',
103+
}),
104+
];
105+
106+
mocks.routingConfigClient.listRoutingConfigs.mockResolvedValueOnce({
107+
data: list,
108+
});
109+
110+
const event = mock<APIGatewayProxyEvent>();
111+
event.requestContext.authorizer = {
112+
user: 'sub',
113+
clientId: 'nhs-notify-client-id',
114+
};
115+
event.queryStringParameters = {
116+
status: 'COMPLETED',
117+
};
118+
119+
const result = await handler(event, mock<Context>(), jest.fn());
120+
121+
expect(result).toEqual({
122+
statusCode: 200,
123+
body: JSON.stringify({ statusCode: 200, data: list }),
124+
});
125+
126+
expect(mocks.routingConfigClient.listRoutingConfigs).toHaveBeenCalledWith(
127+
'nhs-notify-client-id',
128+
{ status: 'COMPLETED' }
129+
);
130+
});
131+
});

0 commit comments

Comments
 (0)