Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Loading