From 75ba90bbab698106499aac0a49fb422349c1e093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dvar=20Oddsson?= Date: Fri, 18 Oct 2024 18:26:08 +0000 Subject: [PATCH] chore(j-s): Send notifications when subpoena changes (#16329) * Checkpoint * Refactor * Checkpoint * Add messages to queue * Checkpoint * Construct the emails * Fix build * Checkpoint * Implement failed notifications * Add failed and defenderchoice emails * Remove unused code * Remove unused code * Refactor * Refactor * Remove unused code * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Refactor * Merge * Cleanup * Remove console.log * Refactor * Refactor * Refactor * Remove unused code * Refactor * Fix tests --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../20241015105921-update-notification.js | 70 +++++++ .../notification/baseNotification.service.ts | 24 +++ .../dto/subpoenaNotification.dto.ts | 18 ++ .../institutionNotification.service.ts | 8 +- .../internalNotification.controller.ts | 22 +- .../internalNotification.service.ts | 24 --- .../notification/notification.module.ts | 4 + .../subpoenaNotification.service.ts | 196 ++++++++++++++++++ .../subpoenaNotification.strings.ts | 43 ++++ .../limitedAccessSubpoena.controller.ts | 2 +- .../app/modules/subpoena/subpoena.module.ts | 3 + .../app/modules/subpoena/subpoena.service.ts | 52 ++++- .../test/createTestingSubpoenaModule.ts | 3 + .../message/src/lib/message.ts | 2 + libs/judicial-system/types/src/index.ts | 1 + .../types/src/lib/defendant.ts | 4 + .../types/src/lib/notification.ts | 3 + 17 files changed, 447 insertions(+), 32 deletions(-) create mode 100644 apps/judicial-system/backend/migrations/20241015105921-update-notification.js create mode 100644 apps/judicial-system/backend/src/app/modules/notification/dto/subpoenaNotification.dto.ts create mode 100644 apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts create mode 100644 apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts diff --git a/apps/judicial-system/backend/migrations/20241015105921-update-notification.js b/apps/judicial-system/backend/migrations/20241015105921-update-notification.js new file mode 100644 index 000000000000..d3979f325f06 --- /dev/null +++ b/apps/judicial-system/backend/migrations/20241015105921-update-notification.js @@ -0,0 +1,70 @@ +'use strict' +const replaceEnum = require('sequelize-replace-enum-postgres').default + +module.exports = { + up: (queryInterface) => { + // replaceEnum does not support transactions + return replaceEnum({ + queryInterface, + tableName: 'notification', + columnName: 'type', + newValues: [ + 'HEADS_UP', + 'READY_FOR_COURT', + 'RECEIVED_BY_COURT', + 'COURT_DATE', + 'RULING', + 'MODIFIED', + 'REVOKED', + 'DEFENDER_ASSIGNED', + 'ADVOCATE_ASSIGNED', + 'DEFENDANTS_NOT_UPDATED_AT_COURT', + 'APPEAL_TO_COURT_OF_APPEALS', + 'APPEAL_RECEIVED_BY_COURT', + 'APPEAL_STATEMENT', + 'APPEAL_COMPLETED', + 'APPEAL_JUDGES_ASSIGNED', + 'APPEAL_CASE_FILES_UPDATED', + 'APPEAL_WITHDRAWN', + 'INDICTMENT_DENIED', + 'INDICTMENT_RETURNED', + 'INDICTMENTS_WAITING_FOR_CONFIRMATION', + 'SERVICE_SUCCESSFUL', // New value + 'SERVICE_FAILED', // New value + 'DEFENDANT_SELECTED_DEFENDER', // New value + ], + enumName: 'enum_notification_type', + }) + }, + + down: (queryInterface) => { + // replaceEnum does not support transactions + return replaceEnum({ + queryInterface, + tableName: 'notification', + columnName: 'type', + newValues: [ + 'HEADS_UP', + 'READY_FOR_COURT', + 'RECEIVED_BY_COURT', + 'COURT_DATE', + 'RULING', + 'MODIFIED', + 'REVOKED', + 'DEFENDER_ASSIGNED', + 'DEFENDANTS_NOT_UPDATED_AT_COURT', + 'APPEAL_TO_COURT_OF_APPEALS', + 'APPEAL_RECEIVED_BY_COURT', + 'APPEAL_STATEMENT', + 'APPEAL_COMPLETED', + 'APPEAL_JUDGES_ASSIGNED', + 'APPEAL_CASE_FILES_UPDATED', + 'APPEAL_WITHDRAWN', + 'INDICTMENT_DENIED', + 'INDICTMENT_RETURNED', + 'INDICTMENTS_WAITING_FOR_CONFIRMATION', + ], + enumName: 'enum_notification_type', + }) + }, +} diff --git a/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts index 2bae99fff0e2..211d44dcd63d 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/baseNotification.service.ts @@ -145,4 +145,28 @@ export abstract class BaseNotificationService { ), } } + + protected hasSentNotification( + type: NotificationType, + notifications?: Notification[], + ) { + return notifications?.some((notification) => notification.type === type) + } + + protected hasReceivedNotification( + type?: NotificationType | NotificationType[], + address?: string, + notifications?: Notification[], + ) { + const types = type ? [type].flat() : Object.values(NotificationType) + + return notifications?.some((notification) => { + return ( + types.includes(notification.type) && + notification.recipients.some( + (recipient) => recipient.address === address && recipient.success, + ) + ) + }) + } } diff --git a/apps/judicial-system/backend/src/app/modules/notification/dto/subpoenaNotification.dto.ts b/apps/judicial-system/backend/src/app/modules/notification/dto/subpoenaNotification.dto.ts new file mode 100644 index 000000000000..e1b0bf519135 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/dto/subpoenaNotification.dto.ts @@ -0,0 +1,18 @@ +import { IsEnum, IsNotEmpty } from 'class-validator' + +import { ApiProperty } from '@nestjs/swagger' + +import { NotificationType } from '@island.is/judicial-system/types' + +import { Subpoena } from '../../subpoena' + +export class SubpoenaNotificationDto { + @IsNotEmpty() + @IsEnum(NotificationType) + @ApiProperty({ enum: NotificationType }) + readonly type!: NotificationType + + @IsNotEmpty() + @ApiProperty({ type: Subpoena }) + readonly subpoena!: Subpoena +} diff --git a/apps/judicial-system/backend/src/app/modules/notification/institutionNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/institutionNotification.service.ts index 1562251ab13d..cad5fba282fa 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/institutionNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/institutionNotification.service.ts @@ -46,8 +46,12 @@ export class InstitutionNotificationService extends BaseNotificationService { } private async sendIndictmentsWaitingForConfirmationNotification( - prosecutorsOfficeId: string, + prosecutorsOfficeId?: string, ): Promise { + if (!prosecutorsOfficeId) { + return + } + const count = await this.internalCaseService.countIndictmentsWaitingForConfirmation( prosecutorsOfficeId, @@ -87,7 +91,7 @@ export class InstitutionNotificationService extends BaseNotificationService { async sendNotification( type: NotificationType, - prosecutorsOfficeId: string, + prosecutorsOfficeId?: string, ): Promise { try { switch (type) { diff --git a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts index aaa0b1441cd8..2b96d6aaac7a 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts @@ -20,11 +20,12 @@ import { import { Case, CaseHasExistedGuard, CurrentCase } from '../case' import { CaseNotificationDto } from './dto/caseNotification.dto' import { InstitutionNotificationDto } from './dto/institutionNotification.dto' -import { NotificationDto } from './dto/notification.dto' +import { SubpoenaNotificationDto } from './dto/subpoenaNotification.dto' import { DeliverResponse } from './models/deliver.response' import { InstitutionNotificationService } from './institutionNotification.service' import { InternalNotificationService } from './internalNotification.service' import { NotificationDispatchService } from './notificationDispatch.service' +import { SubpoenaNotificationService } from './subpoenaNotification.service' @UseGuards(TokenGuard) @Controller('api/internal') @@ -34,6 +35,7 @@ export class InternalNotificationController { private readonly internalNotificationService: InternalNotificationService, private readonly notificationDispatchService: NotificationDispatchService, private readonly institutionNotificationService: InstitutionNotificationService, + private readonly subpoenaNotificationService: SubpoenaNotificationService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} @@ -75,13 +77,29 @@ export class InternalNotificationController { ) } + @Post(messageEndpoint[MessageType.SUBPOENA_NOTIFICATION]) + @ApiCreatedResponse({ + type: DeliverResponse, + description: 'Sends a new notification', + }) + sendSubpoenaNotification( + @Body() notificationDto: SubpoenaNotificationDto, + ): Promise { + this.logger.debug(`Sending ${notificationDto.type} notification`) + + return this.subpoenaNotificationService.sendNotification( + notificationDto.type, + notificationDto.subpoena, + ) + } + @Post(messageEndpoint[MessageType.NOTIFICATION_DISPATCH]) @ApiCreatedResponse({ type: DeliverResponse, description: 'Dispatches notifications', }) dispatchNotification( - @Body() notificationDto: NotificationDto, + @Body() notificationDto: InstitutionNotificationDto, ): Promise { this.logger.debug(`Dispatching ${notificationDto.type} notification`) diff --git a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.service.ts index 7be128ecb9b4..b8b670d07c24 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.service.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.service.ts @@ -118,30 +118,6 @@ export class InternalNotificationService extends BaseNotificationService { ) } - private hasSentNotification( - type: NotificationType, - notifications?: Notification[], - ) { - return notifications?.some((notification) => notification.type === type) - } - - private hasReceivedNotification( - type?: NotificationType | NotificationType[], - address?: string, - notifications?: Notification[], - ) { - const types = type ? [type].flat() : Object.values(NotificationType) - - return notifications?.some((notification) => { - return ( - types.includes(notification.type) && - notification.recipients.some( - (recipient) => recipient.address === address && recipient.success, - ) - ) - }) - } - private async shouldSendNotificationToPrison( theCase: Case, ): Promise { diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts index 162c49216243..f2c3054e7845 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.module.ts @@ -13,6 +13,7 @@ import { DefendantModule, EventModule, InstitutionModule, + SubpoenaModule, UserModule, } from '../index' import { Notification } from './models/notification.model' @@ -22,6 +23,7 @@ import { InternalNotificationService } from './internalNotification.service' import { NotificationController } from './notification.controller' import { NotificationService } from './notification.service' import { NotificationDispatchService } from './notificationDispatch.service' +import { SubpoenaNotificationService } from './subpoenaNotification.service' @Module({ imports: [ @@ -31,6 +33,7 @@ import { NotificationDispatchService } from './notificationDispatch.service' MessageModule, InstitutionModule, UserModule, + forwardRef(() => SubpoenaModule), forwardRef(() => CaseModule), forwardRef(() => CourtModule), forwardRef(() => EventModule), @@ -43,6 +46,7 @@ import { NotificationDispatchService } from './notificationDispatch.service' InternalNotificationService, NotificationDispatchService, InstitutionNotificationService, + SubpoenaNotificationService, ], }) export class NotificationModule {} diff --git a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts new file mode 100644 index 000000000000..a8ac52f19be1 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.service.ts @@ -0,0 +1,196 @@ +import { MessageDescriptor } from 'react-intl' + +import { + Inject, + Injectable, + InternalServerErrorException, +} from '@nestjs/common' +import { InjectModel } from '@nestjs/sequelize' + +import { IntlService } from '@island.is/cms-translations' +import { EmailService } from '@island.is/email-service' +import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' +import { type ConfigType } from '@island.is/nest/config' + +import { INDICTMENTS_COURT_OVERVIEW_ROUTE } from '@island.is/judicial-system/consts' +import { NotificationType } from '@island.is/judicial-system/types' + +import { EventService } from '../event' +import { Subpoena } from '../subpoena' +import { DeliverResponse } from './models/deliver.response' +import { Notification, Recipient } from './models/notification.model' +import { BaseNotificationService } from './baseNotification.service' +import { notificationModuleConfig } from './notification.config' +import { strings } from './subpoenaNotification.strings' + +type SubpoenaNotificationType = + | NotificationType.SERVICE_SUCCESSFUL + | NotificationType.SERVICE_FAILED + | NotificationType.DEFENDANT_SELECTED_DEFENDER + +@Injectable() +export class SubpoenaNotificationService extends BaseNotificationService { + constructor( + @InjectModel(Notification) + notificationModel: typeof Notification, + @Inject(notificationModuleConfig.KEY) + config: ConfigType, + @Inject(LOGGER_PROVIDER) logger: Logger, + intlService: IntlService, + emailService: EmailService, + eventService: EventService, + ) { + super( + notificationModel, + emailService, + intlService, + config, + eventService, + logger, + ) + } + + private getEmailContents(notificationType: SubpoenaNotificationType): { + subject: MessageDescriptor + body: MessageDescriptor + } { + switch (notificationType) { + case NotificationType.SERVICE_SUCCESSFUL: + return { + subject: strings.serviceSuccessfulSubject, + body: strings.serviceSuccessfulBody, + } + case NotificationType.SERVICE_FAILED: + return { + subject: strings.serviceFailedSubject, + body: strings.serviceFailedBody, + } + case NotificationType.DEFENDANT_SELECTED_DEFENDER: + return { + subject: strings.defendantSelectedDefenderSubject, + body: strings.defendantSelectedDefenderBody, + } + default: + throw new InternalServerErrorException('Email contents not found') + } + } + + private async sendSubpoenaNotification( + notificationType: SubpoenaNotificationType, + subpoena: Subpoena, + ): Promise { + const theCase = subpoena.case + + if (!theCase) { + throw new InternalServerErrorException('Missing case') + } + + const hasSentSuccessfulServiceNotification = Boolean( + this.hasSentNotification( + NotificationType.SERVICE_SUCCESSFUL, + theCase.notifications, + ) && notificationType === NotificationType.SERVICE_SUCCESSFUL, + ) + + const hasSentFailedServiceNotification = Boolean( + this.hasSentNotification( + NotificationType.SERVICE_FAILED, + theCase.notifications, + ) && notificationType === NotificationType.SERVICE_FAILED, + ) + + const hasSendDefendantSelectedDefenderNotification = Boolean( + this.hasSentNotification( + NotificationType.DEFENDANT_SELECTED_DEFENDER, + theCase.notifications, + ) && notificationType === NotificationType.DEFENDANT_SELECTED_DEFENDER, + ) + + if ( + hasSentSuccessfulServiceNotification || + hasSentFailedServiceNotification || + hasSendDefendantSelectedDefenderNotification + ) { + return + } + + await this.refreshFormatMessage() + + const emailContents = this.getEmailContents(notificationType) + + const formattedSubject = this.formatMessage(emailContents.subject, { + courtCaseNumber: theCase.courtCaseNumber, + }) + + const formattedBody = this.formatMessage(emailContents.body, { + courtCaseNumber: theCase.courtCaseNumber, + linkStart: ``, + linkEnd: '', + }) + + return this.sendEmails( + theCase.id, + notificationType, + formattedSubject, + formattedBody, + theCase.judge?.name, + theCase.judge?.email, + theCase.registrar?.name, + theCase.registrar?.email, + ) + } + + private async sendEmails( + caseId: string, + notificationType: SubpoenaNotificationType, + subject: string, + body: string, + judgeName?: string, + judgeEmail?: string, + registrarName?: string, + registrarEmail?: string, + ) { + const promises: Promise[] = [] + + if (judgeName && judgeEmail) { + promises.push( + this.sendEmail(subject, body, judgeName, judgeEmail, undefined, true), + ) + } + + if (registrarName && registrarEmail) { + promises.push( + this.sendEmail( + subject, + body, + registrarName, + registrarEmail, + undefined, + true, + ), + ) + } + + const recipients = await Promise.all(promises) + + return this.recordNotification(caseId, notificationType, recipients) + } + + async sendNotification( + type: NotificationType, + subpoena: Subpoena, + ): Promise { + try { + await this.sendSubpoenaNotification( + type as SubpoenaNotificationType, + subpoena, + ) + } catch (error) { + this.logger.error('Failed to send notification', error) + + return { delivered: false } + } + + return { delivered: true } + } +} diff --git a/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts new file mode 100644 index 000000000000..8f08b6f475ae --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/notification/subpoenaNotification.strings.ts @@ -0,0 +1,43 @@ +import { defineMessage } from '@formatjs/intl' + +export const strings = { + serviceSuccessfulSubject: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.service_successful_subject', + defaultMessage: 'Birting tókst í máli {courtCaseNumber}', + description: + 'Subject of the notification sent when the serive status in an indictment has changed', + }), + serviceSuccessfulBody: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.service_successful_body', + defaultMessage: + 'Birting ákæru og fyrirkalls tókst í máli {courtCaseNumber}.

Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}.', + description: + 'Body of the notification sent when the serive status in an indictment has changed', + }), + serviceFailedSubject: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.service_failed_subject', + defaultMessage: 'Birting árangurslaus í máli {courtCaseNumber}', + description: + 'Subject of the notification sent when the serive status in an indictment has changed', + }), + serviceFailedBody: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.service_failed_body', + defaultMessage: + 'Birting ákæru og fyrirkalls var árangurslaus í máli {courtCaseNumber}.

Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}.', + description: + 'Body of the notification sent when the serive status in an indictment has changed', + }), + defendantSelectedDefenderSubject: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.defendant_selected_defender_subject', + defaultMessage: 'Val á verjanda í máli {courtCaseNumber}', + description: + 'Subject of the notification sent when the serive status in an indictment has changed', + }), + defendantSelectedDefenderBody: defineMessage({ + id: 'judicial.system.backend:subpoena_notifications.defendant_selected_defender_body', + defaultMessage: + 'Verjandi hefur verið valinn í máli {courtCaseNumber}.

Sjá nánar á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}.', + description: + 'Body of the notification sent when the serive status in an indictment has changed', + }), +} diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts index 0da62cac4fa5..b805d15ba33e 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts @@ -47,7 +47,7 @@ import { Subpoena } from './models/subpoena.model' DefendantExistsGuard, SubpoenaExistsOptionalGuard, ) -@ApiTags('limited access defendants') +@ApiTags('limited access subpoenas') export class LimitedAccessSubpoenaController { constructor( private readonly pdfService: PdfService, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts index fc8981500d91..42f6249d39f2 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.module.ts @@ -1,6 +1,8 @@ import { forwardRef, Module } from '@nestjs/common' import { SequelizeModule } from '@nestjs/sequelize' +import { MessageModule } from '@island.is/judicial-system/message' + import { CaseModule } from '../case/case.module' import { Defendant } from '../defendant/models/defendant.model' import { FileModule } from '../file/file.module' @@ -16,6 +18,7 @@ import { SubpoenaService } from './subpoena.service' forwardRef(() => CaseModule), forwardRef(() => PoliceModule), forwardRef(() => FileModule), + forwardRef(() => MessageModule), SequelizeModule.forFeature([Subpoena, Defendant]), ], controllers: [ diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index c476d085e835..26175ace9bb1 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -14,10 +14,15 @@ import { InjectConnection, InjectModel } from '@nestjs/sequelize' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' +import { MessageService, MessageType } from '@island.is/judicial-system/message' import { CaseFileCategory, + DefenderChoice, + isFailedServiceStatus, + isSuccessfulServiceStatus, isTrafficViolationCase, - type User, + NotificationType, + type User as TUser, } from '@island.is/judicial-system/types' import { Case } from '../case/models/case.model' @@ -25,12 +30,26 @@ import { PdfService } from '../case/pdf.service' import { Defendant } from '../defendant/models/defendant.model' import { FileService } from '../file' import { PoliceService } from '../police' +import { User } from '../user' import { UpdateSubpoenaDto } from './dto/updateSubpoena.dto' import { DeliverResponse } from './models/deliver.response' import { Subpoena } from './models/subpoena.model' export const include: Includeable[] = [ - { model: Case, as: 'case' }, + { + model: Case, + as: 'case', + include: [ + { + model: User, + as: 'judge', + }, + { + model: User, + as: 'registrar', + }, + ], + }, { model: Defendant, as: 'defendant' }, ] @Injectable() @@ -40,6 +59,7 @@ export class SubpoenaService { @InjectModel(Subpoena) private readonly subpoenaModel: typeof Subpoena, @InjectModel(Defendant) private readonly defendantModel: typeof Defendant, private readonly pdfService: PdfService, + private readonly messageService: MessageService, @Inject(forwardRef(() => PoliceService)) private readonly policeService: PoliceService, @Inject(forwardRef(() => FileService)) @@ -92,11 +112,20 @@ export class SubpoenaService { defenderEmail, defenderPhoneNumber, defenderName, + serviceStatus, requestedDefenderChoice, requestedDefenderNationalId, requestedDefenderName, } = update + const notificationType = isSuccessfulServiceStatus(serviceStatus) + ? NotificationType.SERVICE_SUCCESSFUL + : isFailedServiceStatus(serviceStatus) + ? NotificationType.SERVICE_FAILED + : defenderChoice === DefenderChoice.CHOOSE && defenderNationalId + ? NotificationType.DEFENDANT_SELECTED_DEFENDER + : undefined + const [numberOfAffectedRows] = await this.subpoenaModel.update(update, { where: { subpoenaId: subpoena.subpoenaId }, returning: true, @@ -138,6 +167,23 @@ export class SubpoenaService { ) } + if (notificationType) { + this.messageService + .sendMessagesToQueue([ + { + type: MessageType.SUBPOENA_NOTIFICATION, + body: { + type: notificationType, + subpoena, + }, + }, + ]) + .catch((reason) => + // Tolerate failure, but log + this.logger.error('Failed to dispatch notifications', { reason }), + ) + } + const updatedSubpoena = await this.findBySubpoenaId(subpoena.subpoenaId) return updatedSubpoena @@ -185,7 +231,7 @@ export class SubpoenaService { theCase: Case, defendant: Defendant, subpoena: Subpoena, - user: User, + user: TUser, ): Promise { try { const subpoenaPdf = await this.pdfService.getSubpoenaPdf( diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts index 3ae092fe8887..b25adab82dfa 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/createTestingSubpoenaModule.ts @@ -10,6 +10,7 @@ import { SharedAuthModule, sharedAuthModuleConfig, } from '@island.is/judicial-system/auth' +import { MessageService } from '@island.is/judicial-system/message' import { CaseService, PdfService } from '../../case' import { Defendant } from '../../defendant' @@ -27,6 +28,7 @@ jest.mock('../../case/case.service') jest.mock('../../case/pdf.service') jest.mock('../../police/police.service') jest.mock('../../file/file.service') +jest.mock('@island.is/judicial-system/message') export const createTestingSubpoenaModule = async () => { const subpoenaModule = await Test.createTestingModule({ @@ -75,6 +77,7 @@ export const createTestingSubpoenaModule = async () => { }, }, SubpoenaService, + MessageService, ], }).compile() diff --git a/libs/judicial-system/message/src/lib/message.ts b/libs/judicial-system/message/src/lib/message.ts index 1b5aaf576864..2cdc08ea958e 100644 --- a/libs/judicial-system/message/src/lib/message.ts +++ b/libs/judicial-system/message/src/lib/message.ts @@ -26,6 +26,7 @@ export enum MessageType { DELIVERY_TO_POLICE_SIGNED_RULING = 'DELIVERY_TO_POLICE_SIGNED_RULING', DELIVERY_TO_POLICE_APPEAL = 'DELIVERY_TO_POLICE_APPEAL', NOTIFICATION = 'NOTIFICATION', + SUBPOENA_NOTIFICATION = 'SUBPOENA_NOTIFICATION', NOTIFICATION_DISPATCH = 'NOTIFICATION_DISPATCH', } @@ -59,6 +60,7 @@ export const messageEndpoint: { [key in MessageType]: string } = { DELIVERY_TO_POLICE_SIGNED_RULING: 'deliverSignedRulingToPolice', DELIVERY_TO_POLICE_APPEAL: 'deliverAppealToPolice', NOTIFICATION: 'notification', + SUBPOENA_NOTIFICATION: 'subpoenaNotification', NOTIFICATION_DISPATCH: 'notification/dispatch', } diff --git a/libs/judicial-system/types/src/index.ts b/libs/judicial-system/types/src/index.ts index 502e7ce2f93f..a63b26a41b5a 100644 --- a/libs/judicial-system/types/src/index.ts +++ b/libs/judicial-system/types/src/index.ts @@ -8,6 +8,7 @@ export { ServiceRequirement, ServiceStatus, isSuccessfulServiceStatus, + isFailedServiceStatus, } from './lib/defendant' export { InstitutionType } from './lib/institution' export { NotificationType } from './lib/notification' diff --git a/libs/judicial-system/types/src/lib/defendant.ts b/libs/judicial-system/types/src/lib/defendant.ts index be51efe47cef..a2cb9852d27a 100644 --- a/libs/judicial-system/types/src/lib/defendant.ts +++ b/libs/judicial-system/types/src/lib/defendant.ts @@ -45,3 +45,7 @@ export const successfulServiceStatus: string[] = [ export const isSuccessfulServiceStatus = (status?: ServiceStatus): boolean => { return Boolean(status && successfulServiceStatus.includes(status)) } + +export const isFailedServiceStatus = (status?: ServiceStatus): boolean => { + return Boolean(status && status === ServiceStatus.FAILED) +} diff --git a/libs/judicial-system/types/src/lib/notification.ts b/libs/judicial-system/types/src/lib/notification.ts index 26d38136f834..1860778fdedf 100644 --- a/libs/judicial-system/types/src/lib/notification.ts +++ b/libs/judicial-system/types/src/lib/notification.ts @@ -18,4 +18,7 @@ export enum NotificationType { INDICTMENT_DENIED = 'INDICTMENT_DENIED', INDICTMENT_RETURNED = 'INDICTMENT_RETURNED', INDICTMENTS_WAITING_FOR_CONFIRMATION = 'INDICTMENTS_WAITING_FOR_CONFIRMATION', + SERVICE_SUCCESSFUL = 'SERVICE_SUCCESSFUL', + SERVICE_FAILED = 'SERVICE_FAILED', + DEFENDANT_SELECTED_DEFENDER = 'DEFENDANT_SELECTED_DEFENDER', }