Skip to content

Commit 64285f2

Browse files
committed
WIP
1 parent 54b0fc2 commit 64285f2

File tree

9 files changed

+138
-7
lines changed

9 files changed

+138
-7
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { DescribeTableCommand } from "@aws-sdk/client-dynamodb";
2+
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3+
import { LetterRepositoryConfig } from "./letter-repository";
4+
5+
export class DBHealthcheck {
6+
constructor(readonly ddbClient: DynamoDBDocumentClient,
7+
readonly config: LetterRepositoryConfig) {}
8+
9+
async check(): Promise<void> {
10+
this.ddbClient.send(new DescribeTableCommand({
11+
TableName: this.config.lettersTableName}));
12+
}
13+
}

internal/datastore/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './types';
22
export * from './letter-repository';
3+
export * from './healthcheck';
34
export * from './types';

lambdas/api-handler/src/config/deps.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { S3Client } from "@aws-sdk/client-s3";
22
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
33
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
44
import pino from 'pino';
5-
import { LetterRepository } from '../../../../internal/datastore';
5+
import { LetterRepository, DBHealthcheck } from '../../../../internal/datastore';
66
import { envVars, EnvVars } from "../config/env";
77

88
export type Deps = {
99
s3Client: S3Client;
1010
letterRepo: LetterRepository;
11-
logger: pino.Logger,
11+
dbHealthcheck: DBHealthcheck;
12+
logger: pino.Logger;
1213
env: EnvVars
1314
};
1415

@@ -23,13 +24,25 @@ function createLetterRepository(log: pino.Logger, envVars: EnvVars): LetterRepos
2324
return new LetterRepository(docClient, log, config);
2425
}
2526

27+
function createDBHealthcheck(envVars: EnvVars): DBHealthcheck {
28+
const ddbClient = new DynamoDBClient({});
29+
const docClient = DynamoDBDocumentClient.from(ddbClient);
30+
const config = {
31+
lettersTableName: envVars.LETTERS_TABLE_NAME,
32+
ttlHours: envVars.LETTER_TTL_HOURS
33+
};
34+
35+
return new DBHealthcheck(docClient, config);
36+
}
37+
2638
export function createDependenciesContainer(): Deps {
2739

2840
const log = pino();
2941

3042
return {
3143
s3Client: new S3Client(),
3244
letterRepo: createLetterRepository(log, envVars),
45+
dbHealthcheck: createDBHealthcheck(envVars),
3346
logger: log,
3447
env: envVars
3548
};

lambdas/api-handler/src/handlers/__tests__/get-letter-data.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { makeApiGwEvent } from './utils/test-utils';
1818
import { ValidationError } from '../../errors';
1919
import * as errors from '../../contracts/errors';
2020
import { createGetLetterDataHandler } from '../get-letter-data';
21-
import { S3Client } from '@aws-sdk/client-s3';
21+
import { Destination, S3Client } from '@aws-sdk/client-s3';
2222
import pino from 'pino';
2323
import { LetterRepository } from '../../../../../internal/datastore/src';
2424
import { EnvVars } from '../../config/env';
@@ -37,7 +37,7 @@ describe('API Lambda handler', () => {
3737
LETTER_TTL_HOURS: 12960,
3838
DOWNLOAD_URL_TTL_SECONDS: 60
3939
} as unknown as EnvVars
40-
}
40+
} as Deps;
4141

4242
beforeEach(() => {
4343
jest.clearAllMocks();

lambdas/api-handler/src/handlers/__tests__/get-letters.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe('API Lambda handler', () => {
3838
DOWNLOAD_URL_TTL_SECONDS: 60,
3939
MAX_LIMIT: 2500
4040
} as unknown as EnvVars
41-
}
41+
} as Deps;
4242

4343
beforeEach(() => {
4444
jest.clearAllMocks();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { S3, S3Client } from "@aws-sdk/client-s3";
2+
import { DBHealthcheck } from "../../../../../internal/datastore/src";
3+
import pino from "pino";
4+
import { Deps } from "../../config/deps";
5+
import { makeApiGwEvent } from "./utils/test-utils";
6+
import { mockDeep } from "jest-mock-extended";
7+
import { Context } from "aws-lambda";
8+
import { createGetStatusHandler } from "../get-status";
9+
10+
describe('API Lambda handler', () => {
11+
12+
const mockedDeps: jest.Mocked<Deps> = {
13+
s3Client: { send: jest.fn()} as unknown as S3Client,
14+
dbHealthcheck: {check: jest.fn()} as unknown as DBHealthcheck,
15+
logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger,
16+
env: {
17+
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
18+
APIM_CORRELATION_HEADER: 'nhsd-correlation-id'
19+
}
20+
} as Deps;
21+
22+
beforeEach(() => {
23+
jest.clearAllMocks();
24+
jest.resetModules();
25+
});
26+
27+
it('passes if S3 and DynamoDB are available', async() => {
28+
29+
const event = makeApiGwEvent({path: '/_status',
30+
headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId'},
31+
pathParameters: {id: 'id1'}
32+
});
33+
34+
const getLetterDataHandler = createGetStatusHandler(mockedDeps);
35+
const result = await getLetterDataHandler(event, mockDeep<Context>(), jest.fn());
36+
37+
expect(result).toEqual({
38+
statusCode: 200,
39+
body: JSON.stringify({}, null, 2),
40+
});
41+
});
42+
43+
it('fails if S3 is unavailable', async() => {
44+
mockedDeps.s3Client = {
45+
send: jest.fn().mockRejectedValue(new Error('unexpected error'))
46+
} as unknown as S3Client;
47+
48+
const event = makeApiGwEvent({path: '/_status',
49+
headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId'},
50+
pathParameters: {id: 'id1'}
51+
});
52+
53+
54+
const getLetterDataHandler = createGetStatusHandler(mockedDeps);
55+
const result = await getLetterDataHandler(event, mockDeep<Context>(), jest.fn());
56+
57+
expect(result).toEqual(expect.objectContaining({
58+
statusCode: 500
59+
}));
60+
});
61+
62+
});

lambdas/api-handler/src/handlers/__tests__/patch-letter.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ describe('patchLetter API Handler', () => {
5757
LETTER_TTL_HOURS: 12960,
5858
DOWNLOAD_URL_TTL_SECONDS: 60
5959
} as unknown as EnvVars
60-
}
60+
} as Deps;
6161

6262
it('returns 200 OK with updated resource', async () => {
6363
const event = makeApiGwEvent({
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { APIGatewayProxyHandler } from "aws-lambda";
2+
import { Deps } from "../config/deps";
3+
import { ListBucketsCommand, S3Client } from "@aws-sdk/client-s3";
4+
import { validateCommonHeaders } from "../utils/validation";
5+
import { mapErrorToResponse } from "../mappers/error-mapper";
6+
7+
export function createGetStatusHandler(deps: Deps): APIGatewayProxyHandler {
8+
9+
return async(event) => {
10+
11+
const commonHeadersResult = validateCommonHeaders(event.headers, deps);
12+
13+
if (!commonHeadersResult.ok) {
14+
return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger);
15+
}
16+
17+
try {
18+
deps.dbHealthcheck.check();
19+
s3HealthCheck(deps.s3Client);
20+
21+
deps.logger.info({
22+
description: 'Healthcheck passed',
23+
supplierId: commonHeadersResult.value.supplierId
24+
});
25+
26+
return {
27+
statusCode: 200,
28+
body: JSON.stringify({}, null, 2)
29+
};
30+
} catch (error) {
31+
return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger);
32+
}
33+
}
34+
}
35+
36+
37+
function s3HealthCheck(s3Client: S3Client) {
38+
const command: ListBucketsCommand = new ListBucketsCommand({
39+
MaxBuckets: 1
40+
});
41+
s3Client.send(command);
42+
}

lambdas/api-handler/src/services/__tests__/letter-operations.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ describe('getLetterDataUrl function', () => {
133133
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
134134
DOWNLOAD_URL_TTL_SECONDS: 60
135135
};
136-
const deps: Deps = { s3Client, letterRepo, logger, env };
136+
const deps: Deps = { s3Client, letterRepo, logger, env } as Deps;
137137

138138
it('should return pre signed url successfully', async () => {
139139

0 commit comments

Comments
 (0)