diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 4c2c6f7309..975e937752 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -38,7 +38,7 @@ jobs: echo >> projects/${{ matrix.project }}/.trivyignore - name: Scan image - uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 + uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # v0.27.0 with: image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest' ignore-unfixed: true @@ -56,7 +56,7 @@ jobs: sarif_file: 'trivy-results.sarif' - name: Get Trivy results - uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 + uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # v0.27.0 with: image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest' ignore-unfixed: true diff --git a/build.gradle.kts b/build.gradle.kts index a8cf8bcc3c..e23176bc3d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,9 +8,9 @@ import uk.gov.justice.digital.hmpps.plugins.ClassPathPlugin import uk.gov.justice.digital.hmpps.plugins.JibConfigPlugin plugins { - kotlin("jvm") version "2.0.20" - kotlin("plugin.spring") version "2.0.20" apply false - kotlin("plugin.jpa") version "2.0.20" apply false + kotlin("jvm") version "2.0.21" + kotlin("plugin.spring") version "2.0.21" apply false + kotlin("plugin.jpa") version "2.0.21" apply false id("org.springframework.boot") version "3.3.4" apply false id("io.spring.dependency-management") version "1.1.6" apply false id("com.gorylenko.gradle-git-properties") version "2.4.2" apply false @@ -22,7 +22,7 @@ plugins { val agentDeps: Configuration by configurations.creating dependencies { - agentDeps("com.microsoft.azure:applicationinsights-agent:3.6.0") + agentDeps("com.microsoft.azure:applicationinsights-agent:3.6.1") } val copyAgentTask = project.tasks.register("copyAgent") { diff --git a/projects/common-platform-and-delius/deploy/values-dev.yml b/projects/common-platform-and-delius/deploy/values-dev.yml index 8b3373918a..0018911381 100644 --- a/projects/common-platform-and-delius/deploy/values-dev.yml +++ b/projects/common-platform-and-delius/deploy/values-dev.yml @@ -1,3 +1,5 @@ +enabled: false + generic-service: ingress: host: common-platform-and-delius-dev.hmpps.service.justice.gov.uk diff --git a/projects/common-platform-and-delius/deploy/values-preprod.yml b/projects/common-platform-and-delius/deploy/values-preprod.yml index a46cf293f4..3aa1c2df9d 100644 --- a/projects/common-platform-and-delius/deploy/values-preprod.yml +++ b/projects/common-platform-and-delius/deploy/values-preprod.yml @@ -1,3 +1,5 @@ +enabled: false + generic-service: ingress: host: common-platform-and-delius-preprod.hmpps.service.justice.gov.uk diff --git a/projects/prison-custody-status-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt b/projects/prison-custody-status-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt index ceed34c35c..2b694b8dc8 100644 --- a/projects/prison-custody-status-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt +++ b/projects/prison-custody-status-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/data/generator/PersonGenerator.kt @@ -23,5 +23,6 @@ object PersonGenerator { val ECSLIRC_IN_CUSTODY = generate(NotificationGenerator.PRISONER_ECSLIRC_IN_CUSTODY.nomsId()) val ADMIN_MERGE = generate(NotificationGenerator.PRISONER_ADMIN_MERGE.nomsId()) - fun generate(nomsNumber: String, id: Long = IdGenerator.getAndIncrement()) = Person(id, nomsNumber) + fun generate(nomsNumber: String, id: Long = IdGenerator.getAndIncrement()) = + Person(id, nomsNumber.reversed(), nomsNumber) } diff --git a/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/person/entity/Person.kt b/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/person/entity/Person.kt index 9891bcb995..45aeafbdd3 100644 --- a/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/person/entity/Person.kt +++ b/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/person/entity/Person.kt @@ -5,6 +5,7 @@ import org.hibernate.annotations.Immutable import org.hibernate.annotations.SQLRestriction import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query +import uk.gov.justice.digital.hmpps.exception.NotFoundException import uk.gov.justice.digital.hmpps.integrations.delius.referencedata.ReferenceData @Immutable @@ -16,6 +17,9 @@ class Person( @Column(name = "offender_id") val id: Long, + @Column(columnDefinition = "char(7)") + val crn: String, + @Column(columnDefinition = "char(7)") val nomsNumber: String, @@ -25,8 +29,14 @@ class Person( interface PersonRepository : JpaRepository { fun findByNomsNumberAndSoftDeletedIsFalse(nomsNumber: String): List + + @Query("select p.nomsNumber from Person p where p.crn = :crn and p.softDeleted = false") + fun findNomsNumberByCrn(crn: String): String? } +fun PersonRepository.getNomsNumberByCrn(crn: String) = + findNomsNumberByCrn(crn) ?: throw NotFoundException("NOMS number for case", "crn", crn) + @Immutable @Entity @SQLRestriction("soft_deleted = 0") diff --git a/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt b/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt index 7b10965cce..2ccba192f4 100644 --- a/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt +++ b/projects/prison-custody-status-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt @@ -11,6 +11,8 @@ import uk.gov.justice.digital.hmpps.converter.NotificationConverter import uk.gov.justice.digital.hmpps.datetime.EuropeLondon import uk.gov.justice.digital.hmpps.exception.IgnorableMessageException import uk.gov.justice.digital.hmpps.flags.FeatureFlags +import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonRepository +import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.getNomsNumberByCrn import uk.gov.justice.digital.hmpps.integrations.prison.Booking import uk.gov.justice.digital.hmpps.integrations.prison.Movement import uk.gov.justice.digital.hmpps.integrations.prison.PrisonApiClient @@ -30,7 +32,8 @@ class Handler( private val telemetryService: TelemetryService, private val prisonApiClient: PrisonApiClient, private val actionProcessor: ActionProcessor, - override val converter: NotificationConverter + private val personRepository: PersonRepository, + override val converter: NotificationConverter, ) : NotificationHandler { private val configs = configContainer.configs @@ -46,10 +49,12 @@ class Handler( telemetryService.notificationReceived(notification) val message = notification.message val eventType = DomainEventType.of(message.eventType) + val nomsId = message.personReference.findNomsNumber() + ?: personRepository.getNomsNumberByCrn(requireNotNull(message.personReference.findCrn())) + try { val movement = when (eventType) { IdentifierAdded, IdentifierUpdated, PrisonerReceived, PrisonerReleased -> { - val nomsId = message.personReference.findNomsNumber()!! val booking = prisonApiClient.bookingFromNomsId(nomsId) val movement = prisonApiClient.getLatestMovement(listOf(nomsId)).firstOrNull() movement?.let { booking.prisonerMovement(it) } @@ -63,7 +68,7 @@ class Handler( if (movement == null) { throw IgnorableMessageException( "NoMovementInNomis", mapOf( - "nomsNumber" to message.personReference.findNomsNumber()!! + "nomsNumber" to nomsId ) ) } @@ -109,10 +114,7 @@ class Handler( throw failure.exception } } catch (e: IgnorableMessageException) { - telemetryService.trackEvent( - e.message, - message.telemetryProperties() + e.additionalProperties - ) + telemetryService.trackEvent(e.message, message.telemetryProperties(nomsId) + e.additionalProperties) } } @@ -126,9 +128,9 @@ class Handler( fun HmppsDomainEvent.prisonId() = additionalInformation["prisonId"] as String? fun HmppsDomainEvent.details() = additionalInformation["details"] as String? -fun HmppsDomainEvent.telemetryProperties() = listOfNotNull( +fun HmppsDomainEvent.telemetryProperties(nomsId: String) = listOfNotNull( "occurredAt" to occurredAt.toString(), - "nomsNumber" to personReference.findNomsNumber()!!, + "nomsNumber" to nomsId, prisonId()?.let { "institution" to it }, details()?.let { "details" to it } ).toMap() diff --git a/projects/prison-custody-status-to-delius/src/main/resources/application-prisoner-movement.yml b/projects/prison-custody-status-to-delius/src/main/resources/application-prisoner-movement.yml index 262320dfce..7229999120 100644 --- a/projects/prison-custody-status-to-delius/src/main/resources/application-prisoner-movement.yml +++ b/projects/prison-custody-status-to-delius/src/main/resources/application-prisoner-movement.yml @@ -55,6 +55,8 @@ prisoner.movement.configs: # Do nothing for the following movement reasons - MRG # Merged - RE # Repatriated + - BL # Bailed + - BD # Bailed Detainee - types: - RELEASED actionNames: diff --git a/projects/prison-custody-status-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt b/projects/prison-custody-status-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt index 561e7363f1..1ae425b51c 100644 --- a/projects/prison-custody-status-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt +++ b/projects/prison-custody-status-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt @@ -13,6 +13,7 @@ import uk.gov.justice.digital.hmpps.converter.NotificationConverter import uk.gov.justice.digital.hmpps.data.generator.InstitutionGenerator import uk.gov.justice.digital.hmpps.datetime.EuropeLondon import uk.gov.justice.digital.hmpps.flags.FeatureFlags +import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonRepository import uk.gov.justice.digital.hmpps.integrations.prison.Booking import uk.gov.justice.digital.hmpps.integrations.prison.Movement import uk.gov.justice.digital.hmpps.integrations.prison.PrisonApiClient @@ -40,6 +41,9 @@ internal class HandlerTest { @Mock lateinit var actionProcessor: ActionProcessor + @Mock + lateinit var personRepository: PersonRepository + private val configs = PrisonerMovementConfigs( listOf( PrisonerMovementConfig( @@ -58,7 +62,15 @@ internal class HandlerTest { @BeforeEach fun setup() { - handler = Handler(configs, featureFlags, telemetryService, prisonApiClient, actionProcessor, converter) + handler = Handler( + configs, + featureFlags, + telemetryService, + prisonApiClient, + actionProcessor, + personRepository, + converter + ) } private val notification = Notification( @@ -292,6 +304,25 @@ internal class HandlerTest { ) } + @Test + fun `lookup noms number by crn`() { + whenever(personRepository.findNomsNumberByCrn("X123456")).thenReturn("A1234AA") + whenever(prisonApiClient.getBookingByNomsId("A1234AA")).thenReturn(booking.copy(active = false)) + + handler.handle( + notification.copy( + message = notification.message.copy( + personReference = PersonReference(listOf(PersonIdentifier("CRN", "X123456"))), + ) + ) + ) + + verify(personRepository).findNomsNumberByCrn("X123456") + verify(telemetryService).trackEvent(eq("BookingInactive"), check { + assertThat(it["nomsNumber"], equalTo("A1234AA")) + }, any()) + } + private fun Booking.movement() = Movement( "OUT", agencyId, diff --git a/projects/redrive-dead-letter-queues/container/Dockerfile b/projects/redrive-dead-letter-queues/container/Dockerfile index d8468d8086..f93a06c635 100644 --- a/projects/redrive-dead-letter-queues/container/Dockerfile +++ b/projects/redrive-dead-letter-queues/container/Dockerfile @@ -1,4 +1,4 @@ -FROM public.ecr.aws/aws-cli/aws-cli:2.18.0 +FROM public.ecr.aws/aws-cli/aws-cli:2.18.5 USER root SHELL ["/bin/bash", "-o", "pipefail", "-c"] diff --git a/settings.gradle.kts b/settings.gradle.kts index b834e0e999..4665abc873 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -73,8 +73,8 @@ dependencyResolutionManagement { library("aws-starter", "io.awspring.cloud:spring-cloud-aws-starter:3.2.0") library("aws-sns", "io.awspring.cloud:spring-cloud-aws-starter-sns:3.2.0") library("aws-sqs", "io.awspring.cloud:spring-cloud-aws-starter-sqs:3.2.0") - library("aws-sts", "software.amazon.awssdk:sts:2.28.16") - library("aws-query-protocol", "software.amazon.awssdk:aws-query-protocol:2.28.16") + library("aws-sts", "software.amazon.awssdk:sts:2.28.21") + library("aws-query-protocol", "software.amazon.awssdk:aws-query-protocol:2.28.21") bundle( "aws-messaging", listOf("aws-autoconfigure", "aws-starter", "aws-sns", "aws-sqs", "aws-sts", "aws-query-protocol") @@ -82,8 +82,8 @@ dependencyResolutionManagement { library("mockito-kotlin", "org.mockito.kotlin:mockito-kotlin:5.4.0") library("mockito-inline", "org.mockito:mockito-inline:5.2.0") bundle("mockito", listOf("mockito-kotlin", "mockito-inline")) - library("insights", "com.microsoft.azure:applicationinsights-web:3.6.0") - library("sentry", "io.sentry:sentry-spring-boot-starter-jakarta:7.14.0") + library("insights", "com.microsoft.azure:applicationinsights-web:3.6.1") + library("sentry", "io.sentry:sentry-spring-boot-starter-jakarta:7.15.0") library( "opentelemetry-annotations", "io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.8.0"