Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(j-s): Add punishment type to indictment overview #17198

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
67a2110
feat: add labels for new punishment type in indictment overview
Dec 6, 2024
3858838
feat(j-s): add punishment type ts type and remaining radio buttons
Dec 6, 2024
6676277
chore: nx format:write update dirty files
andes-it Dec 6, 2024
58479b4
feat(j-s): add punishment type ts type and remaining radio buttons
Dec 6, 2024
1d65fb5
fix
Dec 6, 2024
aad341a
chore: nx format:write update dirty files
andes-it Dec 9, 2024
0e43e2c
feat(j-s): add field to relevant models and fetch prop via graphql sc…
thorhildurt Dec 9, 2024
fb4fee0
chore: nx format:write update dirty files
andes-it Dec 9, 2024
dfedd76
refactor(j-s): add punishmentType to defendant update model
thorhildurt Dec 9, 2024
92d77b9
feat(j-s): Update defendant for fmst
thorhildurt Dec 10, 2024
bd07dcd
feat(j-s): Add limited access defendant endpoint
thorhildurt Dec 11, 2024
f30ac12
fix(f-j): add roles rules to limited access defendant controller
thorhildurt Dec 11, 2024
a72ba2a
feat(j-s): add new punishment type field to defendant
thorhildurt Dec 11, 2024
5676d31
chore: nx format:write update dirty files
andes-it Dec 11, 2024
35d42c0
fix(j-s): clean-up after self-review
thorhildurt Dec 11, 2024
86b4ae4
fix(j-s): merge conflict
thorhildurt Dec 11, 2024
caa07e6
Merge branch 'main' into j-s/update-punishment-type-prison-indictment…
thorhildurt Dec 11, 2024
768de8b
fix(j-s): formatting
thorhildurt Dec 11, 2024
aa1ca4f
Merge branch 'j-s/update-punishment-type-prison-indictment-overview' …
thorhildurt Dec 11, 2024
527b33b
fix(j-s): eslint and unsused imports
thorhildurt Dec 11, 2024
f2198b6
chore: nx format:write update dirty files
andes-it Dec 11, 2024
60bd3a0
fix(j-s): linter
thorhildurt Dec 11, 2024
c26df00
refactor(j-s): add tests and small fixes
thorhildurt Dec 11, 2024
96cb79f
gMerge branch 'j-s/update-punishment-type-prison-indictment-overview'…
thorhildurt Dec 11, 2024
ae5dfd5
fix(j-s): type import
thorhildurt Dec 11, 2024
449581e
fix(j-s): remove test case since the controller bypasses the decorato…
thorhildurt Dec 11, 2024
0b994d0
chore: nx format:write update dirty files
andes-it Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Author

Choose a reason for hiding this comment

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

Using the same value for the general update endpoint

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,
}
thorhildurt marked this conversation as resolved.
Show resolved Hide resolved
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)
Copy link
Author

Choose a reason for hiding this comment

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

This will ensure that prison staff can only pass in punishmentType and no other fields of the UpdateDefendantDto

Copy link
Author

Choose a reason for hiding this comment

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

I only added tests that check the Roles rules config but not a test that enforces the logic it applies.

@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> {
thorhildurt marked this conversation as resolved.
Show resolved Hide resolved
this.logger.debug(
`Updating limitedAccess defendant ${defendantId} of case ${caseId}`,
)
return this.defendantService.updateRequestCaseDefendant(
theCase,
defendant,
updateDto,
user,
)
thorhildurt marked this conversation as resolved.
Show resolved Hide resolved
}
}
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
Loading