Skip to content

Commit

Permalink
CDPS-1074 Connect to edit nationality endpoint in Prison API (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
scottrowley authored Dec 16, 2024
1 parent 445dc56 commit f1f6d00
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.service.annotation.HttpExchange
import org.springframework.web.service.annotation.PutExchange
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality

@HttpExchange("/api/offenders")
interface PrisonApiClient {
Expand All @@ -14,4 +15,10 @@ interface PrisonApiClient {
@PathVariable offenderNo: String,
@RequestBody updateBirthPlace: UpdateBirthPlace,
): ResponseEntity<Void>

@PutExchange("/{offenderNo}/nationality")
fun updateNationalityForWorkingName(
@PathVariable offenderNo: String,
@RequestBody updateNationality: UpdateNationality,
): ResponseEntity<Void>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto

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

@Schema(description = "Update to prisoner nationality")
data class UpdateNationality(
@Schema(description = "Nationality code", example = "BRIT", required = true, nullable = true)
val nationality: String?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import java.time.LocalDate
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.BIRTHPLACE, value = BirthplaceUpdateDto::class),
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.COUNTRY_OF_BIRTH, value = CountryOfBirthUpdateDto::class),
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.DATE_OF_BIRTH, value = DateOfBirthUpdateDto::class),
JsonSubTypes.Type(name = CorePersonRecordV1UpdateRequestDto.NATIONALITY, value = NationalityUpdateDto::class),
)
@Schema(description = "Core Person Record V1 update request base")
sealed class CorePersonRecordV1UpdateRequestDto {
Expand All @@ -26,6 +27,7 @@ sealed class CorePersonRecordV1UpdateRequestDto {
const val BIRTHPLACE = "BIRTHPLACE"
const val COUNTRY_OF_BIRTH = "COUNTRY_OF_BIRTH"
const val DATE_OF_BIRTH = "DATE_OF_BIRTH"
const val NATIONALITY = "NATIONALITY"
}
}

Expand Down Expand Up @@ -73,7 +75,7 @@ data class DateOfBirthUpdateDto(
@Schema(description = "Core Person Record V1 country of birth update request")
data class CountryOfBirthUpdateDto(
@Schema(
description = "The new value for the country of brith field",
description = "The new value for the country of birth field",
example = "UK",
required = true,
nullable = true,
Expand All @@ -89,3 +91,23 @@ data class CountryOfBirthUpdateDto(
)
override val fieldName: String = COUNTRY_OF_BIRTH
}

@Schema(description = "Core Person Record V1 nationality update request")
data class NationalityUpdateDto(
@Schema(
description = "The new value for the nationality field",
example = "BRIT",
required = true,
nullable = true,
)
override val value: String?,
) : CorePersonRecordV1UpdateRequestDto() {
@Schema(
type = "String",
description = "The field to be updated",
allowableValues = [NATIONALITY],
required = true,
nullable = false,
)
override val fieldName: String = COUNTRY_OF_BIRTH
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.servi
import org.springframework.stereotype.Service
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.BirthplaceUpdateDto
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.CorePersonRecordV1UpdateRequestDto
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.NationalityUpdateDto
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.exception.UnknownCorePersonFieldException

@Service
Expand All @@ -15,6 +17,7 @@ class CorePersonRecordService(
fun updateCorePersonRecordField(prisonerNumber: String, updateRequestDto: CorePersonRecordV1UpdateRequestDto) {
when (updateRequestDto) {
is BirthplaceUpdateDto -> prisonApiClient.updateBirthPlaceForWorkingName(prisonerNumber, UpdateBirthPlace(updateRequestDto.value))
is NationalityUpdateDto -> prisonApiClient.updateNationalityForWorkingName(prisonerNumber, UpdateNationality(updateRequestDto.value))
else -> throw UnknownCorePersonFieldException("Field '${updateRequestDto.fieldName}' cannot be updated.")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,26 @@ class CorePersonRecordV1ResourceIntTest : IntegrationTestBase() {
.exchange()
.expectStatus().isNoContent
}

@Test
fun `can patch core person record nationality by prisoner number`() {
webTestClient.patch().uri("/v1/core-person-record?prisonerNumber=$PRISONER_NUMBER")
.contentType(MediaType.APPLICATION_JSON)
.headers(setAuthorisation(roles = listOf(CorePersonRecordRoleConstants.CORE_PERSON_RECORD_READ_WRITE_ROLE)))
.bodyValue(NATIONALITY_PATCH_REQUEST_BODY)
.exchange()
.expectStatus().isNoContent
}

@Test
fun `patch core person record nationality accepts null value`() {
webTestClient.patch().uri("/v1/core-person-record?prisonerNumber=$PRISONER_NUMBER")
.contentType(MediaType.APPLICATION_JSON)
.headers(setAuthorisation(roles = listOf(CorePersonRecordRoleConstants.CORE_PERSON_RECORD_READ_WRITE_ROLE)))
.bodyValue(NULL_NATIONALITY_PATCH_REQUEST_BODY)
.exchange()
.expectStatus().isNoContent
}
}

@Nested
Expand Down Expand Up @@ -191,6 +211,24 @@ class CorePersonRecordV1ResourceIntTest : IntegrationTestBase() {
}
""".trimIndent()

val NATIONALITY_PATCH_REQUEST_BODY =
// language=json
"""
{
"fieldName": "NATIONALITY",
"value": "BRIT"
}
""".trimIndent()

val NULL_NATIONALITY_PATCH_REQUEST_BODY =
// language=json
"""
{
"fieldName": "NATIONALITY",
"value": null
}
""".trimIndent()

val VALID_PATCH_REQUEST_BODY = BIRTHPLACE_PATCH_REQUEST_BODY

val MULTIPART_FILE: MultipartFile = MockMultipartFile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import org.mockito.kotlin.whenever
import org.springframework.http.ResponseEntity
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.PrisonApiClient
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateBirthPlace
import uk.gov.justice.digital.hmpps.personintegrationapi.common.client.dto.UpdateNationality
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.BirthplaceUpdateDto
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.DateOfBirthUpdateDto
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.dto.v1.request.NationalityUpdateDto
import uk.gov.justice.digital.hmpps.personintegrationapi.corepersonrecord.exception.UnknownCorePersonFieldException
import java.time.LocalDate

Expand All @@ -35,7 +37,10 @@ class CorePersonRecordServiceTest {

@BeforeEach
fun beforeEach() {
whenever(prisonApiClient.updateBirthPlaceForWorkingName(PRISONER_NUMBER, TEST_BIRTHPLACE_BODY)).thenReturn(ResponseEntity.noContent().build())
whenever(prisonApiClient.updateBirthPlaceForWorkingName(PRISONER_NUMBER, TEST_BIRTHPLACE_BODY))
.thenReturn(ResponseEntity.noContent().build())
whenever(prisonApiClient.updateNationalityForWorkingName(PRISONER_NUMBER, TEST_NATIONALITY_BODY))
.thenReturn(ResponseEntity.noContent().build())
}

@Nested
Expand All @@ -45,6 +50,11 @@ class CorePersonRecordServiceTest {
underTest.updateCorePersonRecordField(PRISONER_NUMBER, BirthplaceUpdateDto(TEST_BIRTHPLACE_VALUE))
}

@Test
fun `can update the nationality field`() {
underTest.updateCorePersonRecordField(PRISONER_NUMBER, NationalityUpdateDto(TEST_NATIONALITY_VALUE))
}

@Test
fun `throws an exception if the field type is not supported`() {
assertThrows<UnknownCorePersonFieldException> {
Expand All @@ -56,6 +66,8 @@ class CorePersonRecordServiceTest {
private companion object {
const val PRISONER_NUMBER = "A1234AA"
const val TEST_BIRTHPLACE_VALUE = "London"
const val TEST_NATIONALITY_VALUE = "BRIT"
val TEST_BIRTHPLACE_BODY = UpdateBirthPlace("London")
val TEST_NATIONALITY_BODY = UpdateNationality("BRIT")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,36 @@ class PrisonApiMockServer : WireMockServer(8082) {
)
}

fun stubUpdateBirthPlaceForWorkingName(prisonerNumber: String = PRISONER_NUMBER) {
stubFor(
put(urlPathMatching("/api/offenders/$prisonerNumber/birth-place")).willReturn(
aResponse().withHeader("Content-Type", "application/json")
.withStatus(HttpStatus.NO_CONTENT.value()),
),
fun stubUpdateBirthPlaceForWorkingName() {
val endpoint = "birth-place"
stubOffenderEndpoint(endpoint, HttpStatus.NO_CONTENT, PRISONER_NUMBER)
stubOffenderEndpoint(endpoint, HttpStatus.INTERNAL_SERVER_ERROR, PRISONER_NUMBER_THROW_EXCEPTION)
stubOffenderEndpoint(
endpoint,
HttpStatus.NOT_FOUND,
PRISONER_NUMBER_NOT_FOUND,
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
)
}

fun stubUpdateBirthPlaceForWorkingNameException(prisonerNumber: String = PRISONER_NUMBER_THROW_EXCEPTION) {
stubFor(
put(urlPathMatching("/api/offenders/$prisonerNumber/birth-place")).willReturn(
aResponse().withHeader("Content-Type", "application/json")
.withStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()),
),
fun stubUpdateNationalityForWorkingName() {
val endpoint = "nationality"
stubOffenderEndpoint(endpoint, HttpStatus.NO_CONTENT, PRISONER_NUMBER)
stubOffenderEndpoint(endpoint, HttpStatus.INTERNAL_SERVER_ERROR, PRISONER_NUMBER_THROW_EXCEPTION)
stubOffenderEndpoint(
endpoint,
HttpStatus.NOT_FOUND,
PRISONER_NUMBER_NOT_FOUND,
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
)
}

fun stubUpdateBirthPlaceForWorkingNameNotFound(prisonerNumber: String = PRISONER_NUMBER_NOT_FOUND) {
private fun stubOffenderEndpoint(endpoint: String, status: HttpStatus, prisonerNumber: String, body: String? = null) {
stubFor(
put(urlPathMatching("/api/offenders/$prisonerNumber/birth-place")).willReturn(
put(urlPathMatching("/api/offenders/$prisonerNumber/$endpoint")).willReturn(
aResponse().withHeader("Content-Type", "application/json")
.withStatus(HttpStatus.NOT_FOUND.value()).withBody(
PRISON_API_NOT_FOUND_RESPONSE.trimIndent(),
),
.withStatus(status.value())
.withBody(body),
),
)
}
Expand All @@ -73,8 +78,7 @@ class PrisonApiExtension : BeforeAllCallback, AfterAllCallback, BeforeEachCallba
override fun beforeEach(context: ExtensionContext) {
prisonApi.resetAll()
prisonApi.stubUpdateBirthPlaceForWorkingName()
prisonApi.stubUpdateBirthPlaceForWorkingNameException()
prisonApi.stubUpdateBirthPlaceForWorkingNameNotFound()
prisonApi.stubUpdateNationalityForWorkingName()
}

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

0 comments on commit f1f6d00

Please sign in to comment.