Skip to content

Commit

Permalink
feat(j-s): Connect case files to defendant or civil claimants (#17043)
Browse files Browse the repository at this point in the history
* Add a send to fmst button that routes to a page

* Remove unused code

* Add UI

* Create event log when indictment is sent to FMSt

* Create event log when indictment is sent to FMSt

* Create event log when indictment is sent to FMSt

* Refactor

* Refactor

* Refactor

* Updates FMST queries

* Refactor

* Refactor

* Refactor

* Refactor

* Add DefendantEventLog table

* Refactor

* Send sentToPrisonAdminDate to client

* Show sent to prison admin date on info cards

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Frontend: Upload file to prison admin

* Frontend: Upload file to prison admin

* Add SentToPrisonAdmin tag in cases list for PP

* Merge

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* Refactor

* chore(j-s): Notifications to prison when indictment sent or withdrawn

* fix(j-s): Test fix

* chore: charts update dirty files

* fix(j-s): Tests

* chore: nx format:write update dirty files

* Refactor

* Remove caseId from defendant event log table

* Remove caseId from defendant event log table

* Revert

* Remove unused code

* Refactor

* Removes unused event type

* Removes the defendant event log from the api

* Moves defendant event log to defendant model

* Fixes backend case retrieval

* test(j-s): add missing test

* fix(j-s): Tests

* Update civilClaimantNotification.service.ts

* feat(j-s): Connect case files to defendant or civil claimant

* Fixes backend endpoints

* Changes wording

* Removes duplicate notification types

* Blocks limited acces create civil claimant file for now

* Adds back category restiction guards when creating files for defendants and civil claimants

* Removes redundant arguments

* Removes unused imports

* Rewrites create case file api

* Rewrites use S3 upload

* Fixes argument declarations

---------

Co-authored-by: Ívar Oddsson <[email protected]>
Co-authored-by: Guðjón Guðjónsson <[email protected]>
Co-authored-by: andes-it <[email protected]>
  • Loading branch information
4 people authored Dec 1, 2024
1 parent 8140945 commit 5815544
Show file tree
Hide file tree
Showing 22 changed files with 556 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,25 @@ export class BackendService extends DataSource<{ req: Request }> {
return this.post(`case/${id}/file`, createFile)
}

createDefendantCaseFile(
id: string,
createFile: unknown,
defendantId: string,
): Promise<CaseFile> {
return this.post(`case/${id}/defendant/${defendantId}/file`, createFile)
}

createCivilClaimantCaseFile(
id: string,
createFile: unknown,
civilClaimantId: string,
): Promise<CaseFile> {
return this.post(
`case/${id}/civilClaimant/${civilClaimantId}/file`,
createFile,
)
}

getCaseFileSignedUrl(
caseId: string,
id: string,
Expand Down Expand Up @@ -439,6 +458,28 @@ export class BackendService extends DataSource<{ req: Request }> {
return this.post(`case/${id}/limitedAccess/file`, createFile)
}

limitedAccessCreateDefendantCaseFile(
id: string,
createFile: unknown,
defendantId: string,
): Promise<CaseFile> {
return this.post(
`case/${id}/limitedAccess$/defendant/${defendantId}/file`,
createFile,
)
}

limitedAccessCreateCivilClaimantCaseFile(
id: string,
createFile: unknown,
civilClaimantId: string,
): Promise<CaseFile> {
return this.post(
`case/${id}/limitedAccess$/civilClaimant/${civilClaimantId}/file`,
createFile,
)
}

limitedAccessGetCaseFileSignedUrl(
caseId: string,
id: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Allow } from 'class-validator'

import { Field, ID, InputType } from '@nestjs/graphql'

import { CreateFileInput } from './createFile.input'

@InputType()
export class CreateCivilClaimantFileInput extends CreateFileInput {
@Allow()
@Field(() => ID)
readonly civilClaimantId!: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Allow } from 'class-validator'

import { Field, ID, InputType } from '@nestjs/graphql'

import { CreateFileInput } from './createFile.input'

@InputType()
export class CreateDefendantFileInput extends CreateFileInput {
@Allow()
@Field(() => ID)
readonly defendantId!: string
}
50 changes: 50 additions & 0 deletions apps/judicial-system/api/src/app/modules/file/file.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
import type { User } from '@island.is/judicial-system/types'

import { BackendService } from '../backend'
import { CreateCivilClaimantFileInput } from './dto/createCivilClaimantFile.input'
import { CreateDefendantFileInput } from './dto/createDefendantFile.input'
import { CreateFileInput } from './dto/createFile.input'
import { CreatePresignedPostInput } from './dto/createPresignedPost.input'
import { DeleteFileInput } from './dto/deleteFile.input'
Expand Down Expand Up @@ -77,6 +79,54 @@ export class FileResolver {
)
}

@Mutation(() => CaseFile)
createDefendantFile(
@Args('input', { type: () => CreateDefendantFileInput })
input: CreateDefendantFileInput,
@CurrentGraphQlUser() user: User,
@Context('dataSources')
{ backendService }: { backendService: BackendService },
): Promise<CaseFile> {
const { caseId, defendantId, ...createFile } = input

this.logger.debug(
`Creating a file for case ${caseId} and defendant ${defendantId}`,
)

return this.auditTrailService.audit(
user.id,
AuditedAction.CREATE_FILE,
backendService.createDefendantCaseFile(caseId, createFile, defendantId),
(file) => file.id,
)
}

@Mutation(() => CaseFile)
createCivilClaimantFile(
@Args('input', { type: () => CreateCivilClaimantFileInput })
input: CreateCivilClaimantFileInput,
@CurrentGraphQlUser() user: User,
@Context('dataSources')
{ backendService }: { backendService: BackendService },
): Promise<CaseFile> {
const { caseId, civilClaimantId, ...createFile } = input

this.logger.debug(
`Creating a file for case ${caseId} and civil claimant ${civilClaimantId}`,
)

return this.auditTrailService.audit(
user.id,
AuditedAction.CREATE_FILE,
backendService.createCivilClaimantCaseFile(
caseId,
createFile,
civilClaimantId,
),
(file) => file.id,
)
}

@Query(() => SignedUrl, { nullable: true })
getSignedUrl(
@Args('input', { type: () => GetSignedUrlInput })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
import type { User } from '@island.is/judicial-system/types'

import { BackendService } from '../backend'
import { CreateCivilClaimantFileInput } from './dto/createCivilClaimantFile.input'
import { CreateDefendantFileInput } from './dto/createDefendantFile.input'
import { CreateFileInput } from './dto/createFile.input'
import { CreatePresignedPostInput } from './dto/createPresignedPost.input'
import { DeleteFileInput } from './dto/deleteFile.input'
Expand Down Expand Up @@ -76,6 +78,58 @@ export class LimitedAccessFileResolver {
)
}

@Mutation(() => CaseFile)
limitedAccessCreateDefendantFile(
@Args('input', { type: () => CreateDefendantFileInput })
input: CreateDefendantFileInput,
@CurrentGraphQlUser() user: User,
@Context('dataSources')
{ backendService }: { backendService: BackendService },
): Promise<CaseFile> {
const { caseId, defendantId, ...createFile } = input

this.logger.debug(
`Creating a file for case ${caseId} and defendant ${defendantId}`,
)

return this.auditTrailService.audit(
user.id,
AuditedAction.CREATE_FILE,
backendService.limitedAccessCreateDefendantCaseFile(
caseId,
createFile,
defendantId,
),
(file) => file.id,
)
}

@Mutation(() => CaseFile)
limitedAccessCreateCivilClaimantFile(
@Args('input', { type: () => CreateCivilClaimantFileInput })
input: CreateCivilClaimantFileInput,
@CurrentGraphQlUser() user: User,
@Context('dataSources')
{ backendService }: { backendService: BackendService },
): Promise<CaseFile> {
const { caseId, civilClaimantId, ...createFile } = input

this.logger.debug(
`Creating a file for case ${caseId} and civil claimant ${civilClaimantId}`,
)

return this.auditTrailService.audit(
user.id,
AuditedAction.CREATE_FILE,
backendService.limitedAccessCreateCivilClaimantCaseFile(
caseId,
createFile,
civilClaimantId,
),
(file) => file.id,
)
}

@Query(() => SignedUrl, { nullable: true })
limitedAccessGetSignedUrl(
@Args('input', { type: () => GetSignedUrlInput })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict'

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(async (t) => {
await queryInterface.addColumn(
'case_file',
'defendant_id',
{
type: Sequelize.UUID,
references: {
model: 'defendant',
key: 'id',
},
allowNull: true,
},
{ transaction: t },
)
await queryInterface.addColumn(
'case_file',
'civil_claimant_id',
{
type: Sequelize.UUID,
references: {
model: 'civil_claimant',
key: 'id',
},
allowNull: true,
},
{ transaction: t },
)
})
},
down: (queryInterface) => {
return queryInterface.sequelize.transaction(async (t) => {
await queryInterface.removeColumn('case_file', 'civil_claimant_id', {
transaction: t,
})
await queryInterface.removeColumn('case_file', 'defendant_id', {
transaction: t,
})
})
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ interface UpdateDateLog {
date?: Date
location?: string
}

export interface UpdateCase
extends Pick<
Case,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ import {
CurrentCase,
} from '../case'
import { MergedCaseExistsGuard } from '../case/guards/mergedCaseExists.guard'
import { CivilClaimantExistsGuard, DefendantExistsGuard } from '../defendant'
import { CreateFileDto } from './dto/createFile.dto'
import { CreatePresignedPostDto } from './dto/createPresignedPost.dto'
import { UpdateFilesDto } from './dto/updateFile.dto'
import { CurrentCaseFile } from './guards/caseFile.decorator'
import { CaseFileExistsGuard } from './guards/caseFileExists.guard'
import { CreateCivilClaimantCaseFileGuard } from './guards/createCivilClaimantCaseFile.guard'
import { CreateDefendantCaseFileGuard } from './guards/createDefendantCaseFile.guard'
import { ViewCaseFileGuard } from './guards/viewCaseFile.guard'
import { DeleteFileResponse } from './models/deleteFile.response'
import { CaseFile } from './models/file.model'
Expand Down Expand Up @@ -126,6 +129,68 @@ export class FileController {
return this.fileService.createCaseFile(theCase, createFile, user)
}

@UseGuards(
RolesGuard,
CaseExistsGuard,
DefendantExistsGuard,
CaseWriteGuard,
CreateDefendantCaseFileGuard,
)
@RolesRules(publicProsecutorStaffRule)
@Post('defendant/:defendantId/file')
@ApiCreatedResponse({
type: CaseFile,
description: 'Creates a new case file connected to a defendant',
})
async createDefendantCaseFile(
@Param('caseId') caseId: string,
@Param('defendantId') defendantId: string,
@CurrentHttpUser() user: User,
@CurrentCase() theCase: Case,
@Body() createFile: CreateFileDto,
): Promise<CaseFile> {
this.logger.debug(
`Creating a file for case ${caseId} for defendant ${defendantId}`,
)

return this.fileService.createCaseFile(
theCase,
{ ...createFile, defendantId },
user,
)
}

@UseGuards(
RolesGuard,
CaseExistsGuard,
CivilClaimantExistsGuard,
CaseWriteGuard,
CreateCivilClaimantCaseFileGuard,
)
@RolesRules() // This endpoint is not used by any role at the moment
@Post('civilClaimant/:civilClaimantId/file')
@ApiCreatedResponse({
type: CaseFile,
description: 'Creates a new case file connected to a civil claimant',
})
async createCivilClaimantCaseFile(
@Param('caseId') caseId: string,
@Param('civilClaimantId') civilClaimantId: string,
@CurrentHttpUser() user: User,
@CurrentCase() theCase: Case,
@Body() createFile: CreateFileDto,
): Promise<CaseFile> {
this.logger.debug(
`Creating a file for case ${caseId} for civil claimant ${civilClaimantId}`,
)

return this.fileService.createCaseFile(
theCase,
{ ...createFile, civilClaimantId },
user,
)
}

@UseGuards(
RolesGuard,
CaseExistsGuard,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ import { SignedUrl } from './models/signedUrl.model'
import { UploadFileToCourtResponse } from './models/uploadFileToCourt.response'
import { fileModuleConfig } from './file.config'

interface CreateFile extends CreateFileDto {
defendantId?: string
civilClaimantId?: string
}

// File keys have the following format:
// <uuid>/<uuid>/<filename>
// As uuid-s have length 36, the filename starts at position 82 in the key.
Expand Down Expand Up @@ -337,7 +342,7 @@ export class FileService {

async createCaseFile(
theCase: Case,
createFile: CreateFileDto,
createFile: CreateFile,
user: User,
): Promise<CaseFile> {
const { key } = createFile
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
} from '@nestjs/common'

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

@Injectable()
export class CreateCivilClaimantCaseFileGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest()

const caseFileCategory: CaseFileCategory = request.body?.category

if (caseFileCategory !== CaseFileCategory.CIVIL_CLAIM) {
throw new ForbiddenException(
`Forbidden for case file category ${caseFileCategory}`,
)
}

return true
}
}
Loading

0 comments on commit 5815544

Please sign in to comment.