Skip to content

Commit a30f7c1

Browse files
authored
CDPS-1075 Get reference data codes from Prison API (#15)
1 parent f1f6d00 commit a30f7c1

File tree

11 files changed

+183
-23
lines changed

11 files changed

+183
-23
lines changed

src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/common/client/PrisonApiClient.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import org.springframework.web.bind.annotation.PathVariable
55
import org.springframework.web.bind.annotation.RequestBody
66
import org.springframework.web.service.annotation.HttpExchange
77
import org.springframework.web.service.annotation.PutExchange
8-
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
9-
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
8+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request.UpdateBirthPlace
9+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request.UpdateNationality
1010

1111
@HttpExchange("/api/offenders")
1212
interface PrisonApiClient {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client
2+
3+
import org.springframework.http.ResponseEntity
4+
import org.springframework.web.bind.annotation.PathVariable
5+
import org.springframework.web.service.annotation.GetExchange
6+
import org.springframework.web.service.annotation.HttpExchange
7+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.response.ReferenceDataCode
8+
9+
@HttpExchange("/api/reference-domains")
10+
interface ReferenceDataClient {
11+
@GetExchange("/domains/{domain}/all-codes")
12+
fun getReferenceDataByDomain(
13+
@PathVariable domain: String,
14+
): ResponseEntity<List<ReferenceDataCode>>
15+
}

src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/common/client/dto/UpdateBirthPlace.kt src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/common/client/request/UpdateBirthPlace.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto
1+
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request
22

33
import io.swagger.v3.oas.annotations.media.Schema
44

src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/common/client/dto/UpdateNationality.kt src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/common/client/request/UpdateNationality.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto
1+
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request
22

33
import io.swagger.v3.oas.annotations.media.Schema
44

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client.response
2+
3+
data class ReferenceDataCode(
4+
val domain: String,
5+
val code: String,
6+
val description: String,
7+
val activeFlag: String,
8+
val listSeq: Int,
9+
)

src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/common/config/WebClientConfiguration.kt

+11
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.springframework.web.reactive.function.client.support.WebClientAdapter
2121
import org.springframework.web.service.invoker.HttpServiceProxyFactory
2222
import reactor.netty.http.client.HttpClient
2323
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
24+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.ReferenceDataClient
2425
import uk.gov.justice.digital.hmpps.personintegrationapi.config.UserEnhancedOAuth2ClientCredentialGrantRequestConverter
2526
import uk.gov.justice.hmpps.kotlin.auth.healthWebClient
2627
import java.time.Duration
@@ -67,6 +68,16 @@ class WebClientConfiguration(
6768
return client
6869
}
6970

71+
@Bean
72+
@DependsOn("prisonApiWebClient")
73+
fun referenceDataClient(prisonApiWebClient: WebClient): ReferenceDataClient {
74+
val factory =
75+
HttpServiceProxyFactory.builderFor(WebClientAdapter.create(prisonApiWebClient)).build()
76+
val client = factory.createClient(ReferenceDataClient::class.java)
77+
78+
return client
79+
}
80+
7081
private fun authorizedClientManagerUserEnhanced(clients: ClientRegistrationRepository?): OAuth2AuthorizedClientManager {
7182
val service: OAuth2AuthorizedClientService = InMemoryOAuth2AuthorizedClientService(clients)
7283
val manager = AuthorizedClientServiceOAuth2AuthorizedClientManager(clients, service)

src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/corepersonrecord/resource/CorePersonRecordV1Resource.kt

+11-1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ class CorePersonRecordV1Resource(
167167
description = "Unauthorised, requires a valid Oauth2 token",
168168
content = [Content(schema = Schema(implementation = ErrorResponse::class))],
169169
),
170+
ApiResponse(
171+
responseCode = "403",
172+
description = "Missing required role. Requires ${CorePersonRecordRoleConstants.CORE_PERSON_RECORD_READ_ROLE} or ${CorePersonRecordRoleConstants.CORE_PERSON_RECORD_READ_WRITE_ROLE}.",
173+
content = [
174+
Content(
175+
mediaType = MediaType.APPLICATION_JSON_VALUE,
176+
schema = Schema(implementation = ErrorResponse::class),
177+
),
178+
],
179+
),
170180
ApiResponse(
171181
responseCode = "404",
172182
description = "Not found, the reference data domain was not found",
@@ -180,5 +190,5 @@ class CorePersonRecordV1Resource(
180190
description = "The reference data domain",
181191
example = "COUNTRY",
182192
) domain: String,
183-
): Collection<ReferenceDataCodeDto> = listOf()
193+
): ResponseEntity<List<ReferenceDataCodeDto>> = corePersonRecordService.getReferenceDataCodes(domain)
184194
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.service
22

3+
import org.springframework.http.ResponseEntity
34
import org.springframework.stereotype.Service
45
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
5-
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
6-
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
6+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.ReferenceDataClient
7+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request.UpdateBirthPlace
8+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request.UpdateNationality
9+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.dto.ReferenceDataCodeDto
710
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.BirthplaceUpdateDto
811
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.CorePersonRecordV1UpdateRequestDto
912
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.NationalityUpdateDto
@@ -12,13 +15,41 @@ import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.except
1215
@Service
1316
class CorePersonRecordService(
1417
private val prisonApiClient: PrisonApiClient,
18+
private val referenceDataClient: ReferenceDataClient,
1519
) {
1620

1721
fun updateCorePersonRecordField(prisonerNumber: String, updateRequestDto: CorePersonRecordV1UpdateRequestDto) {
1822
when (updateRequestDto) {
19-
is BirthplaceUpdateDto -> prisonApiClient.updateBirthPlaceForWorkingName(prisonerNumber, UpdateBirthPlace(updateRequestDto.value))
20-
is NationalityUpdateDto -> prisonApiClient.updateNationalityForWorkingName(prisonerNumber, UpdateNationality(updateRequestDto.value))
23+
is BirthplaceUpdateDto -> prisonApiClient.updateBirthPlaceForWorkingName(
24+
prisonerNumber,
25+
UpdateBirthPlace(updateRequestDto.value),
26+
)
27+
28+
is NationalityUpdateDto -> prisonApiClient.updateNationalityForWorkingName(
29+
prisonerNumber,
30+
UpdateNationality(updateRequestDto.value),
31+
)
32+
2133
else -> throw UnknownCorePersonFieldException("Field '${updateRequestDto.fieldName}' cannot be updated.")
2234
}
2335
}
36+
37+
fun getReferenceDataCodes(domain: String): ResponseEntity<List<ReferenceDataCodeDto>> {
38+
val response = referenceDataClient.getReferenceDataByDomain(domain)
39+
40+
if (response.statusCode.is2xxSuccessful) {
41+
val mappedResponse = response.body?.map {
42+
ReferenceDataCodeDto(
43+
"${domain}_${it.code}",
44+
it.code,
45+
it.description,
46+
it.listSeq,
47+
it.activeFlag == "Y",
48+
)
49+
}
50+
return ResponseEntity.ok(mappedResponse)
51+
} else {
52+
return ResponseEntity.status(response.statusCode).build()
53+
}
54+
}
2455
}

src/test/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/corepersonrecord/resource/CorePersonRecordV1ResourceIntTest.kt

+9-3
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,22 @@ class CorePersonRecordV1ResourceIntTest : IntegrationTestBase() {
177177
inner class HappyPath {
178178

179179
@Test
180-
fun `can update core person record profile image by prisoner number`() {
180+
fun `can get reference data codes by domain`() {
181+
val domain = "TEST"
181182
val response =
182-
webTestClient.get().uri("/v1/core-person-record/reference-data/domain/$TEST_DOMAIN/codes")
183+
webTestClient.get().uri("/v1/core-person-record/reference-data/domain/$domain/codes")
183184
.headers(setAuthorisation(roles = listOf(CorePersonRecordRoleConstants.CORE_PERSON_RECORD_READ_ROLE)))
184185
.exchange()
185186
.expectStatus().isOk
186187
.expectBodyList(ReferenceDataCodeDto::class.java)
187188
.returnResult().responseBody
188189

189-
assertThat(response).isEqualTo(emptyList<ReferenceDataCodeDto>())
190+
assertThat(response).isEqualTo(
191+
listOf(
192+
ReferenceDataCodeDto("TEST_ONE", "ONE", "Code One", 99, true),
193+
ReferenceDataCodeDto("TEST_TWO", "TWO", "Code Two", 99, true),
194+
),
195+
)
190196
}
191197
}
192198
}

src/test/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/corepersonrecord/service/CorePersonRecordServiceTest.kt

+58-11
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
package uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.service
22

3+
import org.assertj.core.api.Assertions.assertThat
34
import org.junit.jupiter.api.AfterEach
45
import org.junit.jupiter.api.BeforeEach
56
import org.junit.jupiter.api.Nested
67
import org.junit.jupiter.api.Test
78
import org.junit.jupiter.api.assertThrows
89
import org.junit.jupiter.api.extension.ExtendWith
10+
import org.junit.jupiter.params.ParameterizedTest
11+
import org.junit.jupiter.params.provider.ValueSource
912
import org.mockito.InjectMocks
1013
import org.mockito.Mock
1114
import org.mockito.junit.jupiter.MockitoExtension
1215
import org.mockito.kotlin.reset
1316
import org.mockito.kotlin.whenever
17+
import org.springframework.http.HttpStatus
1418
import org.springframework.http.ResponseEntity
1519
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
16-
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
17-
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
20+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.ReferenceDataClient
21+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request.UpdateBirthPlace
22+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.request.UpdateNationality
23+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.response.ReferenceDataCode
24+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.dto.ReferenceDataCodeDto
1825
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.BirthplaceUpdateDto
1926
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.DateOfBirthUpdateDto
2027
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.NationalityUpdateDto
@@ -27,24 +34,27 @@ class CorePersonRecordServiceTest {
2734
@Mock
2835
lateinit var prisonApiClient: PrisonApiClient
2936

37+
@Mock
38+
lateinit var referenceDataClient: ReferenceDataClient
39+
3040
@InjectMocks
3141
lateinit var underTest: CorePersonRecordService
3242

3343
@AfterEach
3444
fun afterEach() {
35-
reset(prisonApiClient)
36-
}
37-
38-
@BeforeEach
39-
fun beforeEach() {
40-
whenever(prisonApiClient.updateBirthPlaceForWorkingName(PRISONER_NUMBER, TEST_BIRTHPLACE_BODY))
41-
.thenReturn(ResponseEntity.noContent().build())
42-
whenever(prisonApiClient.updateNationalityForWorkingName(PRISONER_NUMBER, TEST_NATIONALITY_BODY))
43-
.thenReturn(ResponseEntity.noContent().build())
45+
reset(prisonApiClient, referenceDataClient)
4446
}
4547

4648
@Nested
4749
inner class UpdateCorePersonRecordField {
50+
@BeforeEach
51+
fun beforeEach() {
52+
whenever(prisonApiClient.updateBirthPlaceForWorkingName(PRISONER_NUMBER, TEST_BIRTHPLACE_BODY))
53+
.thenReturn(ResponseEntity.noContent().build())
54+
whenever(prisonApiClient.updateNationalityForWorkingName(PRISONER_NUMBER, TEST_NATIONALITY_BODY))
55+
.thenReturn(ResponseEntity.noContent().build())
56+
}
57+
4858
@Test
4959
fun `can update the birthplace field`() {
5060
underTest.updateCorePersonRecordField(PRISONER_NUMBER, BirthplaceUpdateDto(TEST_BIRTHPLACE_VALUE))
@@ -63,6 +73,43 @@ class CorePersonRecordServiceTest {
6373
}
6474
}
6575

76+
@Nested
77+
inner class ReferenceData {
78+
private val domain = "TEST"
79+
80+
@Test
81+
fun `Can retrieve reference data codes`() {
82+
val referenceCodes = listOf(
83+
ReferenceDataCode(domain, "CODE1", "Code one", "Y", 1),
84+
ReferenceDataCode(domain, "CODE2", "Code two", "Y", 2),
85+
ReferenceDataCode(domain, "CODE3", "Code three", "F", 3),
86+
)
87+
val expected = listOf(
88+
ReferenceDataCodeDto("TEST_CODE1", "CODE1", "Code one", 1, true),
89+
ReferenceDataCodeDto("TEST_CODE2", "CODE2", "Code two", 2, true),
90+
ReferenceDataCodeDto("TEST_CODE3", "CODE3", "Code three", 3, false),
91+
)
92+
whenever(referenceDataClient.getReferenceDataByDomain(domain)).thenReturn(
93+
ResponseEntity.ok(referenceCodes),
94+
)
95+
96+
val response = underTest.getReferenceDataCodes(domain)
97+
assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
98+
assertThat(response.body).isEqualTo(expected)
99+
}
100+
101+
@ParameterizedTest(name = "{0}")
102+
@ValueSource(ints = [400, 401, 403, 404, 422, 500])
103+
fun `Propagates non-2xx status codes`(status: Int) {
104+
whenever(referenceDataClient.getReferenceDataByDomain(domain)).thenReturn(
105+
ResponseEntity.status(status).build(),
106+
)
107+
108+
val response = underTest.getReferenceDataCodes(domain)
109+
assertThat(response.statusCode.value()).isEqualTo(status)
110+
}
111+
}
112+
66113
private companion object {
67114
const val PRISONER_NUMBER = "A1234AA"
68115
const val TEST_BIRTHPLACE_VALUE = "London"

src/test/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/integration/wiremock/PrisonApiMockServer.kt

+31
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@ internal const val PRISON_API_NOT_FOUND_RESPONSE = """
2222
"developerMessage": "Prisoner not found"
2323
}
2424
"""
25+
internal const val PRISON_API_REFERENCE_CODES = """
26+
[
27+
{
28+
"domain": "TEST",
29+
"code": "ONE",
30+
"description": "Code One",
31+
"activeFlag": "Y",
32+
"listSeq": 99,
33+
"subCodes": []
34+
},
35+
{
36+
"domain": "TEST",
37+
"code": "TWO",
38+
"description": "Code Two",
39+
"activeFlag": "Y",
40+
"listSeq": 99,
41+
"subCodes": []
42+
}
43+
]
44+
"""
2545

2646
class PrisonApiMockServer : WireMockServer(8082) {
2747
fun stubHealthPing(status: Int) {
@@ -57,6 +77,16 @@ class PrisonApiMockServer : WireMockServer(8082) {
5777
)
5878
}
5979

80+
fun stubReferenceDataCodes(domain: String = "TEST", body: String = PRISON_API_REFERENCE_CODES) {
81+
stubFor(
82+
get(urlPathMatching("/api/reference-domains/domains/$domain/all-codes")).willReturn(
83+
aResponse().withHeader("Content-Type", "application/json")
84+
.withStatus(HttpStatus.OK.value())
85+
.withBody(body),
86+
),
87+
)
88+
}
89+
6090
private fun stubOffenderEndpoint(endpoint: String, status: HttpStatus, prisonerNumber: String, body: String? = null) {
6191
stubFor(
6292
put(urlPathMatching("/api/offenders/$prisonerNumber/$endpoint")).willReturn(
@@ -79,6 +109,7 @@ class PrisonApiExtension : BeforeAllCallback, AfterAllCallback, BeforeEachCallba
79109
prisonApi.resetAll()
80110
prisonApi.stubUpdateBirthPlaceForWorkingName()
81111
prisonApi.stubUpdateNationalityForWorkingName()
112+
prisonApi.stubReferenceDataCodes()
82113
}
83114

84115
override fun afterAll(context: ExtensionContext): Unit = prisonApi.stop()

0 commit comments

Comments
 (0)