Skip to content

Commit

Permalink
feat(j-s): Case Files Added Notification (#16452)
Browse files Browse the repository at this point in the history
* Refactors file upload to return more granular upload result

* Sends case files updated notification from client when files are added

* Sends notifications when case files are added to an indictment case

* Reverts debugging

* Fixes naming

* Fixes naming

* Fixes notification type when storing notification result

* Fixes court name and unit tests

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
gudjong and kodiakhq[bot] authored Oct 23, 2024
1 parent e660494 commit 487d14e
Show file tree
Hide file tree
Showing 16 changed files with 468 additions and 51 deletions.
13 changes: 13 additions & 0 deletions apps/judicial-system/backend/src/app/messages/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -859,4 +859,17 @@ export const notifications = {
description: 'Texti í pósti til dómstóls þegar ákæra er afturkölluð',
},
}),
caseFilesUpdated: defineMessages({
subject: {
id: 'judicial.system.backend:notifications.case_files_updated.subject',
defaultMessage: 'Ný gögn í máli {courtCaseNumber}',
description: 'Fyrirsögn í pósti til aðila máls þegar ný gögn eru send',
},
body: {
id: 'judicial.system.backend:notifications.case_files_updated.body',
defaultMessage:
'Ný gögn hafa borist vegna máls {courtCaseNumber}. {userHasAccessToRVG, select, true {Hægt er að nálgast gögn málsins á {linkStart}yfirlitssíðu málsins í Réttarvörslugátt{linkEnd}} other {Hægt er að nálgast gögn málsins hjá {court} ef þau hafa ekki þegar verið afhent}}.',
description: 'Texti í pósti til aðila máls þegar ný gögn eru send',
},
}),
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const prosecutorNotificationRule: RolesRule = {
NotificationType.HEADS_UP,
NotificationType.READY_FOR_COURT,
NotificationType.APPEAL_CASE_FILES_UPDATED,
NotificationType.CASE_FILES_UPDATED,
],
} as RolesRule

Expand All @@ -18,7 +19,10 @@ export const defenderNotificationRule: RolesRule = {
role: UserRole.DEFENDER,
type: RulesType.FIELD_VALUES,
dtoField: 'type',
dtoFieldValues: [NotificationType.APPEAL_CASE_FILES_UPDATED],
dtoFieldValues: [
NotificationType.APPEAL_CASE_FILES_UPDATED,
NotificationType.CASE_FILES_UPDATED,
],
} as RolesRule

// Allows district court judges to send notifiications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import {
RequestSharedWithDefender,
SessionArrangements,
type User,
UserRole,
} from '@island.is/judicial-system/types'

import {
Expand Down Expand Up @@ -75,7 +74,11 @@ import {
import { notifications } from '../../messages'
import { type Case, DateLog } from '../case'
import { CourtService } from '../court'
import { type Defendant, DefendantService } from '../defendant'
import {
type CivilClaimant,
type Defendant,
DefendantService,
} from '../defendant'
import { EventService } from '../event'
import { DeliverResponse } from './models/deliver.response'
import { Notification, Recipient } from './models/notification.model'
Expand Down Expand Up @@ -1521,7 +1524,9 @@ export class InternalNotificationService extends BaseNotificationService {
SessionArrangements.ALL_PRESENT_SPOKESPERSON,
].includes(theCase.sessionArrangements)

if (!isDefenderIncludedInSessionArrangements) return false
if (!isDefenderIncludedInSessionArrangements) {
return false
}
} else {
const hasDefenderBeenNotified = this.hasReceivedNotification(
[
Expand Down Expand Up @@ -1609,10 +1614,11 @@ export class InternalNotificationService extends BaseNotificationService {
} = civilClaimant

const shouldSend =
hasSpokesperson &&
this.shouldSendAdvocateAssignedNotification(
theCase,
spokespersonEmail,
) && hasSpokesperson
)

if (shouldSend === true) {
promises.push(
Expand Down Expand Up @@ -1757,6 +1763,116 @@ export class InternalNotificationService extends BaseNotificationService {
}
//#endregion

//#region CASE_FILES_UPDATED notifications
private sendCaseFilesUpdatedNotification(
courtCaseNumber?: string,
court?: string,
link?: string,
name?: string,
email?: string,
) {
const subject = this.formatMessage(notifications.caseFilesUpdated.subject, {
courtCaseNumber,
})
const html = this.formatMessage(notifications.caseFilesUpdated.body, {
courtCaseNumber,
court: court?.replace('dómur', 'dómi'),
userHasAccessToRVG: Boolean(link),
linkStart: `<a href="${link}">`,
linkEnd: '</a>',
})

return this.sendEmail(subject, html, name, email, undefined, !link)
}

private async sendCaseFilesUpdatedNotifications(
theCase: Case,
user: User,
): Promise<DeliverResponse> {
const promises = [
this.sendCaseFilesUpdatedNotification(
theCase.courtCaseNumber,
theCase.court?.name,
`${this.config.clientUrl}${INDICTMENTS_COURT_OVERVIEW_ROUTE}/${theCase.id}`,
theCase.judge?.name,
theCase.judge?.email,
),
]

const uniqueSpokespersons = _uniqBy(
theCase.civilClaimants?.filter((c) => c.hasSpokesperson) ?? [],
(c: CivilClaimant) => c.spokespersonEmail,
)
uniqueSpokespersons.forEach((civilClaimant) => {
if (civilClaimant.spokespersonEmail) {
promises.push(
this.sendCaseFilesUpdatedNotification(
theCase.courtCaseNumber,
theCase.court?.name,
civilClaimant.spokespersonNationalId &&
formatDefenderRoute(
this.config.clientUrl,
theCase.type,
theCase.id,
),
civilClaimant.spokespersonName,
civilClaimant.spokespersonEmail,
),
)
}
})

if (isProsecutionUser(user)) {
const uniqueDefendants = _uniqBy(
theCase.defendants ?? [],
(d: Defendant) => d.defenderEmail,
)
uniqueDefendants.forEach((defendant) => {
if (defendant.defenderEmail) {
promises.push(
this.sendCaseFilesUpdatedNotification(
theCase.courtCaseNumber,
theCase.court?.name,
defendant.defenderNationalId &&
formatDefenderRoute(
this.config.clientUrl,
theCase.type,
theCase.id,
),
defendant.defenderName,
defendant.defenderEmail,
),
)
}
})
}

if (isDefenceUser(user)) {
promises.push(
this.sendCaseFilesUpdatedNotification(
theCase.courtCaseNumber,
theCase.court?.name,
`${this.config.clientUrl}${INDICTMENTS_OVERVIEW_ROUTE}/${theCase.id}`,
theCase.prosecutor?.name,
theCase.prosecutor?.email,
),
)
}

const recipients = await Promise.all(promises)

if (recipients.length > 0) {
return this.recordNotification(
theCase.id,
NotificationType.CASE_FILES_UPDATED,
recipients,
)
}

return { delivered: true }
}
//#endregion

//#region Appeal notifications
//#region COURT_OF_APPEAL_JUDGE_ASSIGNED notifications
private async sendCourtOfAppealJudgeAssignedNotification(
Expand Down Expand Up @@ -1851,7 +1967,7 @@ export class InternalNotificationService extends BaseNotificationService {
)
}

if (user.role === UserRole.DEFENDER) {
if (isDefenceUser(user)) {
promises.push(
this.sendEmail(
subject,
Expand All @@ -1863,7 +1979,7 @@ export class InternalNotificationService extends BaseNotificationService {
promises.push(this.sendSms(smsText, theCase.prosecutor?.mobileNumber))
}

if (user.role === UserRole.PROSECUTOR && theCase.defenderEmail) {
if (isProsecutionUser(user) && theCase.defenderEmail) {
const url =
theCase.defenderNationalId &&
formatDefenderRoute(this.config.clientUrl, theCase.type, theCase.id)
Expand Down Expand Up @@ -2081,7 +2197,7 @@ export class InternalNotificationService extends BaseNotificationService {
}
}

if (user.role === UserRole.DEFENDER) {
if (isDefenceUser(user)) {
const prosecutorHtml = this.formatMessage(
notifications.caseAppealStatement.body,
{
Expand All @@ -2103,7 +2219,7 @@ export class InternalNotificationService extends BaseNotificationService {
)
}

if (user.role === UserRole.PROSECUTOR && theCase.defenderEmail) {
if (isProsecutionUser(user)) {
const url =
theCase.defenderNationalId &&
formatDefenderRoute(this.config.clientUrl, theCase.type, theCase.id)
Expand Down Expand Up @@ -2186,7 +2302,7 @@ export class InternalNotificationService extends BaseNotificationService {
}
})

if (user.role === UserRole.DEFENDER) {
if (isDefenceUser(user)) {
const prosecutorHtml = this.formatMessage(
notifications.caseAppealCaseFilesUpdated.body,
{
Expand Down Expand Up @@ -2563,6 +2679,8 @@ export class InternalNotificationService extends BaseNotificationService {
return this.sendIndictmentDeniedNotifications(theCase)
case NotificationType.INDICTMENT_RETURNED:
return this.sendIndictmentReturnedNotifications(theCase)
case NotificationType.CASE_FILES_UPDATED:
return this.sendCaseFilesUpdatedNotifications(theCase, user)
default:
throw new InternalServerErrorException(
`Invalid notification type ${type}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class NotificationService {
case NotificationType.ADVOCATE_ASSIGNED:
case NotificationType.APPEAL_JUDGES_ASSIGNED:
case NotificationType.APPEAL_CASE_FILES_UPDATED:
case NotificationType.CASE_FILES_UPDATED:
messages = [this.getNotificationMessage(type, user, theCase)]
break
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { uuid } from 'uuidv4'
import { EmailService } from '@island.is/email-service'

import {
InstitutionType,
NotificationType,
User,
UserRole,
Expand All @@ -19,13 +20,12 @@ interface Then {
}

type GivenWhenThen = (
role: UserRole,
user: User,
defenderNationalId?: string,
appealsCourtNumber?: string,
) => Promise<Then>

describe('InternalNotificationController - Send appeal statement notifications', () => {
const userId = uuid()
const caseId = uuid()
const prosecutorName = uuid()
const prosecutorEmail = uuid()
Expand Down Expand Up @@ -54,7 +54,7 @@ describe('InternalNotificationController - Send appeal statement notifications',
mockEmailService = emailService

givenWhenThen = async (
role: UserRole,
user: User,
defenderNationalId?: string,
appealCaseNumber?: string,
) => {
Expand All @@ -77,7 +77,7 @@ describe('InternalNotificationController - Send appeal statement notifications',
appealJudge1: { name: judgeName1, email: judgeEmail1 },
} as Case,
{
user: { id: userId, role } as User,
user,
type: NotificationType.APPEAL_STATEMENT,
},
)
Expand All @@ -99,7 +99,14 @@ describe('InternalNotificationController - Send appeal statement notifications',
let then: Then

beforeEach(async () => {
then = await givenWhenThen(UserRole.PROSECUTOR, uuid(), appealCaseNumber)
then = await givenWhenThen(
{
role: UserRole.PROSECUTOR,
institution: { type: InstitutionType.PROSECUTORS_OFFICE },
} as User,
uuid(),
appealCaseNumber,
)
})

it('should send notification to appeals court and defender', () => {
Expand All @@ -123,7 +130,10 @@ describe('InternalNotificationController - Send appeal statement notifications',

beforeEach(async () => {
then = await givenWhenThen(
UserRole.PROSECUTOR,
{
role: UserRole.PROSECUTOR,
institution: { type: InstitutionType.PROSECUTORS_OFFICE },
} as User,
undefined,
appealCaseNumber,
)
Expand All @@ -149,7 +159,11 @@ describe('InternalNotificationController - Send appeal statement notifications',
let then: Then

beforeEach(async () => {
then = await givenWhenThen(UserRole.DEFENDER, uuid(), appealCaseNumber)
then = await givenWhenThen(
{ role: UserRole.DEFENDER } as User,
uuid(),
appealCaseNumber,
)
})

it('should send notification to appeals court and prosecutor', () => {
Expand All @@ -172,7 +186,13 @@ describe('InternalNotificationController - Send appeal statement notifications',
let then: Then

beforeEach(async () => {
then = await givenWhenThen(UserRole.PROSECUTOR, uuid())
then = await givenWhenThen(
{
role: UserRole.PROSECUTOR,
institution: { type: InstitutionType.PROSECUTORS_OFFICE },
} as User,
uuid(),
)
})

it('should send notification to defender', () => {
Expand All @@ -191,7 +211,10 @@ describe('InternalNotificationController - Send appeal statement notifications',
let then: Then

beforeEach(async () => {
then = await givenWhenThen(UserRole.PROSECUTOR)
then = await givenWhenThen({
role: UserRole.PROSECUTOR,
institution: { type: InstitutionType.PROSECUTORS_OFFICE },
} as User)
})

it('should send notification to defender', () => {
Expand All @@ -210,7 +233,7 @@ describe('InternalNotificationController - Send appeal statement notifications',
let then: Then

beforeEach(async () => {
then = await givenWhenThen(UserRole.DEFENDER, uuid())
then = await givenWhenThen({ role: UserRole.DEFENDER } as User, uuid())
})

it('should send notification to prosecutor', () => {
Expand Down
Loading

0 comments on commit 487d14e

Please sign in to comment.