Skip to content

Commit

Permalink
Add CAS1 CRU Management Area to User API Model
Browse files Browse the repository at this point in the history
This commit adds CRU Management Area fields to the ApprovedPremisesUser API Model to support:

* Determining which CRU Management Area the user is in (when retrieved via /profile/v2)
* Logic to determine/set/get override configuration for the user management page

This commit is quite large because i had fix existing test data where entries in the users table were created for CAS1 testing and the CRU Management Area was not set, something which wouldn’t be the case in production (it’s logically mandatory for CAS1 users).
  • Loading branch information
davidatkinsuk committed Sep 30, 2024
1 parent 1f42ebd commit ca8a6f8
Show file tree
Hide file tree
Showing 21 changed files with 224 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,6 @@ data class UserEntity(
) {
fun hasRole(userRole: UserRole) = roles.any { it.role == userRole }
fun hasAnyRole(vararg userRoles: UserRole) = userRoles.any(::hasRole)
fun hasAnyRole(userRoles: List<UserRole>) = userRoles.any(::hasRole)
fun hasQualification(userQualification: UserQualification) =
qualifications.any { it.qualification === userQualification }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer
import org.springframework.stereotype.Component
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApprovedPremisesUser
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApprovedPremisesUserRole
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.NamedId
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ProfileResponse
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ServiceName
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.TemporaryAccommodationUser
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.TemporaryAccommodationUserRole
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.UserSummary
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.UserWithWorkload
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.Cas1CruManagementAreaEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.UserEntity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.UserPermission
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.UserQualification
Expand Down Expand Up @@ -59,21 +61,28 @@ class UserTransformer(
ServiceName.cas2 -> throw RuntimeException("CAS2 not supported")
}

fun transformCas1JpaToApi(jpa: UserEntity) = ApprovedPremisesUser(
id = jpa.id,
deliusUsername = jpa.deliusUsername,
roles = jpa.roles.distinctBy { it.role }.mapNotNull(::transformApprovedPremisesRoleToApi),
email = jpa.email,
name = jpa.name,
telephoneNumber = jpa.telephoneNumber,
isActive = jpa.isActive,
qualifications = jpa.qualifications.map(::transformQualificationToApi),
permissions = jpa.roles.distinctBy { it.role }.mapNotNull(::transformApprovedPremisesRoleToPermissionApi).flatten().distinct(),
region = probationRegionTransformer.transformJpaToApi(jpa.probationRegion),
service = "CAS1",
apArea = jpa.apArea?.let { apAreaTransformer.transformJpaToApi(it) } ?: throw InternalServerErrorProblem("CAS1 user ${jpa.id} should have AP Area Set"),
version = UserEntity.getVersionHashCode((jpa.roles.map { it.role })),
)
fun transformCas1JpaToApi(jpa: UserEntity): ApprovedPremisesUser {
val apArea = jpa.apArea ?: throw InternalServerErrorProblem("CAS1 user ${jpa.id} should have AP Area Set")
val cruManagementArea = jpa.cruManagementArea ?: throw InternalServerErrorProblem("CAS1 user ${jpa.id} should have CRU Management Area Set")
return ApprovedPremisesUser(
id = jpa.id,
deliusUsername = jpa.deliusUsername,
roles = jpa.roles.distinctBy { it.role }.mapNotNull(::transformApprovedPremisesRoleToApi),
email = jpa.email,
name = jpa.name,
telephoneNumber = jpa.telephoneNumber,
isActive = jpa.isActive,
qualifications = jpa.qualifications.map(::transformQualificationToApi),
permissions = jpa.roles.distinctBy { it.role }.map(::transformApprovedPremisesRoleToPermissionApi).flatten().distinct(),
region = probationRegionTransformer.transformJpaToApi(jpa.probationRegion),
service = "CAS1",
apArea = apArea.let { apAreaTransformer.transformJpaToApi(it) },
cruManagementArea = cruManagementArea.toNamedId(),
cruManagementAreaDefault = apArea.defaultCruManagementArea.toNamedId(),
cruManagementAreaOverride = jpa.cruManagementAreaOverride?.toNamedId(),
version = UserEntity.getVersionHashCode((jpa.roles.map { it.role })),
)
}

fun transformCas3JpatoApi(jpa: UserEntity) = TemporaryAccommodationUser(
id = jpa.id,
Expand All @@ -88,6 +97,8 @@ class UserTransformer(
service = "CAS3",
)

fun Cas1CruManagementAreaEntity.toNamedId() = NamedId(id, name)

fun transformProfileResponseToApi(userName: String, userResponse: UserService.GetUserResponse, xServiceName: ServiceName): ProfileResponse {
return when (userResponse) {
UserService.GetUserResponse.StaffRecordNotFound -> ProfileResponse(userName, ProfileResponse.LoadError.staffRecordNotFound)
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/static/_shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3091,12 +3091,26 @@ components:
$ref: '#/components/schemas/ApprovedPremisesUserPermission'
apArea:
$ref: '#/components/schemas/ApArea'
cruManagementArea:
description: CRU Management Area to use. This will be the same as cruManagementAreaDefault unless cruManagementAreaOverride is defined
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaDefault:
description: The CRU Management Area used if no override is defined. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaOverride:
description: The CRU Management Area manually set on this user. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
version:
type: integer
required:
- qualifications
- roles
- apArea
- cruManagementArea
- cruManagementAreaDefault
UserRolesAndQualifications:
type: object
properties:
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/static/codegen/built-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7569,12 +7569,26 @@ components:
$ref: '#/components/schemas/ApprovedPremisesUserPermission'
apArea:
$ref: '#/components/schemas/ApArea'
cruManagementArea:
description: CRU Management Area to use. This will be the same as cruManagementAreaDefault unless cruManagementAreaOverride is defined
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaDefault:
description: The CRU Management Area used if no override is defined. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaOverride:
description: The CRU Management Area manually set on this user. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
version:
type: integer
required:
- qualifications
- roles
- apArea
- cruManagementArea
- cruManagementAreaDefault
UserRolesAndQualifications:
type: object
properties:
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/static/codegen/built-cas1-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4230,12 +4230,26 @@ components:
$ref: '#/components/schemas/ApprovedPremisesUserPermission'
apArea:
$ref: '#/components/schemas/ApArea'
cruManagementArea:
description: CRU Management Area to use. This will be the same as cruManagementAreaDefault unless cruManagementAreaOverride is defined
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaDefault:
description: The CRU Management Area used if no override is defined. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaOverride:
description: The CRU Management Area manually set on this user. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
version:
type: integer
required:
- qualifications
- roles
- apArea
- cruManagementArea
- cruManagementAreaDefault
UserRolesAndQualifications:
type: object
properties:
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/static/codegen/built-cas2-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3682,12 +3682,26 @@ components:
$ref: '#/components/schemas/ApprovedPremisesUserPermission'
apArea:
$ref: '#/components/schemas/ApArea'
cruManagementArea:
description: CRU Management Area to use. This will be the same as cruManagementAreaDefault unless cruManagementAreaOverride is defined
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaDefault:
description: The CRU Management Area used if no override is defined. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaOverride:
description: The CRU Management Area manually set on this user. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
version:
type: integer
required:
- qualifications
- roles
- apArea
- cruManagementArea
- cruManagementAreaDefault
UserRolesAndQualifications:
type: object
properties:
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/static/codegen/built-cas3-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3182,12 +3182,26 @@ components:
$ref: '#/components/schemas/ApprovedPremisesUserPermission'
apArea:
$ref: '#/components/schemas/ApArea'
cruManagementArea:
description: CRU Management Area to use. This will be the same as cruManagementAreaDefault unless cruManagementAreaOverride is defined
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaDefault:
description: The CRU Management Area used if no override is defined. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
cruManagementAreaOverride:
description: The CRU Management Area manually set on this user. This is provided to support the user configuration page.
allOf:
- $ref: "#/components/schemas/NamedId"
version:
type: integer
required:
- qualifications
- roles
- apArea
- cruManagementArea
- cruManagementAreaDefault
UserRolesAndQualifications:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ class UserEntityFactory : Factory<UserEntity> {
this.apArea = { apArea }
}

fun withCruManagementArea(cruManagementArea: Cas1CruManagementAreaEntity?) = apply {
this.cruManagementArea = { cruManagementArea }
}

fun withCruManagementAreaOverride(cruManagementAreaOverride: Cas1CruManagementAreaEntity?) = apply {
this.cruManagementAreaOverride = { cruManagementAreaOverride }
}

fun withTeamCodes(teamCodes: List<String>) = apply {
this.teamCodes = { teamCodes }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.github.bluegroundltd.kfactory.Yielded
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApArea
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApprovedPremisesUser
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApprovedPremisesUserRole
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.NamedId
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ProbationRegion
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.UserQualification
import uk.gov.justice.digital.hmpps.approvedpremisesapi.util.randomStringMultiCaseWithNumbers
Expand All @@ -21,6 +22,13 @@ class ApprovedPremisesUserFactory : Factory<ApprovedPremisesUser> {
randomStringMultiCaseWithNumbers(20),
)
}
private var cruManagementArea: Yielded<NamedId> = {
NamedId(UUID.randomUUID(), randomStringUpperCase(6))
}
private var cruManagementAreaDefault: Yielded<NamedId> = {
NamedId(UUID.randomUUID(), randomStringUpperCase(6))
}
private var cruManagementAreaOverride: Yielded<NamedId?> = { null }
private var service: Yielded<String> = { randomStringMultiCaseWithNumbers(10) }
private var id: Yielded<UUID> = { UUID.randomUUID() }
private var name: Yielded<String> = { randomStringMultiCaseWithNumbers(20) }
Expand Down Expand Up @@ -83,6 +91,9 @@ class ApprovedPremisesUserFactory : Factory<ApprovedPremisesUser> {
qualifications = this.qualifications(),
roles = this.roles(),
apArea = this.apArea(),
cruManagementArea = this.cruManagementArea(),
cruManagementAreaDefault = this.cruManagementAreaDefault(),
cruManagementAreaOverride = this.cruManagementAreaOverride(),
service = this.service(),
id = this.id(),
name = this.name(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApArea
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApplicationTimeline
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApprovedPremisesApplicationStatus
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ApprovedPremisesUser
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.NamedId
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.PersonalTimeline
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ProbationRegion
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.TimelineEvent
Expand Down Expand Up @@ -133,6 +134,8 @@ class PersonalTimelineTest : IntegrationTestBase() {
inmateDetail = inmateDetails,
)

val apArea = userEntity.apArea!!

webTestClient.get()
.uri("/people/${offenderDetails.otherIds.crn}/timeline")
.header("Authorization", "Bearer $jwt")
Expand All @@ -155,9 +158,9 @@ class PersonalTimelineTest : IntegrationTestBase() {
roles = emptyList(),
permissions = emptyList(),
apArea = ApArea(
id = userEntity.apArea!!.id,
identifier = userEntity.apArea!!.identifier,
name = userEntity.apArea!!.name,
id = apArea.id,
identifier = apArea.identifier,
name = apArea.name,
),
service = "CAS1",
id = userEntity.id,
Expand All @@ -171,6 +174,15 @@ class PersonalTimelineTest : IntegrationTestBase() {
telephoneNumber = userEntity.telephoneNumber,
isActive = userEntity.isActive,
version = 993,
cruManagementArea = NamedId(
id = userEntity.cruManagementArea!!.id,
name = userEntity.cruManagementArea!!.name,
),
cruManagementAreaDefault = NamedId(
id = apArea.defaultCruManagementArea.id,
name = apArea.defaultCruManagementArea.name,
),
cruManagementAreaOverride = null,
),
timelineEvents = listOf(
TimelineEvent(
Expand Down Expand Up @@ -259,6 +271,8 @@ class PersonalTimelineTest : IntegrationTestBase() {
inmateDetail = null,
)

val apArea = userEntity.apArea!!

webTestClient.get()
.uri("/people/${offenderDetails.otherIds.crn}/timeline")
.header("Authorization", "Bearer $jwt")
Expand All @@ -281,9 +295,9 @@ class PersonalTimelineTest : IntegrationTestBase() {
roles = emptyList(),
permissions = emptyList(),
apArea = ApArea(
id = userEntity.apArea!!.id,
identifier = userEntity.apArea!!.identifier,
name = userEntity.apArea!!.name,
id = apArea.id,
identifier = apArea.identifier,
name = apArea.name,
),
service = "CAS1",
id = userEntity.id,
Expand All @@ -297,6 +311,15 @@ class PersonalTimelineTest : IntegrationTestBase() {
telephoneNumber = userEntity.telephoneNumber,
isActive = userEntity.isActive,
version = 993,
cruManagementArea = NamedId(
id = userEntity.cruManagementArea!!.id,
name = userEntity.cruManagementArea!!.name,
),
cruManagementAreaDefault = NamedId(
id = apArea.defaultCruManagementArea.id,
name = apArea.defaultCruManagementArea.name,
),
cruManagementAreaOverride = null,
),
timelineEvents = listOf(
TimelineEvent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.WithdrawPlacem
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.WithdrawPlacementRequestReason
import uk.gov.justice.digital.hmpps.approvedpremisesapi.factory.CaseAccessFactory
import uk.gov.justice.digital.hmpps.approvedpremisesapi.factory.PersonRisksFactory
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given a CAS1 CRU Management Area`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given a Placement Application`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given a Placement Request`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given a Probation Region`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given a User`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given an AP Area`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given an Application`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given an CAS1 CRU Management Area`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.givens.`Given an Offender`
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.httpmocks.ApDeliusContext_addResponseToUserAccessCall
import uk.gov.justice.digital.hmpps.approvedpremisesapi.integration.httpmocks.CommunityAPI_mockOffenderUserAccessCall
Expand Down Expand Up @@ -652,8 +652,8 @@ class PlacementRequestsTest : IntegrationTestBase() {
`Given a User`(roles = listOf(UserRole.CAS1_WORKFLOW_MANAGER)) { user, jwt ->
`Given an Offender` { offenderDetails, inmateDetails ->

val cruArea1 = `Given an CAS1 CRU Management Area`()
val cruArea2 = `Given an CAS1 CRU Management Area`()
val cruArea1 = `Given a CAS1 CRU Management Area`()
val cruArea2 = `Given a CAS1 CRU Management Area`()

createPlacementRequest(offenderDetails, user, cruManagementArea = cruArea1)
val placementRequestA1 = createPlacementRequest(offenderDetails, user, cruManagementArea = cruArea2)
Expand Down Expand Up @@ -783,8 +783,8 @@ class PlacementRequestsTest : IntegrationTestBase() {
`Given an Offender` { offender1Details, inmate1Details ->
`Given an Offender` { offender2Details, _ ->

val cruArea1 = `Given an CAS1 CRU Management Area`()
val cruArea2 = `Given an CAS1 CRU Management Area`()
val cruArea1 = `Given a CAS1 CRU Management Area`()
val cruArea2 = `Given a CAS1 CRU Management Area`()

createPlacementRequest(offender1Details, user, expectedArrival = LocalDate.of(2022, 1, 1), tier = RiskTierLevel.a2)
createPlacementRequest(offender1Details, user, expectedArrival = LocalDate.of(2022, 1, 5), tier = RiskTierLevel.a1)
Expand Down
Loading

0 comments on commit ca8a6f8

Please sign in to comment.