Skip to content

Commit

Permalink
feat(j-s): Add punishment type to indictment overview (#17198)
Browse files Browse the repository at this point in the history
* feat: add labels for new punishment type in indictment overview

* feat(j-s): add punishment type ts type and remaining radio buttons

* chore: nx format:write update dirty files

* feat(j-s): add punishment type ts type and remaining radio buttons

* fix

* chore: nx format:write update dirty files

* feat(j-s): add field to relevant models and fetch prop via graphql schema

* chore: nx format:write update dirty files

* refactor(j-s): add punishmentType to defendant update model

* feat(j-s): Update defendant for fmst

* feat(j-s): Add limited access defendant endpoint

* fix(f-j): add roles rules to limited access defendant controller

* feat(j-s): add new punishment type field to defendant

* chore: nx format:write update dirty files

* fix(j-s): clean-up after self-review

* fix(j-s): formatting

* fix(j-s): eslint and unsused imports

* chore: nx format:write update dirty files

* fix(j-s): linter

* refactor(j-s): add tests and small fixes

* fix(j-s): type import

* fix(j-s): remove test case since the controller bypasses the decorator logic

* chore: nx format:write update dirty files

* fix(f-s): address feedback

* fix(j-s): update the defendant state in working case

---------

Co-authored-by: Thorhildur Thorleiksdottir <[email protected]>
Co-authored-by: andes-it <[email protected]>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Dec 16, 2024
1 parent c4bc9c4 commit e6a12da
Show file tree
Hide file tree
Showing 22 changed files with 507 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,17 @@ export class BackendService extends DataSource<{ req: Request }> {
)
}

limitedAccessUpdateDefendant(
caseId: string,
defendantId: string,
updateDefendant: unknown,
): Promise<Defendant> {
return this.patch(
`case/${caseId}/limitedAccess/defendant/${defendantId}`,
updateDefendant,
)
}

deleteDefendant(
caseId: string,
defendantId: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import { Module } from '@nestjs/common'

import { CivilClaimantResolver } from './civilClaimant.resolver'
import { DefendantResolver } from './defendant.resolver'
import { LimitedAccessDefendantResolver } from './limitedAccessDefendant.resolver'

@Module({
providers: [DefendantResolver, CivilClaimantResolver],
providers: [
DefendantResolver,
CivilClaimantResolver,
LimitedAccessDefendantResolver,
],
})
export class DefendantModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
DefendantPlea,
DefenderChoice,
Gender,
PunishmentType,
ServiceRequirement,
SubpoenaType,
} from '@island.is/judicial-system/types'
Expand Down Expand Up @@ -114,4 +115,9 @@ export class UpdateDefendantInput {
@IsOptional()
@Field(() => Boolean, { nullable: true })
readonly isSentToPrisonAdmin?: boolean

@Allow()
@IsOptional()
@Field(() => PunishmentType, { nullable: true })
readonly punishmentType?: PunishmentType
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Inject, UseGuards } from '@nestjs/common'
import { Args, Context, Mutation, 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 { UpdateDefendantInput } from './dto/updateDefendant.input'
import { Defendant } from './models/defendant.model'

@UseGuards(JwtGraphQlAuthGuard)
@Resolver()
export class LimitedAccessDefendantResolver {
constructor(
private readonly auditTrailService: AuditTrailService,
@Inject(LOGGER_PROVIDER)
private readonly logger: Logger,
) {}

@Mutation(() => Defendant, { nullable: true })
limitedAccessUpdateDefendant(
@Args('input', { type: () => UpdateDefendantInput })
input: UpdateDefendantInput,
@CurrentGraphQlUser() user: User,
@Context('dataSources')
{ backendService }: { backendService: BackendService },
): Promise<Defendant> {
const { caseId, defendantId, ...updateDefendant } = input
this.logger.debug(
`Updating limitedAccess defendant ${defendantId} for case ${caseId}`,
)

return this.auditTrailService.audit(
user.id,
AuditedAction.UPDATE_DEFENDANT,
backendService.limitedAccessUpdateDefendant(
caseId,
defendantId,
updateDefendant,
),
defendantId,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DefendantPlea,
DefenderChoice,
Gender,
PunishmentType,
ServiceRequirement,
SubpoenaType,
} from '@island.is/judicial-system/types'
Expand All @@ -15,6 +16,7 @@ registerEnumType(DefendantPlea, { name: 'DefendantPlea' })
registerEnumType(ServiceRequirement, { name: 'ServiceRequirement' })
registerEnumType(DefenderChoice, { name: 'DefenderChoice' })
registerEnumType(SubpoenaType, { name: 'SubpoenaType' })
registerEnumType(PunishmentType, { name: 'PunishmentType' })

@ObjectType()
export class Defendant {
Expand Down Expand Up @@ -107,4 +109,7 @@ export class Defendant {

@Field(() => String, { nullable: true })
readonly sentToPrisonAdminDate?: string

@Field(() => PunishmentType, { nullable: true })
readonly punishmentType?: PunishmentType
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict'

module.exports = {
async up(queryInterface, Sequelize) {
return queryInterface.sequelize.transaction((t) =>
Promise.all([
queryInterface.addColumn(
'defendant',
'punishment_type',
{
type: Sequelize.STRING,
allowNull: true,
},
{ transaction: t },
),
]),
)
},
async down(queryInterface, Sequelize) {
return queryInterface.sequelize.transaction((t) =>
queryInterface.removeColumn('defendant', 'punishment_type', {
transaction: t,
}),
)
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { CivilClaimantService } from './civilClaimant.service'
import { DefendantController } from './defendant.controller'
import { DefendantService } from './defendant.service'
import { InternalDefendantController } from './internalDefendant.controller'
import { LimitedAccessDefendantController } from './limitedAccessDefendant.controller'

@Module({
imports: [
Expand All @@ -25,6 +26,7 @@ import { InternalDefendantController } from './internalDefendant.controller'
DefendantController,
InternalDefendantController,
CivilClaimantController,
LimitedAccessDefendantController,
],
providers: [DefendantService, CivilClaimantService],
exports: [DefendantService, CivilClaimantService],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
DefendantPlea,
DefenderChoice,
Gender,
PunishmentType,
ServiceRequirement,
SubpoenaType,
} from '@island.is/judicial-system/types'
Expand Down Expand Up @@ -149,4 +150,9 @@ export class UpdateDefendantDto {
@IsBoolean()
@ApiPropertyOptional({ type: Boolean })
readonly isSentToPrisonAdmin?: boolean

@IsOptional()
@IsEnum(PunishmentType)
@ApiPropertyOptional({ enum: PunishmentType })
readonly punishmentType?: PunishmentType
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { RolesRule, RulesType } from '@island.is/judicial-system/auth'
import { UserRole } from '@island.is/judicial-system/types'

import { UpdateDefendantDto } from '../dto/updateDefendant.dto'

const limitedAccessFields: (keyof UpdateDefendantDto)[] = ['punishmentType']

// Allows prison staff to update a specific set of fields for defendant
export const prisonSystemStaffUpdateRule: RolesRule = {
role: UserRole.PRISON_SYSTEM_STAFF,
type: RulesType.FIELD,
dtoFields: limitedAccessFields,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
Body,
Controller,
Inject,
Param,
Patch,
UseGuards,
} from '@nestjs/common'
import { ApiOkResponse, ApiTags } from '@nestjs/swagger'

import type { Logger } from '@island.is/logging'
import { LOGGER_PROVIDER } from '@island.is/logging'

import {
CurrentHttpUser,
JwtAuthGuard,
RolesGuard,
RolesRules,
} from '@island.is/judicial-system/auth'
import { type User } from '@island.is/judicial-system/types'

import { Case, CaseExistsGuard, CurrentCase } from '../case'
import { UpdateDefendantDto } from './dto/updateDefendant.dto'
import { CurrentDefendant } from './guards/defendant.decorator'
import { DefendantExistsGuard } from './guards/defendantExists.guard'
import { prisonSystemStaffUpdateRule } from './guards/rolesRules'
import { Defendant } from './models/defendant.model'
import { DefendantService } from './defendant.service'

@Controller('api/case/:caseId/limitedAccess/defendant')
@ApiTags('limited access defendant')
@UseGuards(JwtAuthGuard, RolesGuard)
export class LimitedAccessDefendantController {
constructor(
private readonly defendantService: DefendantService,
@Inject(LOGGER_PROVIDER) private readonly logger: Logger,
) {}

@UseGuards(CaseExistsGuard, DefendantExistsGuard)
@RolesRules(prisonSystemStaffUpdateRule)
@Patch(':defendantId')
@ApiOkResponse({
type: Defendant,
description: 'Updates a defendant',
})
updateDefendant(
@Param('caseId') caseId: string,
@Param('defendantId') defendantId: string,
@CurrentHttpUser() user: User,
@CurrentCase() theCase: Case,
@CurrentDefendant() defendant: Defendant,
@Body() updateDto: Pick<UpdateDefendantDto, 'punishmentType'>,
): Promise<Defendant> {
this.logger.debug(
`Updating limitedAccess defendant ${defendantId} of case ${caseId}`,
)
return this.defendantService.updateRequestCaseDefendant(
theCase,
defendant,
updateDto,
user,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
DefendantPlea,
DefenderChoice,
Gender,
PunishmentType,
ServiceRequirement,
SubpoenaType,
} from '@island.is/judicial-system/types'
Expand Down Expand Up @@ -201,6 +202,14 @@ export class Defendant extends Model {
@ApiPropertyOptional({ type: Boolean })
isSentToPrisonAdmin?: boolean

@Column({
type: DataType.ENUM,
allowNull: true,
values: Object.values(PunishmentType),
})
@ApiPropertyOptional({ enum: PunishmentType })
punishmentType?: PunishmentType

@HasMany(() => DefendantEventLog, { foreignKey: 'defendantId' })
@ApiPropertyOptional({ type: () => DefendantEventLog, isArray: true })
eventLogs?: DefendantEventLog[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { CivilClaimantService } from '../civilClaimant.service'
import { DefendantController } from '../defendant.controller'
import { DefendantService } from '../defendant.service'
import { InternalDefendantController } from '../internalDefendant.controller'
import { LimitedAccessDefendantController } from '../limitedAccessDefendant.controller'
import { CivilClaimant } from '../models/civilClaimant.model'
import { Defendant } from '../models/defendant.model'
import { DefendantEventLog } from '../models/defendantEventLog.model'
Expand All @@ -32,6 +33,7 @@ export const createTestingDefendantModule = async () => {
imports: [ConfigModule.forRoot({ load: [sharedAuthModuleConfig] })],
controllers: [
DefendantController,
LimitedAccessDefendantController,
InternalDefendantController,
CivilClaimantController,
],
Expand Down Expand Up @@ -108,6 +110,11 @@ export const createTestingDefendantModule = async () => {
InternalDefendantController,
)

const limitedAccessDefendantController =
defendantModule.get<LimitedAccessDefendantController>(
LimitedAccessDefendantController,
)

const civilClaimantModel = await defendantModule.resolve<
typeof CivilClaimant
>(getModelToken(CivilClaimant))
Expand All @@ -129,6 +136,7 @@ export const createTestingDefendantModule = async () => {
defendantService,
defendantController,
internalDefendantController,
limitedAccessDefendantController,
civilClaimantService,
civilClaimantController,
civilClaimantModel,
Expand Down
Loading

0 comments on commit e6a12da

Please sign in to comment.