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

chore(j-s): Send notifications when subpoena changes #16329

Merged
merged 51 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
067d728
Checkpoint
oddsson Oct 8, 2024
414f562
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 8, 2024
287a3a1
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 10, 2024
924ad3c
Refactor
oddsson Oct 10, 2024
0682944
Checkpoint
oddsson Oct 10, 2024
09f681f
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 10, 2024
bdbef5a
Add messages to queue
oddsson Oct 11, 2024
9353c83
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 11, 2024
b0953dc
Checkpoint
oddsson Oct 11, 2024
7544204
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 11, 2024
44e734d
Construct the emails
oddsson Oct 14, 2024
2588cb5
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 14, 2024
07f4561
Fix build
oddsson Oct 14, 2024
8479561
Checkpoint
oddsson Oct 14, 2024
9e42a37
Implement failed notifications
oddsson Oct 14, 2024
f56c6f1
Add failed and defenderchoice emails
oddsson Oct 14, 2024
82f925f
Remove unused code
oddsson Oct 14, 2024
3745b64
Remove unused code
oddsson Oct 14, 2024
ac82411
Refactor
oddsson Oct 14, 2024
1cb10f2
Refactor
oddsson Oct 14, 2024
bd9fca7
Remove unused code
oddsson Oct 14, 2024
83129b6
Refactor
oddsson Oct 14, 2024
4f5ca7a
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 14, 2024
548a152
Refactor
oddsson Oct 15, 2024
05da915
Refactor
oddsson Oct 15, 2024
f54358c
Refactor
oddsson Oct 15, 2024
b64168a
Refactor
oddsson Oct 15, 2024
a06a9cd
Refactor
oddsson Oct 15, 2024
11cb44e
Refactor
oddsson Oct 15, 2024
6a715e9
Refactor
oddsson Oct 15, 2024
5e1de12
Refactor
oddsson Oct 15, 2024
48d0c4d
Refactor
oddsson Oct 15, 2024
f7382b5
Refactor
oddsson Oct 15, 2024
deb2092
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 15, 2024
01ca587
Refactor
oddsson Oct 15, 2024
2c4d6e2
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 16, 2024
bb1561b
Merge branch 'j-s/notifications-defendant-selected-defender' of githu…
oddsson Oct 16, 2024
40f6e8f
Merge
oddsson Oct 16, 2024
e91e402
Cleanup
oddsson Oct 16, 2024
7cf8113
Remove console.log
oddsson Oct 16, 2024
4322579
Refactor
oddsson Oct 16, 2024
91959e9
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 17, 2024
65203c4
Refactor
oddsson Oct 17, 2024
c411118
Refactor
oddsson Oct 17, 2024
5bfeb79
Remove unused code
oddsson Oct 17, 2024
dfa4abe
Refactor
oddsson Oct 17, 2024
e980af2
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 18, 2024
bd5b1b2
Fix tests
oddsson Oct 18, 2024
44a7c57
Merge branch 'main' of github.com:island-is/island.is into j-s/notifi…
oddsson Oct 18, 2024
c26d006
Merge branch 'main' into j-s/notifications-defendant-selected-defender
kodiakhq[bot] Oct 18, 2024
1c7be80
Merge branch 'main' into j-s/notifications-defendant-selected-defender
kodiakhq[bot] Oct 18, 2024
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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { IsEnum, IsNotEmpty, IsOptional, IsUUID } from 'class-validator'

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'

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

export class NotificationBodyDto {
@IsNotEmpty()
@IsEnum(NotificationType)
@ApiProperty({ enum: NotificationType })
readonly type!: NotificationType

@IsOptional()
@IsUUID()
@ApiPropertyOptional({ type: String })
readonly prosecutorsOfficeId?: string

@IsOptional()
@IsUUID()
@ApiPropertyOptional({ type: String })
readonly subpoenaId?: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,12 @@ export class InstitutionNotificationService extends BaseNotificationService {
}

private async sendIndictmentsWaitingForConfirmationNotification(
prosecutorsOfficeId: string,
prosecutorsOfficeId?: string,
): Promise<unknown> {
if (!prosecutorsOfficeId) {
oddsson marked this conversation as resolved.
Show resolved Hide resolved
return
}

const count =
await this.internalCaseService.countIndictmentsWaitingForConfirmation(
prosecutorsOfficeId,
Expand Down Expand Up @@ -87,7 +91,7 @@ export class InstitutionNotificationService extends BaseNotificationService {

async sendNotification(
type: NotificationType,
prosecutorsOfficeId: string,
prosecutorsOfficeId?: string,
): Promise<DeliverResponse> {
try {
switch (type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,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 { NotificationBodyDto } from './dto/notificationBody.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')
Expand All @@ -34,6 +34,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,
) {}

Expand Down Expand Up @@ -65,14 +66,19 @@ export class InternalNotificationController {
description: 'Sends a new notification',
})
sendNotification(
@Body() notificationDto: InstitutionNotificationDto,
@Body() notificationDto: NotificationBodyDto,
): Promise<DeliverResponse> {
this.logger.debug(`Sending ${notificationDto.type} notification`)

return this.institutionNotificationService.sendNotification(
notificationDto.type,
notificationDto.prosecutorsOfficeId,
)
return notificationDto.prosecutorsOfficeId
? this.institutionNotificationService.sendNotification(
notificationDto.type,
notificationDto.prosecutorsOfficeId,
)
: this.subpoenaNotificationService.sendNotification(
notificationDto.type,
notificationDto.subpoenaId,
)
oddsson marked this conversation as resolved.
Show resolved Hide resolved
}

@Post(messageEndpoint[MessageType.NOTIFICATION_DISPATCH])
Expand All @@ -81,7 +87,7 @@ export class InternalNotificationController {
description: 'Dispatches notifications',
})
dispatchNotification(
@Body() notificationDto: NotificationDto,
@Body() notificationDto: NotificationBodyDto,
): Promise<DeliverResponse> {
this.logger.debug(`Dispatching ${notificationDto.type} notification`)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
DefendantModule,
EventModule,
InstitutionModule,
SubpoenaModule,
UserModule,
} from '../index'
import { Notification } from './models/notification.model'
Expand All @@ -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: [
Expand All @@ -31,6 +33,7 @@ import { NotificationDispatchService } from './notificationDispatch.service'
MessageModule,
InstitutionModule,
UserModule,
forwardRef(() => SubpoenaModule),
forwardRef(() => CaseModule),
forwardRef(() => CourtModule),
forwardRef(() => EventModule),
Expand All @@ -43,6 +46,7 @@ import { NotificationDispatchService } from './notificationDispatch.service'
InternalNotificationService,
NotificationDispatchService,
InstitutionNotificationService,
SubpoenaNotificationService,
],
})
export class NotificationModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
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 { CaseService } from '../case'
import { EventService } from '../event'
import { SubpoenaService } 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'

@Injectable()
export class SubpoenaNotificationService extends BaseNotificationService {
constructor(
@InjectModel(Notification)
notificationModel: typeof Notification,
@Inject(notificationModuleConfig.KEY)
config: ConfigType<typeof notificationModuleConfig>,
@Inject(LOGGER_PROVIDER) logger: Logger,
intlService: IntlService,
emailService: EmailService,
eventService: EventService,
private readonly caseService: CaseService,
private readonly subpoenaService: SubpoenaService,
) {
super(
notificationModel,
emailService,
intlService,
config,
eventService,
logger,
)
}

private async sendServiceSuccessfulNotification(
oddsson marked this conversation as resolved.
Show resolved Hide resolved
subpoenaId?: string,
): Promise<unknown> {
oddsson marked this conversation as resolved.
Show resolved Hide resolved
if (!subpoenaId) {
return
}

const theCase = await this.getCase(subpoenaId)
oddsson marked this conversation as resolved.
Show resolved Hide resolved

await this.refreshFormatMessage()

const subject = this.formatMessage(strings.serviceSuccessfulSubject, {
courtCaseNumber: theCase.courtCaseNumber,
})

const body = this.formatMessage(strings.serviceSuccessfulBody, {
courtCaseNumber: theCase.courtCaseNumber,
linkStart: `<a href="${this.config.clientUrl}${INDICTMENTS_COURT_OVERVIEW_ROUTE}/${theCase.id}">`,
linkEnd: '</a>',
})

return this.sendEmails(
subject,
body,
theCase.judge?.name,
theCase.judge?.email,
theCase.registrar?.name,
theCase.registrar?.email,
)
}
oddsson marked this conversation as resolved.
Show resolved Hide resolved

private async sendServiceFailedNotification(
subpoenaId?: string,
): Promise<unknown> {
if (!subpoenaId) {
return
}

const theCase = await this.getCase(subpoenaId)

await this.refreshFormatMessage()

const subject = this.formatMessage(strings.serviceFailedSubject, {
courtCaseNumber: theCase.courtCaseNumber,
})

const body = this.formatMessage(strings.serviceFailedBody, {
courtCaseNumber: theCase.courtCaseNumber,
linkStart: `<a href="${this.config.clientUrl}${INDICTMENTS_COURT_OVERVIEW_ROUTE}/${theCase.id}">`,
linkEnd: '</a>',
})

return this.sendEmails(
subject,
body,
theCase.judge?.name,
theCase.judge?.email,
theCase.registrar?.name,
theCase.registrar?.email,
)
}

private async sendDefendantSelectedDefenderNotification(
subpoenaId?: string,
): Promise<unknown> {
if (!subpoenaId) {
return
}

const theCase = await this.getCase(subpoenaId)

await this.refreshFormatMessage()

const subject = this.formatMessage(
strings.defendantSelectedDefenderSubject,
{
courtCaseNumber: theCase.courtCaseNumber,
},
)

const body = this.formatMessage(strings.defendantSelectedDefenderBody, {
courtCaseNumber: theCase.courtCaseNumber,
linkStart: `<a href="${this.config.clientUrl}${INDICTMENTS_COURT_OVERVIEW_ROUTE}/${theCase.id}">`,
linkEnd: '</a>',
})

return this.sendEmails(
subject,
body,
theCase.judge?.name,
theCase.judge?.email,
theCase.registrar?.name,
theCase.registrar?.email,
)
}

private async getCase(subpoenaId: string) {
const subpoena = await this.subpoenaService.findBySubpoenaId(subpoenaId)
const theCase = await this.caseService.findById(subpoena.caseId)

if (!theCase.id) {
throw new InternalServerErrorException(`Case not found`)
}
oddsson marked this conversation as resolved.
Show resolved Hide resolved

if (!theCase.courtCaseNumber || !theCase.id) {
throw new InternalServerErrorException(
`Unable to find courtCaseNumber for case ${theCase.id}`,
)
}

return theCase
}
oddsson marked this conversation as resolved.
Show resolved Hide resolved

private sendEmails(
subject: string,
body: string,
judgeName?: string,
judgeEmail?: string,
registrarName?: string,
registrarEmail?: string,
) {
const promises: Promise<Recipient>[] = []

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,
),
)
}

return Promise.all(promises)
}

async sendNotification(
type: NotificationType,
subpoenaId?: string,
): Promise<DeliverResponse> {
try {
switch (type) {
case NotificationType.SERVICE_SUCCESSFUL:
await this.sendServiceSuccessfulNotification(subpoenaId)
break
case NotificationType.SERVICE_FAILED:
await this.sendServiceFailedNotification(subpoenaId)
break
case NotificationType.DEFENDANT_SELECTED_DEFENDER:
await this.sendDefendantSelectedDefenderNotification(subpoenaId)
break
default:
throw new InternalServerErrorException(
`Invalid notification type ${type}`,
)
}
} catch (error) {
this.logger.error('Failed to send notification', error)

return { delivered: false }
}

return { delivered: true }
}
}
Loading
Loading