Skip to content

Commit f114b63

Browse files
WIP
1 parent f5708be commit f114b63

File tree

10 files changed

+223
-87
lines changed

10 files changed

+223
-87
lines changed

lambdas/core-notifier/src/__tests__/app/notify-api-client.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ import axios, {
55
type AxiosResponse,
66
type AxiosStatic,
77
} from 'axios';
8-
import {
9-
RetryErrorConditionFn,
10-
conditionalRetry as _retry,
11-
} from 'utils';
8+
import { RetryErrorConditionFn, conditionalRetry as _retry } from 'utils';
129
import type { Logger } from 'utils';
1310
import { mockRequest1, mockResponse } from '__tests__/constants';
1411
import { IAccessTokenRepository, NotifyClient } from 'app/notify-api-client';

lambdas/core-notifier/src/apis/sqs-handler.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ import type {
44
SQSEvent,
55
SQSRecord,
66
} from 'aws-lambda';
7-
import { Logger } from 'utils';
8-
// import { SenderManagement } from 'sender-management';
7+
import { Logger, Sender } from 'utils';
8+
import { PDMResourceAvailable } from 'digital-letters-events';
99
import { mapQueueToRequest } from 'domain/mapper';
1010
import { parseSqsRecord } from 'app/parse-sqs-message';
1111

1212
import type { NotifyMessageProcessor } from 'app/notify-message-processor';
13-
import { SenderRepository } from 'sender-management/src/infra/sender-repository/repository';
13+
import { SenderManagement } from 'sender-management';
1414

1515
export interface SqsHandlerDependencies {
1616
notifyMessageProcessor: NotifyMessageProcessor;
1717
logger: Logger;
18-
senderRepository: SenderRepository;
18+
senderManagement: SenderManagement;
1919
}
2020

2121
export const createHandler = ({
22-
notifyMessageProcessor,
2322
logger,
24-
senderRepository,
23+
notifyMessageProcessor,
24+
senderManagement,
2525
}: SqsHandlerDependencies) =>
2626
async function handler(sqsEvent: SQSEvent): Promise<SQSBatchResponse> {
2727
const receivedItemCount = sqsEvent.Records.length;
@@ -33,8 +33,15 @@ export const createHandler = ({
3333
await Promise.all(
3434
sqsEvent.Records.map(async (sqsRecord: SQSRecord) => {
3535
try {
36-
const incoming = parseSqsRecord(sqsRecord, logger);
37-
const request = mapQueueToRequest(incoming, senderRepository);
36+
const incoming: PDMResourceAvailable = parseSqsRecord(
37+
sqsRecord,
38+
logger,
39+
);
40+
const sender: Sender = senderManagement.getSender(
41+
incoming.data.senderId,
42+
);
43+
44+
const request = mapQueueToRequest(incoming, senderManagement);
3845
await notifyMessageProcessor.process(request);
3946
} catch (error: any) {
4047
logger.warn({

lambdas/core-notifier/src/app/notify-api-client.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,9 @@ import type {
66
SingleMessageResponse,
77
} from 'domain/request';
88
import { RequestAlreadyReceivedError } from 'domain/request-already-received-error';
9-
import {
10-
IAccessibleService,
11-
RetryConfig,
12-
conditionalRetry,
13-
} from 'utils';
9+
import { IAccessibleService, RetryConfig, conditionalRetry } from 'utils';
1410
import type { Logger } from 'utils';
1511

16-
1712
export interface IAccessTokenRepository {
1813
getAccessToken(): Promise<string>;
1914
}
@@ -29,9 +24,9 @@ export interface INotifyClient {
2924
): Promise<SingleMessageResponse>;
3025
}
3126
/*
32-
* Client for sending requests to the NHS Notify API using FHIR API, see
33-
* https://digital.nhs.uk/developer/api-catalogue/nhs-notify
34-
*/
27+
* Client for sending requests to the NHS Notify API using FHIR API, see
28+
* https://digital.nhs.uk/developer/api-catalogue/nhs-notify
29+
*/
3530
export class NotifyClient implements INotifyClient, IAccessibleService {
3631
private client: AxiosInstance;
3732

lambdas/core-notifier/src/app/notify-message-processor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type Dependencies = {
1010

1111
export class NotifyMessageProcessor {
1212
private readonly logger: Logger;
13+
1314
private readonly nhsNotifyClient: NotifyClient;
1415

1516
constructor({ logger, nhsNotifyClient }: Dependencies) {
Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
11
import { SQSRecord } from 'aws-lambda';
22
import { Logger } from 'utils';
3-
import { NudgeCommand } from 'domain/nudge-command';
4-
import { $NudgeCommand } from 'app/nudge-command-validator';
3+
import { PDMResourceAvailable } from 'digital-letters-events';
4+
import { InvalidPdmResourceAvailableEvent } from 'domain/invalid-pdm-resource-available-event';
5+
import { messageDownloadedValidator } from 'digital-letters-events/PDMResourceAvailable.js';
56

67
export const parseSqsRecord = (
78
sqsRecord: SQSRecord,
89
logger: Logger,
9-
): NudgeCommand => {
10+
): PDMResourceAvailable => {
1011
logger.info('Parsing SQS Record', {
1112
messageId: sqsRecord.messageId,
1213
});
14+
const sqsEventBody = JSON.parse(sqsRecord.body);
15+
const sqsEventDetail = sqsEventBody.detail;
16+
const isEventValid = messageDownloadedValidator(sqsEventDetail);
17+
if (!isEventValid) {
18+
logger.error({
19+
err: messageDownloadedValidator.errors,
20+
description:
21+
'The SQS message does not contain a valid PDMResourceAvailable event',
22+
messageId: sqsRecord.messageId,
23+
});
24+
throw new InvalidPdmResourceAvailableEvent(sqsRecord.messageId);
25+
}
1326

14-
const jsonParsed = JSON.parse(sqsRecord.body) as NudgeCommand;
15-
const zodParsed = $NudgeCommand.parse(jsonParsed);
16-
17-
logger.info('Parsed SQS Record as Nudge Command Event', {
27+
logger.info('Parsed valid PDMResourceAvailable Event', {
1828
messageId: sqsRecord.messageId,
19-
sourceEventId: zodParsed.sourceEventId,
2029
});
2130

22-
return zodParsed;
31+
return sqsEventDetail as PDMResourceAvailable;
2332
};

lambdas/core-notifier/src/container.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
import {
2-
ParameterStoreCache,
3-
createGetApimAccessToken,
4-
logger,
5-
} from 'utils';
1+
import { ParameterStoreCache, createGetApimAccessToken, logger } from 'utils';
62
import { NotifyClient } from 'app/notify-api-client';
73
import { NotifyMessageProcessor } from 'app/notify-message-processor';
84
import type { SqsHandlerDependencies } from 'apis/sqs-handler';
95
import { loadConfig } from 'infra/config';
10-
import { SenderRepository } from 'sender-management/src/infra/sender-repository';
6+
import { SenderManagement } from '@sender-management';
117

128
export async function createContainer(): Promise<SqsHandlerDependencies> {
139
const parameterStore = new ParameterStoreCache();
1410
const config = loadConfig();
15-
const senderRepository = new SenderRepository({
16-
config,
17-
logger,
11+
const senderManagement = new SenderManagement({
1812
parameterStore,
1913
});
2014

@@ -38,8 +32,8 @@ export async function createContainer(): Promise<SqsHandlerDependencies> {
3832
});
3933

4034
return {
41-
notifyMessageProcessor: notifyMessageProcessor,
35+
notifyMessageProcessor,
4236
logger,
43-
senderRepository
37+
senderManagement,
4438
};
4539
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export class InvalidPdmResourceAvailableEvent extends Error {
2+
readonly sqsMessageId: string;
3+
4+
constructor(sqsMessageId: string) {
5+
super('Unable to parse PDMResourceAvailable event from SQS message');
6+
this.sqsMessageId = sqsMessageId;
7+
}
8+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { logger, Sender } from 'utils';
2+
import { PDMResourceAvailable } from 'digital-letters-events';
3+
import type { SingleMessageRequest } from 'domain/request';
4+
5+
export function mapQueueToRequest(
6+
pdmResourceAvailable: PDMResourceAvailable,
7+
sender: Sender,
8+
): SingleMessageRequest {
9+
const data = pdmResourceAvailable.data;
10+
const messageReference = data.messageReference;
11+
12+
logger.info(`Mapping resource available with reference: ${messageReference} for sender: ${sender.senderId}`);
13+
14+
const request: SingleMessageRequest = {
15+
data: {
16+
type: 'Message',
17+
attributes: {
18+
sender.routingConfigId!,
19+
messageReference,
20+
billingReference,
21+
recipient: {
22+
nhsNumber: data.nhsNumber,
23+
},
24+
originator: {
25+
odsCode: data.odsCode;
26+
},
27+
personalisation: {
28+
digitalLetterURL: `https://www.nhsapp.service.nhs.uk/digital-letters?letterid=${data.resourceId}`,
29+
}
30+
},
31+
},
32+
};
33+
return request;
34+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { EventPublisher, Logger } from 'utils';
2+
import { MessageRequestSubmitted, MessageRequestRejected, MessageRequestSkipped } from 'digital-letters-events';
3+
import messageRequestSubmittedValidator from 'digital-letters-events/MessageRequestSubmitted.js';
4+
import messageRequestRejectedValidator from 'digital-letters-events/MessageRequestRejected.js';
5+
import messageRequestSkippedValidator from 'digital-letters-events/MessageRequestSkipped.js';
6+
export class EventPublisherFacade {
7+
constructor(private readonly messageRequestSubmittedEventPublisher: EventPublisher,
8+
private readonly messageRequestSkippedEventPublisher: EventPublisher,
9+
private readonly messageRequestRejectedEventPublisher: EventPublisher,
10+
private readonly logger: Logger
11+
){}
12+
13+
async publishMessageRequestSubmitted(events: [MessageRequestSubmitted]): Promise<void>{
14+
this.logger.info(`Publishing ${events.length} MessageRequestSubmitted events`);
15+
this.messageRequestSubmittedEventPublisher.sendEvents<MessageRequestSubmitted>(
16+
events, messageRequestSubmittedValidator);
17+
}
18+
19+
async publishMessageRequestRejected(events: [MessageRequestRejected]): Promise<void>{
20+
this.logger.info(`Publishing ${events.length} MessageRequestRejected events`);
21+
this.messageRequestRejectedEventPublisher.sendEvents<MessageRequestRejected>(
22+
events, messageRequestRejectedValidator);
23+
}
24+
25+
async publishMessageRequestSkipped(events: [MessageRequestSkipped]): Promise<void>{
26+
this.logger.info(`Publishing ${events.length} MessageRequestSkipped events`);
27+
this.messageRequestSkippedEventPublisher.sendEvents<MessageRequestSkipped>(
28+
events, messageRequestSkippedValidator);
29+
}
30+
}

0 commit comments

Comments
 (0)