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

EY-4847 plukk opp manglende avslag statistikk #6595

Merged
merged 6 commits into from
Dec 13, 2024
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
5 changes: 5 additions & 0 deletions apps/etterlatte-statistikk/.nais/dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,18 @@ spec:
value: api://dev-gcp.etterlatte.etterlatte-behandling/.default
- name: BEREGNING_AZURE_SCOPE
value: api://dev-gcp.etterlatte.etterlatte-beregning/.default
- name: ETTERLATTE_VEDTAK_CLIENT_ID
value: dev-gcp.etterlatte.etterlatte-vedtaksvurdering
- name: ETTERLATTE_VEDTAK_URL
value: http://etterlatte-vedtaksvurdering
envFrom:
- secret: my-application-unleash-api-token
accessPolicy:
outbound:
rules:
- application: etterlatte-behandling
- application: etterlatte-beregning
- application: etterlatte-vedtaksvurdering # TODO tilgangen er midlertidig for å rydde i resultat for vedtak
external:
- host: etterlatte-unleash-api.nav.cloud.nais.io
inbound:
Expand Down
6 changes: 6 additions & 0 deletions apps/etterlatte-statistikk/.nais/prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,18 @@ spec:
value: api://prod-gcp.etterlatte.etterlatte-behandling/.default
- name: BEREGNING_AZURE_SCOPE
value: api://prod-gcp.etterlatte.etterlatte-beregning/.default
- name: ETTERLATTE_VEDTAK_CLIENT_ID
value: prod-gcp.etterlatte.etterlatte-vedtaksvurdering
- name: ETTERLATTE_VEDTAK_URL
value: http://etterlatte-vedtaksvurdering
envFrom:
- secret: my-application-unleash-api-token
accessPolicy:
outbound:
rules:
- application: etterlatte-behandling
- application: etterlatte-beregning
# Denne tilgangen er midlertidig og skal fjernes når vedtaksresultat er fikset bakover i tid
- application: etterlatte-vedtaksvurdering
external:
- host: etterlatte-unleash-api.nav.cloud.nais.io
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import no.nav.etterlatte.statistikk.config.ApplicationContext
fun main() =
with(ApplicationContext()) {
maanedligStatistikkJob.schedule().also { addShutdownHook(it) }
ryddVedtakResultatJob.schedule().also { addShutdownHook(it) }
initRapidsConnection()
sikkerLoggOppstart("etterlatte-statistikk")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package no.nav.etterlatte.statistikk.clients

import com.fasterxml.jackson.module.kotlin.readValue
import com.github.michaelbull.result.mapBoth
import com.typesafe.config.Config
import io.ktor.client.HttpClient
import kotlinx.coroutines.runBlocking
import no.nav.etterlatte.libs.common.objectMapper
import no.nav.etterlatte.libs.common.vedtak.VedtakDto
import no.nav.etterlatte.libs.ktor.ktor.ktorobo.AzureAdClient
import no.nav.etterlatte.libs.ktor.ktor.ktorobo.DownstreamResourceClient
import no.nav.etterlatte.libs.ktor.ktor.ktorobo.Resource
import no.nav.etterlatte.libs.ktor.token.Systembruker
import java.util.UUID

class VedtakKlient(
config: Config,
httpClient: HttpClient,
) {
private val azureAdClient = AzureAdClient(config)
private val downstreamResourceClient = DownstreamResourceClient(azureAdClient, httpClient)

private val clientId = config.getString("vedtak.client.id")
private val resourceUrl = config.getString("vedtak.resource.url")

fun hentVedtak(
behandlingId: UUID,
systembruker: Systembruker,
): VedtakDto =
runBlocking {
downstreamResourceClient
.get(
Resource(
clientId = clientId,
url = "$resourceUrl/api/vedtak/$behandlingId",
),
systembruker,
).mapBoth(
success = { resource -> resource.response.let { objectMapper.readValue(it.toString()) } },
failure = { errorResponse -> throw errorResponse },
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package no.nav.etterlatte.statistikk.config

import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import io.ktor.client.HttpClient
import no.nav.etterlatte.EnvKey.BEHANDLING_AZURE_SCOPE
import no.nav.etterlatte.EnvKey.BEREGNING_AZURE_SCOPE
Expand All @@ -11,16 +13,20 @@ import no.nav.etterlatte.libs.ktor.AppConfig.ELECTOR_PATH
import no.nav.etterlatte.libs.ktor.AzureEnums.AZURE_APP_CLIENT_ID
import no.nav.etterlatte.libs.ktor.AzureEnums.AZURE_APP_JWK
import no.nav.etterlatte.libs.ktor.AzureEnums.AZURE_APP_WELL_KNOWN_URL
import no.nav.etterlatte.libs.ktor.httpClient
import no.nav.etterlatte.libs.ktor.httpClientClientCredentials
import no.nav.etterlatte.statistikk.clients.BehandlingKlient
import no.nav.etterlatte.statistikk.clients.BehandlingKlientImpl
import no.nav.etterlatte.statistikk.clients.BeregningKlient
import no.nav.etterlatte.statistikk.clients.BeregningKlientImpl
import no.nav.etterlatte.statistikk.clients.VedtakKlient
import no.nav.etterlatte.statistikk.database.AktivitetspliktRepo
import no.nav.etterlatte.statistikk.database.RyddVedtakResultatDao
import no.nav.etterlatte.statistikk.database.SakRepository
import no.nav.etterlatte.statistikk.database.SoeknadStatistikkRepository
import no.nav.etterlatte.statistikk.database.StoenadRepository
import no.nav.etterlatte.statistikk.jobs.MaanedligStatistikkJob
import no.nav.etterlatte.statistikk.jobs.RyddVedtakResultatJob
import no.nav.etterlatte.statistikk.river.AktivitetspliktHendelseRiver
import no.nav.etterlatte.statistikk.river.AvbruttOpprettetBehandlinghendelseRiver
import no.nav.etterlatte.statistikk.river.BehandlingPaaVentHendelseRiver
Expand Down Expand Up @@ -96,6 +102,23 @@ class ApplicationContext {
)
}

private val ryddVedtakResultatDao: RyddVedtakResultatDao by lazy {
RyddVedtakResultatDao(datasource)
}

val config: Config = ConfigFactory.load()
private val vedtakKlient: VedtakKlient by lazy {
VedtakKlient(config, httpClient())
}

val ryddVedtakResultatJob: RyddVedtakResultatJob by lazy {
RyddVedtakResultatJob(
ryddVedtakResultatDao,
vedtakKlient,
leaderElection,
)
}

private val leaderElection: LeaderElection by lazy {
LeaderElection(env[ELECTOR_PATH])
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package no.nav.etterlatte.statistikk.database

import no.nav.etterlatte.libs.common.behandling.BehandlingType
import no.nav.etterlatte.libs.database.toList
import no.nav.etterlatte.statistikk.domain.BehandlingResultat
import java.util.UUID
import javax.sql.DataSource

class RyddVedtakResultatDao(
private val datasource: DataSource,
) {
fun hentRaderMedPotensiellFeil(): List<RadMedKanskjeFeilResultat> {
datasource.connection.use { connection ->
val statement =
connection.prepareStatement(
"""
select id, behandling_id, behandling_type, fikset from sak_rader_med_potensielt_feil_resultat
where fikset = false order by behandling_id limit 100
""".trimIndent(),
)

return statement.executeQuery().toList {
RadMedKanskjeFeilResultat(
id = getLong("id"),
behandlingId = getObject("behandling_id") as UUID,
behandlingType = enumValueOf(getString("behandling_type")),
fikset = getBoolean("fikset"),
)
}
}
}

fun oppdaterResultat(
rad: RadMedKanskjeFeilResultat,
resultat: BehandlingResultat,
) {
datasource.connection.use { connection ->
connection.autoCommit = false
val stmntOppdaterSak =
connection.prepareStatement(
"""
update sak set behandling_resultat = ? where id = ? and behandling_id = ?
""".trimIndent(),
)

stmntOppdaterSak.setString(1, resultat.name)
stmntOppdaterSak.setLong(2, rad.id)
stmntOppdaterSak.setObject(3, rad.behandlingId)
stmntOppdaterSak.executeUpdate()

val stmntOppdaterRydderad =
connection.prepareStatement(
"""
update sak_rader_med_potensielt_feil_resultat set fikset = true where id = ? and behandling_id = ?
""".trimIndent(),
)

stmntOppdaterRydderad.setLong(1, rad.id)
stmntOppdaterRydderad.setObject(2, rad.behandlingId)
stmntOppdaterRydderad.executeUpdate()

connection.commit()
}
}
}

data class RadMedKanskjeFeilResultat(
val id: Long,
val behandlingId: UUID,
val behandlingType: BehandlingType,
val fikset: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ enum class BehandlingResultat {
INNVILGELSE,
AVBRUTT,
OPPHOER,
ENDRING,
AVSLAG,
}

enum class SakYtelsesgruppe {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package no.nav.etterlatte.statistikk.jobs

import no.nav.etterlatte.jobs.LoggerInfo
import no.nav.etterlatte.jobs.fixedRateCancellableTimer
import no.nav.etterlatte.libs.common.TimerJob
import no.nav.etterlatte.libs.common.behandling.BehandlingStatus
import no.nav.etterlatte.libs.common.feilhaandtering.InternfeilException
import no.nav.etterlatte.libs.common.vedtak.VedtakKafkaHendelseHendelseType
import no.nav.etterlatte.libs.jobs.LeaderElection
import no.nav.etterlatte.libs.ktor.token.HardkodaSystembruker
import no.nav.etterlatte.statistikk.clients.VedtakKlient
import no.nav.etterlatte.statistikk.database.RyddVedtakResultatDao
import no.nav.etterlatte.statistikk.service.behandlingResultatFraVedtak
import org.slf4j.LoggerFactory
import java.time.Duration
import java.time.temporal.ChronoUnit
import java.util.Timer

class RyddVedtakResultatJob(
private val dao: RyddVedtakResultatDao,
private val vedtakKlient: VedtakKlient,
private val leaderElection: LeaderElection,
) : TimerJob {
private val logger = LoggerFactory.getLogger(this::class.java)
private val jobbNavn = this::class.simpleName
private val periode = Duration.of(5, ChronoUnit.MINUTES)
private val initialDelay = Duration.of(2, ChronoUnit.MINUTES).toMillis()

override fun schedule(): Timer {
logger.info("$jobbNavn er satt til å kjøre med periode $periode etter $initialDelay ms")

return fixedRateCancellableTimer(
name = jobbNavn,
period = periode.toMillis(),
initialDelay = initialDelay,
loggerInfo = LoggerInfo(logger = logger, loggTilSikkerLogg = false),
) {
OppdaterResultatSakRad(
leaderElection = leaderElection,
dao = dao,
klient = vedtakKlient,
).run()
}
}

class OppdaterResultatSakRad(
val leaderElection: LeaderElection,
val dao: RyddVedtakResultatDao,
val klient: VedtakKlient,
) {
private val logger = LoggerFactory.getLogger(this::class.java)

fun run() {
if (!leaderElection.isLeader()) {
logger.info("Er ikke leader, kjører ikke oppdater sak resultat jobb")
return
}

try {
val saker = dao.hentRaderMedPotensiellFeil().groupBy { it.behandlingId }
saker.entries.forEach { (behandlingId, rader) ->
val vedtak =
try {
klient.hentVedtak(behandlingId, HardkodaSystembruker.statistikk)
} catch (e: Exception) {
logger.warn(
"Feilet i henting / oppdatering av behandling resultat " +
"for vedtak til behandling med id = $behandlingId",
)
return@forEach
}
val resultat =
behandlingResultatFraVedtak(
vedtak,
VedtakKafkaHendelseHendelseType.ATTESTERT,
behandligStatus = BehandlingStatus.ATTESTERT,
) ?: throw InternfeilException(
"Fikk ikke utledet resultat fra vedtak til " +
"behandling med id = $behandlingId",
)
rader.forEach {
try {
dao.oppdaterResultat(it, resultat)
} catch (e: Exception) {
logger.warn(
"Kunne ikke oppdatere resulatet for sak rad med id = ${it.id} for " +
"behandling med id = $behandlingId",
)
}
}
}
} catch (e: Exception) {
logger.warn("Feilet i uthenting av rader med potensiell feil", e)
}
}
}
}
Loading