Skip to content

Commit 698eede

Browse files
authored
Fix: [AEA-0000] - Update how the callback is handled. (#2132)
## Summary - Routine Change ### Details The callback lambda currently kinda works, but not really. It rejects requests that lack a request ID, and notify do not provide one, so that needs to be fixed. Also, I realised I never unit tested the new callback type, so I did that. Additionally, the logging of incoming updates is not really in line with how statuses actually flow. I have tweaked it to be more generalised.
1 parent 42cd1ab commit 698eede

10 files changed

Lines changed: 218 additions & 80 deletions

File tree

packages/common/commonTypes/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ export interface LastNotificationStateType {
2626
ODSCode: string
2727
RequestId: string // x-request-id header
2828
SQSMessageID?: string // The SQS message ID
29-
DeliveryStatus: string
29+
MessageStatus: string
30+
// These are undefined until we get callbacks from Notify
31+
ChannelStatus?: string
32+
SupplierStatus?: string
3033
NotifyMessageID?: string // The UUID we got back from Notify for the submitted message
3134
NotifyMessageReference: string // The references we generated for the message
3235
NotifyMessageBatchReference?: string // As above

packages/nhsNotifyLambda/src/utils/dynamo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export async function addPrescriptionMessagesToNotificationStateStore(
3737
RequestId: data.PSUDataItem.RequestID,
3838
SQSMessageID: data.MessageId,
3939
LastNotifiedPrescriptionStatus: data.PSUDataItem.Status,
40-
DeliveryStatus: data.deliveryStatus ?? "unknown", // Fall back to unknown if not set
40+
MessageStatus: data.messageStatus ?? "unknown", // Fall back to unknown if not set
4141
NotifyMessageID: data.notifyMessageId, // This is a GSI, but leaving it blank is fine
4242
NotifyMessageReference: data.messageReference,
4343
NotifyMessageBatchReference: data.messageBatchReference, // Will be undefined when request fails

packages/nhsNotifyLambda/src/utils/notify.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export async function makeBatchNotifyRequest(
108108
logger.info("Not doing real Notify requests. Simply waiting for some time and returning success on all messages")
109109
await new Promise(f => setTimeout(f, DUMMY_NOTIFY_DELAY_MS))
110110

111-
const deliveryStatus = "silent running"
111+
const messageStatus = "silent running"
112112

113113
logger.info("Requested notifications OK!", {
114114
messageBatchReference,
@@ -117,15 +117,15 @@ export async function makeBatchNotifyRequest(
117117
messageReference: e.messageReference,
118118
psuRequestId: data.find((el) => el.messageReference === e.messageReference)?.PSUDataItem.RequestID
119119
})),
120-
deliveryStatus
120+
deliveryStatus: messageStatus // TODO: change splunk report query to messageStatus
121121
})
122122

123123
// Map each input item to a "successful" NotifyDataItemMessage
124124
return data.map(item => {
125125
return {
126126
...item,
127127
messageBatchReference,
128-
deliveryStatus,
128+
messageStatus,
129129
notifyMessageId: v4() // Create a dummy UUID
130130
}
131131
})
@@ -169,7 +169,7 @@ export async function makeBatchNotifyRequest(
169169
messageReference: e.messageReference,
170170
psuRequestId: data.find((el) => el.messageReference === e.messageReference)?.PSUDataItem.RequestID
171171
})),
172-
deliveryStatus: "requested"
172+
deliveryStatus: "requested" // TODO: change splunk report query to messageStatus
173173
})
174174

175175
// Map each input item to a NotifyDataItemMessage, marking success and attaching the notify ID
@@ -182,7 +182,7 @@ export async function makeBatchNotifyRequest(
182182
return {
183183
...item,
184184
messageBatchReference,
185-
deliveryStatus: match ? "requested" : "notify request failed",
185+
messageStatus: match ? "requested" : "notify request failed",
186186
notifyMessageId: match?.id
187187
}
188188
})
@@ -197,7 +197,7 @@ export async function makeBatchNotifyRequest(
197197
messageReference: e.messageReference,
198198
psuRequestId: data.find((el) => el.messageReference === e.messageReference)?.PSUDataItem.RequestID
199199
})),
200-
deliveryStatus: "notify request failed"
200+
messageStatus: "notify request failed"
201201
})
202202
throw new Error("Notify batch request failed")
203203
}
@@ -206,7 +206,7 @@ export async function makeBatchNotifyRequest(
206206
logger.error("Notify batch request failed", {error})
207207
return data.map(item => ({
208208
...item,
209-
deliveryStatus: "notify request failed",
209+
messageStatus: "notify request failed",
210210
messageBatchReference,
211211
messageReferences: messages.map(e => ({
212212
nhsNumber: e.recipient.nhsNumber,

packages/nhsNotifyLambda/src/utils/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {NotifyDataItem} from "@PrescriptionStatusUpdate_common/commonTypes"
55
// and helps track the nhs notify results
66
export interface NotifyDataItemMessage extends Message {
77
PSUDataItem: NotifyDataItem
8-
deliveryStatus?: string
8+
messageStatus?: string
99
messageBatchReference?: string,
1010
// message reference is our internal UUID for the message
1111
messageReference: string

packages/nhsNotifyLambda/tests/testUtils.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,14 @@ describe("NHS notify lambda helper functions", () => {
509509
expect(result).toHaveLength(2)
510510
expect(result[0]).toMatchObject({
511511
PSUDataItem: data[0].PSUDataItem,
512-
deliveryStatus: "requested",
512+
messageStatus: "requested",
513513
notifyMessageId: "msg-id-1",
514514
messageBatchReference: expect.any(String),
515515
messageReference: expect.any(String)
516516
})
517517
expect(result[1]).toMatchObject({
518518
PSUDataItem: data[1].PSUDataItem,
519-
deliveryStatus: "notify request failed",
519+
messageStatus: "notify request failed",
520520
notifyMessageId: undefined,
521521
messageBatchReference: expect.any(String),
522522
messageReference: expect.any(String)
@@ -549,7 +549,7 @@ describe("NHS notify lambda helper functions", () => {
549549
expect(result).toMatchObject([
550550
{
551551
PSUDataItem: data[0].PSUDataItem,
552-
deliveryStatus: "notify request failed",
552+
messageStatus: "notify request failed",
553553
notifyMessageId: undefined,
554554
messageBatchReference: expect.any(String),
555555
messageReference: expect.any(String)
@@ -598,7 +598,7 @@ describe("NHS notify lambda helper functions", () => {
598598
result.forEach((r) =>
599599
expect(r).toEqual(
600600
expect.objectContaining({
601-
deliveryStatus: "notify request failed",
601+
messageStatus: "notify request failed",
602602
notifyMessageId: undefined
603603
})
604604
)
@@ -745,14 +745,14 @@ describe("NHS notify lambda helper functions", () => {
745745
expect(result).toHaveLength(2)
746746
expect(result[0]).toMatchObject({
747747
PSUDataItem: data[0].PSUDataItem,
748-
deliveryStatus: "silent running",
748+
messageStatus: "silent running",
749749
notifyMessageId: expect.any(String), // it will be assigned a dummy ID
750750
messageBatchReference: expect.any(String),
751751
messageReference: expect.any(String)
752752
})
753753
expect(result[1]).toMatchObject({
754754
PSUDataItem: data[1].PSUDataItem,
755-
deliveryStatus: "silent running",
755+
messageStatus: "silent running",
756756
notifyMessageId: expect.any(String),
757757
messageBatchReference: expect.any(String),
758758
messageReference: expect.any(String)

packages/nhsNotifyUpdateCallback/src/helpers.ts

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,30 @@ export async function updateNotificationsTable(
130130
// For each callback resource, return a promise
131131
const callbackPromises = bodyData.data.map(async (resource) => {
132132
let messageId: string
133-
let messageStatus: string
133+
let messageStatus: string | undefined
134+
let channelStatus: string | undefined
135+
let supplierStatus: string | undefined
134136
let timestamp: string
135137

136138
if (resource.type === CallbackType.message) {
137139
messageId = resource.attributes.messageId
138140
messageStatus = resource.attributes.messageStatus
141+
channelStatus = resource.attributes.channels?.[0]?.channelStatus // If missing, undefined
142+
supplierStatus = undefined
139143
timestamp = resource.attributes.timestamp
140144
} else if (resource.type === CallbackType.channel) {
141145
messageId = resource.attributes.messageId
142-
messageStatus = resource.attributes.channelStatus
146+
messageStatus = undefined
147+
channelStatus = resource.attributes.channelStatus
148+
supplierStatus = resource.attributes.supplierStatus
143149
timestamp = resource.attributes.timestamp
144150
} else {
145151
logger.error("Unknown data structure - cannot store to notifications table.", {resource})
146152
// Set to junk data, so that when we try and update the table we will fail. This is fine, and handled later.
147153
messageId = "unknown"
148-
messageStatus = "unknown"
154+
messageStatus = undefined
155+
channelStatus = undefined
156+
supplierStatus = undefined
149157
timestamp = "unknown"
150158
}
151159

@@ -190,28 +198,54 @@ export async function updateNotificationsTable(
190198
NHSNumber: item.NHSNumber,
191199
RequestId: item.RequestId
192200
}
201+
202+
// Build UpdateExpression so undefined statuses are not written/updated.
203+
const sets: Array<string> = [
204+
"LastNotificationRequestTimestamp = :ts",
205+
"ExpiryTime = :et"
206+
]
207+
const eav: Record<string, unknown> = {
208+
":ts": timestamp,
209+
":et": newExpiry
210+
}
211+
212+
if (messageStatus !== undefined) {
213+
sets.push("MessageStatus = :ms")
214+
eav[":ms"] = messageStatus
215+
}
216+
if (channelStatus !== undefined) {
217+
sets.push("ChannelStatus = :cs")
218+
eav[":cs"] = channelStatus
219+
}
220+
if (supplierStatus !== undefined) {
221+
sets.push("SupplierStatus = :ss")
222+
eav[":ss"] = supplierStatus
223+
}
224+
225+
const UpdateExpression = `SET ${sets.join(", ")}`
226+
193227
try {
194228
await docClient.send(new UpdateCommand({
195229
TableName: dynamoTable,
196230
Key: key,
197-
UpdateExpression: [
198-
"SET DeliveryStatus = :ds",
199-
" , LastNotificationRequestTimestamp = :ts",
200-
" , ExpiryTime = :et"
201-
].join(""),
202-
ExpressionAttributeValues: {
203-
":ds": messageStatus,
204-
":ts": timestamp,
205-
":et": newExpiry
206-
}
231+
UpdateExpression,
232+
ExpressionAttributeValues: eav
207233
}))
234+
208235
logger.info(
209236
"Updated notification state",
210237
{
211238
NotifyMessageID: item.NotifyMessageID,
212239
nhsNumber: item.NHSNumber,
213240
psuRequestId: item.RequestId,
214-
newStatus: messageStatus,
241+
// The overall delivery status is whichever of
242+
// messageStatus or channelStatus is defined (prefer messageStatus)
243+
// TODO: Update the splunk query to use the below statuses
244+
deliveryStatus: messageStatus ?? channelStatus,
245+
// Parse to a string, or else undefined stuff doesn't get logged (thanks aws)
246+
messageStatus: `${messageStatus}`,
247+
channelStatus: `${channelStatus}`,
248+
supplierStatus: `${supplierStatus}`,
215249
newTimestamp: timestamp,
216250
newExpiryTime: newExpiry
217251
}

packages/nhsNotifyUpdateCallback/src/lambdaHandler.ts

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,10 @@ export const logger = new Logger({serviceName: "nhsNotifyUpdateCallback"})
1616

1717
const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
1818
logger.appendKeys({
19-
"x-correlation-id": event.headers["x-correlation-id"],
2019
"apigw-request-id": event.headers["apigw-request-id"],
21-
"x-request-id": event.headers["x-request-id"]
20+
"nhsd-correlation-id": event.headers["nhsd-correlation-id"]
2221
})
2322

24-
// Require a request ID
25-
if (!event.headers["x-request-id"]) return response(400, {message: "No x-request-id given"})
26-
2723
// Check the request signature
2824
const isErr = await checkSignature(logger, event)
2925
if (isErr) return isErr
@@ -39,34 +35,26 @@ const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayPro
3935
}
4036

4137
let receivedUnknownCallbackType = false
38+
4239
payload.data.forEach(m => {
43-
let logPayload = {}
44-
if (m.type === CallbackType.message) {
45-
logPayload = {
46-
callbackType: m.type,
47-
messageStatus: m.attributes.messageStatus,
48-
messageReference: m.attributes.messageReference,
49-
messageId: m.attributes.messageId,
50-
receivedTimestamp: m.attributes.timestamp
51-
}
40+
let logPayload = {
41+
callbackType: m.type,
42+
messageId: m.attributes.messageId,
43+
// Only defined for message status callbacks
44+
messageStatus: m.type === CallbackType.message ? m.attributes.messageStatus : undefined,
45+
// Only defined for channel/supplier status callbacks
46+
channelStatus: m.type === CallbackType.channel ? m.attributes.channelStatus : undefined,
47+
supplierStatus: m.type === CallbackType.channel ? m.attributes.supplierStatus : undefined,
48+
retryCount: m.type === CallbackType.channel ? m.attributes.retryCount : undefined,
49+
callbackMessageTimestamp: m.attributes.timestamp,
50+
messageReference: m.attributes.messageReference
51+
}
52+
logger.info("Message state updated", logPayload)
5253

53-
} else if (m.type === CallbackType.channel) {
54-
logPayload = {
55-
callbackType: m.type,
56-
messageStatus: m.attributes.channelStatus,
57-
supplierStatus: m.attributes.supplierStatus ?? "not given",
58-
retryCount: m.attributes.retryCount,
59-
messageReference: m.attributes.messageReference,
60-
messageId: m.attributes.messageId,
61-
receivedTimestamp: m.attributes.timestamp
62-
}
63-
} else {
54+
if ((m.type !== CallbackType.message) && (m.type !== CallbackType.channel)) {
6455
logger.warn("Unknown callback data structure.", {data: m})
6556
receivedUnknownCallbackType = true
6657
}
67-
68-
// If we have populated the logPayload object, then log it.
69-
if (Object.keys(logPayload).length > 0) logger.info("Message state updated", logPayload)
7058
})
7159

7260
try {

0 commit comments

Comments
 (0)