Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDPS-1074 Connect to edit nationality endpoint in Prison API #14

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading