Skip to content

Commit a7feeba

Browse files
authored
Merge branch 'main' into feature/CCM-11189
2 parents 9c4fe30 + 54b0fc2 commit a7feeba

File tree

13 files changed

+286
-146
lines changed

13 files changed

+286
-146
lines changed

docs/assets/diagrams/phase1.png

-3.32 KB
Loading

docs/collections/_consumers/acceptance.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,14 +511,13 @@ Your MI submissions accurately match the letters you've processed.
511511
**Endpoints:**
512512

513513
- POST /mi
514-
- GET /mi
515514

516515
**What this test proves:**
517516
That you can submit MI data in the correct format and confirm it's recorded correctly.
518517

519518
| Criteria | Description |
520519
|---|---|
521-
| **Steps** | - Submit MI for a known number of processed letters. The MI should be grouped by specification and group ID<br>- Retrieve the same data with GET /mi<br>- Reconcile counts and timestamps with your local records. |
520+
| **Steps** | - Submit MI for a known number of processed letters. The MI should be grouped by specification and group ID<br>- Reconcile counts and timestamps with your local records. |
522521
| **Acceptance** | - MI entries appear in NHS Notify within expected timeframe<br>- Counts match processed letters <br>- No duplicates MI entries for the same date or missing records. |
523522
| **Evidence** | MI payloads and responses. |
524523
| **Business value** | Shows that operational and billing data are aligned and complete. |

infrastructure/terraform/components/api/locals.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ locals {
1919
common_lambda_env_vars = {
2020
LETTERS_TABLE_NAME = aws_dynamodb_table.letters.name,
2121
MI_TABLE_NAME = aws_dynamodb_table.mi.name,
22-
LETTER_TTL_HOURS = 24,
22+
LETTER_TTL_HOURS = 12960, # 18 months * 30 days * 24 hours
2323
SUPPLIER_ID_HEADER = "nhsd-supplier-id",
2424
APIM_CORRELATION_HEADER = "nhsd-correlation-id",
25-
DOWNLOAD_URL_TTL_SECONDS = 3600
25+
DOWNLOAD_URL_TTL_SECONDS = 60
2626
}
2727
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ describe('createDependenciesContainer', () => {
3232
jest.mock('../env', () => ({
3333
envVars: {
3434
LETTERS_TABLE_NAME: 'LettersTable',
35-
LETTER_TTL_HOURS: 24,
35+
LETTER_TTL_HOURS: 12960,
3636
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
3737
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
38-
DOWNLOAD_URL_TTL_SECONDS: 3600
38+
DOWNLOAD_URL_TTL_SECONDS: 60
3939
},
4040
}));
4141
});
@@ -56,15 +56,15 @@ describe('createDependenciesContainer', () => {
5656
const repoCtorArgs = (LetterRepository as jest.Mock).mock.calls[0];
5757
expect(repoCtorArgs[2]).toEqual({
5858
lettersTableName: 'LettersTable',
59-
ttlHours: 24
59+
ttlHours: 12960
6060
});
6161

6262
expect(deps.env).toEqual({
6363
LETTERS_TABLE_NAME: 'LettersTable',
64-
LETTER_TTL_HOURS: 24,
64+
LETTER_TTL_HOURS: 12960,
6565
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
6666
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
67-
DOWNLOAD_URL_TTL_SECONDS: 3600
67+
DOWNLOAD_URL_TTL_SECONDS: 60
6868
});
6969
});
7070
});

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,54 @@ describe('lambdaEnv', () => {
1313
});
1414

1515
it('should load all environment variables successfully', () => {
16-
process.env.SUPPLIER_ID_HEADER = 'x-supplier-id';
17-
process.env.APIM_CORRELATION_HEADER = 'x-correlation-id';
16+
process.env.SUPPLIER_ID_HEADER = 'nhsd-supplier-id';
17+
process.env.APIM_CORRELATION_HEADER = 'nhsd-correlation-id';
1818
process.env.LETTERS_TABLE_NAME = 'letters-table';
1919
process.env.MI_TABLE_NAME = 'mi-table';
20-
process.env.LETTER_TTL_HOURS = '24';
21-
process.env.DOWNLOAD_URL_TTL_SECONDS = '3600';
20+
process.env.LETTER_TTL_HOURS = '12960';
21+
process.env.DOWNLOAD_URL_TTL_SECONDS = '60';
22+
process.env.MAX_LIMIT = '2500';
2223

2324
const { envVars } = require('../env');
2425

2526
expect(envVars).toEqual({
26-
SUPPLIER_ID_HEADER: 'x-supplier-id',
27-
APIM_CORRELATION_HEADER: 'x-correlation-id',
27+
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
28+
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
2829
LETTERS_TABLE_NAME: 'letters-table',
2930
MI_TABLE_NAME: 'mi-table',
30-
LETTER_TTL_HOURS: 24,
31-
DOWNLOAD_URL_TTL_SECONDS: 3600
31+
LETTER_TTL_HOURS: 12960,
32+
DOWNLOAD_URL_TTL_SECONDS: 60,
33+
MAX_LIMIT: 2500,
3234
});
3335
});
3436

3537
it('should throw if a required env var is missing', () => {
36-
process.env.SUPPLIER_ID_HEADER = 'x-supplier-id';
37-
process.env.APIM_CORRELATION_HEADER = 'x-correlation-id';
38+
process.env.SUPPLIER_ID_HEADER = 'nhsd-supplier-id';
39+
process.env.APIM_CORRELATION_HEADER = 'nhsd-correlation-id';
3840
process.env.LETTERS_TABLE_NAME = undefined; // simulate missing var
39-
process.env.MI_TABLE_NAME = 'mi-table'; process.env.LETTER_TTL_HOURS = '24';
40-
process.env.DOWNLOAD_URL_TTL_SECONDS = '3600';
41+
process.env.MI_TABLE_NAME = 'mi-table';
42+
process.env.LETTER_TTL_HOURS = '12960';
43+
process.env.DOWNLOAD_URL_TTL_SECONDS = '60';
4144

4245
expect(() => require('../env')).toThrow(ZodError);
4346
});
47+
48+
it('should not throw if optional are not set', () => {
49+
process.env.SUPPLIER_ID_HEADER = 'nhsd-supplier-id';
50+
process.env.APIM_CORRELATION_HEADER = 'nhsd-correlation-id';
51+
process.env.LETTERS_TABLE_NAME = 'letters-table';
52+
process.env.LETTER_TTL_HOURS = '12960';
53+
process.env.DOWNLOAD_URL_TTL_SECONDS = '60';
54+
55+
const { envVars } = require('../env');
56+
57+
expect(envVars).toEqual({
58+
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
59+
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
60+
LETTERS_TABLE_NAME: 'letters-table',
61+
LETTER_TTL_HOURS: 12960,
62+
DOWNLOAD_URL_TTL_SECONDS: 60,
63+
MAX_LIMIT: undefined
64+
});
65+
});
4466
});

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ const EnvVarsSchema = z.object({
66
LETTERS_TABLE_NAME: z.string(),
77
MI_TABLE_NAME: z.string(),
88
LETTER_TTL_HOURS: z.coerce.number().int(),
9-
DOWNLOAD_URL_TTL_SECONDS: z.coerce.number().int()
9+
DOWNLOAD_URL_TTL_SECONDS: z.coerce.number().int(),
10+
MAX_LIMIT: z.coerce.number().int().optional()
1011
});
1112

1213
export type EnvVars = z.infer<typeof EnvVarsSchema>;

lambdas/api-handler/src/contracts/errors.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export enum ApiErrorStatus {
2727

2828
export enum ApiErrorDetail {
2929
NotFoundLetterId = 'No resource found with that ID',
30-
InvalidRequestMissingSupplierId = 'The supplier ID is missing from the request',
3130
InvalidRequestMissingBody = 'The request is missing the body',
3231
InvalidRequestMissingLetterIdPathParameter = 'The request is missing the letter id path parameter',
3332
InvalidRequestLetterIdsMismatch = 'The letter ID in the request body does not match the letter ID path parameter',

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ describe('API Lambda handler', () => {
3434
SUPPLIER_ID_HEADER: 'nhsd-supplier-id',
3535
APIM_CORRELATION_HEADER: 'nhsd-correlation-id',
3636
LETTERS_TABLE_NAME: 'LETTERS_TABLE_NAME',
37-
LETTER_TTL_HOURS: 1,
38-
DOWNLOAD_URL_TTL_SECONDS: 1
37+
LETTER_TTL_HOURS: 12960,
38+
DOWNLOAD_URL_TTL_SECONDS: 60
3939
} as unknown as EnvVars
4040
} as Deps;
4141

@@ -48,8 +48,13 @@ describe('API Lambda handler', () => {
4848
const mockedGetLetterDataUrlService = letterService.getLetterDataUrl as jest.Mock;
4949
mockedGetLetterDataUrlService.mockResolvedValue('https://somePreSignedUrl.com');
5050

51-
const event = makeApiGwEvent({path: '/letters/letter1/data',
52-
headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId'},
51+
const event = makeApiGwEvent({
52+
path: '/letters/letter1/data',
53+
headers: {
54+
'nhsd-supplier-id': 'supplier1',
55+
'nhsd-correlation-id': 'correlationId',
56+
'x-request-id': 'requestId'
57+
},
5358
pathParameters: {id: 'id1'}
5459
});
5560
const context = mockDeep<Context>();
@@ -67,7 +72,7 @@ describe('API Lambda handler', () => {
6772
});
6873
});
6974

70-
it('returns 400 for missing supplier ID (empty headers)', async () => {
75+
it('returns error if headers are empty', async () => {
7176
const event = makeApiGwEvent({ path: '/letters/letter1/data', headers: {},
7277
pathParameters: {id: 'id1'}
7378
});
@@ -81,11 +86,14 @@ describe('API Lambda handler', () => {
8186
expect(result).toEqual(expectedErrorResponse);
8287
});
8388

84-
it('returns 500 if correlation id not provided in request', async () => {
89+
it('returns error if correlation id not provided in request', async () => {
8590
const event = makeApiGwEvent({
8691
path: '/letters/letter1/data',
8792
queryStringParameters: { limit: '2000' },
88-
headers: {'nhsd-supplier-id': 'supplier1'},
93+
headers: {
94+
'nhsd-supplier-id': 'supplier1',
95+
'x-request-id': 'requestId'
96+
},
8997
pathParameters: {id: 'id1'}
9098
});
9199
const context = mockDeep<Context>();
@@ -101,7 +109,11 @@ describe('API Lambda handler', () => {
101109
it('returns error response when path parameter letterId is not found', async () => {
102110
const event = makeApiGwEvent({
103111
path: '/letters/',
104-
headers: {'nhsd-supplier-id': 'supplier1', 'nhsd-correlation-id': 'correlationId'}
112+
headers: {
113+
'nhsd-supplier-id': 'supplier1',
114+
'nhsd-correlation-id': 'correlationId',
115+
'x-request-id': 'requestId'
116+
},
105117
});
106118
const context = mockDeep<Context>();
107119
const callback = jest.fn();

0 commit comments

Comments
 (0)