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

PI-1573 #2412

Merged
merged 1 commit into from
Oct 19, 2023
Merged

PI-1573 #2412

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 @@ -83,7 +83,7 @@ class DataLoader(

override fun onApplicationEvent(are: ApplicationReadyEvent) {
createReferenceData()
createReleasablePerson()
createReleasablePerson(PersonGenerator.RELEASABLE)
createRecallablePerson()
createPersonToDie()
createMatchablePerson()
Expand All @@ -94,7 +94,8 @@ class DataLoader(
createTemporaryAbsenceReturnFromRotl()
createIrcReleased()
createIrcInCustody()
createReleasableEcslPerson()
createReleasablePerson(PersonGenerator.RELEASABLE_ECSL_ACTIVE)
createReleasablePerson(PersonGenerator.RELEASABLE_ECSL_INACTIVE)
}

private fun createReferenceData() {
Expand Down Expand Up @@ -149,14 +150,9 @@ class DataLoader(
createEvent(EventGenerator.custodialEvent(PersonGenerator.DIED, InstitutionGenerator.DEFAULT))
}

private fun createReleasablePerson() {
createPerson(PersonGenerator.RELEASABLE)
createEvent(EventGenerator.custodialEvent(PersonGenerator.RELEASABLE, InstitutionGenerator.DEFAULT))
}

private fun createReleasableEcslPerson() {
createPerson(PersonGenerator.RELEASABLE_ECSL)
createEvent(EventGenerator.custodialEvent(PersonGenerator.RELEASABLE_ECSL, InstitutionGenerator.DEFAULT))
private fun createReleasablePerson(person: Person) {
createPerson(person)
createEvent(EventGenerator.custodialEvent(person, InstitutionGenerator.DEFAULT))
}

private fun createRecallablePerson() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ object NotificationGenerator {
val PRISONER_ROTL_RETURN = ResourceLoader.notification<HmppsDomainEvent>("prisoner-received-rotl")
val PRISONER_IRC_RELEASED = ResourceLoader.notification<HmppsDomainEvent>("prisoner-received-irc-released")
val PRISONER_IRC_IN_CUSTODY = ResourceLoader.notification<HmppsDomainEvent>("prisoner-received-irc-custody")
val PRISONER_RELEASED_ECSL = ResourceLoader.notification<HmppsDomainEvent>("prisoner-released-ecsl")
val PRISONER_RELEASED_ECSL_ACTIVE = ResourceLoader.notification<HmppsDomainEvent>("prisoner-released-ecsl-active")
val PRISONER_RELEASED_ECSL_INACTIVE = ResourceLoader.notification<HmppsDomainEvent>("prisoner-released-ecsl-inactive")
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ object PersonGenerator {
val ROTL = generate(NotificationGenerator.PRISONER_ROTL_RETURN.nomsId())
val IRC_RELEASED = generate(NotificationGenerator.PRISONER_IRC_RELEASED.nomsId())
val IRC_IN_CUSTODY = generate(NotificationGenerator.PRISONER_IRC_IN_CUSTODY.nomsId())
val RELEASABLE_ECSL = generate(NotificationGenerator.PRISONER_RELEASED_ECSL.nomsId())
val RELEASABLE_ECSL_ACTIVE = generate(NotificationGenerator.PRISONER_RELEASED_ECSL_ACTIVE.nomsId())
val RELEASABLE_ECSL_INACTIVE = generate(NotificationGenerator.PRISONER_RELEASED_ECSL_INACTIVE.nomsId())

fun generate(nomsNumber: String, id: Long = IdGenerator.getAndIncrement()) = Person(id, nomsNumber)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"Type": "Notification",
"MessageId": "682dd2a4-c193-461c-a8e9-cc976a961aa5",
"TopicArn": "",
"Message": "{\"eventType\":\"prison-offender-events.prisoner.released\",\"additionalInformation\":{\"nomsNumber\":\"A0015AA\",\"reason\":\"RELEASED\",\"details\":\"Movement reason code ECSL\",\"nomisMovementReasonCode\":\"ECSL\",\"currentLocation\":\"OUTSIDE_PRISON\",\"prisonId\":\"WSI\",\"currentPrisonStatus\":\"NOT_UNDER_PRISON_CARE\"},\"version\":1,\"occurredAt\":\"2022-05-04T07:03:50.912169+01:00\",\"publishedAt\":\"2022-05-04T08:00:33.477735848+01:00\",\"description\":\"A prisoner has been released from prison\",\"personReference\":{\"identifiers\":[{\"type\":\"NOMS\",\"value\":\"A0015AA\"}]}}",
"Timestamp": "2022-05-04T07:00:33.487Z",
"SignatureVersion": "1",
"Signature": "",
"SigningCertURL": "",
"UnsubscribeURL": "",
"MessageAttributes": {
"eventType": {
"Type": "String",
"Value": "prison-offender-events.prisoner.released"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import uk.gov.justice.digital.hmpps.test.CustomMatchers.isCloseTo
import java.time.ZonedDateTime

class PcstdIntegrationTest : PcstdIntegrationTestBase() {
private val releaseOnLicence = "Released on Licence"

@Test
fun `release a prisoner`() {
val notification = NotificationGenerator.PRISONER_RELEASED
Expand All @@ -38,7 +40,7 @@ class PcstdIntegrationTest : PcstdIntegrationTestBase() {

verifyCustodyHistory(
custody,
CustodyEventTester(CustodyEventTypeCode.STATUS_CHANGE, "Released on Licence"),
CustodyEventTester(CustodyEventTypeCode.STATUS_CHANGE, releaseOnLicence),
CustodyEventTester(
CustodyEventTypeCode.LOCATION_CHANGE,
InstitutionGenerator.STANDARD_INSTITUTIONS[InstitutionCode.IN_COMMUNITY]?.description
Expand Down Expand Up @@ -443,9 +445,9 @@ class PcstdIntegrationTest : PcstdIntegrationTestBase() {
}

@Test
fun `release a ecsl prisoner`() {
fun `release a ecsl prisoner with feature active`() {
whenever(featureFlags.enabled("messages_released_ecsl")).thenReturn(true)
val notification = NotificationGenerator.PRISONER_RELEASED_ECSL
val notification = NotificationGenerator.PRISONER_RELEASED_ECSL_ACTIVE
val nomsNumber = notification.nomsId()
assertTrue(getCustody(nomsNumber).isInCustody())

Expand All @@ -458,7 +460,7 @@ class PcstdIntegrationTest : PcstdIntegrationTestBase() {

verifyCustodyHistory(
custody,
CustodyEventTester(CustodyEventTypeCode.STATUS_CHANGE, "Released on Licence"),
CustodyEventTester(CustodyEventTypeCode.STATUS_CHANGE, releaseOnLicence),
CustodyEventTester(
CustodyEventTypeCode.LOCATION_CHANGE,
InstitutionGenerator.STANDARD_INSTITUTIONS[InstitutionCode.IN_COMMUNITY]?.description
Expand All @@ -478,4 +480,41 @@ class PcstdIntegrationTest : PcstdIntegrationTestBase() {
)
}
}

@Test
fun `release a ecsl prisoner with feature inactive`() {
whenever(featureFlags.enabled("messages_released_ecsl")).thenReturn(false)
val notification = NotificationGenerator.PRISONER_RELEASED_ECSL_INACTIVE
val nomsNumber = notification.nomsId()
assertTrue(getCustody(nomsNumber).isInCustody())

channelManager.getChannel(queueName).publishAndWait(notification)

val custody = getCustody(nomsNumber)
assertFalse(custody.isInCustody())

verifyRelease(custody, notification.message.occurredAt, ReleaseTypeCode.ADULT_LICENCE)

verifyCustodyHistory(
custody,
CustodyEventTester(CustodyEventTypeCode.STATUS_CHANGE, releaseOnLicence),
CustodyEventTester(
CustodyEventTypeCode.LOCATION_CHANGE,
InstitutionGenerator.STANDARD_INSTITUTIONS[InstitutionCode.IN_COMMUNITY]?.description
)
)

verifyContact(custody, ContactType.Code.RELEASE_FROM_CUSTODY)

verifyTelemetry("Released", "LocationUpdated", "StatusUpdated") {
mapOf(
"occurredAt" to notification.message.occurredAt.toString(),
"nomsNumber" to "A0015AA",
"institution" to "WSI",
"reason" to "RELEASED",
"movementReason" to "ECSL",
"movementType" to "Released"
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ class Handler(
)

if (config.featureFlag != null && !featureFlags.enabled(config.featureFlag)) {
return telemetryService.trackEvent(
"FeatureFlagNotActive",
movement.telemetryProperties() + ("featureFlag" to config.featureFlag)
)
if (config.reasonOverride == null) {
return telemetryService.trackEvent(
"FeatureFlagNotActive",
movement.telemetryProperties() + ("featureFlag" to config.featureFlag)
)
} else {
movement.reasonOverride = config.reasonOverride
}
}

val results = actionProcessor.processActions(movement, config.actionNames)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ sealed interface PrisonerMovement {
val type: Type
val reason: String
val occurredAt: ZonedDateTime
var reasonOverride: String?

data class Received(
override val nomsId: String,
override val prisonId: String,
override val type: Type,
override val reason: String,
override val occurredAt: ZonedDateTime
override val occurredAt: ZonedDateTime,
override var reasonOverride: String? = null
) : PrisonerMovement

data class Released(
override val nomsId: String,
override val prisonId: String?,
override val type: Type,
override val reason: String,
override val occurredAt: ZonedDateTime
override val occurredAt: ZonedDateTime,
override var reasonOverride: String? = null
) : PrisonerMovement

enum class Type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ data class PrisonerMovementConfig(
val types: List<PrisonerMovement.Type>,
val reasons: List<String> = listOf(),
val actionNames: List<String> = listOf(),
val featureFlag: String? = null
val featureFlag: String? = null,
val reasonOverride: String? = null
) {
fun validFor(type: PrisonerMovement.Type, reason: String): Boolean =
type in types && (reasons.isEmpty() || reason in reasons)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private fun PrisonerMovement.releaseType(): ReleaseTypeCode {
if (type != PrisonerMovement.Type.RELEASED) {
throw IgnorableMessageException("UnsupportedReleaseType")
}
return when (reason) {
return when (reasonOverride ?: reason) {
"ECSL" -> ReleaseTypeCode.END_CUSTODY_SUPERVISED_LICENCE
else -> ReleaseTypeCode.ADULT_LICENCE
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ prisoner.movement.configs:
- RELEASED
reasons:
- ECSL
reasonOverride: RO
actionNames:
- Release
- UpdateStatus
Expand Down