Skip to content

Commit

Permalink
PI-2670 - Feature flag to toggle data creation
Browse files Browse the repository at this point in the history
- Adds a feature flag that when enabled will save records to the database, and when disabled will not save records to the database
- Expanded telemetry logging to include further details about record creation
  • Loading branch information
joseph-bcl committed Dec 12, 2024
1 parent 91794af commit a1dc4e1
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import org.springframework.boot.test.mock.mockito.SpyBean
import uk.gov.justice.digital.hmpps.audit.entity.AuditedInteraction
import uk.gov.justice.digital.hmpps.audit.service.AuditedInteractionService
import uk.gov.justice.digital.hmpps.data.generator.MessageGenerator
import uk.gov.justice.digital.hmpps.flags.FeatureFlags
import uk.gov.justice.digital.hmpps.integrations.delius.audit.BusinessInteractionCode
import uk.gov.justice.digital.hmpps.integrations.delius.entity.Person
import uk.gov.justice.digital.hmpps.integrations.delius.entity.PersonManagerRepository
import uk.gov.justice.digital.hmpps.integrations.delius.entity.PersonRepository
import uk.gov.justice.digital.hmpps.integrations.delius.entity.ReferenceData
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonAddress
Expand Down Expand Up @@ -63,12 +65,19 @@ internal class IntegrationTest {
@SpyBean
lateinit var addressRepository: PersonAddressRepository

@SpyBean
lateinit var personManagerRepository: PersonManagerRepository

@SpyBean
lateinit var personService: PersonService

@MockBean
private lateinit var featureFlags: FeatureFlags

@BeforeEach
fun setup() {
doReturn("A111111").whenever(personService).generateCrn()
whenever(featureFlags.enabled("common-platform-record-creation-toggle")).thenReturn(true)
}

@Test
Expand Down Expand Up @@ -319,10 +328,21 @@ internal class IntegrationTest {
})
}

@Test
fun `When feature flag is disabled no records are inserted`() {
whenever(featureFlags.enabled("common-platform-record-creation-toggle")).thenReturn(false)
val notification = Notification(message = MessageGenerator.COMMON_PLATFORM_EVENT)
channelManager.getChannel(queueName).publishAndWait(notification)
verify(personService).insertPerson(any(), any())
thenNoRecordsAreInserted()
verify(auditedInteractionService, Mockito.never())
.createAuditedInteraction(any(), any(), eq(AuditedInteraction.Outcome.FAIL), any(), anyOrNull())
}

private fun thenNoRecordsAreInserted() {
verify(personService, never()).insertAddress(any())
verify(addressRepository, never()).save(any())
verify(personRepository, never()).save(any())
verify(personManagerRepository, never()).save(any())
verify(auditedInteractionService, Mockito.never())
.createAuditedInteraction(any(), any(), eq(AuditedInteraction.Outcome.SUCCESS), any(), anyOrNull())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.openfolder.kotlinasyncapi.annotation.channel.Message
import org.openfolder.kotlinasyncapi.annotation.channel.Publish
import org.springframework.stereotype.Component
import uk.gov.justice.digital.hmpps.converter.NotificationConverter
import uk.gov.justice.digital.hmpps.flags.FeatureFlags
import uk.gov.justice.digital.hmpps.integrations.client.ProbationMatchRequest
import uk.gov.justice.digital.hmpps.integrations.client.ProbationSearchClient
import uk.gov.justice.digital.hmpps.message.Notification
Expand All @@ -20,7 +21,8 @@ class Handler(
private val notifier: Notifier,
private val telemetryService: TelemetryService,
private val personService: PersonService,
private val probationSearchClient: ProbationSearchClient
private val probationSearchClient: ProbationSearchClient,
private val featureFlags: FeatureFlags
) : NotificationHandler<CommonPlatformHearing> {

@Publish(messages = [Message(title = "COMMON_PLATFORM_HEARING", payload = Schema(CommonPlatformHearing::class))])
Expand All @@ -41,8 +43,6 @@ class Handler(
return
}

val courtCode = notification.message.hearing.courtCentre.code

defendants.forEach { defendant ->

val matchRequest = defendant.toProbationMatchRequest() ?: return@forEach
Expand All @@ -54,19 +54,44 @@ class Handler(
}

// Insert each defendant as a person record
val savedEntities = personService.insertPerson(defendant, courtCode)

notifier.caseCreated(savedEntities.person)
savedEntities.address?.let { notifier.addressCreated(it) }
val savedEntities = personService.insertPerson(defendant, notification.message.hearing.courtCentre.code)

val eventName = if (featureFlags.enabled("common-platform-record-creation-toggle")) {
notifier.caseCreated(savedEntities.person)
savedEntities.address?.let { notifier.addressCreated(it) }
"PersonCreated"
} else {
"SimulatedPersonCreated"
}

telemetryService.trackEvent(
"PersonCreated", mapOf(
eventName,
mapOf(
"hearingId" to notification.message.hearing.id,
"CRN" to savedEntities.person.crn,
"personId" to savedEntities.person.id.toString(),
"firstName" to savedEntities.person.forename,
"surname" to savedEntities.person.surname,
"dob" to savedEntities.person.dateOfBirth.toString(),
"genderCode" to savedEntities.person.gender.description,
"pnc" to savedEntities.person.pncNumber.toString(),
"personManagerId" to savedEntities.personManager.id.toString(),
"allocationDate" to savedEntities.personManager.allocationDate.toString(),
"allocationReason" to savedEntities.personManager.allocationReason.description,
"providerCode" to savedEntities.personManager.provider.code,
"teamCode" to savedEntities.personManager.team.code,
"staffCode" to savedEntities.personManager.staff.code,
"equalityId" to savedEntities.equality.id.toString(),
"addressId" to savedEntities.address?.id.toString()
"addressId" to savedEntities.address?.id.toString(),
"buildingName" to savedEntities.address?.buildingName.toString(),
"addressNumber" to savedEntities.address?.addressNumber.toString(),
"streetName" to savedEntities.address?.streetName.toString(),
"town" to savedEntities.address?.town.toString(),
"district" to savedEntities.address?.district.toString(),
"county" to savedEntities.address?.county.toString(),
"type" to savedEntities.address?.type?.description.toString(),
"status" to savedEntities.address?.status?.description.toString(),
"postcode" to savedEntities.address?.postcode.toString(),
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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.flags.FeatureFlags
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
Expand All @@ -27,113 +28,138 @@ class PersonService(
private val staffRepository: StaffRepository,
private val referenceDataRepository: ReferenceDataRepository,
private val personAddressRepository: PersonAddressRepository,
private val osClient: OsClient
private val osClient: OsClient,
private val featureFlags: FeatureFlags
) : AuditableService(auditedInteractionService) {

@Transactional
fun insertPerson(defendant: Defendant, courtCode: String): InsertPersonResult =
audit(BusinessInteractionCode.INSERT_PERSON) { audit ->
if (featureFlags.enabled("common-platform-record-creation-toggle")) {
audit(BusinessInteractionCode.INSERT_PERSON) { audit ->
val result = insertPersonLogic(defendant, courtCode)
audit["offenderId"] = result.person.id!!
result
}
} else {
insertPersonLogic(defendant, courtCode)
}

val dateOfBirth = defendant.personDefendant?.personDetails?.dateOfBirth
?: throw IllegalArgumentException("Date of birth not found in message")
fun insertPersonLogic(defendant: Defendant, courtCode: String): InsertPersonResult {
val dateOfBirth = defendant.personDefendant?.personDetails?.dateOfBirth
?: throw IllegalArgumentException("Date of birth not found in message")

// Under 10 years old validation
dateOfBirth.let {
val age = Period.between(it, LocalDate.now()).years
require(age > 10) {
"Date of birth would indicate person is under ten years old: $it"
}
// Under 10 years old validation
dateOfBirth.let {
val age = Period.between(it, LocalDate.now()).years
require(age > 10) {
"Date of birth would indicate person is under ten years old: $it"
}
}

// Person record
val savedPerson = personRepository.save(defendant.toPerson())

val courtLinkedProvider = courtRepository.getByOuCode(courtCode).provider
val initialAllocation = referenceDataRepository.initialAllocationReason()
val unallocatedTeam = teamRepository.findByCode(courtLinkedProvider.code + "UAT")
val unallocatedStaff = staffRepository.findByCode(unallocatedTeam.code + "U")

// Person manager record
val manager = PersonManager(
person = savedPerson,
staff = unallocatedStaff,
team = unallocatedTeam,
provider = courtLinkedProvider,
softDeleted = false,
active = true,
allocationReason = initialAllocation,
staffEmployeeID = unallocatedStaff.id,
trustProviderTeamId = unallocatedTeam.id,
allocationDate = LocalDateTime.of(1900, 1, 1, 0, 0)
// Person record
val savedPerson = if (featureFlags.enabled("common-platform-record-creation-toggle")) {
personRepository.save(defendant.toPerson())
} else {
defendant.toPerson()
}

)
val savedManager = personManagerRepository.save(manager)
val courtLinkedProvider = courtRepository.getByOuCode(courtCode).provider
val initialAllocation = referenceDataRepository.initialAllocationReason()
val unallocatedTeam = teamRepository.findByCode(courtLinkedProvider.code + "UAT")
val unallocatedStaff = staffRepository.findByCode(unallocatedTeam.code + "U")

// Person manager record
val manager = PersonManager(
person = savedPerson,
staff = unallocatedStaff,
team = unallocatedTeam,
provider = courtLinkedProvider,
softDeleted = false,
active = true,
allocationReason = initialAllocation,
staffEmployeeID = unallocatedStaff.id,
trustProviderTeamId = unallocatedTeam.id,
allocationDate = LocalDateTime.of(1900, 1, 1, 0, 0)

// Equality record
val equality = Equality(
id = null,
personId = savedPerson.id!!,
softDeleted = false,
)
)

val savedEquality = equalityRepository.save(equality)
val savedManager = if (featureFlags.enabled("common-platform-record-creation-toggle")) {
personManagerRepository.save(manager)
} else {
manager
}

val addressInfo = defendant.personDefendant.personDetails.address
val osPlacesResponse = addressInfo?.takeIf { it.containsInformation() && !it.postcode.isNullOrBlank() }
?.let { findAddressByFreeText(it) }
// Equality record
val equality = Equality(
id = null,
personId = savedPerson.id ?: 0L,
softDeleted = false,
)

val deliveryPointAddress = osPlacesResponse?.results?.firstOrNull()?.dpa
val savedEquality = if (featureFlags.enabled("common-platform-record-creation-toggle")) {
equalityRepository.save(equality)
} else {
equality
}

val savedAddress = if (deliveryPointAddress != null) {
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,
postcode = it.postcode,
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
streetName = it.address1,
district = it.address2,
town = it.address3,
county = listOfNotNull(it.address4, it.address5).joinToString(", ")
)
)
} else {
addressInfo?.takeIf { it.containsInformation() }?.let {
insertAddress(
PersonAddress(
id = null,
start = LocalDate.now(),
status = referenceDataRepository.mainAddressStatus(),
person = savedPerson,
postcode = it.postcode,
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)
}

@Transactional
fun insertAddress(address: PersonAddress): PersonAddress = audit(BusinessInteractionCode.INSERT_ADDRESS) { audit ->
val savedAddress = personAddressRepository.save(address)
audit["addressId"] = address.id!!
savedAddress
return InsertPersonResult(savedPerson, savedManager, savedEquality, savedAddress)
}

fun insertAddress(address: PersonAddress): PersonAddress =
if (featureFlags.enabled("common-platform-record-creation-toggle")) {
audit(BusinessInteractionCode.INSERT_ADDRESS) { audit ->
val result = personAddressRepository.save(address)
audit["offenderId"] = result.person.id!!
result
}
} else {
address
}

fun generateCrn(): String {
return personRepository.getNextCrn()
}
Expand Down
Loading

0 comments on commit a1dc4e1

Please sign in to comment.