Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lambdas/print-status-handler/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"dependencies": {
"@nhsdigital/nhs-notify-event-schemas-supplier-api": "1.0.17",
"digital-letters-events": "^0.0.1",
"utils": "^0.0.1",
"zod": "^4.1.12"
Comment thread
simonlabarere marked this conversation as resolved.
},
"devDependencies": {
"@nhsdigital/nhs-notify-event-schemas-supplier-api": "^1.0.17",
"@tsconfig/node22": "^22.0.2",
"@types/aws-lambda": "^8.10.155",
"@types/jest": "^29.5.14",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
failedLetterEvent,
recordEvent,
} from '__tests__/test-data';
import { LetterStatusChangeEvent } from '@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events';

const logger = mock<Logger>();
const eventPublisher = mock<EventPublisher>();
Expand Down Expand Up @@ -37,11 +38,16 @@ describe('SQS Handler', () => {
expect(eventPublisher.sendEvents).toHaveBeenCalledWith(
[
{
...acceptedLetterEvent,
id: '550e8400-e29b-41d4-a716-446655440001',
time: '2023-06-20T12:00:00.250Z',
recordedtime: '2023-06-20T12:00:00.250Z',
plane: 'data',
specversion: '1.0',
datacontenttype: 'application/json',
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
severitynumber: 2,
severitytext: 'INFO',
dataschema:
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-print-letter-transitioned-data.schema.json',
type: 'uk.nhs.notify.digital.letters.print.letter.transitioned.v1',
Expand Down Expand Up @@ -78,10 +84,16 @@ describe('SQS Handler', () => {
expect(eventPublisher.sendEvents).toHaveBeenCalledWith(
[
{
...failedLetterEvent,
id: '550e8400-e29b-41d4-a716-446655440001',
time: '2023-06-20T12:00:00.250Z',
recordedtime: '2023-06-20T12:00:00.250Z',
plane: 'data',
specversion: '1.0',
datacontenttype: 'application/json',
traceparent:
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
severitynumber: 2,
severitytext: 'INFO',
dataschema:
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-print-letter-transitioned-data.schema.json',
type: 'uk.nhs.notify.digital.letters.print.letter.transitioned.v1',
Expand Down Expand Up @@ -181,17 +193,22 @@ describe('SQS Handler', () => {
it('should return failed items to the queue if an invalid letter.ACCEPTED event is received', async () => {
const invalidAcceptedLetterEvent = {
...acceptedLetterEvent,
source: 'invalid letter.ACCEPTED source',
data: {
...acceptedLetterEvent.data,
status: 'INVALID_STATUS',
},
};
const event = recordEvent([invalidAcceptedLetterEvent]);
const event = recordEvent([
invalidAcceptedLetterEvent as LetterStatusChangeEvent,
]);

const result = await handler(event);

expect(logger.warn).toHaveBeenCalledWith({
err: expect.objectContaining({
issues: expect.arrayContaining([
expect.objectContaining({
path: ['source'],
path: ['data', 'status'],
}),
]),
}),
Expand Down Expand Up @@ -231,7 +248,7 @@ describe('SQS Handler', () => {
}),
]),
}),
description: 'Invalid origin.subject format',
description: 'Error parsing queue item',
});

expect(logger.info).toHaveBeenCalledWith(
Expand Down
8 changes: 4 additions & 4 deletions lambdas/print-status-handler/src/__tests__/test-data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { SQSEvent, SQSRecord } from 'aws-lambda';
import { LetterEvent } from '@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events';
import { LetterStatusChangeEvent } from '@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events';

export const acceptedLetterEvent = {
id: '550e8400-e29b-41d4-a716-446655440001',
Expand Down Expand Up @@ -34,7 +34,7 @@ export const acceptedLetterEvent = {
'client/f47ac10b-58cc-4372-a567-0e02b2c3d479/letter-request/2503cbd5-6722-4e90-9fbd-5f1e96d65c22',
},
},
} as LetterEvent;
} as LetterStatusChangeEvent;

export const failedLetterEvent = {
id: '550e8400-e29b-41d4-a716-446655440002',
Expand Down Expand Up @@ -71,7 +71,7 @@ export const failedLetterEvent = {
'client/f47ac10b-58cc-4372-a567-0e02b2c3d480/letter-request/2503cbd5-6722-4e90-9fbd-5f1e96d65c22',
},
},
} as LetterEvent;
} as LetterStatusChangeEvent;

const busEvent = {
version: '0',
Expand All @@ -94,7 +94,7 @@ const sqsRecord = {
awsRegion: '',
} as SQSRecord;

export const recordEvent = (events: LetterEvent[]): SQSEvent => ({
export const recordEvent = (events: LetterStatusChangeEvent[]): SQSEvent => ({
Records: events.map((event, i) => ({
...sqsRecord,
messageId: String(i + 1),
Expand Down
46 changes: 16 additions & 30 deletions lambdas/print-status-handler/src/apis/sqs-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import type {
SQSEvent,
} from 'aws-lambda';
import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import {
$LetterEvent,
LetterEvent,
} from '@nhsdigital/nhs-notify-event-schemas-supplier-api/src/events/letter-events';
import {
PrintLetterTransitioned,
validatePrintLetterTransitioned,
} from 'digital-letters-events';
import { EventPublisher, Logger } from 'utils';
import {
$SupplierApiLetterEvent,
EventPublisher,
Logger,
SupplierApiLetterEvent,
} from 'utils';

export interface HandlerDependencies {
eventPublisher: EventPublisher;
Expand All @@ -22,16 +22,9 @@ export interface HandlerDependencies {

type ValidatedRecord = {
messageId: string;
event: LetterEvent;
event: SupplierApiLetterEvent;
};

const originSubjectSchema = z
.string()
.regex(
/^client\/[^/]+\/letter-request\/[^/]+$/,
'Subject must be in format: client/{senderId}/letter-request/{messageReference}',
);

function validateRecord(
{ body, messageId }: { body: string; messageId: string },
logger: Logger,
Expand All @@ -44,7 +37,7 @@ function validateRecord(
data: item,
error: parseError,
success: parseSuccess,
} = $LetterEvent.safeParse(sqsEventDetail);
} = $SupplierApiLetterEvent.safeParse(sqsEventDetail);

if (!parseSuccess) {
logger.warn({
Expand All @@ -55,19 +48,6 @@ function validateRecord(
return null;
}

const subjectValidation = originSubjectSchema.safeParse(
item.data.origin.subject,
);

if (!subjectValidation.success) {
logger.warn({
err: subjectValidation.error,
description: 'Invalid origin.subject format',
});

return null;
}

logger.info({
description: 'Successfully validated SQS record',
messageId,
Expand All @@ -85,7 +65,9 @@ function validateRecord(
}
}

function generateUpdatedEvent(event: LetterEvent): PrintLetterTransitioned {
function generateUpdatedEvent(
event: SupplierApiLetterEvent,
): PrintLetterTransitioned {
const eventTime = new Date().toISOString();

const {
Expand All @@ -104,7 +86,6 @@ function generateUpdatedEvent(event: LetterEvent): PrintLetterTransitioned {
const messageReference = subject.split('/')[3];

return {
...event,
id: randomUUID(),
time: eventTime,
recordedtime: eventTime,
Expand All @@ -116,6 +97,11 @@ function generateUpdatedEvent(event: LetterEvent): PrintLetterTransitioned {
source: '/nhs/england/notify/production/primary/digitalletters/print',
plane: 'data',
dataschemaversion: '1.0.0',
specversion: '1.0',
datacontenttype: 'application/json',
traceparent: '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
severitynumber: 2,
severitytext: 'INFO',
Comment thread
simonlabarere marked this conversation as resolved.
data: {
senderId,
messageReference,
Expand Down
12 changes: 7 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pact-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
"test:unit:coverage": "echo Unit tests not required",
"typecheck": "echo Typecheck not required"
},
"version": "1.0.0"
"version": "1.0.1"
}
37 changes: 21 additions & 16 deletions scripts/tests/contract.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,31 @@

set -euo pipefail

schema_package="@nhsdigital/nhs-notify-event-schemas-status-published"
schema_packages=(
"@nhsdigital/nhs-notify-event-schemas-status-published"
"@nhsdigital/nhs-notify-event-schemas-supplier-api"
)

cd "$(git rev-parse --show-toplevel)"

if npm outdated "$schema_package" >/dev/null 2>&1; then
outdated_status=0
else
outdated_status=$?
fi
for schema_package in "${schema_packages[@]}"; do
if npm outdated "$schema_package" >/dev/null 2>&1; then
outdated_status=0
else
outdated_status=$?
fi

if [[ $outdated_status -eq 1 ]]; then
echo "The provider schema package ($schema_package) is outdated."
echo "Please run npm update $schema_package to update to the latest version and re-run."
echo
exit 1
fi
if [[ $outdated_status -eq 1 ]]; then
echo "The provider schema package ($schema_package) is outdated."
echo "Please run npm update $schema_package to update to the latest version and re-run."
echo
exit 1
fi

if [[ $outdated_status -gt 1 ]]; then
echo "Failed to check provider schema package version (npm outdated exited with status $outdated_status)."
exit $outdated_status
fi
if [[ $outdated_status -gt 1 ]]; then
echo "Failed to check provider schema package version (npm outdated exited with status $outdated_status)."
exit $outdated_status
fi
done

npm run test:contract
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@ import {
import { $ChannelStatusPublishedEvent } from 'utils';
import {
PACT_CONSUMER,
PACT_DIRECTORY,
PACT_MESSAGE_DESCRIPTION,
PACT_PROVIDER,
PACT_STATUS_PUBLISHED_PROVIDER,
} from '../utils/pact-config';
import { getPathFromProvider } from '../utils/path-utils';

async function handle(event: unknown) {
// The schema used by the nhsapp-status-handler to validate the event.
$ChannelStatusPublishedEvent.parse(event);
}

const PACT_DIRECTORY = getPathFromProvider(PACT_STATUS_PUBLISHED_PROVIDER);

describe('Pact message consumer - ChannelStatusPublished event', () => {
const messagePact = new MessageConsumerPact({
consumer: PACT_CONSUMER,
provider: PACT_PROVIDER,
provider: PACT_STATUS_PUBLISHED_PROVIDER,
dir: PACT_DIRECTORY,
logLevel: 'error',
pactfileWriteMode: 'update',
Expand Down
Loading
Loading