From 7dae2bf8f4e4ea168aa84704d7a3e2b8ea15f750 Mon Sep 17 00:00:00 2001 From: Daniel Searle <84069850+Daniel-Searle@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:10:51 +0100 Subject: [PATCH] CB2-11819: Move SMC prohibition to SNS/SQS pattern (#21) * feat(CB2-11819): cahnged event type * feat(CB2-11819): cahnged event type * feat(CB2-11819): event handling * feat(CB2-11819): event handling * feat(CB2-11819): event handling * feat(CB2-11819): event handling * feat(CB2-11819): event handling * feat(CB2-11819): event handling * feat(CB2-11819): event handling * feat(CB2-11819): event handling * feat(CB2-11819): removed logs * feat(CB2-11819): json parse change * feat(CB2-11819): json parse change * feat(CB2-11819): json parse change --- src/handler.ts | 22 +++++-- src/utils/ExtractTestResults.ts | 5 +- tests/unit/Handler.test.ts | 111 ++++++++++++++++++-------------- 3 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/handler.ts b/src/handler.ts index dc71082..17cd226 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -1,12 +1,13 @@ /* eslint-disable */ -import { DynamoDBStreamEvent, Context, Callback } from 'aws-lambda'; +import { Context, Callback, SQSEvent, DynamoDBRecord } from 'aws-lambda'; import { extractMCTestResults } from './utils/ExtractTestResults'; import { sendMCProhibition } from './eventbridge/Send'; import logger from './observability/Logger'; import { MCRequest } from './utils/MCRequest'; + const handler = async ( - event: DynamoDBStreamEvent, + event: SQSEvent, _context: Context, callback: Callback, ) => { @@ -20,13 +21,22 @@ const handler = async ( try { logger.debug(`Function triggered with '${JSON.stringify(event)}'.`); - // We want to process these in sequence to maintain order of database changes for (const record of event.Records) { - const mcRequests: MCRequest[] = extractMCTestResults(record); - if (mcRequests != null) { - await sendMCProhibition(mcRequests); + const dynamoDBEvent: DynamoDBRecord = JSON.parse(record.body) as DynamoDBRecord; + if (dynamoDBEvent){ + const mcRequests: MCRequest[] = extractMCTestResults(dynamoDBEvent); + + if (mcRequests != null && mcRequests.length > 0) { + await sendMCProhibition(mcRequests); + } else { + logger.info(`No relevant MC test results found in the record: ${JSON.stringify(dynamoDBEvent)}`); + } + } else { + logger.info('Function not triggered, empty notification.'); + callback(null, 'Function not triggered, empty notification.'); } } + callback(null, 'Data processed successfully.'); } catch (error) { if (error.body) { diff --git a/src/utils/ExtractTestResults.ts b/src/utils/ExtractTestResults.ts index ad930b5..629ad40 100644 --- a/src/utils/ExtractTestResults.ts +++ b/src/utils/ExtractTestResults.ts @@ -8,6 +8,7 @@ import { unmarshall } from '@aws-sdk/util-dynamodb'; import { DateTime } from 'luxon'; +import { DynamoDBRecord } from 'aws-lambda'; import { PROHIB_CLEARANCE_TEST_TYPE_IDS } from '../assets/Enums'; import logger from '../observability/Logger'; import { HTTPError } from './HTTPError'; @@ -20,8 +21,8 @@ import { ValidationUtil } from './ValidationUtil'; * required to be sent to MC in order to clear prohibitions * @param record */ -export const extractMCTestResults = (record: any): MCRequest[] => { - const testResultUnmarshall = unmarshall(record.dynamodb.NewImage as { any }); +export const extractMCTestResults = (record: DynamoDBRecord): MCRequest[] => { + const testResultUnmarshall = unmarshall(record.dynamodb.NewImage as any); logger.info( `Processing testResultId: ${JSON.stringify( testResultUnmarshall.testResultId, diff --git a/tests/unit/Handler.test.ts b/tests/unit/Handler.test.ts index 0f46d05..4d1ef17 100644 --- a/tests/unit/Handler.test.ts +++ b/tests/unit/Handler.test.ts @@ -1,11 +1,4 @@ -/* eslint-disable import/first */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable no-underscore-dangle */ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable import/no-unresolved */ -import { DynamoDBRecord, DynamoDBStreamEvent } from 'aws-lambda'; +import { SQSEvent } from 'aws-lambda'; import { sendMCProhibition } from '../../src/eventbridge/Send'; import { SendResponse } from '../../src/eventbridge/SendResponse'; import { extractMCTestResults } from '../../src/utils/ExtractTestResults'; @@ -17,67 +10,91 @@ jest.mock('../../src/eventbridge/Send'); jest.mock('../../src/utils/ExtractTestResults'); describe('Application entry', () => { - let event: DynamoDBStreamEvent; + const event: SQSEvent = { + Records: [ + { + messageId: '1317d15-a23b2-4c68-a2da-67cc685dda5b', + receiptHandle: 'aer3fiu34yufybuy34f334', + body: JSON.stringify({ + Message: { + eventID: '...', + eventName: 'INSERT', + dynamodb: { + NewImage: dynamoRecordFiltered.dynamodb.NewImage, + }, + }, + }), + attributes: { + ApproximateReceiveCount: '1', + SentTimestamp: '1717678383236', + SenderId: 'AIDAISMY7JYY5F7RTT6AO', + ApproximateFirstReceiveTimestamp: '1717678383247', + }, + messageAttributes: {}, + md5OfBody: '45bd1375e48194d7e1563cf20462d', + eventSource: 'aws:sqs', + eventSourceARN: 'arn:aws:sqs:eu-west-1:local:cvs-smc-prohibition-local-queue', + awsRegion: 'eu-west-1', + }, + ], + }; + const sendResponse: SendResponse = { + SuccessCount: 1, + FailCount: 0, + }; + jest.mocked(extractMCTestResults).mockReturnValue(Array()); afterEach(() => { jest.clearAllMocks(); }); describe('Handler', () => { - it('When there is an event that gets processed successfully no errors are produced', async () => { - event = { - Records: [dynamoRecordFiltered as DynamoDBRecord], - }; - const sendResponse: SendResponse = { - SuccessCount: 1, - FailCount: 0, - }; + process.env.SEND_TO_SMC = 'True'; + it('should process a valid event successfully', async () => { + const expectedMCRequests: MCRequest[] = [ + { + vehicleIdentifier: 'ABC1234', + testDate: '14/01/2019', + vin: 'XMGDE02FS0H012303', + testResult: 'P', + hgvPsvTrailFlag: 'T', + testResultId: 'some-test-result-id', + }, + ]; + + jest.mocked(extractMCTestResults).mockReturnValue(expectedMCRequests); jest.mocked(sendMCProhibition).mockResolvedValue(sendResponse); - await handler(event, null, (error: string | Error, result: string) => { - expect(result).toBe('Data processed successfully.'); + + await handler(event, null, (error, result) => { expect(error).toBeNull(); - expect(sendMCProhibition).toHaveBeenCalledTimes(1); - }); - }); - it('When there is an event that gets processed successfully in proper case then no errors are produced', async () => { - process.env.SEND_TO_SMC = 'True'; - event = { - Records: [dynamoRecordFiltered as DynamoDBRecord], - }; - const sendResponse: SendResponse = { - SuccessCount: 1, - FailCount: 0, - }; - jest.mocked(sendMCProhibition).mockResolvedValue(sendResponse); - await handler(event, null, (error: string | Error, result: string) => { expect(result).toBe('Data processed successfully.'); - expect(error).toBeNull(); - expect(sendMCProhibition).toHaveBeenCalledTimes(1); + expect(sendMCProhibition).toHaveBeenCalledWith(expectedMCRequests); }); }); - it('When there is an error when sending the object and error is produced', async () => { + + it('should handle an error when sending the object', async () => { process.env.SEND_TO_SMC = 'True'; - event = { - Records: [dynamoRecordFiltered as DynamoDBRecord], - }; + const expectedErrorMessage = 'Data processed unsuccessfully: Error: Oh no!'; jest.mocked(sendMCProhibition).mockRejectedValue(new Error('Oh no!')); - await handler(event, null, (error: string | Error, result: string) => { + + await handler(event, null, (error, result) => { expect(error).toBeNull(); - expect(result).toBe('Data processed unsuccessfully: Error: Oh no!'); + expect(result).toBe(expectedErrorMessage); expect(sendMCProhibition).toHaveBeenCalledTimes(1); }); }); - it('When there is an invalid environment variable a log is produced', async () => { + + it('should handle an invalid environment variable', async () => { process.env.SEND_TO_SMC = 'false'; - event = { - Records: [dynamoRecordFiltered as DynamoDBRecord], - }; - jest.spyOn(console, 'log'); - await handler(event, null, (error: string | Error, result: string) => { + await handler(event, null, (error, result) => { expect(error).toBeNull(); expect(result).toBe('Function not triggered, Missing or not true environment variable present'); + expect(extractMCTestResults).not.toHaveBeenCalled(); + expect(sendMCProhibition).not.toHaveBeenCalled(); }); }); }); }); + +