generated from ministryofjustice/hmpps-template-kotlin
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
317e9fd
commit f683937
Showing
13 changed files
with
592 additions
and
21 deletions.
There are no files selected for viewing
35 changes: 35 additions & 0 deletions
35
helm_deploy/hmpps-prisoner-from-nomis-migration/templates/report-incidents-cronjob.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
apiVersion: batch/v1 | ||
kind: CronJob | ||
metadata: | ||
name: {{ include "app.fullname" . }}-report-incidents | ||
labels: | ||
{{- include "app.labels" . | nindent 4 }} | ||
spec: | ||
schedule: "15 2 * * 1" | ||
suspend: true | ||
concurrencyPolicy: Forbid | ||
failedJobsHistoryLimit: 5 | ||
startingDeadlineSeconds: 600 | ||
successfulJobsHistoryLimit: 5 | ||
jobTemplate: | ||
spec: | ||
# Tidy up all jobs after 4 days | ||
ttlSecondsAfterFinished: 345600 | ||
template: | ||
spec: | ||
containers: | ||
- name: report-incidents | ||
image: ghcr.io/ministryofjustice/hmpps-devops-tools | ||
args: | ||
- /bin/sh | ||
- -c | ||
- curl --retry 2 -XPUT http://hmpps-prisoner-from-nomis-migration/incidents/reports/reconciliation | ||
securityContext: | ||
capabilities: | ||
drop: | ||
- ALL | ||
runAsNonRoot: true | ||
allowPrivilegeEscalation: false | ||
seccompProfile: | ||
type: RuntimeDefault | ||
restartPolicy: Never |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
.../kotlin/uk/gov/justice/digital/hmpps/prisonerfromnomismigration/config/CoroutineScopes.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package uk.gov.justice.digital.hmpps.prisonerfromnomismigration.config | ||
|
||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
|
||
@Configuration | ||
class CoroutineScopes { | ||
// since everything is non-blocking, we don't need to worry about blocking threads so use whatever thread | ||
// the last coroutine was on, continue on that one rather than switching threads | ||
@Bean | ||
fun nonBlockingReportScope() = CoroutineScope(Dispatchers.Unconfined) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
...ice/digital/hmpps/prisonerfromnomismigration/incidents/IncidentsReconciliationResource.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package uk.gov.justice.digital.hmpps.prisonerfromnomismigration.incidents | ||
|
||
import com.microsoft.applicationinsights.TelemetryClient | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.launch | ||
import org.slf4j.Logger | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.http.MediaType | ||
import org.springframework.web.bind.annotation.PutMapping | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import org.springframework.web.bind.annotation.ResponseStatus | ||
import org.springframework.web.bind.annotation.RestController | ||
import uk.gov.justice.digital.hmpps.prisonerfromnomismigration.helpers.trackEvent | ||
|
||
@RestController | ||
@RequestMapping("/incidents/reports/reconciliation", produces = [MediaType.APPLICATION_JSON_VALUE]) | ||
class IncidentsReconciliationResource( | ||
private val incidentsReconciliationService: IncidentsReconciliationService, | ||
private val telemetryClient: TelemetryClient, | ||
private val reportScope: CoroutineScope, | ||
|
||
private val nomisIncidentsApiService: IncidentsNomisApiService, | ||
|
||
) { | ||
private companion object { | ||
val log: Logger = LoggerFactory.getLogger(this::class.java) | ||
} | ||
|
||
@PutMapping | ||
@ResponseStatus(HttpStatus.ACCEPTED) | ||
suspend fun incidentsReconciliation() { | ||
nomisIncidentsApiService.getAllAgencies() | ||
.also { agencyIds -> | ||
telemetryClient.trackEvent( | ||
"incidents-reports-reconciliation-requested", | ||
mapOf("prisonCount" to agencyIds.size), | ||
) | ||
|
||
reportScope.launch { | ||
runCatching { incidentsReconciliationService.generateReconciliationReport(agencyIds) } | ||
.onSuccess { | ||
log.info("Incidents reconciliation report completed with ${it.size} mismatches") | ||
telemetryClient.trackEvent( | ||
"incidents-reports-reconciliation-report", | ||
mapOf("mismatch-count" to it.size.toString(), "success" to "true") + it.asMap(), | ||
) | ||
} | ||
.onFailure { | ||
telemetryClient.trackEvent("incidents-reports-reconciliation-report", mapOf("success" to "false")) | ||
log.error("Incidents reconciliation report failed", it) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
private fun List<MismatchIncidents>.asMap(): Map<String, String> { | ||
return this.associate { | ||
it.agencyId to | ||
("open-dps=${it.dpsOpenIncidents}:open-nomis=${it.nomisOpenIncidents}; closed-dps=${it.dpsClosedIncidents}:closed-nomis=${it.nomisClosedIncidents}") | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
...tice/digital/hmpps/prisonerfromnomismigration/incidents/IncidentsReconciliationService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package uk.gov.justice.digital.hmpps.prisonerfromnomismigration.incidents | ||
|
||
import com.microsoft.applicationinsights.TelemetryClient | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.async | ||
import kotlinx.coroutines.awaitAll | ||
import kotlinx.coroutines.withContext | ||
import org.slf4j.Logger | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.stereotype.Service | ||
import uk.gov.justice.digital.hmpps.prisonerfromnomismigration.helpers.trackEvent | ||
import uk.gov.justice.digital.hmpps.prisonerfromnomismigration.nomissync.model.IncidentAgencyId | ||
import uk.gov.justice.digital.hmpps.prisonerfromnomismigration.service.doApiCallWithRetries | ||
|
||
@Service | ||
class IncidentsReconciliationService( | ||
private val telemetryClient: TelemetryClient, | ||
private val incidentsService: IncidentsService, | ||
private val nomisIncidentsApiService: IncidentsNomisApiService, | ||
) { | ||
private companion object { | ||
val log: Logger = LoggerFactory.getLogger(this::class.java) | ||
} | ||
|
||
suspend fun generateReconciliationReport(agencies: List<IncidentAgencyId>): List<MismatchIncidents> = | ||
withContext(Dispatchers.Unconfined) { | ||
agencies.map { async { checkIncidentsMatch(it.agencyId) } } | ||
}.awaitAll().filterNotNull() | ||
|
||
suspend fun checkIncidentsMatch(agencyId: String): MismatchIncidents? = runCatching { | ||
val nomisIncidents = doApiCallWithRetries { nomisIncidentsApiService.getIncidentsReconciliation(agencyId) } | ||
val dpsOpenIncidentsCount = doApiCallWithRetries { incidentsService.getOpenIncidentsCount(agencyId) } | ||
val dpsClosedIncidentsCount = doApiCallWithRetries { incidentsService.getClosedIncidentsCount(agencyId) } | ||
val nomisOpenIncidentsCount = nomisIncidents.incidentCount.openIncidents | ||
val nomisClosedIncidentsCount = nomisIncidents.incidentCount.closedIncidents | ||
|
||
return if (nomisOpenIncidentsCount != dpsOpenIncidentsCount || nomisClosedIncidentsCount != dpsClosedIncidentsCount) { | ||
MismatchIncidents( | ||
agencyId = agencyId, | ||
dpsOpenIncidents = dpsOpenIncidentsCount, | ||
nomisOpenIncidents = nomisOpenIncidentsCount, | ||
dpsClosedIncidents = dpsOpenIncidentsCount, | ||
nomisClosedIncidents = nomisClosedIncidentsCount, | ||
) | ||
.also { mismatch -> | ||
log.info("Incidents Mismatch found $mismatch") | ||
telemetryClient.trackEvent( | ||
"incidents-reports-reconciliation-mismatch", | ||
mapOf( | ||
"agencyId" to mismatch.agencyId, | ||
"dpsOpenIncidents" to mismatch.dpsOpenIncidents, | ||
"nomisOpenIncidents" to mismatch.nomisOpenIncidents, | ||
"dpsClosedIncidents" to mismatch.dpsClosedIncidents, | ||
"nomisClosedIncidents" to mismatch.nomisClosedIncidents, | ||
), | ||
) | ||
} | ||
} else { | ||
null | ||
} | ||
}.onFailure { | ||
log.error("Unable to match incidents for agency with $agencyId ", it) | ||
telemetryClient.trackEvent( | ||
"incidents-reports-reconciliation-mismatch-error", | ||
mapOf( | ||
"agencyId" to agencyId, | ||
), | ||
) | ||
}.getOrNull() | ||
} | ||
|
||
data class MismatchIncidents( | ||
val agencyId: String, | ||
val dpsOpenIncidents: Long, | ||
val nomisOpenIncidents: Long, | ||
val dpsClosedIncidents: Long, | ||
val nomisClosedIncidents: Long, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
src/main/kotlin/uk/gov/justice/digital/hmpps/prisonerfromnomismigration/service/HelperApi.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package uk.gov.justice.digital.hmpps.prisonerfromnomismigration.service | ||
|
||
import org.slf4j.Logger | ||
import org.slf4j.LoggerFactory | ||
|
||
val log: Logger = LoggerFactory.getLogger("HelperApi") | ||
|
||
suspend fun <T> doApiCallWithRetries(api: suspend () -> T) = runCatching { | ||
api() | ||
}.recoverCatching { | ||
log.warn("Retrying API call 1", it) | ||
api() | ||
}.recoverCatching { | ||
log.warn("Retrying API call 2", it) | ||
api() | ||
}.getOrThrow() |
Oops, something went wrong.