Skip to content

Commit

Permalink
PI-2667 Common Platform Address records - OS Places API (#4481)
Browse files Browse the repository at this point in the history
* PI-2667 Common Platform Address records - OS Places API

* PI-2667 Common Platform Address records - OS Places API

* PI-2667 Common Platform Address records - OS Places API
- Added wiremock mappings files
- Fixed failing tests

* PI-2667 Common Platform Address records - OS Places API

* PI-2667 Common Platform Address records - OS Places API

* Formatting changes

* PI-2667 Common Platform Address records - OS Places API

* PI-2667 Common Platform Address records - OS Places API

---------

Co-authored-by: probation-integration-bot[bot] <177347787+probation-integration-bot[bot]@users.noreply.github.com>
  • Loading branch information
1 parent b14f213 commit 8948a7e
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"header": { "totalresults": 0 },
"results": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"header": {
"totalresults": 1
},
"results": [
{
"DPA": {
"UPRN": 123456789012,
"UDPRN": 12345678,
"ADDRESS": "123 Test Street, Test, AB1 2CD",
"POSTCODE": "AB1 2CD",
"BUILDING_NUMBER": 123,
"THOROUGHFARE_NAME": "Test Street",
"POST_TOWN": "Test",
"RPC": "2",
"X_COORDINATE": 123456.78,
"Y_COORDINATE": 876543.21,
"STATUS": "APPROVED",
"MATCH": 0.9,
"LANGUAGE": "EN",
"COUNTRY_CODE": "E",
"COUNTRY_CODE_DESCRIPTION": "This record is within England",
"LOCAL_CUSTODIAN_CODE": 1760,
"LOCAL_CUSTODIAN_CODE_DESCRIPTION": "Test",
"CLASSIFICATION_CODE": "CO01GV",
"CLASSIFICATION_CODE_DESCRIPTION": "Central Government Service",
"POSTAL_ADDRESS_CODE": "D",
"POSTAL_ADDRESS_CODE_DESCRIPTION": "A record which is linked to PAF",
"LOGICAL_STATUS_CODE": 1,
"BLPU_STATE_CODE": 2,
"BLPU_STATE_CODE_DESCRIPTION": "In use",
"TOPOGRAPHY_LAYER_TOID": "osgb1234567890123456",
"LAST_UPDATE_DATE": "2024-01-01",
"ENTRY_DATE": "2024-01-01",
"DELIVERY_POINT_SUFFIX": "1A",
"PARISH_CODE": "E87654321",
"WARD_CODE": "E12345678"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"request": {
"method": "GET",
"urlPath": "/address-lookup/search/places/v1/find"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"bodyFileName": "address-lookup-single-result.json"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"request": {
"method": "POST",
"urlPath": "/probation-search/match"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"bodyFileName": "probation-search-no-results.json"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,67 +97,27 @@ internal class IntegrationTest {

@Test
fun `When a message with no prosecution cases is found no insert is performed`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT_NO_CASES)
channelManager.getChannel(queueName).publishAndWait(notification)
thenNoRecordsAreInserted()
}

@Test
fun `When a message without a judicial result of remanded in custody is found`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT_NO_REMAND)
channelManager.getChannel(queueName).publishAndWait(notification)
thenNoRecordsAreInserted()
}

@Test
fun `When a person under 10 years old is found no insert is performed`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT_DOB_ERROR)
channelManager.getChannel(queueName).publishAndWait(notification)
thenNoRecordsAreInserted()
}

@Test
fun `When a probation search match is not detected then a person is inserted`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT)
channelManager.getChannel(queueName).publishAndWait(notification)

Expand All @@ -181,37 +141,18 @@ internal class IntegrationTest {

@Test
fun `When a hearing message with missing required fields is detected no records are inserted`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT_NULL_FIELDS)
channelManager.getChannel(queueName).publishAndWait(notification)
thenNoRecordsAreInserted()
}

@Test
fun `When a hearing with an address is received then an address record is inserted`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT)
channelManager.getChannel(queueName).publishAndWait(notification)

verify(personService).insertAddress(any())
verify(personService).findAddressByFreeText(any())

verify(addressRepository).save(check<PersonAddress> {
assertThat(it.start, Matchers.equalTo(LocalDate.now()))
Expand All @@ -235,16 +176,6 @@ internal class IntegrationTest {

@Test
fun `When a hearing with an empty address is received then an address record is not inserted`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT_BLANK_ADDRESS)
channelManager.getChannel(queueName).publishAndWait(notification)

Expand All @@ -264,16 +195,6 @@ internal class IntegrationTest {
@Order(1)
@Test
fun `engagement created and address created sns messages are published on insert person`() {
wireMockServer.stubFor(
post(urlPathEqualTo("/probation-search/match"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("probation-search-no-results.json")
)
)

val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT)
channelManager.getChannel(queueName).publishAndWait(notification)

Expand All @@ -286,6 +207,17 @@ internal class IntegrationTest {
assertThat(it.telephoneNumber, Matchers.equalTo("01234567890"))
})

verify(addressRepository).save(check<PersonAddress> {
assertThat(it.start, Matchers.equalTo(LocalDate.now()))
assertNull(it.endDate)
assertNotNull(it.notes)
assertThat(it.softDeleted, Matchers.equalTo(false))
assertThat(it.status.code, Matchers.equalTo(ReferenceData.StandardRefDataCode.ADDRESS_MAIN_STATUS.code))
assertThat(it.noFixedAbode, Matchers.equalTo(false))
assertThat(it.type.code, Matchers.equalTo(ReferenceData.StandardRefDataCode.AWAITING_ASSESSMENT.code))
assertThat(it.typeVerified, Matchers.equalTo(false))
})

val topic = hmppsChannelManager.getChannel(topicName)
val messages = topic.pollFor(2)
val messageTypes = messages.mapNotNull { it.eventType }
Expand Down Expand Up @@ -331,11 +263,72 @@ internal class IntegrationTest {
}
}

@Test
fun `court hearing address is inserted when no address lookup is found`() {
wireMockServer.stubFor(
get(urlPathEqualTo("/address-lookup/search/places/v1/find"))
.willReturn(
okJson(
"""
{
"header": { "totalresults": 0 },
"results": []
}
"""
)
)
)
val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT)
channelManager.getChannel(queueName).publishAndWait(notification)

verify(addressRepository).save(check<PersonAddress> {
assertThat(it.start, Matchers.equalTo(LocalDate.now()))
assertThat(it.streetName, Matchers.containsString("Example Address Line 1"))
assertThat(it.district, Matchers.containsString("Example Address Line 2"))
assertThat(it.town, Matchers.containsString("Example Address Line 3"))
assertThat(it.postcode, Matchers.containsString("AA1 1AA"))
assertNull(it.endDate)
assertNull(it.notes)
assertThat(it.softDeleted, Matchers.equalTo(false))
assertThat(it.status.code, Matchers.equalTo(ReferenceData.StandardRefDataCode.ADDRESS_MAIN_STATUS.code))
assertThat(it.noFixedAbode, Matchers.equalTo(false))
assertThat(it.type.code, Matchers.equalTo(ReferenceData.StandardRefDataCode.AWAITING_ASSESSMENT.code))
assertThat(it.typeVerified, Matchers.equalTo(false))
})
}

@Test
fun `Address lookup api is inserted when result is found`() {
val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT)
channelManager.getChannel(queueName).publishAndWait(notification)

verify(addressRepository).save(check<PersonAddress> {
assertThat(it.start, Matchers.equalTo(LocalDate.now()))
assertThat(it.notes, Matchers.containsString("UPRN: 123456789012"))
assertThat(it.postcode, Matchers.containsString("AB1 2CD"))
assertThat(it.streetName, Matchers.containsString("Test Street"))
assertThat(it.addressNumber, Matchers.containsString("123"))
assertThat(it.town, Matchers.containsString("Test"))
assertNull(it.endDate)
assertNotNull(it.notes)
assertThat(it.softDeleted, Matchers.equalTo(false))
assertThat(it.status.code, Matchers.equalTo(ReferenceData.StandardRefDataCode.ADDRESS_MAIN_STATUS.code))
assertThat(it.noFixedAbode, Matchers.equalTo(false))
assertThat(it.type.code, Matchers.equalTo(ReferenceData.StandardRefDataCode.AWAITING_ASSESSMENT.code))
assertThat(it.typeVerified, Matchers.equalTo(false))
})
}

private fun thenNoRecordsAreInserted() {
verify(personService, never()).insertAddress(any())
verify(addressRepository, never()).save(any())
verify(personRepository, never()).save(any())
verify(auditedInteractionService, Mockito.never())
.createAuditedInteraction(any(), any(), eq(AuditedInteraction.Outcome.SUCCESS), any(), anyOrNull())
}

@AfterEach
fun resetWireMock() {
wireMockServer.resetAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,42 @@ package uk.gov.justice.digital.hmpps.config
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.client.JdkClientHttpRequestFactory
import org.springframework.http.client.support.HttpRequestWrapper
import org.springframework.web.client.RestClient
import org.springframework.web.util.UriComponentsBuilder
import uk.gov.justice.digital.hmpps.config.security.RetryInterceptor
import uk.gov.justice.digital.hmpps.config.security.createClient
import uk.gov.justice.digital.hmpps.integrations.client.OsClient
import uk.gov.justice.digital.hmpps.integrations.client.ProbationSearchClient
import java.net.http.HttpClient
import java.time.Duration

@Configuration
class RestClientConfig(private val oauth2Client: RestClient) {

@Bean
fun probationSearchClient(@Value("\${integrations.probation-search.url}") apiBaseUrl: String): ProbationSearchClient =
createClient(oauth2Client.mutate().baseUrl(apiBaseUrl).build())

@Bean
fun osPlacesRestClient(
@Value("\${os-places.api.key}") apiKey: String,
@Value("\${os-places.api.url}") baseUrl: String
): OsClient = createClient(
RestClient.builder()
.requestFactory(withTimeouts(Duration.ofSeconds(1), Duration.ofSeconds(5)))
.requestInterceptor(RetryInterceptor())
.requestInterceptor { request, body, execution ->
execution.execute(object : HttpRequestWrapper(request) {
override fun getURI() =
UriComponentsBuilder.fromUri(request.uri).queryParam("key", apiKey).build(true).toUri()
}, body)
}
.baseUrl(baseUrl)
.build())

fun withTimeouts(connection: Duration, read: Duration) =
JdkClientHttpRequestFactory(HttpClient.newBuilder().connectTimeout(connection).build())
.also { it.setReadTimeout(read) }
}
Loading

0 comments on commit 8948a7e

Please sign in to comment.