Skip to content

Commit 8fcb28f

Browse files
Add url ttl as config
1 parent aa36690 commit 8fcb28f

File tree

9 files changed

+47
-18
lines changed

9 files changed

+47
-18
lines changed

infrastructure/terraform/components/api/locals.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ locals {
1818
common_lambda_env_vars = {
1919
LETTERS_TABLE_NAME = aws_dynamodb_table.letters.name,
2020
LETTER_TTL_HOURS = 24,
21-
SUPPLIER_ID_HEADER = "nhsd-supplier-id"
22-
APIM_CORRELATION_HEADER = "nhsd-correlation-id"
21+
SUPPLIER_ID_HEADER = "nhsd-supplier-id",
22+
APIM_CORRELATION_HEADER = "nhsd-correlation-id",
23+
DOWNLOAD_URL_TTL_SECONDS = 3600
2324
}
2425
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe('getDeps()', () => {
3333
LETTER_TTL_HOURS: '24',
3434
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
3535
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
36+
DOWNLOAD_URL_TTL_SECONDS: '3600'
3637
},
3738
}));
3839
});
@@ -61,6 +62,7 @@ describe('getDeps()', () => {
6162
LETTER_TTL_HOURS: '24',
6263
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
6364
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
65+
DOWNLOAD_URL_TTL_SECONDS: '3600'
6466
});
6567
});
6668

lambdas/api-handler/src/config/__tests__/env.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ describe('lambdaEnv', () => {
1515
process.env.APIM_CORRELATION_HEADER = 'x-correlation-id';
1616
process.env.LETTERS_TABLE_NAME = 'letters-table';
1717
process.env.LETTER_TTL_HOURS = '24';
18+
process.env.DOWNLOAD_URL_TTL_SECONDS = '3600';
1819

1920
const { lambdaEnv } = require('../env');
2021

2122
expect(lambdaEnv).toEqual({
2223
SUPPLIER_ID_HEADER: 'x-supplier-id',
2324
APIM_CORRELATION_HEADER: 'x-correlation-id',
2425
LETTERS_TABLE_NAME: 'letters-table',
25-
LETTER_TTL_HOURS: '24'
26+
LETTER_TTL_HOURS: '24',
27+
DOWNLOAD_URL_TTL_SECONDS: '3600'
2628
});
2729
});
2830

@@ -31,6 +33,7 @@ describe('lambdaEnv', () => {
3133
process.env.APIM_CORRELATION_HEADER = 'x-correlation-id';
3234
process.env.LETTERS_TABLE_NAME = undefined; // simulate missing var
3335
process.env.LETTER_TTL_HOURS = '24';
36+
process.env.DOWNLOAD_URL_TTL_SECONDS = '3600';
3437

3538
expect(() => require('../env')).toThrow(
3639
'Missing required env var: LETTERS_TABLE_NAME'

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ export interface LambdaEnv {
33
APIM_CORRELATION_HEADER: string;
44
LETTERS_TABLE_NAME: string;
55
LETTER_TTL_HOURS: string;
6+
DOWNLOAD_URL_TTL_SECONDS: string;
67
}
78

89
export const lambdaEnv: LambdaEnv = {
910
SUPPLIER_ID_HEADER: getEnv('SUPPLIER_ID_HEADER')!,
1011
APIM_CORRELATION_HEADER: getEnv('APIM_CORRELATION_HEADER')!,
1112
LETTERS_TABLE_NAME: getEnv('LETTERS_TABLE_NAME')!,
12-
LETTER_TTL_HOURS: getEnv('LETTER_TTL_HOURS')!
13+
LETTER_TTL_HOURS: getEnv('LETTER_TTL_HOURS')!,
14+
DOWNLOAD_URL_TTL_SECONDS: getEnv('DOWNLOAD_URL_TTL_SECONDS')!
1315
};
1416

1517
function getEnv(name: string, required = true): string | undefined {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ const fakeDeps: jest.Mocked<Deps> = {
2424
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
2525
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
2626
LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME',
27-
LETTER_TTL_HOURS: 'LETTER_TTL_HOURS'
27+
LETTER_TTL_HOURS: 'LETTER_TTL_HOURS',
28+
DOWNLOAD_URL_TTL_SECONDS: 'DOWNLOAD_URL_TTL_SECONDS'
2829
} as unknown as LambdaEnv
2930
}
3031
mockedGetDeps.mockReturnValue(fakeDeps);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const fakeDeps: jest.Mocked<Deps> = {
1010
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
1111
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
1212
LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME',
13-
LETTER_TTL_HOURS: 'LETTER_TTL_HOURS'
13+
LETTER_TTL_HOURS: 'LETTER_TTL_HOURS',
14+
DOWNLOAD_URL_TTL_SECONDS: 'DOWNLOAD_URL_TTL_SECONDS'
1415
} as unknown as LambdaEnv
1516
}
1617
mockedGetDeps.mockReturnValue(fakeDeps);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const fakeDeps: jest.Mocked<Deps> = {
1010
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
1111
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
1212
LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME',
13-
LETTER_TTL_HOURS: 'LETTER_TTL_HOURS'
13+
LETTER_TTL_HOURS: 'LETTER_TTL_HOURS',
14+
DOWNLOAD_URL_TTL_SECONDS: 'DOWNLOAD_URL_TTL_SECONDS'
1415
} as unknown as LambdaEnv
1516
}
1617
mockedGetDeps.mockReturnValue(fakeDeps);

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ import { Deps } from '../../config/deps';
33
import { LetterDto } from '../../contracts/letters';
44
import { getLetterDataUrl, getLettersForSupplier, patchLetterStatus } from '../letter-operations';
55
import pino from 'pino';
6-
import { LambdaEnv } from '../../config/env';
76

87
jest.mock('@aws-sdk/s3-request-presigner', () => ({
98
getSignedUrl: jest.fn(),
109
}));
1110
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
12-
const mockedGetSignedUrl = getSignedUrl as jest.MockedFunction<typeof getSignedUrl>;
1311

1412
jest.mock('@aws-sdk/client-s3', () => {
1513
const originalModule = jest.requireActual('@aws-sdk/client-s3');
@@ -18,9 +16,13 @@ jest.mock('@aws-sdk/client-s3', () => {
1816
};
1917
});
2018
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
21-
const MockedGetObjectCommand = GetObjectCommand as unknown as jest.Mock;
2219

2320
describe("getLetterIdsForSupplier", () => {
21+
22+
beforeEach(() => {
23+
jest.clearAllMocks();
24+
});
25+
2426
it("returns letter IDs from the repository", async () => {
2527
const mockRepo = {
2628
getLettersBySupplier: jest.fn().mockResolvedValue([
@@ -50,6 +52,10 @@ describe("getLetterIdsForSupplier", () => {
5052

5153
describe('patchLetterStatus function', () => {
5254

55+
beforeEach(() => {
56+
jest.clearAllMocks();
57+
});
58+
5359
const updatedLetterDto: LetterDto = {
5460
id: 'letter1',
5561
supplierId: 'supplier1',
@@ -106,14 +112,27 @@ describe('patchLetterStatus function', () => {
106112

107113
describe('getLetterDataUrl function', () => {
108114

115+
beforeEach(() => {
116+
jest.clearAllMocks();
117+
});
118+
119+
const mockedGetSignedUrl = getSignedUrl as jest.MockedFunction<typeof getSignedUrl>;
120+
const MockedGetObjectCommand = GetObjectCommand as unknown as jest.Mock;
121+
109122
const updatedLetter = makeLetter("letter1", "REJECTED");
110123

111124
const s3Client = { send: jest.fn() } as unknown as S3Client;
112125
const letterRepo = {
113126
getLetterById: jest.fn().mockResolvedValue(updatedLetter)
114127
} as unknown as LetterRepository;
115128
const logger = jest.fn() as unknown as pino.Logger;;
116-
const env = jest.fn() as unknown as LambdaEnv;
129+
const env = {
130+
LETTERS_TABLE_NAME: 'LettersTable',
131+
LETTER_TTL_HOURS: '24',
132+
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
133+
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
134+
DOWNLOAD_URL_TTL_SECONDS: '3600'
135+
};
117136
const deps: Deps = { s3Client, letterRepo, logger, env };
118137

119138
it('should return pre signed url successfully', async () => {
@@ -122,12 +141,11 @@ describe('getLetterDataUrl function', () => {
122141

123142
const result = await getLetterDataUrl('supplier1', 'letter1', deps);
124143

125-
expect(mockedGetSignedUrl).toHaveBeenCalled();
126-
expect(MockedGetObjectCommand).toHaveBeenCalledWith({
144+
const expectedCommandInput = {
127145
Bucket: 'letterDataBucket',
128146
Key: 'letter1.pdf'
129-
});
130-
147+
};
148+
expect(mockedGetSignedUrl).toHaveBeenCalledWith(s3Client, { input: expectedCommandInput}, { expiresIn: 3600});
131149
expect(result).toEqual('http://somePreSignedUrl.com');
132150
});
133151

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const getLetterDataUrl = async (supplierId: string, letterId: string, dep
3939

4040
try {
4141
letter = await deps.letterRepo.getLetterById(supplierId, letterId);
42-
return await getPresignedUrl(letter.url, deps.s3Client);
42+
return await getDownloadUrl(letter.url, deps.s3Client, deps.env.DOWNLOAD_URL_TTL_SECONDS);
4343
} catch (error) {
4444
if (error instanceof Error && /^Letter with id \w+ not found for supplier \w+$/.test(error.message)) {
4545
throw new NotFoundError(ApiErrorDetail.NotFoundLetterId);
@@ -48,7 +48,7 @@ export const getLetterDataUrl = async (supplierId: string, letterId: string, dep
4848
}
4949
}
5050

51-
async function getPresignedUrl(s3Uri: string, s3Client: S3Client) {
51+
async function getDownloadUrl(s3Uri: string, s3Client: S3Client, expiry: string) {
5252

5353
const url = new URL(s3Uri); // works for s3:// URIs
5454
const bucket = url.hostname;
@@ -59,5 +59,5 @@ async function getPresignedUrl(s3Uri: string, s3Client: S3Client) {
5959
Key: key,
6060
});
6161

62-
return await getSignedUrl(s3Client, command, { expiresIn: 3600 });
62+
return await getSignedUrl(s3Client, command, { expiresIn: Number.parseInt(expiry) });
6363
}

0 commit comments

Comments
 (0)