diff --git a/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiver.kt b/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiver.kt index 2cb8aee45..dbc1725b1 100644 --- a/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiver.kt +++ b/db/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiver.kt @@ -38,8 +38,7 @@ class LagreJournalpostIdRiver( private val sikkerLogger = sikkerLogger() override fun les(json: Map): LagreJournalpostIdMelding? = - // TODO legg til Key.BEHOV etter overgangsfase - if (setOf(Key.DATA, Key.FAIL).any(json::containsKey)) { + if (setOf(Key.BEHOV, Key.DATA, Key.FAIL).any(json::containsKey)) { null } else { LagreJournalpostIdMelding( diff --git a/db/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiverTest.kt b/db/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiverTest.kt index 7e1e85b2a..242134c74 100644 --- a/db/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiverTest.kt +++ b/db/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/db/river/LagreJournalpostIdRiverTest.kt @@ -16,6 +16,7 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull import no.nav.helsearbeidsgiver.domene.inntektsmelding.v1.Inntektsmelding +import no.nav.helsearbeidsgiver.felles.BehovType import no.nav.helsearbeidsgiver.felles.EventName import no.nav.helsearbeidsgiver.felles.Key import no.nav.helsearbeidsgiver.felles.json.toJson @@ -203,7 +204,7 @@ class LagreJournalpostIdRiverTest : context("ignorerer melding") { withData( mapOf( -// "melding med ukjent behov" to Pair(Key.BEHOV, BehovType.LAGRE_IM_SKJEMA.toJson()), + "melding med ukjent behov" to Pair(Key.BEHOV, BehovType.LAGRE_IM_SKJEMA.toJson()), "melding med data" to Pair(Key.DATA, "".toJson()), "melding med fail" to Pair(Key.FAIL, Mock.fail.toJson(Fail.serializer())), ), diff --git a/distribusjon/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiver.kt b/distribusjon/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiver.kt index 04890552e..a767dda27 100644 --- a/distribusjon/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiver.kt +++ b/distribusjon/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiver.kt @@ -5,7 +5,6 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.JsonElement import no.nav.helsearbeidsgiver.domene.inntektsmelding.Utils.convert import no.nav.helsearbeidsgiver.domene.inntektsmelding.v1.Inntektsmelding -import no.nav.helsearbeidsgiver.felles.BehovType import no.nav.helsearbeidsgiver.felles.EventName import no.nav.helsearbeidsgiver.felles.Key import no.nav.helsearbeidsgiver.felles.json.krev @@ -45,12 +44,8 @@ class DistribusjonRiver( private val logger = logger() private val sikkerLogger = sikkerLogger() - override fun les(json: Map): Melding? { - val behovType = Key.BEHOV.lesOrNull(BehovType.serializer(), json) - return if ( - setOf(Key.DATA, Key.FAIL).any(json::containsKey) || - (behovType != null && behovType != BehovType.DISTRIBUER_IM) - ) { + override fun les(json: Map): Melding? = + if (setOf(Key.BEHOV, Key.DATA, Key.FAIL).any(json::containsKey)) { null } else { Melding( @@ -61,7 +56,6 @@ class DistribusjonRiver( journalpostId = Key.JOURNALPOST_ID.les(String.serializer(), json), ) } - } override fun Melding.haandter(json: Map): Map { "Forsøker å distribuere IM med journalpost-ID '$journalpostId'.".also { @@ -95,11 +89,7 @@ class DistribusjonRiver( event = eventName, transaksjonId = transaksjonId, forespoerselId = null, - utloesendeMelding = - json - .plus( - Key.BEHOV to BehovType.DISTRIBUER_IM.toJson(), - ).toJson(), + utloesendeMelding = json.toJson(), ) logger.error(fail.feilmelding) diff --git a/distribusjon/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiverTest.kt b/distribusjon/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiverTest.kt index 9e39d258c..873cdc746 100644 --- a/distribusjon/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiverTest.kt +++ b/distribusjon/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/distribusjon/DistribusjonRiverTest.kt @@ -47,110 +47,88 @@ class DistribusjonRiverTest : clearAllMocks() } - context("distribuerer inntektsmelding på kafka topic") { - withData( - mapOf( - "for vanlig melding" to null, - "for retry-melding" to BehovType.DISTRIBUER_IM, - ), - ) { innkommendeBehov -> + test("distribuerer inntektsmelding på kafka topic") { + every { mockKafkaProducer.send(any()) } returns CompletableFuture() - every { mockKafkaProducer.send(any()) } returns CompletableFuture() + val innkommendeMelding = Mock.innkommendeMelding() - val innkommendeMelding = Mock.innkommendeMelding() + testRapid.sendJson(innkommendeMelding.toMap()) - testRapid.sendJson( - innkommendeMelding - .toMap() - .plus(Key.BEHOV to innkommendeBehov?.toJson()) - .mapValuesNotNull { it }, + testRapid.inspektør.size shouldBeExactly 1 + + testRapid.firstMessage().toMap() shouldContainExactly + mapOf( + Key.EVENT_NAME to EventName.INNTEKTSMELDING_DISTRIBUERT.toJson(), + Key.UUID to innkommendeMelding.transaksjonId.toJson(), + Key.JOURNALPOST_ID to innkommendeMelding.journalpostId.toJson(), + Key.INNTEKTSMELDING to innkommendeMelding.inntektsmelding.toJson(Inntektsmelding.serializer()), + Key.BESTEMMENDE_FRAVAERSDAG to innkommendeMelding.bestemmendeFravaersdag?.toJson(), + ).mapValuesNotNull { it } + + val forventetRecord = + ProducerRecord( + TOPIC_HELSEARBEIDSGIVER_INNTEKTSMELDING_EKSTERN, + JournalfoertInntektsmelding( + journalpostId = innkommendeMelding.journalpostId, + inntektsmeldingV1 = innkommendeMelding.inntektsmelding, + bestemmendeFravaersdag = innkommendeMelding.bestemmendeFravaersdag, + inntektsmelding = innkommendeMelding.inntektsmelding.convert().copy(bestemmendeFraværsdag = Mock.bestemmendeFravaersdag), + selvbestemt = false, + ).toJsonStr(JournalfoertInntektsmelding.serializer()), ) - testRapid.inspektør.size shouldBeExactly 1 - - testRapid.firstMessage().toMap() shouldContainExactly - mapOf( - Key.EVENT_NAME to EventName.INNTEKTSMELDING_DISTRIBUERT.toJson(), - Key.UUID to innkommendeMelding.transaksjonId.toJson(), - Key.JOURNALPOST_ID to innkommendeMelding.journalpostId.toJson(), - Key.INNTEKTSMELDING to innkommendeMelding.inntektsmelding.toJson(Inntektsmelding.serializer()), - Key.BESTEMMENDE_FRAVAERSDAG to innkommendeMelding.bestemmendeFravaersdag?.toJson(), - ).mapValuesNotNull { it } - - val forventetRecord = - ProducerRecord( - TOPIC_HELSEARBEIDSGIVER_INNTEKTSMELDING_EKSTERN, - JournalfoertInntektsmelding( - journalpostId = innkommendeMelding.journalpostId, - inntektsmeldingV1 = innkommendeMelding.inntektsmelding, - bestemmendeFravaersdag = innkommendeMelding.bestemmendeFravaersdag, - inntektsmelding = innkommendeMelding.inntektsmelding.convert().copy(bestemmendeFraværsdag = Mock.bestemmendeFravaersdag), - selvbestemt = false, - ).toJsonStr(JournalfoertInntektsmelding.serializer()), - ) - - verifySequence { - mockKafkaProducer.send(forventetRecord) - } + verifySequence { + mockKafkaProducer.send(forventetRecord) } } - context("distribuerer selvbestemt inntektsmelding på kafka topic") { - withData( - mapOf( - "for vanlig melding" to null, - "for retry-melding" to BehovType.DISTRIBUER_IM, - ), - ) { innkommendeBehov -> + test("distribuerer selvbestemt inntektsmelding på kafka topic") { + every { mockKafkaProducer.send(any()) } returns CompletableFuture() - every { mockKafkaProducer.send(any()) } returns CompletableFuture() + val selvbestemtInntektsmelding = + mockInntektsmeldingV1().copy( + type = + Inntektsmelding.Type.Selvbestemt( + id = UUID.randomUUID(), + ), + ) - val selvbestemtInntektsmelding = - mockInntektsmeldingV1().copy( - type = - Inntektsmelding.Type.Selvbestemt( - id = UUID.randomUUID(), - ), - ) + val innkommendeMelding = Mock.innkommendeMelding() - val innkommendeMelding = Mock.innkommendeMelding() + testRapid.sendJson( + innkommendeMelding + .toMap() + .minus(Key.BESTEMMENDE_FRAVAERSDAG) + .plus(Key.INNTEKTSMELDING to selvbestemtInntektsmelding.toJson(Inntektsmelding.serializer())), + ) - testRapid.sendJson( - innkommendeMelding - .toMap() - .minus(Key.BESTEMMENDE_FRAVAERSDAG) - .plus(Key.INNTEKTSMELDING to selvbestemtInntektsmelding.toJson(Inntektsmelding.serializer())) - .plus(Key.BEHOV to innkommendeBehov?.toJson()) - .mapValuesNotNull { it }, + testRapid.inspektør.size shouldBeExactly 1 + + testRapid.firstMessage().toMap() shouldContainExactly + mapOf( + Key.EVENT_NAME to EventName.INNTEKTSMELDING_DISTRIBUERT.toJson(), + Key.UUID to innkommendeMelding.transaksjonId.toJson(), + Key.JOURNALPOST_ID to innkommendeMelding.journalpostId.toJson(), + Key.INNTEKTSMELDING to selvbestemtInntektsmelding.toJson(Inntektsmelding.serializer()), ) - testRapid.inspektør.size shouldBeExactly 1 - - testRapid.firstMessage().toMap() shouldContainExactly - mapOf( - Key.EVENT_NAME to EventName.INNTEKTSMELDING_DISTRIBUERT.toJson(), - Key.UUID to innkommendeMelding.transaksjonId.toJson(), - Key.JOURNALPOST_ID to innkommendeMelding.journalpostId.toJson(), - Key.INNTEKTSMELDING to selvbestemtInntektsmelding.toJson(Inntektsmelding.serializer()), - ) - - val forventetRecord = - ProducerRecord( - TOPIC_HELSEARBEIDSGIVER_INNTEKTSMELDING_EKSTERN, - JournalfoertInntektsmelding( - journalpostId = innkommendeMelding.journalpostId, - inntektsmeldingV1 = selvbestemtInntektsmelding, - bestemmendeFravaersdag = null, - inntektsmelding = selvbestemtInntektsmelding.convert(), - selvbestemt = true, - ).toJsonStr(JournalfoertInntektsmelding.serializer()), - ) - - verifySequence { - mockKafkaProducer.send(forventetRecord) - } + val forventetRecord = + ProducerRecord( + TOPIC_HELSEARBEIDSGIVER_INNTEKTSMELDING_EKSTERN, + JournalfoertInntektsmelding( + journalpostId = innkommendeMelding.journalpostId, + inntektsmeldingV1 = selvbestemtInntektsmelding, + bestemmendeFravaersdag = null, + inntektsmelding = selvbestemtInntektsmelding.convert(), + selvbestemt = true, + ).toJsonStr(JournalfoertInntektsmelding.serializer()), + ) + + verifySequence { + mockKafkaProducer.send(forventetRecord) } } + test("håndterer når producer feiler") { every { mockKafkaProducer.send(any()) } throws RuntimeException("feil og feil, fru blom") @@ -164,11 +142,7 @@ class DistribusjonRiverTest : event = innkommendeMelding.eventName, transaksjonId = innkommendeMelding.transaksjonId, forespoerselId = null, - utloesendeMelding = - innkommendeJsonMap - .plus( - Key.BEHOV to BehovType.DISTRIBUER_IM.toJson(), - ).toJson(), + utloesendeMelding = innkommendeJsonMap.toJson(), ) testRapid.sendJson(innkommendeJsonMap) diff --git a/feil-behandler/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytter.kt b/feil-behandler/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytter.kt index 0d6c3caff..b1d3006bb 100644 --- a/feil-behandler/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytter.kt +++ b/feil-behandler/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytter.kt @@ -8,7 +8,6 @@ import kotlinx.serialization.json.JsonElement import no.nav.hag.utils.bakgrunnsjobb.Bakgrunnsjobb import no.nav.hag.utils.bakgrunnsjobb.BakgrunnsjobbRepository import no.nav.hag.utils.bakgrunnsjobb.BakgrunnsjobbStatus -import no.nav.helsearbeidsgiver.felles.BehovType import no.nav.helsearbeidsgiver.felles.EventName import no.nav.helsearbeidsgiver.felles.Key import no.nav.helsearbeidsgiver.felles.json.toJson @@ -30,19 +29,19 @@ class FeilLytter( private val jobbType = FeilProsessor.JOB_TYPE private val sikkerLogger = sikkerLogger() - val behovSomHaandteres = - listOf( - BehovType.LAGRE_FORESPOERSEL, - BehovType.JOURNALFOER, - ) - val eventerSomHaandteres = + + private val eventerSomHaandteres = listOf( EventName.FORESPOERSEL_MOTTATT, EventName.FORESPOERSEL_BESVART, + EventName.FORESPOERSEL_FORKASTET, + EventName.FORESPOERSEL_KASTET_TIL_INFOTRYGD, EventName.SAK_OG_OPPGAVE_OPPRETT_REQUESTED, EventName.INNTEKTSMELDING_SKJEMA_LAGRET, + EventName.INNTEKTSMELDING_MOTTATT, EventName.INNTEKTSMELDING_JOURNALFOERT, EventName.INNTEKTSMELDING_JOURNALPOST_ID_LAGRET, + EventName.SELVBESTEMT_IM_LAGRET, ) init { @@ -60,28 +59,49 @@ class FeilLytter( context: MessageContext, ) { sikkerLogger.info("Mottok feil: ${packet.toJson().parseJson().toPretty()}") - val fail = packet.toJson().parseJson().toFailOrNull() + val fail = + packet + .toJson() + .parseJson() + .toMap()[Key.FAIL] + ?.runCatching { + fromJson(Fail.serializer()) + }?.getOrNull() + if (fail == null) { sikkerLogger.warn("Kunne ikke parse feil-objekt, ignorerer...") return } val utloesendeMelding = fail.utloesendeMelding.toMap() - if (behovSkalHaandteres(utloesendeMelding) || eventSkalHaandteres(utloesendeMelding)) { - // slå opp transaksjonID. Hvis den finnes, kan det være en annen feilende melding i samme transaksjon (forskjelig behov): Lagre i så fall - // med egen id. Denne id vil så sendes med som ny transaksjonID ved rekjøring.. + if (eventSkalHaandteres(utloesendeMelding)) { + // slå opp transaksjonID. Hvis den finnes, kan det være en annen feilende melding i samme transaksjon: Lagre i så fall + // med egen id. Denne id vil så sendes med som ny transaksjonID ved rekjøring. val jobbId = fail.transaksjonId val eksisterendeJobb = repository.getById(jobbId) - if (eksisterendeJobb != null) { - val uliktBehov = - Key.BEHOV in utloesendeMelding && - utloesendeMelding[Key.BEHOV] != eksisterendeJobb.data.parseJson().toMap()[Key.BEHOV] - val ulikEvent = - Key.EVENT_NAME in utloesendeMelding && - utloesendeMelding[Key.EVENT_NAME] != eksisterendeJobb.data.parseJson().toMap()[Key.EVENT_NAME] - if (uliktBehov || ulikEvent) { - sikkerLogger.info("ID $jobbId finnes fra før med annet behov/event. Lagrer en ny jobb.") + when { + // Første gang denne flyten feiler + eksisterendeJobb == null -> { + sikkerLogger.info("Lagrer mottatt pakke!") + lagre( + Bakgrunnsjobb( + uuid = fail.transaksjonId, + type = jobbType, + data = fail.utloesendeMelding.toString(), + maksAntallForsoek = 10, + ), + ) + } + + // Samme feil har inntruffet flere ganger i samme flyt + utloesendeMelding == eksisterendeJobb.data.parseJson().toMap() -> { + oppdater(eksisterendeJobb) + } + + // Feil i flyt som tidligere har opplevd annen type feil + else -> { + sikkerLogger.info("ID $jobbId finnes fra før med annen utløsende melding. Lagrer en ny jobb.") val nyTransaksjonId = UUID.randomUUID() val utloesendeMeldingMedNyTransaksjonId = utloesendeMelding.plus(Key.UUID to nyTransaksjonId.toJson()) lagre( @@ -92,19 +112,7 @@ class FeilLytter( maksAntallForsoek = 10, ), ) - } else { - oppdater(eksisterendeJobb) } - } else { - sikkerLogger.info("Lagrer mottatt pakke!") - val jobb = - Bakgrunnsjobb( - uuid = fail.transaksjonId, - type = jobbType, - data = fail.utloesendeMelding.toString(), - maksAntallForsoek = 10, - ) - lagre(jobb) } } } @@ -135,23 +143,10 @@ class FeilLytter( } } - fun behovSkalHaandteres(utloesendeMelding: Map): Boolean { - val behovFraMelding = utloesendeMelding[Key.BEHOV]?.fromJson(BehovType.serializer()) - val skalHaandteres = behovSomHaandteres.contains(behovFraMelding) - sikkerLogger.info("Behov: $behovFraMelding skal håndteres: $skalHaandteres") - return skalHaandteres - } - - fun eventSkalHaandteres(utloesendeMelding: Map): Boolean { + private fun eventSkalHaandteres(utloesendeMelding: Map): Boolean { val eventFraMelding = utloesendeMelding[Key.EVENT_NAME]?.fromJson(EventName.serializer()) val skalHaandteres = eventerSomHaandteres.contains(eventFraMelding) sikkerLogger.info("Event: $eventFraMelding skal håndteres: $skalHaandteres") return skalHaandteres } } - -fun JsonElement.toFailOrNull(): Fail? = - toMap()[Key.FAIL] - ?.runCatching { - fromJson(Fail.serializer()) - }?.getOrNull() diff --git a/feil-behandler/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytterTest.kt b/feil-behandler/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytterTest.kt index be351c640..206df4964 100644 --- a/feil-behandler/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytterTest.kt +++ b/feil-behandler/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/feilbehandler/river/FeilLytterTest.kt @@ -2,21 +2,16 @@ package no.nav.helsearbeidsgiver.inntektsmelding.feilbehandler.river import com.github.navikt.tbd_libs.rapids_and_rivers.test_support.TestRapid import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.booleans.shouldBeTrue -import io.kotest.matchers.equals.shouldNotBeEqual import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.maps.shouldContainExactly import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.builtins.serializer import no.nav.hag.utils.bakgrunnsjobb.Bakgrunnsjobb import no.nav.hag.utils.bakgrunnsjobb.BakgrunnsjobbStatus import no.nav.hag.utils.bakgrunnsjobb.MockBakgrunnsjobbRepository -import no.nav.helsearbeidsgiver.felles.BehovType import no.nav.helsearbeidsgiver.felles.EventName import no.nav.helsearbeidsgiver.felles.Key -import no.nav.helsearbeidsgiver.felles.json.les import no.nav.helsearbeidsgiver.felles.json.toJson import no.nav.helsearbeidsgiver.felles.json.toMap import no.nav.helsearbeidsgiver.felles.rapidsrivers.model.Fail @@ -35,136 +30,69 @@ class FeilLytterTest : val rapid = TestRapid() val repository = MockBakgrunnsjobbRepository() - val handler = FeilLytter(rapid, repository) + FeilLytter(rapid, repository) afterTest { repository.deleteAll() } - test("skal håndtere gyldige feil med spesifiserte behov") { - handler.behovSomHaandteres.forEach { handler.behovSkalHaandteres(utloesendeMelding(it)) shouldBe true } - } - - test("skal ignorere gyldige feil med visse behov") { - val ignorerteBehov = BehovType.entries.filterNot { handler.behovSomHaandteres.contains(it) } - ignorerteBehov.forEach { handler.behovSkalHaandteres(utloesendeMelding(it)) shouldBe false } - } - - test("skal ignorere feil uten behov") { - val utloesendeMelding = - mapOf( - Key.UUID to UUID.randomUUID().toJson(), - Key.FORESPOERSEL_ID to UUID.randomUUID().toJson(), - ) - handler.behovSkalHaandteres(utloesendeMelding) shouldBe false - } - - test("skal behandle feil uten behov, men med spesifiserte eventer") { - handler.eventerSomHaandteres.forEach { event -> - val utloesendeMelding = - mapOf( - Key.EVENT_NAME to event.toJson(), - Key.UUID to UUID.randomUUID().toJson(), - Key.FORESPOERSEL_ID to UUID.randomUUID().toJson(), - ) - - handler.eventSkalHaandteres(utloesendeMelding).shouldBeTrue() - } - } + test("ved flere feil på samme transaksjon-ID og event, så oppdateres eksisterende jobb") { + val omEttMinutt = LocalDateTime.now().plusMinutes(1) + val forespoerselMottattFail = lagFail(EventName.FORESPOERSEL_MOTTATT) - test("skal håndtere feil uten forespørselId") { - val utloesendeMelding = utloesendeMelding(BehovType.JOURNALFOER).minus(Key.FORESPOERSEL_ID) - handler.behovSkalHaandteres(utloesendeMelding) shouldBe true - } + rapid.sendJson(forespoerselMottattFail.tilMelding()) - test("Ny feil med forskjellig behov og samme id skal lagres") { - val now = LocalDateTime.now() - rapid.sendTestMessage(lagRapidFeilmelding()) - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 1 - rapid.sendTestMessage(lagRapidFeilmelding(BehovType.JOURNALFOER)) - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 2 - } + repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.FEILET), true).size shouldBeExactly 0 + repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBeExactly 1 - test("Duplikatfeil (samme feil etter rekjøring) skal oppdatere eksisterende feil -> status: FEILET") { - val now = LocalDateTime.now() - val feilmelding = lagRapidFeilmelding() - rapid.sendTestMessage(feilmelding) - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 1 - rapid.sendTestMessage(feilmelding) - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 0 - val oppdatert = repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.FEILET), true) - oppdatert.size shouldBe 1 - oppdatert[0].forsoek shouldBe 0 // Antall forsøk oppdateres av bakgrunnsjobbService - - rapid.sendTestMessage(feilmelding) - rapid.sendTestMessage(feilmelding) - rapid.sendTestMessage(feilmelding) - val feilet = repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.FEILET), true) - feilet.size shouldBe 1 - feilet[0].forsoek shouldBe 0 - } + rapid.sendJson(forespoerselMottattFail.tilMelding()) - test("Skal sette jobb til STOPPET når maks antall forsøk er overskredet") { - val now = LocalDateTime.now() - val feilmelding = lagRapidFeilmelding() - val feil = feilmelding.parseJson().toFailOrNull()!! + val jobber = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.FEILET), true) + repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBeExactly 0 - repository.save( - Bakgrunnsjobb( - feil.transaksjonId, - FeilProsessor.JOB_TYPE, - forsoek = 4, - maksAntallForsoek = 3, - data = feil.utloesendeMelding.toString(), - ), - ) - rapid.sendTestMessage(feilmelding) - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.STOPPET), true).size shouldBe 1 - } + jobber.size shouldBeExactly 1 - test("Flere feil i en lang verdikjede (ny feil / nytt behov og ny transaksjon etter en OK rekjøring) skal opprette en ny feil") { - val now = LocalDateTime.now() - val transaksjonId = UUID.randomUUID() - val feilmeldingJournalfoer = lagRapidFeilmelding(BehovType.JOURNALFOER, transaksjonId) - rapid.sendTestMessage(feilmeldingJournalfoer) - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 1 - // nå kjører bakgrunnsjobb, plukker opp feilen og rekjører - det går fint, så feilen kommer ikke på nytt. - // Istedet feiler neste steg - nytt behov fra samme transaksjon - val feilmeldingLagre = lagRapidFeilmelding(BehovType.LAGRE_FORESPOERSEL, transaksjonId) - rapid.sendTestMessage(feilmeldingLagre) - // status på gammel jobb blir ikke oppdatert i denne testen.. - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 2 - - val utloesendeMelding = repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true)[1].data - val nyTransaksjonId = Key.UUID.les(UuidSerializer, Json.parseToJsonElement(utloesendeMelding).toMap()) - transaksjonId shouldNotBeEqual nyTransaksjonId - - val nyFeilmeldingLagre = lagRapidFeilmelding(BehovType.LAGRE_FORESPOERSEL, nyTransaksjonId) - rapid.sendTestMessage(nyFeilmeldingLagre) // !! ny tx, ikke samme igjen! - - // Bakgrunnsjobben har blitt oppdatert og går til status FEILET.. - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 1 - repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.FEILET), true).size shouldBe 1 + jobber[0].uuid shouldBe forespoerselMottattFail.transaksjonId + jobber[0].data.parseJson().toMap() shouldContainExactly forespoerselMottattFail.utloesendeMelding.toMap() } - test("ved flere feil på samme transaksjon-ID og event, så oppdateres eksisterende jobb") { + test("ved flere feil på samme transaksjon-ID og event, men ulikt innhold, så lagres to jobber med ulik transaksjon-ID") { val omEttMinutt = LocalDateTime.now().plusMinutes(1) - val forespoerselMottattFail = lagFail(EventName.FORESPOERSEL_MOTTATT) + val transaksjonId = UUID.randomUUID() + val forespoerselMottattFail = lagFail(EventName.FORESPOERSEL_MOTTATT, transaksjonId) + val forespoerselMottattFailMedUliktInnhold = + lagFail(EventName.FORESPOERSEL_MOTTATT, transaksjonId).let { + it.copy( + utloesendeMelding = + it.utloesendeMelding + .toMap() + .plus(Key.ER_DUPLIKAT_IM to "kanskje".toJson()) + .toJson(), + ) + } rapid.sendJson(forespoerselMottattFail.tilMelding()) repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.FEILET), true).size shouldBeExactly 0 repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBeExactly 1 - rapid.sendJson(forespoerselMottattFail.tilMelding()) + rapid.sendJson(forespoerselMottattFailMedUliktInnhold.tilMelding()) - val jobber = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.FEILET), true) - repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBeExactly 0 + repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.FEILET), true).size shouldBeExactly 0 + val jobber = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.OPPRETTET), true) - jobber.size shouldBeExactly 1 + jobber.size shouldBeExactly 2 - jobber[0].uuid shouldBe forespoerselMottattFail.transaksjonId + jobber[0].uuid shouldBe transaksjonId jobber[0].data.parseJson().toMap() shouldContainExactly forespoerselMottattFail.utloesendeMelding.toMap() + + jobber[1].uuid shouldNotBe transaksjonId + jobber[1].data.parseJson().toMap().also { + it[Key.EVENT_NAME]?.fromJson(EventName.serializer()) shouldBe forespoerselMottattFailMedUliktInnhold.event + it[Key.ER_DUPLIKAT_IM]?.fromJson(String.serializer()) shouldBe "kanskje" + + it[Key.UUID]?.fromJson(UuidSerializer) shouldNotBe transaksjonId + } } test("ved flere feil på samme transaksjon-ID, men ulik event, så lagres to jobber med ulik transaksjon-ID") { @@ -242,35 +170,89 @@ class FeilLytterTest : jobber[1].uuid shouldBe forespoerselBesvartFail.transaksjonId jobber[1].data.parseJson().toMap() shouldContainExactly forespoerselBesvartFail.utloesendeMelding.toMap() } + + test("setter jobb til STOPPET når maks antall forsøk er overskredet") { + val omEttMinutt = LocalDateTime.now().plusMinutes(1) + val feil = lagFail(EventName.INNTEKTSMELDING_JOURNALFOERT) + + repository.save( + Bakgrunnsjobb( + feil.transaksjonId, + FeilProsessor.JOB_TYPE, + forsoek = 4, + maksAntallForsoek = 3, + data = feil.utloesendeMelding.toString(), + ), + ) + + rapid.sendJson(feil.tilMelding()) + + val stoppede = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, setOf(BakgrunnsjobbStatus.STOPPET), true) + + stoppede.size shouldBeExactly 1 + } + + context("ignorerer melding") { + test("med event som ikke støttes") { + val omEttMinutt = LocalDateTime.now().plusMinutes(1) + val ikkeStoettetFail = lagFail(EventName.TILGANG_ORG_REQUESTED) + + rapid.sendJson(ikkeStoettetFail.tilMelding()) + + val jobber = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, BakgrunnsjobbStatus.entries.toSet(), true) + + jobber.size shouldBeExactly 0 + } + + test("uten event") { + val omEttMinutt = LocalDateTime.now().plusMinutes(1) + val stoettetFailUtenEvent = + lagFail(EventName.INNTEKTSMELDING_JOURNALFOERT).let { + it.copy( + utloesendeMelding = + it.utloesendeMelding + .toMap() + .minus(Key.EVENT_NAME) + .toJson(), + ) + } + + rapid.sendJson(stoettetFailUtenEvent.tilMelding()) + + val jobber = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, BakgrunnsjobbStatus.entries.toSet(), true) + + jobber.size shouldBeExactly 0 + } + + test("med event som støttes, men med ugyldig feil") { + val omEttMinutt = LocalDateTime.now().plusMinutes(1) + val stoettetFail = lagFail(EventName.INNTEKTSMELDING_JOURNALFOERT) + + rapid.sendJson( + stoettetFail.tilMelding().plus(Key.FAIL to "ikke en fail".toJson()), + ) + + val jobber = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, BakgrunnsjobbStatus.entries.toSet(), true) + + jobber.size shouldBeExactly 0 + } + + test("med event som støttes, men uten feil") { + val omEttMinutt = LocalDateTime.now().plusMinutes(1) + val stoettetFail = lagFail(EventName.INNTEKTSMELDING_JOURNALFOERT) + + rapid.sendJson( + stoettetFail.tilMelding().minus(Key.FAIL), + ) + + val jobber = repository.findByKjoeretidBeforeAndStatusIn(omEttMinutt, BakgrunnsjobbStatus.entries.toSet(), true) + + jobber.size shouldBeExactly 0 + } + } }) -fun lagRapidFeilmelding( - behovType: BehovType = BehovType.JOURNALFOER, - transaksjonId: UUID = UUID.randomUUID(), -): String { - val eventName = EventName.INNTEKTSMELDING_MOTTATT - val forespoerselId = UUID.randomUUID() - - return mapOf( - Key.FAIL to - Fail( - feilmelding = "Klarte ikke journalføre", - event = eventName, - transaksjonId = transaksjonId, - forespoerselId = forespoerselId, - utloesendeMelding = - mapOf( - Key.EVENT_NAME to eventName.toJson(), - Key.BEHOV to behovType.toJson(), - Key.FORESPOERSEL_ID to forespoerselId.toJson(), - Key.UUID to transaksjonId.toJson(), - ).toJson(), - ).toJson(Fail.serializer()), - ).toJson() - .toString() -} - -fun lagFail( +private fun lagFail( eventName: EventName, transaksjonId: UUID = UUID.randomUUID(), ): Fail = @@ -285,11 +267,3 @@ fun lagFail( Key.UUID to transaksjonId.toJson(), ).toJson(), ) - -fun utloesendeMelding(behov: BehovType): Map = - mapOf( - Key.EVENT_NAME to EventName.KVITTERING_REQUESTED.toJson(), - Key.BEHOV to behov.toJson(), - Key.UUID to UUID.randomUUID().toJson(), - Key.FORESPOERSEL_ID to UUID.randomUUID().toJson(), - ) diff --git a/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/EventTypes.kt b/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/EventTypes.kt index fc3b88597..3c47e0bb6 100644 --- a/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/EventTypes.kt +++ b/felles/src/main/kotlin/no/nav/helsearbeidsgiver/felles/EventTypes.kt @@ -25,10 +25,6 @@ enum class BehovType { // Asynkrone endringer LAGRE_FORESPOERSEL, // TODO slett etter databasen er flyttet - - // Asynkrone endringer, men brukt til å prøve igjen ved feil - JOURNALFOER, - DISTRIBUER_IM, } @Serializable diff --git a/joark/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiver.kt b/joark/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiver.kt index 0cd5a74c9..900f8dba3 100644 --- a/joark/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiver.kt +++ b/joark/src/main/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiver.kt @@ -5,7 +5,6 @@ import no.nav.helsearbeidsgiver.dokarkiv.DokArkivClient import no.nav.helsearbeidsgiver.dokarkiv.domene.Avsender import no.nav.helsearbeidsgiver.dokarkiv.domene.GjelderPerson import no.nav.helsearbeidsgiver.domene.inntektsmelding.v1.Inntektsmelding -import no.nav.helsearbeidsgiver.felles.BehovType import no.nav.helsearbeidsgiver.felles.EventName import no.nav.helsearbeidsgiver.felles.Key import no.nav.helsearbeidsgiver.felles.json.les @@ -38,12 +37,8 @@ class JournalfoerImRiver( private val logger = logger() private val sikkerLogger = sikkerLogger() - override fun les(json: Map): JournalfoerImMelding? { - val behovType = Key.BEHOV.lesOrNull(BehovType.serializer(), json) - return if ( - setOf(Key.DATA, Key.FAIL).any(json::containsKey) || - (behovType != null && behovType != BehovType.JOURNALFOER) - ) { + override fun les(json: Map): JournalfoerImMelding? = + if (setOf(Key.BEHOV, Key.DATA, Key.FAIL).any(json::containsKey)) { null } else { val eventName = Key.EVENT_NAME.les(EventName.serializer(), json) @@ -70,7 +65,6 @@ class JournalfoerImRiver( null } } - } override fun JournalfoerImMelding.haandter(json: Map): Map { "Mottok melding med event '$eventName'.".also { @@ -104,11 +98,7 @@ class JournalfoerImRiver( event = eventName, transaksjonId = transaksjonId, forespoerselId = null, - utloesendeMelding = - json - .plus( - Key.BEHOV to BehovType.JOURNALFOER.toJson(), - ).toJson(), + utloesendeMelding = json.toJson(), ) logger.error(fail.feilmelding) diff --git a/joark/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiverTest.kt b/joark/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiverTest.kt index 116bee8cc..b3ecf9485 100644 --- a/joark/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiverTest.kt +++ b/joark/src/test/kotlin/no/nav/helsearbeidsgiver/inntektsmelding/joark/JournalfoerImRiverTest.kt @@ -149,56 +149,6 @@ class JournalfoerImRiverTest : } } - test("håndterer retries (med BehovType.JOURNALFOER)") { - val journalpostId = UUID.randomUUID().toString() - val selvbestemtId = UUID.randomUUID() - - val innkommendeMelding = - Mock.innkommendeMelding( - EventName.SELVBESTEMT_IM_LAGRET, - Mock.inntektsmelding, - ) - - coEvery { - mockDokArkivKlient.opprettOgFerdigstillJournalpost(any(), any(), any(), any(), any(), any(), any()) - } returns Mock.opprettOgFerdigstillResponse(journalpostId) - - testRapid.sendJson( - innkommendeMelding - .toMap(Key.SELVBESTEMT_INNTEKTSMELDING) - .plus(Key.SELVBESTEMT_ID to selvbestemtId.toJson()) - .plus(Key.BEHOV to BehovType.JOURNALFOER.toJson()), - ) - - testRapid.firstMessage().toMap() shouldContainExactly - mapOf( - Key.EVENT_NAME to EventName.INNTEKTSMELDING_JOURNALFOERT.toJson(), - Key.UUID to innkommendeMelding.transaksjonId.toJson(), - Key.JOURNALPOST_ID to journalpostId.toJson(), - Key.INNTEKTSMELDING to Mock.inntektsmelding.toJson(Inntektsmelding.serializer()), - ) - - coVerifySequence { - mockDokArkivKlient.opprettOgFerdigstillJournalpost( - tittel = "Inntektsmelding", - gjelderPerson = GjelderPerson(Mock.inntektsmelding.sykmeldt.fnr.verdi), - avsender = - KlientAvsender.Organisasjon( - orgnr = Mock.inntektsmelding.avsender.orgnr.verdi, - navn = Mock.inntektsmelding.avsender.orgNavn, - ), - datoMottatt = LocalDate.now(), - dokumenter = - withArg { - it shouldHaveSize 1 - it.first().dokumentVarianter.map(DokumentVariant::filtype) shouldContainExactly listOf("XML", "PDFA") - }, - eksternReferanseId = "ARI-${innkommendeMelding.transaksjonId}", - callId = "callId_${innkommendeMelding.transaksjonId}", - ) - } - } - test("håndterer klientfeil") { coEvery { mockDokArkivKlient.opprettOgFerdigstillJournalpost(any(), any(), any(), any(), any(), any(), any()) @@ -214,11 +164,7 @@ class JournalfoerImRiverTest : event = innkommendeMelding.eventName, transaksjonId = innkommendeMelding.transaksjonId, forespoerselId = null, - utloesendeMelding = - innkommendeJsonMap - .plus( - Key.BEHOV to BehovType.JOURNALFOER.toJson(), - ).toJson(), + utloesendeMelding = innkommendeJsonMap.toJson(), ) testRapid.sendJson(innkommendeJsonMap)