Skip to content

Commit e8f24d9

Browse files
committed
CDPS-1074 Connect to edit nationality endpoint in Prison API
1 parent 445dc56 commit e8f24d9

File tree

7 files changed

+118
-30
lines changed

7 files changed

+118
-30
lines changed

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

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestBody
66
import org.springframework.web.service.annotation.HttpExchange
77
import org.springframework.web.service.annotation.PutExchange
88
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
9+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
910

1011
@HttpExchange("/api/offenders")
1112
interface PrisonApiClient {
@@ -14,4 +15,10 @@ interface PrisonApiClient {
1415
@PathVariable offenderNo: String,
1516
@RequestBody updateBirthPlace: UpdateBirthPlace,
1617
): ResponseEntity<Void>
18+
19+
@PutExchange("/{offenderNo}/nationality")
20+
fun updateNationalityForWorkingName(
21+
@PathVariable offenderNo: String,
22+
@RequestBody updateNationality: UpdateNationality,
23+
): ResponseEntity<Void>
1724
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto
2+
3+
import io.swagger.v3.oas.annotations.media.Schema
4+
5+
@Schema(description = "Update to prisoner nationality")
6+
data class UpdateNationality(
7+
@Schema(description = "Nationality code", example = "BRIT", required = true, nullable = true)
8+
val nationality: String?,
9+
)

src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/corepersonrecord/dto/request/CorePersonRecordV1UpdateRequestDto.kt

+23-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import java.time.LocalDate
1616
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.BIRTHPLACE, value = BirthplaceUpdateDto::class),
1717
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.COUNTRY_OF_BIRTH, value = CountryOfBirthUpdateDto::class),
1818
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.DATE_OF_BIRTH, value = DateOfBirthUpdateDto::class),
19+
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.NATIONALITY, value = NationalityUpdateDto::class),
1920
)
2021
@Schema(description = "Core Person Record V1 update request base")
2122
sealed class CorePersonRecordV1UpdateRequestDto {
@@ -26,6 +27,7 @@ sealed class CorePersonRecordV1UpdateRequestDto {
2627
const val BIRTHPLACE = "BIRTHPLACE"
2728
const val COUNTRY_OF_BIRTH = "COUNTRY_OF_BIRTH"
2829
const val DATE_OF_BIRTH = "DATE_OF_BIRTH"
30+
const val NATIONALITY = "NATIONALITY"
2931
}
3032
}
3133

@@ -73,7 +75,7 @@ data class DateOfBirthUpdateDto(
7375
@Schema(description = "Core Person Record V1 country of birth update request")
7476
data class CountryOfBirthUpdateDto(
7577
@Schema(
76-
description = "The new value for the country of brith field",
78+
description = "The new value for the country of birth field",
7779
example = "UK",
7880
required = true,
7981
nullable = true,
@@ -89,3 +91,23 @@ data class CountryOfBirthUpdateDto(
8991
)
9092
override val fieldName: String = COUNTRY_OF_BIRTH
9193
}
94+
95+
@Schema(description = "Core Person Record V1 nationality update request")
96+
data class NationalityUpdateDto(
97+
@Schema(
98+
description = "The new value for the nationality field",
99+
example = "BRIT",
100+
required = true,
101+
nullable = true,
102+
)
103+
override val value: String?,
104+
) : CorePersonRecordV1UpdateRequestDto() {
105+
@Schema(
106+
type = "String",
107+
description = "The field to be updated",
108+
allowableValues = [NATIONALITY],
109+
required = true,
110+
nullable = false,
111+
)
112+
override val fieldName: String = COUNTRY_OF_BIRTH
113+
}

src/main/kotlin/uk/gov/justice/digital/hmpps/personintegrationapi/corepersonrecord/service/CorePersonRecordService.kt

+3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.servi
33
import org.springframework.stereotype.Service
44
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
55
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
6+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
67
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.BirthplaceUpdateDto
78
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.CorePersonRecordV1UpdateRequestDto
9+
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.NationalityUpdateDto
810
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.exception.UnknownCorePersonFieldException
911

1012
@Service
@@ -15,6 +17,7 @@ class CorePersonRecordService(
1517
fun updateCorePersonRecordField(prisonerNumber: String, updateRequestDto: CorePersonRecordV1UpdateRequestDto) {
1618
when (updateRequestDto) {
1719
is BirthplaceUpdateDto -> prisonApiClient.updateBirthPlaceForWorkingName(prisonerNumber, UpdateBirthPlace(updateRequestDto.value))
20+
is NationalityUpdateDto -> prisonApiClient.updateNationalityForWorkingName(prisonerNumber, UpdateNationality(updateRequestDto.value))
1821
else -> throw UnknownCorePersonFieldException("Field '${updateRequestDto.fieldName}' cannot be updated.")
1922
}
2023
}

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

+38
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ class CorePersonRecordV1ResourceIntTest : IntegrationTestBase() {
6868
.exchange()
6969
.expectStatus().isNoContent
7070
}
71+
72+
@Test
73+
fun `can patch core person record nationality by prisoner number`() {
74+
webTestClient.patch().uri("/v1/core-person-record?prisonerNumber=$PRISONER_NUMBER")
75+
.contentType(MediaType.APPLICATION_JSON)
76+
.headers(setAuthorisation(roles = listOf(CorePersonRecordRoleConstants.CORE_PERSON_RECORD_READ_WRITE_ROLE)))
77+
.bodyValue(NATIONALITY_PATCH_REQUEST_BODY)
78+
.exchange()
79+
.expectStatus().isNoContent
80+
}
81+
82+
@Test
83+
fun `patch core person record nationality accepts null value`() {
84+
webTestClient.patch().uri("/v1/core-person-record?prisonerNumber=$PRISONER_NUMBER")
85+
.contentType(MediaType.APPLICATION_JSON)
86+
.headers(setAuthorisation(roles = listOf(CorePersonRecordRoleConstants.CORE_PERSON_RECORD_READ_WRITE_ROLE)))
87+
.bodyValue(NULL_NATIONALITY_PATCH_REQUEST_BODY)
88+
.exchange()
89+
.expectStatus().isNoContent
90+
}
7191
}
7292

7393
@Nested
@@ -191,6 +211,24 @@ class CorePersonRecordV1ResourceIntTest : IntegrationTestBase() {
191211
}
192212
""".trimIndent()
193213

214+
val NATIONALITY_PATCH_REQUEST_BODY =
215+
// language=json
216+
"""
217+
{
218+
"fieldName": "NATIONALITY",
219+
"value": "BRIT"
220+
}
221+
""".trimIndent()
222+
223+
val NULL_NATIONALITY_PATCH_REQUEST_BODY =
224+
// language=json
225+
"""
226+
{
227+
"fieldName": "NATIONALITY",
228+
"value": null
229+
}
230+
""".trimIndent()
231+
194232
val VALID_PATCH_REQUEST_BODY = BIRTHPLACE_PATCH_REQUEST_BODY
195233

196234
val MULTIPART_FILE: MultipartFile = MockMultipartFile(

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

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.service
22

3-
import org.junit.jupiter.api.AfterEach
4-
import org.junit.jupiter.api.BeforeEach
5-
import org.junit.jupiter.api.Nested
6-
import org.junit.jupiter.api.Test
7-
import org.junit.jupiter.api.assertThrows
3+
import org.junit.jupiter.api.*
84
import org.junit.jupiter.api.extension.ExtendWith
95
import org.mockito.InjectMocks
106
import org.mockito.Mock
@@ -14,8 +10,10 @@ import org.mockito.kotlin.whenever
1410
import org.springframework.http.ResponseEntity
1511
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
1612
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
13+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
1714
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.BirthplaceUpdateDto
1815
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.DateOfBirthUpdateDto
16+
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.NationalityUpdateDto
1917
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.exception.UnknownCorePersonFieldException
2018
import java.time.LocalDate
2119

@@ -35,7 +33,10 @@ class CorePersonRecordServiceTest {
3533

3634
@BeforeEach
3735
fun beforeEach() {
38-
whenever(prisonApiClient.updateBirthPlaceForWorkingName(PRISONER_NUMBER, TEST_BIRTHPLACE_BODY)).thenReturn(ResponseEntity.noContent().build())
36+
whenever(prisonApiClient.updateBirthPlaceForWorkingName(PRISONER_NUMBER, TEST_BIRTHPLACE_BODY))
37+
.thenReturn(ResponseEntity.noContent().build())
38+
whenever(prisonApiClient.updateNationalityForWorkingName(PRISONER_NUMBER, TEST_NATIONALITY_BODY))
39+
.thenReturn(ResponseEntity.noContent().build())
3940
}
4041

4142
@Nested
@@ -45,6 +46,11 @@ class CorePersonRecordServiceTest {
4546
underTest.updateCorePersonRecordField(PRISONER_NUMBER, BirthplaceUpdateDto(TEST_BIRTHPLACE_VALUE))
4647
}
4748

49+
@Test
50+
fun `can update the nationality field`() {
51+
underTest.updateCorePersonRecordField(PRISONER_NUMBER, NationalityUpdateDto(TEST_NATIONALITY_VALUE))
52+
}
53+
4854
@Test
4955
fun `throws an exception if the field type is not supported`() {
5056
assertThrows<UnknownCorePersonFieldException> {
@@ -56,6 +62,8 @@ class CorePersonRecordServiceTest {
5662
private companion object {
5763
const val PRISONER_NUMBER = "A1234AA"
5864
const val TEST_BIRTHPLACE_VALUE = "London"
65+
const val TEST_NATIONALITY_VALUE = "BRIT"
5966
val TEST_BIRTHPLACE_BODY = UpdateBirthPlace("London")
67+
val TEST_NATIONALITY_BODY = UpdateNationality("BRIT")
6068
}
6169
}

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

+24-23
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package uk.gov.justice.digital.hmpps.personintegrationapi.integration.wiremock
22

33
import com.github.tomakehurst.wiremock.WireMockServer
4-
import com.github.tomakehurst.wiremock.client.WireMock.aResponse
5-
import com.github.tomakehurst.wiremock.client.WireMock.get
6-
import com.github.tomakehurst.wiremock.client.WireMock.put
7-
import com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching
4+
import com.github.tomakehurst.wiremock.client.WireMock.*
85
import org.junit.jupiter.api.extension.AfterAllCallback
96
import org.junit.jupiter.api.extension.BeforeAllCallback
107
import org.junit.jupiter.api.extension.BeforeEachCallback
@@ -33,31 +30,36 @@ class PrisonApiMockServer : WireMockServer(8082) {
3330
)
3431
}
3532

36-
fun stubUpdateBirthPlaceForWorkingName(prisonerNumber: String = PRISONER_NUMBER) {
37-
stubFor(
38-
put(urlPathMatching("/api/offenders/$prisonerNumber/birth-place")).willReturn(
39-
aResponse().withHeader("Content-Type", "application/json")
40-
.withStatus(HttpStatus.NO_CONTENT.value()),
41-
),
33+
fun stubUpdateBirthPlaceForWorkingName() {
34+
val endpoint = "birth-place"
35+
stubOffenderEndpoint(endpoint, HttpStatus.NO_CONTENT, PRISONER_NUMBER)
36+
stubOffenderEndpoint(endpoint, HttpStatus.INTERNAL_SERVER_ERROR, PRISONER_NUMBER_THROW_EXCEPTION)
37+
stubOffenderEndpoint(
38+
endpoint,
39+
HttpStatus.NOT_FOUND,
40+
PRISONER_NUMBER_NOT_FOUND,
41+
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
4242
)
4343
}
4444

45-
fun stubUpdateBirthPlaceForWorkingNameException(prisonerNumber: String = PRISONER_NUMBER_THROW_EXCEPTION) {
46-
stubFor(
47-
put(urlPathMatching("/api/offenders/$prisonerNumber/birth-place")).willReturn(
48-
aResponse().withHeader("Content-Type", "application/json")
49-
.withStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()),
50-
),
45+
fun stubUpdateNationalityForWorkingName() {
46+
val endpoint = "nationality"
47+
stubOffenderEndpoint(endpoint, HttpStatus.NO_CONTENT, PRISONER_NUMBER)
48+
stubOffenderEndpoint(endpoint, HttpStatus.INTERNAL_SERVER_ERROR, PRISONER_NUMBER_THROW_EXCEPTION)
49+
stubOffenderEndpoint(
50+
endpoint,
51+
HttpStatus.NOT_FOUND,
52+
PRISONER_NUMBER_NOT_FOUND,
53+
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
5154
)
5255
}
5356

54-
fun stubUpdateBirthPlaceForWorkingNameNotFound(prisonerNumber: String = PRISONER_NUMBER_NOT_FOUND) {
57+
private fun stubOffenderEndpoint(endpoint: String, status: HttpStatus, prisonerNumber: String, body: String? = null) {
5558
stubFor(
56-
put(urlPathMatching("/api/offenders/$prisonerNumber/birth-place")).willReturn(
59+
put(urlPathMatching("/api/offenders/$prisonerNumber/${endpoint}")).willReturn(
5760
aResponse().withHeader("Content-Type", "application/json")
58-
.withStatus(HttpStatus.NOT_FOUND.value()).withBody(
59-
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
60-
),
61+
.withStatus(status.value())
62+
.withBody(body),
6163
),
6264
)
6365
}
@@ -73,8 +75,7 @@ class PrisonApiExtension : BeforeAllCallback, AfterAllCallback, BeforeEachCallba
7375
override fun beforeEach(context: ExtensionContext) {
7476
prisonApi.resetAll()
7577
prisonApi.stubUpdateBirthPlaceForWorkingName()
76-
prisonApi.stubUpdateBirthPlaceForWorkingNameException()
77-
prisonApi.stubUpdateBirthPlaceForWorkingNameNotFound()
78+
prisonApi.stubUpdateNationalityForWorkingName()
7879
}
7980

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

0 commit comments

Comments
 (0)