diff --git a/src/app/modules/bounce/__tests__/bounce.controller.spec.ts b/src/app/modules/bounce/__tests__/bounce.controller.spec.ts index e835515e2c..7fa6bda211 100644 --- a/src/app/modules/bounce/__tests__/bounce.controller.spec.ts +++ b/src/app/modules/bounce/__tests__/bounce.controller.spec.ts @@ -172,6 +172,7 @@ describe('handleSns', () => { expect(MockBounceService.getEditorsWithContactNumbers).toHaveBeenCalledWith( mockForm, ) + expect(mockBounceDoc.hasNotified).toHaveBeenCalled() expect(MockBounceService.sendEmailBounceNotification).toHaveBeenCalledWith( mockBounceDoc, mockForm, @@ -229,6 +230,7 @@ describe('handleSns', () => { expect(MockBounceService.getEditorsWithContactNumbers).toHaveBeenCalledWith( mockForm, ) + expect(mockBounceDoc.hasNotified).toHaveBeenCalled() expect(MockBounceService.sendEmailBounceNotification).toHaveBeenCalledWith( mockBounceDoc, mockForm, @@ -257,6 +259,54 @@ describe('handleSns', () => { expect(MOCK_RES.sendStatus).toHaveBeenCalledWith(200) }) + it('should call services correctly when recipients have already been notified of a critical bounce', async () => { + mockBounceDoc.hasNotified.mockReturnValue(true) + + await handleSns(MOCK_REQ, MOCK_RES, jest.fn()) + + expect(MockBounceService.validateSnsRequest).toHaveBeenCalledWith( + MOCK_REQ.body, + ) + expect(MockBounceService.logEmailNotification).toHaveBeenCalledWith( + MOCK_NOTIFICATION, + ) + expect(MockBounceService.extractEmailType).toHaveBeenCalledWith( + MOCK_NOTIFICATION, + ) + expect(MockBounceService.getUpdatedBounceDoc).toHaveBeenCalledWith( + MOCK_NOTIFICATION, + ) + expect(MockFormService.retrieveFullFormById).toHaveBeenCalledWith( + mockBounceDoc.formId, + ) + expect(mockBounceDoc.isCriticalBounce).toHaveBeenCalled() + expect(MockBounceService.getEditorsWithContactNumbers).toHaveBeenCalledWith( + mockForm, + ) + expect(mockBounceDoc.hasNotified).toHaveBeenCalled() + // Notification functions are not called + expect(MockBounceService.sendEmailBounceNotification).not.toHaveBeenCalled() + expect(MockBounceService.sendSmsBounceNotification).not.toHaveBeenCalled() + expect(mockBounceDoc.setNotificationState).not.toHaveBeenCalled() + expect(mockBounceDoc.areAllPermanentBounces).toHaveBeenCalled() + expect(MockFormService.deactivateForm).toHaveBeenCalledWith( + mockBounceDoc.formId, + ) + expect(MockBounceService.notifyAdminsOfDeactivation).toHaveBeenCalledWith( + mockForm, + MOCK_CONTACTS, + ) + expect(MockBounceService.logCriticalBounce).toHaveBeenCalledWith({ + bounceDoc: mockBounceDoc, + notification: MOCK_NOTIFICATION, + autoEmailRecipients: [], + autoSmsRecipients: [], + hasDeactivated: true, + }) + expect(MockBounceService.saveBounceDoc).toHaveBeenCalledWith(mockBounceDoc) + expect(MOCK_RES.sendStatus).toHaveBeenCalledWith(200) + }) + it('should return 200 even when errors are thrown in getUpdatedBounceDoc', async () => { MockBounceService.getUpdatedBounceDoc.mockReturnValueOnce( errAsync(new DatabaseError()), @@ -334,6 +384,7 @@ describe('handleSns', () => { expect(MockBounceService.getEditorsWithContactNumbers).toHaveBeenCalledWith( mockForm, ) + expect(mockBounceDoc.hasNotified).toHaveBeenCalled() expect(mockBounceDoc.areAllPermanentBounces).toHaveBeenCalled() expect(MockFormService.deactivateForm).toHaveBeenCalledWith( mockBounceDoc.formId, @@ -367,6 +418,7 @@ describe('handleSns', () => { expect(MockBounceService.getEditorsWithContactNumbers).toHaveBeenCalledWith( mockForm, ) + expect(mockBounceDoc.hasNotified).toHaveBeenCalled() expect(mockBounceDoc.areAllPermanentBounces).toHaveBeenCalled() expect(MockFormService.deactivateForm).toHaveBeenCalledWith( mockBounceDoc.formId, diff --git a/src/app/modules/bounce/bounce.controller.ts b/src/app/modules/bounce/bounce.controller.ts index 404cd59568..3de863e75d 100644 --- a/src/app/modules/bounce/bounce.controller.ts +++ b/src/app/modules/bounce/bounce.controller.ts @@ -8,6 +8,7 @@ import { ControllerHandler } from '../core/core.types' import * as FormService from '../form/form.service' import * as BounceService from './bounce.service' +import { AdminNotificationRecipients } from './bounce.types' const logger = createLoggerWithLabel(module) @@ -78,16 +79,29 @@ export const handleSns: ControllerHandler< // Send notifications and deactivate form on best-effort basis, ignore errors const possibleSmsRecipients = await BounceService.getEditorsWithContactNumbers(form).unwrapOr([]) - const emailRecipients = await BounceService.sendEmailBounceNotification( - bounceDoc, - form, - ).unwrapOr([]) - const smsRecipients = await BounceService.sendSmsBounceNotification( - bounceDoc, - form, - possibleSmsRecipients, - ).unwrapOr([]) - bounceDoc.setNotificationState(emailRecipients, smsRecipients) + + const notificationRecipients: AdminNotificationRecipients = { + emailRecipients: [], + smsRecipients: [], + } + // Check if notifications have been sent to form admins and collaborators + if (!bounceDoc.hasNotified()) { + notificationRecipients.emailRecipients = + await BounceService.sendEmailBounceNotification( + bounceDoc, + form, + ).unwrapOr([]) + notificationRecipients.smsRecipients = + await BounceService.sendSmsBounceNotification( + bounceDoc, + form, + possibleSmsRecipients, + ).unwrapOr([]) + bounceDoc.setNotificationState( + notificationRecipients.emailRecipients, + notificationRecipients.smsRecipients, + ) + } const shouldDeactivate = bounceDoc.areAllPermanentBounces() if (shouldDeactivate) { @@ -102,8 +116,8 @@ export const handleSns: ControllerHandler< BounceService.logCriticalBounce({ bounceDoc, notification, - autoEmailRecipients: emailRecipients, - autoSmsRecipients: smsRecipients, + autoEmailRecipients: notificationRecipients.emailRecipients, + autoSmsRecipients: notificationRecipients.smsRecipients, hasDeactivated: shouldDeactivate, }) } diff --git a/src/app/modules/bounce/bounce.types.ts b/src/app/modules/bounce/bounce.types.ts new file mode 100644 index 0000000000..bacec4ab51 --- /dev/null +++ b/src/app/modules/bounce/bounce.types.ts @@ -0,0 +1,6 @@ +import { UserWithContactNumber } from '../user/user.types' + +export interface AdminNotificationRecipients { + emailRecipients: string[] + smsRecipients: UserWithContactNumber[] +}