From 4f942ac5eca173e4bbfc8e063b4ea3c97f6ed56e Mon Sep 17 00:00:00 2001 From: Amardeep Chimber Date: Fri, 13 Dec 2024 19:04:48 +0000 Subject: [PATCH] MAN-183 - add new api --- .../justice/digital/hmpps/data/DataLoader.kt | 1 + .../generator/OffenderManagerGenerator.kt | 8 ++- .../hmpps/UserLocationIntegrationTest.kt | 51 +++++++++++++++ .../api/controller/UserLocationController.kt | 21 +++++++ .../api/model/sentence/UserOfficeLocation.kt | 25 ++++++++ .../delius/sentence/entity/OffenderManager.kt | 62 ++++++++++++++++++- .../hmpps/service/UserLocationService.kt | 28 +++++++++ 7 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserLocationIntegrationTest.kt create mode 100644 projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/controller/UserLocationController.kt create mode 100644 projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/UserOfficeLocation.kt create mode 100644 projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserLocationService.kt diff --git a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt index cebec85ff..36edfa35e 100644 --- a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt +++ b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/DataLoader.kt @@ -103,6 +103,7 @@ class DataLoader( OffenderManagerGenerator.STAFF_2, OffenderManagerGenerator.STAFF_USER_1, OffenderManagerGenerator.STAFF_USER_2, + OffenderManagerGenerator.STAFF_TEAM, OffenderManagerGenerator.OFFENDER_MANAGER_ACTIVE, OffenderManagerGenerator.OFFENDER_MANAGER_INACTIVE, OffenderManagerGenerator.DEFAULT_LOCATION, diff --git a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderManagerGenerator.kt b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderManagerGenerator.kt index fc886cebf..f78c790b1 100644 --- a/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderManagerGenerator.kt +++ b/projects/manage-supervision-and-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderManagerGenerator.kt @@ -14,10 +14,12 @@ object OffenderManagerGenerator { val STAFF_1 = Staff(IdGenerator.getAndIncrement(), "Peter", "Parker", DEFAULT_PROVIDER, null) val STAFF_2 = Staff(IdGenerator.getAndIncrement(), "Bruce", "Wayne", DEFAULT_PROVIDER, null) - val STAFF_USER_1 = StaffUser(IdGenerator.getAndIncrement(), STAFF_1, "peter-parker") - val STAFF_USER_2 = StaffUser(IdGenerator.getAndIncrement(), STAFF_2, "bwayne") + val STAFF_USER_1 = StaffUser(IdGenerator.getAndIncrement(), STAFF_1, "peter-parker", "peter", surname = "parker") + val STAFF_USER_2 = StaffUser(IdGenerator.getAndIncrement(), STAFF_2, "bwayne", "bruce", surname = "wayne") + val STAFF_TEAM = ContactStaffTeam(StaffTeamLinkId(STAFF_1.id, TEAM)) - val DEFAULT_LOCATION = Location(IdGenerator.getAndIncrement(), "B20", "1 Birmingham Street") + val DEFAULT_LOCATION = + Location(IdGenerator.getAndIncrement(), "B20", "1 Birmingham Street", "Bham House", "1", "Birmingham Street", "Birmingham", "West Midlands", "B20 3BA") val TEAM_OFFICE = TeamOfficeLink(TeamOfficeLinkId(TEAM.id, DEFAULT_LOCATION)) diff --git a/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserLocationIntegrationTest.kt b/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserLocationIntegrationTest.kt new file mode 100644 index 000000000..4e4be9c37 --- /dev/null +++ b/projects/manage-supervision-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/UserLocationIntegrationTest.kt @@ -0,0 +1,51 @@ +package uk.gov.justice.digital.hmpps + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.result.MockMvcResultMatchers +import uk.gov.justice.digital.hmpps.api.model.sentence.Address +import uk.gov.justice.digital.hmpps.api.model.sentence.LocationDetails +import uk.gov.justice.digital.hmpps.api.model.sentence.Name +import uk.gov.justice.digital.hmpps.api.model.sentence.UserOfficeLocation +import uk.gov.justice.digital.hmpps.data.generator.OffenderManagerGenerator.DEFAULT_LOCATION +import uk.gov.justice.digital.hmpps.data.generator.OffenderManagerGenerator.STAFF_USER_1 +import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.contentAsJson +import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withToken + +@AutoConfigureMockMvc +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class UserLocationIntegrationTest { + @Autowired + lateinit var mockMvc: MockMvc + + @Test + fun `unauthorized status returned`() { + mockMvc + .perform(MockMvcRequestBuilders.get("/user/peter-parker/locations")) + .andExpect(MockMvcResultMatchers.status().isUnauthorized) + } + + @Test + fun `get user location`() { + val response = mockMvc.perform(MockMvcRequestBuilders.get("/user/peter-parker/locations").withToken()) + .andDo(print()) + .andExpect(MockMvcResultMatchers.status().isOk) + .andReturn().response.contentAsJson() + + val expected = UserOfficeLocation( + Name(STAFF_USER_1.forename, surname = STAFF_USER_1.surname), + listOf(LocationDetails( + DEFAULT_LOCATION.id, + DEFAULT_LOCATION.description, + Address(DEFAULT_LOCATION.buildingNumber, DEFAULT_LOCATION.streetName, DEFAULT_LOCATION.townCity, DEFAULT_LOCATION.county, DEFAULT_LOCATION.postcode)) + ) + ) + assertEquals(expected, response) + } +} \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/controller/UserLocationController.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/controller/UserLocationController.kt new file mode 100644 index 000000000..c11ab980a --- /dev/null +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/controller/UserLocationController.kt @@ -0,0 +1,21 @@ +package uk.gov.justice.digital.hmpps.api.controller + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController +import uk.gov.justice.digital.hmpps.service.UserLocationService + +@RestController +@Tag(name = "Locations") +@RequestMapping("/user/{username}") +@PreAuthorize("hasRole('PROBATION_API__MANAGE_A_SUPERVISION__CASE_DETAIL')") +class UserLocationController(private val userLocationService: UserLocationService) { + + @GetMapping("/locations") + @Operation(summary = "Display user locations") + fun getUserOfficeLocations(@PathVariable username: String) = userLocationService.getUserOfficeLocations(username) +} \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/UserOfficeLocation.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/UserOfficeLocation.kt new file mode 100644 index 000000000..d8a49f793 --- /dev/null +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/api/model/sentence/UserOfficeLocation.kt @@ -0,0 +1,25 @@ +package uk.gov.justice.digital.hmpps.api.model.sentence + +data class UserOfficeLocation( + val name: Name, + val locations: List +) + +data class Name( + val forename: String, + val middleName: String? = null , + val surname: String +) +data class LocationDetails( + val id: Long, + val description: String, + val address: Address +) + +data class Address( + val buildingNumber: String? = null, + val streetName: String? = null, + val town: String? = null, + val county: String? = null, + val postcode: String? = null +) \ No newline at end of file diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/OffenderManager.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/OffenderManager.kt index c6073acd2..860d990a3 100644 --- a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/OffenderManager.kt +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/sentence/entity/OffenderManager.kt @@ -64,6 +64,7 @@ class Staff( val user: StaffUser? ) + @Entity @Immutable @Table(name = "user_") @@ -79,6 +80,11 @@ class StaffUser( @Column(name = "distinguished_name") val username: String, + val forename: String, + + val forename2: String? = null, + + val surname: String ) { @Transient var email: String? = null @@ -89,6 +95,15 @@ class StaffUser( interface StaffUserRepository : JpaRepository { + @Query( + """ + SELECT u + FROM StaffUser u + WHERE UPPER(u.username) = UPPER(:username) + """ + ) + fun findByUsername(username: String) : StaffUser? + @Query( """ SELECT u.id AS userId, st.id AS staffId, t.id AS teamId, st.provider.id AS providerId, l.id AS locationId @@ -103,8 +118,25 @@ interface StaffUserRepository : JpaRepository { """ ) fun findUserAndLocation(username: String, teamName: String): UserLocation? + + @Query( + """ + SELECT l + FROM StaffUser u + JOIN u.staff st + JOIN ContactStaffTeam cst ON cst.id.staffId = st.id + JOIN Team t ON t.id = cst.id.team.id + JOIN TeamOfficeLink tol ON tol.id.teamId = t.id + JOIN Location l ON l = tol.id.officeLocation + WHERE u.id = :id + """ + ) + fun findUserOfficeLocations(id: Long): List } +fun StaffUserRepository.getUser(username: String) = + findByUsername(username) ?: throw NotFoundException("User", "username", username) + fun StaffUserRepository.getUserAndLocation(username: String, teamName: String) = findUserAndLocation(username, teamName) ?: throw NotFoundException( "User", "username", @@ -160,7 +192,18 @@ class Location( val description: String, - ) + val buildingName: String?, + + val buildingNumber: String?, + + val streetName: String?, + + val townCity: String?, + + val county: String?, + + val postcode: String?, +) @Embeddable class TeamOfficeLinkId( @@ -172,3 +215,20 @@ class TeamOfficeLinkId( val officeLocation: Location ) : Serializable +@Entity +@Immutable +@Table(name = "staff_team") +class ContactStaffTeam( + @Id + val id: StaffTeamLinkId +) + +@Embeddable +class StaffTeamLinkId( + @Column(name = "staff_id") + val staffId: Long, + + @ManyToOne + @JoinColumn(name = "team_id") + val team: Team +) : Serializable diff --git a/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserLocationService.kt b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserLocationService.kt new file mode 100644 index 000000000..65ba129fd --- /dev/null +++ b/projects/manage-supervision-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/UserLocationService.kt @@ -0,0 +1,28 @@ +package uk.gov.justice.digital.hmpps.service + +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.api.model.sentence.Address +import uk.gov.justice.digital.hmpps.api.model.sentence.LocationDetails +import uk.gov.justice.digital.hmpps.api.model.sentence.Name +import uk.gov.justice.digital.hmpps.api.model.sentence.UserOfficeLocation +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.Location +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.StaffUserRepository +import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.getUser + +@Service +class UserLocationService(private val staffUserRepository: StaffUserRepository) { + + fun getUserOfficeLocations(username: String): UserOfficeLocation { + val user = staffUserRepository.getUser(username) + + val userLocations = staffUserRepository.findUserOfficeLocations(user.id) + + return UserOfficeLocation( + Name(user.forename, user.forename2, user.surname), + userLocations.map { it.toLocationDetails()} + ) + } + + fun Location.toLocationDetails(): LocationDetails = + LocationDetails(id, description, Address(buildingNumber, streetName, townCity, county, postcode)) +} \ No newline at end of file