Skip to content

Commit f138a2e

Browse files
PI-2729 add subtype filtering in code (#4545)
* PI-2729 add subtype filtering in code --------- Co-authored-by: probation-integration-bot[bot] <177347787+probation-integration-bot[bot]@users.noreply.github.com>
1 parent 159700f commit f138a2e

File tree

12 files changed

+107
-53
lines changed

12 files changed

+107
-53
lines changed

libs/dev-tools/src/main/kotlin/uk/gov/justice/digital/hmpps/TestHelper.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ fun prepNotification(
3030
"{wiremock.port}",
3131
port.toString()
3232
)
33-
)
33+
),
34+
attributes = notification.attributes
3435
)
3536

3637
fun ZonedDateTime.closeTo(dateTime: ZonedDateTime?, unit: ChronoUnit = ChronoUnit.SECONDS, number: Int = 1): Boolean {
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package uk.gov.justice.digital.hmpps.data.generator
22

33
import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent
4+
import uk.gov.justice.digital.hmpps.message.Notification
45
import uk.gov.justice.digital.hmpps.resourceloader.ResourceLoader
56

67
object CaseNoteMessageGenerator {
7-
val EXISTS_IN_DELIUS: HmppsDomainEvent = ResourceLoader.message("case-note-exists-in-delius")
8-
val NEW_TO_DELIUS: HmppsDomainEvent = ResourceLoader.message("case-note-new-to-delius")
9-
val NOT_FOUND: HmppsDomainEvent = ResourceLoader.message("case-note-not-found")
10-
val RESETTLEMENT_PASSPORT: HmppsDomainEvent = ResourceLoader.message("resettlement-passport-casenote")
11-
val NOMS_NUMBER_ADDED: HmppsDomainEvent = ResourceLoader.message("noms-number-added")
8+
val EXISTS_IN_DELIUS: Notification<HmppsDomainEvent> =
9+
ResourceLoader.notification<HmppsDomainEvent>("case-note-exists-in-delius")
10+
val NEW_TO_DELIUS: Notification<HmppsDomainEvent> =
11+
ResourceLoader.notification<HmppsDomainEvent>("case-note-new-to-delius")
12+
val NOT_FOUND: Notification<HmppsDomainEvent> = ResourceLoader.notification<HmppsDomainEvent>("case-note-not-found")
13+
val RESETTLEMENT_PASSPORT: Notification<HmppsDomainEvent> =
14+
ResourceLoader.notification<HmppsDomainEvent>("resettlement-passport-casenote")
15+
val NOMS_NUMBER_ADDED: Notification<HmppsDomainEvent> =
16+
ResourceLoader.notification<HmppsDomainEvent>("noms-number-added")
1217
}

projects/prison-case-notes-to-probation/src/dev/resources/messages/case-note-exists-in-delius.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"MessageId": "ae06c49e-1f41-4b9f-b2f2-dcca610d02cd",
33
"Type": "Notification",
44
"Timestamp": "2019-10-21T14:01:18.500Z",
5-
"Message": "{\"eventType\":\"person.case-note.updated\",\"version\":1,\"description\":\"A prison case note has been amended\",\"occurredAt\":\"2022-10-18T08:19:19.451579+01:00\",\"detailUrl\":\"http://localhost:{wiremock.port}/case-notes/AA0001A/0ec15f8b-6b57-471f-b02a-c89169a6a3e5\",\"additionalInformation\":{\"id\":\"0ec15f8b-6b57-471f-b02a-c89169a6a3e5\",\"legacyId\":\"1111\",\"type\":\"NEG\",\"subType\":\"IEP_WARN\"},\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"AA0001A\"}]}}",
5+
"Message": "{\"eventType\":\"person.case-note.updated\",\"version\":1,\"description\":\"A prison case note has been amended\",\"occurredAt\":\"2022-10-18T08:19:19.451579+01:00\",\"detailUrl\":\"http://localhost:{wiremock.port}/case-notes/AA0001A/0ec15f8b-6b57-471f-b02a-c89169a6a3e5\",\"additionalInformation\":{\"id\":\"0ec15f8b-6b57-471f-b02a-c89169a6a3e5\",\"legacyId\":\"1111\",\"type\":\"GEN\",\"subType\":\"OSE\"},\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"AA0001A\"}]}}",
66
"TopicArn": "arn:aws:sns:eu-west-1:000000000000:offender_events",
77
"MessageAttributes": {
88
"eventType": {
@@ -11,11 +11,11 @@
1111
},
1212
"type": {
1313
"Type": "String",
14-
"Value": "NEG"
14+
"Value": "GEN"
1515
},
1616
"subType": {
1717
"Type": "String",
18-
"Value": "IEP_WARN"
18+
"Value": "OSE"
1919
}
2020
}
2121
}

projects/prison-case-notes-to-probation/src/dev/resources/messages/case-note-new-to-delius.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"MessageId": "ae06c49e-1f41-4b9f-b2f2-dcca610d02cd",
33
"Type": "Notification",
44
"Timestamp": "2019-10-21T14:01:18.500Z",
5-
"Message": "{\"eventType\":\"person.case-note.created\",\"version\":1,\"description\":\"A prison case note has been created\",\"occurredAt\":\"2022-10-18T08:19:19.451579+01:00\",\"detailUrl\":\"http://localhost:{wiremock.port}/case-notes/AA0001A/2cf4f9e1-df22-49a1-a2a7-5968a96529e3\",\"additionalInformation\":{\"id\":\"2cf4f9e1-df22-49a1-a2a7-5968a96529e3\",\"legacyId\":\"2222\",\"type\":\"NEG\",\"subType\":\"IEP_WARN\"},\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"AA0001A\"}]}}",
5+
"Message": "{\"eventType\":\"person.case-note.created\",\"version\":1,\"description\":\"A prison case note has been created\",\"occurredAt\":\"2022-10-18T08:19:19.451579+01:00\",\"detailUrl\":\"http://localhost:{wiremock.port}/case-notes/AA0001A/2cf4f9e1-df22-49a1-a2a7-5968a96529e3\",\"additionalInformation\":{\"id\":\"2cf4f9e1-df22-49a1-a2a7-5968a96529e3\",\"legacyId\":\"2222\",\"type\":\"KA\",\"subType\":\"KS\"},\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"AA0001A\"}]}}",
66
"TopicArn": "arn:aws:sns:eu-west-1:000000000000:offender_events",
77
"MessageAttributes": {
88
"eventType": {
@@ -11,11 +11,11 @@
1111
},
1212
"type": {
1313
"Type": "String",
14-
"Value": "NEG"
14+
"Value": "KA"
1515
},
1616
"subType": {
1717
"Type": "String",
18-
"Value": "IEP_WARN"
18+
"Value": "KS"
1919
}
2020
}
2121
}

projects/prison-case-notes-to-probation/src/dev/resources/messages/case-note-not-found.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"MessageId": "ae06c49e-1f41-4b9f-b2f2-dcca610d02cd",
33
"Type": "Notification",
44
"Timestamp": "2019-10-21T14:01:18.500Z",
5-
"Message": "{\"eventType\":\"person.case-note.created\",\"version\":1,\"description\":\"A prison case note has been created\",\"occurredAt\":\"2022-10-18T08:19:19.451579+01:00\",\"detailUrl\":\"http://localhost:{wiremock.port}/case-notes/AA0001A/da82e1df-1a74-486e-842c-6ce961575211\",\"additionalInformation\":{\"id\":\"da82e1df-1a74-486e-842c-6ce961575211\",\"legacyId\":\"432109\",\"type\":\"NEG\",\"subType\":\"IEP_WARN\"},\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"AA0001A\"}]}}",
5+
"Message": "{\"eventType\":\"person.case-note.created\",\"version\":1,\"description\":\"A prison case note has been created\",\"occurredAt\":\"2022-10-18T08:19:19.451579+01:00\",\"detailUrl\":\"http://localhost:{wiremock.port}/case-notes/AA0001A/da82e1df-1a74-486e-842c-6ce961575211\",\"additionalInformation\":{\"id\":\"da82e1df-1a74-486e-842c-6ce961575211\",\"legacyId\":\"432109\",\"type\":\"OMIC\",\"subType\":\"GEN\"},\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"AA0001A\"}]}}",
66
"TopicArn": "arn:aws:sns:eu-west-1:000000000000:offender_events",
77
"MessageAttributes": {
88
"eventType": {
@@ -11,11 +11,11 @@
1111
},
1212
"type": {
1313
"Type": "String",
14-
"Value": "NEG"
14+
"Value": "OMIC"
1515
},
1616
"subType": {
1717
"Type": "String",
18-
"Value": "IEP_WARN"
18+
"Value": "GEN"
1919
}
2020
}
2121
}

projects/prison-case-notes-to-probation/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/CaseNotesIntegrationTest.kt

+28-8
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ import org.springframework.test.context.bean.override.mockito.MockitoSpyBean
2020
import uk.gov.justice.digital.hmpps.audit.repository.AuditedInteractionRepository
2121
import uk.gov.justice.digital.hmpps.data.generator.*
2222
import uk.gov.justice.digital.hmpps.datetime.DeliusDateTimeFormatter
23+
import uk.gov.justice.digital.hmpps.integrations.delius.model.DeliusCaseNote
2324
import uk.gov.justice.digital.hmpps.integrations.delius.repository.CaseNoteRepository
2425
import uk.gov.justice.digital.hmpps.integrations.delius.repository.OffenderRepository
2526
import uk.gov.justice.digital.hmpps.integrations.delius.repository.StaffRepository
27+
import uk.gov.justice.digital.hmpps.message.MessageAttribute
28+
import uk.gov.justice.digital.hmpps.message.MessageAttributes
2629
import uk.gov.justice.digital.hmpps.messaging.HmppsChannelManager
2730
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService
2831
import uk.gov.justice.digital.hmpps.test.CustomMatchers.isCloseTo
32+
import java.time.Duration
2933
import java.time.ZonedDateTime
3034

3135
const val CASE_NOTE_MERGED = "CaseNoteMerged"
@@ -62,10 +66,10 @@ class CaseNotesIntegrationTest {
6266
val nomisCaseNote = PrisonCaseNoteGenerator.EXISTING_IN_BOTH
6367

6468
channelManager.getChannel(queueName).publishAndWait(
65-
prepMessage(CaseNoteMessageGenerator.EXISTS_IN_DELIUS, wireMockserver.port())
69+
prepNotification(CaseNoteMessageGenerator.EXISTS_IN_DELIUS, wireMockserver.port())
6670
)
6771

68-
val saved = caseNoteRepository.findByNomisId(nomisCaseNote.eventId)
72+
val saved = caseNoteRepository.findByExternalReference("${DeliusCaseNote.URN_PREFIX}${nomisCaseNote.id}")
6973

7074
assertThat(
7175
saved?.notes,
@@ -93,11 +97,11 @@ class CaseNotesIntegrationTest {
9397
assertNull(original)
9498

9599
channelManager.getChannel(queueName).publishAndWait(
96-
prepMessage(CaseNoteMessageGenerator.NEW_TO_DELIUS, wireMockserver.port())
100+
prepNotification(CaseNoteMessageGenerator.NEW_TO_DELIUS, wireMockserver.port())
97101
)
98102

99103
verify(telemetryService).trackEvent(eq(CASE_NOTE_MERGED), anyMap(), anyMap())
100-
val saved = caseNoteRepository.findByNomisId(nomisCaseNote.eventId)
104+
val saved = caseNoteRepository.findByExternalReference("${DeliusCaseNote.URN_PREFIX}${nomisCaseNote.id}")
101105
assertNotNull(saved)
102106

103107
assertThat(
@@ -135,7 +139,7 @@ class CaseNotesIntegrationTest {
135139
@Test
136140
fun `case note not found - noop`() {
137141
channelManager.getChannel(queueName).publishAndWait(
138-
prepMessage(CaseNoteMessageGenerator.NOT_FOUND, wireMockserver.port())
142+
prepNotification(CaseNoteMessageGenerator.NOT_FOUND, wireMockserver.port())
139143
)
140144

141145
verify(telemetryService, never()).trackEvent(eq(CASE_NOTE_MERGED), anyMap(), anyMap())
@@ -146,11 +150,11 @@ class CaseNotesIntegrationTest {
146150
val nomisCaseNote = PrisonCaseNoteGenerator.RESETTLEMENT_PASSPORT
147151

148152
channelManager.getChannel(queueName).publishAndWait(
149-
prepMessage(CaseNoteMessageGenerator.RESETTLEMENT_PASSPORT, wireMockserver.port())
153+
prepNotification(CaseNoteMessageGenerator.RESETTLEMENT_PASSPORT, wireMockserver.port())
150154
)
151155

152156
verify(telemetryService).trackEvent(eq(CASE_NOTE_MERGED), anyMap(), anyMap())
153-
val saved = caseNoteRepository.findByNomisId(nomisCaseNote.eventId)
157+
val saved = caseNoteRepository.findByExternalReference("${DeliusCaseNote.URN_PREFIX}${nomisCaseNote.id}")
154158
assertNotNull(saved)
155159

156160
assertThat(
@@ -179,7 +183,7 @@ class CaseNotesIntegrationTest {
179183
assert(originals.isEmpty())
180184

181185
channelManager.getChannel(queueName).publishAndWait(
182-
prepMessage(CaseNoteMessageGenerator.NOMS_NUMBER_ADDED, wireMockserver.port())
186+
prepNotification(CaseNoteMessageGenerator.NOMS_NUMBER_ADDED, wireMockserver.port())
183187
)
184188

185189
verify(telemetryService).trackEvent(
@@ -190,4 +194,20 @@ class CaseNotesIntegrationTest {
190194
val saved = caseNoteRepository.findAll().filter { it.offenderId == offender.id }
191195
assertThat(saved.size, equalTo(4))
192196
}
197+
198+
@Test
199+
fun `case note not of interest - noop`() {
200+
val existing = CaseNoteMessageGenerator.EXISTS_IN_DELIUS
201+
channelManager.getChannel(queueName).publishAndWait(
202+
prepNotification(
203+
existing.copy(
204+
attributes = MessageAttributes(existing.eventType!!).apply {
205+
this["type"] = MessageAttribute("String", "NOTOF")
206+
this["subType"] = MessageAttribute("String", "INTEREST")
207+
}
208+
), wireMockserver.port())
209+
)
210+
211+
verify(telemetryService, never()).trackEvent(eq(CASE_NOTE_MERGED), anyMap(), anyMap())
212+
}
193213
}

projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/model/DeliusCaseNote.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import java.time.ZonedDateTime
99
import java.util.*
1010

1111
data class DeliusCaseNote(val header: CaseNoteHeader, val body: CaseNoteBody) {
12-
val urn = header.uuid?.let { "urn:uk:gov:hmpps:prison-case-note:${it}" }
12+
val urn = header.uuid?.let { "$URN_PREFIX${it}" }
13+
14+
companion object {
15+
const val URN_PREFIX = "urn:uk:gov:hmpps:prison-case-note:"
16+
}
1317
}
1418

1519
data class CaseNoteHeader(val nomisId: String, val legacyId: Long, val uuid: UUID?)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package uk.gov.justice.digital.hmpps.integrations.prison
2+
3+
object CaseNoteTypesOfInterest {
4+
private val typeSubTypeMap = mapOf(
5+
"PRISON" to setOf("RELEASE"),
6+
"TRANSFER" to setOf("FROMTOL"),
7+
"GEN" to setOf("OSE"),
8+
"RESET" to setOf("BCST"),
9+
"ALERT" to setOf(),
10+
"OMIC" to setOf(),
11+
"OMIC_OPD" to setOf(),
12+
"KA" to setOf()
13+
)
14+
15+
fun forSearchRequest(): Set<TypeSubTypeRequest> =
16+
typeSubTypeMap.map { TypeSubTypeRequest(it.key, it.value) }.toSet()
17+
18+
fun verifyOfInterest(type: String, subType: String): Boolean {
19+
val subTypes = typeSubTypeMap[type] ?: return false
20+
return subTypes.isEmpty() || subTypes.contains(subType)
21+
}
22+
}

projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/prison/PrisonCaseNotesClient.kt

+1-13
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,7 @@ data class SearchCaseNotes(
2525
val page: Int = 1,
2626
val size: Int = Int.MAX_VALUE,
2727
val sort: String = "occurredAt,desc",
28-
) {
29-
companion object {
30-
val TYPES_OF_INTEREST = setOf(
31-
TypeSubTypeRequest("PRISON", setOf("RELEASE")),
32-
TypeSubTypeRequest("TRANSFER", setOf("FROMTOL")),
33-
TypeSubTypeRequest("GEN", setOf("OSE")),
34-
TypeSubTypeRequest("ALERT"),
35-
TypeSubTypeRequest("OMIC"),
36-
TypeSubTypeRequest("OMIC_OPD"),
37-
TypeSubTypeRequest("KA"),
38-
)
39-
}
40-
}
28+
)
4129

4230
data class TypeSubTypeRequest(val type: String, val subTypes: Set<String> = setOf())
4331

projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt

+19-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ import org.openfolder.kotlinasyncapi.annotation.channel.Message
66
import org.openfolder.kotlinasyncapi.annotation.channel.Publish
77
import org.springframework.stereotype.Component
88
import uk.gov.justice.digital.hmpps.converter.NotificationConverter
9+
import uk.gov.justice.digital.hmpps.integrations.prison.CaseNoteTypesOfInterest
910
import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent
1011
import uk.gov.justice.digital.hmpps.message.Notification
12+
import uk.gov.justice.digital.hmpps.messaging.Handler.Companion.CASE_NOTE_PUBLISHED
13+
import uk.gov.justice.digital.hmpps.messaging.Handler.Companion.PERSON_CASE_NOTE_CREATED
14+
import uk.gov.justice.digital.hmpps.messaging.Handler.Companion.PERSON_CASE_NOTE_UPDATED
1115

1216
@Component
1317
@Channel("prison-case-notes-to-probation-queue")
@@ -34,10 +38,21 @@ class Handler(
3438
]
3539
)
3640
override fun handle(notification: Notification<HmppsDomainEvent>) {
37-
when (notification.eventType) {
38-
CASE_NOTE_PUBLISHED -> caseNotePublished.handle(notification.message)
39-
PRISON_IDENTIFIER_ADDED -> prisonIdentifierAdded.handle(notification.message)
40-
PERSON_CASE_NOTE_CREATED, PERSON_CASE_NOTE_UPDATED -> personCaseNote.handle(notification.message)
41+
when {
42+
notification.eventType == PRISON_IDENTIFIER_ADDED -> prisonIdentifierAdded.handle(notification.message)
43+
notification.isCaseNoteOfInterest() -> personCaseNote.handle(notification.message)
44+
notification.publishedOfInterest() -> caseNotePublished.handle(notification.message)
4145
}
4246
}
4347
}
48+
49+
private fun Notification<*>.publishedOfInterest(): Boolean =
50+
eventType == CASE_NOTE_PUBLISHED && typeIsOfInterest()
51+
52+
private fun Notification<*>.isCaseNoteOfInterest(): Boolean =
53+
(eventType == PERSON_CASE_NOTE_CREATED || eventType == PERSON_CASE_NOTE_UPDATED) && typeIsOfInterest()
54+
55+
val Notification<*>.type get() = attributes["type"]?.value
56+
val Notification<*>.subType get() = attributes["subType"]?.value
57+
58+
private fun Notification<*>.typeIsOfInterest() = CaseNoteTypesOfInterest.verifyOfInterest(type!!, subType!!)

projects/prison-case-notes-to-probation/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/PrisonIdentifierAdded.kt

+3-6
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@ import org.springframework.beans.factory.annotation.Value
44
import org.springframework.stereotype.Service
55
import org.springframework.transaction.annotation.Transactional
66
import uk.gov.justice.digital.hmpps.integrations.delius.service.DeliusService
7-
import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNoteFilters
8-
import uk.gov.justice.digital.hmpps.integrations.prison.PrisonCaseNotesClient
9-
import uk.gov.justice.digital.hmpps.integrations.prison.SearchCaseNotes
10-
import uk.gov.justice.digital.hmpps.integrations.prison.SearchCaseNotes.Companion.TYPES_OF_INTEREST
11-
import uk.gov.justice.digital.hmpps.integrations.prison.toDeliusCaseNote
7+
import uk.gov.justice.digital.hmpps.integrations.prison.*
8+
import uk.gov.justice.digital.hmpps.integrations.prison.CaseNoteTypesOfInterest.forSearchRequest
129
import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent
1310
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService
1411
import java.net.URI
@@ -27,7 +24,7 @@ class PrisonIdentifierAdded(
2724
"NomsNumber not found for ${event.eventType}"
2825
}
2926
val uri = URI.create("$caseNotesBaseUrl/search/case-notes/$nomsId")
30-
val caseNotes = caseNotesApi.searchCaseNotes(uri, SearchCaseNotes(TYPES_OF_INTEREST)).content
27+
val caseNotes = caseNotesApi.searchCaseNotes(uri, SearchCaseNotes(forSearchRequest())).content
3128
.filter { cn -> PrisonCaseNoteFilters.filters.none { it.predicate.invoke(cn) } }
3229

3330
caseNotes.forEach { deliusService.mergeCaseNote(it.toDeliusCaseNote()) }

0 commit comments

Comments
 (0)