Skip to content

Commit 0e523ba

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

File tree

7 files changed

+116
-21
lines changed

7 files changed

+116
-21
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

+13-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import org.mockito.kotlin.whenever
1414
import org.springframework.http.ResponseEntity
1515
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
1616
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
17+
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
1718
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.BirthplaceUpdateDto
1819
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.DateOfBirthUpdateDto
20+
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.NationalityUpdateDto
1921
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.exception.UnknownCorePersonFieldException
2022
import java.time.LocalDate
2123

@@ -35,7 +37,10 @@ class CorePersonRecordServiceTest {
3537

3638
@BeforeEach
3739
fun beforeEach() {
38-
whenever(prisonApiClient.updateBirthPlaceForWorkingName(PRISONER_NUMBER, TEST_BIRTHPLACE_BODY)).thenReturn(ResponseEntity.noContent().build())
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())
3944
}
4045

4146
@Nested
@@ -45,6 +50,11 @@ class CorePersonRecordServiceTest {
4550
underTest.updateCorePersonRecordField(PRISONER_NUMBER, BirthplaceUpdateDto(TEST_BIRTHPLACE_VALUE))
4651
}
4752

53+
@Test
54+
fun `can update the nationality field`() {
55+
underTest.updateCorePersonRecordField(PRISONER_NUMBER, NationalityUpdateDto(TEST_NATIONALITY_VALUE))
56+
}
57+
4858
@Test
4959
fun `throws an exception if the field type is not supported`() {
5060
assertThrows<UnknownCorePersonFieldException> {
@@ -56,6 +66,8 @@ class CorePersonRecordServiceTest {
5666
private companion object {
5767
const val PRISONER_NUMBER = "A1234AA"
5868
const val TEST_BIRTHPLACE_VALUE = "London"
69+
const val TEST_NATIONALITY_VALUE = "BRIT"
5970
val TEST_BIRTHPLACE_BODY = UpdateBirthPlace("London")
71+
val TEST_NATIONALITY_BODY = UpdateNationality("BRIT")
6072
}
6173
}

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

+23-19
Original file line numberDiff line numberDiff line change
@@ -33,31 +33,36 @@ class PrisonApiMockServer : WireMockServer(8082) {
3333
)
3434
}
3535

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-
),
36+
fun stubUpdateBirthPlaceForWorkingName() {
37+
val endpoint = "birth-place"
38+
stubOffenderEndpoint(endpoint, HttpStatus.NO_CONTENT, PRISONER_NUMBER)
39+
stubOffenderEndpoint(endpoint, HttpStatus.INTERNAL_SERVER_ERROR, PRISONER_NUMBER_THROW_EXCEPTION)
40+
stubOffenderEndpoint(
41+
endpoint,
42+
HttpStatus.NOT_FOUND,
43+
PRISONER_NUMBER_NOT_FOUND,
44+
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
4245
)
4346
}
4447

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-
),
48+
fun stubUpdateNationalityForWorkingName() {
49+
val endpoint = "nationality"
50+
stubOffenderEndpoint(endpoint, HttpStatus.NO_CONTENT, PRISONER_NUMBER)
51+
stubOffenderEndpoint(endpoint, HttpStatus.INTERNAL_SERVER_ERROR, PRISONER_NUMBER_THROW_EXCEPTION)
52+
stubOffenderEndpoint(
53+
endpoint,
54+
HttpStatus.NOT_FOUND,
55+
PRISONER_NUMBER_NOT_FOUND,
56+
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
5157
)
5258
}
5359

54-
fun stubUpdateBirthPlaceForWorkingNameNotFound(prisonerNumber: String = PRISONER_NUMBER_NOT_FOUND) {
60+
private fun stubOffenderEndpoint(endpoint: String, status: HttpStatus, prisonerNumber: String, body: String? = null) {
5561
stubFor(
56-
put(urlPathMatching("/api/offenders/$prisonerNumber/birth-place")).willReturn(
62+
put(urlPathMatching("/api/offenders/$prisonerNumber/$endpoint")).willReturn(
5763
aResponse().withHeader("Content-Type", "application/json")
58-
.withStatus(HttpStatus.NOT_FOUND.value()).withBody(
59-
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
60-
),
64+
.withStatus(status.value())
65+
.withBody(body),
6166
),
6267
)
6368
}
@@ -73,8 +78,7 @@ class PrisonApiExtension : BeforeAllCallback, AfterAllCallback, BeforeEachCallba
7378
override fun beforeEach(context: ExtensionContext) {
7479
prisonApi.resetAll()
7580
prisonApi.stubUpdateBirthPlaceForWorkingName()
76-
prisonApi.stubUpdateBirthPlaceForWorkingNameException()
77-
prisonApi.stubUpdateBirthPlaceForWorkingNameNotFound()
81+
prisonApi.stubUpdateNationalityForWorkingName()
7882
}
7983

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

0 commit comments

Comments
 (0)