Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
1 change: 1 addition & 0 deletions infrastructure/terraform/modules/backend-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ No requirements.
| Name | Source | Version |
|------|--------|---------|
| <a name="module_authorizer_lambda"></a> [authorizer\_lambda](#module\_authorizer\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
| <a name="module_count_routing_configs_lambda"></a> [count\_routing\_configs\_lambda](#module\_count\_routing\_configs\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
| <a name="module_create_template_lambda"></a> [create\_template\_lambda](#module\_create\_template\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
| <a name="module_delete_template_lambda"></a> [delete\_template\_lambda](#module\_delete\_template\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
| <a name="module_get_client_lambda"></a> [get\_client\_lambda](#module\_get\_client\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip | n/a |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ data "aws_iam_policy_document" "api_gateway_execution_policy" {
resources = [
module.authorizer_lambda.function_arn,
module.upload_letter_template_lambda.function_arn,
module.count_routing_configs_lambda.function_arn,
module.create_template_lambda.function_arn,
module.delete_template_lambda.function_arn,
module.get_client_lambda.function_arn,
Expand Down
29 changes: 15 additions & 14 deletions infrastructure/terraform/modules/backend-api/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,21 @@ locals {
client_ssm_path_pattern = "arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter${local.client_ssm_path_prefix}/*"

openapi_spec = templatefile("${path.module}/spec.tmpl.json", {
APIG_EXECUTION_ROLE_ARN = aws_iam_role.api_gateway_execution_role.arn
AUTHORIZER_LAMBDA_ARN = module.authorizer_lambda.function_arn
AWS_REGION = var.region
CREATE_LAMBDA_ARN = module.create_template_lambda.function_arn
DELETE_LAMBDA_ARN = module.delete_template_lambda.function_arn
GET_CLIENT_LAMBDA_ARN = module.get_client_lambda.function_arn
GET_LAMBDA_ARN = module.get_template_lambda.function_arn
GET_ROUTING_CONFIG_LAMBDA_ARN = module.get_routing_config_lambda.function_arn
LIST_LAMBDA_ARN = module.list_template_lambda.function_arn
LIST_ROUTING_CONFIGS_LAMBDA_ARN = module.list_routing_configs_lambda.function_arn
REQUEST_PROOF_LAMBDA_ARN = module.request_proof_lambda.function_arn
SUBMIT_LAMBDA_ARN = module.submit_template_lambda.function_arn
UPDATE_LAMBDA_ARN = module.update_template_lambda.function_arn
UPLOAD_LETTER_LAMBDA_ARN = module.upload_letter_template_lambda.function_arn
APIG_EXECUTION_ROLE_ARN = aws_iam_role.api_gateway_execution_role.arn
AUTHORIZER_LAMBDA_ARN = module.authorizer_lambda.function_arn
AWS_REGION = var.region
COUNT_ROUTING_CONFIGS_LAMBDA_ARN = module.count_routing_configs_lambda.function_arn
CREATE_LAMBDA_ARN = module.create_template_lambda.function_arn
DELETE_LAMBDA_ARN = module.delete_template_lambda.function_arn
GET_CLIENT_LAMBDA_ARN = module.get_client_lambda.function_arn
GET_LAMBDA_ARN = module.get_template_lambda.function_arn
GET_ROUTING_CONFIG_LAMBDA_ARN = module.get_routing_config_lambda.function_arn
LIST_LAMBDA_ARN = module.list_template_lambda.function_arn
LIST_ROUTING_CONFIGS_LAMBDA_ARN = module.list_routing_configs_lambda.function_arn
REQUEST_PROOF_LAMBDA_ARN = module.request_proof_lambda.function_arn
SUBMIT_LAMBDA_ARN = module.submit_template_lambda.function_arn
UPDATE_LAMBDA_ARN = module.update_template_lambda.function_arn
UPLOAD_LETTER_LAMBDA_ARN = module.upload_letter_template_lambda.function_arn
})

backend_lambda_environment_variables = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module "count_routing_configs_lambda" {
source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.22/terraform-lambda.zip"

project = var.project
environment = var.environment
component = var.component
aws_account_id = var.aws_account_id
region = var.region

kms_key_arn = var.kms_key_arn

function_name = "count-routing-configs"

function_module_name = "count-routing-configs"
handler_function_name = "handler"
description = "Count Routing Configs API endpoint"

memory = 512
timeout = 3
runtime = "nodejs20.x"

log_retention_in_days = var.log_retention_in_days

iam_policy_document = {
body = data.aws_iam_policy_document.count_routing_configs_lambda_policy.json
}

lambda_env_vars = local.backend_lambda_environment_variables
function_s3_bucket = var.function_s3_bucket
function_code_base_path = local.lambdas_dir
function_code_dir = "backend-api/dist/count-routing-configs"

send_to_firehose = var.send_to_firehose
log_destination_arn = var.log_destination_arn
log_subscription_role_arn = var.log_subscription_role_arn
}

data "aws_iam_policy_document" "count_routing_configs_lambda_policy" {
statement {
sid = "AllowDynamoAccess"
effect = "Allow"

actions = [
"dynamodb:Query",
]

resources = [
aws_dynamodb_table.routing_configuration.arn,
]
}

statement {
sid = "AllowKMSAccess"
effect = "Allow"

actions = [
"kms:Decrypt",
"kms:DescribeKey",
"kms:Encrypt",
"kms:GenerateDataKey*",
"kms:ReEncrypt*",
]

resources = [
var.kms_key_arn
]
}
}
87 changes: 87 additions & 0 deletions infrastructure/terraform/modules/backend-api/spec.tmpl.json
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,29 @@
],
"type": "object"
},
"CountSuccess": {
"properties": {
"data": {
"properties": {
"count": {
"type": "integer"
}
},
"required": [
"count"
],
"type": "object"
},
"statusCode": {
"type": "integer"
}
},
"required": [
"data",
"statusCode"
],
"type": "object"
},
"CreateUpdateTemplate": {
"allOf": [
{
Expand Down Expand Up @@ -1117,6 +1140,70 @@
}
}
},
"/v1/routing-configurations/count": {
"get": {
"description": "Get a count of routing configs",
"parameters": [
{
"description": "Filter by a single active status",
"in": "query",
"name": "status",
"schema": {
"$ref": "#/components/schemas/RoutingConfigStatusActive"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CountSuccess"
}
}
},
"description": "200 response",
"headers": {
"Content-Type": {
"schema": {
"type": "string"
}
}
}
},
"default": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Failure"
}
}
},
"description": "Error"
}
},
"security": [
{
"authorizer": []
}
],
"summary": "Count routing configs",
"x-amazon-apigateway-integration": {
"contentHandling": "CONVERT_TO_TEXT",
"credentials": "${APIG_EXECUTION_ROLE_ARN}",
"httpMethod": "POST",
"passthroughBehavior": "WHEN_NO_TEMPLATES",
"responses": {
".*": {
"statusCode": "200"
}
},
"timeoutInMillis": 29000,
"type": "AWS_PROXY",
"uri": "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${COUNT_ROUTING_CONFIGS_LAMBDA_ARN}/invocations"
}
}
},
"/v1/template": {
"post": {
"description": "Create a template",
Expand Down
7 changes: 4 additions & 3 deletions lambdas/backend-api/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ npx esbuild \
--outdir=dist \
--external:pdfjs-dist \
src/templates/copy-scanned-object-to-internal.ts \
src/templates/upload-letter.ts \
src/templates/count-routing-configs.ts \
src/templates/create.ts \
src/templates/delete-failed-scanned-object.ts \
src/templates/delete.ts \
src/templates/get-client.ts \
src/templates/delete-failed-scanned-object.ts \
src/templates/get.ts \
src/templates/get-client.ts \
src/templates/get-routing-config.ts \
src/templates/list.ts \
src/templates/list-routing-configs.ts \
Expand All @@ -29,6 +29,7 @@ npx esbuild \
src/templates/set-letter-upload-virus-scan-status.ts \
src/templates/submit.ts \
src/templates/update.ts \
src/templates/upload-letter.ts \
src/templates/validate-letter-template-files.ts

cp -r ../../utils/utils/src/email-templates ./dist/submit
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import type { APIGatewayProxyEvent, Context } from 'aws-lambda';
import { mock } from 'jest-mock-extended';
import type { Logger } from 'nhs-notify-web-template-management-utils/logger';
import { createHandler } from '@backend-api/templates/api/count-routing-configs';
import { RoutingConfigClient } from '@backend-api/templates/app/routing-config-client';

jest.mock('nhs-notify-web-template-management-utils/logger', () => ({
logger: mock<Logger>({
child: jest.fn().mockReturnThis(),
}),
}));

const setup = () => {
const routingConfigClient = mock<RoutingConfigClient>();

const handler = createHandler({ routingConfigClient });

return { handler, mocks: { routingConfigClient } };
};

describe('CountRoutingConfigs handler', () => {
test.each([
['undefined', undefined],
['missing user', { clientId: 'client-id', user: undefined }],
['missing client', { clientId: undefined, user: 'user-id' }],
])(
'should return 400 - Invalid request when requestContext is %s',
async (_, ctx) => {
const { handler, mocks } = setup();

const event = mock<APIGatewayProxyEvent>({
requestContext: { authorizer: ctx },
});

const result = await handler(event, mock<Context>(), jest.fn());

expect(result).toEqual({
statusCode: 400,
body: JSON.stringify({
statusCode: 400,
technicalMessage: 'Invalid request',
}),
});

expect(
mocks.routingConfigClient.countRoutingConfigs
).not.toHaveBeenCalled();
}
);

test('should return error when counting routing configs fails', async () => {
const { handler, mocks } = setup();

mocks.routingConfigClient.countRoutingConfigs.mockResolvedValueOnce({
error: {
errorMeta: {
code: 500,
description: 'Internal server error',
},
},
});

const event = mock<APIGatewayProxyEvent>();
event.requestContext.authorizer = {
user: 'sub',
clientId: 'nhs-notify-client-id',
};

event.queryStringParameters = {
status: 'DRAFT',
};

const result = await handler(event, mock<Context>(), jest.fn());

expect(result).toEqual({
statusCode: 500,
body: JSON.stringify({
statusCode: 500,
technicalMessage: 'Internal server error',
}),
});

expect(mocks.routingConfigClient.countRoutingConfigs).toHaveBeenCalledWith(
'nhs-notify-client-id',
{ status: 'DRAFT' }
);
});

test('should return count of routing configs', async () => {
const { handler, mocks } = setup();

mocks.routingConfigClient.countRoutingConfigs.mockResolvedValueOnce({
data: { count: 99 },
});

const event = mock<APIGatewayProxyEvent>();
event.requestContext.authorizer = {
user: 'sub',
clientId: 'nhs-notify-client-id',
};
event.queryStringParameters = {
status: 'COMPLETED',
};

const result = await handler(event, mock<Context>(), jest.fn());

expect(result).toEqual({
statusCode: 200,
body: JSON.stringify({ statusCode: 200, data: { count: 99 } }),
});

expect(mocks.routingConfigClient.countRoutingConfigs).toHaveBeenCalledWith(
'nhs-notify-client-id',
{ status: 'COMPLETED' }
);
});
});
Loading