diff --git a/README.md b/README.md index 194cc925c..572590a38 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Oppgavestyrere definerer kriterier som ligger til grunn for køer som fordeler o # Bygge og kjøre lokalt -1. Start k9-verdikjede. Er avhengig av vtp, postgresql og nav-auth-mock. +1. Start k9-verdikjede. Er avhengig av vtp, postgresql og azure-mock. diff --git a/build.gradle.kts b/build.gradle.kts index dc7d07eb5..c45f51a22 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar val mainClass = "no.nav.k9.los.K9LosKt" val hikariVersion = "6.1.0" -val flywayVersion = "10.20.1" +val flywayVersion = "10.21.0" val vaultJdbcVersion = "1.3.10" val koinVersion = "4.0.0" val kotliqueryVersion = "1.9.0" @@ -73,9 +73,9 @@ dependencies { implementation("no.nav.k9.statistikk:kontrakter:2.0_20220411110858_dc06dd1") // opentelemetry - implementation("io.opentelemetry:opentelemetry-api:1.43.0") - implementation("io.opentelemetry:opentelemetry-extension-kotlin:1.43.0") - implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.9.0") + implementation("io.opentelemetry:opentelemetry-api:1.44.1") + implementation("io.opentelemetry:opentelemetry-extension-kotlin:1.44.1") + implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations:2.10.0") // Div implementation(enforcedPlatform( "com.fasterxml.jackson:jackson-bom:$jacksonVersion")) diff --git a/force_merge_fil.txt b/force_merge_fil.txt deleted file mode 100644 index bc8ac10bf..000000000 --- a/force_merge_fil.txt +++ /dev/null @@ -1 +0,0 @@ -fil for å fremtvinge diff, for å se om git sier seg enig i at master og featurebranch er forskjellige diff --git a/nais/kafka/produksjonsstyring-k9sak.yml b/nais/kafka/produksjonsstyring-k9sak.yml index dfd834988..84547aa02 100644 --- a/nais/kafka/produksjonsstyring-k9sak.yml +++ b/nais/kafka/produksjonsstyring-k9sak.yml @@ -19,6 +19,9 @@ spec: - team: k9saksbehandling application: k9-sak access: write + - team: k9saksbehandling + application: ung-sak + access: write - team: k9saksbehandling application: k9-los-api access: read diff --git a/nais/prod-fss.yml b/nais/prod-fss.yml index 3ccd4775b..e6089b84f 100644 --- a/nais/prod-fss.yml +++ b/nais/prod-fss.yml @@ -37,6 +37,8 @@ spec: runtime: java destinations: - id: "elastic-apm" + - id: "grafana-lgtm" + vault: enabled: true paths: diff --git a/src/main/kotlin/no/nav/k9/los/K9Los.kt b/src/main/kotlin/no/nav/k9/los/K9Los.kt index 309c24052..6cba969bc 100644 --- a/src/main/kotlin/no/nav/k9/los/K9Los.kt +++ b/src/main/kotlin/no/nav/k9/los/K9Los.kt @@ -113,7 +113,7 @@ fun Application.k9Los() { val k9TilbakeTilLosAdapterTjeneste = koin.get() k9TilbakeTilLosAdapterTjeneste.setup() - if (LocalDateTime.now().isBefore(LocalDateTime.of(2024, 11, 6, 10, 0))) { + if (LocalDateTime.now().isBefore(LocalDateTime.of(2024, 11, 19, 22, 30))) { if (1 == 0) { //HAXX for å ikke kjøre jobb, men indikere at koden er i bruk og dermed ikke slettes //koin.get().kjørReservasjonskonvertering() //TODO slette //koin.get().kjørFeiloppgaverVask() //TODO slette @@ -122,9 +122,7 @@ fun Application.k9Los() { koin.get().kjørHistorikkvask() koin.get().kjørHistorikkvask() } - if (configuration.koinProfile == KoinProfile.PREPROD) { - koin.get().kjørHistorikkvask() - } + koin.get().kjørHistorikkvask() } install(Authentication) { diff --git a/src/main/kotlin/no/nav/k9/los/eventhandler/RefreshK9v3Tjeneste.kt b/src/main/kotlin/no/nav/k9/los/eventhandler/RefreshK9v3Tjeneste.kt index 22a6631a6..e31bbfd6e 100644 --- a/src/main/kotlin/no/nav/k9/los/eventhandler/RefreshK9v3Tjeneste.kt +++ b/src/main/kotlin/no/nav/k9/los/eventhandler/RefreshK9v3Tjeneste.kt @@ -88,7 +88,8 @@ class RefreshK9v3Tjeneste( @WithSpan fun behandlingerTilOppfriskning(tx: TransactionalSession, antallPrKø: Int) : Set { return DetaljerMetrikker.time("RefreshK9V3", "refreshForKøer", "alle") { - val alleKøer = oppgaveKoRepository.hentListe(false) + oppgaveKoRepository.hentListe(true) + val alleKøer = oppgaveKoRepository.hentListe(medSkjermet = false, medSaksbehandlere = false) + + oppgaveKoRepository.hentListe(medSkjermet = true, medSaksbehandlere = false) val behandlinger = behandlingerTilOppfriskning(tx, alleKøer, antallPrKø) log.info("Hentet ${behandlinger.size} oppgaver fra ${alleKøer.size} køer") behandlinger diff --git a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/K9TilbakeTilLosHistorikkvaskTjeneste.kt b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/K9TilbakeTilLosHistorikkvaskTjeneste.kt index b6ec76783..87b06d142 100644 --- a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/K9TilbakeTilLosHistorikkvaskTjeneste.kt +++ b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/K9TilbakeTilLosHistorikkvaskTjeneste.kt @@ -38,9 +38,9 @@ class K9TilbakeTilLosHistorikkvaskTjeneste( name = TRÅDNAVN ) { - Thread.sleep(2.toDuration(DurationUnit.MINUTES).inWholeMilliseconds) + Thread.sleep(1.toDuration(DurationUnit.MINUTES).inWholeMilliseconds) - val dispatcher = newFixedThreadPoolContext(5, "Historikkvask k9tilbake") + val dispatcher = newFixedThreadPoolContext(1, "Historikkvask k9tilbake") log.info("Starter avspilling av historiske BehandlingProsessEventer") @@ -141,16 +141,16 @@ class K9TilbakeTilLosHistorikkvaskTjeneste( val behandlingProsessEventer = DetaljerMetrikker.time("k9tilbakeHistorikkvask", "hentEventer") { behandlingProsessEventTilbakeRepository.hentMedLås(tx, uuid).eventer } val høyesteInternVersjon = DetaljerMetrikker.time("k9tilbakeHistorikkvask", "hentHøyesteInternVersjon") { - oppgaveV3Tjeneste.hentHøyesteInternVersjon(uuid.toString(), "k9tilbake", "K9", tx)!! + oppgaveV3Tjeneste.hentHøyesteInternVersjon(uuid.toString(), "k9tilbake", "K9", tx) } var eventNrForBehandling = 0L var oppgaveV3 : OppgaveV3? = null for (event in behandlingProsessEventer) { - if (eventNrForBehandling > høyesteInternVersjon) { + if (høyesteInternVersjon != null && eventNrForBehandling > høyesteInternVersjon) { log.info("Avbryter historikkvask for ${event.eksternId} ved eventTid ${event.eventTid}. Forventer at håndteres av vanlig adaptertjeneste.") break //Historikkvasken har funnet eventer som ennå ikke er lastet inn med normalflyt. Dirty eventer skal håndteres av vanlig adaptertjeneste } - var oppgaveDto = TilbakeEventTilDtoMapper.lagOppgaveDto(event, forrigeOppgave) + val oppgaveDto = TilbakeEventTilDtoMapper.lagOppgaveDto(event, forrigeOppgave) oppgaveV3 = DetaljerMetrikker.time("k9tilbakeHistorikkvask", "utledEksisterendeOppgaveversjon") { oppgaveV3Tjeneste.utledEksisterendeOppgaveversjon(oppgaveDto, eventNrForBehandling, tx) } DetaljerMetrikker.time("k9tilbakeHistorikkvask", "oppdaterEksisterendeOppgaveversjon") { oppgaveV3Tjeneste.oppdaterEksisterendeOppgaveversjon(oppgaveV3, eventNrForBehandling, tx) } diff --git a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/TilbakeEventTilDtoMapper.kt b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/TilbakeEventTilDtoMapper.kt index 060030acc..8ab997000 100644 --- a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/TilbakeEventTilDtoMapper.kt +++ b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/domeneadaptere/k9/tilbaketillos/TilbakeEventTilDtoMapper.kt @@ -121,10 +121,14 @@ class TilbakeEventTilDtoMapper { ), OppgaveFeltverdiDto( nøkkel = "registrertDato", - //TODO feltet heter *dato, avrunde til dato? verdi = forrigeOppgave?.hentVerdi("registrertDato") ?: event.opprettetBehandling.truncatedTo(ChronoUnit.SECONDS).toString() ), + OppgaveFeltverdiDto( + nøkkel = "mottattDato", + verdi = forrigeOppgave?.hentVerdi("mottattDato") + ?: event.opprettetBehandling.truncatedTo(ChronoUnit.SECONDS).toString() + ), OppgaveFeltverdiDto( nøkkel = "feilutbetaltBeløp", verdi = event.feilutbetaltBeløp?.toString() ?: forrigeOppgave?.hentVerdi("feilutbetaltBeløp") diff --git a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/AntallOppgaver.kt b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/AntallOppgaver.kt new file mode 100644 index 000000000..7855cbfd5 --- /dev/null +++ b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/AntallOppgaver.kt @@ -0,0 +1,3 @@ +package no.nav.k9.los.nyoppgavestyring.ko + +data class AntallOppgaver(val antallUtenReserverte: Long) diff --git a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoApis.kt b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoApis.kt index 77edc6eb3..b3d47a50b 100644 --- a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoApis.kt +++ b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoApis.kt @@ -105,6 +105,26 @@ fun Route.OppgaveKoApis() { } } + get("/andre-saksbehandleres-koer") { + requestContextService.withRequestContext(call) { + if (pepClient.erOppgaveStyrer()) { + call.respond( + oppgaveKoTjeneste.hentKøerForSaksbehandler( + call.parameters["id"]?.toLong()!!, + pepClient.harTilgangTilKode6() + ).map { + OppgaveKoIdOgTittel( + id = it.id, + tittel = it.tittel + ) + } + ) + } else { + call.respond(HttpStatusCode.Forbidden) + } + } + } + get("/{id}/oppgaver") { requestContextService.withRequestContext(call) { if (pepClient.harTilgangTilReserveringAvOppgaver()) { @@ -160,6 +180,24 @@ fun Route.OppgaveKoApis() { } } + get("/{id}/antall-uten-reserverte") { + requestContextService.withRequestContext(call) { + if (pepClient.harBasisTilgang()) { + val oppgavekøId = call.parameters["id"]!! + + val antallUtenReserverte = OpentelemetrySpanUtil.span("OppgaveKoTjeneste.hentAntallOppgaverForKø") { + oppgaveKoTjeneste.hentAntallOppgaverForKø( + oppgavekøId.toLong(), + true + ) + } + call.respond(AntallOppgaver(antallUtenReserverte)) + } else { + call.respond(HttpStatusCode.Forbidden) + } + } + } + post("/{id}/fa-oppgave") { requestContextService.withRequestContext(call) { if (pepClient.harTilgangTilReserveringAvOppgaver()) { diff --git a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoTjeneste.kt b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoTjeneste.kt index 735d94b62..7c2741986 100644 --- a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoTjeneste.kt +++ b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/OppgaveKoTjeneste.kt @@ -20,7 +20,10 @@ import no.nav.k9.los.nyoppgavestyring.mottak.oppgave.AktivOppgaveRepository import no.nav.k9.los.nyoppgavestyring.query.Avgrensning import no.nav.k9.los.nyoppgavestyring.query.OppgaveQueryService import no.nav.k9.los.nyoppgavestyring.query.QueryRequest -import no.nav.k9.los.nyoppgavestyring.reservasjon.* +import no.nav.k9.los.nyoppgavestyring.reservasjon.AlleredeReservertException +import no.nav.k9.los.nyoppgavestyring.reservasjon.ManglerTilgangException +import no.nav.k9.los.nyoppgavestyring.reservasjon.ReservasjonV3 +import no.nav.k9.los.nyoppgavestyring.reservasjon.ReservasjonV3Tjeneste import no.nav.k9.los.nyoppgavestyring.visningoguttrekk.GenerellOppgaveV3Dto import no.nav.k9.los.nyoppgavestyring.visningoguttrekk.Oppgave import no.nav.k9.los.nyoppgavestyring.visningoguttrekk.OppgaveRepository @@ -87,7 +90,27 @@ class OppgaveKoTjeneste( saksbehandlerEpost: String ): List { return transactionalManager.transaction { tx -> - oppgaveKoRepository.hentKoerMedOppgittSaksbehandler(tx, saksbehandlerEpost) + oppgaveKoRepository.hentKoerMedOppgittSaksbehandler( + tx = tx, + saksbehandlerEpost = saksbehandlerEpost, + medSaksbehandlere = false, + skjermet = false + ) + } + } + + @WithSpan + fun hentKøerForSaksbehandler( + saksbehandlerId: Long, + skjermet: Boolean + ): List { + return transactionalManager.transaction { tx -> + oppgaveKoRepository.hentKoerMedOppgittSaksbehandler( + tx = tx, + saksbehandlerId = saksbehandlerId, + medSaksbehandlere = false, + skjermet = skjermet + ) } } diff --git a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/db/OppgaveKoRepository.kt b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/db/OppgaveKoRepository.kt index 1e7ebfcef..9485567fc 100644 --- a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/db/OppgaveKoRepository.kt +++ b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/db/OppgaveKoRepository.kt @@ -31,21 +31,20 @@ class OppgaveKoRepository( objectMapper.writeValueAsString(kode6OppgaveQuery) } - fun hentListe(medSkjermet: Boolean = false): List { + fun hentListe(medSkjermet: Boolean = false, medSaksbehandlere: Boolean = true): List { return using(sessionOf(datasource)) { - it.transaction { tx -> hentListe(tx, medSkjermet) } + it.transaction { tx -> hentListe(tx, medSaksbehandlere, medSkjermet) } } } - fun hentListe(tx: TransactionalSession, skjermet: Boolean = false): List { + fun hentListe(tx: TransactionalSession, medSaksbehandlere: Boolean, skjermet: Boolean = false): List { return tx.run( queryOf( """SELECT id, versjon, tittel, beskrivelse, query, fritt_valg_av_oppgave, endret_tidspunkt, skjermet FROM OPPGAVEKO_V3 WHERE skjermet = :medSkjermet""", mapOf("medSkjermet" to skjermet) - ).map { row -> row.tilOppgaveKo(objectMapper, tx) }.asList + ).map { row -> row.tilOppgaveKo(objectMapper, medSaksbehandlere, tx) }.asList ) - } fun hent(oppgaveKoId: Long): OppgaveKo { @@ -64,11 +63,11 @@ class OppgaveKoRepository( "id" to oppgaveKoId, "skjermet" to skjermet ) - ).map { it.tilOppgaveKo(objectMapper, tx) to it.boolean("skjermet") }.asSingle + ).map { it.tilOppgaveKo(objectMapper, true, tx) to it.boolean("skjermet") }.asSingle ) ?: throw IllegalStateException("Feil ved henting av oppgavekø: $oppgaveKoId") } - fun Row.tilOppgaveKo(objectMapper: ObjectMapper, tx: TransactionalSession): OppgaveKo { + private fun Row.tilOppgaveKo(objectMapper: ObjectMapper, medSaksbehandlere: Boolean = true, tx: TransactionalSession): OppgaveKo { return OppgaveKo( id = long("id"), versjon = long("versjon"), @@ -76,7 +75,7 @@ class OppgaveKoRepository( beskrivelse = string("beskrivelse"), oppgaveQuery = objectMapper.readValue(string("query"), OppgaveQuery::class.java), frittValgAvOppgave = boolean("fritt_valg_av_oppgave"), - saksbehandlere = hentKoSaksbehandlere(tx, long("id")), + saksbehandlere = if (medSaksbehandlere) hentKoSaksbehandlere(tx, long("id")) else emptyList(), endretTidspunkt = localDateTimeOrNull("endret_tidspunkt"), skjermet = boolean("skjermet") ) @@ -154,7 +153,8 @@ class OppgaveKoRepository( fun hentKoerMedOppgittSaksbehandler( tx: TransactionalSession, saksbehandlerEpost: String, - skjermet: Boolean = false + skjermet: Boolean = false, + medSaksbehandlere: Boolean = true ): List { return tx.run( queryOf( @@ -173,17 +173,36 @@ class OppgaveKoRepository( "skjermet" to skjermet ) ).map { row -> - OppgaveKo( - id = row.long("id"), - versjon = row.long("versjon"), - tittel = row.string("tittel"), - beskrivelse = row.string("beskrivelse"), - oppgaveQuery = objectMapper.readValue(row.string("query"), OppgaveQuery::class.java), - frittValgAvOppgave = row.boolean("fritt_valg_av_oppgave"), - saksbehandlere = hentKoSaksbehandlere(tx, row.long("id")), - endretTidspunkt = row.localDateTimeOrNull("endret_tidspunkt"), - skjermet = row.boolean("skjermet") + row.tilOppgaveKo(objectMapper, medSaksbehandlere, tx) + }.asList + ) + } + + fun hentKoerMedOppgittSaksbehandler( + tx: TransactionalSession, + saksbehandlerId: Long, + skjermet: Boolean, + medSaksbehandlere: Boolean + ): List { + return tx.run( + queryOf( + """ + select id, versjon, tittel, beskrivelse, query, fritt_valg_av_oppgave, endret_tidspunkt, skjermet + from OPPGAVEKO_V3 ko + where skjermet = :skjermet AND + exists ( + select 1 + from oppgaveko_saksbehandler os + inner join saksbehandler s on s.epost = os.saksbehandler_epost + where os.oppgaveko_v3_id = ko.id + and s.id = :saksbehandler_id + )""", + mapOf( + "saksbehandler_id" to saksbehandlerId, + "skjermet" to skjermet ) + ).map { row -> + row.tilOppgaveKo(objectMapper, medSaksbehandlere, tx) }.asList ) } diff --git a/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/dto/OppgaveKoIdOgTittel.kt b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/dto/OppgaveKoIdOgTittel.kt new file mode 100644 index 000000000..d9a373f51 --- /dev/null +++ b/src/main/kotlin/no/nav/k9/los/nyoppgavestyring/ko/dto/OppgaveKoIdOgTittel.kt @@ -0,0 +1,6 @@ +package no.nav.k9.los.nyoppgavestyring.ko.dto + +data class OppgaveKoIdOgTittel( + val id: Long, + val tittel: String, +) diff --git a/src/main/kotlin/no/nav/k9/los/tjenester/avdelingsleder/AvdelingslederTjeneste.kt b/src/main/kotlin/no/nav/k9/los/tjenester/avdelingsleder/AvdelingslederTjeneste.kt index 3d7c70dd3..63b9dc0b7 100644 --- a/src/main/kotlin/no/nav/k9/los/tjenester/avdelingsleder/AvdelingslederTjeneste.kt +++ b/src/main/kotlin/no/nav/k9/los/tjenester/avdelingsleder/AvdelingslederTjeneste.kt @@ -112,7 +112,7 @@ class AvdelingslederTjeneste( transactionalManager.transaction { tx -> // V3-modellen: Sletter køer saksbehandler er med i - oppgaveKøV3Repository.hentKoerMedOppgittSaksbehandler(tx, epost, skjermet).forEach { kø -> + oppgaveKøV3Repository.hentKoerMedOppgittSaksbehandler(tx, epost, skjermet, true).forEach { kø -> oppgaveKøV3Repository.endre(tx, kø.copy(saksbehandlere = kø.saksbehandlere - epost)) } @@ -139,6 +139,7 @@ class AvdelingslederTjeneste( val saksbehandlersKoer = hentSaksbehandlersOppgavekoer() return saksbehandlersKoer.entries.map { SaksbehandlerDto( + id = it.key.id, brukerIdent = it.key.brukerIdent, navn = it.key.navn, epost = it.key.epost, diff --git a/src/main/kotlin/no/nav/k9/los/tjenester/saksbehandler/saksliste/SaksbehandlerDto.kt b/src/main/kotlin/no/nav/k9/los/tjenester/saksbehandler/saksliste/SaksbehandlerDto.kt index 7dd848658..3a9161529 100644 --- a/src/main/kotlin/no/nav/k9/los/tjenester/saksbehandler/saksliste/SaksbehandlerDto.kt +++ b/src/main/kotlin/no/nav/k9/los/tjenester/saksbehandler/saksliste/SaksbehandlerDto.kt @@ -1,6 +1,7 @@ package no.nav.k9.los.tjenester.saksbehandler.saksliste data class SaksbehandlerDto( + val id: Long?, val brukerIdent: String?, val navn: String?, val epost: String, diff --git a/src/main/resources/adapterdefinisjoner/k9-oppgavetyper-k9tilbake.json b/src/main/resources/adapterdefinisjoner/k9-oppgavetyper-k9tilbake.json index 7c4853689..d7ac0d20c 100644 --- a/src/main/resources/adapterdefinisjoner/k9-oppgavetyper-k9tilbake.json +++ b/src/main/resources/adapterdefinisjoner/k9-oppgavetyper-k9tilbake.json @@ -36,6 +36,11 @@ "visPåOppgave": true, "påkrevd": false }, + { + "id": "mottattDato", + "visPåOppgave": true, + "påkrevd": false + }, { "id": "registrertDato", "visPåOppgave": true,