Skip to content

Commit 5eb5c48

Browse files
authored
Chore: [AEA-5824] - avoid undefined supplier status (#2268)
## Summary - Routine Change ### Details Avoid writing supplierStatus as `undefined` by initialising and checking for out of sequence Notify msgs
1 parent dbe7074 commit 5eb5c48

File tree

5 files changed

+71
-21
lines changed

5 files changed

+71
-21
lines changed
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
import defaultConfig from "../../jest.default.config"
21
import type {JestConfigWithTsJest} from "ts-jest"
2+
import defaultConfig from "../../jest.default.config"
33

44
const jestConfig: JestConfigWithTsJest = {
55
...defaultConfig,
66
rootDir: "./",
7-
setupFiles: ["<rootDir>/.jest/setEnvVars.js"],
8-
coveragePathIgnorePatterns: ["<rootDir>/tests/"],
9-
coverageReporters: [
10-
"clover",
11-
"json",
12-
"text",
13-
["lcov", {projectRoot: "../../"}]
14-
]
7+
setupFiles: ["<rootDir>/.jest/setEnvVars.js"]
158
}
169

1710
export default jestConfig

packages/nhsNotifyLambda/src/utils/dynamo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export async function addPrescriptionMessagesToNotificationStateStore(
3838
SQSMessageID: data.MessageId,
3939
LastNotifiedPrescriptionStatus: data.PSUDataItem.Status,
4040
MessageStatus: data.messageStatus ?? "unknown", // Fall back to unknown if not set
41+
SupplierStatus: "unknown", // set explicitly so can distinguish callback values
4142
NotifyMessageID: data.notifyMessageId, // This is a GSI, but leaving it blank is fine
4243
NotifyMessageReference: data.messageReference,
4344
NotifyMessageBatchReference: data.messageBatchReference, // Will be undefined when request fails

packages/nhsNotifyUpdateCallback/jest.config.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,7 @@ import type {JestConfigWithTsJest} from "ts-jest"
44
const jestConfig: JestConfigWithTsJest = {
55
...defaultConfig,
66
rootDir: "./",
7-
setupFiles: ["<rootDir>/.jest/setEnvVars.js"],
8-
coveragePathIgnorePatterns: ["<rootDir>/tests/"],
9-
coverageReporters: [
10-
"clover",
11-
"json",
12-
"text",
13-
["lcov", {projectRoot: "../../"}]
14-
]
7+
setupFiles: ["<rootDir>/.jest/setEnvVars.js"]
158
}
169

1710
export default jestConfig

packages/nhsNotifyUpdateCallback/src/helpers.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ export function extractStatusesAndDescriptions(logger: Logger, resource: Callbac
133133
messageStatus = resource.attributes.messageStatus
134134
messageStatusDescription = resource.attributes.messageStatusDescription
135135
channelStatus = resource.attributes.channels?.[0]?.channelStatus // If missing, undefined
136-
supplierStatus = undefined
137136
} else if (resource.type === CallbackType.channel) {
138137
messageStatus = undefined
139138
retryCount = resource.attributes.retryCount
@@ -247,10 +246,10 @@ export async function updateNotificationsTable(
247246
// But we don't have enough information to do that so we ignore that edge case and
248247
// count it as a success.
249248
}
249+
const upToDateItems = filterOutOfDateItems(logger, resource, items)
250250

251251
const newExpiry = Math.floor(Date.now() / 1000) + TTL_DELTA
252-
253-
const updatePromises = items.map(async item => {
252+
const updatePromises = upToDateItems.map(async item => {
254253
const key = {
255254
NHSNumber: item.NHSNumber,
256255
RequestId: item.RequestId
@@ -315,3 +314,31 @@ export async function updateNotificationsTable(
315314

316315
await Promise.all(callbackPromises)
317316
}
317+
318+
function filterOutOfDateItems(
319+
logger: Logger,
320+
resource: CallbackResource,
321+
items: Array<LastNotificationStateType>
322+
) {
323+
const upToDateItems = items.filter(item => {
324+
const isOld = item.LastNotificationRequestTimestamp &&
325+
item.LastNotificationRequestTimestamp > resource.attributes.timestamp
326+
if (isOld) {
327+
logger.warn(
328+
"Ignoring out-of-date callback",
329+
{
330+
messageId: resource.attributes.messageId,
331+
messageReference: resource.attributes.messageReference,
332+
lastTimestamp: item.LastNotificationRequestTimestamp,
333+
currentTimestamp: resource.attributes.timestamp
334+
}
335+
)
336+
}
337+
return !isOld
338+
})
339+
logger.info(
340+
"Number of up-to-date items remaining",
341+
{count: upToDateItems.length}
342+
)
343+
return upToDateItems
344+
}

packages/nhsNotifyUpdateCallback/tests/testHelpers.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,41 @@ describe("helpers.ts", () => {
165165
)
166166
})
167167

168+
it("skips update when callback is out of date", async () => {
169+
const msgTimestamp = "2025-01-01T00:00:00.000Z"
170+
const lastTimestamp = "2025-02-01T00:00:00.000Z" // newer than msg payload
171+
const msgPayload: MessageStatusResponse = generateMockMessageStatusResponse([
172+
{
173+
attributes: {
174+
timestamp: msgTimestamp
175+
}
176+
}])
177+
const mockItem = {
178+
NHSNumber: "NHS123",
179+
RequestId: "psu-request-id",
180+
NotifyMessageID: "msg-123",
181+
LastNotificationRequestTimestamp: lastTimestamp
182+
}
183+
sendSpy.mockImplementation((cmd) => {
184+
if (cmd instanceof QueryCommand) {
185+
return Promise.resolve({Items: [mockItem]})
186+
}
187+
})
188+
189+
await updateNotificationsTable(logger, msgPayload)
190+
191+
expect(logger.warn).toHaveBeenCalledWith(
192+
"Ignoring out-of-date callback",
193+
expect.objectContaining({messageId: msgPayload.data[0].attributes.messageId})
194+
)
195+
expect(logger.info).toHaveBeenCalledWith(
196+
"Number of up-to-date items remaining",
197+
expect.objectContaining({count: 0})
198+
)
199+
// Only QueryCommand should be called
200+
expect(sendSpy).toHaveBeenCalledTimes(1)
201+
})
202+
168203
it("updates records when matching items found (Message update callback)", async () => {
169204
const overrideTimestamp = "2025-01-01T00:00:00.000Z"
170205
const mockResponse = generateMockMessageStatusResponse([
@@ -180,7 +215,8 @@ describe("helpers.ts", () => {
180215
const mockItem = {
181216
NHSNumber: "NHS123",
182217
RequestId: "psu-request-id",
183-
NotifyMessageID: "msg-123"
218+
NotifyMessageID: "msg-123",
219+
LastNotificationRequestTimestamp: "2024-01-01T00:00:00.000Z" // old timestamp
184220
}
185221

186222
// First call: QueryCommand

0 commit comments

Comments
 (0)