diff --git a/projects/common-platform-and-delius/src/dev/resources/simulations/__files/address-lookup-no-results.json b/projects/common-platform-and-delius/src/dev/resources/simulations/__files/address-lookup-no-results.json new file mode 100644 index 0000000000..d9e9027a2a --- /dev/null +++ b/projects/common-platform-and-delius/src/dev/resources/simulations/__files/address-lookup-no-results.json @@ -0,0 +1,4 @@ +{ + "header": { "totalresults": 0 }, + "results": [] +} \ No newline at end of file diff --git a/projects/common-platform-and-delius/src/dev/resources/simulations/__files/address-lookup-single-result.json b/projects/common-platform-and-delius/src/dev/resources/simulations/__files/address-lookup-single-result.json new file mode 100644 index 0000000000..a9cbd5f4e4 --- /dev/null +++ b/projects/common-platform-and-delius/src/dev/resources/simulations/__files/address-lookup-single-result.json @@ -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" + } + } + ] +} \ No newline at end of file diff --git a/projects/common-platform-and-delius/src/dev/resources/simulations/mappings/address-lookup-mapping.json b/projects/common-platform-and-delius/src/dev/resources/simulations/mappings/address-lookup-mapping.json new file mode 100644 index 0000000000..1fd96c6ba2 --- /dev/null +++ b/projects/common-platform-and-delius/src/dev/resources/simulations/mappings/address-lookup-mapping.json @@ -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" + } +} \ No newline at end of file diff --git a/projects/common-platform-and-delius/src/dev/resources/simulations/mappings/probation-search-mapping.json b/projects/common-platform-and-delius/src/dev/resources/simulations/mappings/probation-search-mapping.json new file mode 100644 index 0000000000..85f814a949 --- /dev/null +++ b/projects/common-platform-and-delius/src/dev/resources/simulations/mappings/probation-search-mapping.json @@ -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" + } +} \ No newline at end of file diff --git a/projects/common-platform-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt b/projects/common-platform-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt index 6276947a5a..7b85be9565 100644 --- a/projects/common-platform-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt +++ b/projects/common-platform-and-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt @@ -97,16 +97,6 @@ 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() @@ -114,16 +104,6 @@ internal class IntegrationTest { @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() @@ -131,16 +111,6 @@ internal class IntegrationTest { @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() @@ -148,16 +118,6 @@ internal class IntegrationTest { @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) @@ -181,16 +141,6 @@ 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() @@ -198,20 +148,11 @@ internal class IntegrationTest { @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 { assertThat(it.start, Matchers.equalTo(LocalDate.now())) @@ -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) @@ -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) @@ -286,6 +207,17 @@ internal class IntegrationTest { assertThat(it.telephoneNumber, Matchers.equalTo("01234567890")) }) + verify(addressRepository).save(check { + 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 } @@ -331,6 +263,62 @@ 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 { + 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 { + 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()) @@ -338,4 +326,9 @@ internal class IntegrationTest { verify(auditedInteractionService, Mockito.never()) .createAuditedInteraction(any(), any(), eq(AuditedInteraction.Outcome.SUCCESS), any(), anyOrNull()) } + + @AfterEach + fun resetWireMock() { + wireMockServer.resetAll() + } } \ No newline at end of file diff --git a/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/RestClientConfig.kt b/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/RestClientConfig.kt index 9341e1a5a1..0ff40a2d0c 100644 --- a/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/RestClientConfig.kt +++ b/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/config/RestClientConfig.kt @@ -3,9 +3,16 @@ 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) { @@ -13,4 +20,25 @@ 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) } } diff --git a/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/client/OsClient.kt b/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/client/OsClient.kt new file mode 100644 index 0000000000..d3a7583fef --- /dev/null +++ b/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/client/OsClient.kt @@ -0,0 +1,146 @@ +package uk.gov.justice.digital.hmpps.integrations.client + +import com.fasterxml.jackson.annotation.JsonProperty +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.service.annotation.GetExchange + +interface OsClient { + @GetExchange("/search/places/v1/find") + fun searchByFreeText( + @RequestParam query: String, + @RequestParam maxResults: Int, + @RequestParam minMatch: Double, + ): OsPlacesResponse +} + +data class OsPlacesResponse( + val header: OsPlacesHeader, + val results: List? +) + +data class OsPlacesHeader( + @JsonProperty("uri") + val uri: String?, + @JsonProperty("query") + val query: String?, + @JsonProperty("offset") + val offset: Int?, + @JsonProperty("totalresults") + val totalResults: Int?, + @JsonProperty("format") + val format: String?, + @JsonProperty("dataset") + val dataset: String?, + @JsonProperty("lr") + val lr: String?, + @JsonProperty("maxresults") + val maxResults: Int?, + @JsonProperty("matchprecision") + val matchPrecision: Double?, + @JsonProperty("filter") + val filter: String?, + @JsonProperty("srs") + val srs: String?, + @JsonProperty("epoch") + val epoch: String?, + @JsonProperty("lastupdate") + val lastUpdate: String?, + @JsonProperty("output_srs") + val outputSrs: String? +) + +class DpaWrapper { + @JsonProperty("DPA") + val dpa: Dpa? = null +} + +data class Dpa( + @JsonProperty("UPRN") + val uprn: Long, + @JsonProperty("UDPRN") + val udprn: Long, + @JsonProperty("ADDRESS") + val address: String, + @JsonProperty("PO_BOX_NUMBER") + val poBoxNumber: String?, + @JsonProperty("ORGANISATION_NAME") + val organisationName: String?, + @JsonProperty("DEPARTMENT_NAME") + val departmentName: String?, + @JsonProperty("SUB_BUILDING_NAME") + val subBuildingName: String?, + @JsonProperty("BUILDING_NAME") + val buildingName: String?, + @JsonProperty("BUILDING_NUMBER") + val buildingNumber: Int?, + @JsonProperty("DEPENDENT_THOROUGHFARE_NAME") + val dependentThoroughfareName: String?, + @JsonProperty("THOROUGHFARE_NAME") + val thoroughfareName: String?, + @JsonProperty("DOUBLE_DEPENDENT_LOCALITY") + val doubleDependentLocality: String?, + @JsonProperty("DEPENDENT_LOCALITY") + val dependentLocality: String?, + @JsonProperty("POST_TOWN") + val postTown: String, + @JsonProperty("POSTCODE") + val postcode: String, + @JsonProperty("RPC") + val rpc: String, + @JsonProperty("X_COORDINATE") + val xCoordinate: Double, + @JsonProperty("Y_COORDINATE") + val yCoordinate: Double, + @JsonProperty("LNG") + val lng: Double?, + @JsonProperty("LAT") + val lat: Double?, + @JsonProperty("STATUS") + val status: String, + @JsonProperty("MATCH") + val match: Float?, + @JsonProperty("MATCH_DESCRIPTION") + val matchDescription: String?, + @JsonProperty("LANGUAGE") + val language: String, + @JsonProperty("COUNTRY_CODE") + val countryCode: String, + @JsonProperty("COUNTRY_CODE_DESCRIPTION") + val countryCodeDescription: String, + @JsonProperty("LOCAL_CUSTODIAN_CODE") + val localCustodianCode: Int, + @JsonProperty("LOCAL_CUSTODIAN_CODE_DESCRIPTION") + val localCustodianCodeDescription: String, + @JsonProperty("CLASSIFICATION_CODE") + val classificationCode: String, + @JsonProperty("CLASSIFICATION_CODE_DESCRIPTION") + val classificationCodeDescription: String, + @JsonProperty("POSTAL_ADDRESS_CODE") + val postalAddressCode: String, + @JsonProperty("POSTAL_ADDRESS_CODE_DESCRIPTION") + val postalAddressCodeDescription: String, + @JsonProperty("LOGICAL_STATUS_CODE") + val logicalStatusCode: Int, + @JsonProperty("BLPU_STATE_CODE") + val blpuStateCode: Int, + @JsonProperty("BLPU_STATE_CODE_DESCRIPTION") + val blpuStateCodeDescription: String?, + @JsonProperty("TOPOGRAPHY_LAYER_TOID") + val topographyLayerToid: String, + @JsonProperty("PARENT_UPRN") + val parentUprn: Long?, + @JsonProperty("LAST_UPDATE_DATE") + val lastUpdateDate: String, + @JsonProperty("ENTRY_DATE") + val entryDate: String, + @JsonProperty("LEGAL_NAME") + val legalName: String?, + @JsonProperty("BLPU_STATE_DATE") + val blpuStateDate: String?, + @JsonProperty("DELIVERY_POINT_SUFFIX") + val deliveryPointSuffix: String, + @JsonProperty("PARISH_CODE") + val parishCode: String, + @JsonProperty("WARD_CODE") + val wardCode: String +) \ No newline at end of file diff --git a/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/PersonService.kt b/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/PersonService.kt index c28a0aa9a8..0fd616c676 100644 --- a/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/PersonService.kt +++ b/projects/common-platform-and-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/service/PersonService.kt @@ -4,6 +4,8 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import uk.gov.justice.digital.hmpps.audit.service.AuditableService import uk.gov.justice.digital.hmpps.audit.service.AuditedInteractionService +import uk.gov.justice.digital.hmpps.integrations.client.OsClient +import uk.gov.justice.digital.hmpps.integrations.client.OsPlacesResponse import uk.gov.justice.digital.hmpps.integrations.delius.audit.BusinessInteractionCode import uk.gov.justice.digital.hmpps.integrations.delius.entity.* import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonAddress @@ -24,7 +26,8 @@ class PersonService( private val teamRepository: TeamRepository, private val staffRepository: StaffRepository, private val referenceDataRepository: ReferenceDataRepository, - private val personAddressRepository: PersonAddressRepository + private val personAddressRepository: PersonAddressRepository, + private val osClient: OsClient ) : AuditableService(auditedInteractionService) { @Transactional @@ -75,20 +78,51 @@ class PersonService( val savedEquality = equalityRepository.save(equality) - val savedAddress = - defendant.personDefendant.personDetails.address.takeIf { it.containsInformation() }?.let { + val addressInfo = defendant.personDefendant.personDetails.address + val osPlacesResponse = addressInfo?.takeIf { it.containsInformation() && !it.postcode.isNullOrBlank() } + ?.let { findAddressByFreeText(it) } + + val deliveryPointAddress = osPlacesResponse?.results?.firstOrNull()?.dpa + + val savedAddress = if (deliveryPointAddress != null) { + insertAddress( + PersonAddress( + id = null, + start = LocalDate.now(), + status = referenceDataRepository.mainAddressStatus(), + person = savedPerson, + type = referenceDataRepository.awaitingAssessmentAddressType(), + postcode = deliveryPointAddress.postcode, + notes = "UPRN: ${deliveryPointAddress.uprn}", + buildingName = listOfNotNull( + deliveryPointAddress.subBuildingName, + deliveryPointAddress.buildingName + ).joinToString(" "), + addressNumber = deliveryPointAddress.buildingNumber?.toString(), + streetName = deliveryPointAddress.thoroughfareName, + town = deliveryPointAddress.postTown, + district = deliveryPointAddress.localCustodianCodeDescription + ) + ) + } else { + addressInfo?.takeIf { it.containsInformation() }?.let { insertAddress( PersonAddress( id = null, start = LocalDate.now(), status = referenceDataRepository.mainAddressStatus(), person = savedPerson, - notes = it.buildNotes(), postcode = it.postcode, - type = referenceDataRepository.awaitingAssessmentAddressType() + type = referenceDataRepository.awaitingAssessmentAddressType(), + streetName = it.address1, + district = it.address2, + town = it.address3, + county = listOfNotNull(it.address4, it.address5).joinToString(", ") ) ) } + } + audit["offenderId"] = savedPerson.id InsertPersonResult(savedPerson, savedManager, savedEquality, savedAddress) } @@ -137,14 +171,19 @@ class PersonService( ).any { !it.isNullOrBlank() } } - fun Address.buildNotes(): String { - return listOf( - "Address record automatically created by common-platform-delius-service with the following information:", - "Address1: ${this.address1 ?: "N/A"}", - "Address2: ${this.address2 ?: "N/A"}", - "Address3: ${this.address3 ?: "N/A"}", - "Address4: ${this.address4 ?: "N/A"}", - "Postcode: ${this.postcode ?: "N/A"}" - ).joinToString("\n") + fun findAddressByFreeText(address: Address): OsPlacesResponse { + val freeText = address.toFreeText() + return osClient.searchByFreeText(query = freeText, maxResults = 1, minMatch = 0.6) + } + + fun Address.toFreeText(): String { + return listOfNotNull( + this.address1?.trim(), + this.address2?.trim(), + this.address3?.trim(), + this.address4?.trim(), + this.address5?.trim(), + this.postcode?.trim() + ).joinToString(", ") } } \ No newline at end of file diff --git a/projects/common-platform-and-delius/src/main/resources/application.yml b/projects/common-platform-and-delius/src/main/resources/application.yml index 400dd58880..d9495d3c96 100644 --- a/projects/common-platform-and-delius/src/main/resources/application.yml +++ b/projects/common-platform-and-delius/src/main/resources/application.yml @@ -60,6 +60,11 @@ messaging.consumer.queue: message-queue integrations: probation-search.url: http://localhost:${wiremock.port}/probation-search +os-places: + api: + url: http://localhost:${wiremock.port}/address-lookup + key: key + oauth2: client-id: common-platform-and-delius client-secret: common-platform-and-delius