diff --git a/projects/prison-case-notes-to-probation/deploy/values-dev.yml b/projects/prison-case-notes-to-probation/deploy/values-dev.yml index 1042f9fa36..60732392a9 100644 --- a/projects/prison-case-notes-to-probation/deploy/values-dev.yml +++ b/projects/prison-case-notes-to-probation/deploy/values-dev.yml @@ -10,6 +10,8 @@ generic-service: LOGGING_LEVEL_COM_AMAZON_SQS: DEBUG LOGGING_LEVEL_UK_GOV_DIGITAL_JUSTICE_HMPPS: DEBUG SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_HMPPS-AUTH_TOKEN-URI: http://hmpps-auth.hmpps-auth-dev.svc.cluster.local/auth/oauth/token + INTEGRATIONS_PRISON_CASE_NOTES_BASE_URL: https://dev.offender-case-notes.service.justice.gov.uk + generic-prometheus-alerts: businessHoursOnly: true \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/deploy/values-preprod.yml b/projects/prison-case-notes-to-probation/deploy/values-preprod.yml index 15b16a303b..5b88a62e1a 100644 --- a/projects/prison-case-notes-to-probation/deploy/values-preprod.yml +++ b/projects/prison-case-notes-to-probation/deploy/values-preprod.yml @@ -8,6 +8,7 @@ generic-service: env: SENTRY_ENVIRONMENT: preprod SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_HMPPS-AUTH_TOKEN-URI: http://hmpps-auth.hmpps-auth-preprod.svc.cluster.local/auth/oauth/token + INTEGRATIONS_PRISON_CASE_NOTES_BASE_URL: https://preprod.offender-case-notes.service.justice.gov.uk generic-prometheus-alerts: businessHoursOnly: true \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/deploy/values-prod.yml b/projects/prison-case-notes-to-probation/deploy/values-prod.yml index 99c072275c..3926b9eb3c 100644 --- a/projects/prison-case-notes-to-probation/deploy/values-prod.yml +++ b/projects/prison-case-notes-to-probation/deploy/values-prod.yml @@ -5,3 +5,4 @@ generic-service: env: SENTRY_ENVIRONMENT: prod SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_HMPPS-AUTH_TOKEN-URI: http://hmpps-auth.hmpps-auth-prod.svc.cluster.local/auth/oauth/token + INTEGRATIONS_PRISON_CASE_NOTES_BASE_URL: https://offender-case-notes.service.justice.gov.uk diff --git a/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/CaseNotesDataLoader.kt b/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/CaseNotesDataLoader.kt index 11f063760d..56437706fe 100644 --- a/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/CaseNotesDataLoader.kt +++ b/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/CaseNotesDataLoader.kt @@ -75,6 +75,7 @@ class CaseNotesDataLoader( StaffGenerator.DEFAULT = staffRepository.save(StaffGenerator.DEFAULT) offenderRepository.save(OffenderGenerator.DEFAULT) + offenderRepository.save(OffenderGenerator.NEW_IDENTIFIER) eventRepository.save(EventGenerator.CUSTODIAL_EVENT) disposalTypeRepository.save(EventGenerator.CUSTODIAL_EVENT.disposal!!.disposalType) diff --git a/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CaseNoteMessageGenerator.kt b/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CaseNoteMessageGenerator.kt index 207864c9f4..9b5983bcec 100644 --- a/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CaseNoteMessageGenerator.kt +++ b/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/CaseNoteMessageGenerator.kt @@ -8,4 +8,5 @@ object CaseNoteMessageGenerator { val NEW_TO_DELIUS: HmppsDomainEvent = ResourceLoader.message("case-note-new-to-delius") val NOT_FOUND: HmppsDomainEvent = ResourceLoader.message("case-note-not-found") val RESETTLEMENT_PASSPORT: HmppsDomainEvent = ResourceLoader.message("resettlement-passport-casenote") + val NOMS_NUMBER_ADDED: HmppsDomainEvent = ResourceLoader.message("noms-number-added") } diff --git a/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderGenerator.kt b/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderGenerator.kt index 58c38894f4..61c53aa0c2 100644 --- a/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderGenerator.kt +++ b/projects/prison-case-notes-to-probation/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/OffenderGenerator.kt @@ -4,5 +4,7 @@ import uk.gov.justice.digital.hmpps.integrations.delius.entity.Offender object OffenderGenerator { const val EXISTING_OFFENDER_ID = "AA0001A" + const val NEW_PRISON_IDENTIFIER = "A4578BX" val DEFAULT = Offender(IdGenerator.getAndIncrement(), EXISTING_OFFENDER_ID) + val NEW_IDENTIFIER = Offender(IdGenerator.getAndIncrement(), NEW_PRISON_IDENTIFIER) } diff --git a/projects/prison-case-notes-to-probation/src/dev/resources/messages/noms-number-added.json b/projects/prison-case-notes-to-probation/src/dev/resources/messages/noms-number-added.json new file mode 100644 index 0000000000..fe95438af7 --- /dev/null +++ b/projects/prison-case-notes-to-probation/src/dev/resources/messages/noms-number-added.json @@ -0,0 +1,11 @@ +{ + "Type": "Notification", + "Message": "{\"eventType\":\"probation-case.prison-identifier.added\",\"version\":1,\"occurredAt\":\"2025-01-15T09:47:39+01:00\",\"publishedAt\":\"2025-01-15T09:47:39+01:00\",\"description\":\"A prisoner identifier has been added\",\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"A4578BX\"}]}}", + "Timestamp": "2025-01-15T09:47:39+01:00", + "MessageAttributes": { + "eventType": { + "Type": "String", + "Value": "probation-case.prison-identifier.added" + } + } +} \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/src/dev/resources/simulations/__files/migrate-for-new-identifier.json b/projects/prison-case-notes-to-probation/src/dev/resources/simulations/__files/migrate-for-new-identifier.json new file mode 100644 index 0000000000..a72be6f4be --- /dev/null +++ b/projects/prison-case-notes-to-probation/src/dev/resources/simulations/__files/migrate-for-new-identifier.json @@ -0,0 +1,69 @@ +{ + "content": [ + { + "caseNoteId": "ff0cfa55-3193-41c9-9332-7766c03a06ff", + "eventId": 77701, + "offenderIdentifier": "A4578BX", + "type": "GEN", + "subType": "OSE", + "creationDateTime": "2025-01-12T11:22:33+01:00", + "occurrenceDateTime": "2025-01-11T10:11:22+01:00", + "authorName": "Some Name", + "text": "note content", + "locationId": "LEI", + "amendments": [] + }, + { + "caseNoteId": "2dd32070-4af0-4d3c-ad8d-5108a43a9322", + "eventId": 77702, + "offenderIdentifier": "A4578BX", + "type": "ALERT", + "subType": "ACTIVE", + "creationDateTime": "2025-01-12T11:22:33+01:00", + "occurrenceDateTime": "2025-01-11T10:11:22+01:00", + "authorName": "Some Name", + "text": "note content", + "locationId": "LEI", + "amendments": [] + }, + { + "caseNoteId": "7f283166-2c7e-404e-9b0c-fa21cfbd338e", + "eventId": 77704, + "offenderIdentifier": "A4578BX", + "type": "KA", + "subType": "KS", + "creationDateTime": "2025-01-12T11:22:33+01:00", + "occurrenceDateTime": "2025-01-11T10:11:22+01:00", + "authorName": "Some Name", + "text": "note content", + "locationId": "LEI", + "amendments": [] + }, + { + "caseNoteId": "d9e02743-3e8a-4ab8-923b-01d4214afeaf", + "eventId": 77705, + "offenderIdentifier": "A4578BX", + "type": "PRISON", + "subType": "RELEASE", + "creationDateTime": "2025-01-12T11:22:33+01:00", + "occurrenceDateTime": "2025-01-11T10:11:22+01:00", + "authorName": "Some Name", + "text": "note content", + "locationId": "LEI", + "amendments": [] + }, + { + "caseNoteId": "bdc8b4af-01a3-44b5-9160-23c4d289f9a2", + "eventId": 77703, + "offenderIdentifier": "A4578BX", + "type": "TRANSFER", + "subType": "FROMTOL", + "creationDateTime": "2025-01-12T11:22:33+01:00", + "occurrenceDateTime": "2025-01-11T10:11:22+01:00", + "authorName": "Some Name", + "text": "note content", + "locationId": "TRN", + "amendments": [] + } + ] +} \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/src/dev/resources/simulations/mappings/get-case-note.json b/projects/prison-case-notes-to-probation/src/dev/resources/simulations/mappings/get-case-note.json index 6a9cce1a09..242796349d 100644 --- a/projects/prison-case-notes-to-probation/src/dev/resources/simulations/mappings/get-case-note.json +++ b/projects/prison-case-notes-to-probation/src/dev/resources/simulations/mappings/get-case-note.json @@ -57,6 +57,20 @@ "developerMessage": "Resource with id [AA0001A] not found." } } + }, + { + "request": { + "method": "POST", + "url": "/search/case-notes/A4578BX" + }, + "response": { + "headers": { + "Accept": "application/json", + "Content-Type": "application/json" + }, + "status": 200, + "bodyFileName": "migrate-for-new-identifier.json" + } } ] } \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CaseNotesIntegrationTest.kt b/projects/prison-case-notes-to-probation/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CaseNotesIntegrationTest.kt index e684198bab..d82b8fcc5e 100644 --- a/projects/prison-case-notes-to-probation/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CaseNotesIntegrationTest.kt +++ b/projects/prison-case-notes-to-probation/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CaseNotesIntegrationTest.kt @@ -21,6 +21,7 @@ import uk.gov.justice.digital.hmpps.audit.repository.AuditedInteractionRepositor import uk.gov.justice.digital.hmpps.data.generator.* import uk.gov.justice.digital.hmpps.datetime.DeliusDateTimeFormatter import uk.gov.justice.digital.hmpps.integrations.delius.repository.CaseNoteRepository +import uk.gov.justice.digital.hmpps.integrations.delius.repository.OffenderRepository import uk.gov.justice.digital.hmpps.integrations.delius.repository.StaffRepository import uk.gov.justice.digital.hmpps.messaging.HmppsChannelManager import uk.gov.justice.digital.hmpps.telemetry.TelemetryService @@ -32,6 +33,9 @@ const val CASE_NOTE_MERGED = "CaseNoteMerged" @SpringBootTest class CaseNotesIntegrationTest { + @Autowired + private lateinit var offenderRepository: OffenderRepository + @Value("\${messaging.consumer.queue}") private lateinit var queueName: String @@ -167,4 +171,23 @@ class CaseNotesIntegrationTest { val staff = staffRepository.findById(saved.staffId).orElseThrow() assertThat(staff.code, equalTo("${ProbationAreaGenerator.DEFAULT.code}B001")) } + + @Test + fun `migrate case notes succesfully when noms number added`() { + val offender = requireNotNull(offenderRepository.findByNomsIdAndSoftDeletedIsFalse("A4578BX")) + val originals = caseNoteRepository.findAll().filter { it.offenderId == offender.id } + assert(originals.isEmpty()) + + channelManager.getChannel(queueName).publishAndWait( + prepMessage(CaseNoteMessageGenerator.NOMS_NUMBER_ADDED, wireMockserver.port()) + ) + + verify(telemetryService).trackEvent( + eq("CaseNotesMigrated"), + eq(mapOf("nomsId" to "A4578BX", "cause" to "probation-case.prison-identifier.added", "count" to "4")), + anyMap() + ) + val saved = caseNoteRepository.findAll().filter { it.offenderId == offender.id } + assertThat(saved.size, equalTo(4)) + } } diff --git a/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/prison/PrisonCaseNotesClient.kt b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/prison/PrisonCaseNotesClient.kt index 906e3e0864..65a9a60757 100644 --- a/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/prison/PrisonCaseNotesClient.kt +++ b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/prison/PrisonCaseNotesClient.kt @@ -1,9 +1,44 @@ package uk.gov.justice.digital.hmpps.integrations.prison +import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.service.annotation.GetExchange +import org.springframework.web.service.annotation.PostExchange import java.net.URI +import java.time.LocalDateTime interface PrisonCaseNotesClient { @GetExchange fun getCaseNote(baseUrl: URI): PrisonCaseNote? + + @PostExchange + fun searchCaseNotes( + uri: URI, + @RequestBody searchCaseNotes: SearchCaseNotes + ): CaseNotesResults +} + +data class SearchCaseNotes( + val typeSubTypes: Set, + val occurredFrom: LocalDateTime? = null, + val occurredTo: LocalDateTime? = null, + val includeSensitive: Boolean = true, + val page: Int = 1, + val size: Int = Int.MAX_VALUE, + val sort: String = "occurredAt,desc", +) { + companion object { + val TYPES_OF_INTEREST = setOf( + TypeSubTypeRequest("PRISON", setOf("RELEASE")), + TypeSubTypeRequest("TRANSFER", setOf("FROMTOL")), + TypeSubTypeRequest("GEN", setOf("OSE")), + TypeSubTypeRequest("ALERT"), + TypeSubTypeRequest("OMIC"), + TypeSubTypeRequest("OMIC_OPD"), + TypeSubTypeRequest("KA"), + ) + } } + +data class TypeSubTypeRequest(val type: String, val subTypes: Set = setOf()) + +data class CaseNotesResults(val content: List) \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/CaseNotePublished.kt b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/CaseNotePublished.kt new file mode 100644 index 0000000000..399a6ee67c --- /dev/null +++ b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/CaseNotePublished.kt @@ -0,0 +1,87 @@ +package uk.gov.justice.digital.hmpps.messaging + +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.web.client.HttpStatusCodeException +import uk.gov.justice.digital.hmpps.datetime.DeliusDateTimeFormatter +import uk.gov.justice.digital.hmpps.exceptions.OffenderNotFoundException +import uk.gov.justice.digital.hmpps.integrations.delius.service.DeliusService +import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNote +import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNoteFilters +import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNotesClient +import uk.gov.justice.digital.hmpps.integrations.prison.toDeliusCaseNote +import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent +import uk.gov.justice.digital.hmpps.telemetry.TelemetryService +import java.net.URI + +@Service +class CaseNotePublished( + private val prisonCaseNotesClient: PrisonCaseNotesClient, + private val deliusService: DeliusService, + private val telemetryService: TelemetryService, +) { + fun handle(event: HmppsDomainEvent) { + val caseNoteId = event.additionalInformation["caseNoteId"] + if (caseNoteId == null) { + telemetryService.trackEvent( + "MissingCaseNoteId", + mapOf( + "eventType" to event.eventType, + "nomsNumber" to event.personReference.findNomsNumber()!! + ) + ) + return + } + + val prisonCaseNote = try { + prisonCaseNotesClient.getCaseNote(URI.create(event.detailUrl!!)) + } catch (ex: HttpStatusCodeException) { + when (ex.statusCode) { + HttpStatus.NOT_FOUND -> { + telemetryService.trackEvent("CaseNoteNotFound", mapOf("detailUrl" to event.detailUrl!!)) + return + } + + else -> throw ex + } + } + + val reasonToIgnore: Lazy = lazy { + PrisonCaseNoteFilters.filters.firstOrNull { it.predicate.invoke(prisonCaseNote!!) }?.reason + } + + if (prisonCaseNote == null || reasonToIgnore.value != null) { + val reason = if (prisonCaseNote == null) { + "case note was not able to be retrieved" + } else { + reasonToIgnore.value!! + } + telemetryService.trackEvent( + "CaseNoteIgnored", + (prisonCaseNote?.properties() ?: emptyMap()) + ("reason" to reason) + ) + return + } + + try { + deliusService.mergeCaseNote(prisonCaseNote.toDeliusCaseNote(event.occurredAt)) + telemetryService.trackEvent("CaseNoteMerged", prisonCaseNote.properties()) + } catch (e: Exception) { + telemetryService.trackEvent( + "CaseNoteMergeFailed", + prisonCaseNote.properties() + ("exception" to (e.message ?: "")) + ) + if (e !is OffenderNotFoundException) throw e + } + } + + private fun PrisonCaseNote.properties() = mapOf( + "caseNoteId" to id, + "type" to type, + "subType" to subType, + "eventId" to eventId.toString(), + "created" to DeliusDateTimeFormatter.format(creationDateTime), + "occurrence" to DeliusDateTimeFormatter.format(occurrenceDateTime), + "location" to locationId + ) +} \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt index 0b02ef8e98..0e983e8617 100644 --- a/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt +++ b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt @@ -1,115 +1,37 @@ package uk.gov.justice.digital.hmpps.messaging +import org.openfolder.kotlinasyncapi.annotation.Schema import org.openfolder.kotlinasyncapi.annotation.channel.Channel import org.openfolder.kotlinasyncapi.annotation.channel.Message import org.openfolder.kotlinasyncapi.annotation.channel.Publish -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.http.HttpStatus import org.springframework.stereotype.Component -import org.springframework.web.client.HttpStatusCodeException import uk.gov.justice.digital.hmpps.converter.NotificationConverter -import uk.gov.justice.digital.hmpps.datetime.DeliusDateTimeFormatter -import uk.gov.justice.digital.hmpps.exceptions.OffenderNotFoundException -import uk.gov.justice.digital.hmpps.integrations.delius.service.DeliusService -import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNote -import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNoteFilters -import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNotesClient -import uk.gov.justice.digital.hmpps.integrations.prison.toDeliusCaseNote import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent import uk.gov.justice.digital.hmpps.message.Notification -import uk.gov.justice.digital.hmpps.telemetry.TelemetryMessagingExtensions.notificationReceived -import uk.gov.justice.digital.hmpps.telemetry.TelemetryService -import java.net.URI @Component @Channel("prison-case-notes-to-probation-queue") class Handler( - val prisonCaseNotesClient: PrisonCaseNotesClient, - val deliusService: DeliusService, - val telemetryService: TelemetryService, + private val caseNotePublished: CaseNotePublished, + private val prisonIdentifierAdded: PrisonIdentifierAdded, override val converter: NotificationConverter, ) : NotificationHandler { companion object { - val log: Logger = LoggerFactory.getLogger(this::class.java) + const val CASE_NOTE_PUBLISHED = "prison.case-note.published" + const val PRISON_IDENTIFIER_ADDED = "probation-case.prison-identifier.added" } - @Publish(messages = [Message(name = "prison/case-note-published")]) + @Publish( + messages = [ + Message(name = "prison/case-note-published"), + Message(title = "probation-case.prison-identifier.added", payload = Schema(HmppsDomainEvent::class)), + ] + ) override fun handle(notification: Notification) { - telemetryService.notificationReceived(notification) - val event = notification.message - val caseNoteId = event.additionalInformation["caseNoteId"] - if (caseNoteId == null) { - log.info("Received ${notification.eventType} for ${event.personReference.findNomsNumber()} without a case note id") - telemetryService.trackEvent( - "MissingCaseNoteId", - mapOf( - "eventType" to event.eventType, - "nomsNumber" to event.personReference.findNomsNumber()!! - ) - ) - return - } - - val prisonCaseNote = try { - prisonCaseNotesClient.getCaseNote(URI.create(event.detailUrl!!)) - } catch (ex: HttpStatusCodeException) { - when (ex.statusCode) { - HttpStatus.NOT_FOUND -> { - telemetryService.trackEvent("CaseNoteNotFound", mapOf("detailUrl" to event.detailUrl!!)) - return - } - - else -> throw ex - } - } - - val reasonToIgnore: Lazy = lazy { - PrisonCaseNoteFilters.filters.firstOrNull { it.predicate.invoke(prisonCaseNote!!) }?.reason - } - - if (prisonCaseNote == null || reasonToIgnore.value != null) { - val reason = if (prisonCaseNote == null) { - "case note was not found" - } else { - telemetryService.trackEvent( - "CaseNoteIgnored", - prisonCaseNote.properties() + ("reason" to (reasonToIgnore.value!!)) - ) - reasonToIgnore.value - } - log.warn("Ignoring case note id {} and type {} because $reason", caseNoteId, notification.eventType) - return - } - - log.debug( - "Found case note {} of type {} {} in case notes service, now pushing to delius with event id {}", - caseNoteId, - prisonCaseNote.type, - prisonCaseNote.subType, - prisonCaseNote.eventId - ) - - try { - deliusService.mergeCaseNote(prisonCaseNote.toDeliusCaseNote(event.occurredAt)) - telemetryService.trackEvent("CaseNoteMerged", prisonCaseNote.properties()) - } catch (e: Exception) { - telemetryService.trackEvent( - "CaseNoteMergeFailed", - prisonCaseNote.properties() + ("exception" to (e.message ?: "")) - ) - if (e !is OffenderNotFoundException) throw e + when (notification.eventType) { + CASE_NOTE_PUBLISHED -> caseNotePublished.handle(notification.message) + PRISON_IDENTIFIER_ADDED -> prisonIdentifierAdded.handle(notification.message) } } - - private fun PrisonCaseNote.properties() = mapOf( - "caseNoteId" to id, - "type" to type, - "subType" to subType, - "eventId" to eventId.toString(), - "created" to DeliusDateTimeFormatter.format(creationDateTime), - "occurrence" to DeliusDateTimeFormatter.format(occurrenceDateTime), - "location" to locationId - ) } diff --git a/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/PrisonIdentifierAdded.kt b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/PrisonIdentifierAdded.kt new file mode 100644 index 0000000000..3bd7449eb9 --- /dev/null +++ b/projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/PrisonIdentifierAdded.kt @@ -0,0 +1,43 @@ +package uk.gov.justice.digital.hmpps.messaging + +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import uk.gov.justice.digital.hmpps.integrations.delius.service.DeliusService +import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNoteFilters +import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNotesClient +import uk.gov.justice.digital.hmpps.integrations.prison.SearchCaseNotes +import uk.gov.justice.digital.hmpps.integrations.prison.SearchCaseNotes.Companion.TYPES_OF_INTEREST +import uk.gov.justice.digital.hmpps.integrations.prison.toDeliusCaseNote +import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent +import uk.gov.justice.digital.hmpps.telemetry.TelemetryService +import java.net.URI + +@Service +class PrisonIdentifierAdded( + private val caseNotesApi: PrisonCaseNotesClient, + private val deliusService: DeliusService, + @Value("\${integrations.prison-case-notes.base_url}") + private val caseNotesBaseUrl: String, + private val telemetryService: TelemetryService +) { + @Transactional + fun handle(event: HmppsDomainEvent) { + val nomsId = checkNotNull(event.personReference.findNomsNumber()) { + "NomsNumber not found for ${event.eventType}" + } + val uri = URI.create("$caseNotesBaseUrl/search/case-notes/$nomsId") + val caseNotes = caseNotesApi.searchCaseNotes(uri, SearchCaseNotes(TYPES_OF_INTEREST)).content + .filter { cn -> PrisonCaseNoteFilters.filters.none { it.predicate.invoke(cn) } } + + caseNotes.forEach { deliusService.mergeCaseNote(it.toDeliusCaseNote(it.occurrenceDateTime)) } + + telemetryService.trackEvent( + "CaseNotesMigrated", mapOf( + "nomsId" to nomsId, + "cause" to event.eventType, + "count" to caseNotes.size.toString() + ) + ) + } +} \ No newline at end of file diff --git a/projects/prison-case-notes-to-probation/src/main/resources/application.yml b/projects/prison-case-notes-to-probation/src/main/resources/application.yml index 6fa12bad9e..2f503c691b 100644 --- a/projects/prison-case-notes-to-probation/src/main/resources/application.yml +++ b/projects/prison-case-notes-to-probation/src/main/resources/application.yml @@ -2,6 +2,9 @@ spring: jackson: default-property-inclusion: non_null + datasource: + hikari: + auto-commit: false jpa: hibernate.ddl-auto: validate properties: @@ -14,6 +17,8 @@ spring: query.mutation_strategy.global_temporary: create_tables: false drop_tables: false + open-in-view: false + security.oauth2.client: registration: default: @@ -57,6 +62,7 @@ integrations: prison-case-notes: client-id: prison-case-notes-to-probation client-secret: prison-case-notes-to-probation + base_url: "http://localhost:${wiremock.port}" logging.level: uk.gov.justice.digital.hmpps: DEBUG diff --git a/projects/prison-case-notes-to-probation/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt b/projects/prison-case-notes-to-probation/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/CaseNotePublishedTest.kt similarity index 89% rename from projects/prison-case-notes-to-probation/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt rename to projects/prison-case-notes-to-probation/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/CaseNotePublishedTest.kt index c706d05883..dfdf40a6f5 100644 --- a/projects/prison-case-notes-to-probation/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt +++ b/projects/prison-case-notes-to-probation/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/CaseNotePublishedTest.kt @@ -10,14 +10,12 @@ import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.* -import uk.gov.justice.digital.hmpps.converter.NotificationConverter import uk.gov.justice.digital.hmpps.data.generator.CaseNoteMessageGenerator import uk.gov.justice.digital.hmpps.exceptions.OffenderNotFoundException import uk.gov.justice.digital.hmpps.exceptions.StaffCodeExhaustedException import uk.gov.justice.digital.hmpps.integrations.delius.service.DeliusService import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNote import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNotesClient -import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent import uk.gov.justice.digital.hmpps.message.Notification import uk.gov.justice.digital.hmpps.prepMessage import uk.gov.justice.digital.hmpps.telemetry.TelemetryService @@ -25,7 +23,7 @@ import java.net.URI import java.time.ZonedDateTime @ExtendWith(MockitoExtension::class) -internal class HandlerTest { +internal class CaseNotePublishedTest { @Mock private lateinit var prisonCaseNotesClient: PrisonCaseNotesClient @@ -36,11 +34,8 @@ internal class HandlerTest { @Mock private lateinit var telemetryService: TelemetryService - @Mock - private lateinit var converter: NotificationConverter - @InjectMocks - private lateinit var handler: Handler + private lateinit var handler: CaseNotePublished @Test fun `when case note not found - noop`() { @@ -48,7 +43,7 @@ internal class HandlerTest { whenever(prisonCaseNotesClient.getCaseNote(URI.create(message.detailUrl!!))) .thenReturn(null) - assertDoesNotThrow { handler.handle(Notification(message = message)) } + assertDoesNotThrow { handler.handle(message) } verify(telemetryService, never()).trackEvent(eq("CaseNoteMerged"), any(), any()) verify(deliusService, never()).mergeCaseNote(any()) } @@ -69,7 +64,7 @@ internal class HandlerTest { ) val message = prepMessage(CaseNoteMessageGenerator.EXISTS_IN_DELIUS).message whenever(prisonCaseNotesClient.getCaseNote(URI.create(message.detailUrl!!))).thenReturn(prisonCaseNote) - handler.handle(Notification(message = message)) + handler.handle(message) verify(deliusService, times(0)).mergeCaseNote(any()) } @@ -91,7 +86,7 @@ internal class HandlerTest { val message = prepMessage(CaseNoteMessageGenerator.EXISTS_IN_DELIUS).message whenever(prisonCaseNotesClient.getCaseNote(URI.create(message.detailUrl!!))).thenReturn(prisonCaseNote) - handler.handle(Notification(message = message)) + handler.handle(message) verify(deliusService, never()).mergeCaseNote(any()) verify(telemetryService).trackEvent(eq("CaseNoteIgnored"), any(), any()) } @@ -117,7 +112,7 @@ internal class HandlerTest { val message = prepMessage(CaseNoteMessageGenerator.EXISTS_IN_DELIUS).message whenever(prisonCaseNotesClient.getCaseNote(URI.create(message.detailUrl!!))).thenReturn(prisonCaseNote) - handler.handle(Notification(message = message)) + handler.handle(message) verify(deliusService, never()).mergeCaseNote(any()) verify(telemetryService).trackEvent(eq("CaseNoteIgnored"), any(), any()) @@ -127,7 +122,7 @@ internal class HandlerTest { fun `get case note from NOMIS has null caseNoteId`() { val message = prepMessage(CaseNoteMessageGenerator.EXISTS_IN_DELIUS).message val prisonOffenderEvent = Notification(message = message.copy(additionalInformation = emptyMap())) - handler.handle(prisonOffenderEvent) + handler.handle(prisonOffenderEvent.message) verify(deliusService, times(0)).mergeCaseNote(any()) verify(prisonCaseNotesClient, times(0)).getCaseNote(any()) } @@ -151,8 +146,7 @@ internal class HandlerTest { whenever(prisonCaseNotesClient.getCaseNote(URI.create(poe.message.detailUrl!!))).thenReturn(prisonCaseNote) whenever(deliusService.mergeCaseNote(any())).thenThrow(OffenderNotFoundException("A001")) - handler.handle(poe) - verify(telemetryService).trackEvent(eq("NotificationReceived"), any(), any()) + handler.handle(poe.message) verify(telemetryService).trackEvent(eq("CaseNoteMergeFailed"), any(), any()) } @@ -175,8 +169,7 @@ internal class HandlerTest { whenever(prisonCaseNotesClient.getCaseNote(URI.create(poe.message.detailUrl!!))).thenReturn(prisonCaseNote) whenever(deliusService.mergeCaseNote(any())).thenThrow(StaffCodeExhaustedException("A999")) - assertThrows { handler.handle(poe) } - verify(telemetryService).trackEvent(eq("NotificationReceived"), any(), any()) + assertThrows { handler.handle(poe.message) } verify(telemetryService).trackEvent(eq("CaseNoteMergeFailed"), any(), any()) } }