Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(j-s): Notifications to prosecutors office in completed indictments #17174

Merged
merged 11 commits into from
Dec 10, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export const serviceSetup = (): ServiceBuilder<'judicial-system-backend'> =>
EMAIL_FROM_NAME: '/k8s/judicial-system/EMAIL_FROM_NAME',
EMAIL_REPLY_TO: '/k8s/judicial-system/EMAIL_REPLY_TO',
EMAIL_REPLY_TO_NAME: '/k8s/judicial-system/EMAIL_REPLY_TO_NAME',
POLICE_INSTITUTIONS_EMAILS:
'/k8s/judicial-system/POLICE_INSTITUTIONS_EMAILS',
PRISON_EMAIL: '/k8s/judicial-system/PRISON_EMAIL',
PRISON_ADMIN_EMAIL: '/k8s/judicial-system/PRISON_ADMIN_EMAIL',
PRISON_ADMIN_INDICTMENT_EMAILS:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const notifications = {
}),
emailWhitelistDomains: defineMessage({
id: 'judicial.system.backend:notifications.email_whitelist_domains',
defaultMessage: 'omnitrix.is,kolibri.is',
defaultMessage: 'omnitrix.is,kolibri.is,dummy.dd',
description: 'Notað til að tilgreina hvort póstfang sé í hvítlista',
}),
readyForCourt: defineMessages({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { Module } from '@nestjs/common'
import { forwardRef, Module } from '@nestjs/common'
import { SequelizeModule } from '@nestjs/sequelize'

import { MessageModule } from '@island.is/judicial-system/message'

import { EventLog } from './models/eventLog.model'
import { EventLogController } from './eventLog.controller'
import { EventLogService } from './eventLog.service'

@Module({
imports: [SequelizeModule.forFeature([EventLog])],
imports: [
forwardRef(() => MessageModule),
SequelizeModule.forFeature([EventLog]),
],
providers: [EventLogService],
exports: [EventLogService],
controllers: [EventLogController],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { InjectModel } from '@nestjs/sequelize'
import type { Logger } from '@island.is/logging'
import { LOGGER_PROVIDER } from '@island.is/logging'

import { EventType } from '@island.is/judicial-system/types'
import { MessageService, MessageType } from '@island.is/judicial-system/message'
import {
EventNotificationType,
EventType,
} from '@island.is/judicial-system/types'

import { CreateEventLogDto } from './dto/createEventLog.dto'
import { EventLog } from './models/eventLog.model'
Expand All @@ -20,11 +24,19 @@ const allowMultiple: EventType[] = [
EventType.INDICTMENT_CONFIRMED,
]

const eventToNotificationMap: Partial<
Record<EventType, EventNotificationType>
> = {
INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR:
EventNotificationType.INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR,
}

@Injectable()
export class EventLogService {
constructor(
@InjectModel(EventLog)
private readonly eventLogModel: typeof EventLog,
private readonly messageService: MessageService,
@Inject(LOGGER_PROVIDER)
private readonly logger: Logger,
) {}
Expand Down Expand Up @@ -58,6 +70,10 @@ export class EventLogService {
// Tolerate failure but log error
this.logger.error('Failed to create event log', error)
}

if (caseId) {
this.addEventNotificationToQueue(eventType, caseId)
}
}

async loginMap(
Expand Down Expand Up @@ -86,4 +102,19 @@ export class EventLogService {
),
)
}

// Sends events to queue for notification dispatch
private addEventNotificationToQueue(eventType: EventType, caseId: string) {
const notificationType = eventToNotificationMap[eventType]

if (notificationType) {
this.messageService.sendMessagesToQueue([
{
type: MessageType.EVENT_NOTIFICATION_DISPATCH,
caseId: caseId,
body: { type: notificationType },
},
])
}
}
unakb marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IsEnum, IsNotEmpty } from 'class-validator'

import { ApiProperty } from '@nestjs/swagger'

import { IndictmentCaseNotificationType } from '@island.is/judicial-system/types'

export class IndictmentCaseNotificationDto {
@IsNotEmpty()
@IsEnum(IndictmentCaseNotificationType)
@ApiProperty({ enum: IndictmentCaseNotificationType })
readonly type!: IndictmentCaseNotificationType
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@ import { IsEnum, IsNotEmpty } from 'class-validator'

import { ApiProperty } from '@nestjs/swagger'

import { NotificationDispatchType } from '@island.is/judicial-system/types'
import {
EventNotificationType,
NotificationDispatchType,
} from '@island.is/judicial-system/types'

export class NotificationDispatchDto {
@IsNotEmpty()
@IsEnum(NotificationDispatchType)
@ApiProperty({ enum: NotificationDispatchType })
readonly type!: NotificationDispatchType
}

export class EventNotificationDispatchDto {
@IsNotEmpty()
@IsEnum(EventNotificationType)
@ApiProperty({ enum: EventNotificationType })
readonly type!: EventNotificationType
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import {
messageEndpoint,
MessageType,
} from '@island.is/judicial-system/message'
import { indictmentCases } from '@island.is/judicial-system/types'

import { Case, CaseHasExistedGuard, CurrentCase } from '../case'
import { Case, CaseHasExistedGuard, CaseTypeGuard, CurrentCase } from '../case'
import {
CivilClaimant,
CivilClaimantExistsGuard,
Expand All @@ -30,13 +31,18 @@ import { SubpoenaExistsGuard } from '../subpoena'
import { CaseNotificationDto } from './dto/caseNotification.dto'
import { CivilClaimantNotificationDto } from './dto/civilClaimantNotification.dto'
import { DefendantNotificationDto } from './dto/defendantNotification.dto'
import { IndictmentCaseNotificationDto } from './dto/indictmentCaseNotification.dto'
import { InstitutionNotificationDto } from './dto/institutionNotification.dto'
import { NotificationDispatchDto } from './dto/notificationDispatch.dto'
import {
EventNotificationDispatchDto,
NotificationDispatchDto,
} from './dto/notificationDispatch.dto'
import { SubpoenaNotificationDto } from './dto/subpoenaNotification.dto'
import { DeliverResponse } from './models/deliver.response'
import { CaseNotificationService } from './services/caseNotification/caseNotification.service'
import { CivilClaimantNotificationService } from './services/civilClaimantNotification/civilClaimantNotification.service'
import { DefendantNotificationService } from './services/defendantNotification/defendantNotification.service'
import { IndictmentCaseNotificationService } from './services/indictmentCaseNotification/indictmentCaseNotification.service'
import { InstitutionNotificationService } from './services/institutionNotification/institutionNotification.service'
import { SubpoenaNotificationService } from './services/subpoenaNotification/subpoenaNotification.service'
import { NotificationDispatchService } from './notificationDispatch.service'
Expand All @@ -52,6 +58,7 @@ export class InternalNotificationController {
private readonly subpoenaNotificationService: SubpoenaNotificationService,
private readonly defendantNotificationService: DefendantNotificationService,
private readonly civilClaimantNotificationService: CivilClaimantNotificationService,
private readonly indictmentCaseNotificationService: IndictmentCaseNotificationService,
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}

Expand All @@ -77,6 +84,29 @@ export class InternalNotificationController {
)
}

@Post(
`case/:caseId/${messageEndpoint[MessageType.INDICTMENT_CASE_NOTIFICATION]}`,
)
@UseGuards(CaseHasExistedGuard, new CaseTypeGuard(indictmentCases))
@ApiCreatedResponse({
type: DeliverResponse,
description: 'Sends a case notification for an existing indictment case',
})
sendIndictmentCaseNotification(
@Param('caseId') caseId: string,
@CurrentCase() theCase: Case,
@Body() notificationDto: IndictmentCaseNotificationDto,
): Promise<DeliverResponse> {
this.logger.debug(
`Sending ${notificationDto.type} indictment case notification for case ${caseId}`,
)

return this.indictmentCaseNotificationService.sendIndictmentCaseNotification(
notificationDto.type,
theCase,
)
}

@Post(
`case/:caseId/${
messageEndpoint[MessageType.SUBPOENA_NOTIFICATION]
Expand Down Expand Up @@ -161,34 +191,58 @@ export class InternalNotificationController {
)
}

@Post(messageEndpoint[MessageType.NOTIFICATION_DISPATCH])
@Post(messageEndpoint[MessageType.INSTITUTION_NOTIFICATION])
@ApiCreatedResponse({
type: DeliverResponse,
description: 'Dispatches notifications',
description: 'Sends an institution notification',
})
dispatchNotification(
@Body() notificationDto: NotificationDispatchDto,
sendInstitutionNotification(
@Body() notificationDto: InstitutionNotificationDto,
): Promise<DeliverResponse> {
this.logger.debug(`Dispatching ${notificationDto.type} notification`)
this.logger.debug(`Sending ${notificationDto.type} notification`)

return this.notificationDispatchService.dispatchNotification(
return this.institutionNotificationService.sendNotification(
notificationDto.type,
notificationDto.prosecutorsOfficeId,
)
}

@Post(messageEndpoint[MessageType.INSTITUTION_NOTIFICATION])
@Post(
`case/:caseId/${messageEndpoint[MessageType.EVENT_NOTIFICATION_DISPATCH]}`,
)
@UseGuards(CaseHasExistedGuard)
@ApiCreatedResponse({
type: DeliverResponse,
description: 'Sends an institution notification',
description:
'Dispatches notifications in response to events logged in event log',
})
sendNotification(
@Body() notificationDto: InstitutionNotificationDto,
dispatchEventNotification(
@Param('caseId') caseId: string,
@CurrentCase() theCase: Case,
@Body() notificationDto: EventNotificationDispatchDto,
): Promise<DeliverResponse> {
this.logger.debug(`Sending ${notificationDto.type} notification`)
this.logger.debug(
`Dispatching ${notificationDto.type} event notification for case ${caseId}`,
)

return this.institutionNotificationService.sendNotification(
return this.notificationDispatchService.dispatchEventNotification(
notificationDto.type,
theCase,
)
}

@Post(messageEndpoint[MessageType.NOTIFICATION_DISPATCH])
@ApiCreatedResponse({
type: DeliverResponse,
description: 'Dispatches notifications',
})
dispatchNotification(
@Body() notificationDto: NotificationDispatchDto,
): Promise<DeliverResponse> {
this.logger.debug(`Dispatching ${notificationDto.type} notification`)

return this.notificationDispatchService.dispatchNotification(
notificationDto.type,
notificationDto.prosecutorsOfficeId,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ export const notificationModuleConfig = defineConfig({
courtsEmails: env.requiredJSON('COURTS_EMAILS', {}) as {
[key: string]: string
},
policeInstitutionEmails: env.requiredJSON(
'POLICE_INSTITUTIONS_EMAILS',
{},
) as {
[key: string]: string
},
},
sms: {
courtsMobileNumbers: env.requiredJSON('COURTS_MOBILE_NUMBERS', {}) as {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Notification } from './models/notification.model'
import { CaseNotificationService } from './services/caseNotification/caseNotification.service'
import { CivilClaimantNotificationService } from './services/civilClaimantNotification/civilClaimantNotification.service'
import { DefendantNotificationService } from './services/defendantNotification/defendantNotification.service'
import { IndictmentCaseNotificationService } from './services/indictmentCaseNotification/indictmentCaseNotification.service'
import { InstitutionNotificationService } from './services/institutionNotification/institutionNotification.service'
import { SubpoenaNotificationService } from './services/subpoenaNotification/subpoenaNotification.service'
import { InternalNotificationController } from './internalNotification.controller'
Expand Down Expand Up @@ -47,6 +48,7 @@ import { NotificationDispatchService } from './notificationDispatch.service'
CaseNotificationService,
CivilClaimantNotificationService,
DefendantNotificationService,
IndictmentCaseNotificationService,
InstitutionNotificationService,
NotificationService,
NotificationDispatchService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ import {
Injectable,
InternalServerErrorException,
} from '@nestjs/common'
import { ConfigType } from '@nestjs/config'

import { type Logger, LOGGER_PROVIDER } from '@island.is/logging'

import { MessageService, MessageType } from '@island.is/judicial-system/message'
import {
EventNotificationType,
IndictmentCaseNotificationType,
InstitutionNotificationType,
InstitutionType,
NotificationDispatchType,
} from '@island.is/judicial-system/types'

import { Case } from '../case'
import { Institution, InstitutionService } from '../institution'
import { DeliverResponse } from './models/deliver.response'

Expand Down Expand Up @@ -63,4 +67,43 @@ export class NotificationDispatchService {

return { delivered: true }
}

private async dispatchIndictmentSentToPublicProsecutorNotifications(
theCase: Case,
): Promise<void> {
return this.messageService.sendMessagesToQueue([
{
type: MessageType.INDICTMENT_CASE_NOTIFICATION,
caseId: theCase.id,
body: {
type: IndictmentCaseNotificationType.INDICTMENT_VERDICT_INFO,
},
},
])
}

async dispatchEventNotification(
type: EventNotificationType,
theCase: Case,
): Promise<DeliverResponse> {
try {
switch (type) {
case EventNotificationType.INDICTMENT_SENT_TO_PUBLIC_PROSECUTOR:
await this.dispatchIndictmentSentToPublicProsecutorNotifications(
theCase,
)
break
default:
throw new InternalServerErrorException(
`Invalid notification type ${type}`,
)
}
} catch (error) {
this.logger.error('Failed to dispatch event notification', error)

return { delivered: false }
}

return { delivered: true }
}
}
Loading
Loading