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): add slack lock for daily court hearing arrangements #17468

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,24 @@ export class InternalCaseController {
return this.internalCaseService.archive()
}

@Post('cases/postHearingArrangements/:date')
@ApiOkResponse({
type: Case,
isArray: true,
description:
'Fetch all cases that have court hearing arrangements for a given date',
})
async postHearingArrangements(@Param('date') date: Date): Promise<void> {
this.logger.debug(
`Post internal summary of all cases that have court hearing arrangement at ${date}`,
)

const cases = await this.internalCaseService.getCaseHearingArrangements(
date,
)
await this.eventService.postDailyHearingArrangementEvents(date, cases)
}
thorhildurt marked this conversation as resolved.
Show resolved Hide resolved

@Get('cases/indictments/defendant/:defendantNationalId')
@ApiOkResponse({
type: Case,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,29 @@ export class InternalCaseService {
return { caseArchived: true }
}

async getCaseHearingArrangements(date: Date): Promise<Case[]> {
const startOfDay = new Date(date.setHours(0, 0, 0, 0))
const endOfDay = new Date(date.setHours(23, 59, 59, 999))

return this.caseModel.findAll({
include: [
{
model: DateLog,
as: 'dateLogs',
where: {
date_type: ['ARRAIGNMENT_DATE', 'COURT_DATE'],
date: {
[Op.gte]: startOfDay,
[Op.lte]: endOfDay,
},
},
required: true,
},
],
order: [[{ model: DateLog, as: 'dateLogs' }, 'date', 'ASC']],
})
}

async deliverProsecutorToCourt(
theCase: Case,
user: TUser,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type { User as TUser } from '@island.is/judicial-system/types'
import {
CaseState,
CaseType,
DefendantEventType,
indictmentCases,
investigationCases,
restrictionCases,
Expand All @@ -39,7 +38,6 @@ import {

import { nowFactory } from '../../factories'
import { defenderRule, prisonSystemStaffRule } from '../../guards'
import { DefendantService } from '../defendant'
import { EventService } from '../event'
import { User } from '../user'
import { TransitionCaseDto } from './dto/transitionCase.dto'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,51 @@ export class EventService {
}
}

async postDailyHearingArrangementEvents(date: Date, cases: Case[]) {
const title = `:judge: Fyrirtökur ${formatDate(date)}`

const arrangementTexts = cases.map((theCase) => {
return `>${theCase.courtCaseNumber}: ${
formatDate(
DateLog.courtDate(theCase.dateLogs)?.date ??
DateLog.arraignmentDate(theCase.dateLogs)?.date,
'p',
) ?? date
}`
})

const arrangementSummary =
arrangementTexts.length > 0
? arrangementTexts.join('\n')
: '>Engar fyrirtökur á dagskrá'

try {
if (!this.config.url) {
return
}

await fetch(`${this.config.url}`, {
method: 'POST',
headers: { 'Content-type': 'application/json' },
body: JSON.stringify({
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*${title}*\n${arrangementSummary}`,
},
},
],
}),
})
} catch (error) {
this.logger.error(`Failed to post court hearing arrangement summary`, {
error,
})
}
}

async postErrorEvent(
message: string,
info: { [key: string]: string | boolean | Date | undefined },
Expand Down
46 changes: 39 additions & 7 deletions apps/judicial-system/scheduler/src/app/app.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fetch from 'node-fetch'

import { Inject, Injectable } from '@nestjs/common'
import { BadGatewayException, Inject, Injectable } from '@nestjs/common'

import { type Logger, LOGGER_PROVIDER } from '@island.is/logging'
import { type ConfigType } from '@island.is/nest/config'
Expand All @@ -24,12 +24,8 @@ export class AppService {
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}

async run() {
this.logger.info('Scheduler starting')

const startTime = now()

this.messageService
private addMessagesForIndictmentsWaitingForConfirmationToQueue() {
return this.messageService
.sendMessagesToQueue([
{
type: MessageType.NOTIFICATION_DISPATCH,
Expand All @@ -42,6 +38,10 @@ export class AppService {
// Tolerate failure, but log
this.logger.error('Failed to dispatch notifications', { reason }),
)
}

private async archiveCases() {
const startTime = now()

let done = false

Expand Down Expand Up @@ -76,6 +76,38 @@ export class AppService {
!done &&
minutesBetween(startTime, now()) < this.config.timeToLiveMinutes
)
}

private async postDailyHearingArrangementSummary() {
const today = now()
try {
const res = await fetch(
`${this.config.backendUrl}/api/internal/cases/postHearingArrangements/${today}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: `Bearer ${this.config.backendAccessToken}`,
},
},
)

if (!res.ok) {
throw new BadGatewayException(
'Unexpected error occurred while fetching cases',
)
}
} catch (error) {
throw new BadGatewayException(`Failed to fetch cases: ${error.message}`)
}
}

async run() {
this.logger.info('Scheduler starting')

await this.addMessagesForIndictmentsWaitingForConfirmationToQueue()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor: splitting previous scheduler tasks into functions

await this.archiveCases()
await this.postDailyHearingArrangementSummary()

this.logger.info('Scheduler done')
}
Expand Down
30 changes: 27 additions & 3 deletions apps/judicial-system/scheduler/src/app/test/run.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('AppService - Run', () => {
beforeEach(() => {
mockNow.mockClear()
mockFetch.mockClear()

mockNow.mockReturnValue(new Date('2020-01-01T00:01:00.000Z'))

givenWhenThen = async (): Promise<Then> => {
Expand Down Expand Up @@ -75,7 +76,7 @@ describe('AppService - Run', () => {
body: { type: 'INDICTMENTS_WAITING_FOR_CONFIRMATION' },
},
])
expect(fetch).toHaveBeenCalledTimes(3)
expect(fetch).toHaveBeenCalledTimes(4)
expect(fetch).toHaveBeenCalledWith(
`${appModuleConfig().backendUrl}/api/internal/cases/archive`,
{
Expand All @@ -86,6 +87,20 @@ describe('AppService - Run', () => {
},
},
)
expect(fetch).toHaveBeenCalledWith(
`${
appModuleConfig().backendUrl
}/api/internal/cases/postHearingArrangements/${new Date(
'2020-01-01T00:01:00.000Z',
)}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: `Bearer ${appModuleConfig().backendAccessToken}`,
},
},
)
})
})

Expand All @@ -102,8 +117,17 @@ describe('AppService - Run', () => {
await givenWhenThen()
})

it('should call the backend twice', () => {
expect(fetch).toHaveBeenCalledTimes(2)
it('should attempt archiving twice', () => {
expect(fetch).toHaveBeenNthCalledWith(
1,
`${appModuleConfig().backendUrl}/api/internal/cases/archive`,
expect.any(Object),
)
expect(fetch).toHaveBeenNthCalledWith(
2,
`${appModuleConfig().backendUrl}/api/internal/cases/archive`,
expect.any(Object),
)
})
})

Expand Down
Loading