Skip to content

Commit 003c3e5

Browse files
CCM-13161: Use client config for TTL (#165)
* CCM-13161: Use client config for TTL * CCM-13161: Address review comment * CCM-13539: fix trivy scan failing because of qs * CCM-13161: Merge main * CCM-13161: Merge main
1 parent 74df107 commit 003c3e5

16 files changed

Lines changed: 121 additions & 33 deletions

File tree

docs/package-lock.json

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

infrastructure/terraform/components/dl/module_lambda_ttl_create.tf

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ module "ttl_create" {
3535
log_subscription_role_arn = local.acct.log_subscription_role_arn
3636

3737
lambda_env_vars = {
38+
"ENVIRONMENT" = var.environment
3839
"TTL_TABLE_NAME" = aws_dynamodb_table.ttl.name
39-
"TTL_WAIT_TIME_HOURS" = 24
4040
"TTL_SHARD_COUNT" = local.ttl_shard_count
4141
"EVENT_PUBLISHER_EVENT_BUS_ARN" = aws_cloudwatch_event_bus.main.arn
4242
"EVENT_PUBLISHER_DLQ_URL" = module.sqs_event_publisher_errors.sqs_queue_url
@@ -113,4 +113,19 @@ data "aws_iam_policy_document" "ttl_create_lambda" {
113113
module.sqs_event_publisher_errors.sqs_queue_arn,
114114
]
115115
}
116+
117+
statement {
118+
sid = "AllowSSMParam"
119+
effect = "Allow"
120+
121+
actions = [
122+
"ssm:GetParameter",
123+
"ssm:GetParameters",
124+
"ssm:GetParametersByPath",
125+
]
126+
127+
resources = [
128+
"arn:aws:ssm:${var.region}:${var.aws_account_id}:parameter/${var.component}/${var.environment}/senders/*"
129+
]
130+
}
116131
}

lambdas/refresh-apim-access-token/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
"axios": "1.12.0",
66
"esbuild": "^0.25.9",
77
"jsonwebtoken": "^9.0.2",
8-
"utils": "*",
9-
"qs": "^6.14.0"
8+
"qs": "^6.14.1",
9+
"utils": "*"
1010
},
1111
"devDependencies": {
1212
"@tsconfig/node22": "^22.0.2",

lambdas/ttl-create-lambda/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"dependencies": {
33
"@aws-sdk/lib-dynamodb": "^3.908.0",
44
"digital-letters-events": "^0.0.1",
5+
"sender-management": "^0.0.1",
56
"utils": "^0.0.1"
67
},
78
"devDependencies": {

lambdas/ttl-create-lambda/src/__tests__/container.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jest.mock('infra/config', () => ({
66
eventPublisherEventBusArn: 'test-arn',
77
ttlShardCount: 1,
88
ttlTableName: 'test-table',
9-
ttlWaitTimeHours: 24,
9+
environment: 'test-environment',
1010
})),
1111
}));
1212

@@ -18,12 +18,17 @@ jest.mock('app/create-ttl', () => ({
1818
CreateTtl: jest.fn(() => ({})),
1919
}));
2020

21+
jest.mock('sender-management', () => ({
22+
SenderManagement: jest.fn(() => ({})),
23+
}));
24+
2125
jest.mock('utils', () => ({
2226
EventPublisher: jest.fn(() => ({})),
2327
dynamoClient: {},
2428
eventBridgeClient: {},
2529
logger: {},
2630
sqsClient: {},
31+
ParameterStoreCache: jest.fn(() => ({})),
2732
}));
2833

2934
describe('container', () => {

lambdas/ttl-create-lambda/src/__tests__/infra/ttl-repository.test.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ jest.useFakeTimers();
66

77
const randomNumber = 0.42;
88
const shardCount = 3;
9+
const ttlWaitTimeSeconds = 24 * 60 * 60;
910
const expectedShard = Math.floor(randomNumber * shardCount);
1011
jest.spyOn(Math, 'random').mockReturnValue(randomNumber);
1112

1213
describe('TtlRepository', () => {
1314
let logger: any;
1415
let dynamoClient: any;
16+
let senderRepository: any;
1517
let repo: TtlRepository;
1618
const tableName = 'table';
17-
const ttlWaitTimeHours = 24;
1819
const item: MESHInboxMessageDownloaded = {
1920
id: '550e8400-e29b-41d4-a716-446655440001',
2021
specversion: '1.0',
@@ -40,12 +41,17 @@ describe('TtlRepository', () => {
4041
beforeEach(() => {
4142
logger = { info: jest.fn(), error: jest.fn() };
4243
dynamoClient = { send: jest.fn().mockResolvedValue({}) };
44+
senderRepository = {
45+
getSender: jest
46+
.fn()
47+
.mockResolvedValue({ fallbackWaitTimeSeconds: ttlWaitTimeSeconds }),
48+
};
4349
repo = new TtlRepository(
4450
tableName,
45-
ttlWaitTimeHours,
4651
logger,
4752
dynamoClient,
4853
shardCount,
54+
senderRepository,
4955
);
5056
});
5157

@@ -56,7 +62,6 @@ describe('TtlRepository', () => {
5662
it('logs and inserts item', async () => {
5763
const now = new Date('2020-01-01T12:00:00').getTime();
5864
jest.setSystemTime(now);
59-
const ttlWaitTimeSeconds = ttlWaitTimeHours * 60 * 60;
6065
const expectedTtlSeconds = Math.round(now / 1000) + ttlWaitTimeSeconds;
6166
const expectedTtlDate = new Date(expectedTtlSeconds * 1000)
6267
.toISOString()
@@ -71,6 +76,10 @@ describe('TtlRepository', () => {
7176
}),
7277
);
7378

79+
expect(senderRepository.getSender).toHaveBeenCalledWith({
80+
senderId: item.data.senderId,
81+
});
82+
7483
const putCommand: PutCommand = dynamoClient.send.mock.calls[0][0];
7584
expect(putCommand.input).toStrictEqual({
7685
TableName: tableName,
@@ -109,4 +118,16 @@ describe('TtlRepository', () => {
109118

110119
expect(gsiPk).toMatch(/^\d{4}-\d{2}-\d{2}#\d{1,2}$/);
111120
});
121+
122+
it('throws and logs error when sender not found', async () => {
123+
senderRepository.getSender.mockResolvedValue(null);
124+
125+
await expect(repo.insertTtlRecord(item)).rejects.toThrow(
126+
`Sender not found for sender ID ${item.data.senderId}`,
127+
);
128+
129+
expect(logger.error).toHaveBeenCalledWith({
130+
description: `Sender not found for sender ID ${item.data.senderId}`,
131+
});
132+
});
112133
});

lambdas/ttl-create-lambda/src/container.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
EventPublisher,
3+
ParameterStoreCache,
34
dynamoClient,
45
eventBridgeClient,
56
logger,
@@ -8,22 +9,30 @@ import {
89
import { loadConfig } from 'infra/config';
910
import { TtlRepository } from 'infra/ttl-repository';
1011
import { CreateTtl } from 'app/create-ttl';
12+
import { SenderManagement } from 'sender-management';
1113

1214
export const createContainer = () => {
1315
const {
16+
environment,
1417
eventPublisherDlqUrl,
1518
eventPublisherEventBusArn,
1619
ttlShardCount,
1720
ttlTableName,
18-
ttlWaitTimeHours,
1921
} = loadConfig();
2022

23+
const parameterStore = new ParameterStoreCache();
24+
25+
const senderRepository = SenderManagement({
26+
configOverrides: { environment },
27+
parameterStore,
28+
});
29+
2130
const requestTtlRepository = new TtlRepository(
2231
ttlTableName,
23-
ttlWaitTimeHours,
2432
logger,
2533
dynamoClient,
2634
ttlShardCount,
35+
senderRepository,
2736
);
2837

2938
const createTtl = new CreateTtl(requestTtlRepository, logger);

lambdas/ttl-create-lambda/src/infra/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { defaultConfigReader } from 'utils';
22

33
export type TtlCreateConfig = {
4+
environment: string;
45
ttlTableName: string;
5-
ttlWaitTimeHours: number;
66
ttlShardCount: number;
77
eventPublisherEventBusArn: string;
88
eventPublisherDlqUrl: string;
99
};
1010

1111
export function loadConfig(): TtlCreateConfig {
1212
return {
13+
environment: defaultConfigReader.getValue('ENVIRONMENT'),
1314
ttlTableName: defaultConfigReader.getValue('TTL_TABLE_NAME'),
14-
ttlWaitTimeHours: defaultConfigReader.getInt('TTL_WAIT_TIME_HOURS'),
1515
ttlShardCount: defaultConfigReader.getInt('TTL_SHARD_COUNT'),
1616
eventPublisherEventBusArn: defaultConfigReader.getValue(
1717
'EVENT_PUBLISHER_EVENT_BUS_ARN',

lambdas/ttl-create-lambda/src/infra/ttl-repository.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
11
import { PutCommand, PutCommandOutput } from '@aws-sdk/lib-dynamodb';
22
import { MESHInboxMessageDownloaded } from 'digital-letters-events';
3+
import { ISenderManagement } from 'sender-management';
34
import { Logger } from 'utils';
45

56
interface IDynamoCaller {
67
send: (updateCommand: PutCommand) => Promise<PutCommandOutput>;
78
}
89

910
export class TtlRepository {
10-
private readonly ttlWaitTimeSeconds;
11-
1211
constructor(
1312
private readonly tableName: string,
14-
ttlWaitTimeHours: number,
1513
private readonly logger: Logger,
1614
private readonly dynamoClient: IDynamoCaller,
1715
private readonly shardCount: number,
18-
) {
19-
this.ttlWaitTimeSeconds = ttlWaitTimeHours * 60 * 60;
20-
}
16+
private readonly senderRepository: ISenderManagement,
17+
) {}
2118

2219
public async insertTtlRecord(item: MESHInboxMessageDownloaded) {
23-
const ttlTime = Math.round(Date.now() / 1000) + this.ttlWaitTimeSeconds;
20+
const sender = await this.senderRepository.getSender({
21+
senderId: item.data.senderId,
22+
});
23+
if (!sender) {
24+
this.logger.error({
25+
description: `Sender not found for sender ID ${item.data.senderId}`,
26+
});
27+
throw new Error(`Sender not found for sender ID ${item.data.senderId}`);
28+
}
29+
30+
const ttlTime =
31+
Math.round(Date.now() / 1000) + sender.fallbackWaitTimeSeconds;
2432

2533
this.logger.info({
2634
description: 'Inserting item into TTL table',

package-lock.json

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)