diff --git a/apps/judicial-system/backend/src/app/modules/case/case.service.ts b/apps/judicial-system/backend/src/app/modules/case/case.service.ts index c89bbc8c0e0c..5e701d6fa2a7 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.service.ts @@ -45,6 +45,7 @@ import { eventTypes, isCompletedCase, isIndictmentCase, + isInvestigationCase, isRequestCase, isTrafficViolationCase, notificationTypes, @@ -846,6 +847,27 @@ export class CaseService { ) } + private addMessagesForSignedCourtRecordToQueue(theCase: Case, user: TUser) { + const messages = [] + + if ( + theCase.origin === CaseOrigin.LOKE && + isInvestigationCase(theCase.type) + ) { + messages.push({ + type: MessageType.DELIVERY_TO_POLICE_SIGNED_COURT_RECORD, + user, + caseId: theCase.id, + }) + } + + if (messages && messages.length > 0) { + return this.messageService.sendMessagesToQueue(messages) + } + + return + } + private addMessagesForSignedRulingToQueue( theCase: Case, user: TUser, @@ -1899,6 +1921,8 @@ export class CaseService { false, ) + await this.addMessagesForSignedCourtRecordToQueue(theCase, user) + return { documentSigned: true } } catch (error) { this.eventService.postErrorEvent( diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts index 60991a38b33f..c6a9f4c26e29 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.controller.ts @@ -571,6 +571,35 @@ export class InternalCaseController { ) } + @UseGuards( + CaseExistsGuard, + new CaseTypeGuard([...restrictionCases, ...investigationCases]), + CaseCompletedGuard, + ) + @Post( + `case/:caseId/${ + messageEndpoint[MessageType.DELIVERY_TO_POLICE_SIGNED_COURT_RECORD] + }`, + ) + @ApiOkResponse({ + type: DeliverResponse, + description: 'Delivers a signed ruling to police', + }) + deliverSignedCourtRecordToPolice( + @Param('caseId') caseId: string, + @CurrentCase() theCase: Case, + @Body() deliverDto: DeliverDto, + ): Promise { + this.logger.debug( + `Delivering the signed court record for case ${caseId} to police`, + ) + + return this.internalCaseService.deliverSignedCourtRecordToPolice( + theCase, + deliverDto.user, + ) + } + @UseGuards( CaseExistsGuard, new CaseTypeGuard([...restrictionCases, ...investigationCases]), diff --git a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts index a969cf40351b..38faed84b9c1 100644 --- a/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts +++ b/apps/judicial-system/backend/src/app/modules/case/internalCase.service.ts @@ -307,6 +307,13 @@ export class InternalCaseService { ) } + private getSignedCourtRecordPdf(theCase: Case) { + return this.awsS3Service.getGeneratedRequestCaseObject( + theCase.type, + `${theCase.id}/courtRecord.pdf`, + ) + } + private async deliverSignedRulingPdfToCourt( theCase: Case, user: TUser, @@ -1099,6 +1106,32 @@ export class InternalCaseService { return { delivered } } + async deliverSignedCourtRecordToPolice( + theCase: Case, + user: TUser, + ): Promise { + const delivered = await this.getSignedCourtRecordPdf(theCase) + .then((pdf) => + this.deliverCaseToPoliceWithFiles(theCase, user, [ + { + type: PoliceDocumentType.RVTB, + courtDocument: Base64.btoa(pdf.toString('binary')), + }, + ]), + ) + .catch((reason) => { + // Tolerate failure, but log error + this.logger.error( + `Failed to deliver signed court record for case ${theCase.id} to police`, + { reason }, + ) + + return false + }) + + return { delivered } + } + async deliverSignedRulingToPolice( theCase: Case, user: TUser, @@ -1115,7 +1148,7 @@ export class InternalCaseService { .catch((reason) => { // Tolerate failure, but log error this.logger.error( - `Failed to deliver sigend ruling for case ${theCase.id} to police`, + `Failed to deliver signed ruling for case ${theCase.id} to police`, { reason }, ) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedCourtRecordToPolice.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedCourtRecordToPolice.spec.ts new file mode 100644 index 000000000000..dabef1f9f88d --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedCourtRecordToPolice.spec.ts @@ -0,0 +1,124 @@ +import { Base64 } from 'js-base64' +import { uuid } from 'uuidv4' + +import { + CaseOrigin, + CaseState, + CaseType, + User, +} from '@island.is/judicial-system/types' + +import { createTestingCaseModule } from '../createTestingCaseModule' + +import { nowFactory } from '../../../../factories' +import { randomDate } from '../../../../test' +import { AwsS3Service } from '../../../aws-s3' +import { PoliceDocumentType, PoliceService } from '../../../police' +import { Case } from '../../models/case.model' +import { DeliverResponse } from '../../models/deliver.response' + +jest.mock('../../../../factories') + +interface Then { + result: DeliverResponse + error: Error +} + +type GivenWhenThen = (caseId: string, theCase: Case) => Promise + +describe('InternalCaseController - Deliver signed court record to police', () => { + const userId = uuid() + const user = { id: userId } as User + + let mockAwsS3Service: AwsS3Service + let mockPoliceService: PoliceService + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { awsS3Service, policeService, internalCaseController } = + await createTestingCaseModule() + + mockAwsS3Service = awsS3Service + mockPoliceService = policeService + + const mockGetGeneratedObject = + awsS3Service.getGeneratedRequestCaseObject as jest.Mock + mockGetGeneratedObject.mockRejectedValue(new Error('Some error')) + const mockUpdatePoliceCase = mockPoliceService.updatePoliceCase as jest.Mock + mockUpdatePoliceCase.mockRejectedValue(new Error('Some error')) + + givenWhenThen = async (caseId: string, theCase: Case) => { + const then = {} as Then + + await internalCaseController + .deliverSignedCourtRecordToPolice(caseId, theCase, { user }) + .then((result) => (then.result = result)) + .catch((error) => (then.error = error)) + + return then + } + }) + + describe('deliver case to police', () => { + const caseId = uuid() + const caseType = CaseType.PHONE_TAPPING + const caseState = CaseState.ACCEPTED + const policeCaseNumber = uuid() + const courtCaseNumber = uuid() + const defendantNationalId = '0123456789' + const validToDate = randomDate() + const caseConclusion = 'test conclusion' + const theCase = { + id: caseId, + origin: CaseOrigin.LOKE, + type: caseType, + state: caseState, + policeCaseNumbers: [policeCaseNumber], + courtCaseNumber, + defendants: [{ nationalId: defendantNationalId }], + validToDate, + conclusion: caseConclusion, + } as Case + const courtRecordPdf = 'test court record' + + let then: Then + + beforeEach(async () => { + const mockToday = nowFactory as jest.Mock + mockToday.mockReturnValueOnce(validToDate) + + const mockGetGeneratedObject = + mockAwsS3Service.getGeneratedRequestCaseObject as jest.Mock + mockGetGeneratedObject.mockResolvedValueOnce(courtRecordPdf) + const mockUpdatePoliceCase = + mockPoliceService.updatePoliceCase as jest.Mock + mockUpdatePoliceCase.mockResolvedValueOnce(true) + + then = await givenWhenThen(caseId, theCase) + }) + + it('should update the police case with a signed court record', async () => { + expect( + mockAwsS3Service.getGeneratedRequestCaseObject, + ).toHaveBeenCalledWith(caseType, `${caseId}/courtRecord.pdf`) + expect(mockPoliceService.updatePoliceCase).toHaveBeenCalledWith( + user, + caseId, + caseType, + caseState, + policeCaseNumber, + courtCaseNumber, + defendantNationalId, + validToDate, + caseConclusion, + [ + { + type: PoliceDocumentType.RVTB, + courtDocument: Base64.btoa(courtRecordPdf), + }, + ], + ) + expect(then.result.delivered).toEqual(true) + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedCourtRecordToPoliceGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedCourtRecordToPoliceGuards.spec.ts new file mode 100644 index 000000000000..38394cc82fd3 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/deliverSignedCourtRecordToPoliceGuards.spec.ts @@ -0,0 +1,66 @@ +import { CanActivate } from '@nestjs/common' + +import { + investigationCases, + restrictionCases, +} from '@island.is/judicial-system/types' + +import { CaseCompletedGuard } from '../../guards/caseCompleted.guard' +import { CaseExistsGuard } from '../../guards/caseExists.guard' +import { CaseTypeGuard } from '../../guards/caseType.guard' +import { InternalCaseController } from '../../internalCase.controller' + +describe('InternalCaseController - Deliver signed court record to police guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalCaseController.prototype.deliverSignedCourtRecordToPolice, + ) + }) + + it('should have three guards', () => { + expect(guards).toHaveLength(3) + }) + + describe('CaseExistsGuard', () => { + let guard: CanActivate + + beforeEach(() => { + guard = new guards[0]() + }) + + it('should have CaseExistsGuard as guard 1', () => { + expect(guard).toBeInstanceOf(CaseExistsGuard) + }) + }) + + describe('CaseTypeGuard', () => { + let guard: CanActivate + + beforeEach(() => { + guard = guards[1] + }) + + it('should have CaseTypeGuard as guard 2', () => { + expect(guard).toBeInstanceOf(CaseTypeGuard) + expect(guard).toEqual({ + allowedCaseTypes: [...restrictionCases, ...investigationCases], + }) + }) + }) + + describe('CaseCompletedGuard', () => { + let guard: CanActivate + + beforeEach(() => { + guard = new guards[2]() + }) + + it('should have CaseCompletedGuard as guard 3', () => { + expect(guard).toBeInstanceOf(CaseCompletedGuard) + }) + }) +}) diff --git a/apps/judicial-system/web/messages/Core/errors.ts b/apps/judicial-system/web/messages/Core/errors.ts index b2388cfda63e..02927f00f07f 100644 --- a/apps/judicial-system/web/messages/Core/errors.ts +++ b/apps/judicial-system/web/messages/Core/errors.ts @@ -43,8 +43,8 @@ export const errors = defineMessages({ 'Notaður sem villuskilaboð þegar ekki gengur að eyða kröfuhafa', }, createCase: { - id: 'judicial.system.core:errors.create_case', - defaultMessage: 'Upp kom villa við að stofnun máls', + id: 'judicial.system.core:errors.create_case_v1', + defaultMessage: 'Upp kom villa við stofnun máls', description: 'Notaður sem villuskilaboð þegar ekki gengur að stofna mál', }, updateCase: { diff --git a/libs/judicial-system/message/src/lib/message.service.ts b/libs/judicial-system/message/src/lib/message.service.ts index 671b1d099478..a457fc512d70 100644 --- a/libs/judicial-system/message/src/lib/message.service.ts +++ b/libs/judicial-system/message/src/lib/message.service.ts @@ -198,6 +198,9 @@ export class MessageService { ): Promise { const MAX_BATCH_SIZE = 10 + if (messages.length === 0) { + return // No messages to send + } if (messages.length <= MAX_BATCH_SIZE) { const queueUrl = await this.getQueueUrl() diff --git a/libs/judicial-system/message/src/lib/message.ts b/libs/judicial-system/message/src/lib/message.ts index c93854bab4e6..bb32cdc84e05 100644 --- a/libs/judicial-system/message/src/lib/message.ts +++ b/libs/judicial-system/message/src/lib/message.ts @@ -23,6 +23,7 @@ export enum MessageType { DELIVERY_TO_POLICE_INDICTMENT = 'DELIVERY_TO_POLICE_INDICTMENT', DELIVERY_TO_POLICE_CASE_FILES_RECORD = 'DELIVERY_TO_POLICE_CASE_FILES_RECORD', DELIVERY_TO_POLICE_SUBPOENA = 'DELIVERY_TO_POLICE_SUBPOENA', + DELIVERY_TO_POLICE_SIGNED_COURT_RECORD = 'DELIVERY_TO_POLICE_SIGNED_COURT_RECORD', DELIVERY_TO_POLICE_SIGNED_RULING = 'DELIVERY_TO_POLICE_SIGNED_RULING', DELIVERY_TO_POLICE_APPEAL = 'DELIVERY_TO_POLICE_APPEAL', NOTIFICATION = 'NOTIFICATION', @@ -62,6 +63,7 @@ export const messageEndpoint: { [key in MessageType]: string } = { DELIVERY_TO_POLICE_INDICTMENT: 'deliverIndictmentToPolice', DELIVERY_TO_POLICE_CASE_FILES_RECORD: 'deliverCaseFilesRecordToPolice', DELIVERY_TO_POLICE_SUBPOENA: 'deliverSubpoenaToPolice', + DELIVERY_TO_POLICE_SIGNED_COURT_RECORD: 'deliverSignedCourtRecordToPolice', DELIVERY_TO_POLICE_SIGNED_RULING: 'deliverSignedRulingToPolice', DELIVERY_TO_POLICE_APPEAL: 'deliverAppealToPolice', NOTIFICATION: 'notification',