From 7158dda8b647ed0cbdaf9ccd914aff0993effdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 12 Nov 2024 20:09:58 +0000 Subject: [PATCH 01/41] Throws proper exceptions from digital mailbox --- .../app/modules/defenders/defender.controller.ts | 16 ++++++++++++---- .../lawyers/src/lib/lawyers.service.ts | 4 ++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts index 24162164dc56..6865aac04ea5 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts @@ -1,9 +1,10 @@ import { CacheInterceptor } from '@nestjs/cache-manager' import { + BadGatewayException, Controller, Get, Inject, - InternalServerErrorException, + NotFoundException, Param, UseInterceptors, } from '@nestjs/common' @@ -29,7 +30,7 @@ export class DefenderController { type: [Defender], description: 'Returns a list of defenders', }) - @ApiResponse({ status: 500, description: 'Failed to retrieve defenders' }) + @ApiResponse({ status: 502, description: 'Failed to retrieve defenders' }) async getLawyers(): Promise { try { this.logger.debug('Retrieving litigators from lawyer registry') @@ -45,7 +46,7 @@ export class DefenderController { })) } catch (error) { this.logger.error('Failed to retrieve lawyers', error) - throw new InternalServerErrorException('Failed to retrieve lawyers') + throw new BadGatewayException('Failed to retrieve lawyers') } } @@ -54,6 +55,8 @@ export class DefenderController { type: Defender, description: 'Retrieves a defender by national id', }) + @ApiResponse({ status: 404, description: 'Defender not found' }) + @ApiResponse({ status: 502, description: 'Failed to retrieve defender' }) async getLawyer(@Param('nationalId') nationalId: string): Promise { try { this.logger.debug(`Retrieving lawyer by national id ${nationalId}`) @@ -66,7 +69,12 @@ export class DefenderController { } } catch (error) { this.logger.error('Failed to retrieve lawyer', error) - throw new InternalServerErrorException('Failed to retrieve lawyer') + + if (error instanceof NotFoundException) { + throw error + } + + throw new BadGatewayException('Failed to retrieve lawyer') } } } diff --git a/libs/judicial-system/lawyers/src/lib/lawyers.service.ts b/libs/judicial-system/lawyers/src/lib/lawyers.service.ts index 4c89ce69bb67..21265a84c43d 100644 --- a/libs/judicial-system/lawyers/src/lib/lawyers.service.ts +++ b/libs/judicial-system/lawyers/src/lib/lawyers.service.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common' +import { Inject, Injectable, NotFoundException } from '@nestjs/common' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' @@ -56,6 +56,6 @@ export class LawyersService { const reason = await response.text() this.logger.info('Failed to get lawyer from lawyer registry:', reason) - throw new Error(reason) + throw new NotFoundException(reason) } } From a096bdf1a70c68052f59251498c46843a848b5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 14 Nov 2024 11:45:55 +0000 Subject: [PATCH 02/41] Refactors code --- .../src/app/modules/defenders/defender.controller.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts index 6865aac04ea5..3e9b45a2b3ac 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/defenders/defender.controller.ts @@ -68,12 +68,11 @@ export class DefenderController { practice: lawyer.Practice, } } catch (error) { - this.logger.error('Failed to retrieve lawyer', error) - if (error instanceof NotFoundException) { throw error } + this.logger.error('Failed to retrieve lawyer', error) throw new BadGatewayException('Failed to retrieve lawyer') } } From fa15f767902b551634402c0c04a8d44726a95bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 14 Nov 2024 11:46:08 +0000 Subject: [PATCH 03/41] Refactors code --- .../lawyers/src/lib/lawyers.service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libs/judicial-system/lawyers/src/lib/lawyers.service.ts b/libs/judicial-system/lawyers/src/lib/lawyers.service.ts index 21265a84c43d..251de7a67721 100644 --- a/libs/judicial-system/lawyers/src/lib/lawyers.service.ts +++ b/libs/judicial-system/lawyers/src/lib/lawyers.service.ts @@ -31,7 +31,7 @@ export class LawyersService { ) if (response.ok) { - return await response.json() + return response.json() } const reason = await response.text() @@ -51,11 +51,16 @@ export class LawyersService { ) if (response.ok) { - return await response.json() + return response.json() } const reason = await response.text() this.logger.info('Failed to get lawyer from lawyer registry:', reason) - throw new NotFoundException(reason) + + if (response.status === 404) { + throw new NotFoundException('Lawyer not found') + } + + throw new Error(reason) } } From 8a01c179d0b7f8d9441c10d668e3a8f61bb42e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 14 Nov 2024 12:17:39 +0000 Subject: [PATCH 04/41] Refactors code --- .../xrd-api/src/app/app.controller.ts | 15 ++++++++++----- .../xrd-api/src/app/app.service.ts | 5 +++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/judicial-system/xrd-api/src/app/app.controller.ts b/apps/judicial-system/xrd-api/src/app/app.controller.ts index d5e3a6fd9de8..fca046a78ba6 100644 --- a/apps/judicial-system/xrd-api/src/app/app.controller.ts +++ b/apps/judicial-system/xrd-api/src/app/app.controller.ts @@ -1,8 +1,8 @@ import { + BadGatewayException, Body, Controller, Inject, - InternalServerErrorException, Param, ParseUUIDPipe, Patch, @@ -10,7 +10,7 @@ import { UseInterceptors, } from '@nestjs/common' import { Get } from '@nestjs/common' -import { ApiCreatedResponse, ApiResponse } from '@nestjs/swagger' +import { ApiCreatedResponse, ApiOkResponse, ApiResponse } from '@nestjs/swagger' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' @@ -45,7 +45,11 @@ export class AppController { } @Get('defenders') - @ApiResponse({ status: 500, description: 'Failed to retrieve defenders' }) + @ApiOkResponse({ + type: [Defender], + description: 'Returns a list of defenders', + }) + @ApiResponse({ status: 502, description: 'Failed to retrieve defenders' }) async getLawyers(): Promise { try { this.logger.debug('Retrieving litigators from lawyer registry') @@ -61,12 +65,13 @@ export class AppController { })) } catch (error) { this.logger.error('Failed to retrieve lawyers', error) - throw new InternalServerErrorException('Failed to retrieve lawyers') + throw new BadGatewayException('Failed to retrieve lawyers') } } @Patch('subpoena/:subpoenaId') - @ApiResponse({ status: 500, description: 'Failed to update subpoena' }) + @ApiResponse({ status: 400, description: 'Invalid input' }) + @ApiResponse({ status: 502, description: 'Failed to update subpoena' }) async updateSubpoena( @Param('subpoenaId', new ParseUUIDPipe()) subpoenaId: string, @Body() updateSubpoena: UpdateSubpoenaDto, diff --git a/apps/judicial-system/xrd-api/src/app/app.service.ts b/apps/judicial-system/xrd-api/src/app/app.service.ts index 57648e9254f7..98a72c8fade9 100644 --- a/apps/judicial-system/xrd-api/src/app/app.service.ts +++ b/apps/judicial-system/xrd-api/src/app/app.service.ts @@ -62,6 +62,7 @@ export class AppService { return { id: response?.id } } + // TODO: Reconsider exception type if (res.status < 500) { throw new BadRequestException(response?.detail) } @@ -73,6 +74,7 @@ export class AppService { throw reason } + // TODO: Reconsider exception type throw new BadGatewayException({ ...reason, message: 'Failed to create a new case', @@ -132,6 +134,7 @@ export class AppService { `Failed to retrieve lawyer with national id ${updateSubpoena.defenderNationalId}`, reason, ) + // TODO: Reconsider exception type throw new BadRequestException('Lawyer not found') } } @@ -189,6 +192,7 @@ export class AppService { } if (res.status < 500) { + // TODO: Reconsider exception type throw new BadRequestException(response?.detail) } @@ -198,6 +202,7 @@ export class AppService { throw reason } + // TODO: Reconsider exception type throw new BadGatewayException({ ...reason, message: 'Failed to update subpoena', From d18b483e820baba4954060bd0655c1d80504a7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 14 Nov 2024 15:38:40 +0000 Subject: [PATCH 05/41] Removes unused imports --- .../backend/src/app/modules/case/case.controller.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts index cbab6e505062..63b82483e3a9 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts @@ -39,14 +39,10 @@ import { } from '@island.is/judicial-system/formatters' import type { User } from '@island.is/judicial-system/types' import { - CaseAppealRulingDecision, - CaseDecision, CaseState, - CaseTransition, CaseType, indictmentCases, investigationCases, - isRestrictionCase, restrictionCases, UserRole, } from '@island.is/judicial-system/types' From 0e03bfe67112214019f81d5a52f87c188088ff5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 14 Nov 2024 15:40:27 +0000 Subject: [PATCH 06/41] Removes unused imports --- .../backend/src/app/modules/file/file.controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts index 9982d5c839c3..8e0a7940bc95 100644 --- a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts @@ -34,7 +34,6 @@ import { districtCourtAssistantRule, districtCourtJudgeRule, districtCourtRegistrarRule, - prisonSystemStaffRule, prosecutorRepresentativeRule, prosecutorRule, publicProsecutorStaffRule, From adc77fe7e9f509b52828a7ddf1666615fc4c8a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Thu, 14 Nov 2024 15:42:57 +0000 Subject: [PATCH 07/41] Removes unused import --- .../backend/src/app/modules/subpoena/subpoena.controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index da84d9c66c59..da139a356906 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -5,7 +5,6 @@ import { Get, Header, Inject, - InternalServerErrorException, Param, Query, Res, From d6ab433891f20bd2683b8bd7eadbd3607ce9a618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 15 Nov 2024 09:11:42 +0000 Subject: [PATCH 08/41] Removes redundant code --- .../sendAdvocateAssignedNotifications.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts index eff36227b407..8d4a7f652c2f 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/test/internalNotificationController/sendAdvocateAssignedNotifications.spec.ts @@ -19,7 +19,6 @@ import { createTestingNotificationModule } from '../createTestingNotificationMod import { Case } from '../../../case' import { CaseNotificationDto } from '../../dto/caseNotification.dto' import { DeliverResponse } from '../../models/deliver.response' -import { Notification } from '../../models/notification.model' import { notificationModuleConfig } from '../../notification.config' jest.mock('../../../../factories') @@ -41,7 +40,6 @@ describe('InternalNotificationController - Send defender assigned notifications' let mockEmailService: EmailService let mockConfig: ConfigType - let mockNotificationModel: typeof Notification let givenWhenThen: GivenWhenThen let notificationDTO: CaseNotificationDto From c7d1d988329fa1b45591bc5a5d990ccb34c626ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 10:51:24 +0000 Subject: [PATCH 09/41] Rewrites internal indictment case endpoint --- .../src/app/modules/case/case.controller.ts | 2 +- .../modules/case/internalCase.controller.ts | 24 +++++++++++-------- .../src/app/modules/defendant/index.ts | 1 + .../src/app/modules/cases/case.service.ts | 3 +-- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts index 63b82483e3a9..9728dc420508 100644 --- a/apps/judicial-system/backend/src/app/modules/case/case.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/case/case.controller.ts @@ -354,7 +354,7 @@ export class CaseController { ) @UseInterceptors(CompletedAppealAccessedInterceptor, CaseInterceptor) @Get('case/:caseId') - @ApiOkResponse({ type: Case, description: 'Gets an existing case' }) + @ApiOkResponse({ type: Case, description: 'Gets an existing case by id' }) getById(@Param('caseId') caseId: string, @CurrentCase() theCase: Case): Case { this.logger.debug(`Getting case ${caseId} by id`) 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 0a7dce8d89b7..ad1af2ac9c70 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 @@ -24,6 +24,7 @@ import { restrictionCases, } from '@island.is/judicial-system/types' +import { DefendantNationalIdExistsGuard } from '../defendant' import { EventService } from '../event' import { DeliverDto } from './dto/deliver.dto' import { DeliverCancellationNoticeDto } from './dto/deliverCancellationNotice.dto' @@ -93,22 +94,25 @@ export class InternalCaseController { ) } - @Post('case/indictment/:caseId') + @UseGuards( + CaseExistsGuard, + new CaseTypeGuard(indictmentCases), + DefendantNationalIdExistsGuard, + ) + @Post('case/indictment/:caseId/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, - description: 'Gets indictment case by id for digital mailbox', + description: 'Gets an existing indictment case by id', }) @UseInterceptors(CaseInterceptor) - getIndictmentCase( + getIndictmentCaseById( @Param('caseId') caseId: string, - @Body() internalCasesDto: InternalCasesDto, - ): Promise { - this.logger.debug(`Getting indictment case ${caseId}`) + @Param('defendantNationalId') _: string, + @CurrentCase() theCase: Case, + ): Case { + this.logger.debug(`Getting indictment case ${caseId} by id`) - return this.internalCaseService.getIndictmentCase( - caseId, - internalCasesDto.nationalId, - ) + return theCase } @UseGuards(CaseExistsGuard) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/index.ts b/apps/judicial-system/backend/src/app/modules/defendant/index.ts index 5fb8a9c29896..11d2f1fc5639 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/index.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/index.ts @@ -2,6 +2,7 @@ export { Defendant } from './models/defendant.model' export { DefendantService } from './defendant.service' export { DefendantExistsGuard } from './guards/defendantExists.guard' export { CurrentDefendant } from './guards/defendant.decorator' +export { DefendantNationalIdExistsGuard } from './guards/defendantNationalIdExists.guard' export { CivilClaimant } from './models/civilClaimant.model' export { CivilClaimantService } from './civilClaimant.service' diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index 80fe904f3ff2..65c9e4acc05c 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -211,14 +211,13 @@ export class CaseService { ): Promise { try { const res = await fetch( - `${this.config.backendUrl}/api/internal/case/indictment/${id}`, + `${this.config.backendUrl}/api/internal/case/indictment/${id}/defendant/${nationalId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', authorization: `Bearer ${this.config.secretToken}`, }, - body: JSON.stringify({ nationalId }), }, ) From b815229e89766aa3d67ce228a9d5d402e7755918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 11:06:52 +0000 Subject: [PATCH 10/41] Adds unit tests --- .../getIndictmentCaseById.spec.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts new file mode 100644 index 000000000000..dfc0a8b315de --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts @@ -0,0 +1,28 @@ +import { indictmentCases } from '@island.is/judicial-system/types' + +import { DefendantNationalIdExistsGuard } from '../../../defendant' +import { CaseExistsGuard } from '../../guards/caseExists.guard' +import { CaseTypeGuard } from '../../guards/caseType.guard' +import { InternalCaseController } from '../../internalCase.controller' + +describe('InternalCaseController - Get indictment case by id', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalCaseController.prototype.getIndictmentCaseById, + ) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(3) + expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard) + expect(guards[1]).toBeInstanceOf(CaseTypeGuard) + expect(guards[1]).toEqual({ + allowedCaseTypes: indictmentCases, + }) + expect(new guards[2]()).toBeInstanceOf(DefendantNationalIdExistsGuard) + }) +}) From c57154a77f51984ad74466c8e6a74a124fa30bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 13:50:58 +0000 Subject: [PATCH 11/41] Reorders query conditions --- .../backend/src/app/modules/case/filters/cases.filter.ts | 4 ++-- .../src/app/modules/case/filters/test/cases.filter.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts index e4bf7c5af74d..68f0cf92e30c 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/cases.filter.ts @@ -188,7 +188,6 @@ const getPrisonStaffUserCasesQueryFilter = (): WhereOptions => { return { [Op.and]: [ { is_archived: false }, - { state: CaseState.ACCEPTED }, { type: [ CaseType.CUSTODY, @@ -196,6 +195,7 @@ const getPrisonStaffUserCasesQueryFilter = (): WhereOptions => { CaseType.PAROLE_REVOCATION, ], }, + { state: CaseState.ACCEPTED }, { decision: [CaseDecision.ACCEPTING, CaseDecision.ACCEPTING_PARTIALLY] }, ], } @@ -206,8 +206,8 @@ const getPrisonAdminUserCasesQueryFilter = (): WhereOptions => { is_archived: false, [Op.or]: [ { - state: CaseState.ACCEPTED, type: [...restrictionCases, CaseType.PAROLE_REVOCATION], + state: CaseState.ACCEPTED, }, { type: indictmentCases, diff --git a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts index eb3cc40db316..3f073c3ea280 100644 --- a/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/filters/test/cases.filter.spec.ts @@ -348,7 +348,6 @@ describe('getCasesQueryFilter', () => { expect(res).toStrictEqual({ [Op.and]: [ { is_archived: false }, - { state: CaseState.ACCEPTED }, { type: [ CaseType.CUSTODY, @@ -356,6 +355,7 @@ describe('getCasesQueryFilter', () => { CaseType.PAROLE_REVOCATION, ], }, + { state: CaseState.ACCEPTED }, { decision: [CaseDecision.ACCEPTING, CaseDecision.ACCEPTING_PARTIALLY], }, @@ -383,8 +383,8 @@ describe('getCasesQueryFilter', () => { [Op.or]: [ { - state: CaseState.ACCEPTED, type: [...restrictionCases, CaseType.PAROLE_REVOCATION], + state: CaseState.ACCEPTED, }, { type: indictmentCases, From 7dd133fd732e8426f7bc8a1cd293812cd5afbdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 14:15:41 +0000 Subject: [PATCH 12/41] Rewrites defendant indictment cases endpoint --- .../app/modules/case/dto/internalCases.dto.ts | 10 ----- .../modules/case/internalCase.controller.ts | 23 +++++----- .../app/modules/case/internalCase.service.ts | 43 +------------------ .../getIndictmentCaseById.spec.ts | 2 +- .../src/app/modules/cases/case.service.ts | 2 +- 5 files changed, 15 insertions(+), 65 deletions(-) delete mode 100644 apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts diff --git a/apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts b/apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts deleted file mode 100644 index c4b9adffdcd7..000000000000 --- a/apps/judicial-system/backend/src/app/modules/case/dto/internalCases.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator' - -import { ApiProperty } from '@nestjs/swagger' - -export class InternalCasesDto { - @IsNotEmpty() - @IsString() - @ApiProperty({ type: String }) - readonly nationalId!: string -} 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 ad1af2ac9c70..547168d1305d 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 @@ -28,7 +28,6 @@ import { DefendantNationalIdExistsGuard } from '../defendant' import { EventService } from '../event' import { DeliverDto } from './dto/deliver.dto' import { DeliverCancellationNoticeDto } from './dto/deliverCancellationNotice.dto' -import { InternalCasesDto } from './dto/internalCases.dto' import { InternalCreateCaseDto } from './dto/internalCreateCase.dto' import { CurrentCase } from './guards/case.decorator' import { CaseCompletedGuard } from './guards/caseCompleted.guard' @@ -77,20 +76,20 @@ export class InternalCaseController { return this.internalCaseService.archive() } - @Post('cases/indictments') + @Post('cases/indictments/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, isArray: true, - description: 'Gets all indictment cases for digital mailbox', + description: 'Gets all indictment cases for a given defendant', }) @UseInterceptors(CasesInterceptor) - getIndictmentCases( - @Body() internalCasesDto: InternalCasesDto, + getAllDefendantIndictmentCases( + @Param('defendantNationalId') defendantNationalId: string, ): Promise { - this.logger.debug('Getting all indictment cases') + this.logger.debug('Getting all indictment cases for a given defendant') - return this.internalCaseService.getIndictmentCases( - internalCasesDto.nationalId, + return this.internalCaseService.getAllDefendantIndictmentCases( + defendantNationalId, ) } @@ -102,15 +101,17 @@ export class InternalCaseController { @Post('case/indictment/:caseId/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, - description: 'Gets an existing indictment case by id', + description: 'Gets an existing indictment case by id for a given defendant', }) @UseInterceptors(CaseInterceptor) - getIndictmentCaseById( + getDefendantIndictmentCaseById( @Param('caseId') caseId: string, @Param('defendantNationalId') _: string, @CurrentCase() theCase: Case, ): Case { - this.logger.debug(`Getting indictment case ${caseId} by id`) + this.logger.debug( + `Getting indictment case ${caseId} by id for a given defendant`, + ) return theCase } 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 c47fd37ca093..c76146af9ab7 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 @@ -1180,7 +1180,7 @@ export class InternalCaseService { // As this is only currently used by the digital mailbox API // we will only return indictment cases that have a court date - async getIndictmentCases(nationalId: string): Promise { + async getAllDefendantIndictmentCases(nationalId: string): Promise { return this.caseModel.findAll({ include: [ { @@ -1213,47 +1213,6 @@ export class InternalCaseService { }) } - async getIndictmentCase(caseId: string, nationalId: string): Promise { - const caseById = await this.caseModel.findOne({ - include: [ - { - model: Defendant, - as: 'defendants', - include: [ - { - model: Subpoena, - as: 'subpoenas', - order: [['created', 'DESC']], - }, - ], - }, - { model: Institution, as: 'court' }, - { model: Institution, as: 'prosecutorsOffice' }, - { model: User, as: 'judge' }, - { - model: User, - as: 'prosecutor', - include: [{ model: Institution, as: 'institution' }], - }, - { model: DateLog, as: 'dateLogs' }, - ], - attributes: ['courtCaseNumber', 'id'], - where: { - type: CaseType.INDICTMENT, - id: caseId, - // The national id could be without a hyphen or with a hyphen so we need to - // search for both - '$defendants.national_id$': normalizeAndFormatNationalId(nationalId), - }, - }) - - if (!caseById) { - throw new NotFoundException(`Case ${caseId} not found`) - } - - return caseById - } - countIndictmentsWaitingForConfirmation(prosecutorsOfficeId: string) { return this.caseModel.count({ include: [{ model: User, as: 'creatingProsecutor' }], diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts index dfc0a8b315de..c3927d45d501 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts @@ -12,7 +12,7 @@ describe('InternalCaseController - Get indictment case by id', () => { beforeEach(() => { guards = Reflect.getMetadata( '__guards__', - InternalCaseController.prototype.getIndictmentCaseById, + InternalCaseController.prototype.getDefendantIndictmentCaseById, ) }) diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index 65c9e4acc05c..c81f7f634669 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -182,7 +182,7 @@ export class CaseService { ): Promise { try { const res = await fetch( - `${this.config.backendUrl}/api/internal/cases/indictments`, + `${this.config.backendUrl}/api/internal/cases/indictments/defendant/${nationalId}`, { method: 'POST', headers: { From 34d2a25d0ef6691cf4299806cebd408c9b33ad55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 15:43:05 +0000 Subject: [PATCH 13/41] Adds civil claimant exists guard to update and delete civil claimant --- .../defendant/civilClaimant.controller.ts | 15 ++++--- .../defendant/civilClaimant.service.ts | 40 +++++++++++++------ .../modules/defendant/defendant.service.ts | 38 ++++++------------ .../deleteGuards.spec.ts | 7 +++- .../civilClaimantController/update.spec.ts | 2 +- .../updateGuards.spec.ts | 7 +++- 6 files changed, 62 insertions(+), 47 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts index 439904735d4e..a82ffb62912c 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts @@ -28,6 +28,8 @@ import { } from '../../guards' import { Case, CaseExistsGuard, CaseWriteGuard, CurrentCase } from '../case' import { UpdateCivilClaimantDto } from './dto/updateCivilClaimant.dto' +import { CurrentCivilClaimant } from './guards/civilClaimaint.decorator' +import { CivilClaimantExistsGuard } from './guards/civilClaimantExists.guard' import { CivilClaimant } from './models/civilClaimant.model' import { DeleteCivilClaimantResponse } from './models/deleteCivilClaimant.response' import { CivilClaimantService } from './civilClaimant.service' @@ -63,7 +65,7 @@ export class CivilClaimantController { return this.civilClaimantService.create(theCase) } - @UseGuards(CaseExistsGuard, CaseWriteGuard) + @UseGuards(CaseExistsGuard, CaseWriteGuard, CivilClaimantExistsGuard) @RolesRules( prosecutorRule, prosecutorRepresentativeRule, @@ -79,19 +81,20 @@ export class CivilClaimantController { async update( @Param('caseId') caseId: string, @Param('civilClaimantId') civilClaimantId: string, + @CurrentCivilClaimant() civilClaimant: CivilClaimant, @Body() updateCivilClaimantDto: UpdateCivilClaimantDto, ): Promise { this.logger.debug( - `Updating civil claimant ${civilClaimantId} in case ${caseId}`, + `Updating civil claimant ${civilClaimantId} of case ${caseId}`, ) return this.civilClaimantService.update( caseId, - civilClaimantId, + civilClaimant, updateCivilClaimantDto, ) } - @UseGuards(CaseExistsGuard, CaseWriteGuard) + @UseGuards(CaseExistsGuard, CaseWriteGuard, CivilClaimantExistsGuard) @RolesRules(prosecutorRule, prosecutorRepresentativeRule) @Delete(':civilClaimantId') @ApiOkResponse({ @@ -102,7 +105,9 @@ export class CivilClaimantController { @Param('caseId') caseId: string, @Param('civilClaimantId') civilClaimantId: string, ): Promise { - this.logger.debug(`Deleting civil claimant ${civilClaimantId}`) + this.logger.debug( + `Deleting civil claimant ${civilClaimantId} of case ${caseId}`, + ) const deleted = await this.civilClaimantService.delete( caseId, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts index 285092718ba5..c0d676283258 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.service.ts @@ -1,6 +1,10 @@ import { Op } from 'sequelize' -import { Inject, Injectable } from '@nestjs/common' +import { + Inject, + Injectable, + InternalServerErrorException, +} from '@nestjs/common' import { InjectModel } from '@nestjs/sequelize' import type { Logger } from '@island.is/logging' @@ -33,17 +37,18 @@ export class CivilClaimantService { } private async sendUpdateCivilClaimantMessages( - update: UpdateCivilClaimantDto, + oldCivilClaimant: CivilClaimant, updatedCivilClaimant: CivilClaimant, ): Promise { - if (update.isSpokespersonConfirmed === true) { + if ( + updatedCivilClaimant.isSpokespersonConfirmed && + !oldCivilClaimant.isSpokespersonConfirmed + ) { return this.messageService.sendMessagesToQueue([ { type: MessageType.CIVIL_CLAIMANT_NOTIFICATION, caseId: updatedCivilClaimant.caseId, - body: { - type: CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED, - }, + body: { type: CivilClaimantNotificationType.SPOKESPERSON_ASSIGNED }, elementId: updatedCivilClaimant.id, }, ]) @@ -52,13 +57,13 @@ export class CivilClaimantService { async update( caseId: string, - civilClaimantId: string, + civilClaimant: CivilClaimant, update: UpdateCivilClaimantDto, ): Promise { const [numberOfAffectedRows, civilClaimants] = await this.civilClaimantModel.update(update, { where: { - id: civilClaimantId, + id: civilClaimant.id, caseId: caseId, }, returning: true, @@ -66,15 +71,22 @@ export class CivilClaimantService { if (numberOfAffectedRows > 1) { this.logger.error( - `Unexpected number of rows (${numberOfAffectedRows}) affected when updating civil claimant ${civilClaimantId} of case ${caseId}`, + `Unexpected number of rows (${numberOfAffectedRows}) affected when updating civil claimant ${civilClaimant.id} of case ${caseId}`, ) } else if (numberOfAffectedRows < 1) { - throw new Error(`Could not update civil claimant ${civilClaimantId}`) + throw new InternalServerErrorException( + `Could not update civil claimant ${civilClaimant.id} of case ${caseId}`, + ) } - await this.sendUpdateCivilClaimantMessages(update, civilClaimants[0]) + const updatedCivilClaimant = civilClaimants[0] + + await this.sendUpdateCivilClaimantMessages( + civilClaimant, + updatedCivilClaimant, + ) - return civilClaimants[0] + return updatedCivilClaimant } async delete(caseId: string, civilClaimantId: string): Promise { @@ -91,7 +103,9 @@ export class CivilClaimantService { `Unexpected number of rows (${numberOfAffectedRows}) affected when deleting civil claimant ${civilClaimantId} of case ${caseId}`, ) } else if (numberOfAffectedRows < 1) { - throw new Error(`Could not delete civil claimant ${civilClaimantId}`) + throw new InternalServerErrorException( + `Could not delete civil claimant ${civilClaimantId}`, + ) } return true diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts index b266c32f2a76..f664ad2d8132 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.service.ts @@ -70,26 +70,6 @@ export class DefendantService { return message } - private getUpdatedDefendant( - numberOfAffectedRows: number, - defendants: Defendant[], - defendantId: string, - caseId: string, - ): Defendant { - if (numberOfAffectedRows > 1) { - // Tolerate failure, but log error - this.logger.error( - `Unexpected number of rows (${numberOfAffectedRows}) affected when updating defendant ${defendantId} of case ${caseId}`, - ) - } else if (numberOfAffectedRows < 1) { - throw new InternalServerErrorException( - `Could not update defendant ${defendantId} of case ${caseId}`, - ) - } - - return defendants[0] - } - private async sendRequestCaseUpdateDefendantMessages( theCase: Case, updatedDefendant: Defendant, @@ -216,12 +196,18 @@ export class DefendantService { { where: { id: defendantId, caseId }, returning: true, transaction }, ) - return this.getUpdatedDefendant( - numberOfAffectedRows, - defendants, - defendantId, - caseId, - ) + if (numberOfAffectedRows > 1) { + // Tolerate failure, but log error + this.logger.error( + `Unexpected number of rows (${numberOfAffectedRows}) affected when updating defendant ${defendantId} of case ${caseId}`, + ) + } else if (numberOfAffectedRows < 1) { + throw new InternalServerErrorException( + `Could not update defendant ${defendantId} of case ${caseId}`, + ) + } + + return defendants[0] } async updateRequestCaseDefendant( diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts index 20e8ef89e2f6..522d7fa661a7 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/deleteGuards.spec.ts @@ -2,10 +2,15 @@ import { CanActivate } from '@nestjs/common' import { CaseExistsGuard, CaseWriteGuard } from '../../../case' import { CivilClaimantController } from '../../civilClaimant.controller' +import { CivilClaimantExistsGuard } from '../../guards/civilClaimantExists.guard' describe('CivilClaimantController - Delete guards', () => { let guards: Array CanActivate> - const expectedGuards = [CaseExistsGuard, CaseWriteGuard] + const expectedGuards = [ + CaseExistsGuard, + CaseWriteGuard, + CivilClaimantExistsGuard, + ] beforeEach(() => { guards = Reflect.getMetadata( diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts index d8661906faa4..1c64917ceae9 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts @@ -48,7 +48,7 @@ describe('CivilClaimantController - Update', () => { const then = {} as Then await civilClaimantController - .update(caseId, civilClaimantId, updateData) + .update(caseId, civilClaimantId, civilClaimaint, updateData) .then((result) => (then.result = result)) .catch((error) => (then.error = error)) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts index d333af01f86f..cc106a2fce27 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/updateGuards.spec.ts @@ -2,10 +2,15 @@ import { CanActivate } from '@nestjs/common' import { CaseExistsGuard, CaseWriteGuard } from '../../../case' import { CivilClaimantController } from '../../civilClaimant.controller' +import { CivilClaimantExistsGuard } from '../../guards/civilClaimantExists.guard' describe('CivilClaimantController - Update guards', () => { let guards: Array CanActivate> - const expectedGuards = [CaseExistsGuard, CaseWriteGuard] + const expectedGuards = [ + CaseExistsGuard, + CaseWriteGuard, + CivilClaimantExistsGuard, + ] beforeEach(() => { guards = Reflect.getMetadata( From 1ca021b5640b35a22fd75dc4e975d92deb56a31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 16:51:21 +0000 Subject: [PATCH 14/41] Removes unused endpoint --- .../modules/subpoena/internalSubpoena.controller.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts index 968440871a92..7e0e3435d03f 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, - Get, Inject, Param, Patch, @@ -42,17 +41,6 @@ export class InternalSubpoenaController { @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - @UseGuards(SubpoenaExistsGuard) - @Get('subpoena/:subpoenaId') - async getSubpoena( - @Param('subpoenaId') subpoenaId: string, - @CurrentSubpoena() subpoena: Subpoena, - ): Promise { - this.logger.debug(`Getting subpoena by subpoena id ${subpoenaId}`) - - return subpoena - } - @UseGuards(SubpoenaExistsGuard) @Patch('subpoena/:subpoenaId') async updateSubpoena( From a6cce51cbea3f2350597d3692de9916b9846eefe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 17:10:22 +0000 Subject: [PATCH 15/41] Uses proper subpoena gueard for limited access subpoena controller --- .../app/modules/subpoena/limitedAccessSubpoena.controller.ts | 4 ++-- .../test/subpoenaController/getSubpoenaPdfGuards.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts index 36e2676df68b..40f5765dfa6e 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts @@ -32,7 +32,7 @@ import { } from '../case' import { CurrentDefendant, Defendant, DefendantExistsGuard } from '../defendant' import { CurrentSubpoena } from './guards/subpoena.decorator' -import { SubpoenaExistsOptionalGuard } from './guards/subpoenaExists.guard' +import { SubpoenaExistsGuard } from './guards/subpoenaExists.guard' import { Subpoena } from './models/subpoena.model' @Controller([ @@ -53,7 +53,7 @@ export class LimitedAccessSubpoenaController { @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - @UseGuards(SubpoenaExistsOptionalGuard) + @UseGuards(SubpoenaExistsGuard) @RolesRules(defenderGeneratedPdfRule) @Get() @Header('Content-Type', 'application/pdf') diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts index 085cc4d55b71..9ed23d7309b2 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/subpoenaController/getSubpoenaPdfGuards.spec.ts @@ -1,4 +1,4 @@ -import { SubpoenaExistsOptionalGuard } from '../../guards/subpoenaExists.guard' +import { SubpoenaExistsGuard } from '../../guards/subpoenaExists.guard' import { SubpoenaController } from '../../subpoena.controller' describe('SubpoenaController - Get subpoena pdf guards', () => { @@ -14,6 +14,6 @@ describe('SubpoenaController - Get subpoena pdf guards', () => { it('should have the right guard configuration', () => { expect(guards).toHaveLength(1) - expect(new guards[0]()).toBeInstanceOf(SubpoenaExistsOptionalGuard) + expect(new guards[0]()).toBeInstanceOf(SubpoenaExistsGuard) }) }) From 25b461ac89d3149c86090bc78748977551f9ff46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 17:42:06 +0000 Subject: [PATCH 16/41] Splits subpoena exists guard and reorders controller decorators --- .../defendant/civilClaimant.controller.ts | 2 +- .../modules/defendant/defendant.controller.ts | 2 +- .../modules/event-log/eventLog.controller.ts | 3 +- .../src/app/modules/file/file.controller.ts | 2 +- .../modules/file/internalFile.controller.ts | 2 +- .../file/limitedAccessFile.controller.ts | 2 +- .../indictmentCount.controller.ts | 2 +- .../internalNotification.controller.ts | 2 +- .../notification/notification.controller.ts | 2 +- .../police/internalPolice.controller.ts | 2 +- .../app/modules/police/police.controller.ts | 4 +-- .../guards/policeSubpoenaExists.guard.ts | 28 +++++++++++++++++++ .../subpoena/guards/subpoenaExists.guard.ts | 16 ++++------- .../subpoena/internalSubpoena.controller.ts | 3 +- .../limitedAccessSubpoena.controller.ts | 2 +- .../modules/subpoena/subpoena.controller.ts | 4 +-- .../internalSubpoenaControllerGuards.spec.ts | 17 +++++++++++ 17 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts index a82ffb62912c..bd19b67c84f7 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/civilClaimant.controller.ts @@ -34,9 +34,9 @@ import { CivilClaimant } from './models/civilClaimant.model' import { DeleteCivilClaimantResponse } from './models/deleteCivilClaimant.response' import { CivilClaimantService } from './civilClaimant.service' -@UseGuards(JwtAuthGuard, RolesGuard) @Controller('api/case/:caseId/civilClaimant') @ApiTags('civilClaimants') +@UseGuards(JwtAuthGuard, RolesGuard) export class CivilClaimantController { constructor( private readonly civilClaimantService: CivilClaimantService, diff --git a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts index 83edd4cd8b1e..de64f7538c41 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/defendant.controller.ts @@ -38,9 +38,9 @@ import { Defendant } from './models/defendant.model' import { DeleteDefendantResponse } from './models/delete.response' import { DefendantService } from './defendant.service' -@UseGuards(JwtAuthGuard, RolesGuard) @Controller('api/case/:caseId/defendant') @ApiTags('defendants') +@UseGuards(JwtAuthGuard, RolesGuard) export class DefendantController { constructor( private readonly defendantService: DefendantService, diff --git a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts index 30af6da23667..853fe1fe0ef3 100644 --- a/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/event-log/eventLog.controller.ts @@ -1,5 +1,5 @@ import { Body, Controller, Post, UseGuards } from '@nestjs/common' -import { ApiCreatedResponse } from '@nestjs/swagger' +import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger' import { TokenGuard } from '@island.is/judicial-system/auth' @@ -7,6 +7,7 @@ import { CreateEventLogDto } from './dto/createEventLog.dto' import { EventLogService } from './eventLog.service' @Controller('api/eventLog') +@ApiTags('eventLogs') export class EventLogController { constructor(private readonly eventLogService: EventLogService) {} diff --git a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts index 8e0a7940bc95..a80e9b976ad6 100644 --- a/apps/judicial-system/backend/src/app/modules/file/file.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/file.controller.ts @@ -62,9 +62,9 @@ import { SignedUrl } from './models/signedUrl.model' import { UploadFileToCourtResponse } from './models/uploadFileToCourt.response' import { FileService } from './file.service' -@UseGuards(JwtAuthGuard) @Controller('api/case/:caseId') @ApiTags('files') +@UseGuards(JwtAuthGuard) export class FileController { constructor( private readonly fileService: FileService, diff --git a/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts b/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts index 6c6f5ddba847..2241f9db7bf7 100644 --- a/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/internalFile.controller.ts @@ -25,9 +25,9 @@ import { DeliverResponse } from './models/deliver.response' import { CaseFile } from './models/file.model' import { FileService } from './file.service' -@UseGuards(TokenGuard) @Controller('api/internal/case/:caseId') @ApiTags('internal files') +@UseGuards(TokenGuard) export class InternalFileController { constructor( private readonly fileService: FileService, diff --git a/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts b/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts index 3c1605da9c12..1d1166dfca17 100644 --- a/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/file/limitedAccessFile.controller.ts @@ -49,9 +49,9 @@ import { PresignedPost } from './models/presignedPost.model' import { SignedUrl } from './models/signedUrl.model' import { FileService } from './file.service' -@UseGuards(JwtAuthGuard, RolesGuard, LimitedAccessCaseExistsGuard) @Controller('api/case/:caseId/limitedAccess') @ApiTags('files') +@UseGuards(JwtAuthGuard, RolesGuard, LimitedAccessCaseExistsGuard) export class LimitedAccessFileController { constructor( private readonly fileService: FileService, diff --git a/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts b/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts index 1c32f7a87303..3303ff096517 100644 --- a/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/indictment-count/indictmentCount.controller.ts @@ -27,9 +27,9 @@ import { DeleteIndictmentCountResponse } from './models/delete.response' import { IndictmentCount } from './models/indictmentCount.model' import { IndictmentCountService } from './indictmentCount.service' -@UseGuards(JwtAuthGuard, RolesGuard) @Controller('api/case/:caseId/indictmentCount') @ApiTags('indictment-counts') +@UseGuards(JwtAuthGuard, RolesGuard) export class IndictmentCountController { constructor( private readonly indictmentCountService: IndictmentCountService, diff --git a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts index c0e2ca1a0003..10c419c39761 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/internalNotification.controller.ts @@ -41,9 +41,9 @@ import { InstitutionNotificationService } from './institutionNotification.servic import { NotificationDispatchService } from './notificationDispatch.service' import { SubpoenaNotificationService } from './subpoenaNotification.service' -@UseGuards(TokenGuard) @Controller('api/internal') @ApiTags('internal notifications') +@UseGuards(TokenGuard) export class InternalNotificationController { constructor( private readonly caseNotificationService: CaseNotificationService, diff --git a/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts b/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts index 4b8ce92cb0c2..60f979a3ff9f 100644 --- a/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/notification/notification.controller.ts @@ -34,9 +34,9 @@ import { import { SendNotificationResponse } from './models/sendNotification.response' import { NotificationService } from './notification.service' -@UseGuards(JwtAuthGuard, RolesGuard, CaseExistsGuard) @Controller('api/case/:caseId/notification') @ApiTags('notifications') +@UseGuards(JwtAuthGuard, RolesGuard, CaseExistsGuard) export class NotificationController { constructor( private readonly notificationService: NotificationService, diff --git a/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts b/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts index 4680100a5d85..0b869caed792 100644 --- a/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts @@ -9,9 +9,9 @@ import { Case, CaseExistsGuard, CurrentCase } from '../case' import { Subpoena } from '../subpoena' import { PoliceService } from './police.service' -@UseGuards(TokenGuard, CaseExistsGuard) @Controller('api/internal/case/:caseId') @ApiTags('internal police') +@UseGuards(TokenGuard, CaseExistsGuard) export class InternalPoliceController { constructor( private readonly policeService: PoliceService, diff --git a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts index 40c5f1829985..3c0e355919f6 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts @@ -43,6 +43,8 @@ import { PoliceCaseInfo } from './models/policeCaseInfo.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' import { PoliceService } from './police.service' +@Controller('api/case/:caseId') +@ApiTags('police files') @UseGuards( JwtAuthGuard, RolesGuard, @@ -50,8 +52,6 @@ import { PoliceService } from './police.service' CaseReadGuard, CaseNotCompletedGuard, ) -@Controller('api/case/:caseId') -@ApiTags('police files') export class PoliceController { constructor( private readonly policeService: PoliceService, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts new file mode 100644 index 000000000000..d7021c9528c3 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts @@ -0,0 +1,28 @@ +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common' + +import { SubpoenaService } from '../subpoena.service' + +@Injectable() +export class PoliceSubpoenaExistsGuard implements CanActivate { + constructor(private readonly subpoenaService: SubpoenaService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + + const subpoenaId = request.params.subpoenaId + + if (!subpoenaId) { + throw new BadRequestException('Missing subpoena id') + } + + // subpoenaId is the external police document id + request.subpoena = await this.subpoenaService.findBySubpoenaId(subpoenaId) + + return true + } +} diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts index f5dc6c406cb1..13cc5aa37a0c 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts @@ -15,22 +15,18 @@ export class SubpoenaExistsGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest() - const subpoenaId = request.params.subpoenaId - - if (!subpoenaId) { - throw new BadRequestException('Missing subpoena id') - } - const defendant: Defendant = request.defendant if (!defendant) { - // subpoenaId is the external police document id - request.subpoena = await this.subpoenaService.findBySubpoenaId(subpoenaId) + throw new BadRequestException('Missing defendant') + } - return true + const subpoenaId = request.params.subpoenaId + + if (!subpoenaId) { + throw new BadRequestException('Missing subpoena id') } - // subpoenaId is the internal subpoena id const subpoena = defendant.subpoenas?.find( (subpoena) => subpoena.id === subpoenaId, ) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts index 7e0e3435d03f..4daa6f54028e 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts @@ -26,6 +26,7 @@ import { DefendantExistsGuard } from '../defendant/guards/defendantExists.guard' import { Defendant } from '../defendant/models/defendant.model' import { DeliverDto } from './dto/deliver.dto' import { UpdateSubpoenaDto } from './dto/updateSubpoena.dto' +import { PoliceSubpoenaExistsGuard } from './guards/policeSubpoenaExists.guard' import { CurrentSubpoena } from './guards/subpoena.decorator' import { SubpoenaExistsGuard } from './guards/subpoenaExists.guard' import { DeliverResponse } from './models/deliver.response' @@ -41,7 +42,7 @@ export class InternalSubpoenaController { @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} - @UseGuards(SubpoenaExistsGuard) + @UseGuards(PoliceSubpoenaExistsGuard) @Patch('subpoena/:subpoenaId') async updateSubpoena( @Param('subpoenaId') subpoenaId: string, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts index 40f5765dfa6e..54bfcdacd19a 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/limitedAccessSubpoena.controller.ts @@ -38,6 +38,7 @@ import { Subpoena } from './models/subpoena.model' @Controller([ 'api/case/:caseId/limitedAccess/defendant/:defendantId/subpoena/:subpoenaId', ]) +@ApiTags('limited access subpoenas') @UseGuards( JwtAuthGuard, CaseExistsGuard, @@ -46,7 +47,6 @@ import { Subpoena } from './models/subpoena.model' CaseReadGuard, DefendantExistsGuard, ) -@ApiTags('limited access subpoenas') export class LimitedAccessSubpoenaController { constructor( private readonly pdfService: PdfService, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index da139a356906..5c6dde866d50 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -47,6 +47,8 @@ import { } from './guards/subpoenaExists.guard' import { Subpoena } from './models/subpoena.model' +@Controller('api/case/:caseId/defendant/:defendantId/subpoena') +@ApiTags('subpoenas') @UseGuards( JwtAuthGuard, RolesGuard, @@ -55,8 +57,6 @@ import { Subpoena } from './models/subpoena.model' CaseReadGuard, DefendantExistsGuard, ) -@Controller('api/case/:caseId/defendant/:defendantId/subpoena') -@ApiTags('subpoenas') export class SubpoenaController { constructor( private readonly pdfService: PdfService, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts new file mode 100644 index 000000000000..09ed4d41ebe3 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/internalSubpoenaControllerGuards.spec.ts @@ -0,0 +1,17 @@ +import { TokenGuard } from '@island.is/judicial-system/auth' + +import { InternalSubpoenaController } from '../../internalSubpoena.controller' + +describe('InternalSubpoenaController - guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata('__guards__', InternalSubpoenaController) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(1) + expect(new guards[0]()).toBeInstanceOf(TokenGuard) + }) +}) From 6d17832e990da47d1e77dd8dfc691916d9c059e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Fri, 22 Nov 2024 18:13:37 +0000 Subject: [PATCH 17/41] Adds unit test --- .../updateSubpoeanaGuards.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts new file mode 100644 index 000000000000..1cad7bd28cdb --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/test/internalSubpoenaController/updateSubpoeanaGuards.spec.ts @@ -0,0 +1,19 @@ +import { PoliceSubpoenaExistsGuard } from '../../guards/policeSubpoenaExists.guard' +import { InternalSubpoenaController } from '../../internalSubpoena.controller' + +describe('InternalSubpoenaController - Update subpoena guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata( + '__guards__', + InternalSubpoenaController.prototype.updateSubpoena, + ) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(1) + expect(new guards[0]()).toBeInstanceOf(PoliceSubpoenaExistsGuard) + }) +}) From 106114d557910202d60394fab9bf5fa6222929d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Sun, 24 Nov 2024 18:14:56 +0000 Subject: [PATCH 18/41] Renames variables for clarity --- .../src/app/modules/cases/case.service.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index c81f7f634669..f964121ec319 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -43,15 +43,15 @@ export class CaseService { } async getCase( - id: string, + caseId: string, nationalId: string, lang?: string, ): Promise { return this.auditTrailService.audit( 'digital-mailbox-api', AuditedAction.GET_INDICTMENT, - this.getCaseInfo(id, nationalId, lang), - () => id, + this.getCaseInfo(caseId, nationalId, lang), + () => caseId, ) } @@ -92,11 +92,11 @@ export class CaseService { } private async getCaseInfo( - id: string, + caseId: string, nationalId: string, lang?: string, ): Promise { - const response = await this.fetchCase(id, nationalId) + const response = await this.fetchCase(caseId, nationalId) const defendantInfo = response.defendants.find( (defendant) => defendant.nationalId && @@ -111,7 +111,10 @@ export class CaseService { defendantInfo.subpoenas?.[0]?.subpoenaId && !defendantInfo.subpoenas[0].serviceStatus ) { - await this.fetchServiceStatus(id, defendantInfo.subpoenas[0].subpoenaId) + await this.fetchServiceStatus( + caseId, + defendantInfo.subpoenas[0].subpoenaId, + ) } return CaseResponse.fromInternalCaseResponse(response, lang) @@ -168,6 +171,7 @@ export class CaseService { requestedDefenderName: chosenLawyer?.Name, } await this.patchDefenseInfo(defendantNationalId, caseId, defenderChoice) + const updatedCase = await this.fetchCase(caseId, defendantNationalId) return SubpoenaResponse.fromInternalCaseResponse( @@ -206,12 +210,12 @@ export class CaseService { } private async fetchCase( - id: string, + caseId: string, nationalId: string, ): Promise { try { const res = await fetch( - `${this.config.backendUrl}/api/internal/case/indictment/${id}/defendant/${nationalId}`, + `${this.config.backendUrl}/api/internal/case/indictment/${caseId}/defendant/${nationalId}`, { method: 'POST', headers: { @@ -223,7 +227,7 @@ export class CaseService { if (!res.ok) { if (res.status === 404) { - throw new NotFoundException(`Case ${id} not found`) + throw new NotFoundException(`Case ${caseId} not found`) } const reason = await res.text() From 24783ded72c3e0b51320ec44ef6eac983d3e232f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Sun, 24 Nov 2024 18:22:36 +0000 Subject: [PATCH 19/41] Uses correct http method when getting cases --- .../backend/src/app/modules/case/internalCase.controller.ts | 5 +++-- .../src/app/modules/cases/case.service.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) 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 547168d1305d..c20e279824ea 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 @@ -2,6 +2,7 @@ import { BadRequestException, Body, Controller, + Get, Inject, Param, Post, @@ -76,7 +77,7 @@ export class InternalCaseController { return this.internalCaseService.archive() } - @Post('cases/indictments/defendant/:defendantNationalId') + @Get('cases/indictments/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, isArray: true, @@ -98,7 +99,7 @@ export class InternalCaseController { new CaseTypeGuard(indictmentCases), DefendantNationalIdExistsGuard, ) - @Post('case/indictment/:caseId/defendant/:defendantNationalId') + @Get('case/indictment/:caseId/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, description: 'Gets an existing indictment case by id for a given defendant', diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index f964121ec319..9c0ed86d1b05 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -188,7 +188,7 @@ export class CaseService { const res = await fetch( `${this.config.backendUrl}/api/internal/cases/indictments/defendant/${nationalId}`, { - method: 'POST', + method: 'GET', headers: { 'Content-Type': 'application/json', authorization: `Bearer ${this.config.secretToken}`, @@ -217,7 +217,7 @@ export class CaseService { const res = await fetch( `${this.config.backendUrl}/api/internal/case/indictment/${caseId}/defendant/${nationalId}`, { - method: 'POST', + method: 'GET', headers: { 'Content-Type': 'application/json', authorization: `Bearer ${this.config.secretToken}`, From a7908646b30494bf39072d07c2b9241a6c7a7f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Mon, 25 Nov 2024 22:41:12 +0000 Subject: [PATCH 20/41] Moves subpoena status updates to subpoena module --- .../judicial-system/api/src/app/app.module.ts | 2 + .../app/modules/backend/backend.service.ts | 19 ++- .../api/src/app/modules/defendant/index.ts | 1 - .../defendant/models/defendant.model.ts | 2 +- .../api/src/app/modules/index.ts | 1 + .../api/src/app/modules/police/index.ts | 1 - .../police/models/subpoenaStatus.model.ts | 21 --- .../src/app/modules/police/police.resolver.ts | 22 --- .../dto/subpoena.input.ts} | 6 +- .../api/src/app/modules/subpoena/index.ts | 1 + .../models/subpoena.model.ts | 0 .../app/modules/subpoena/subpoena.module.ts | 8 + .../app/modules/subpoena/subpoena.resolver.ts | 53 ++++++ .../indictmentCaseExistsForDefendant.guard.ts | 37 +++++ .../modules/case/internalCase.controller.ts | 17 +- .../app/modules/case/internalCase.service.ts | 71 +++++++- .../police/internalPolice.controller.ts | 34 ---- .../police/models/subpoenaStatus.model.ts | 20 +++ .../app/modules/police/police.controller.ts | 31 +--- .../src/app/modules/police/police.module.ts | 3 +- .../src/app/modules/police/police.service.ts | 58 +++---- .../subpoena/dto/updateSubpoena.dto.ts | 4 +- .../subpoena/internalSubpoena.controller.ts | 6 +- .../modules/subpoena/subpoena.controller.ts | 36 ++++- .../app/modules/subpoena/subpoena.service.ts | 151 +++++++++++------- .../src/app/modules/cases/case.service.ts | 66 -------- .../src/components/FormProvider/case.graphql | 1 + .../ServiceAnnouncement.tsx | 31 +--- .../Court/Indictments/Overview/Overview.tsx | 17 +- .../Indictments/Overview/Overview.tsx | 17 +- .../useSubpoena/getSubpoenaStatus.graphql | 9 -- .../web/src/utils/hooks/useSubpoena/index.ts | 29 ++-- .../utils/hooks/useSubpoena/subpoena.graphql | 14 ++ .../audit-trail/src/lib/auditTrail.service.ts | 1 - 34 files changed, 430 insertions(+), 360 deletions(-) delete mode 100644 apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts rename apps/judicial-system/api/src/app/modules/{police/dto/subpoenaStatus.input.ts => subpoena/dto/subpoena.input.ts} (70%) create mode 100644 apps/judicial-system/api/src/app/modules/subpoena/index.ts rename apps/judicial-system/api/src/app/modules/{defendant => subpoena}/models/subpoena.model.ts (100%) create mode 100644 apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts create mode 100644 apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts delete mode 100644 apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts create mode 100644 apps/judicial-system/backend/src/app/modules/police/models/subpoenaStatus.model.ts delete mode 100644 apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql create mode 100644 apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql diff --git a/apps/judicial-system/api/src/app/app.module.ts b/apps/judicial-system/api/src/app/app.module.ts index d73082ad8cd3..b9d12a0626b1 100644 --- a/apps/judicial-system/api/src/app/app.module.ts +++ b/apps/judicial-system/api/src/app/app.module.ts @@ -34,6 +34,7 @@ import { IndictmentCountModule, InstitutionModule, PoliceModule, + SubpoenaModule, UserModule, } from './modules' @@ -68,6 +69,7 @@ const autoSchemaFile = production CaseListModule, DefendantModule, DefenderModule, + SubpoenaModule, IndictmentCountModule, FileModule, InstitutionModule, diff --git a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts index 099c3bf98b10..28d8c99b9ac9 100644 --- a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts @@ -41,9 +41,9 @@ import { Institution } from '../institution' import { PoliceCaseFile, PoliceCaseInfo, - SubpoenaStatus, UploadPoliceCaseFileResponse, } from '../police' +import { Subpoena } from '../subpoena' import { backendModuleConfig } from './backend.config' @Injectable() @@ -307,13 +307,6 @@ export class BackendService extends DataSource<{ req: Request }> { return this.get(`case/${caseId}/policeFiles`) } - getSubpoenaStatus( - caseId: string, - subpoenaId: string, - ): Promise { - return this.get(`case/${caseId}/subpoenaStatus/${subpoenaId}`) - } - getPoliceCaseInfo(caseId: string): Promise { return this.get(`case/${caseId}/policeCaseInfo`) } @@ -350,6 +343,16 @@ export class BackendService extends DataSource<{ req: Request }> { return this.delete(`case/${caseId}/defendant/${defendantId}`) } + getSubpoena( + caseId: string, + defendantId: string, + subpoenaId: string, + ): Promise { + return this.get( + `case/${caseId}/defendnat/${defendantId}/subpoena/${subpoenaId}`, + ) + } + createCivilClaimant( caseId: string, createCivilClaimant: unknown, diff --git a/apps/judicial-system/api/src/app/modules/defendant/index.ts b/apps/judicial-system/api/src/app/modules/defendant/index.ts index 040ddad841e3..0811956a0ca8 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/index.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/index.ts @@ -2,4 +2,3 @@ export { Defendant } from './models/defendant.model' export { DeleteDefendantResponse } from './models/delete.response' export { CivilClaimant } from './models/civilClaimant.model' export { DeleteCivilClaimantResponse } from './models/deleteCivilClaimant.response' -export { Subpoena } from './models/subpoena.model' diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts index 4f641c32efe8..7761eacb9d93 100644 --- a/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts +++ b/apps/judicial-system/api/src/app/modules/defendant/models/defendant.model.ts @@ -8,7 +8,7 @@ import { SubpoenaType, } from '@island.is/judicial-system/types' -import { Subpoena } from './subpoena.model' +import { Subpoena } from '../../subpoena' registerEnumType(Gender, { name: 'Gender' }) registerEnumType(DefendantPlea, { name: 'DefendantPlea' }) diff --git a/apps/judicial-system/api/src/app/modules/index.ts b/apps/judicial-system/api/src/app/modules/index.ts index de4bc6ecb048..6615aac49117 100644 --- a/apps/judicial-system/api/src/app/modules/index.ts +++ b/apps/judicial-system/api/src/app/modules/index.ts @@ -17,3 +17,4 @@ export { EventLogModule } from './event-log/eventLog.module' export { backendModuleConfig } from './backend/backend.config' export { BackendService } from './backend/backend.service' export { BackendModule } from './backend/backend.module' +export { SubpoenaModule } from './subpoena/subpoena.module' diff --git a/apps/judicial-system/api/src/app/modules/police/index.ts b/apps/judicial-system/api/src/app/modules/police/index.ts index a1fe72f38c8e..c2c2457a57b5 100644 --- a/apps/judicial-system/api/src/app/modules/police/index.ts +++ b/apps/judicial-system/api/src/app/modules/police/index.ts @@ -1,4 +1,3 @@ export { PoliceCaseInfo } from './models/policeCaseInfo.model' -export { SubpoenaStatus } from './models/subpoenaStatus.model' export { PoliceCaseFile } from './models/policeCaseFile.model' export { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' diff --git a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts b/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts deleted file mode 100644 index 6f7a85461e10..000000000000 --- a/apps/judicial-system/api/src/app/modules/police/models/subpoenaStatus.model.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Field, ObjectType } from '@nestjs/graphql' - -import { ServiceStatus } from '@island.is/judicial-system/types' - -@ObjectType() -export class SubpoenaStatus { - @Field(() => ServiceStatus, { nullable: true }) - readonly serviceStatus?: ServiceStatus - - @Field(() => String, { nullable: true }) - readonly servedBy?: string - - @Field(() => String, { nullable: true }) - readonly comment?: string - - @Field(() => String, { nullable: true }) - readonly serviceDate?: string - - @Field(() => String, { nullable: true }) - readonly defenderNationalId?: string -} diff --git a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts index 1eef19336e76..8d4fbf9cd90a 100644 --- a/apps/judicial-system/api/src/app/modules/police/police.resolver.ts +++ b/apps/judicial-system/api/src/app/modules/police/police.resolver.ts @@ -17,11 +17,9 @@ import type { User } from '@island.is/judicial-system/types' import { BackendService } from '../backend' import { PoliceCaseFilesQueryInput } from './dto/policeCaseFiles.input' import { PoliceCaseInfoQueryInput } from './dto/policeCaseInfo.input' -import { SubpoenaStatusQueryInput } from './dto/subpoenaStatus.input' import { UploadPoliceCaseFileInput } from './dto/uploadPoliceCaseFile.input' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' -import { SubpoenaStatus } from './models/subpoenaStatus.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' @UseGuards(JwtGraphQlAuthGuard) @@ -51,26 +49,6 @@ export class PoliceResolver { ) } - @Query(() => SubpoenaStatus, { nullable: true }) - subpoenaStatus( - @Args('input', { type: () => SubpoenaStatusQueryInput }) - input: SubpoenaStatusQueryInput, - @CurrentGraphQlUser() user: User, - @Context('dataSources') - { backendService }: { backendService: BackendService }, - ): Promise { - this.logger.debug( - `Getting subpoena status for subpoena ${input.subpoenaId} of case ${input.caseId}`, - ) - - return this.auditTrailService.audit( - user.id, - AuditedAction.GET_SUBPOENA_STATUS, - backendService.getSubpoenaStatus(input.caseId, input.subpoenaId), - input.caseId, - ) - } - @Query(() => [PoliceCaseInfo], { nullable: true }) policeCaseInfo( @Args('input', { type: () => PoliceCaseInfoQueryInput }) diff --git a/apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts b/apps/judicial-system/api/src/app/modules/subpoena/dto/subpoena.input.ts similarity index 70% rename from apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts rename to apps/judicial-system/api/src/app/modules/subpoena/dto/subpoena.input.ts index dcc11761c0be..4f0e3ca81c1f 100644 --- a/apps/judicial-system/api/src/app/modules/police/dto/subpoenaStatus.input.ts +++ b/apps/judicial-system/api/src/app/modules/subpoena/dto/subpoena.input.ts @@ -3,11 +3,15 @@ import { Allow } from 'class-validator' import { Field, ID, InputType } from '@nestjs/graphql' @InputType() -export class SubpoenaStatusQueryInput { +export class SubpoenaQueryInput { @Allow() @Field(() => ID) readonly caseId!: string + @Allow() + @Field(() => ID) + readonly defendantId!: string + @Allow() @Field(() => ID) readonly subpoenaId!: string diff --git a/apps/judicial-system/api/src/app/modules/subpoena/index.ts b/apps/judicial-system/api/src/app/modules/subpoena/index.ts new file mode 100644 index 000000000000..59471a541a5d --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/subpoena/index.ts @@ -0,0 +1 @@ +export { Subpoena } from './models/subpoena.model' diff --git a/apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts b/apps/judicial-system/api/src/app/modules/subpoena/models/subpoena.model.ts similarity index 100% rename from apps/judicial-system/api/src/app/modules/defendant/models/subpoena.model.ts rename to apps/judicial-system/api/src/app/modules/subpoena/models/subpoena.model.ts diff --git a/apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts new file mode 100644 index 000000000000..313600b022e9 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common' + +import { SubpoenaResolver } from './subpoena.resolver' + +@Module({ + providers: [SubpoenaResolver], +}) +export class SubpoenaModule {} diff --git a/apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts new file mode 100644 index 000000000000..51752826bec6 --- /dev/null +++ b/apps/judicial-system/api/src/app/modules/subpoena/subpoena.resolver.ts @@ -0,0 +1,53 @@ +import { Inject, UseGuards } from '@nestjs/common' +import { Args, Context, Query, Resolver } from '@nestjs/graphql' + +import type { Logger } from '@island.is/logging' +import { LOGGER_PROVIDER } from '@island.is/logging' + +import { + AuditedAction, + AuditTrailService, +} from '@island.is/judicial-system/audit-trail' +import { + CurrentGraphQlUser, + JwtGraphQlAuthGuard, +} from '@island.is/judicial-system/auth' +import type { User } from '@island.is/judicial-system/types' + +import { BackendService } from '../backend' +import { SubpoenaQueryInput } from './dto/subpoena.input' +import { Subpoena } from './models/subpoena.model' + +@UseGuards(JwtGraphQlAuthGuard) +@Resolver() +export class SubpoenaResolver { + constructor( + private readonly auditTrailService: AuditTrailService, + @Inject(LOGGER_PROVIDER) + private readonly logger: Logger, + ) {} + + @Query(() => Subpoena, { nullable: true }) + subpoena( + @Args('input', { type: () => SubpoenaQueryInput }) + input: SubpoenaQueryInput, + @CurrentGraphQlUser() user: User, + @Context('dataSources') + { backendService }: { backendService: BackendService }, + ): Promise { + this.logger.debug( + `Getting subpoena ${input.subpoenaId} for defendant ${input.defendantId} of case ${input.caseId}`, + ) + + return this.auditTrailService.audit( + user.id, + AuditedAction.GET_SUBPOENA, + backendService.getSubpoena( + input.caseId, + input.defendantId, + input.subpoenaId, + ), + input.caseId, + ) + } +} diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts b/apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts new file mode 100644 index 000000000000..f7165aeb9d29 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/indictmentCaseExistsForDefendant.guard.ts @@ -0,0 +1,37 @@ +import { + BadRequestException, + CanActivate, + ExecutionContext, + Injectable, +} from '@nestjs/common' + +import { InternalCaseService } from '../internalCase.service' + +@Injectable() +export class IndictmentCaseExistsForDefendantGuard implements CanActivate { + constructor(private readonly internalCaseService: InternalCaseService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + + const caseId = request.params.caseId + + if (!caseId) { + throw new BadRequestException('Missing case id') + } + + const defendantNationalId = request.params.defendantNationalId + + if (!defendantNationalId) { + throw new BadRequestException('Missing defendant national id') + } + + request.case = + await this.internalCaseService.findByIdAndDefendantNationalId( + caseId, + defendantNationalId, + ) + + return true + } +} 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 c20e279824ea..60991a38b33f 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 @@ -25,7 +25,6 @@ import { restrictionCases, } from '@island.is/judicial-system/types' -import { DefendantNationalIdExistsGuard } from '../defendant' import { EventService } from '../event' import { DeliverDto } from './dto/deliver.dto' import { DeliverCancellationNoticeDto } from './dto/deliverCancellationNotice.dto' @@ -34,6 +33,7 @@ import { CurrentCase } from './guards/case.decorator' import { CaseCompletedGuard } from './guards/caseCompleted.guard' import { CaseExistsGuard } from './guards/caseExists.guard' import { CaseTypeGuard } from './guards/caseType.guard' +import { IndictmentCaseExistsForDefendantGuard } from './guards/indictmentCaseExistsForDefendant.guard' import { CaseInterceptor, CasesInterceptor, @@ -94,11 +94,7 @@ export class InternalCaseController { ) } - @UseGuards( - CaseExistsGuard, - new CaseTypeGuard(indictmentCases), - DefendantNationalIdExistsGuard, - ) + @UseGuards(IndictmentCaseExistsForDefendantGuard) @Get('case/indictment/:caseId/defendant/:defendantNationalId') @ApiOkResponse({ type: Case, @@ -107,14 +103,17 @@ export class InternalCaseController { @UseInterceptors(CaseInterceptor) getDefendantIndictmentCaseById( @Param('caseId') caseId: string, - @Param('defendantNationalId') _: string, + @Param('defendantNationalId') defendantNationalId: string, @CurrentCase() theCase: Case, - ): Case { + ): Promise { this.logger.debug( `Getting indictment case ${caseId} by id for a given defendant`, ) - return theCase + return this.internalCaseService.getDefendantIndictmentCase( + theCase, + defendantNationalId, + ) } @UseGuards(CaseExistsGuard) 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 c76146af9ab7..a969cf40351b 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 @@ -1,6 +1,7 @@ import CryptoJS from 'crypto-js' import format from 'date-fns/format' import { Base64 } from 'js-base64' +import { Op } from 'sequelize' import { Sequelize } from 'sequelize-typescript' import { @@ -57,7 +58,7 @@ import { CaseFile, FileService } from '../file' import { IndictmentCount, IndictmentCountService } from '../indictment-count' import { Institution } from '../institution' import { PoliceDocument, PoliceDocumentType, PoliceService } from '../police' -import { Subpoena } from '../subpoena' +import { Subpoena, SubpoenaService } from '../subpoena' import { User, UserService } from '../user' import { InternalCreateCaseDto } from './dto/internalCreateCase.dto' import { archiveFilter } from './filters/case.archiveFilter' @@ -171,6 +172,8 @@ export class InternalCaseService { private readonly fileService: FileService, @Inject(forwardRef(() => DefendantService)) private readonly defendantService: DefendantService, + @Inject(forwardRef(() => SubpoenaService)) + private readonly subpoenaService: SubpoenaService, @Inject(forwardRef(() => PdfService)) private readonly pdfService: PdfService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, @@ -1213,6 +1216,72 @@ export class InternalCaseService { }) } + async findByIdAndDefendantNationalId( + caseId: string, + defendantNationalId: string, + ): Promise { + const theCase = await this.caseModel.findOne({ + include: [ + { + model: Defendant, + as: 'defendants', + include: [ + { + model: Subpoena, + as: 'subpoenas', + order: [['created', 'DESC']], + }, + ], + }, + { model: Institution, as: 'court' }, + { model: Institution, as: 'prosecutorsOffice' }, + { model: User, as: 'judge' }, + { + model: User, + as: 'prosecutor', + include: [{ model: Institution, as: 'institution' }], + }, + { model: DateLog, as: 'dateLogs' }, + ], + attributes: ['courtCaseNumber', 'id'], + where: { + type: CaseType.INDICTMENT, + id: caseId, + state: { [Op.not]: CaseState.DELETED }, + isArchived: false, + // This select only defendants with the given national id, other defendants are not included + '$defendants.national_id$': + normalizeAndFormatNationalId(defendantNationalId), + }, + }) + + if (!theCase) { + throw new NotFoundException(`Case ${caseId} does not exist`) + } + + return theCase + } + + async getDefendantIndictmentCase( + theCase: Case, + defendantNationalId: string, + ): Promise { + const subpoena = theCase.defendants?.[0].subpoenas?.[0] + + if (!subpoena) { + return theCase + } + + const latestSubpoena = await this.subpoenaService.getSubpoena(subpoena) + + if (latestSubpoena === subpoena) { + // The subpoena was up to date + return theCase + } + + return this.findByIdAndDefendantNationalId(theCase.id, defendantNationalId) + } + countIndictmentsWaitingForConfirmation(prosecutorsOfficeId: string) { return this.caseModel.count({ include: [{ model: User, as: 'creatingProsecutor' }], diff --git a/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts b/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts deleted file mode 100644 index 0b869caed792..000000000000 --- a/apps/judicial-system/backend/src/app/modules/police/internalPolice.controller.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Controller, Get, Inject, Param, UseGuards } from '@nestjs/common' -import { ApiOkResponse, ApiTags } from '@nestjs/swagger' - -import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' - -import { TokenGuard } from '@island.is/judicial-system/auth' - -import { Case, CaseExistsGuard, CurrentCase } from '../case' -import { Subpoena } from '../subpoena' -import { PoliceService } from './police.service' - -@Controller('api/internal/case/:caseId') -@ApiTags('internal police') -@UseGuards(TokenGuard, CaseExistsGuard) -export class InternalPoliceController { - constructor( - private readonly policeService: PoliceService, - @Inject(LOGGER_PROVIDER) private readonly logger: Logger, - ) {} - - @Get('subpoenaStatus/:subpoenaId') - @ApiOkResponse({ - type: Subpoena, - description: 'Gets subpoena status', - }) - getSubpoenaStatus( - @Param('subpoenaId') subpoenaId: string, - @CurrentCase() theCase: Case, - ): Promise { - this.logger.debug(`Gets subpoena status in case ${theCase.id}`) - - return this.policeService.getSubpoenaStatus(subpoenaId) - } -} diff --git a/apps/judicial-system/backend/src/app/modules/police/models/subpoenaStatus.model.ts b/apps/judicial-system/backend/src/app/modules/police/models/subpoenaStatus.model.ts new file mode 100644 index 000000000000..8be61ad871a7 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/police/models/subpoenaStatus.model.ts @@ -0,0 +1,20 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' + +import { ServiceStatus } from '@island.is/judicial-system/types' + +export class SubpoenaInfo { + @ApiProperty({ type: ServiceStatus }) + serviceStatus?: ServiceStatus + + @ApiPropertyOptional({ type: String }) + comment?: string + + @ApiPropertyOptional({ type: String }) + servedBy?: string + + @ApiPropertyOptional({ type: String }) + defenderNationalId?: string + + @ApiPropertyOptional({ type: Date }) + serviceDate?: Date +} diff --git a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts index 3c0e355919f6..c80db84e51db 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.controller.ts @@ -21,13 +21,7 @@ import { } from '@island.is/judicial-system/auth' import type { User } from '@island.is/judicial-system/types' -import { - districtCourtAssistantRule, - districtCourtJudgeRule, - districtCourtRegistrarRule, - prosecutorRepresentativeRule, - prosecutorRule, -} from '../../guards' +import { prosecutorRepresentativeRule, prosecutorRule } from '../../guards' import { Case, CaseExistsGuard, @@ -36,7 +30,6 @@ import { CaseReadGuard, CurrentCase, } from '../case' -import { Subpoena } from '../subpoena' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' @@ -76,28 +69,6 @@ export class PoliceController { return this.policeService.getAllPoliceCaseFiles(theCase.id, user) } - @RolesRules( - prosecutorRule, - prosecutorRepresentativeRule, - districtCourtJudgeRule, - districtCourtAssistantRule, - districtCourtRegistrarRule, - ) - @Get('subpoenaStatus/:subpoenaId') - @ApiOkResponse({ - type: Subpoena, - description: 'Gets subpoena status', - }) - getSubpoenaStatus( - @Param('subpoenaId') subpoenaId: string, - @CurrentCase() theCase: Case, - @CurrentHttpUser() user: User, - ): Promise { - this.logger.debug(`Gets subpoena status in case ${theCase.id}`) - - return this.policeService.getSubpoenaStatus(subpoenaId, user) - } - @RolesRules(prosecutorRule, prosecutorRepresentativeRule) @UseInterceptors(CaseOriginalAncestorInterceptor) @Get('policeCaseInfo') diff --git a/apps/judicial-system/backend/src/app/modules/police/police.module.ts b/apps/judicial-system/backend/src/app/modules/police/police.module.ts index c6868f89b050..1df72c2b8794 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.module.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.module.ts @@ -1,7 +1,6 @@ import { forwardRef, Module } from '@nestjs/common' import { AwsS3Module, CaseModule, EventModule, SubpoenaModule } from '../index' -import { InternalPoliceController } from './internalPolice.controller' import { PoliceController } from './police.controller' import { PoliceService } from './police.service' @@ -14,6 +13,6 @@ import { PoliceService } from './police.service' ], providers: [PoliceService], exports: [PoliceService], - controllers: [PoliceController, InternalPoliceController], + controllers: [PoliceController], }) export class PoliceModule {} diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index c753e4e4db5e..ed503fe70b01 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -34,12 +34,12 @@ import { AwsS3Service } from '../aws-s3' import { Case } from '../case' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' -import { Subpoena, SubpoenaService } from '../subpoena' -import { UpdateSubpoenaDto } from '../subpoena/dto/updateSubpoena.dto' +import { SubpoenaService } from '../subpoena' import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' +import { SubpoenaInfo } from './models/subpoenaStatus.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' import { policeModuleConfig } from './police.config' @@ -336,7 +336,10 @@ export class PoliceService { }) } - async getSubpoenaStatus(subpoenaId: string, user?: User): Promise { + async getSubpoenaStatus( + subpoenaId: string, + user?: User, + ): Promise { return this.fetchPoliceDocumentApi( `${this.xRoadPath}/GetSubpoenaStatus?id=${subpoenaId}`, ) @@ -347,37 +350,24 @@ export class PoliceService { this.subpoenaStructure.parse(response) - const subpoenaToUpdate = await this.subpoenaService.findBySubpoenaId( - subpoenaId, - ) - - const serviceStatus = response.deliveredToLawyer - ? ServiceStatus.DEFENDER - : response.prosecutedConfirmedSubpoenaThroughIslandis - ? ServiceStatus.ELECTRONICALLY - : response.deliveredOnPaper || response.delivered === true - ? ServiceStatus.IN_PERSON - : response.acknowledged === false && response.delivered === false - ? ServiceStatus.FAILED - : // TODO: handle expired - undefined - - if (serviceStatus === undefined) { - return subpoenaToUpdate + return { + serviceStatus: response.deliveredToLawyer + ? ServiceStatus.DEFENDER + : response.prosecutedConfirmedSubpoenaThroughIslandis + ? ServiceStatus.ELECTRONICALLY + : response.deliveredOnPaper || response.delivered === true + ? ServiceStatus.IN_PERSON + : response.acknowledged === false && response.delivered === false + ? ServiceStatus.FAILED + : // TODO: handle expired + undefined, + comment: response.comment ?? undefined, + servedBy: response.servedBy ?? undefined, + defenderNationalId: response.defenderNationalId ?? undefined, + serviceDate: response.servedAt + ? new Date(response.servedAt) + : undefined, } - - const updatedSubpoena = await this.subpoenaService.update( - subpoenaToUpdate, - { - comment: response.comment ?? undefined, - servedBy: response.servedBy ?? undefined, - defenderNationalId: response.defenderNationalId ?? undefined, - serviceDate: response.servedAt ?? undefined, - serviceStatus, - } as UpdateSubpoenaDto, - ) - - return updatedSubpoena } const reason = await res.text() @@ -395,7 +385,7 @@ export class PoliceService { } if (reason instanceof ServiceUnavailableException) { - // Act as if the case does not exist + // Act as if the subpoena does not exist throw new NotFoundException({ ...reason, message: `Subpoena ${subpoenaId} does not exist`, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index 89e83477f4a2..712077d58c1d 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -18,8 +18,8 @@ export class UpdateSubpoenaDto { @IsOptional() @IsString() - @ApiPropertyOptional({ type: String }) - readonly serviceDate?: string + @ApiPropertyOptional({ type: Date }) + readonly serviceDate?: Date @IsOptional() @IsString() diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts index 4daa6f54028e..87402f7b3ada 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/internalSubpoena.controller.ts @@ -44,7 +44,7 @@ export class InternalSubpoenaController { @UseGuards(PoliceSubpoenaExistsGuard) @Patch('subpoena/:subpoenaId') - async updateSubpoena( + updateSubpoena( @Param('subpoenaId') subpoenaId: string, @CurrentSubpoena() subpoena: Subpoena, @Body() update: UpdateSubpoenaDto, @@ -69,7 +69,7 @@ export class InternalSubpoenaController { type: DeliverResponse, description: 'Delivers a subpoena to police', }) - async deliverSubpoenaToPolice( + deliverSubpoenaToPolice( @Param('caseId') caseId: string, @Param('defendantId') defendantId: string, @Param('subpoenaId') subpoenaId: string, @@ -82,7 +82,7 @@ export class InternalSubpoenaController { `Delivering subpoena ${subpoenaId} to police for defendant ${defendantId} of case ${caseId}`, ) - return await this.subpoenaService.deliverSubpoenaToPolice( + return this.subpoenaService.deliverSubpoenaToPolice( theCase, defendant, subpoena, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index 5c6dde866d50..1d4e152ed5fe 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -15,11 +15,16 @@ import { ApiOkResponse, ApiTags } from '@nestjs/swagger' import { type Logger, LOGGER_PROVIDER } from '@island.is/logging' import { + CurrentHttpUser, JwtAuthGuard, RolesGuard, RolesRules, } from '@island.is/judicial-system/auth' -import { indictmentCases, SubpoenaType } from '@island.is/judicial-system/types' +import { + indictmentCases, + SubpoenaType, + User, +} from '@island.is/judicial-system/types' import { districtCourtAssistantRule, @@ -46,6 +51,7 @@ import { SubpoenaExistsOptionalGuard, } from './guards/subpoenaExists.guard' import { Subpoena } from './models/subpoena.model' +import { SubpoenaService } from './subpoena.service' @Controller('api/case/:caseId/defendant/:defendantId/subpoena') @ApiTags('subpoenas') @@ -60,9 +66,37 @@ import { Subpoena } from './models/subpoena.model' export class SubpoenaController { constructor( private readonly pdfService: PdfService, + private readonly subpoenaService: SubpoenaService, @Inject(LOGGER_PROVIDER) private readonly logger: Logger, ) {} + @RolesRules( + prosecutorRule, + prosecutorRepresentativeRule, + districtCourtJudgeRule, + districtCourtAssistantRule, + districtCourtRegistrarRule, + ) + @Get(':subpoenaId') + @UseGuards(SubpoenaExistsGuard) + @ApiOkResponse({ + type: Subpoena, + description: 'Gets the subpoena for a given defendant', + }) + getSubpoena( + @Param('caseId') caseId: string, + @Param('defendantId') defendantId: string, + @Param('subpoenaId') subpoenaId: string, + @CurrentSubpoena() subpoena: Subpoena, + @CurrentHttpUser() user: User, + ): Promise { + this.logger.debug( + `Gets subpoena ${subpoenaId} for defendant ${defendantId} of case ${caseId}`, + ) + + return this.subpoenaService.getSubpoena(subpoena, user) + } + @RolesRules( prosecutorRule, prosecutorRepresentativeRule, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 64fc48de8dc9..8a1347299238 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -7,6 +7,7 @@ import { Inject, Injectable, InternalServerErrorException, + NotFoundException, } from '@nestjs/common' import { InjectConnection, InjectModel } from '@nestjs/sequelize' @@ -61,6 +62,7 @@ export const include: Includeable[] = [ @Injectable() export class SubpoenaService { constructor( + @InjectConnection() private readonly sequelize: Sequelize, @InjectModel(Subpoena) private readonly subpoenaModel: typeof Subpoena, private readonly pdfService: PdfService, private readonly messageService: MessageService, @@ -145,7 +147,6 @@ export class SubpoenaService { async update( subpoena: Subpoena, update: UpdateSubpoenaDto, - transaction?: Transaction, ): Promise { const { defenderChoice, @@ -159,61 +160,63 @@ export class SubpoenaService { requestedDefenderName, } = update - const [numberOfAffectedRows] = await this.subpoenaModel.update(update, { - where: { subpoenaId: subpoena.subpoenaId }, - returning: true, - transaction, - }) + await this.sequelize.transaction(async (transaction) => { + const [numberOfAffectedRows] = await this.subpoenaModel.update(update, { + where: { id: subpoena.id }, + returning: true, + transaction, + }) - if (numberOfAffectedRows > 1) { - // Tolerate failure, but log error - this.logger.error( - `Unexpected number of rows ${numberOfAffectedRows} affected when updating subpoena`, - ) - } + if (numberOfAffectedRows > 1) { + // Tolerate failure, but log error + this.logger.error( + `Unexpected number of rows ${numberOfAffectedRows} affected when updating subpoena`, + ) + } - if ( - subpoena.case && - subpoena.defendant && - (defenderChoice || - defenderNationalId || - defenderName || - defenderEmail || - defenderPhoneNumber || - requestedDefenderChoice || - requestedDefenderNationalId || - requestedDefenderName) - ) { - // If there is a change in the defender choice after the judge has confirmed the choice, - // we need to set the isDefenderChoiceConfirmed to false - const resetDefenderChoiceConfirmed = - subpoena.defendant?.isDefenderChoiceConfirmed && - ((defenderChoice && - subpoena.defendant?.defenderChoice !== defenderChoice) || - (defenderNationalId && - subpoena.defendant?.defenderNationalId !== defenderNationalId)) - - // Færa message handling í defendant service - await this.defendantService.updateRestricted( - subpoena.case, - subpoena.defendant, - { - defenderChoice, - defenderNationalId, - defenderName, - defenderEmail, - defenderPhoneNumber, - requestedDefenderChoice, - requestedDefenderNationalId, - requestedDefenderName, - }, - resetDefenderChoiceConfirmed ? false : undefined, - transaction, - ) - } + if ( + subpoena.case && + subpoena.defendant && + (defenderChoice || + defenderNationalId || + defenderName || + defenderEmail || + defenderPhoneNumber || + requestedDefenderChoice || + requestedDefenderNationalId || + requestedDefenderName) + ) { + // If there is a change in the defender choice after the judge has confirmed the choice, + // we need to set the isDefenderChoiceConfirmed to false + const resetDefenderChoiceConfirmed = + subpoena.defendant?.isDefenderChoiceConfirmed && + ((defenderChoice && + subpoena.defendant?.defenderChoice !== defenderChoice) || + (defenderNationalId && + subpoena.defendant?.defenderNationalId !== defenderNationalId)) + + // Færa message handling í defendant service + await this.defendantService.updateRestricted( + subpoena.case, + subpoena.defendant, + { + defenderChoice, + defenderNationalId, + defenderName, + defenderEmail, + defenderPhoneNumber, + requestedDefenderChoice, + requestedDefenderNationalId, + requestedDefenderName, + }, + resetDefenderChoiceConfirmed ? false : undefined, + transaction, + ) + } + }) // No need to wait for this to finish - this.addMessagesForSubpoenaUpdateToQueue(subpoena, serviceStatus) + await this.addMessagesForSubpoenaUpdateToQueue(subpoena, serviceStatus) if ( update.serviceStatus && @@ -228,21 +231,32 @@ export class SubpoenaService { ) } - return this.findBySubpoenaId(subpoena.subpoenaId) + return this.findById(subpoena.id) } - async findBySubpoenaId(subpoenaId?: string): Promise { - if (!subpoenaId) { - throw new Error('Missing subpoena id') + async findById(subpoenaId: string): Promise { + const subpoena = await this.subpoenaModel.findOne({ + include, + where: { id: subpoenaId }, + }) + + if (!subpoena) { + throw new NotFoundException(`Subpoena ${subpoenaId} does not exist`) } + return subpoena + } + + async findBySubpoenaId(policeSubpoenaId?: string): Promise { const subpoena = await this.subpoenaModel.findOne({ include, - where: { subpoenaId }, + where: { subpoenaId: policeSubpoenaId }, }) if (!subpoena) { - throw new Error(`Subpoena with subpoena id ${subpoenaId} not found`) + throw new NotFoundException( + `Subpoena with subpoena id ${policeSubpoenaId} does not exist`, + ) } return subpoena @@ -320,4 +334,25 @@ export class SubpoenaService { return { delivered: false } } } + + async getSubpoena(subpoena: Subpoena, user?: TUser): Promise { + if (!subpoena.subpoenaId) { + // The subpoena has not been delivered to the police + return subpoena + } + + if (subpoena.serviceStatus) { + // The subpoena has already been served to the defendant + return subpoena + } + + // We don't know if the subpoena has been served to the defendant + // so we need to check the police service + const serviceInfo = await this.policeService.getSubpoenaStatus( + subpoena.subpoenaId, + user, + ) + + return this.update(subpoena, serviceInfo) + } } diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts index 9c0ed86d1b05..6f4274fcbe56 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.service.ts @@ -11,7 +11,6 @@ import { AuditedAction, AuditTrailService, } from '@island.is/judicial-system/audit-trail' -import { normalizeAndFormatNationalId } from '@island.is/judicial-system/formatters' import { LawyersService } from '@island.is/judicial-system/lawyers' import { DefenderChoice } from '@island.is/judicial-system/types' @@ -97,25 +96,6 @@ export class CaseService { lang?: string, ): Promise { const response = await this.fetchCase(caseId, nationalId) - const defendantInfo = response.defendants.find( - (defendant) => - defendant.nationalId && - normalizeAndFormatNationalId(nationalId).includes(defendant.nationalId), - ) - - if (!defendantInfo) { - throw new NotFoundException('Defendant not found') - } - - if ( - defendantInfo.subpoenas?.[0]?.subpoenaId && - !defendantInfo.subpoenas[0].serviceStatus - ) { - await this.fetchServiceStatus( - caseId, - defendantInfo.subpoenas[0].subpoenaId, - ) - } return CaseResponse.fromInternalCaseResponse(response, lang) } @@ -254,52 +234,6 @@ export class CaseService { } } - private async fetchServiceStatus( - caseId: string, - subpoenaId: string, - ): Promise { - try { - const res = await fetch( - `${this.config.backendUrl}/api/internal/case/${caseId}/subpoenaStatus/${subpoenaId}`, - { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - authorization: `Bearer ${this.config.secretToken}`, - }, - }, - ) - - if (!res.ok) { - if (res.status === 404) { - throw new NotFoundException(`Case ${caseId} not found`) - } - - const reason = await res.text() - - throw new BadGatewayException( - reason || - 'Unexpected error occurred while fetching serviceStatus by subpoenaID', - ) - } - - const caseData = await res.json() - - return caseData - } catch (reason) { - if ( - reason instanceof BadGatewayException || - reason instanceof NotFoundException - ) { - throw reason - } - - throw new BadGatewayException( - `Failed to fetch serviceStatus by subpoenaId: ${reason.message}`, - ) - } - } - private async patchDefenseInfo( defendantNationalId: string, caseId: string, diff --git a/apps/judicial-system/web/src/components/FormProvider/case.graphql b/apps/judicial-system/web/src/components/FormProvider/case.graphql index d80ada64e561..3fe0ac8012b1 100644 --- a/apps/judicial-system/web/src/components/FormProvider/case.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/case.graphql @@ -42,6 +42,7 @@ query Case($input: CaseQueryInput!) { comment defenderNationalId caseId + defendantId subpoenaId } } diff --git a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx index 2c2b09df085b..8dac19a8c448 100644 --- a/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx +++ b/apps/judicial-system/web/src/components/ServiceAnnouncement/ServiceAnnouncement.tsx @@ -94,10 +94,7 @@ interface ServiceAnnouncementProps { const ServiceAnnouncement: FC = (props) => { const { subpoena: localSubpoena, defendantName } = props - const [subpoena, setSubpoena] = useState() - - const { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } = - useSubpoena(localSubpoena.caseId, localSubpoena.subpoenaId) + const { subpoena, subpoenaLoading } = useSubpoena(localSubpoena) const { formatMessage } = useIntl() @@ -111,38 +108,22 @@ const ServiceAnnouncement: FC = (props) => { ? mapServiceStatusMessages(subpoena, formatMessage, lawyer) : [] - // Use data from RLS but fallback to local data - useEffect(() => { - if (subpoenaStatusError || localSubpoena.serviceStatus) { - setSubpoena(localSubpoena) - } else { - setSubpoena({ - ...localSubpoena, - servedBy: subpoenaStatus?.subpoenaStatus?.servedBy, - serviceStatus: subpoenaStatus?.subpoenaStatus?.serviceStatus, - serviceDate: subpoenaStatus?.subpoenaStatus?.serviceDate, - comment: subpoenaStatus?.subpoenaStatus?.comment, - defenderNationalId: subpoenaStatus?.subpoenaStatus?.defenderNationalId, - }) - } - }, [localSubpoena, subpoenaStatus, subpoenaStatusError]) - - return !subpoena && !subpoenaStatusLoading ? ( - {renderError(formatMessage)} - ) : subpoenaStatusLoading && !localSubpoena.serviceStatus ? ( + return subpoenaLoading ? ( + ) : !subpoena ? ( + {renderError(formatMessage)} ) : ( {messages.map((msg) => ( - + {msg} ))} diff --git a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx index f22e56f7832d..d8528fc3bef7 100644 --- a/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Court/Indictments/Overview/Overview.tsx @@ -79,16 +79,13 @@ const IndictmentOverview = () => { {formatMessage(strings.inProgressTitle)} {workingCase.defendants?.map((defendant) => - defendant.subpoenas?.map( - (subpoena) => - subpoena.subpoenaId && ( - - ), - ), + defendant.subpoenas?.map((subpoena) => ( + + )), )} {workingCase.court && latestDate?.date && diff --git a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx index 3f1c8110e6b9..2640b512c74d 100644 --- a/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx +++ b/apps/judicial-system/web/src/routes/Prosecutor/Indictments/Overview/Overview.tsx @@ -191,16 +191,13 @@ const Overview: FC = () => { {formatMessage(strings.heading)} {workingCase.defendants?.map((defendant) => - defendant.subpoenas?.map( - (subpoena) => - subpoena.subpoenaId && ( - - ), - ), + defendant.subpoenas?.map((subpoena) => ( + + )), )} {workingCase.court && latestDate?.date && diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql b/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql deleted file mode 100644 index a87df3e15366..000000000000 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/getSubpoenaStatus.graphql +++ /dev/null @@ -1,9 +0,0 @@ -query SubpoenaStatus($input: SubpoenaStatusQueryInput!) { - subpoenaStatus(input: $input) { - serviceStatus - servedBy - comment - serviceDate - defenderNationalId - } -} diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts index 3d2ab133f847..55572496cba9 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -1,20 +1,29 @@ -import { useSubpoenaStatusQuery } from './getSubpoenaStatus.generated' +import { Subpoena } from '@island.is/judicial-system-web/src/graphql/schema' -const useSubpoena = (caseId?: string | null, subpoenaId?: string | null) => { - const { - data: subpoenaStatus, - loading: subpoenaStatusLoading, - error: subpoenaStatusError, - } = useSubpoenaStatusQuery({ +import { useSubpoenaQuery } from './subpoena.generated' + +const useSubpoena = (subpoena: Subpoena) => { + // Skip if the subpoena has not been sent to the police + // or if the subpoena has already been served + const skip = !subpoena.subpoenaId || Boolean(subpoena.serviceStatus) + + const { data, loading, error } = useSubpoenaQuery({ + skip, variables: { input: { - caseId: caseId ?? '', - subpoenaId: subpoenaId ?? '', + caseId: subpoena?.caseId ?? '', + defendantId: subpoena?.defendantId ?? '', + subpoenaId: subpoena?.id ?? '', }, }, + fetchPolicy: 'no-cache', + errorPolicy: 'all', }) - return { subpoenaStatus, subpoenaStatusLoading, subpoenaStatusError } + return { + subpoena: skip || error ? subpoena : data?.subpoena, + subpoenaLoading: skip ? false : loading, + } } export default useSubpoena diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql b/apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql new file mode 100644 index 000000000000..745293ed61eb --- /dev/null +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/subpoena.graphql @@ -0,0 +1,14 @@ +query Subpoena($input: SubpoenaQueryInput!) { + subpoena(input: $input) { + id + created + serviceStatus + serviceDate + servedBy + comment + defenderNationalId + caseId + defendantId + subpoenaId + } +} diff --git a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts index d20af4e3c6c7..70b82ad7d3ef 100644 --- a/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts +++ b/libs/judicial-system/audit-trail/src/lib/auditTrail.service.ts @@ -41,7 +41,6 @@ export enum AuditedAction { GET_SERVICE_CERTIFICATE_PDF = 'GET_SERVICE_CERTIFICATE', GET_ALL_FILES_ZIP = 'GET_ALL_FILES_ZIP', GET_INSTITUTIONS = 'GET_INSTITUTIONS', - GET_SUBPOENA_STATUS = 'GET_SUBPOENA_STATUS', CREATE_PRESIGNED_POST = 'CREATE_PRESIGNED_POST', CREATE_FILE = 'CREATE_FILE', UPDATE_FILES = 'UPDATE_FILES', From 684327eacb86a0f64cf92dae55dec87d7c5708df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 10:14:24 +0000 Subject: [PATCH 21/41] Removes comments --- apps/judicial-system/xrd-api/src/app/app.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/judicial-system/xrd-api/src/app/app.service.ts b/apps/judicial-system/xrd-api/src/app/app.service.ts index 98a72c8fade9..e1b870319535 100644 --- a/apps/judicial-system/xrd-api/src/app/app.service.ts +++ b/apps/judicial-system/xrd-api/src/app/app.service.ts @@ -62,7 +62,6 @@ export class AppService { return { id: response?.id } } - // TODO: Reconsider exception type if (res.status < 500) { throw new BadRequestException(response?.detail) } @@ -74,7 +73,6 @@ export class AppService { throw reason } - // TODO: Reconsider exception type throw new BadGatewayException({ ...reason, message: 'Failed to create a new case', @@ -134,7 +132,7 @@ export class AppService { `Failed to retrieve lawyer with national id ${updateSubpoena.defenderNationalId}`, reason, ) - // TODO: Reconsider exception type + throw new BadRequestException('Lawyer not found') } } @@ -192,7 +190,6 @@ export class AppService { } if (res.status < 500) { - // TODO: Reconsider exception type throw new BadRequestException(response?.detail) } From f8be4f9ea4606e120b5b5ec39398f1221ad43ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 10:16:00 +0000 Subject: [PATCH 22/41] Removes comments --- apps/judicial-system/xrd-api/src/app/app.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/judicial-system/xrd-api/src/app/app.service.ts b/apps/judicial-system/xrd-api/src/app/app.service.ts index e1b870319535..2cdd54556f9b 100644 --- a/apps/judicial-system/xrd-api/src/app/app.service.ts +++ b/apps/judicial-system/xrd-api/src/app/app.service.ts @@ -199,7 +199,6 @@ export class AppService { throw reason } - // TODO: Reconsider exception type throw new BadGatewayException({ ...reason, message: 'Failed to update subpoena', From ff843aaf1e5888ee90a32da3126ddf9475a44260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 12:27:38 +0000 Subject: [PATCH 23/41] Adds unit tests --- .../case/guards/mergedCaseExists.guard.ts | 2 +- ...ictmentCaseExistsForDefendantGuard.spec.ts | 161 ++++++++++++++++++ .../guards/test/mergedCaseExistsGuard.spec.ts | 130 ++++++++++++++ .../case/test/createTestingCaseModule.ts | 4 + .../app/modules/subpoena/subpoena.service.ts | 4 +- 5 files changed, 298 insertions(+), 3 deletions(-) create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts create mode 100644 apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts b/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts index d3892e9036aa..7e983cf79987 100644 --- a/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/case/guards/mergedCaseExists.guard.ts @@ -34,7 +34,7 @@ export class MergedCaseExistsGuard implements CanActivate { ) if (!mergedCase) { - throw new BadRequestException('Merged case not found') + throw new BadRequestException(`Merged case ${mergedCaseId} not found`) } request.mergedCaseParent = theCase diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts new file mode 100644 index 000000000000..b3a71e778c34 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts @@ -0,0 +1,161 @@ +import { Op } from 'sequelize' +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + NotFoundException, +} from '@nestjs/common' + +import { normalizeAndFormatNationalId } from '@island.is/judicial-system/formatters' +import { CaseState, CaseType } from '@island.is/judicial-system/types' + +import { createTestingCaseModule } from '../../test/createTestingCaseModule' + +import { Defendant } from '../../../defendant' +import { Institution } from '../../../institution' +import { Subpoena } from '../../../subpoena' +import { User } from '../../../user' +import { Case } from '../../models/case.model' +import { DateLog } from '../../models/dateLog.model' +import { IndictmentCaseExistsForDefendantGuard } from '../indictmentCaseExistsForDefendant.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Inditment Case Exists For Defendant Guard', () => { + const mockRequest = jest.fn() + let mockCaseModel: typeof Case + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { caseModel, internalCaseService } = await createTestingCaseModule() + + mockCaseModel = caseModel + + givenWhenThen = async (): Promise => { + const guard = new IndictmentCaseExistsForDefendantGuard( + internalCaseService, + ) + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('case exists', () => { + const caseId = uuid() + const defendantNationalId = uuid() + const theCase = { id: caseId } + const request = { params: { caseId, defendantNationalId }, case: undefined } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + const mockFindOne = mockCaseModel.findOne as jest.Mock + mockFindOne.mockResolvedValueOnce(theCase) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(mockCaseModel.findOne).toHaveBeenCalledWith({ + include: [ + { + model: Defendant, + as: 'defendants', + include: [ + { + model: Subpoena, + as: 'subpoenas', + order: [['created', 'DESC']], + }, + ], + }, + { model: Institution, as: 'court' }, + { model: Institution, as: 'prosecutorsOffice' }, + { model: User, as: 'judge' }, + { + model: User, + as: 'prosecutor', + include: [{ model: Institution, as: 'institution' }], + }, + { model: DateLog, as: 'dateLogs' }, + ], + attributes: ['courtCaseNumber', 'id'], + where: { + type: CaseType.INDICTMENT, + id: caseId, + state: { [Op.not]: CaseState.DELETED }, + isArchived: false, + '$defendants.national_id$': + normalizeAndFormatNationalId(defendantNationalId), + }, + }) + expect(then.result).toBe(true) + expect(request.case).toBe(theCase) + }) + }) + + describe('case does not exist', () => { + const caseId = uuid() + const defendantNationalId = uuid() + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ + params: { caseId, defendantNationalId }, + }) + + then = await givenWhenThen() + }) + + it('should throw NotFoundException', () => { + expect(then.error).toBeInstanceOf(NotFoundException) + expect(then.error.message).toBe(`Case ${caseId} does not exist`) + }) + }) + + describe('missing case id', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {} }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing case id') + }) + }) + + describe('missing case id', () => { + const caseId = uuid() + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: { caseId } }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing defendant national id') + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts new file mode 100644 index 000000000000..9800abe3a1c0 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/case/guards/test/mergedCaseExistsGuard.spec.ts @@ -0,0 +1,130 @@ +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + InternalServerErrorException, +} from '@nestjs/common' + +import { createTestingCaseModule } from '../../test/createTestingCaseModule' + +import { MergedCaseExistsGuard } from '../mergedCaseExists.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Merged Case Exists Guard', () => { + const mockRequest = jest.fn() + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { caseService } = await createTestingCaseModule() + + givenWhenThen = async (): Promise => { + const guard = new MergedCaseExistsGuard(caseService) + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('merged case exists', () => { + const mergedCaseId = uuid() + const mergedCase = { id: mergedCaseId } + const caseId = uuid() + const theCase = { id: caseId, mergedCases: [mergedCase] } + const request = { + params: { caseId, mergedCaseId }, + mergedCaseParent: undefined, + case: theCase, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(then.result).toBe(true) + expect(request.mergedCaseParent).toBe(theCase) + expect(request.params.caseId).toBe(mergedCaseId) + expect(request.case).toBe(mergedCase) + }) + }) + + describe('no merged case id', () => { + const caseId = uuid() + const theCase = { id: caseId, mergedCases: [] } + const request = { + params: { caseId }, + mergedCaseParent: undefined, + case: theCase, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(then.result).toBe(true) + expect(request.mergedCaseParent).toBeUndefined() + expect(request.params.caseId).toBe(caseId) + expect(request.case).toBe(theCase) + }) + }) + + describe('merged case not found', () => { + const mergedCaseId = uuid() + const caseId = uuid() + const theCase = { id: caseId, mergedCases: [] } + const request = { + params: { caseId, mergedCaseId }, + mergedCaseParent: undefined, + case: theCase, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should throw NotFoundException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe(`Merged case ${mergedCaseId} not found`) + }) + }) + + describe('missing case', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: { mergedCaseId: uuid() } }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(InternalServerErrorException) + expect(then.error.message).toBe('Missing case') + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts b/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts index 358e3028963a..3ecf8b656acc 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/createTestingCaseModule.ts @@ -180,6 +180,9 @@ export const createTestingCaseModule = async () => { const caseService = caseModule.get(CaseService) + const internalCaseService = + caseModule.get(InternalCaseService) + const limitedAccessCaseService = caseModule.get( LimitedAccessCaseService, ) @@ -213,6 +216,7 @@ export const createTestingCaseModule = async () => { caseStringModel, caseConfig, caseService, + internalCaseService, limitedAccessCaseService, caseController, internalCaseController, diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 3989a7e71d58..8a1347299238 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -1,5 +1,5 @@ import { Base64 } from 'js-base64' -import { Includeable } from 'sequelize' +import { Includeable, Sequelize } from 'sequelize' import { Transaction } from 'sequelize/types' import { @@ -9,7 +9,7 @@ import { InternalServerErrorException, NotFoundException, } from '@nestjs/common' -import { InjectModel } from '@nestjs/sequelize' +import { InjectConnection, InjectModel } from '@nestjs/sequelize' import type { Logger } from '@island.is/logging' import { LOGGER_PROVIDER } from '@island.is/logging' From 764c1a6bb180de980cebc1d2de572d84cbf52736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 12:36:38 +0000 Subject: [PATCH 24/41] Renames unit test --- ...mentCaseById.spec.ts => getIndictmentCaseByIdGuards.spec.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/{getIndictmentCaseById.spec.ts => getIndictmentCaseByIdGuards.spec.ts} (92%) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts similarity index 92% rename from apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts rename to apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts index c3927d45d501..cd10dbc8f131 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseById.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts @@ -5,7 +5,7 @@ import { CaseExistsGuard } from '../../guards/caseExists.guard' import { CaseTypeGuard } from '../../guards/caseType.guard' import { InternalCaseController } from '../../internalCase.controller' -describe('InternalCaseController - Get indictment case by id', () => { +describe('InternalCaseController - Get indictment case by id guards', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let guards: any[] From 582784ba76576511840245978144a77d67b240f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 12:53:55 +0000 Subject: [PATCH 25/41] Updates unit tests --- .../civilClaimantControllerGuards.spec.ts | 18 ++++++++++++++++++ .../civilClaimantController/update.spec.ts | 7 ++++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts new file mode 100644 index 000000000000..a6029164ba58 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/civilClaimantControllerGuards.spec.ts @@ -0,0 +1,18 @@ +import { JwtAuthGuard, RolesGuard } from '@island.is/judicial-system/auth' + +import { CivilClaimantController } from '../../civilClaimant.controller' + +describe('CivilClaimantController - guards', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let guards: any[] + + beforeEach(() => { + guards = Reflect.getMetadata('__guards__', CivilClaimantController) + }) + + it('should have the right guard configuration', () => { + expect(guards).toHaveLength(2) + expect(new guards[0]()).toBeInstanceOf(JwtAuthGuard) + expect(new guards[1]()).toBeInstanceOf(RolesGuard) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts index 2bb93041fa8b..391d99e3eb0a 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/test/civilClaimantController/update.spec.ts @@ -42,7 +42,12 @@ describe('CivilClaimantController - Update', () => { const then = {} as Then await civilClaimantController - .update(caseId, civilClaimantId, civilClaimaint, updateData) + .update( + caseId, + civilClaimantId, + { id: civilClaimantId } as CivilClaimant, + updateData, + ) .then((result) => (then.result = result)) .catch((error) => (then.error = error)) From f7181f8e3c6462bd4f8daacc8e43bcbf23b94f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 13:12:11 +0000 Subject: [PATCH 26/41] Refactors code --- .../models/{subpoenaStatus.model.ts => subpoenaInfo.model.ts} | 0 .../backend/src/app/modules/police/police.service.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename apps/judicial-system/backend/src/app/modules/police/models/{subpoenaStatus.model.ts => subpoenaInfo.model.ts} (100%) diff --git a/apps/judicial-system/backend/src/app/modules/police/models/subpoenaStatus.model.ts b/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts similarity index 100% rename from apps/judicial-system/backend/src/app/modules/police/models/subpoenaStatus.model.ts rename to apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index ed503fe70b01..3093eee31ad4 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -39,7 +39,7 @@ import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' -import { SubpoenaInfo } from './models/subpoenaStatus.model' +import { SubpoenaInfo } from './models/subpoenaInfo.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' import { policeModuleConfig } from './police.config' From 24b6c5e5876496d47d6b92e034f3c78b11073cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 13:38:26 +0000 Subject: [PATCH 27/41] Adds unit tests --- .../subpoena/guards/subpoenaExists.guard.ts | 6 +- .../guards/test/subpoenaExistsGuard.spec.ts | 109 ++++++++++++++++++ 2 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts index 13cc5aa37a0c..e4c02b4dec2f 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/subpoenaExists.guard.ts @@ -3,22 +3,20 @@ import { CanActivate, ExecutionContext, Injectable, + InternalServerErrorException, } from '@nestjs/common' import { Defendant } from '../../defendant' -import { SubpoenaService } from '../subpoena.service' @Injectable() export class SubpoenaExistsGuard implements CanActivate { - constructor(private readonly subpoenaService: SubpoenaService) {} - async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest() const defendant: Defendant = request.defendant if (!defendant) { - throw new BadRequestException('Missing defendant') + throw new InternalServerErrorException('Missing defendant') } const subpoenaId = request.params.subpoenaId diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts new file mode 100644 index 000000000000..a8fb413fdd84 --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts @@ -0,0 +1,109 @@ +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + InternalServerErrorException, +} from '@nestjs/common' + +import { SubpoenaExistsGuard } from '../subpoenaExists.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Subpoena Exists Guard', () => { + const mockRequest = jest.fn() + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + givenWhenThen = async (): Promise => { + const guard = new SubpoenaExistsGuard() + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('subpoena exists', () => { + const subpoenaId = uuid() + const subpoena = { id: subpoenaId } + const defendantId = uuid() + const defendant = { id: defendantId, subpoenas: [subpoena] } + const request = { params: { subpoenaId }, defendant, subpoena: undefined } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(then.result).toBe(true) + expect(request.subpoena).toBe(subpoena) + }) + }) + + describe('subpoena does not exist', () => { + const subpoenaId = uuid() + const defendantId = uuid() + const defendant = { id: defendantId, subpoenas: [] } + const request = { params: { subpoenaId }, defendant, subpoena: undefined } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + + then = await givenWhenThen() + }) + + it('should throw NotFoundException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe( + `Subpoena ${subpoenaId} of defendant ${defendantId} does not exist`, + ) + }) + }) + + describe('missing defendant', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {} }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(InternalServerErrorException) + expect(then.error.message).toBe('Missing defendant') + }) + }) + + describe('missing subpoena id', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {}, defendant: { id: uuid() } }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing subpoena id') + }) + }) +}) From 8c757ec2af973fbee6b4e249c3c0117bd665448b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 15:27:50 +0000 Subject: [PATCH 28/41] Adds unit tests --- .../guards/policeSubpoenaExists.guard.ts | 2 +- .../test/policeSubpoenaExistsGuard.spec.ts | 110 ++++++++++++++++++ .../app/modules/subpoena/subpoena.service.ts | 2 +- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts index d7021c9528c3..e351570b9714 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/policeSubpoenaExists.guard.ts @@ -17,7 +17,7 @@ export class PoliceSubpoenaExistsGuard implements CanActivate { const subpoenaId = request.params.subpoenaId if (!subpoenaId) { - throw new BadRequestException('Missing subpoena id') + throw new BadRequestException('Missing police subpoena id') } // subpoenaId is the external police document id diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts new file mode 100644 index 000000000000..c924bb6a938f --- /dev/null +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/policeSubpoenaExistsGuard.spec.ts @@ -0,0 +1,110 @@ +import { uuid } from 'uuidv4' + +import { + BadRequestException, + ExecutionContext, + NotFoundException, +} from '@nestjs/common' + +import { createTestingSubpoenaModule } from '../../test/createTestingSubpoenaModule' + +import { Subpoena } from '../../models/subpoena.model' +import { include } from '../../subpoena.service' +import { PoliceSubpoenaExistsGuard } from '../policeSubpoenaExists.guard' + +interface Then { + result: boolean + error: Error +} + +type GivenWhenThen = () => Promise + +describe('Police Subpoena Exists Guard', () => { + const mockRequest = jest.fn() + let mockSubpoenaModel: typeof Subpoena + let givenWhenThen: GivenWhenThen + + beforeEach(async () => { + const { subpoenaModel, subpoenaService } = + await createTestingSubpoenaModule() + + mockSubpoenaModel = subpoenaModel + + givenWhenThen = async (): Promise => { + const guard = new PoliceSubpoenaExistsGuard(subpoenaService) + const then = {} as Then + + try { + then.result = await guard.canActivate({ + switchToHttp: () => ({ getRequest: mockRequest }), + } as unknown as ExecutionContext) + } catch (error) { + then.error = error as Error + } + + return then + } + }) + + describe('subpoena exists', () => { + const policeSubpoenaId = uuid() + const subpoena = { id: uuid(), subpoenaId: policeSubpoenaId } + const request = { + params: { subpoenaId: policeSubpoenaId }, + subpoena: undefined, + } + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce(request) + const mockFindOne = mockSubpoenaModel.findOne as jest.Mock + mockFindOne.mockResolvedValueOnce(subpoena) + + then = await givenWhenThen() + }) + + it('should activate', () => { + expect(mockSubpoenaModel.findOne).toHaveBeenCalledWith({ + include, + where: { subpoenaId: policeSubpoenaId }, + }) + expect(then.result).toBe(true) + expect(request.subpoena).toBe(subpoena) + }) + }) + + describe('subpoena does not exist', () => { + const policeSubpoenaId = uuid() + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ + params: { subpoenaId: policeSubpoenaId }, + }) + + then = await givenWhenThen() + }) + + it('should throw NotFoundException', () => { + expect(then.error).toBeInstanceOf(NotFoundException) + expect(then.error.message).toBe( + `Subpoena with police subpoena id ${policeSubpoenaId} does not exist`, + ) + }) + }) + + describe('missing subpoena id', () => { + let then: Then + + beforeEach(async () => { + mockRequest.mockReturnValueOnce({ params: {} }) + + then = await givenWhenThen() + }) + + it('should throw BadRequestException', () => { + expect(then.error).toBeInstanceOf(BadRequestException) + expect(then.error.message).toBe('Missing police subpoena id') + }) + }) +}) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 8a1347299238..a96e81674f6d 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -255,7 +255,7 @@ export class SubpoenaService { if (!subpoena) { throw new NotFoundException( - `Subpoena with subpoena id ${policeSubpoenaId} does not exist`, + `Subpoena with police subpoena id ${policeSubpoenaId} does not exist`, ) } From 720dada37496a8604bdf24701292041f2ce1acf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 16:23:35 +0000 Subject: [PATCH 29/41] Fixes subpoena refresh --- .../app/modules/backend/backend.service.ts | 2 +- .../app/modules/subpoena/subpoena.service.ts | 23 +++++++++++++++++-- .../FormProvider/limitedAccessCase.graphql | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts index 28d8c99b9ac9..42cd21e2aa6b 100644 --- a/apps/judicial-system/api/src/app/modules/backend/backend.service.ts +++ b/apps/judicial-system/api/src/app/modules/backend/backend.service.ts @@ -349,7 +349,7 @@ export class BackendService extends DataSource<{ req: Request }> { subpoenaId: string, ): Promise { return this.get( - `case/${caseId}/defendnat/${defendantId}/subpoena/${subpoenaId}`, + `case/${caseId}/defendant/${defendantId}/subpoena/${subpoenaId}`, ) } diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index a96e81674f6d..839c8a2ebc6f 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -348,11 +348,30 @@ export class SubpoenaService { // We don't know if the subpoena has been served to the defendant // so we need to check the police service - const serviceInfo = await this.policeService.getSubpoenaStatus( + const subpoenaInfo = await this.policeService.getSubpoenaStatus( subpoena.subpoenaId, user, ) - return this.update(subpoena, serviceInfo) + if ( + (!subpoenaInfo.serviceStatus || + subpoenaInfo.serviceStatus === subpoena.serviceStatus) && + (!subpoenaInfo.comment || subpoenaInfo.comment === subpoena.comment) && + (!subpoenaInfo.servedBy || subpoenaInfo.servedBy === subpoena.servedBy) && + (!subpoenaInfo.defenderNationalId || + subpoenaInfo.defenderNationalId === subpoena.defenderNationalId) && + (!subpoenaInfo.serviceDate || + subpoenaInfo.serviceDate === subpoena.serviceDate) + ) { + // The subpoena has not changed + return subpoena + } + + console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!', { + subpoena, + subpoenaInfo, + }) + + return this.update(subpoena, subpoenaInfo) } } diff --git a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql index df8b33ad5c86..b61738d433a9 100644 --- a/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql +++ b/apps/judicial-system/web/src/components/FormProvider/limitedAccessCase.graphql @@ -56,6 +56,7 @@ query LimitedAccessCase($input: CaseQueryInput!) { comment defenderNationalId caseId + defendantId subpoenaId } } From 199626014cd998d9f246a60af392a6344c54dd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 17:05:16 +0000 Subject: [PATCH 30/41] Cleans up some code --- .../src/app/modules/cases/case.controller.ts | 8 ++++---- .../web/src/utils/hooks/useSubpoena/index.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts index 3088b8a144f1..6663f8cd5c21 100644 --- a/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts +++ b/apps/judicial-system/digital-mailbox-api/src/app/modules/cases/case.controller.ts @@ -65,7 +65,7 @@ export class CaseController { }) @CommonApiResponses @ApiLocaleQuery - async getAllCases( + getAllCases( @CurrentUser() user: User, @Query() query?: { locale: string }, ): Promise { @@ -85,7 +85,7 @@ export class CaseController { description: 'Case for given case id and authenticated user not found', }) @ApiLocaleQuery - async getCase( + getCase( @Param('caseId', new ParseUUIDPipe()) caseId: string, @CurrentUser() user: User, @Query() query?: { locale: string }, @@ -106,7 +106,7 @@ export class CaseController { description: 'Subpoena for given case id and authenticated user not found', }) @ApiLocaleQuery - async getSubpoena( + getSubpoena( @Param('caseId', new ParseUUIDPipe()) caseId: string, @CurrentUser() user: User, @Query() query?: { locale: string }, @@ -131,7 +131,7 @@ export class CaseController { description: 'User is not allowed to update subpoena', }) @ApiLocaleQuery - async updateSubpoena( + updateSubpoena( @CurrentUser() user: User, @Param('caseId', new ParseUUIDPipe()) caseId: string, @Body() defenderAssignment: UpdateSubpoenaDto, diff --git a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts index 55572496cba9..de5fc94af08d 100644 --- a/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts +++ b/apps/judicial-system/web/src/utils/hooks/useSubpoena/index.ts @@ -4,7 +4,7 @@ import { useSubpoenaQuery } from './subpoena.generated' const useSubpoena = (subpoena: Subpoena) => { // Skip if the subpoena has not been sent to the police - // or if the subpoena has already been served + // or if the subpoena already has a service status const skip = !subpoena.subpoenaId || Boolean(subpoena.serviceStatus) const { data, loading, error } = useSubpoenaQuery({ From 51ab701bf9af80f65d2cf9fbf2833c86234bf8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 17:08:46 +0000 Subject: [PATCH 31/41] Removes unnecessary export --- apps/judicial-system/backend/src/app/modules/defendant/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/defendant/index.ts b/apps/judicial-system/backend/src/app/modules/defendant/index.ts index 50c7e69457fb..6b6c33d19c71 100644 --- a/apps/judicial-system/backend/src/app/modules/defendant/index.ts +++ b/apps/judicial-system/backend/src/app/modules/defendant/index.ts @@ -2,7 +2,6 @@ export { Defendant } from './models/defendant.model' export { DefendantService } from './defendant.service' export { DefendantExistsGuard } from './guards/defendantExists.guard' export { CurrentDefendant } from './guards/defendant.decorator' -export { DefendantNationalIdExistsGuard } from './guards/defendantNationalIdExists.guard' export { DefendantEventLog } from './models/defendantEventLog.model' export { CivilClaimant } from './models/civilClaimant.model' From 99de0317de4d5a191cd6e74e81a4605fb4af02ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 26 Nov 2024 17:28:29 +0000 Subject: [PATCH 32/41] Rmoves console log. --- .../backend/src/app/modules/subpoena/subpoena.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 839c8a2ebc6f..0da3c0ea2d3d 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -367,11 +367,6 @@ export class SubpoenaService { return subpoena } - console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!', { - subpoena, - subpoenaInfo, - }) - return this.update(subpoena, subpoenaInfo) } } From 62128a946228a8864a6789bd620b22d6f7251945 Mon Sep 17 00:00:00 2001 From: unakb Date: Wed, 27 Nov 2024 10:46:48 +0000 Subject: [PATCH 33/41] Update apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts --- .../guards/test/indictmentCaseExistsForDefendantGuard.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts index b3a71e778c34..20fb69b3f012 100644 --- a/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts @@ -27,7 +27,7 @@ interface Then { type GivenWhenThen = () => Promise -describe('Inditment Case Exists For Defendant Guard', () => { +describe('Indictment Case Exists For Defendant Guard', () => { const mockRequest = jest.fn() let mockCaseModel: typeof Case let givenWhenThen: GivenWhenThen From b8526a5ab5cfc82718f5757ec8f0d86af5136fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 27 Nov 2024 14:53:34 +0000 Subject: [PATCH 34/41] Updates unit test --- .../getIndictmentCaseByIdGuards.spec.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts index cd10dbc8f131..5854213f6614 100644 --- a/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/test/internalCaseController/getIndictmentCaseByIdGuards.spec.ts @@ -1,11 +1,7 @@ -import { indictmentCases } from '@island.is/judicial-system/types' - -import { DefendantNationalIdExistsGuard } from '../../../defendant' -import { CaseExistsGuard } from '../../guards/caseExists.guard' -import { CaseTypeGuard } from '../../guards/caseType.guard' +import { IndictmentCaseExistsForDefendantGuard } from '../../guards/indictmentCaseExistsForDefendant.guard' import { InternalCaseController } from '../../internalCase.controller' -describe('InternalCaseController - Get indictment case by id guards', () => { +describe('InternalCaseController - Get defendant indictment case by id guards', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let guards: any[] @@ -17,12 +13,9 @@ describe('InternalCaseController - Get indictment case by id guards', () => { }) it('should have the right guard configuration', () => { - expect(guards).toHaveLength(3) - expect(new guards[0]()).toBeInstanceOf(CaseExistsGuard) - expect(guards[1]).toBeInstanceOf(CaseTypeGuard) - expect(guards[1]).toEqual({ - allowedCaseTypes: indictmentCases, - }) - expect(new guards[2]()).toBeInstanceOf(DefendantNationalIdExistsGuard) + expect(guards).toHaveLength(1) + expect(new guards[0]()).toBeInstanceOf( + IndictmentCaseExistsForDefendantGuard, + ) }) }) From ae684161175beae5a71fc1205143583224dab104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 27 Nov 2024 15:05:19 +0000 Subject: [PATCH 35/41] Fixes type import --- .../backend/src/app/modules/subpoena/subpoena.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts index 1d4e152ed5fe..8242d7867970 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.controller.ts @@ -23,7 +23,7 @@ import { import { indictmentCases, SubpoenaType, - User, + type User, } from '@island.is/judicial-system/types' import { From e86322d306188f7f3255db74ad11be30266e190d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 27 Nov 2024 15:16:14 +0000 Subject: [PATCH 36/41] Fixes typo --- .../modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts index a8fb413fdd84..59a2831a1895 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts @@ -69,7 +69,7 @@ describe('Subpoena Exists Guard', () => { then = await givenWhenThen() }) - it('should throw NotFoundException', () => { + it('should throw BadRequestException', () => { expect(then.error).toBeInstanceOf(BadRequestException) expect(then.error.message).toBe( `Subpoena ${subpoenaId} of defendant ${defendantId} does not exist`, From ea60915d59d017f293c5b6c8d5ac1633c0e9deb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 27 Nov 2024 15:18:01 +0000 Subject: [PATCH 37/41] Fixes typo --- .../modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts index 59a2831a1895..504b56c01e61 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/guards/test/subpoenaExistsGuard.spec.ts @@ -86,7 +86,7 @@ describe('Subpoena Exists Guard', () => { then = await givenWhenThen() }) - it('should throw BadRequestException', () => { + it('should throw InternalServerErrorException', () => { expect(then.error).toBeInstanceOf(InternalServerErrorException) expect(then.error.message).toBe('Missing defendant') }) From ca5cac9baed3bf6ace85e287d474dd309b38507d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 27 Nov 2024 15:22:04 +0000 Subject: [PATCH 38/41] Fixes type decorators --- .../app/modules/subpoena/dto/updateSubpoena.dto.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts index 712077d58c1d..b6c9eac5b182 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/dto/updateSubpoena.dto.ts @@ -1,4 +1,11 @@ -import { IsEnum, IsOptional, IsString, MaxLength } from 'class-validator' +import { Type } from 'class-transformer' +import { + IsDate, + IsEnum, + IsOptional, + IsString, + MaxLength, +} from 'class-validator' import { ApiPropertyOptional } from '@nestjs/swagger' @@ -17,7 +24,8 @@ export class UpdateSubpoenaDto { readonly servedBy?: string @IsOptional() - @IsString() + @Type(() => Date) + @IsDate() @ApiPropertyOptional({ type: Date }) readonly serviceDate?: Date From a6e0ae6e973c00f857b1d779fd2fc52841992777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Wed, 27 Nov 2024 15:24:59 +0000 Subject: [PATCH 39/41] Fixes typo --- .../guards/test/indictmentCaseExistsForDefendantGuard.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts index 20fb69b3f012..076ac2c882c4 100644 --- a/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts +++ b/apps/judicial-system/backend/src/app/modules/case/guards/test/indictmentCaseExistsForDefendantGuard.spec.ts @@ -143,7 +143,7 @@ describe('Indictment Case Exists For Defendant Guard', () => { }) }) - describe('missing case id', () => { + describe('missing defendant national id', () => { const caseId = uuid() let then: Then From a92bd7fe34e24701d5c1679432d882a5dbd06a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 3 Dec 2024 12:01:58 +0000 Subject: [PATCH 40/41] Refactors code --- .../police/models/subpoenaInfo.model.ts | 29 +++++++++++++++++++ .../app/modules/subpoena/subpoena.service.ts | 11 +------ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts b/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts index 8be61ad871a7..2bd84b54109a 100644 --- a/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts +++ b/apps/judicial-system/backend/src/app/modules/police/models/subpoenaInfo.model.ts @@ -2,7 +2,36 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' import { ServiceStatus } from '@island.is/judicial-system/types' +interface SubpoenaUpdate + extends Pick< + SubpoenaInfo, + | 'serviceStatus' + | 'comment' + | 'servedBy' + | 'defenderNationalId' + | 'serviceDate' + > {} + +const subpoenaUpdateKeys: Array = [ + 'serviceStatus', + 'comment', + 'servedBy', + 'defenderNationalId', + 'serviceDate', +] + export class SubpoenaInfo { + private isNewValueSetAndDifferent = ( + newValue: unknown, + oldValue: unknown, + ): boolean => Boolean(newValue) && newValue !== oldValue + + isSubpoenaInfoChanged(oldSubpoenaInfo: SubpoenaUpdate) { + return subpoenaUpdateKeys.some((key) => + this.isNewValueSetAndDifferent(this[key], oldSubpoenaInfo[key]), + ) + } + @ApiProperty({ type: ServiceStatus }) serviceStatus?: ServiceStatus diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 0da3c0ea2d3d..111c3fc21cbd 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -353,16 +353,7 @@ export class SubpoenaService { user, ) - if ( - (!subpoenaInfo.serviceStatus || - subpoenaInfo.serviceStatus === subpoena.serviceStatus) && - (!subpoenaInfo.comment || subpoenaInfo.comment === subpoena.comment) && - (!subpoenaInfo.servedBy || subpoenaInfo.servedBy === subpoena.servedBy) && - (!subpoenaInfo.defenderNationalId || - subpoenaInfo.defenderNationalId === subpoena.defenderNationalId) && - (!subpoenaInfo.serviceDate || - subpoenaInfo.serviceDate === subpoena.serviceDate) - ) { + if (!subpoenaInfo.isSubpoenaInfoChanged(subpoena)) { // The subpoena has not changed return subpoena } From 7b928492bc603dc0a1b4f67e09648ac489e3107b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=B0j=C3=B3n=20Gu=C3=B0j=C3=B3nsson?= Date: Tue, 3 Dec 2024 18:07:43 +0000 Subject: [PATCH 41/41] Rewrites subpoena info diff --- .../backend/src/app/modules/police/index.ts | 1 + .../src/app/modules/police/police.service.ts | 9 ++++++- .../app/modules/subpoena/subpoena.service.ts | 25 +++++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/apps/judicial-system/backend/src/app/modules/police/index.ts b/apps/judicial-system/backend/src/app/modules/police/index.ts index 6f0613c334bc..f269e4fca945 100644 --- a/apps/judicial-system/backend/src/app/modules/police/index.ts +++ b/apps/judicial-system/backend/src/app/modules/police/index.ts @@ -1,5 +1,6 @@ export { PoliceDocumentType, PoliceDocument, + SubpoenaInfo, PoliceService, } from './police.service' diff --git a/apps/judicial-system/backend/src/app/modules/police/police.service.ts b/apps/judicial-system/backend/src/app/modules/police/police.service.ts index 3093eee31ad4..bdb539eb73cd 100644 --- a/apps/judicial-system/backend/src/app/modules/police/police.service.ts +++ b/apps/judicial-system/backend/src/app/modules/police/police.service.ts @@ -39,7 +39,6 @@ import { UploadPoliceCaseFileDto } from './dto/uploadPoliceCaseFile.dto' import { CreateSubpoenaResponse } from './models/createSubpoena.response' import { PoliceCaseFile } from './models/policeCaseFile.model' import { PoliceCaseInfo } from './models/policeCaseInfo.model' -import { SubpoenaInfo } from './models/subpoenaInfo.model' import { UploadPoliceCaseFileResponse } from './models/uploadPoliceCaseFile.response' import { policeModuleConfig } from './police.config' @@ -59,6 +58,14 @@ export interface PoliceDocument { courtDocument: string } +export interface SubpoenaInfo { + serviceStatus?: ServiceStatus + comment?: string + servedBy?: string + defenderNationalId?: string + serviceDate?: Date +} + const getChapter = (category?: string): number | undefined => { if (!category) { return undefined diff --git a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts index 111c3fc21cbd..e2d3116d4aa5 100644 --- a/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts +++ b/apps/judicial-system/backend/src/app/modules/subpoena/subpoena.service.ts @@ -35,7 +35,7 @@ import { DefendantService } from '../defendant/defendant.service' import { Defendant } from '../defendant/models/defendant.model' import { EventService } from '../event' import { FileService } from '../file' -import { PoliceService } from '../police' +import { PoliceService, SubpoenaInfo } from '../police' import { User } from '../user' import { UpdateSubpoenaDto } from './dto/updateSubpoena.dto' import { DeliverResponse } from './models/deliver.response' @@ -59,6 +59,27 @@ export const include: Includeable[] = [ { model: Defendant, as: 'defendant' }, ] +const subpoenaInfoKeys: Array = [ + 'serviceStatus', + 'comment', + 'servedBy', + 'defenderNationalId', + 'serviceDate', +] + +const isNewValueSetAndDifferent = ( + newValue: unknown, + oldValue: unknown, +): boolean => Boolean(newValue) && newValue !== oldValue + +export const isSubpoenaInfoChanged = ( + newSubpoenaInfo: SubpoenaInfo, + oldSubpoenaInfo: SubpoenaInfo, +) => + subpoenaInfoKeys.some((key) => + isNewValueSetAndDifferent(newSubpoenaInfo[key], oldSubpoenaInfo[key]), + ) + @Injectable() export class SubpoenaService { constructor( @@ -353,7 +374,7 @@ export class SubpoenaService { user, ) - if (!subpoenaInfo.isSubpoenaInfoChanged(subpoena)) { + if (!isSubpoenaInfoChanged(subpoenaInfo, subpoena)) { // The subpoena has not changed return subpoena }