Skip to content

Commit cbb862c

Browse files
authored
PI-2161: Created post endpoint for /nomis-case-note/{crn} (#3828)
* PI-2161: Created post endpoint for /nomis-case-note/{crn} * PI-2161: Moved staff code generator to common library
1 parent 4987200 commit cbb862c

File tree

54 files changed

+1138
-371
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1138
-371
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package uk.gov.justice.digital.hmpps.exception
2+
3+
open class InvalidRequestException(message: String) : RuntimeException(message) {
4+
constructor(
5+
fieldName: String,
6+
value: Any
7+
) : this("Invalid $fieldName of $value sent in payload")
8+
}

libs/oauth-server/src/main/kotlin/uk/gov/justice/digital/hmpps/advice/ControllerAdvice.kt

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package uk.gov.justice.digital.hmpps.advice
22

3-
import org.springframework.http.HttpStatus.BAD_REQUEST
4-
import org.springframework.http.HttpStatus.CONFLICT
5-
import org.springframework.http.HttpStatus.NOT_FOUND
3+
import org.springframework.http.HttpStatus.*
64
import org.springframework.http.ResponseEntity
75
import org.springframework.web.bind.MethodArgumentNotValidException
86
import org.springframework.web.bind.annotation.ExceptionHandler
97
import org.springframework.web.bind.annotation.RestControllerAdvice
108
import uk.gov.justice.digital.hmpps.exception.ConflictException
9+
import uk.gov.justice.digital.hmpps.exception.InvalidRequestException
1110
import uk.gov.justice.digital.hmpps.exception.NotFoundException
1211

1312
@RestControllerAdvice(basePackages = ["uk.gov.justice.digital.hmpps"])
@@ -32,4 +31,9 @@ class ControllerAdvice {
3231
fields = e.bindingResult.fieldErrors.map { FieldError(it.code, it.defaultMessage, it.field) }
3332
)
3433
)
34+
35+
@ExceptionHandler(InvalidRequestException::class)
36+
fun handleInvalidRequest(e: InvalidRequestException) = ResponseEntity
37+
.status(BAD_REQUEST)
38+
.body(ErrorResponse(status = BAD_REQUEST.value(), message = e.message))
3539
}

libs/prison-staff/build.gradle.kts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import uk.gov.justice.digital.hmpps.extensions.ClassPathExtension
2+
3+
dependencies {
4+
compileOnly("org.springframework.boot:spring-boot-starter-data-jpa")
5+
implementation(project(":libs:commons"))
6+
implementation("org.springframework.boot:spring-boot-starter-web")
7+
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
8+
implementation("org.springframework.boot:spring-boot-starter-validation")
9+
testImplementation("org.springframework.boot:spring-boot-starter-test")
10+
testImplementation("org.springframework.boot:spring-boot-starter-data-jpa")
11+
testImplementation(libs.bundles.mockito)
12+
}
13+
14+
configure<ClassPathExtension> {
15+
jacocoExclusions = listOf(
16+
"**/exception/**",
17+
"**/config/**",
18+
"**/entity**",
19+
"**/logging/**"
20+
)
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package uk.gov.justice.digital.hmpps.entity
2+
3+
import jakarta.persistence.*
4+
import org.springframework.data.annotation.CreatedBy
5+
import org.springframework.data.annotation.CreatedDate
6+
import org.springframework.data.annotation.LastModifiedBy
7+
import org.springframework.data.annotation.LastModifiedDate
8+
import org.springframework.data.jpa.domain.support.AuditingEntityListener
9+
import java.time.ZonedDateTime
10+
11+
@EntityListeners(AuditingEntityListener::class)
12+
@Entity(name = "PrisonStaff")
13+
@Table(name = "staff")
14+
class PrisonStaff(
15+
16+
@Id
17+
@Column(name = "staff_id")
18+
@SequenceGenerator(name = "staff_id_seq", sequenceName = "staff_id_seq", allocationSize = 1)
19+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "staff_id_seq")
20+
val id: Long = 0,
21+
22+
@Column(name = "forename")
23+
val forename: String,
24+
25+
@Column(name = "surname")
26+
val surname: String,
27+
28+
@Column(name = "officer_code", columnDefinition = "char(7)")
29+
val code: String,
30+
31+
@Column(name = "probation_area_id")
32+
val probationAreaId: Long,
33+
34+
@Column(name = "start_date", updatable = false)
35+
val startDate: ZonedDateTime = ZonedDateTime.now(),
36+
37+
@CreatedDate
38+
@Column(name = "created_datetime", updatable = false)
39+
val createdDateTime: ZonedDateTime = ZonedDateTime.now(),
40+
41+
@LastModifiedDate
42+
@Column(name = "last_updated_datetime")
43+
val lastModifiedDate: ZonedDateTime = ZonedDateTime.now(),
44+
45+
@Column(name = "private", columnDefinition = "NUMBER", nullable = false)
46+
var privateStaff: Boolean = false,
47+
48+
@CreatedBy
49+
@Column(name = "created_by_user_id", updatable = false)
50+
var createdByUserId: Long = 0,
51+
52+
@LastModifiedBy
53+
@Column(name = "last_updated_user_id")
54+
var lastModifiedUserId: Long = 0,
55+
56+
@Version
57+
@Column(name = "row_version")
58+
val version: Long = 0
59+
) {
60+
fun isUnallocated() = code.endsWith("U")
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package uk.gov.justice.digital.hmpps.entity
2+
3+
import jakarta.persistence.*
4+
import org.springframework.data.annotation.CreatedBy
5+
import org.springframework.data.annotation.CreatedDate
6+
import org.springframework.data.annotation.LastModifiedBy
7+
import org.springframework.data.annotation.LastModifiedDate
8+
import org.springframework.data.jpa.domain.support.AuditingEntityListener
9+
import java.io.Serializable
10+
import java.time.ZonedDateTime
11+
12+
@EntityListeners(AuditingEntityListener::class)
13+
@Entity
14+
@Table(name = "staff_team")
15+
@IdClass(StaffTeamId::class)
16+
class PrisonStaffTeam(
17+
18+
@Id
19+
@Column(name = "staff_id")
20+
val staffId: Long,
21+
22+
@Id
23+
@Column(name = "team_id")
24+
val teamId: Long,
25+
26+
@CreatedBy
27+
@Column(name = "created_by_user_id", updatable = false)
28+
var createdByUserId: Long = 0,
29+
30+
@LastModifiedBy
31+
@Column(name = "last_updated_user_id")
32+
val lastModifiedUserId: Long = 0,
33+
34+
@CreatedDate
35+
@Column(name = "created_datetime", updatable = false)
36+
val createdDateTime: ZonedDateTime = ZonedDateTime.now(),
37+
38+
@LastModifiedDate
39+
@Column(name = "last_updated_datetime")
40+
val lastModifiedDate: ZonedDateTime = ZonedDateTime.now(),
41+
42+
@Version
43+
@Column(name = "row_version")
44+
val version: Long = 0
45+
)
46+
47+
data class StaffTeamId(val staffId: Long = 0, val teamId: Long = 0) : Serializable
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package uk.gov.justice.digital.hmpps.entity
2+
3+
import jakarta.persistence.Column
4+
import jakarta.persistence.Entity
5+
import jakarta.persistence.Id
6+
import jakarta.persistence.Table
7+
import org.hibernate.annotations.Immutable
8+
9+
@Immutable
10+
@Entity(name = "PrisonTeam")
11+
@Table(name = "team")
12+
class PrisonTeam(
13+
14+
@Id
15+
@Column(name = "team_id")
16+
val id: Long,
17+
18+
@Column(name = "code", columnDefinition = "char(6)")
19+
val code: String
20+
21+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package uk.gov.justice.digital.hmpps.entity
2+
3+
import jakarta.persistence.*
4+
import org.hibernate.annotations.Immutable
5+
6+
@Immutable
7+
@Entity(name = "Provider")
8+
@Table(name = "probation_area")
9+
class Provider(
10+
@Id
11+
@Column(name = "probation_area_id")
12+
val id: Long,
13+
14+
@Column(name = "code", columnDefinition = "char(3)")
15+
val code: String,
16+
17+
@OneToOne
18+
@JoinColumn(
19+
name = "institution_id",
20+
referencedColumnName = "institution_id",
21+
updatable = false
22+
)
23+
val institution: Prison? = null
24+
)
25+
26+
@Immutable
27+
@Entity(name = "Prison")
28+
@Table(name = "r_institution")
29+
class Prison(
30+
@Id
31+
@Column(name = "institution_id")
32+
val id: Long,
33+
34+
@Column(name = "nomis_cde_code")
35+
val nomisCode: String
36+
37+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package uk.gov.justice.digital.hmpps.exceptions
2+
3+
class InvalidEstablishmentCodeException(establishmentCode: String) :
4+
RuntimeException("Invalid establishment: $establishmentCode")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package uk.gov.justice.digital.hmpps.exceptions
2+
3+
class StaffCodeExhaustedException(code: String) : RuntimeException("Officer codes exhausted for: $code")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package uk.gov.justice.digital.hmpps.model
2+
3+
import jakarta.validation.constraints.NotBlank
4+
5+
data class StaffName(@NotBlank val forename: String, @NotBlank val surname: String)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package uk.gov.justice.digital.hmpps.repository
2+
3+
import org.springframework.data.jpa.repository.JpaRepository
4+
import uk.gov.justice.digital.hmpps.entity.Provider
5+
6+
interface PrisonProbationAreaRepository : JpaRepository<Provider, Long> {
7+
fun findByInstitutionNomisCode(nomisCode: String): Provider?
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package uk.gov.justice.digital.hmpps.repository
2+
3+
import org.springframework.data.jpa.repository.JpaRepository
4+
import org.springframework.data.jpa.repository.Query
5+
import uk.gov.justice.digital.hmpps.entity.PrisonStaff
6+
import uk.gov.justice.digital.hmpps.exception.NotFoundException
7+
8+
interface PrisonStaffRepository : JpaRepository<PrisonStaff, Long> {
9+
fun findTopByProbationAreaIdAndForenameIgnoreCaseAndSurnameIgnoreCase(
10+
probationAreaId: Long,
11+
forename: String,
12+
surname: String
13+
): PrisonStaff?
14+
15+
@Query(
16+
"""
17+
select officer_code from staff
18+
where regexp_like(officer_code, ?1, 'i')
19+
order by officer_code desc
20+
fetch next 1 rows only
21+
""",
22+
nativeQuery = true
23+
)
24+
fun getLatestStaffReference(regex: String): String?
25+
26+
fun findByCode(code: String): PrisonStaff?
27+
}
28+
29+
fun PrisonStaffRepository.getByCode(code: String) =
30+
findByCode(code) ?: throw NotFoundException("Staff", "code", code)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package uk.gov.justice.digital.hmpps.repository
2+
3+
import org.springframework.data.jpa.repository.JpaRepository
4+
import uk.gov.justice.digital.hmpps.entity.PrisonStaffTeam
5+
import uk.gov.justice.digital.hmpps.entity.StaffTeamId
6+
7+
interface PrisonStaffTeamRepository : JpaRepository<PrisonStaffTeam, StaffTeamId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package uk.gov.justice.digital.hmpps.repository
2+
3+
import org.springframework.data.jpa.repository.JpaRepository
4+
import uk.gov.justice.digital.hmpps.entity.PrisonTeam
5+
6+
interface PrisonTeamRepository : JpaRepository<PrisonTeam, Long> {
7+
fun findByCode(code: String): PrisonTeam?
8+
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
package uk.gov.justice.digital.hmpps.integrations.delius.service
1+
package uk.gov.justice.digital.hmpps.service
22

33
import org.springframework.stereotype.Service
4+
import uk.gov.justice.digital.hmpps.entity.PrisonStaff
45
import uk.gov.justice.digital.hmpps.exception.NotFoundException
56
import uk.gov.justice.digital.hmpps.exceptions.InvalidEstablishmentCodeException
6-
import uk.gov.justice.digital.hmpps.integrations.delius.entity.ProbationArea
7-
import uk.gov.justice.digital.hmpps.integrations.delius.entity.Staff
8-
import uk.gov.justice.digital.hmpps.integrations.delius.entity.Team
9-
import uk.gov.justice.digital.hmpps.integrations.delius.model.StaffName
10-
import uk.gov.justice.digital.hmpps.integrations.delius.repository.ProbationAreaRepository
11-
import uk.gov.justice.digital.hmpps.integrations.delius.repository.TeamRepository
7+
import uk.gov.justice.digital.hmpps.model.StaffName
8+
import uk.gov.justice.digital.hmpps.repository.PrisonProbationAreaRepository
9+
import uk.gov.justice.digital.hmpps.repository.PrisonTeamRepository
1210
import uk.gov.justice.digital.hmpps.retry.retry
11+
import java.time.ZonedDateTime
1312

1413
@Service
1514
class AssignmentService(
16-
private val probationAreaRepository: ProbationAreaRepository,
17-
private val teamRepository: TeamRepository,
15+
private val probationAreaRepository: PrisonProbationAreaRepository,
16+
private val teamRepository: PrisonTeamRepository,
1817
private val staffService: StaffService
1918
) {
2019

@@ -27,14 +26,20 @@ class AssignmentService(
2726
)
2827
val team = teamRepository.findByCode("${pa.code}CSN")
2928
?: throw NotFoundException("Team", "code", "${pa.code}CSN")
30-
val staff = getStaff(pa, team, staffName)
29+
val staff = getStaff(pa.id, pa.code, team.id, staffName)
3130
return Triple(pa.id, team.id, staff.id)
3231
}
3332

34-
private fun getStaff(probationArea: ProbationArea, team: Team, staffName: StaffName): Staff {
35-
val findStaff = { staffService.findStaff(probationArea.id, staffName) }
33+
fun getStaff(
34+
probationAreaId: Long,
35+
probationAreaCode: String,
36+
teamId: Long,
37+
staffName: StaffName,
38+
allocationDate: ZonedDateTime? = null
39+
): PrisonStaff {
40+
val findStaff = { staffService.findStaff(probationAreaId, staffName) }
3641
return retry(3) {
37-
findStaff() ?: staffService.create(probationArea, team, staffName)
42+
findStaff() ?: staffService.create(probationAreaId, probationAreaCode, teamId, staffName, allocationDate)
3843
}
3944
}
4045
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
package uk.gov.justice.digital.hmpps.integrations.delius.service
1+
package uk.gov.justice.digital.hmpps.service
22

33
import org.springframework.stereotype.Component
44
import uk.gov.justice.digital.hmpps.exceptions.StaffCodeExhaustedException
5-
import uk.gov.justice.digital.hmpps.integrations.delius.repository.StaffRepository
5+
import uk.gov.justice.digital.hmpps.repository.PrisonStaffRepository
66

77
@Component
8-
class OfficerCodeGenerator(private val staffRepository: StaffRepository) {
8+
class OfficerCodeGenerator(private val staffRepository: PrisonStaffRepository) {
99
private val alphabet = ('A'..'Z').toList()
1010

1111
fun generateFor(probationAreaCode: String, index: Int = 0): String {

0 commit comments

Comments
 (0)