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-1473 add enhancement ability for domain events #2295

Merged
merged 4 commits into from
Sep 21, 2023
Merged
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
1 change: 1 addition & 0 deletions projects/domain-events-and-delius/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies {
implementation(project(":libs:audit"))
implementation(project(":libs:commons"))
implementation(project(":libs:messaging"))
implementation(project(":libs:oauth-server"))

implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
Expand Down
1 change: 1 addition & 0 deletions projects/domain-events-and-delius/deploy/values-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ generic-service:
enabled: true

env:
DOMAIN_EVENTS_BASE_URL: domain-events-and-delius-dev.hmpps.service.justice.gov.uk
SENTRY_ENVIRONMENT: dev
LOGGING_LEVEL_UK_GOV_DIGITAL_JUSTICE_HMPPS: DEBUG

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ generic-service:
enabled: true

env:
DOMAIN_EVENTS_BASE_URL: domain-events-and-delius-preprod.hmpps.service.justice.gov.uk
SENTRY_ENVIRONMENT: preprod

generic-prometheus-alerts:
Expand Down
1 change: 1 addition & 0 deletions projects/domain-events-and-delius/deploy/values-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ generic-service:
host: domain-events-and-delius.hmpps.service.justice.gov.uk

env:
DOMAIN_EVENTS_BASE_URL: domain-events-and-delius.hmpps.service.justice.gov.uk
SENTRY_ENVIRONMENT: prod
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
package uk.gov.justice.digital.hmpps.data

import jakarta.annotation.PostConstruct
import jakarta.persistence.EntityManager
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.context.ApplicationListener
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator
import uk.gov.justice.digital.hmpps.data.generator.UserGenerator
import uk.gov.justice.digital.hmpps.user.AuditUserRepository

@Component
@ConditionalOnProperty("seed.database")
class DataLoader(
private val auditUserRepository: AuditUserRepository
private val auditUserRepository: AuditUserRepository,
private val em: EntityManager
) : ApplicationListener<ApplicationReadyEvent> {
@PostConstruct
fun saveAuditUser() {
auditUserRepository.save(UserGenerator.AUDIT_USER)
}

override fun onApplicationEvent(event: ApplicationReadyEvent) {}
@Transactional
override fun onApplicationEvent(event: ApplicationReadyEvent) {
em.persist(PersonGenerator.ENGAGEMENT_CREATED)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package uk.gov.justice.digital.hmpps.data.generator

import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person
import java.time.LocalDate

object PersonGenerator {
val ENGAGEMENT_CREATED =
generate("X789654", "Bernard", "Shepherd", LocalDate.of(1977, 10, 9), pnc = "1977/9999748M")

fun generate(
crn: String,
forename: String,
surname: String,
dateOfBirth: LocalDate,
secondName: String? = null,
thirdName: String? = null,
pnc: String? = null,
softDeleted: Boolean = false,
id: Long = IdGenerator.getAndIncrement()
) = Person(crn, pnc, forename, secondName, thirdName, surname, dateOfBirth, softDeleted, id)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDju2wS/xXRfjdRI9thM2yJ7xO4
cy5o8eJ1tljIrqJvKcdsmuflNv7IXGZP4OIQBhALkS3CQxIhBl9gNAtyu2AdYUdn
bSlBS5qLtLg2EoPn2GyGSwRiZCmZcmDsHdn8DX0zNCgZN9i/B/yCL0gXgvHRGEqP
QCtNM3Urqkvn97LVjQIDAQAB
-----END PUBLIC KEY-----
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"eventType": {
"Type": "String",
"Value": "probation-case.engagement.created"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"eventType": "probation-case.engagement.created",
"version": 1,
"description": "A new probation case has been created",
"detailUrl": "https://example.com",
"occurredAt": "2023-09-20T16:05:59.442+01:00",
"personReference": {
"identifiers": [
{
"type": "CRN",
"value": "X789654"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwcm9iYXRpb24taW50ZWdyYXRpb24tZGV2IiwiZ3JhbnRfdHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyIsInVzZXJfbmFtZSI6InByb2JhdGlvbi1pbnRlZ3JhdGlvbi1kZXYiLCJzY29wZSI6WyJyZWFkIl0sImF1dGhfc291cmNlIjoibm9uZSIsImlzcyI6Imh0dHBzOi8vc2lnbi1pbi1kZXYuaG1wcHMuc2VydmljZS5qdXN0aWNlLmdvdi51ay9hdXRoL2lzc3VlciIsImV4cCI6OTk5OTk5OTk5OSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9WSUVXX1BST0JBVElPTl9DQVNFX0VOR0FHRU1FTlRfQ1JFQVRFRCJdLCJqdGkiOiIyNUR1Um4xLWh5SFpld0xjZEpKeHdWTDAzS1UiLCJjbGllbnRfaWQiOiJwcm9iYXRpb24taW50ZWdyYXRpb24tZGV2IiwiaWF0IjoxNjYzNzU3MzExfQ.w-fJ016upnUJKTPCyGVuIupXRESglhmdD3cvhCyeCU67c8bOdb6gVgpjCMsCxuP-Z5EkIMjLmIe_XvxZQPuLtExgpBbfQ596JMWqco5E6r53Fp8n7BMqR54xhxVoTJRk8em8lrIwGgNnRv9Ee2zhidtxlP2f3GS1G7F48oSTPuE",
"token_type": "bearer",
"expires_in": 9999999999,
"scope": "read write",
"sub": "probation-integration-dev",
"auth_source": "none",
"jti": "fN29JHJy1N7gcYvqe-8B_k5T0mA",
"iss": "https://sign-in-dev.hmpps.service.justice.gov.uk/auth/issuer"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"request": {
"method": "POST",
"urlPath": "/auth/oauth/token"
},
"response": {
"headers": {
"Content-Type": "application/json"
},
"status": 200,
"bodyFileName": "hmpps-auth-token-body.json"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package uk.gov.justice.digital.hmpps

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.github.tomakehurst.wiremock.WireMockServer
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator
import uk.gov.justice.digital.hmpps.security.withOAuth2Token
import uk.gov.justice.digital.hmpps.service.Engagement
import uk.gov.justice.digital.hmpps.service.Identifiers
import uk.gov.justice.digital.hmpps.service.Name

@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
internal class EngagementCreatedTest {
@Autowired
internal lateinit var mockMvc: MockMvc

@Autowired
internal lateinit var wireMockServer: WireMockServer

@Autowired
internal lateinit var objectMapper: ObjectMapper

@Test
fun `engagement details returned from detail url`() {
val person = PersonGenerator.ENGAGEMENT_CREATED
val response = mockMvc
.perform(get("/probation-case.engagement.created/${person.crn}").withOAuth2Token(wireMockServer))
.andExpect(MockMvcResultMatchers.status().is2xxSuccessful).andReturn().response.contentAsString

val engagement = objectMapper.readValue<Engagement>(response)
assertThat(
engagement,
equalTo(
Engagement(
Identifiers(person.crn, person.pnc),
Name(person.forename, person.surname, listOf()),
person.dateOfBirth
)
)
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.justice.digital.hmpps

import com.github.tomakehurst.wiremock.WireMockServer
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Assertions.assertEquals
Expand All @@ -14,11 +15,14 @@ import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import uk.gov.justice.digital.hmpps.data.generator.DomainEventGenerator
import uk.gov.justice.digital.hmpps.integrations.delius.DomainEventRepository
import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent
import uk.gov.justice.digital.hmpps.message.Notification
import uk.gov.justice.digital.hmpps.messaging.HmppsChannelManager
import uk.gov.justice.digital.hmpps.service.enhancement.EnhancedEventType
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService

@SpringBootTest
internal class IntegrationTest {
internal class PublishingIntegrationTest {
@Value("\${messaging.producer.topic}")
lateinit var topicName: String

Expand All @@ -31,6 +35,9 @@ internal class IntegrationTest {
@MockBean
lateinit var telemetryService: TelemetryService

@Autowired
lateinit var wireMockServer: WireMockServer

@Test
fun `messages are published successfully`() {
domainEventRepository.saveAll(
Expand All @@ -46,7 +53,31 @@ internal class IntegrationTest {
topic.receive()?.eventType?.let { messages.add(it) }
}

assertThat(messages.sorted(), equalTo(listOf("probation-case.registration.added", "probation-case.risk-scores.ogrs.manual-calculation")))
assertThat(
messages.sorted(),
equalTo(listOf("probation-case.registration.added", "probation-case.risk-scores.ogrs.manual-calculation"))
)
verify(telemetryService, timeout(30000)).trackEvent(eq("DomainEventsProcessed"), any(), any())
assertEquals(0, domainEventRepository.count())
}

@Test
fun `engagement created messages include detail url`() {
domainEventRepository.save(DomainEventGenerator.generate(EnhancedEventType.ProbationCaseEngagementCreated.value))

val topic = hmppsChannelManager.getChannel(topicName)
val messages = mutableListOf<Notification<*>>()
while (messages.size < 1) {
topic.receive()?.let { messages.add(it) }
}

assertThat(
messages.first().eventType,
equalTo("probation-case.engagement.created")
)
val domainEvent = messages.first().message as HmppsDomainEvent
assertThat(domainEvent.eventType, equalTo("probation-case.engagement.created"))
assertThat(domainEvent.detailUrl, equalTo("http://localhost:${wireMockServer.port()}/probation-case.engagement.created/X789654"))
verify(telemetryService, timeout(30000)).trackEvent(eq("DomainEventsProcessed"), any(), any())
assertEquals(0, domainEventRepository.count())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package uk.gov.justice.digital.hmpps.api.engagement

import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import uk.gov.justice.digital.hmpps.service.Engagement
import uk.gov.justice.digital.hmpps.service.PersonService

@RestController
@RequestMapping
class EngagementResource(private val personService: PersonService) {
@PreAuthorize("hasRole('VIEW_PROBATION_CASE_ENGAGEMENT_CREATED')")
@GetMapping("probation-case.engagement.created/{crn}")
fun getEngagement(@PathVariable crn: String): Engagement = personService.findEngagement(crn)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package uk.gov.justice.digital.hmpps.integrations.delius.person.entity

import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.hibernate.annotations.Immutable
import org.hibernate.annotations.Where
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDate

@Immutable
@Entity
@Table(name = "offender")
@Where(clause = "soft_deleted = 0")
class Person(

@Column(columnDefinition = "char(7)")
val crn: String,

@Column(name = "pnc_number", columnDefinition = "char(13)")
val pnc: String?,

@Column(name = "first_name")
val forename: String,

@Column(name = "second_name")
val secondName: String?,

@Column(name = "third_name")
val thirdName: String?,

@Column(name = "surname")
val surname: String,

@Column(name = "date_of_birth_date")
val dateOfBirth: LocalDate,

@Column(columnDefinition = "number")
val softDeleted: Boolean,

@Id
@Column(name = "offender_id")
val id: Long
)

interface PersonRepository : JpaRepository<Person, Long> {
fun findByCrn(crn: String): Person?
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ import uk.gov.justice.digital.hmpps.integrations.delius.DomainEventRepository
import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent
import uk.gov.justice.digital.hmpps.message.Notification
import uk.gov.justice.digital.hmpps.publisher.NotificationPublisher
import uk.gov.justice.digital.hmpps.service.enhancement.NotificationEnhancer

@Service
class DomainEventService(
@Value("\${poller.batch-size:50}")
private val batchSize: Int,
private val objectMapper: ObjectMapper,
private val domainEventRepository: DomainEventRepository,
private val notificationPublisher: NotificationPublisher
private val notificationPublisher: NotificationPublisher,
private val notificationEnhancer: NotificationEnhancer
) {
@Transactional
fun publishBatch(): Int {
val deltas = domainEventRepository.findAll(Pageable.ofSize(batchSize)).content
val notifications = deltas.map { it.asNotification() }
val notifications = deltas.map { notificationEnhancer.enhance(it.asNotification()) }
notifications.forEach { notificationPublisher.publish(it) }
domainEventRepository.deleteAllByIdInBatch(deltas.map { it.id })
return deltas.size
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package uk.gov.justice.digital.hmpps.service

import org.springframework.stereotype.Service
import uk.gov.justice.digital.hmpps.exception.NotFoundException
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonRepository
import java.time.LocalDate

@Service
class PersonService(private val personRepository: PersonRepository) {
fun findEngagement(crn: String): Engagement = personRepository.findByCrn(crn)?.asEngagement()
?: throw NotFoundException("Engagement", "crn", crn)
}

private fun Person.asEngagement() = Engagement(
Identifiers(crn, pnc),
Name(forename, surname, listOfNotNull(secondName, thirdName)),
dateOfBirth
)

data class Engagement(
val identifiers: Identifiers,
val name: Name,
val dateOfBirth: LocalDate
)

data class Identifiers(val crn: String, val pnc: String?)

data class Name(
val forename: String,
val surname: String,
val otherNames: List<String>
)
Loading