Skip to content

Commit cffd242

Browse files
authored
Merge branch 'main' into feature/prod-smoke-letter
2 parents 544d6a6 + 4400b7e commit cffd242

File tree

32 files changed

+2236
-768
lines changed

32 files changed

+2236
-768
lines changed

.github/workflows/cicd-3-deploy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ jobs:
103103
run: |
104104
gh release download ${{steps.get-asset-version.outputs.release_version}} -p jekyll-docs-*.tar --output artifact.tar
105105
106-
- uses: actions/upload-artifact@v6
106+
- uses: actions/upload-artifact@v7
107107
with:
108108
name: jekyll-docs-${{steps.get-asset-version.outputs.release_version}}
109109
path: artifact.tar

.github/workflows/scorecard.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
6060
# format to the repository Actions tab.
6161
- name: "Upload artifact"
62-
uses: actions/upload-artifact@v6
62+
uses: actions/upload-artifact@v7
6363
with:
6464
name: SARIF file
6565
path: results.sarif
@@ -68,6 +68,6 @@ jobs:
6868
# Upload the results to GitHub's code scanning dashboard (optional).
6969
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
7070
- name: "Upload to code-scanning"
71-
uses: github/codeql-action/upload-sarif@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
71+
uses: github/codeql-action/upload-sarif@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v4.33.0
7272
with:
7373
sarif_file: results.sarif

.github/workflows/stage-2-test.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,14 @@ jobs:
105105
run: |
106106
make test-unit
107107
- name: "Save the result of fast test suite"
108-
uses: actions/upload-artifact@v6
108+
uses: actions/upload-artifact@v7
109109
with:
110110
name: unit-tests
111111
path: "**/.reports/unit"
112112
include-hidden-files: true
113113
if: always()
114114
- name: "Save the result of code coverage"
115-
uses: actions/upload-artifact@v6
115+
uses: actions/upload-artifact@v7
116116
with:
117117
name: code-coverage-report
118118
path: ".reports/lcov.info"
@@ -241,7 +241,7 @@ jobs:
241241
with:
242242
fetch-depth: 0 # Full history is needed to improving relevancy of reporting
243243
- name: "Download coverage report for SONAR"
244-
uses: actions/download-artifact@v6
244+
uses: actions/download-artifact@v8
245245
with:
246246
name: code-coverage-report
247247
- name: "Perform static analysis"

.github/workflows/stage-5-publish.yaml

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,50 +49,50 @@ jobs:
4949
uses: actions/checkout@v5
5050

5151
- name: "Get the artefacts 1"
52-
uses: actions/download-artifact@v6
52+
uses: actions/download-artifact@v8
5353
with:
5454
path: ./artifacts/jekyll-docs-${{ inputs.version }}
5555
name: jekyll-docs-${{ inputs.version }}
5656

5757
- name: "Get the artefacts 2"
58-
uses: actions/download-artifact@v6
58+
uses: actions/download-artifact@v8
5959
with:
6060
path: ./artifacts/sdk-html-docs-${{ inputs.version }}
6161
name: sdk-html-docs-${{ inputs.version }}
6262

6363
- name: "Get the artefacts 3"
64-
uses: actions/download-artifact@v6
64+
uses: actions/download-artifact@v8
6565
with:
6666
path: ./artifacts/sdk-swagger-docs-${{ inputs.version }}
6767
name: sdk-swagger-docs-${{ inputs.version }}
6868

6969
- name: "Get the artefacts 4"
70-
uses: actions/download-artifact@v6
70+
uses: actions/download-artifact@v8
7171
with:
7272
path: ./artifacts/sdk-html-${{ inputs.version }}
7373
name: sdk-html-${{ inputs.version }}
7474

7575
- name: "Get the artefacts 5"
76-
uses: actions/download-artifact@v6
76+
uses: actions/download-artifact@v8
7777
with:
7878
path: ./artifacts/sdk-ts-${{ inputs.version }}
7979
name: sdk-ts-${{ inputs.version }}
8080

8181
- name: "Get the artefacts 6"
82-
uses: actions/download-artifact@v6
82+
uses: actions/download-artifact@v8
8383
with:
8484
path: ./artifacts/sdk-python-${{ inputs.version }}
8585
name: sdk-python-${{ inputs.version }}
8686

8787
- name: "Get the artefacts 7"
88-
uses: actions/download-artifact@v6
88+
uses: actions/download-artifact@v8
8989
with:
9090
path: ./artifacts/sdk-csharp-${{ inputs.version }}
9191
name: sdk-csharp-${{ inputs.version }}
9292

9393
# Take out for now - might add again in the future
9494
# - name: "Get the artefacts 9"
95-
# uses: actions/download-artifact@v6
95+
# uses: actions/download-artifact@v8
9696
# with:
9797
# path: ./artifacts/server-csharp-${{ inputs.version }}
9898
# name: server-csharp-${{ inputs.version }}
@@ -235,7 +235,7 @@ jobs:
235235
apimEnv: [internal-dev, int, ref, prod]
236236
steps:
237237
- name: "Download OAS spec artifact"
238-
uses: actions/download-artifact@v6
238+
uses: actions/download-artifact@v8
239239
with:
240240
path: ./artifacts/api-oas-specification-${{ matrix.apimEnv }}-${{ inputs.version }}
241241
name: api-oas-specification-${{ matrix.apimEnv }}-${{ inputs.version }}
@@ -266,12 +266,12 @@ jobs:
266266
# contents: read
267267
# steps:
268268
# - name: "Get the artefacts csharp docker"
269-
# uses: actions/download-artifact@v6
269+
# uses: actions/download-artifact@v8
270270
# with:
271271
# path: .
272272
# name: server-csharp-docker-${{ inputs.version }}
273273
# - name: "Get the artefacts csharp server"
274-
# uses: actions/download-artifact@v6
274+
# uses: actions/download-artifact@v8
275275
# with:
276276
# path: ./csharp-server
277277
# name: server-csharp-${{ inputs.version }}
@@ -293,7 +293,7 @@ jobs:
293293
contents: read
294294
steps:
295295
- name: "Get the artefacts"
296-
uses: actions/download-artifact@v6
296+
uses: actions/download-artifact@v8
297297
with:
298298
path: .
299299
name: sdk-csharp-${{ inputs.version }}
@@ -349,7 +349,7 @@ jobs:
349349
contents: read
350350
steps:
351351
- name: "Get the artefacts"
352-
uses: actions/download-artifact@v6
352+
uses: actions/download-artifact@v8
353353
with:
354354
path: .
355355
name: sdk-ts-${{ inputs.version }}
@@ -378,7 +378,7 @@ jobs:
378378
run: echo "secret_exist=${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL != '' }}" >> $GITHUB_OUTPUT
379379
- name: "Notify on publishing packages"
380380
if: steps.check.outputs.secret_exist == 'true'
381-
uses: nhs-england-tools/notify-msteams-action@v1.0.0
381+
uses: nhs-england-tools/notify-msteams-action@v1.0.5
382382
with:
383383
github-token: ${{ secrets.GITHUB_TOKEN }}
384384
teams-webhook-url: ${{ secrets.TEAMS_NOTIFICATION_WEBHOOK_URL }}
@@ -453,7 +453,7 @@ jobs:
453453
# contents: read
454454
# steps:
455455
# - name: "Get the artefacts"
456-
# uses: actions/download-artifact@v6
456+
# uses: actions/download-artifact@v8
457457
# with:
458458
# path: .
459459
# name: libs-letter-${{ inputs.version }}

docs/Gemfile.lock

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,11 @@ GEM
105105
jekyll-seo-tag (~> 2.1)
106106
minitest (5.24.1)
107107
mutex_m (0.2.0)
108-
nokogiri (1.18.9-aarch64-linux-gnu)
108+
nokogiri (1.19.1-aarch64-linux-gnu)
109109
racc (~> 1.4)
110-
nokogiri (1.18.9-arm64-darwin)
111-
nokogiri (1.18.9-x86_64-linux-gnu)
110+
nokogiri (1.19.1-arm64-darwin)
111+
racc (~> 1.4)
112+
nokogiri (1.19.1-x86_64-linux-gnu)
112113
racc (~> 1.4)
113114
pathutil (0.16.2)
114115
forwardable-extended (~> 2.6)

infrastructure/terraform/components/api/ddb_table_letter_queue.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ resource "aws_dynamodb_table" "letter_queue" {
33
billing_mode = "PAY_PER_REQUEST"
44

55
hash_key = "supplierId"
6-
range_key = "queueTimestamp"
6+
range_key = "letterId"
77

88
ttl {
99
attribute_name = "ttl"
1010
enabled = true
1111
}
1212

1313
local_secondary_index {
14-
name = "letterId-index"
15-
range_key = "letterId"
14+
name = "queueSortOrder-index"
15+
range_key = "queueTimestamp"
1616
projection_type = "ALL"
1717
}
1818

infrastructure/terraform/components/api/module_lambda_update_letter_queue.tf

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module "update_letter_queue" {
3535
log_subscription_role_arn = local.acct.log_subscription_role_arn
3636

3737
lambda_env_vars = merge(local.common_lambda_env_vars, {
38-
LETTER_QUEUE_TABLE_NAME = aws_dynamodb_table.letter_queue.name,
38+
LETTER_QUEUE_TABLE_NAME = "${local.csi}-letter-queue",
3939
LETTER_QUEUE_TTL_HOURS = 168 # 7 days
4040
})
4141
}
@@ -47,11 +47,12 @@ data "aws_iam_policy_document" "update_letter_queue_lambda" {
4747

4848
actions = [
4949
"dynamodb:PutItem",
50+
"dynamodb:DeleteItem",
5051
]
5152

5253
resources = [
53-
aws_dynamodb_table.letter_queue.arn,
54-
"${aws_dynamodb_table.letter_queue.arn}/index/*"
54+
"arn:aws:dynamodb:${var.region}:${var.aws_account_id}:table/${local.csi}-letter-queue",
55+
"arn:aws:dynamodb:${var.region}:${var.aws_account_id}:table/${local.csi}-letter-queue/index/*"
5556
]
5657
}
5758

internal/datastore/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"dependencies": {
33
"@aws-sdk/client-dynamodb": "^3.984.0",
4-
"@aws-sdk/lib-dynamodb": "^3.984.0",
4+
"@aws-sdk/lib-dynamodb": "^3.1008.0",
55
"@internal/helpers": "*",
66
"pino": "^10.3.0",
77
"zod": "^4.1.11",

internal/datastore/src/__test__/db.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ const createLetterQueueTableCommand = new CreateTableCommand({
129129
],
130130
LocalSecondaryIndexes: [
131131
{
132-
IndexName: "timestamp-index",
132+
IndexName: "queueSortOrder-index",
133133
KeySchema: [
134134
{ AttributeName: "supplierId", KeyType: "HASH" }, // Partition key for LSI
135135
{ AttributeName: "queueTimestamp", KeyType: "RANGE" }, // Sort key for LSI

internal/datastore/src/__test__/letter-queue-repository.test.ts

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { GetCommand } from "@aws-sdk/lib-dynamodb";
12
import { Logger } from "pino";
23
import {
34
DBContext,
@@ -7,8 +8,9 @@ import {
78
} from "./db";
89
import LetterQueueRepository from "../letter-queue-repository";
910
import { InsertPendingLetter } from "../types";
10-
import { LetterAlreadyExistsError } from "../errors";
11+
import { LetterAlreadyExistsError } from "../letter-already-exists-error";
1112
import { createTestLogger } from "./logs";
13+
import { LetterDoesNotExistError } from "../letter-does-not-exist-error";
1214

1315
function createLetter(letterId = "letter1"): InsertPendingLetter {
1416
return {
@@ -51,32 +53,19 @@ describe("LetterQueueRepository", () => {
5153
await db.container.stop();
5254
});
5355

54-
function assertTtl(ttl: number, before: number, after: number) {
55-
const expectedLower = Math.floor(
56-
before / 1000 + 60 * 60 * db.config.letterQueueTtlHours,
57-
);
58-
const expectedUpper = Math.floor(
59-
after / 1000 + 60 * 60 * db.config.lettersTtlHours,
60-
);
61-
expect(ttl).toBeGreaterThanOrEqual(expectedLower);
62-
expect(ttl).toBeLessThanOrEqual(expectedUpper);
63-
}
64-
6556
describe("putLetter", () => {
6657
it("adds a letter to the database", async () => {
67-
const before = Date.now();
58+
jest.useFakeTimers().setSystemTime(new Date("2026-03-04T13:15:45.000Z"));
6859

6960
const pendingLetter =
7061
await letterQueueRepository.putLetter(createLetter());
7162

72-
const after = Date.now();
73-
74-
const timestampInMillis = new Date(
75-
pendingLetter.queueTimestamp,
76-
).valueOf();
77-
expect(timestampInMillis).toBeGreaterThanOrEqual(before);
78-
expect(timestampInMillis).toBeLessThanOrEqual(after);
79-
assertTtl(pendingLetter.ttl, before, after);
63+
expect(pendingLetter.queueTimestamp).toBe("2026-03-04T13:15:45.000Z");
64+
expect(pendingLetter.visibilityTimestamp).toBe(
65+
"2026-03-04T13:15:45.000Z",
66+
);
67+
expect(pendingLetter.ttl).toBe(1_772_633_745);
68+
expect(await letterExists(db, "supplier1", "letter1")).toBe(true);
8069
});
8170

8271
it("throws LetterAlreadyExistsError when creating a letter which already exists", async () => {
@@ -101,4 +90,48 @@ describe("LetterQueueRepository", () => {
10190
).rejects.toThrow("Cannot do operations on a non-existent table");
10291
});
10392
});
93+
94+
describe("deleteLetter", () => {
95+
it("deletes a letter from the database", async () => {
96+
await letterQueueRepository.putLetter(createLetter());
97+
98+
await letterQueueRepository.deleteLetter("supplier1", "letter1");
99+
100+
expect(await letterExists(db, "supplier1", "letter1")).toBe(false);
101+
});
102+
103+
it("throws an error when the letter does not exist", async () => {
104+
await expect(
105+
letterQueueRepository.deleteLetter("supplier1", "letter1"),
106+
).rejects.toThrow(LetterDoesNotExistError);
107+
});
108+
109+
it("rethrows errors from DynamoDB when deleting a letter", async () => {
110+
const misconfiguredRepository = new LetterQueueRepository(
111+
db.docClient,
112+
logger,
113+
{
114+
...db.config,
115+
letterQueueTableName: "nonexistent-table",
116+
},
117+
);
118+
await expect(
119+
misconfiguredRepository.deleteLetter("supplier1", "letter1"),
120+
).rejects.toThrow("Cannot do operations on a non-existent table");
121+
});
122+
});
104123
});
124+
125+
async function letterExists(
126+
db: DBContext,
127+
supplierId: string,
128+
letterId: string,
129+
): Promise<boolean> {
130+
const result = await db.docClient.send(
131+
new GetCommand({
132+
TableName: db.config.letterQueueTableName,
133+
Key: { supplierId, letterId },
134+
}),
135+
);
136+
return result.Item !== undefined;
137+
}

0 commit comments

Comments
 (0)