From 338f7205446a07a72c2df2e822e3109efab48f53 Mon Sep 17 00:00:00 2001 From: Jacob Meidell Date: Mon, 4 Sep 2023 10:20:57 +0200 Subject: [PATCH] Refactoring, resctructiring, remodeling --- build.gradle.kts | 2 +- .../start/innlesning/Innlesing.kt | 28 -- .../omsorgsopptjening/start/innlesning/Mdc.kt | 8 +- .../barnetrygd/Barnetrygdmelding.kt | 33 -- .../BarnetrygdmottakerKafkaListener.kt | 119 ------- .../barnetrygd/domain/BarnetrygdInnlesing.kt | 84 +++++ .../{ => domain}/Barnetrygdmottaker.kt | 2 +- .../domain/BarnetrygdmottakerMelding.kt | 25 ++ .../BarnetrygdmottakerMessageHandler.kt | 98 ++++++ .../BarnetrygdmottakerProcessingThread.kt | 6 +- .../BarnetrygdmottakerService.kt} | 90 +++-- .../{ => external}/BarnetrygdClient.kt | 102 +----- .../{ => external}/BarnetrygdTokenProvider.kt | 2 +- .../BestillBarnetrygdResponseHandler.kt | 30 ++ .../external/HentBarnetrygdDomainMapper.kt | 24 ++ .../external/HentBarnetrygdResponseHandler.kt | 101 ++++++ .../BarnetrygdmottakerKafkaErrorHandler.kt} | 36 +- .../kafka/BarnetrygdmottakerKafkaListener.kt | 91 ++++++ ...ottakerKafkaListenerRetryBackoffConfig.kt} | 4 +- .../kafka/BarnetrygdmottakerKafkaMelding.kt | 15 + .../BarnetrygdmottakerKafkaTopic.kt} | 4 +- .../BarnetrygdtmottakerKafkaListenerConfig.kt | 44 +++ .../BarnetrygdInnlesingRepository.kt} | 36 +- .../BarnetrygdmottakerRepository.kt | 23 +- .../web/BarnetrygdWebApi.kt} | 29 +- .../start/innlesning/config/KafkaConfig.kt | 24 +- .../InnlesingCascadingDeleteTest.kt | 42 --- .../barnetrygd/BarnetrygdClientTest.kt | 255 --------------- .../BarnetrygdmottakerRepositoryTest.kt | 82 ----- .../innlesning/barnetrygd/EndToEndTest.kt | 107 ++++++ .../barnetrygd/InnlesingInvalideringTest.kt | 81 ----- .../barnetrygd/KafkaIntegrationTest.kt | 107 ------ .../barnetrygd/SpringContextTest.kt | 31 +- .../start/innlesning/barnetrygd/StatusTest.kt | 1 + .../BarnetrygdmottakerMessageHandlerTest.kt | 115 +++++++ .../BarnetrygdmottakerServiceTest.kt} | 102 +++--- .../external/BarnetrygdClientTest.kt | 188 +++++++++++ .../barnetrygd/external/WiremockScenario.kt | 160 +++++++++ .../BarnetrygdmottakerKafkaListenerTest.kt | 308 ++++++++++++++++++ .../BarnetrygdInnlesingRepositoryTest.kt | 65 ++++ .../BarnetrygdmottakerRepositoryTest.kt | 140 ++++++++ 41 files changed, 1817 insertions(+), 1027 deletions(-) delete mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Innlesing.kt delete mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/Barnetrygdmelding.kt delete mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerKafkaListener.kt create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdInnlesing.kt rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/{ => domain}/Barnetrygdmottaker.kt (99%) create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMelding.kt create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandler.kt rename "src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerProsesseringsTr\303\245d.kt" => src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerProcessingThread.kt (88%) rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/{BarnetrygdService.kt => domain/BarnetrygdmottakerService.kt} (62%) rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/{ => external}/BarnetrygdClient.kt (55%) rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/{ => external}/BarnetrygdTokenProvider.kt (97%) create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BestillBarnetrygdResponseHandler.kt create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdDomainMapper.kt create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdResponseHandler.kt rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/{config/KafkaErrorHandler.kt => barnetrygd/kafka/BarnetrygdmottakerKafkaErrorHandler.kt} (55%) create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListener.kt rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/{config/BackoffConfig.kt => barnetrygd/kafka/BarnetrygdmottakerKafkaListenerRetryBackoffConfig.kt} (85%) create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaMelding.kt rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/{BarnetrygdTopic.kt => kafka/BarnetrygdmottakerKafkaTopic.kt} (78%) create mode 100644 src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdtmottakerKafkaListenerConfig.kt rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/{InnlesingRepository.kt => barnetrygd/repository/BarnetrygdInnlesingRepository.kt} (62%) rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/{ => repository}/BarnetrygdmottakerRepository.kt (82%) rename src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/{WebApi.kt => barnetrygd/web/BarnetrygdWebApi.kt} (52%) delete mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/InnlesingCascadingDeleteTest.kt delete mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdClientTest.kt delete mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerRepositoryTest.kt create mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/EndToEndTest.kt delete mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/InnlesingInvalideringTest.kt delete mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/KafkaIntegrationTest.kt create mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandlerTest.kt rename src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/{RetryTest.kt => domain/BarnetrygdmottakerServiceTest.kt} (70%) create mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdClientTest.kt create mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/WiremockScenario.kt create mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListenerTest.kt create mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdInnlesingRepositoryTest.kt create mode 100644 src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdmottakerRepositoryTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index c8cb8f7..6b9e143 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -67,7 +67,7 @@ dependencies { testImplementation("no.nav.security:token-validation-spring-test:$navTokenSupportVersion") testImplementation("org.testcontainers:postgresql:$testcontainersVersion") testImplementation("org.springframework.kafka:spring-kafka-test:$springKafkaTestVersion") - + testImplementation("org.mockito.kotlin:mockito-kotlin:5.1.0") } tasks.test { diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Innlesing.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Innlesing.kt deleted file mode 100644 index 5680370..0000000 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Innlesing.kt +++ /dev/null @@ -1,28 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning - -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.KafkaMelding -import java.time.Instant - -data class Innlesing( - val id: InnlesingId, - val år: Int, - val forespurtTidspunkt: Instant? = null, - val startTidspunkt: Instant? = null, - val ferdigTidspunkt: Instant? = null -) { - fun kanStartes() = erBestilt() && !erStartet() && !erFerdig() - - fun kanMottaData() = erStartet() && !erFerdig() - - fun kanAvsluttes() = kanMottaData() - private fun erBestilt() = forespurtTidspunkt != null - private fun erStartet() = startTidspunkt != null - private fun erFerdig() = ferdigTidspunkt != null -} - -sealed class InnlesingException : RuntimeException() { - data class EksistererIkke(val id: String) : RuntimeException() - data class UgyldigTistand(val kafkaMelding: KafkaMelding.Type, val innlesing: Innlesing) : - RuntimeException("Ugyldig tilstand for innlesing: $innlesing og kafkamelding: $kafkaMelding") -} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Mdc.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Mdc.kt index f700d8b..937ad80 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Mdc.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/Mdc.kt @@ -13,11 +13,11 @@ object Mdc { return MDC.putCloseable(InnlesingId.identifier, innlesingId.toString()).use { block(innlesingId) } } - fun getCorrelationId(): String { - return MDC.get(CorrelationId.identifier).toString() + fun getCorrelationId(): CorrelationId { + return CorrelationId.fromString(MDC.get(CorrelationId.identifier).toString()) } - fun getInnlesingId(): String? { - return MDC.get(InnlesingId.identifier).toString() + fun getInnlesingId(): InnlesingId { + return InnlesingId.fromString(MDC.get(InnlesingId.identifier).toString()) } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/Barnetrygdmelding.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/Barnetrygdmelding.kt deleted file mode 100644 index b64a481..0000000 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/Barnetrygdmelding.kt +++ /dev/null @@ -1,33 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd - -import com.fasterxml.jackson.annotation.JsonIgnore -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.periode.Periode -import java.time.YearMonth - -/* - * Finnes barna til personen det spørres på i flere fagsaker vil det være flere elementer i listen - * Ett element pr. fagsak barnet er knyttet til. - * Kan være andre personer enn mor og far. - */ -data class Barnetrygdmelding( - val ident: String, - val list: List -){ - data class Sak( - val fagsakId: String, - val fagsakEiersIdent: String, - val barnetrygdPerioder: List, - ) - - data class Periode( - val personIdent: String, - val delingsprosentYtelse: Int, - val ytelseTypeEkstern: String?, //TODO returner json i tester, ikke seialisert objekt - val utbetaltPerMnd: Int, - val stønadFom: YearMonth, - val stønadTom: YearMonth - ){ - @JsonIgnore - val periode: no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.periode.Periode = Periode(stønadFom, stønadTom) - } -} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerKafkaListener.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerKafkaListener.kt deleted file mode 100644 index c6fed4f..0000000 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerKafkaListener.kt +++ /dev/null @@ -1,119 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd - -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.deserialize -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingException -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingRepository -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Mdc -import org.apache.kafka.clients.consumer.ConsumerRecord -import org.slf4j.LoggerFactory -import org.springframework.context.annotation.Profile -import org.springframework.kafka.annotation.KafkaListener -import org.springframework.kafka.support.Acknowledgment -import org.springframework.stereotype.Component -import java.util.UUID - - -@Component -@Profile("dev-gcp", "prod-gcp", "kafkaIntegrationTest") -class BarnetrygdmottakerKafkaListener( - private val innlesingRepository: InnlesingRepository, - private val barnetrygdmottakerRepository: BarnetrygdmottakerRepository -) { - companion object { - private val log = LoggerFactory.getLogger(this::class.java) - } - - @KafkaListener( - containerFactory = "listener", - topics = [BarnetrygdTopic.NAME], - groupId = "omsorgsopptjening-start-innlesning" - ) - fun poll( - consumerRecord: ConsumerRecord, - acknowledgment: Acknowledgment - ) { - val kafkaMelding = try { - deserialize(consumerRecord.value()) - } catch (ex: Throwable) { - throw UkjentKafkaMeldingException(consumerRecord, ex) - } - - Mdc.scopedMdc(CorrelationId.generate()) { correlationId -> - Mdc.scopedMdc(InnlesingId.fromString(kafkaMelding.requestId.toString())) { innlesingId -> - try { - innlesingRepository.finn(innlesingId.toString()) - ?.also { innlesing -> - when (kafkaMelding.meldingstype) { - KafkaMelding.Type.START -> { - if(!innlesing.kanStartes()) throw InnlesingException.UgyldigTistand(kafkaMelding.meldingstype, innlesing) - log.info("Starter ny innlesing, id: $innlesingId") - innlesingRepository.start(innlesingId.toString()) - } - - KafkaMelding.Type.DATA -> { - if(!innlesing.kanMottaData()) throw InnlesingException.UgyldigTistand(kafkaMelding.meldingstype, innlesing) - log.info("Mottatt melding om barnetrygdmottaker") - barnetrygdmottakerRepository.save( - kafkaMelding.toBarnetrygdmottaker( - correlationId = correlationId, - innlesingId = innlesingId - ) - ) - log.info("Melding prosessert") - } - - KafkaMelding.Type.SLUTT -> { - if(!innlesing.kanAvsluttes()) throw InnlesingException.UgyldigTistand(kafkaMelding.meldingstype, innlesing) - log.info("Fullført innlesing, id: $innlesingId") - innlesingRepository.fullført(innlesingId.toString()) - } - } - acknowledgment.acknowledge() - - } ?: throw InnlesingException.EksistererIkke(innlesingId.toString()) - } catch (ex: InnlesingException.EksistererIkke) { - //forventet dersom vi har invalidert innlesingen, hopp over - log.info("Innlesing med id:${ex.id} eksisterer ikke i databasen - innlesingen er ikke bestilt eller invalidert grunnet feil, hopper over.") - acknowledgment.acknowledge() - } catch (ex: Throwable) { - //catch all for resterende feil, sørg for invalidering etter retries - throw InvalidateOnExceptionWrapper( - innlesingId = innlesingId.toUUID(), - ex = ex - ) - } - } - } - } -} - - -class InvalidateOnExceptionWrapper(val innlesingId: UUID, ex: Throwable) : RuntimeException(ex) -class UkjentKafkaMeldingException(val consumerRecord: ConsumerRecord, ex: Throwable) : - RuntimeException(ex) - -data class KafkaMelding( - val meldingstype: Type, - val requestId: UUID, - val personident: String? -) { - fun toBarnetrygdmottaker( - correlationId: CorrelationId, - innlesingId: InnlesingId, - ): Barnetrygdmottaker { - return Barnetrygdmottaker( - ident = personident!!, - correlationId = correlationId, - innlesingId = innlesingId - ) - } - - - enum class Type { - START, - DATA, - SLUTT; - } -} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdInnlesing.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdInnlesing.kt new file mode 100644 index 0000000..2d3725a --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdInnlesing.kt @@ -0,0 +1,84 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import java.time.Instant + +sealed class BarnetrygdInnlesing { + abstract val id: InnlesingId + abstract val år: Int + abstract val forespurtTidspunkt: Instant + open val startTidspunkt: Instant? = null + open val ferdigTidspunkt: Instant? = null + + open fun startet(): Startet = throw UgyldigTilstand( + fra = this::class.java.simpleName, + til = Startet::class.java.simpleName + ) + + open fun mottaData(): Startet = throw UgyldigTilstand( + fra = this::class.java.simpleName, + til = Startet::class.java.simpleName + ) + + open fun ferdig(): Ferdig = throw UgyldigTilstand( + fra = this::class.java.simpleName, + til = Ferdig::class.java.simpleName + ) + + data class Bestilt( + override val id: InnlesingId, + override val år: Int, + override val forespurtTidspunkt: Instant + ) : BarnetrygdInnlesing() { + override fun startet(): Startet { + return Startet(id, år, forespurtTidspunkt, Instant.now()) + } + } + + data class Startet( + override val id: InnlesingId, + override val år: Int, + override val forespurtTidspunkt: Instant, + override val startTidspunkt: Instant, + ) : BarnetrygdInnlesing() { + + override fun mottaData(): Startet { + return this + } + + override fun ferdig(): Ferdig { + return Ferdig(id, år, forespurtTidspunkt, startTidspunkt, Instant.now()) + } + } + + data class Ferdig( + override val id: InnlesingId, + override val år: Int, + override val forespurtTidspunkt: Instant, + override val startTidspunkt: Instant, + override val ferdigTidspunkt: Instant + ) : BarnetrygdInnlesing() + + companion object Factory { + fun of( + id: InnlesingId, + år: Int, + forespurtTidspunkt: Instant, + startTidspunkt: Instant?, + ferdigTidspunkt: Instant? + ): BarnetrygdInnlesing { + return if (ferdigTidspunkt != null && startTidspunkt != null) { + Ferdig(id, år, forespurtTidspunkt, startTidspunkt, ferdigTidspunkt) + } else if (ferdigTidspunkt == null && startTidspunkt != null) { + Startet(id, år, forespurtTidspunkt, startTidspunkt) + } else { + Bestilt(id, år, forespurtTidspunkt) + } + } + } + + data class UgyldigTilstand( + val fra: String, + val til: String + ) : RuntimeException() +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/Barnetrygdmottaker.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/Barnetrygdmottaker.kt similarity index 99% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/Barnetrygdmottaker.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/Barnetrygdmottaker.kt index df3f0ef..50bd9e4 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/Barnetrygdmottaker.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/Barnetrygdmottaker.kt @@ -1,4 +1,4 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.annotation.JsonTypeName diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMelding.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMelding.kt new file mode 100644 index 0000000..3e4f251 --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMelding.kt @@ -0,0 +1,25 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId + +sealed class BarnetrygdmottakerMelding { + abstract val correlationId: CorrelationId + abstract val innlesingId: InnlesingId + + data class Start( + override val correlationId: CorrelationId, + override val innlesingId: InnlesingId + ) : BarnetrygdmottakerMelding() + + data class Data( + val personIdent: String, + override val correlationId: CorrelationId, + override val innlesingId: InnlesingId + ) : BarnetrygdmottakerMelding() + + data class Slutt( + override val correlationId: CorrelationId, + override val innlesingId: InnlesingId + ) : BarnetrygdmottakerMelding() +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandler.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandler.kt new file mode 100644 index 0000000..edef3b2 --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandler.kt @@ -0,0 +1,98 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdInnlesingRepository +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdmottakerRepository +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Component + +@Component +class BarnetrygdmottakerMessageHandler( + private val innlesingRepository: BarnetrygdInnlesingRepository, + private val barnetrygdmottakerRepository: BarnetrygdmottakerRepository +) { + companion object { + private val log = LoggerFactory.getLogger(this::class.java) + } + + @Throws( + BarnetrygdInnlesingException.EksistererIkke::class, BarnetrygdInnlesingException.UgyldigTistand::class + ) + fun handle(melding: BarnetrygdmottakerMelding) { + finnInnlesing(melding.innlesingId) + .also { it.håndterMelding(melding) } + } + + + @Throws(BarnetrygdInnlesingException.EksistererIkke::class) + private fun finnInnlesing(innlesingId: InnlesingId): BarnetrygdInnlesing { + return innlesingRepository.finn(innlesingId.toString()) + ?: throw BarnetrygdInnlesingException.EksistererIkke(innlesingId.toString()) + } + + @Throws(BarnetrygdInnlesingException.UgyldigTistand::class) + private fun BarnetrygdInnlesing.håndterMelding(melding: BarnetrygdmottakerMelding) { + when (melding) { + is BarnetrygdmottakerMelding.Start -> håndterStartmelding(melding) + is BarnetrygdmottakerMelding.Data -> håndterDatamelding(melding) + is BarnetrygdmottakerMelding.Slutt -> håndterSluttmelding(melding) + } + } + + private fun BarnetrygdInnlesing.håndterStartmelding(melding: BarnetrygdmottakerMelding.Start) { + try { + log.info("Starter ny innlesing, id: ${this.id}") + innlesingRepository.start(startet()) + } catch (ex: BarnetrygdInnlesing.UgyldigTilstand) { + throw BarnetrygdInnlesingException.UgyldigTistand(this.id.toString(), melding::class.java.simpleName) + } + } + + private fun BarnetrygdInnlesing.håndterDatamelding(melding: BarnetrygdmottakerMelding.Data) { + try { + log.info("Mottatt melding om barnetrygdmottaker") + mottaData().also { + barnetrygdmottakerRepository.insert( + toBarnetrygdmottaker( + personident = melding.personIdent, + correlationId = melding.correlationId, + innlesingId = melding.innlesingId + ) + ) + log.info("Melding prosessert") + } + } catch (ex: BarnetrygdInnlesing.UgyldigTilstand) { + throw BarnetrygdInnlesingException.UgyldigTistand(this.id.toString(), melding::class.java.simpleName) + } + } + + private fun BarnetrygdInnlesing.håndterSluttmelding(melding: BarnetrygdmottakerMelding.Slutt) { + try { + log.info("Fullført innlesing, id: ${this.id}") + innlesingRepository.fullført(ferdig()) + } catch (ex: BarnetrygdInnlesing.UgyldigTilstand) { + throw BarnetrygdInnlesingException.UgyldigTistand(this.id.toString(), melding::class.java.simpleName) + } + } + + private fun toBarnetrygdmottaker( + personident: String, + correlationId: CorrelationId, + innlesingId: InnlesingId, + ): Barnetrygdmottaker { + return Barnetrygdmottaker( + ident = personident, + correlationId = correlationId, + innlesingId = innlesingId + ) + } +} + +sealed class BarnetrygdInnlesingException : RuntimeException() { + data class EksistererIkke(val id: String) : RuntimeException() + data class UgyldigTistand( + val id: String, + val meldingstype: String + ) : RuntimeException() +} \ No newline at end of file diff --git "a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerProsesseringsTr\303\245d.kt" b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerProcessingThread.kt similarity index 88% rename from "src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerProsesseringsTr\303\245d.kt" rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerProcessingThread.kt index 2c72374..9905840 100644 --- "a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerProsesseringsTr\303\245d.kt" +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerProcessingThread.kt @@ -1,4 +1,4 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain import jakarta.annotation.PostConstruct import org.slf4j.LoggerFactory @@ -7,8 +7,8 @@ import org.springframework.stereotype.Component @Component @Profile("dev-gcp", "prod-gcp", "kafkaIntegrationTest") -class BarnetrygdmottakerProsesseringsTråd( - private val service: BarnetrygdService +class BarnetrygdmottakerProcessingThread( + private val service: BarnetrygdmottakerService ) : Runnable { companion object { diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdService.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerService.kt similarity index 62% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdService.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerService.kt index 90a736e..ff75dca 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdService.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerService.kt @@ -1,4 +1,4 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.RådataFraKilde import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.Topics @@ -7,6 +7,10 @@ import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.messages. import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.messages.domene.Omsorgstype import no.nav.pensjon.opptjening.omsorgsopptjening.felles.serialize import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Mdc +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external.BarnetrygdClient +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external.BestillBarnetrygdmottakereResponse +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external.HentBarnetrygdResponse +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdmottakerRepository import org.apache.kafka.clients.producer.ProducerRecord import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired @@ -17,7 +21,7 @@ import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Transactional @Service -class BarnetrygdService( +class BarnetrygdmottakerService( private val client: BarnetrygdClient, private val repo: BarnetrygdmottakerRepository, private val kafkaProducer: KafkaTemplate, @@ -26,8 +30,8 @@ class BarnetrygdService( private val log = LoggerFactory.getLogger(this::class.java) } - fun bestillPersonerMedBarnetrygd(ar: Int): HentBarnetygdmottakereResponse { - return client.bestillPersonerMedBarnetrygd(ar) + fun bestillPersonerMedBarnetrygd(ar: Int): BestillBarnetrygdmottakereResponse { + return client.bestillBarnetrygdmottakere(ar) } @Transactional(rollbackFor = [Throwable::class]) @@ -42,35 +46,11 @@ class BarnetrygdService( client.hentBarnetrygd( ident = barnetrygdmottaker.ident, ar = barnetrygdmottaker.år!!, - ).let { response -> - when (response) { - is HentBarnetrygdResponse.Feil -> { - """Feil ved henting av detaljer om barnetrygd, httpStatus: ${response.status}, body: ${response.body}""".let { melding -> - log.error(melding) - barnetrygdmottaker.retry(melding) - .also { repo.updateStatus(it) } - } - } - - is HentBarnetrygdResponse.Ok -> { - log.info("Publiserer detaljer til topic:${Topics.Omsorgsopptjening.NAME}") - barnetrygdmottaker.ferdig() - .also { - kafkaProducer.send( - createKafkaMessage( - barnetrygdmottaker = it, - saker = response.barnetrygdsaker - ) - ).get() - repo.updateStatus(it) - log.info("Prosessering fullført") - } - - } - } + ).let { + barnetrygdmottaker.handle(it) } } catch (exception: Throwable) { - log.error("Exception caught while processing id: ${barnetrygdmottaker.id}, exeption:$exception") + log.warn("Exception caught while processing id: ${barnetrygdmottaker.id}, exeption:$exception") statusoppdatering.markerForRetry(barnetrygdmottaker, exception) throw exception } @@ -79,9 +59,39 @@ class BarnetrygdService( } } + private fun Barnetrygdmottaker.handle(response: HentBarnetrygdResponse): Barnetrygdmottaker { + return when (response) { + is HentBarnetrygdResponse.Feil -> { + """Feil ved henting av detaljer om barnetrygd, httpStatus: ${response.status}, body: ${response.body}""".let { melding -> + log.warn(melding) + retry(melding) + .also { repo.updateStatus(it) } + } + } + + is HentBarnetrygdResponse.Ok -> { + log.info("Publiserer detaljer til topic:${Topics.Omsorgsopptjening.NAME}") + ferdig() + .also { + repo.updateStatus(it) + kafkaProducer.send( + createKafkaMessage( + barnetrygdmottaker = it, + saker = response.barnetrygdsaker, + rådataFraKilde = response.rådataFraKilde, + ) + ).get() + log.info("Prosessering fullført") + } + + } + } + } + private fun createKafkaMessage( barnetrygdmottaker: Barnetrygdmottaker, - saker: List + saker: List, + rådataFraKilde: RådataFraKilde ): ProducerRecord { return ProducerRecord( Topics.Omsorgsopptjening.NAME, @@ -96,20 +106,8 @@ class BarnetrygdService( omsorgsyter = barnetrygdmottaker.ident, omsorgstype = Omsorgstype.BARNETRYGD, kilde = Kilde.BARNETRYGD, - saker = saker.map { barnetrygdSak -> - OmsorgsgrunnlagMelding.Sak( - omsorgsyter = barnetrygdSak.fagsakEiersIdent, - vedtaksperioder = barnetrygdSak.barnetrygdPerioder.map { - OmsorgsgrunnlagMelding.VedtakPeriode( - fom = it.stønadFom, - tom = it.stønadTom, - prosent = it.delingsprosentYtelse, - omsorgsmottaker = it.personIdent - ) - } - ) - }, - rådata = RådataFraKilde(serialize(saker)), + saker = saker, + rådata = rådataFraKilde, innlesingId = barnetrygdmottaker.innlesingId, correlationId = barnetrygdmottaker.correlationId, ) diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdClient.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdClient.kt similarity index 55% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdClient.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdClient.kt index 73332db..2997047 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdClient.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdClient.kt @@ -1,8 +1,7 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.deserialize import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Mdc import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value @@ -10,7 +9,6 @@ import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.HttpStatusCode import org.springframework.http.MediaType -import org.springframework.http.ResponseEntity import org.springframework.stereotype.Component import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient @@ -40,9 +38,9 @@ class BarnetrygdClient( * Signaliserer til barnetrygd-systemet at de skal sende oss identen til alle mottakere av barnetrygd i året [ar] * og fremover. Barnetrydmottakerne publiseres til topic [no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.Topics.BARNETRYGDMOTTAKER]. */ - fun bestillPersonerMedBarnetrygd( + fun bestillBarnetrygdmottakere( ar: Int - ): HentBarnetygdmottakereResponse { + ): BestillBarnetrygdmottakereResponse { log.info("Initiating sending of barnetrygdmottakere") return webClient .get() @@ -53,33 +51,12 @@ class BarnetrygdClient( .retrieve() .onStatus(not202()) { Mono.empty() } .toEntity() - .block()?.let { handleBestillResponse(it, ar) } ?: HentBarnetygdmottakereResponse.Feil( + .block()?.let { BestillBarnetrygdResponseHandler.handle(it, ar) } ?: BestillBarnetrygdmottakereResponse.Feil( null, - null + "Response var null" ) } - private fun not202(): Predicate = Predicate.not(Predicate.isEqual(HttpStatus.ACCEPTED)) - - - private fun handleBestillResponse(it: ResponseEntity, ar: Int): HentBarnetygdmottakereResponse { - return when (it.statusCode) { - HttpStatus.ACCEPTED -> { - HentBarnetygdmottakereResponse.Ok( - innlesingId = InnlesingId.fromString(it.body.toString()), - år = ar - ) - } - - else -> { - HentBarnetygdmottakereResponse.Feil( - status = it.statusCode.value(), - body = it.body.toString() - ) - } - } - } - /** * Hent detaljer om barnetrygdsaken identifisert av [ident], samt det tidligste året vi ønsker å hente data for, * angitt av [ar]. @@ -96,8 +73,8 @@ class BarnetrygdClient( return webClient .post() .uri("/api/ekstern/pensjon/hent-barnetrygd") - .header(CorrelationId.identifier, Mdc.getCorrelationId()) - .header(InnlesingId.identifier, Mdc.getInnlesingId()) + .header(CorrelationId.identifier, Mdc.getCorrelationId().toString()) + .header(InnlesingId.identifier, Mdc.getInnlesingId().toString()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .header(HttpHeaders.AUTHORIZATION, "Bearer " + tokenProvider.getToken()) @@ -112,68 +89,15 @@ class BarnetrygdClient( .retrieve() .onStatus(not200()) { Mono.empty() } .toEntity() - .block()?.let { handleHentBarnetrygd(it) } ?: HentBarnetrygdResponse.Feil( + .block()?.let { HentBarnetrygdResponseHandler.handle(it) } ?: HentBarnetrygdResponse.Feil( null, - null + "Response var null" ) } - private fun not200(): Predicate = Predicate.not(Predicate.isEqual(HttpStatus.OK)) - - private fun handleHentBarnetrygd(it: ResponseEntity): HentBarnetrygdResponse { - return when (val status = it.statusCode) { - HttpStatus.OK -> { - when { - it.body == null -> { - HentBarnetrygdResponse.Feil( - status = it.statusCode.value(), - body = null - ) - } + data class HentBarnetrygdRequest(val ident: String, val fraDato: String) - else -> { - deserialize(it.body!!).let { - when { - it.fagsaker.isEmpty() -> { - HentBarnetrygdResponse.Feil( - status = status.value(), - body = "Liste med barnetrygdsaker er tom" - ) - } - - else -> { - HentBarnetrygdResponse.Ok( - barnetrygdsaker = it.fagsaker - ) - } - } - } - } - } - } - - else -> { - HentBarnetrygdResponse.Feil( - status = it.statusCode.value(), - body = it.body.toString() - ) - } - } - } -} - -sealed class HentBarnetygdmottakereResponse { - data class Ok(val innlesingId: InnlesingId, val år: Int) : HentBarnetygdmottakereResponse() - data class Feil(val status: Int?, val body: String?) : HentBarnetygdmottakereResponse() -} - -data class FagsakWrapper( - val fagsaker: List -) - -sealed class HentBarnetrygdResponse { - data class Ok(val barnetrygdsaker: List) : HentBarnetrygdResponse() - data class Feil(val status: Int?, val body: String?) : HentBarnetrygdResponse() -} + private fun not200(): Predicate = Predicate.not(Predicate.isEqual(HttpStatus.OK)) + private fun not202(): Predicate = Predicate.not(Predicate.isEqual(HttpStatus.ACCEPTED)) -private data class HentBarnetrygdRequest(val ident: String, val fraDato: String) \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdTokenProvider.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdTokenProvider.kt similarity index 97% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdTokenProvider.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdTokenProvider.kt index e1ff0e9..ee59c11 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdTokenProvider.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdTokenProvider.kt @@ -1,4 +1,4 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.config.AzureAdTokenClientConfig import org.springframework.beans.factory.annotation.Value diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BestillBarnetrygdResponseHandler.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BestillBarnetrygdResponseHandler.kt new file mode 100644 index 0000000..380624e --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BestillBarnetrygdResponseHandler.kt @@ -0,0 +1,30 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity + +object BestillBarnetrygdResponseHandler { + fun handle(response: ResponseEntity, år: Int): BestillBarnetrygdmottakereResponse { + return when (response.statusCode) { + HttpStatus.ACCEPTED -> { + BestillBarnetrygdmottakereResponse.Ok( + innlesingId = InnlesingId.fromString(response.body.toString()), + år = år + ) + } + + else -> { + BestillBarnetrygdmottakereResponse.Feil( + status = response.statusCode.value(), + melding = response.body.toString() + ) + } + } + } +} + +sealed class BestillBarnetrygdmottakereResponse { + data class Ok(val innlesingId: InnlesingId, val år: Int) : BestillBarnetrygdmottakereResponse() + data class Feil(val status: Int?, val melding: String?) : BestillBarnetrygdmottakereResponse() +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdDomainMapper.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdDomainMapper.kt new file mode 100644 index 0000000..3a3ef85 --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdDomainMapper.kt @@ -0,0 +1,24 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.messages.domene.OmsorgsgrunnlagMelding + +object HentBarnetrygdDomainMapper { + + fun map(external: List): List { + return external.map { map(it) } + } + + fun map(external: Sak): OmsorgsgrunnlagMelding.Sak { + return OmsorgsgrunnlagMelding.Sak( + omsorgsyter = external.fagsakEiersIdent, + vedtaksperioder = external.barnetrygdPerioder.map { + OmsorgsgrunnlagMelding.VedtakPeriode( + fom = it.stønadFom, + tom = it.stønadTom, + prosent = it.delingsprosentYtelse, + omsorgsmottaker = it.personIdent + ) + } + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdResponseHandler.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdResponseHandler.kt new file mode 100644 index 0000000..24f8c9d --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/HentBarnetrygdResponseHandler.kt @@ -0,0 +1,101 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external + +import com.fasterxml.jackson.annotation.JsonIgnore +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.deserialize +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.RådataFraKilde +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.messages.domene.OmsorgsgrunnlagMelding +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import java.time.YearMonth + +object HentBarnetrygdResponseHandler { + fun handle(response: ResponseEntity): HentBarnetrygdResponse { + /** + * Finnes barna til personen det spørres på i flere fagsaker vil det være flere elementer i listen + * Ett element pr. fagsak barnet er knyttet til. + * Kan være andre personer enn mor og far. + */ + return when (val status = response.statusCode) { + HttpStatus.OK -> { + when { + response.body == null -> { + HentBarnetrygdResponse.Feil( + status = response.statusCode.value(), + body = null + ) + } + + else -> { + deserialize(response.body!!).let { wrapper -> + when { + wrapper.fagsaker.isEmpty() -> { + HentBarnetrygdResponse.Feil( + status = status.value(), + body = "Liste med barnetrygdsaker er tom" + ) + } + + wrapper.fagsaker.any { it.barnetrygdPerioder.isEmpty() } -> { + HentBarnetrygdResponse.Feil( + status = status.value(), + body = "En eller flere av barnetrygdsakene mangler perioder" + ) + } + + else -> { + HentBarnetrygdResponse.Ok( + barnetrygdsaker = HentBarnetrygdDomainMapper.map(wrapper.fagsaker), + rådataFraKilde = RådataFraKilde(response.body!!) + ) + } + } + } + } + } + } + + else -> { + HentBarnetrygdResponse.Feil( + status = response.statusCode.value(), + body = response.body.toString() + ) + } + } + } +} + +sealed class HentBarnetrygdResponse { + data class Ok( + val barnetrygdsaker: List, + val rådataFraKilde: RådataFraKilde + ) : HentBarnetrygdResponse() + + data class Feil( + val status: Int?, + val body: String? + ) : HentBarnetrygdResponse() +} + + +private data class FagsakListeWrapper( + val fagsaker: List +) + +data class Sak( + val fagsakId: String, + val fagsakEiersIdent: String, + val barnetrygdPerioder: List, +) + +data class Periode( + val personIdent: String, + val delingsprosentYtelse: Int, + val ytelseTypeEkstern: String?, + val utbetaltPerMnd: Int, + val stønadFom: YearMonth, + val stønadTom: YearMonth +) { + @JsonIgnore + val periode: no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.periode.Periode = + no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.periode.Periode(stønadFom, stønadTom) +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/KafkaErrorHandler.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaErrorHandler.kt similarity index 55% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/KafkaErrorHandler.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaErrorHandler.kt index 476ebc9..30c81de 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/KafkaErrorHandler.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaErrorHandler.kt @@ -1,8 +1,7 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.config +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingException -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingRepository -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.InvalidateOnExceptionWrapper +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesingException +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdInnlesingRepository import org.apache.kafka.clients.consumer.ConsumerRecord import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -13,22 +12,24 @@ import org.springframework.util.backoff.BackOff import java.util.UUID @Component -class KafkaErrorHandler( - innlesingRepository: InnlesingRepository, - backOff: BackOff +class BarnetrygdmottakerKafkaErrorHandler( + backOff: BackOff, + retryListener: InnlesingInvalidatingRetryListener ) : DefaultErrorHandler(backOff) { init { - this.setRetryListeners(InnlesingInvalidatingRetryListener(innlesingRepository)) - this.addNotRetryableExceptions(InnlesingException.EksistererIkke::class.java) - this.addNotRetryableExceptions(InnlesingException.UgyldigTistand::class.java) + this.setRetryListeners(retryListener) + this.addNotRetryableExceptions(BarnetrygdInnlesingException.EksistererIkke::class.java) + this.addNotRetryableExceptions(BarnetrygdInnlesingException.UgyldigTistand::class.java) + this.addNotRetryableExceptions(KafkaMeldingDeserialiseringException::class.java) } } +@Component class InnlesingInvalidatingRetryListener( - private val innlesingRepository: InnlesingRepository + private val innlesingRepository: BarnetrygdInnlesingRepository ) : RetryListener { - private val invalidated: MutableList = mutableListOf() + private val invalidated: MutableList = mutableListOf() companion object { private val log: Logger = LoggerFactory.getLogger(this::class.java) @@ -41,11 +42,11 @@ class InnlesingInvalidatingRetryListener( ex.cause?.also { throwable -> when (throwable) { is InvalidateOnExceptionWrapper -> { - if (!invalidated.contains(throwable.innlesingId)) { - log.info("Invalidating (deleting all related data) innlesing with id: ${throwable.innlesingId} due to all records not being processed successfully.") - innlesingRepository.invalider(throwable.innlesingId) - .also { invalidated.add(throwable.innlesingId) } - log.info("Invalidated id: ${throwable.innlesingId}") + if (!invalidated.contains(throwable.id)) { + log.info("Invalidating (deleting all related data) innlesing with id: ${throwable.id} due to all records not being processed successfully.") + innlesingRepository.invalider(UUID.fromString(throwable.id)) + .also { invalidated.add(throwable.id) } + log.info("Invalidated id: ${throwable.id}") } } @@ -54,6 +55,5 @@ class InnlesingInvalidatingRetryListener( } } } - super.recovered(record, ex) } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListener.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListener.kt new file mode 100644 index 0000000..652b85b --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListener.kt @@ -0,0 +1,91 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.deserialize +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Mdc +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdmottakerMessageHandler +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdmottakerMelding +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesingException +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.slf4j.LoggerFactory +import org.springframework.context.annotation.Profile +import org.springframework.kafka.annotation.KafkaListener +import org.springframework.kafka.support.Acknowledgment +import org.springframework.stereotype.Component + +@Component +@Profile("dev-gcp", "prod-gcp", "kafkaIntegrationTest") +class BarnetrygdmottakerKafkaListener( + private val handler: BarnetrygdmottakerMessageHandler +) { + companion object { + private val log = LoggerFactory.getLogger(this::class.java) + } + + @KafkaListener( + containerFactory = "listener", + topics = [BarnetrygdmottakerKafkaTopic.NAME], + groupId = "omsorgsopptjening-start-innlesning" + ) + fun poll( + consumerRecord: ConsumerRecord, + acknowledgment: Acknowledgment + ) { + val kafkaMelding = try { + consumerRecord.deserialiser() + } catch (ex: KafkaMeldingDeserialiseringException) { + log.info("Klarte ikke å deserialisere til kjent meldingsformat. Ignorerer melding ${ex.consumerRecord}, exception: $ex") + throw ex + } + + Mdc.scopedMdc(CorrelationId.generate()) { correlationId -> + Mdc.scopedMdc(InnlesingId.fromString(kafkaMelding.requestId.toString())) { innlesingId -> + try { + handler.handle(consumerRecord.deserialiser().toDomain(correlationId, innlesingId)) + } catch (ex: BarnetrygdInnlesingException.EksistererIkke) { + log.info("Innlesing med id: ${ex.id} eksisterer ikke. Det mangler bestilling for innlesingen, eller innlesingen har blitt invalidert som følge av feil i overføring. Ignorerer påfølgende meldinger for denne bestillingen.") + throw ex + } catch (ex: BarnetrygdInnlesingException.UgyldigTistand) { + log.info("Ugyldig tilstandsendring av innlesing: ${ex.id} for meldingstype: ${ex.meldingstype}. Invaliderer innsending.") + throw InvalidateOnExceptionWrapper(ex.id, ex) + } catch (ex: Throwable) { + log.info("Ukjent feil ved prosessering av melding $kafkaMelding, exception: $ex. Invaliderer melding dersom problemet vedvarer etter retries.") + throw InvalidateOnExceptionWrapper(innlesingId.toString(), ex) + } + } + } + } + + private fun ConsumerRecord.deserialiser(): BarnetrygdmottakerKafkaMelding { + return try { + deserialize(this.value()) + } catch (ex: Throwable) { + throw KafkaMeldingDeserialiseringException(this, ex) + } + } + + private fun BarnetrygdmottakerKafkaMelding.toDomain( + correlationId: CorrelationId, + innlesingId: InnlesingId, + ): BarnetrygdmottakerMelding { + return when (meldingstype) { + BarnetrygdmottakerKafkaMelding.Type.START -> { + BarnetrygdmottakerMelding.Start(correlationId, innlesingId) + } + + BarnetrygdmottakerKafkaMelding.Type.DATA -> { + BarnetrygdmottakerMelding.Data(personident!!, correlationId, innlesingId) + } + + BarnetrygdmottakerKafkaMelding.Type.SLUTT -> { + BarnetrygdmottakerMelding.Slutt(correlationId, innlesingId) + } + } + } +} + + +class InvalidateOnExceptionWrapper(val id: String, ex: Throwable) : RuntimeException(ex) +class KafkaMeldingDeserialiseringException(val consumerRecord: ConsumerRecord, ex: Throwable) : + RuntimeException(ex) diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/BackoffConfig.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListenerRetryBackoffConfig.kt similarity index 85% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/BackoffConfig.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListenerRetryBackoffConfig.kt index fb3d0fa..d59b63d 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/BackoffConfig.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListenerRetryBackoffConfig.kt @@ -1,4 +1,4 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.config +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -8,7 +8,7 @@ import org.springframework.util.backoff.FixedBackOff @Configuration @Profile("dev-gcp", "prod-gcp") -class BackoffConfig { +class BarnetrygdmottakerKafkaListenerRetryBackoffConfig { @Bean fun backoff(): BackOff { return FixedBackOff(3000, 3) diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaMelding.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaMelding.kt new file mode 100644 index 0000000..850d972 --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaMelding.kt @@ -0,0 +1,15 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka + +import java.util.UUID + +data class BarnetrygdmottakerKafkaMelding( + val meldingstype: Type, + val requestId: UUID, + val personident: String? +) { + enum class Type { + START, + DATA, + SLUTT; + } +} diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdTopic.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaTopic.kt similarity index 78% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdTopic.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaTopic.kt index f50a716..7542d51 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdTopic.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaTopic.kt @@ -1,6 +1,6 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka -object BarnetrygdTopic { +object BarnetrygdmottakerKafkaTopic { // const val NAME = "pensjonopptjening.omsorgsarbeid-mock-topic" const val NAME = "teamfamilie.aapen-familie-ba-sak-identer-med-barnetrygd" } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdtmottakerKafkaListenerConfig.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdtmottakerKafkaListenerConfig.kt new file mode 100644 index 0000000..584f175 --- /dev/null +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdtmottakerKafkaListenerConfig.kt @@ -0,0 +1,44 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka + +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.config.KafkaConfig +import org.apache.kafka.clients.consumer.ConsumerConfig +import org.apache.kafka.common.serialization.StringDeserializer +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Profile +import org.springframework.kafka.annotation.EnableKafka +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory +import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.listener.ContainerProperties +import java.time.Duration + +@EnableKafka +@Configuration +@Profile("dev-gcp", "prod-gcp", "kafkaIntegrationTest") +class BarnetrygdtmottakerKafkaListenerConfig( + @Value("\${kafka.brokers}") private val aivenBootstrapServers: String, + private val customErrorHandler: BarnetrygdmottakerKafkaErrorHandler +) { + + @Bean("listener") + fun listener(securityConfig: KafkaConfig.SecurityConfig): ConcurrentKafkaListenerContainerFactory? = + ConcurrentKafkaListenerContainerFactory().apply { + containerProperties.ackMode = ContainerProperties.AckMode.MANUAL + containerProperties.setAuthExceptionRetryInterval(Duration.ofSeconds(4L)) + consumerFactory = DefaultKafkaConsumerFactory( + consumerConfig() + securityConfig, + StringDeserializer(), + StringDeserializer() + ) + setCommonErrorHandler(customErrorHandler) + } + + private fun consumerConfig(): Map = mapOf( + ConsumerConfig.CLIENT_ID_CONFIG to "omsorgsopptjening-start-innlesning", + ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG to aivenBootstrapServers, + ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG to false, + ConsumerConfig.AUTO_OFFSET_RESET_CONFIG to "earliest", + ConsumerConfig.MAX_POLL_RECORDS_CONFIG to 1, + ) +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/InnlesingRepository.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdInnlesingRepository.kt similarity index 62% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/InnlesingRepository.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdInnlesingRepository.kt index 43fad0e..2236c26 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/InnlesingRepository.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdInnlesingRepository.kt @@ -1,6 +1,7 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesing import org.springframework.jdbc.core.RowMapper import org.springframework.jdbc.core.namedparam.MapSqlParameterSource import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate @@ -10,17 +11,18 @@ import java.sql.ResultSet import java.util.UUID @Component -class InnlesingRepository( +class BarnetrygdInnlesingRepository( private val jdbcTemplate: NamedParameterJdbcTemplate, ) { - fun bestilt(innlesing: Innlesing): Innlesing { + fun bestilt(innlesing: BarnetrygdInnlesing.Bestilt): BarnetrygdInnlesing { val keyHolder = GeneratedKeyHolder() jdbcTemplate.update( - """insert into innlesing (id, år, forespurt_tidspunkt) values (:id, :ar, now())""", + """insert into innlesing (id, år, forespurt_tidspunkt) values (:id, :ar, :forespurt_tidspunkt::timestamptz)""", MapSqlParameterSource( mapOf( "id" to innlesing.id.toString(), "ar" to innlesing.år, + "forespurt_tidspunkt" to innlesing.forespurtTidspunkt.toString() ), ), keyHolder @@ -28,31 +30,33 @@ class InnlesingRepository( return finn(keyHolder.keys!!["id"] as String)!! } - fun start(id: String): Innlesing { + fun start(startet: BarnetrygdInnlesing.Startet): BarnetrygdInnlesing { jdbcTemplate.update( - """update innlesing set start_tidspunkt = now() where id = :id""", + """update innlesing set start_tidspunkt = :start_tidspunkt::timestamptz where id = :id""", MapSqlParameterSource( mapOf( - "id" to id, + "id" to startet.id.toString(), + "start_tidspunkt" to startet.startTidspunkt.toString() ), ), ) - return finn(id)!! + return finn(startet.id.toString())!! } - fun fullført(id: String): Innlesing { + fun fullført(ferdig: BarnetrygdInnlesing.Ferdig): BarnetrygdInnlesing { jdbcTemplate.update( - """update innlesing set ferdig_tidspunkt = now() where id = :id""", + """update innlesing set ferdig_tidspunkt = :ferdig_tidspunkt::timestamptz where id = :id""", MapSqlParameterSource( mapOf( - "id" to id, + "id" to ferdig.id.toString(), + "ferdig_tidspunkt" to ferdig.ferdigTidspunkt.toString() ), ), ) - return finn(id)!! + return finn(ferdig.id.toString())!! } - fun finn(id: String): Innlesing? { + fun finn(id: String): BarnetrygdInnlesing? { return jdbcTemplate.query( """select * from innlesing where id = :id""", MapSqlParameterSource( @@ -75,9 +79,9 @@ class InnlesingRepository( ) } - private class InnlesingRowMapper : RowMapper { - override fun mapRow(rs: ResultSet, rowNum: Int): Innlesing? { - return Innlesing( + private class InnlesingRowMapper : RowMapper { + override fun mapRow(rs: ResultSet, rowNum: Int): BarnetrygdInnlesing { + return BarnetrygdInnlesing.of( id = InnlesingId.fromString(rs.getString("id")), år = rs.getInt("år"), forespurtTidspunkt = rs.getTimestamp("forespurt_tidspunkt").toInstant(), diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerRepository.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdmottakerRepository.kt similarity index 82% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerRepository.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdmottakerRepository.kt index 69a98d7..4d8d0cf 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerRepository.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdmottakerRepository.kt @@ -1,10 +1,11 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId import no.nav.pensjon.opptjening.omsorgsopptjening.felles.deserializeList import no.nav.pensjon.opptjening.omsorgsopptjening.felles.serialize import no.nav.pensjon.opptjening.omsorgsopptjening.felles.serializeList +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.Barnetrygdmottaker import org.springframework.jdbc.core.RowMapper import org.springframework.jdbc.core.namedparam.MapSqlParameterSource import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate @@ -20,15 +21,15 @@ class BarnetrygdmottakerRepository( private val jdbcTemplate: NamedParameterJdbcTemplate, private val clock: Clock = Clock.systemUTC() ) { - fun save(melding: Barnetrygdmottaker): Barnetrygdmottaker { + fun insert(barnetrygdmottaker: Barnetrygdmottaker): Barnetrygdmottaker { val keyHolder = GeneratedKeyHolder() jdbcTemplate.update( """insert into barnetrygdmottaker (ident, correlation_id, innlesing_id) values (:ident, :correlation_id, :innlesing_id)""", MapSqlParameterSource( mapOf( - "ident" to melding.ident, - "correlation_id" to melding.correlationId.toString(), - "innlesing_id" to melding.innlesingId.toString(), + "ident" to barnetrygdmottaker.ident, + "correlation_id" to barnetrygdmottaker.correlationId.toString(), + "innlesing_id" to barnetrygdmottaker.innlesingId.toString(), ), ), keyHolder @@ -38,22 +39,22 @@ class BarnetrygdmottakerRepository( MapSqlParameterSource( mapOf( "id" to keyHolder.keys!!["id"] as UUID, - "status" to serialize(melding.status), - "statushistorikk" to melding.statushistorikk.serializeList() + "status" to serialize(barnetrygdmottaker.status), + "statushistorikk" to barnetrygdmottaker.statushistorikk.serializeList() ), ), ) return find(keyHolder.keys!!["id"] as UUID)!! } - fun updateStatus(melding: Barnetrygdmottaker) { + fun updateStatus(barnetrygdmottaker: Barnetrygdmottaker) { jdbcTemplate.update( """update barnetrygdmottaker_status set status = to_json(:status::json), statushistorikk = to_json(:statushistorikk::json) where id = :id""", MapSqlParameterSource( mapOf( - "id" to melding.id!!, - "status" to serialize(melding.status), - "statushistorikk" to melding.statushistorikk.serializeList() + "id" to barnetrygdmottaker.id!!, + "status" to serialize(barnetrygdmottaker.status), + "statushistorikk" to barnetrygdmottaker.statushistorikk.serializeList() ), ), ) diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/WebApi.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/web/BarnetrygdWebApi.kt similarity index 52% rename from src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/WebApi.kt rename to src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/web/BarnetrygdWebApi.kt index 8ed3637..9b61249 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/WebApi.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/web/BarnetrygdWebApi.kt @@ -1,7 +1,9 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.web -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.BarnetrygdService -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.HentBarnetygdmottakereResponse +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesing +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdInnlesingRepository +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdmottakerService +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external.BestillBarnetrygdmottakereResponse import no.nav.security.token.support.core.api.Protected import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -9,12 +11,13 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RestController +import java.time.Instant @RestController @Protected -class WebApi( - private val barnetrygdService: BarnetrygdService, - private val innlesingRepository: InnlesingRepository, +class BarnetrygdWebApi( + private val barnetrygdService: BarnetrygdmottakerService, + private val innlesingRepository: BarnetrygdInnlesingRepository, ) { companion object { val log: Logger = LoggerFactory.getLogger(this::class.java) @@ -24,18 +27,20 @@ class WebApi( fun startInnlesning(@PathVariable ar: Int): ResponseEntity { return barnetrygdService.bestillPersonerMedBarnetrygd(ar).let { when (it) { - is HentBarnetygdmottakereResponse.Feil -> { - ResponseEntity.status(it.status ?: 500).body(it.body ?: "Ukjent feil") + is BestillBarnetrygdmottakereResponse.Feil -> { + ResponseEntity.status(it.status ?: 500).body(it.melding ?: "Ukjent feil") } - is HentBarnetygdmottakereResponse.Ok -> { + is BestillBarnetrygdmottakereResponse.Ok -> { + log.info("Bestilt innlesing: ${it.innlesingId} av barnetrygdmottakere for år:${it.år}") innlesingRepository.bestilt( - Innlesing( + BarnetrygdInnlesing.Bestilt( id = it.innlesingId, - år = it.år + år = it.år, + forespurtTidspunkt = Instant.now(), ) ) - ResponseEntity.ok("""Bestilt innlesning: ${it.innlesingId} for $ar""") + ResponseEntity.ok("""${it.innlesingId}""") } } } diff --git a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/KafkaConfig.kt b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/KafkaConfig.kt index d8e6f51..33ad209 100644 --- a/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/KafkaConfig.kt +++ b/src/main/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/config/KafkaConfig.kt @@ -1,5 +1,6 @@ package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.config +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka.BarnetrygdmottakerKafkaErrorHandler import org.apache.kafka.clients.CommonClientConfigs import org.apache.kafka.clients.consumer.ConsumerConfig import org.apache.kafka.clients.producer.ProducerConfig @@ -24,7 +25,7 @@ import java.time.Duration @Profile("dev-gcp", "prod-gcp", "kafkaIntegrationTest") class KafkaConfig( @Value("\${kafka.brokers}") private val aivenBootstrapServers: String, - private val errorHandler: KafkaErrorHandler + private val errorHandler: BarnetrygdmottakerKafkaErrorHandler ) { @Bean @Profile("dev-gcp", "prod-gcp") @@ -45,27 +46,6 @@ class KafkaConfig( class SecurityConfig(vararg input: Pair) : Map by input.toMap() - @Bean("listener") - fun listener(securityConfig: SecurityConfig): ConcurrentKafkaListenerContainerFactory? = - ConcurrentKafkaListenerContainerFactory().apply { - containerProperties.ackMode = ContainerProperties.AckMode.MANUAL - containerProperties.setAuthExceptionRetryInterval(Duration.ofSeconds(4L)) - consumerFactory = DefaultKafkaConsumerFactory( - consumerConfig() + securityConfig, - StringDeserializer(), - StringDeserializer() - ) - setCommonErrorHandler(errorHandler) - } - - private fun consumerConfig(): Map = mapOf( - ConsumerConfig.CLIENT_ID_CONFIG to "omsorgsopptjening-start-innlesning", - ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG to aivenBootstrapServers, - ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG to false, - ConsumerConfig.AUTO_OFFSET_RESET_CONFIG to "earliest", - ConsumerConfig.MAX_POLL_RECORDS_CONFIG to 1, - ) - @Bean("producer") fun producer(securityConfig: SecurityConfig): KafkaTemplate { return KafkaTemplate(DefaultKafkaProducerFactory(omsorgsopptjeningProducerConfig() + securityConfig)) diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/InnlesingCascadingDeleteTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/InnlesingCascadingDeleteTest.kt deleted file mode 100644 index d7d2d71..0000000 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/InnlesingCascadingDeleteTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning - -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.Barnetrygdmottaker -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.BarnetrygdmottakerRepository -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.SpringContextTest -import org.springframework.beans.factory.annotation.Autowired -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class InnlesingCascadingDeleteTest : SpringContextTest.NoKafka() { - @Autowired - private lateinit var innlesingRepository: InnlesingRepository - - @Autowired - private lateinit var barnetrygdmottakerRepository: BarnetrygdmottakerRepository - - @Test - fun `invalidering av innlesing sletter alle barnetrygdmottakere knyttet til innlesingen`() { - val a = Innlesing( - id = InnlesingId.generate(), - år = 2023 - ) - - val b = Barnetrygdmottaker( - ident = "12345678910", - correlationId = CorrelationId.generate(), - innlesingId = a.id - ) - - val aa = innlesingRepository.bestilt(a) - val bb = barnetrygdmottakerRepository.save(b) - - assertEquals(aa, innlesingRepository.finn(a.id.toString())) - assertEquals(bb, barnetrygdmottakerRepository.find(bb.id!!)) - - innlesingRepository.invalider(aa.id.toUUID()) - assertNull(barnetrygdmottakerRepository.find(bb.id!!)) - } -} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdClientTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdClientTest.kt deleted file mode 100644 index d33c73e..0000000 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdClientTest.kt +++ /dev/null @@ -1,255 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd - -import com.github.tomakehurst.wiremock.client.WireMock -import com.github.tomakehurst.wiremock.client.WireMock.equalTo -import com.github.tomakehurst.wiremock.core.WireMockConfiguration -import com.github.tomakehurst.wiremock.junit5.WireMockExtension -import com.github.tomakehurst.wiremock.matching.AnythingPattern -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.serialize -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Mdc -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpHeaders -import org.springframework.http.MediaType - -class BarnetrygdClientTest : SpringContextTest.NoKafka() { - - @Autowired - private lateinit var client: BarnetrygdClient - - companion object { - @RegisterExtension - private val wiremock = WireMockExtension.newInstance() - .options(WireMockConfiguration.wireMockConfig().port(WIREMOCK_PORT)) - .build()!! - } - - @Test - fun `returner ok dersom kall til bestill-personer-med-barnetrygd går bra`() { - wiremock.stubFor( - WireMock.get(WireMock.urlPathEqualTo("/api/ekstern/pensjon/bestill-personer-med-barnetrygd/2020")) - .withHeader(CorrelationId.identifier, AnythingPattern()) - .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.TEXT_PLAIN_VALUE)) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Bearer test.token.test")) - .willReturn( - WireMock.aResponse() - .withStatus(202) - .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .withBody("3d797c7d-6273-4be3-bd57-e13de35251f8") - ) - ) - - client.bestillPersonerMedBarnetrygd(ar = 2020).also { - assertEquals( - HentBarnetygdmottakereResponse.Ok( - InnlesingId.fromString("3d797c7d-6273-4be3-bd57-e13de35251f8"), - 2020 - ), it - ) - } - } - - @Test - fun `returner feil med diverse informasjon dersom kall til bestill-personer-med-barnetrygd gir 500`() { - wiremock.stubFor( - WireMock.get(WireMock.urlPathEqualTo("/api/ekstern/pensjon/bestill-personer-med-barnetrygd/2020")) - .withHeader(CorrelationId.identifier, AnythingPattern()) - .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.TEXT_PLAIN_VALUE)) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Bearer test.token.test")) - .willReturn( - WireMock.serverError() - .withBody( - """ - [ - { - "data": { - "key":"value" - }, - "status":"FEILET", - "melding":"Her ble det bare krøll", - "frontendMelding": "test", - "stacktrace": null - } - ] - """.trimIndent() - ) - ) - ) - - client.bestillPersonerMedBarnetrygd(ar = 2020).also { - assertEquals( - HentBarnetygdmottakereResponse.Feil( - 500, - """ - [ - { - "data": { - "key":"value" - }, - "status":"FEILET", - "melding":"Her ble det bare krøll", - "frontendMelding": "test", - "stacktrace": null - } - ] - """.trimIndent() - ), it - ) - } - } - - @Test - fun `returner feil dersom kall til bestill-personer-med-barnetrygd gir andre feil enn 500`() { - wiremock.stubFor( - WireMock.get(WireMock.urlPathEqualTo("/api/ekstern/pensjon/bestill-personer-med-barnetrygd/2020")) - .withHeader(CorrelationId.identifier, AnythingPattern()) - .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.TEXT_PLAIN_VALUE)) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Bearer test.token.test")) - .willReturn( - WireMock.forbidden().withBody("Forbidden!") - ) - ) - - client.bestillPersonerMedBarnetrygd(ar = 2020).also { - assertEquals( - HentBarnetygdmottakereResponse.Feil( - 403, - "Forbidden!" - ), it - ) - } - } - - @Test - fun `returner ok dersom kall til hent-barnetrygd går bra`() { - Mdc.scopedMdc(CorrelationId.generate()) { - Mdc.scopedMdc(InnlesingId.generate()) { - wiremock.stubFor( - WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) - .withHeader(CorrelationId.identifier, AnythingPattern()) - .withHeader(InnlesingId.identifier, AnythingPattern()) - .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Bearer test.token.test")) - .willReturn( - WireMock.ok() - .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .withBody( - serialize( - FagsakWrapper( - listOf( - Barnetrygdmelding.Sak( - fagsakId = "1", - fagsakEiersIdent = "11", - barnetrygdPerioder = emptyList() - ) - ) - ) - ) - ) - ) - ) - - client.hentBarnetrygd( - ident = "123", - ar = 2020 - ).also { - assertEquals( - HentBarnetrygdResponse.Ok( - listOf( - Barnetrygdmelding.Sak( - fagsakId = "1", - fagsakEiersIdent = "11", - barnetrygdPerioder = emptyList() - ) - ) - ), it - ) - } - } - } - } - - @Test - fun `returner feil med diverse informasjon dersom kall til hent-barnetrygd gir 500`() { - Mdc.scopedMdc(CorrelationId.generate()) { - Mdc.scopedMdc(InnlesingId.generate()) { - wiremock.stubFor( - WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) - .withHeader(CorrelationId.identifier, AnythingPattern()) - .withHeader(InnlesingId.identifier, AnythingPattern()) - .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Bearer test.token.test")) - .willReturn( - WireMock.serverError() - .withBody( - """ - [ - { - "status":"FUNKSJONELL_FEIL", - "melding":"Dette gikk ikke så bra" - } - ] - """.trimIndent() - ) - ) - ) - - client.hentBarnetrygd( - ident = "123", - ar = 2020 - ).also { - assertEquals( - HentBarnetrygdResponse.Feil( - 500, - """ - [ - { - "status":"FUNKSJONELL_FEIL", - "melding":"Dette gikk ikke så bra" - } - ] - """.trimIndent() - ), it - ) - } - } - } - } - - @Test - fun `returner feil dersom kall til hent-barnetrygd gir andre feil enn 500`() { - Mdc.scopedMdc(CorrelationId.generate()) { - Mdc.scopedMdc(InnlesingId.generate()) { - wiremock.stubFor( - WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) - .withHeader(CorrelationId.identifier, AnythingPattern()) - .withHeader(InnlesingId.identifier, AnythingPattern()) - .withHeader(HttpHeaders.CONTENT_TYPE, equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.ACCEPT, equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.AUTHORIZATION, equalTo("Bearer test.token.test")) - .willReturn( - WireMock.forbidden().withBody("Forbidden!") - ) - ) - - client.hentBarnetrygd( - ident = "123", - ar = 2020 - ).also { - assertEquals( - HentBarnetrygdResponse.Feil( - 403, - "Forbidden!" - ), it - ) - } - } - } - } -} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerRepositoryTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerRepositoryTest.kt deleted file mode 100644 index f5a3cac..0000000 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/BarnetrygdmottakerRepositoryTest.kt +++ /dev/null @@ -1,82 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd - -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Innlesing -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingRepository -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.transaction.TransactionDefinition -import org.springframework.transaction.support.TransactionTemplate -import kotlin.test.assertNotNull -import kotlin.test.assertNull - -class BarnetrygdmottakerRepositoryTest : SpringContextTest.NoKafka() { - - @Autowired - private lateinit var innlesingRepository: InnlesingRepository - - @Autowired - private lateinit var barnetrygdmottakerRepository: BarnetrygdmottakerRepository - - @Autowired - private lateinit var transactionTemplate: TransactionTemplate - - @Test - fun `finner ingen meldinger som skal prosesseres før alle meldingene i forsendelsen er lest inn`() { - val innlesing = innlesingRepository.bestilt(Innlesing(id = InnlesingId.generate(), år = 2023)) - - barnetrygdmottakerRepository.save( - melding = Barnetrygdmottaker( - ident = "123", - correlationId = CorrelationId.generate(), - innlesingId = innlesing.id - ) - ) - - assertNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) - - innlesingRepository.fullført(innlesing.id.toString()) - - assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) - } - - @Test - fun `finnNesteUprosesserte låser raden slik at den ikke plukkes opp av andre connections`() { - val innlesing = innlesingRepository.bestilt(Innlesing( - id = InnlesingId.generate(), - år = 2023 - )).also { innlesingRepository.fullført(it.id.toString()) } - - barnetrygdmottakerRepository.save( - melding = Barnetrygdmottaker( - ident = "123", - correlationId = CorrelationId.generate(), - innlesingId = innlesing.id - ) - ) - - //krev ny transaksjon slik at det opprettes ny connection - transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW) - - transactionTemplate.execute { - //låser den aktuelle raden for denne transaksjonens varighet - Assertions.assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) - - //opprett ny transaksjon mens den forrige fortsatt lever - transactionTemplate.execute { - //skal ikke finne noe siden raden er låst pga "select for update skip locked" - Assertions.assertNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) - } - //fortsatt samme transaksjon - Assertions.assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) - } //rad ikke låst lenger ved transaksjon slutt - - - //ny transaksjon finner raden da den ikke lenger er låst - transactionTemplate.execute { - Assertions.assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) - } - } -} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/EndToEndTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/EndToEndTest.kt new file mode 100644 index 0000000..84ca78f --- /dev/null +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/EndToEndTest.kt @@ -0,0 +1,107 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd + +import com.github.tomakehurst.wiremock.core.WireMockConfiguration +import com.github.tomakehurst.wiremock.junit5.WireMockExtension +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.deserialize +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.messages.domene.OmsorgsgrunnlagMelding +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external.`bestill-personer-med-barnetrygd accepted` +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external.`hent-barnetrygd ok` +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka.BarnetrygdmottakerKafkaMelding +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.web.BarnetrygdWebApi +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +import org.springframework.beans.factory.annotation.Autowired +import java.time.Month +import java.time.YearMonth +import java.util.UUID + +class EndToEndTest : SpringContextTest.WithKafka() { + + @Autowired + private lateinit var listener: OmsorgsopptjeningTopicListener + + @Autowired + private lateinit var webApi: BarnetrygdWebApi + + companion object { + @RegisterExtension + private val wiremock = WireMockExtension.newInstance() + .options(WireMockConfiguration.wireMockConfig().port(WIREMOCK_PORT)) + .build()!! + } + + @Test + fun `leser melding fra barnetryd topic, prosesserer meldingen og sender ny melding til intern topic`() { + wiremock.`bestill-personer-med-barnetrygd accepted`() + wiremock.`hent-barnetrygd ok`() + + val innlesingId = webApi.startInnlesning(2020).body!! + + sendStartInnlesingKafka(innlesingId) + sendBarnetrygdmottakerDataKafka( + melding = BarnetrygdmottakerKafkaMelding( + meldingstype = BarnetrygdmottakerKafkaMelding.Type.DATA, + requestId = UUID.fromString(innlesingId), + personident = "12345678910" + ) + ) + sendSluttInnlesingKafka(innlesingId) + + listener.removeFirstRecord(3).let { consumerRecord -> + assertEquals( + """ + {"ident":"12345678910"} + """.trimIndent(), + consumerRecord.key() + ) + deserialize(consumerRecord.value()).also { + assertEquals("12345678910", it.omsorgsyter) + assertEquals("BARNETRYGD", it.omsorgstype.toString()) + assertEquals("BARNETRYGD", it.kilde.toString()) + assertEquals( + listOf( + OmsorgsgrunnlagMelding.Sak( + omsorgsyter = "12345678910", + vedtaksperioder = listOf( + OmsorgsgrunnlagMelding.VedtakPeriode( + fom = YearMonth.of(2020, Month.JANUARY), + tom = YearMonth.of(2025, Month.DECEMBER), + omsorgsmottaker = "09876543210", + prosent = 100, + ) + ) + ) + ), + it.saker + ) + assertEquals( + """ + { + "fagsaker": [ + { + "fagsakId":"1", + "fagsakEiersIdent":"12345678910", + "barnetrygdPerioder":[ + { + "personIdent":"09876543210", + "delingsprosentYtelse":100, + "ytelseTypeEkstern":"ORDINÆR_BARNETRYGD", + "utbetaltPerMnd":2000, + "stønadFom": "2020-01", + "stønadTom": "2025-12" + } + ] + } + ] + } + """.trimIndent(), + it.rådata.toString() + ) + assertEquals(innlesingId, it.innlesingId.toString()) + assertNotNull(it.correlationId) //opprettes internt + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/InnlesingInvalideringTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/InnlesingInvalideringTest.kt deleted file mode 100644 index ddb5055..0000000 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/InnlesingInvalideringTest.kt +++ /dev/null @@ -1,81 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd - -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Innlesing -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingRepository -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import java.util.UUID - -class InnlesingInvalideringTest : SpringContextTest.WithKafka() { - - @Autowired - private lateinit var innlesingRepository: InnlesingRepository - - @Test - fun `invaliderer innlesing etter retries dersom en record ikke leses ok`() { - val innlesing = innlesingRepository.bestilt(Innlesing(InnlesingId.generate(), 2020)) - - sendStartInnlesingKafka(innlesing.id.toString()) - - sendBarnetrygdmottakerDataKafka( - melding = KafkaMelding( - meldingstype = KafkaMelding.Type.DATA, - requestId = UUID.fromString(innlesing.id.toString()), - personident = "12345678910" - ) - ) - - Thread.sleep(1000) - assertNotNull(innlesingRepository.finn(innlesing.id.toString())) - - sendBarnetrygdmottakerDataKafka( - melding = KafkaMelding( - meldingstype = KafkaMelding.Type.DATA, - requestId = UUID.fromString(innlesing.id.toString()), - personident = null - ) - ) - - assertNotNull(innlesingRepository.finn(innlesing.id.toString())) - Thread.sleep(1200) - //første retry fulført - assertNotNull(innlesingRepository.finn(innlesing.id.toString())) - Thread.sleep(1200) - //andre retry fullført + invalidert - assertNull(innlesingRepository.finn(innlesing.id.toString())) - - //ok melding - innlesing invalidert, forbigår i stillhet - sendBarnetrygdmottakerDataKafka( - melding = KafkaMelding( - meldingstype = KafkaMelding.Type.DATA, - requestId = UUID.fromString(innlesing.id.toString()), - personident = "12345678910" - ) - ) - - Thread.sleep(1000) - assertNull(innlesingRepository.finn(innlesing.id.toString())) - - //ok melding - innlesing invalidert, forbigår i stillhet - sendSluttInnlesingKafka(innlesing.id.toString()) - - Thread.sleep(1000) - assertNull(innlesingRepository.finn(innlesing.id.toString())) - } - - @Test - fun `invaliderer innlesing uten retries dersom innlesing er i ugyldig tilstand`() { - val innlesing = innlesingRepository.bestilt(Innlesing(InnlesingId.generate(), 2020)) - - sendStartInnlesingKafka(innlesing.id.toString()) - assertNotNull(innlesingRepository.finn(innlesing.id.toString())) - - sendStartInnlesingKafka(innlesing.id.toString()) - //litt tid til å prosessere en melding, men mindre enne en full retry cycle - Thread.sleep(500) - assertNull(innlesingRepository.finn(innlesing.id.toString())) - } -} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/KafkaIntegrationTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/KafkaIntegrationTest.kt deleted file mode 100644 index 0f8eaf3..0000000 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/KafkaIntegrationTest.kt +++ /dev/null @@ -1,107 +0,0 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd - -import com.github.tomakehurst.wiremock.client.WireMock -import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo -import com.github.tomakehurst.wiremock.core.WireMockConfiguration -import com.github.tomakehurst.wiremock.junit5.WireMockExtension -import com.github.tomakehurst.wiremock.matching.AnythingPattern -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.deserialize -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.messages.domene.OmsorgsgrunnlagMelding -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.serialize -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Innlesing -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingRepository -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNotNull -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.RegisterExtension -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.http.HttpHeaders -import org.springframework.http.MediaType -import java.util.UUID - -class KafkaIntegrationTest : SpringContextTest.WithKafka() { - - @Autowired - private lateinit var listener: OmsorgsopptjeningTopicListener - - @Autowired - private lateinit var innlesingRepository: InnlesingRepository - - companion object { - @RegisterExtension - private val wiremock = WireMockExtension.newInstance() - .options(WireMockConfiguration.wireMockConfig().port(WIREMOCK_PORT)) - .build()!! - } - - @Test - fun test() { - wiremock.stubFor( - WireMock.post(urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) - .withHeader(CorrelationId.identifier, AnythingPattern()) - .withHeader(HttpHeaders.CONTENT_TYPE, WireMock.equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.ACCEPT, WireMock.equalTo(MediaType.APPLICATION_JSON_VALUE)) - .withHeader(HttpHeaders.AUTHORIZATION, WireMock.equalTo("Bearer test.token.test")) - .willReturn( - WireMock.ok() - .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .withBody( - serialize( - FagsakWrapper( - listOf( - Barnetrygdmelding.Sak( - fagsakId = "1", - fagsakEiersIdent = "12345678910", - barnetrygdPerioder = emptyList() - ) - ) - ) - ) - ) - ) - - ) - - val innlesing = innlesingRepository.bestilt(Innlesing(InnlesingId.generate(), 2020)) - sendStartInnlesingKafka(innlesing.id.toString()) - sendBarnetrygdmottakerDataKafka( - melding = KafkaMelding( - meldingstype = KafkaMelding.Type.DATA, - requestId = UUID.fromString(innlesing.id.toString()), - personident = "12345678910" - ) - ) - sendSluttInnlesingKafka(innlesing.id.toString()) - - listener.removeFirstRecord(5).let { - assertEquals( - """ - {"ident":"12345678910"} - """.trimIndent(), - it.key() - ) - deserialize(it.value()).also { - assertEquals("12345678910", it.omsorgsyter) - assertEquals("BARNETRYGD", it.omsorgstype.toString()) - assertEquals("BARNETRYGD", it.kilde.toString()) - assertEquals( - listOf( - OmsorgsgrunnlagMelding.Sak( - omsorgsyter = "12345678910", - vedtaksperioder = emptyList() - ) - ), - it.saker - ) - assertEquals( - """[{"fagsakId":"1","fagsakEiersIdent":"12345678910","barnetrygdPerioder":[]}]""", - it.rådata.toString() - ) - assertEquals(innlesing.id, it.innlesingId) - assertNotNull(it.correlationId) //opprettes internt - } - } - } -} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/SpringContextTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/SpringContextTest.kt index 8144973..fea14ec 100644 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/SpringContextTest.kt +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/SpringContextTest.kt @@ -4,6 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.Topics import no.nav.pensjon.opptjening.omsorgsopptjening.felles.serialize import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Application +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka.BarnetrygdmottakerKafkaTopic +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka.BarnetrygdmottakerKafkaMelding import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.config.KafkaIntegrationTestConfig import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.databasecontainer.PostgresqlTestContainer import no.nav.security.token.support.spring.test.EnableMockOAuth2Server @@ -37,7 +39,7 @@ sealed class SpringContextTest { } @ActiveProfiles("kafkaIntegrationTest") - @EmbeddedKafka(partitions = 1, topics = [BarnetrygdTopic.NAME, Topics.Omsorgsopptjening.NAME]) + @EmbeddedKafka(partitions = 1, topics = [BarnetrygdmottakerKafkaTopic.NAME, Topics.Omsorgsopptjening.NAME]) @SpringBootTest(classes = [Application::class]) @Import(KafkaIntegrationTestConfig::class) @EnableMockOAuth2Server @@ -52,12 +54,12 @@ sealed class SpringContextTest { requestId: String ) { val pr = ProducerRecord( - BarnetrygdTopic.NAME, + BarnetrygdmottakerKafkaTopic.NAME, null, "", serialize( - KafkaMelding( - meldingstype = KafkaMelding.Type.START, + BarnetrygdmottakerKafkaMelding( + meldingstype = BarnetrygdmottakerKafkaMelding.Type.START, requestId = UUID.fromString(requestId), personident = null ) @@ -67,10 +69,10 @@ sealed class SpringContextTest { } fun sendBarnetrygdmottakerDataKafka( - melding: KafkaMelding, + melding: BarnetrygdmottakerKafkaMelding, ) { val pr = ProducerRecord( - BarnetrygdTopic.NAME, + BarnetrygdmottakerKafkaTopic.NAME, null, null, melding.personident, @@ -79,16 +81,27 @@ sealed class SpringContextTest { kafkaProducer.send(pr).get() } + fun sendUgyldigMeldingKafka(){ + val pr = ProducerRecord( + BarnetrygdmottakerKafkaTopic.NAME, + null, + null, + "", + """{"bogus":"format"}""", + ) + kafkaProducer.send(pr).get() + } + fun sendSluttInnlesingKafka( requestId: String ) { val pr = ProducerRecord( - BarnetrygdTopic.NAME, + BarnetrygdmottakerKafkaTopic.NAME, null, "", serialize( - KafkaMelding( - meldingstype = KafkaMelding.Type.SLUTT, + BarnetrygdmottakerKafkaMelding( + meldingstype = BarnetrygdmottakerKafkaMelding.Type.SLUTT, requestId = UUID.fromString(requestId), personident = null ) diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/StatusTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/StatusTest.kt index ae3f355..9eb0b0a 100644 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/StatusTest.kt +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/StatusTest.kt @@ -2,6 +2,7 @@ package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.Barnetrygdmottaker import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandlerTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandlerTest.kt new file mode 100644 index 0000000..5d950b4 --- /dev/null +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerMessageHandlerTest.kt @@ -0,0 +1,115 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdInnlesingRepository +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdmottakerRepository +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.mockito.kotlin.any +import org.mockito.kotlin.given +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions +import java.time.Instant + +class BarnetrygdmottakerMessageHandlerTest { + + private val innlesingRepository: BarnetrygdInnlesingRepository = mock() + private val barnetrygdmottakerRepository: BarnetrygdmottakerRepository = mock() + private val handler: BarnetrygdmottakerMessageHandler = BarnetrygdmottakerMessageHandler( + innlesingRepository, + barnetrygdmottakerRepository + ) + + private val startmelding = BarnetrygdmottakerMelding.Start( + correlationId = CorrelationId.generate(), + innlesingId = InnlesingId.generate() + ) + + private val datamelding = BarnetrygdmottakerMelding.Data( + personIdent = "12345", + correlationId = CorrelationId.generate(), + innlesingId = InnlesingId.generate() + ) + + private val sluttmelding = BarnetrygdmottakerMelding.Slutt( + correlationId = CorrelationId.generate(), + innlesingId = InnlesingId.generate() + ) + + @Test + fun `kaster exception dersom innlesingen ikke eksisterer`() { + given(innlesingRepository.finn(any())).willReturn(null) + assertThrows { + handler.handle(startmelding) + } + } + + + @Test + fun `gitt at status på innlesingen er startet skal man kunne håndtere en datamelding`() { + given(innlesingRepository.finn(any())).willReturn( + BarnetrygdInnlesing.Startet( + id = InnlesingId.generate(), + år = 6624, + forespurtTidspunkt = Instant.now(), + startTidspunkt = Instant.now(), + ) + ) + handler.handle(datamelding) + verify(innlesingRepository).finn(any()) + verify(barnetrygdmottakerRepository).insert(any()) + } + + @Test + fun `gitt at status på innlesingen er startet skal man kunne håndtere en sluttmelding`() { + given(innlesingRepository.finn(any())).willReturn( + BarnetrygdInnlesing.Startet( + id = InnlesingId.generate(), + år = 6624, + forespurtTidspunkt = Instant.now(), + startTidspunkt = Instant.now(), + ) + ) + handler.handle(sluttmelding) + verify(innlesingRepository).finn(any()) + verify(innlesingRepository).fullført(any()) + } + + @Test + fun `gitt at status på innlesingen er avsluttet kastest det exception dersom man forsøker håndtere data igjen`() { + given(innlesingRepository.finn(any())).willReturn( + BarnetrygdInnlesing.Ferdig( + id = InnlesingId.generate(), + år = 6624, + forespurtTidspunkt = Instant.now(), + startTidspunkt = Instant.now(), + ferdigTidspunkt = Instant.now() + ) + ) + assertThrows { + handler.handle(datamelding) + } + verify(innlesingRepository).finn(any()) + verifyNoInteractions(barnetrygdmottakerRepository) + } + + @Test + fun `gitt at status på innlesingen er avsluttet kastest det exception dersom man forsøker å avslutte igjen`() { + given(innlesingRepository.finn(any())).willReturn( + BarnetrygdInnlesing.Ferdig( + id = InnlesingId.generate(), + år = 6624, + forespurtTidspunkt = Instant.now(), + startTidspunkt = Instant.now(), + ferdigTidspunkt = Instant.now() + ) + ) + assertThrows { + handler.handle(sluttmelding) + } + verify(innlesingRepository).finn(any()) + verifyNoInteractions(barnetrygdmottakerRepository) + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/RetryTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerServiceTest.kt similarity index 70% rename from src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/RetryTest.kt rename to src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerServiceTest.kt index d75be60..0e80f72 100644 --- a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/RetryTest.kt +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/domain/BarnetrygdmottakerServiceTest.kt @@ -1,4 +1,4 @@ -package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain import com.fasterxml.jackson.core.JsonParseException import com.github.tomakehurst.wiremock.client.WireMock @@ -7,13 +7,12 @@ import com.github.tomakehurst.wiremock.junit5.WireMockExtension import com.github.tomakehurst.wiremock.stubbing.Scenario import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId -import no.nav.pensjon.opptjening.omsorgsopptjening.felles.serialize -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Innlesing -import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.InnlesingRepository +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.SpringContextTest +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdInnlesingRepository +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdmottakerRepository import org.apache.kafka.clients.producer.ProducerRecord import org.junit.jupiter.api.Assertions.assertInstanceOf import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.RegisterExtension import org.mockito.BDDMockito.any import org.mockito.BDDMockito.given @@ -26,14 +25,13 @@ import java.time.Clock import java.time.Instant import java.time.temporal.ChronoUnit import java.util.concurrent.CompletableFuture -import kotlin.test.assertEquals -class RetryTest : SpringContextTest.NoKafka() { +class BarnetrygdmottakerServiceTest : SpringContextTest.NoKafka() { @Autowired private lateinit var barnetrygdmottakerRepository: BarnetrygdmottakerRepository @Autowired - private lateinit var barnetrygdService: BarnetrygdService + private lateinit var barnetrygdService: BarnetrygdmottakerService @MockBean private lateinit var kafkaTemplate: KafkaTemplate @@ -42,7 +40,7 @@ class RetryTest : SpringContextTest.NoKafka() { private lateinit var clock: Clock @Autowired - private lateinit var innlesingRepository: InnlesingRepository + private lateinit var innlesingRepository: BarnetrygdInnlesingRepository companion object { @RegisterExtension @@ -62,10 +60,9 @@ class RetryTest : SpringContextTest.NoKafka() { */ given(clock.instant()).willReturn(Instant.now().plus(10, ChronoUnit.DAYS)) - val innlesing = innlesingRepository.bestilt(Innlesing(id = InnlesingId.generate(), år = 2023)) - .also { innlesingRepository.fullført(id = it.id.toString()) } + val innlesing = lagreFullførtInnlesing() - val barnetrygdmottaker = barnetrygdmottakerRepository.save( + val barnetrygdmottaker = barnetrygdmottakerRepository.insert( Barnetrygdmottaker( ident = "12345678910", correlationId = CorrelationId.generate(), @@ -80,8 +77,8 @@ class RetryTest : SpringContextTest.NoKafka() { barnetrygdService.process() barnetrygdmottakerRepository.find(barnetrygdmottaker.id!!)!!.also { assertInstanceOf(Barnetrygdmottaker.Status.Retry::class.java, it.status).also { - assertEquals(1, it.antallForsøk) - assertEquals(3, it.maxAntallForsøk) + kotlin.test.assertEquals(1, it.antallForsøk) + kotlin.test.assertEquals(3, it.maxAntallForsøk) } } barnetrygdService.process() @@ -104,8 +101,15 @@ class RetryTest : SpringContextTest.NoKafka() { */ given(clock.instant()).willReturn(Instant.now().plus(10, ChronoUnit.DAYS)) - val innlesing = innlesingRepository.bestilt(Innlesing(id = InnlesingId.generate(), år = 2023)) - .also { innlesingRepository.fullført(id = it.id.toString()) } + val innlesing = lagreFullførtInnlesing() + + val barnetrygdmottaker = barnetrygdmottakerRepository.insert( + Barnetrygdmottaker( + ident = "12345678910", + correlationId = CorrelationId.generate(), + innlesingId = innlesing.id + ) + ) wiremock.stubFor( WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) @@ -123,29 +127,30 @@ class RetryTest : SpringContextTest.NoKafka() { WireMock.ok() .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .withBody( - serialize( - FagsakWrapper( - listOf( - Barnetrygdmelding.Sak( - fagsakId = "1", - fagsakEiersIdent = "12345678910", - barnetrygdPerioder = emptyList() - ) - ) - ) - ) + """ + { + "fagsaker": [ + { + "fagsakId":"1", + "fagsakEiersIdent":"12345678910", + "barnetrygdPerioder":[ + { + "personIdent":"09876543210", + "delingsprosentYtelse":100, + "ytelseTypeEkstern":"ORDINÆR_BARNETRYGD", + "utbetaltPerMnd":2000, + "stønadFom": "2020-01", + "stønadTom": "2025-12" + } + ] + } + ] + } + """.trimIndent() ) ) ) - val barnetrygdmottaker = barnetrygdmottakerRepository.save( - Barnetrygdmottaker( - ident = "12345678910", - correlationId = CorrelationId.generate(), - innlesingId = innlesing.id - ) - ) - assertInstanceOf( Barnetrygdmottaker.Status.Klar::class.java, barnetrygdmottakerRepository.find(barnetrygdmottaker.id!!)!!.status @@ -154,8 +159,8 @@ class RetryTest : SpringContextTest.NoKafka() { barnetrygdService.process() barnetrygdmottakerRepository.find(barnetrygdmottaker.id!!).let { assertInstanceOf(Barnetrygdmottaker.Status.Retry::class.java, it!!.status).also { - assertEquals(1, it.antallForsøk) - assertEquals(3, it.maxAntallForsøk) + kotlin.test.assertEquals(1, it.antallForsøk) + kotlin.test.assertEquals(3, it.maxAntallForsøk) } } @@ -187,10 +192,9 @@ class RetryTest : SpringContextTest.NoKafka() { */ given(clock.instant()).willReturn(Instant.now().plus(10, ChronoUnit.DAYS)) - val innlesing = innlesingRepository.bestilt(Innlesing(id = InnlesingId.generate(), år = 2023)) - .also { innlesingRepository.fullført(id = it.id.toString()) } + val innlesing = lagreFullførtInnlesing() - val barnetrygdmottaker = barnetrygdmottakerRepository.save( + val barnetrygdmottaker = barnetrygdmottakerRepository.insert( Barnetrygdmottaker( ident = "12345678910", correlationId = CorrelationId.generate(), @@ -203,15 +207,27 @@ class RetryTest : SpringContextTest.NoKafka() { barnetrygdmottakerRepository.find(barnetrygdmottaker.id!!)!!.status ) - assertThrows { + org.junit.jupiter.api.assertThrows { barnetrygdService.process() } barnetrygdmottakerRepository.find(barnetrygdmottaker.id!!).also { assertInstanceOf(Barnetrygdmottaker.Status.Retry::class.java, it!!.status).also { - assertEquals(1, it.antallForsøk) - assertEquals(3, it.maxAntallForsøk) + kotlin.test.assertEquals(1, it.antallForsøk) + kotlin.test.assertEquals(3, it.maxAntallForsøk) } } } + + private fun lagreFullførtInnlesing(): BarnetrygdInnlesing { + val bestilt = innlesingRepository.bestilt( + BarnetrygdInnlesing.Bestilt( + id = InnlesingId.generate(), + år = 2023, + forespurtTidspunkt = Instant.now() + ) + ) + val startet = innlesingRepository.start(bestilt.startet()) + return innlesingRepository.fullført(startet.ferdig()) + } } \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdClientTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdClientTest.kt new file mode 100644 index 0000000..3b384ae --- /dev/null +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/BarnetrygdClientTest.kt @@ -0,0 +1,188 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external + +import com.github.tomakehurst.wiremock.core.WireMockConfiguration +import com.github.tomakehurst.wiremock.junit5.WireMockExtension +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.RådataFraKilde +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.domene.kafka.messages.domene.OmsorgsgrunnlagMelding +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.Mdc +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.SpringContextTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension +import org.springframework.beans.factory.annotation.Autowired +import java.time.Month +import java.time.YearMonth + +class BarnetrygdClientTest : SpringContextTest.NoKafka() { + + @Autowired + private lateinit var client: BarnetrygdClient + + companion object { + @RegisterExtension + private val wiremock = WireMockExtension.newInstance() + .options(WireMockConfiguration.wireMockConfig().port(WIREMOCK_PORT)) + .build()!! + } + + @Nested + inner class BestillPersonerMedBarnetrygd { + @Test + fun `returner ok dersom kall til bestill-personer-med-barnetrygd svarer med accepted`() { + wiremock.`bestill-personer-med-barnetrygd accepted`() + + client.bestillBarnetrygdmottakere(ar = 2020).also { + assertEquals( + BestillBarnetrygdmottakereResponse.Ok( + InnlesingId.fromString("3d797c7d-6273-4be3-bd57-e13de35251f8"), + 2020 + ), it + ) + } + } + + @Test + fun `returner feil med diverse informasjon dersom kall til bestill-personer-med-barnetrygd svarer med noe annet enn accepted`() { + wiremock.`bestill-personer-med-barnetrygd internal server error`() + + client.bestillBarnetrygdmottakere(ar = 2020).also { + assertEquals( + BestillBarnetrygdmottakereResponse.Feil( + 500, + """{whatever this may contain}""" + ), it + ) + } + } + } + + @Nested + inner class HentBarnetrygd { + @Test + fun `returner ok dersom kall til hent-barnetrygd svarer med 200`() { + Mdc.scopedMdc(CorrelationId.generate()) { + Mdc.scopedMdc(InnlesingId.generate()) { + wiremock.`hent-barnetrygd ok`() + + client.hentBarnetrygd( + ident = "123", + ar = 2020 + ).also { + assertEquals( + HentBarnetrygdResponse.Ok( + barnetrygdsaker = listOf( + OmsorgsgrunnlagMelding.Sak( + omsorgsyter = "12345678910", + vedtaksperioder = listOf( + OmsorgsgrunnlagMelding.VedtakPeriode( + omsorgsmottaker = "09876543210", + prosent = 100, + fom = YearMonth.of(2020, Month.JANUARY), + tom = YearMonth.of(2025, Month.DECEMBER) + ) + ) + ) + ), + rådataFraKilde = RådataFraKilde( + """ + { + "fagsaker": [ + { + "fagsakId":"1", + "fagsakEiersIdent":"12345678910", + "barnetrygdPerioder":[ + { + "personIdent":"09876543210", + "delingsprosentYtelse":100, + "ytelseTypeEkstern":"ORDINÆR_BARNETRYGD", + "utbetaltPerMnd":2000, + "stønadFom": "2020-01", + "stønadTom": "2025-12" + } + ] + } + ] + } + """.trimIndent() + ) + ), it + ) + } + } + } + } + + @Test + fun `returner feil med diverse informasjon dersom kall til hent-barnetrygd svarer med noe annet enn 200`() { + Mdc.scopedMdc(CorrelationId.generate()) { + Mdc.scopedMdc(InnlesingId.generate()) { + wiremock.`hent-barnetrygd internal server error`() + + client.hentBarnetrygd( + ident = "123", + ar = 2020 + ).also { + assertEquals( + HentBarnetrygdResponse.Feil( + 500, + """ + [ + { + "status":"FUNKSJONELL_FEIL", + "melding":"Dette gikk ikke så bra" + } + ] + """.trimIndent() + ), it + ) + } + } + } + } + + @Test + fun `returner feil dersom kall til hent-barnetrygd svarer med 200 ok med tom liste`() { + Mdc.scopedMdc(CorrelationId.generate()) { + Mdc.scopedMdc(InnlesingId.generate()) { + wiremock.`hent-barnetrygd ok uten fagsaker`() + + client.hentBarnetrygd( + ident = "123", + ar = 2020 + ).also { + assertEquals( + HentBarnetrygdResponse.Feil( + 200, + "Liste med barnetrygdsaker er tom" + ), it + ) + } + } + } + } + + @Test + fun `returner feil dersom kall til hent-barnetrygd svarer med 200 ok og saker mangler barnetrygdperioder`() { + Mdc.scopedMdc(CorrelationId.generate()) { + Mdc.scopedMdc(InnlesingId.generate()) { + wiremock.`hent-barnetrygd ok uten barnetrygdperioder`() + + client.hentBarnetrygd( + ident = "123", + ar = 2020 + ).also { + assertEquals( + HentBarnetrygdResponse.Feil( + 200, + "En eller flere av barnetrygdsakene mangler perioder" + ), it + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/WiremockScenario.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/WiremockScenario.kt new file mode 100644 index 0000000..f9234b9 --- /dev/null +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/external/WiremockScenario.kt @@ -0,0 +1,160 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.external + +import com.github.tomakehurst.wiremock.client.MappingBuilder +import com.github.tomakehurst.wiremock.client.WireMock +import com.github.tomakehurst.wiremock.junit5.WireMockExtension +import com.github.tomakehurst.wiremock.matching.AnythingPattern +import com.github.tomakehurst.wiremock.matching.StringValuePattern +import com.github.tomakehurst.wiremock.stubbing.StubMapping +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType + +fun WireMockExtension.`bestill-personer-med-barnetrygd accepted`(): StubMapping { + return this.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/api/ekstern/pensjon/bestill-personer-med-barnetrygd/2020")) + .withExpectedRequestHeadersBestillPersonerMedBarnetrygd() + .willReturn( + WireMock.aResponse() + .withStatus(202) + .withBody("3d797c7d-6273-4be3-bd57-e13de35251f8") + ) + ) +} + +fun WireMockExtension.`bestill-personer-med-barnetrygd internal server error`(): StubMapping { + return this.stubFor( + WireMock.get(WireMock.urlPathEqualTo("/api/ekstern/pensjon/bestill-personer-med-barnetrygd/2020")) + .withExpectedRequestHeadersBestillPersonerMedBarnetrygd() + .willReturn( + WireMock.serverError() + .withBody("""{whatever this may contain}""") + ) + ) +} + +private fun MappingBuilder.withExpectedRequestHeadersBestillPersonerMedBarnetrygd( + headers: List> = listOf( + CorrelationId.identifier to AnythingPattern(), + InnlesingId.identifier to AnythingPattern(), + HttpHeaders.ACCEPT to WireMock.equalTo(MediaType.TEXT_PLAIN_VALUE), + HttpHeaders.AUTHORIZATION to WireMock.equalTo("Bearer test.token.test"), + ) +): MappingBuilder { + headers.forEach { + this.withHeader(it.first, it.second) + } + return this +} + + +fun WireMockExtension.`hent-barnetrygd ok`(): StubMapping { + return this.stubFor( + WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) + .withExpectedRequestHeadersHentBarnetryd() + .willReturn( + WireMock.ok() + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody( + """ + { + "fagsaker": [ + { + "fagsakId":"1", + "fagsakEiersIdent":"12345678910", + "barnetrygdPerioder":[ + { + "personIdent":"09876543210", + "delingsprosentYtelse":100, + "ytelseTypeEkstern":"ORDINÆR_BARNETRYGD", + "utbetaltPerMnd":2000, + "stønadFom": "2020-01", + "stønadTom": "2025-12" + } + ] + } + ] + } + """.trimIndent() + ) + ) + ) +} + +fun WireMockExtension.`hent-barnetrygd ok uten fagsaker`(): StubMapping { + return this.stubFor( + WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) + .withExpectedRequestHeadersHentBarnetryd() + .willReturn( + WireMock.ok() + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody( + """ + { + "fagsaker": [] + } + """.trimIndent() + ) + ) + ) +} + +fun WireMockExtension.`hent-barnetrygd ok uten barnetrygdperioder`(): StubMapping { + return this.stubFor( + WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) + .withExpectedRequestHeadersHentBarnetryd() + .willReturn( + WireMock.ok() + .withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .withBody( + """ + { + "fagsaker": [ + { + "fagsakId":"1", + "fagsakEiersIdent":"12345678910", + "barnetrygdPerioder":[] + } + ] + } + """.trimIndent() + ) + ) + ) +} + +fun WireMockExtension.`hent-barnetrygd internal server error`(): StubMapping { + return this.stubFor( + WireMock.post(WireMock.urlPathEqualTo("/api/ekstern/pensjon/hent-barnetrygd")) + .withExpectedRequestHeadersHentBarnetryd() + .willReturn( + WireMock.serverError() + .withBody( + """ + [ + { + "status":"FUNKSJONELL_FEIL", + "melding":"Dette gikk ikke så bra" + } + ] + """.trimIndent() + ) + ) + ) +} + +private fun MappingBuilder.withExpectedRequestHeadersHentBarnetryd( + headers: List> = listOf( + CorrelationId.identifier to AnythingPattern(), + HttpHeaders.CONTENT_TYPE to WireMock.equalTo(MediaType.APPLICATION_JSON_VALUE), + HttpHeaders.ACCEPT to WireMock.equalTo(MediaType.APPLICATION_JSON_VALUE), + HttpHeaders.AUTHORIZATION to WireMock.equalTo("Bearer test.token.test"), + ) +): MappingBuilder { + headers.forEach { + this.withHeader(it.first, it.second) + } + return this +} + diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListenerTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListenerTest.kt new file mode 100644 index 0000000..b67dac5 --- /dev/null +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/kafka/BarnetrygdmottakerKafkaListenerTest.kt @@ -0,0 +1,308 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.kafka + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.SpringContextTest +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesing +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesingException +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdmottakerMessageHandler +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository.BarnetrygdInnlesingRepository +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.given +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException +import org.springframework.kafka.support.Acknowledgment +import org.springframework.test.util.ReflectionTestUtils +import java.time.Instant +import java.util.UUID +import kotlin.test.assertEquals + +class BarnetrygdmottakerKafkaListenerTest { + + @Nested + inner class ExceptionHandlingTest { + + private val handler: BarnetrygdmottakerMessageHandler = mock() + private val listener: BarnetrygdmottakerKafkaListener = BarnetrygdmottakerKafkaListener(handler) + private val ack: Acknowledgment = mock() + private val gyldigRecord: ConsumerRecord = ConsumerRecord( + BarnetrygdmottakerKafkaTopic.NAME, + 0, + 0, + null, + """ + { + "meldingstype":"DATA", + "requestId":"17de91e9-b01a-4d95-84bc-80630ded678e", + "personident":"12345" + } + """.trimIndent() + ) + private val ugyldigRecord: ConsumerRecord = ConsumerRecord( + BarnetrygdmottakerKafkaTopic.NAME, + 0, + 0, + null, + """ + { + "abc":"123" + } + """.trimIndent() + ) + + @Test + fun `gitt at innlesingen ikke eksisterer kastes det en ny exception`() { + whenever(handler.handle(any())).thenThrow(BarnetrygdInnlesingException.EksistererIkke("17de91e9-b01a-4d95-84bc-80630ded678e")) + assertThrows { + listener.poll( + gyldigRecord, + ack + ) + } + verify(handler).handle(any()) + verifyNoInteractions(ack) + } + + @Test + fun `gitt at vi mottar en melding med ukjent dataformat kastes en ny exception`() { + assertThrows { + listener.poll( + ugyldigRecord, + ack + ) + } + verifyNoInteractions(handler) + verifyNoInteractions(ack) + } + + @Test + fun `gitt at innlesingen er i ugyldig tilstand kastes en ny exception som signaliserer at innlesingen skal invalideres`() { + whenever(handler.handle(any())).thenThrow( + BarnetrygdInnlesingException.UgyldigTistand( + "17de91e9-b01a-4d95-84bc-80630ded678e", + "data" + ) + ) + assertThrows { + listener.poll( + gyldigRecord, + ack + ) + } + verify(handler).handle(any()) + verifyNoInteractions(ack) + } + + @Test + fun `gitt at det oppstår en ukjent feil ved prosessering av innlesingen kastes en ny exception som signaliserer at innlesingen skal invalideres`() { + whenever(handler.handle(any())).thenThrow( + IncorrectUpdateSemanticsDataAccessException("something weird with the db") + ) + assertThrows { + listener.poll( + gyldigRecord, + ack + ) + } + verify(handler).handle(any()) + verifyNoInteractions(ack) + } + } + + @Nested + inner class KafkaRetryHandlingTest : SpringContextTest.WithKafka() { + @MockBean + private lateinit var handler: BarnetrygdmottakerMessageHandler + + @MockBean + private lateinit var retryListener: InnlesingInvalidatingRetryListener + + @MockBean + private lateinit var innlesingRepository: BarnetrygdInnlesingRepository + + @Test + fun `gitt at meldingsformatet er ukjent, skal det ikke gjøres noe forsøk på retry`() { + val captor = argumentCaptor { + doNothing().whenever( + retryListener + ).failedDelivery( + any>(), + capture(), + any() + ) + } + sendUgyldigMeldingKafka() + + Thread.sleep(500) + + verify(retryListener).failedDelivery(any>(), any(), any()) + verify(retryListener).recovered(any>(), any()) + assertInstanceOf(KafkaMeldingDeserialiseringException::class.java, captor.allValues.single().cause) + verifyNoMoreInteractions(retryListener) + } + + @Test + fun `gitt innlesingen ikke eksisterer skal det ikke gjøres noe forsøk på retry`() { + given(handler.handle(any())).willThrow(BarnetrygdInnlesingException.EksistererIkke("17de91e9-b01a-4d95-84bc-80630ded678e")) + val captor = argumentCaptor { + doNothing().whenever( + retryListener + ).failedDelivery( + any>(), + capture(), + any() + ) + } + sendStartInnlesingKafka("17de91e9-b01a-4d95-84bc-80630ded678e") + + Thread.sleep(500) + + verify(retryListener).failedDelivery(any>(), any(), any()) + verify(retryListener).recovered(any>(), any()) + assertInstanceOf(BarnetrygdInnlesingException.EksistererIkke::class.java, captor.allValues.single().cause) + verifyNoMoreInteractions(retryListener) + } + + @Test + fun `gitt innlesingen er i ugyldig tilstand skal det ikke gjøres noe forsøk på retry`() { + given(handler.handle(any())).willThrow( + BarnetrygdInnlesingException.UgyldigTistand( + "17de91e9-b01a-4d95-84bc-80630ded678e", + "STOPP" + ) + ) + val captor = argumentCaptor { + doNothing().whenever( + retryListener + ).failedDelivery( + any>(), + capture(), + any() + ) + } + sendStartInnlesingKafka("17de91e9-b01a-4d95-84bc-80630ded678e") + + Thread.sleep(500) + + verify(retryListener).failedDelivery(any>(), any(), any()) + verify(retryListener).recovered(any>(), any()) + assertInstanceOf(InvalidateOnExceptionWrapper::class.java, captor.allValues.single().cause).also { + assertInstanceOf(BarnetrygdInnlesingException.UgyldigTistand::class.java, it.cause) + } + verifyNoMoreInteractions(retryListener) + } + + @Test + fun `gitt at det kastes en ukjent feil ved proessering skal meldingen forsøkes på nytt x antall ganger med y tids mellomrom`() { + given(handler.handle(any())).willThrow(IncorrectUpdateSemanticsDataAccessException("something weird with the db")) + val captor = argumentCaptor { + doNothing().whenever( + retryListener + ).failedDelivery( + any>(), + capture(), + any() + ) + } + sendStartInnlesingKafka("17de91e9-b01a-4d95-84bc-80630ded678e") + + Thread.sleep(3000) + + verify(retryListener, times(3)).failedDelivery(any>(), any(), any()) + verify(retryListener, times(1)).recovered(any>(), any()) + assertInstanceOf(InvalidateOnExceptionWrapper::class.java, captor.lastValue.cause).also { + assertInstanceOf(IncorrectUpdateSemanticsDataAccessException::class.java, it.cause) + } + verifyNoMoreInteractions(retryListener) + } + + @Test + fun `gitt at det kastes en ukjent feil ved proessering skal meldingen forsøkes på nytt x antall ganger med y tids mellomrom og invalideres`() { + given(handler.handle(any())).willThrow(IncorrectUpdateSemanticsDataAccessException("something weird with the db")) + given(retryListener.recovered(any>(), any())).willCallRealMethod() + ReflectionTestUtils.setField(retryListener, "innlesingRepository", innlesingRepository) + ReflectionTestUtils.setField(retryListener, "invalidated", mutableListOf()) + + sendStartInnlesingKafka("17de91e9-b01a-4d95-84bc-80630ded678e") + + Thread.sleep(3000) + + verify(retryListener, times(3)).failedDelivery(any>(), any(), any()) + verify(retryListener, times(1)).recovered(any>(), any()) + verify(innlesingRepository).invalider(UUID.fromString("17de91e9-b01a-4d95-84bc-80630ded678e")) + verifyNoMoreInteractions(retryListener) + } + } + + @Nested + inner class KafkaIntegrationTest : SpringContextTest.WithKafka() { + @Autowired + private lateinit var innlesingRepository: BarnetrygdInnlesingRepository + + @Test + fun `happy path`() { + val innlesing = innlesingRepository.bestilt( + BarnetrygdInnlesing.Bestilt( + id = InnlesingId.generate(), + 2020, + forespurtTidspunkt = Instant.now() + ) + ) + + sendStartInnlesingKafka(innlesing.id.toString()) + sendBarnetrygdmottakerDataKafka( + melding = BarnetrygdmottakerKafkaMelding( + meldingstype = BarnetrygdmottakerKafkaMelding.Type.DATA, + requestId = UUID.fromString(innlesing.id.toString()), + personident = "12345678910" + ) + ) + sendSluttInnlesingKafka(innlesing.id.toString()) + + Thread.sleep(500) + + innlesingRepository.finn(innlesing.id.toString())!!.also { + assertEquals(innlesing.id, it.id) + assertEquals(innlesing.år, 2020) + assertNotNull(it.forespurtTidspunkt) + assertNotNull(it.startTidspunkt) + assertNotNull(it.ferdigTidspunkt) + } + } + + @Test + fun `invalidering av innlesing`() { + val innlesing = innlesingRepository.bestilt( + BarnetrygdInnlesing.Bestilt( + id = InnlesingId.generate(), + år = 2020, + forespurtTidspunkt = Instant.now() + ) + ) + + sendStartInnlesingKafka(innlesing.id.toString()) + Thread.sleep(200) + assertNotNull(innlesingRepository.finn(innlesing.id.toString())) + + sendStartInnlesingKafka(innlesing.id.toString()) + Thread.sleep(200) + + assertNull(innlesingRepository.finn(innlesing.id.toString())) + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdInnlesingRepositoryTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdInnlesingRepositoryTest.kt new file mode 100644 index 0000000..be77b92 --- /dev/null +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdInnlesingRepositoryTest.kt @@ -0,0 +1,65 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.SpringContextTest +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesing +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.Barnetrygdmottaker +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.springframework.beans.factory.annotation.Autowired +import java.time.Instant +import kotlin.test.Test + +class BarnetrygdInnlesingRepositoryTest : SpringContextTest.NoKafka() { + + @Autowired + private lateinit var innlesingRepository: BarnetrygdInnlesingRepository + + @Autowired + private lateinit var barnetrygdmottakerRepository: BarnetrygdmottakerRepository + + @Test + fun `insert, update, delete`() { + val bestilt = BarnetrygdInnlesing.Bestilt( + id = InnlesingId.generate(), + år = 2023, + forespurtTidspunkt = Instant.now() + ) + val startet = bestilt.startet() + val ferdig = startet.ferdig() + + val b = innlesingRepository.bestilt(bestilt) + assertEquals(b, innlesingRepository.finn(bestilt.id.toString())) + val s = innlesingRepository.start(startet) + assertEquals(s, innlesingRepository.finn(bestilt.id.toString())) + val f = innlesingRepository.fullført(ferdig) + assertEquals(f, innlesingRepository.finn(bestilt.id.toString())) + innlesingRepository.invalider(bestilt.id.toUUID()) + assertNull(innlesingRepository.finn(bestilt.id.toString())) + } + + @Test + fun `invalidering av innlesing sletter alle barnetrygdmottakere knyttet til innlesingen`() { + val bestilt = BarnetrygdInnlesing.Bestilt( + id = InnlesingId.generate(), + år = 2023, + forespurtTidspunkt = Instant.EPOCH + ) + + val b = Barnetrygdmottaker( + ident = "12345678910", + correlationId = CorrelationId.generate(), + innlesingId = bestilt.id + ) + + val aa = innlesingRepository.bestilt(bestilt) + val bb = barnetrygdmottakerRepository.insert(b) + + assertEquals(aa, innlesingRepository.finn(bestilt.id.toString())) + assertEquals(bb, barnetrygdmottakerRepository.find(bb.id!!)) + + innlesingRepository.invalider(aa.id.toUUID()) + assertNull(barnetrygdmottakerRepository.find(bb.id!!)) + } +} diff --git a/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdmottakerRepositoryTest.kt b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdmottakerRepositoryTest.kt new file mode 100644 index 0000000..70e37bb --- /dev/null +++ b/src/test/kotlin/no/nav/pensjon/opptjening/omsorgsopptjening/start/innlesning/barnetrygd/repository/BarnetrygdmottakerRepositoryTest.kt @@ -0,0 +1,140 @@ +package no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.repository + +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.CorrelationId +import no.nav.pensjon.opptjening.omsorgsopptjening.felles.InnlesingId +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.SpringContextTest +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.BarnetrygdInnlesing +import no.nav.pensjon.opptjening.omsorgsopptjening.start.innlesning.barnetrygd.domain.Barnetrygdmottaker +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.mockito.kotlin.given +import org.mockito.kotlin.willReturnConsecutively +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.transaction.TransactionDefinition +import org.springframework.transaction.support.TransactionTemplate +import java.time.Clock +import java.time.Instant +import java.time.temporal.ChronoUnit +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class BarnetrygdmottakerRepositoryTest : SpringContextTest.NoKafka() { + + @Autowired + private lateinit var innlesingRepository: BarnetrygdInnlesingRepository + + @Autowired + private lateinit var barnetrygdmottakerRepository: BarnetrygdmottakerRepository + + @Autowired + private lateinit var transactionTemplate: TransactionTemplate + + @MockBean + private lateinit var clock: Clock + + @Test + fun `finner ingen barnetrygdmottakere som skal prosesseres før alle i forsendelsen er lest inn`() { + val innlesing = innlesingRepository.bestilt( + BarnetrygdInnlesing.Bestilt( + id = InnlesingId.generate(), + år = 2023, + forespurtTidspunkt = Instant.now(), + ) + ).let { innlesingRepository.start(it.startet()) } + + given(clock.instant()).willReturn(Instant.now()) + + barnetrygdmottakerRepository.insert( + barnetrygdmottaker = Barnetrygdmottaker( + ident = "123", + correlationId = CorrelationId.generate(), + innlesingId = innlesing.id + ) + ) + + assertNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) + + innlesingRepository.fullført(innlesing.ferdig()) + + assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) + } + + @Test + fun `barnetrygdmottakere havner i karantene i 5 timer dersom de havner i status retry`() { + val now = Instant.now() + given(clock.instant()).willReturnConsecutively( + listOf( + now, //1 + now.plus(3, ChronoUnit.HOURS), //2 + now.plus(6, ChronoUnit.HOURS), //3 + ) + ) + + val innlesing = lagreFullførtInnlesing() + + val mottaker = barnetrygdmottakerRepository.insert( + barnetrygdmottaker = Barnetrygdmottaker( + ident = "123", + correlationId = CorrelationId.generate(), + innlesingId = innlesing.id + ) + ) + + assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) //1 + + barnetrygdmottakerRepository.updateStatus(mottaker.retry("noe gikk gærnt")) + + assertNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) //2 + assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) //3 + } + + @Test + fun `finnNesteUprosesserte låser raden slik at den ikke plukkes opp av andre connections`() { + val innlesing = lagreFullførtInnlesing() + + given(clock.instant()).willReturn(Instant.now()) + + barnetrygdmottakerRepository.insert( + barnetrygdmottaker = Barnetrygdmottaker( + ident = "123", + correlationId = CorrelationId.generate(), + innlesingId = innlesing.id + ) + ) + + //krev ny transaksjon slik at det opprettes ny connection + transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW) + + transactionTemplate.execute { + //låser den aktuelle raden for denne transaksjonens varighet + Assertions.assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) + + //opprett ny transaksjon mens den forrige fortsatt lever + transactionTemplate.execute { + //skal ikke finne noe siden raden er låst pga "select for update skip locked" + Assertions.assertNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) + } + //fortsatt samme transaksjon + Assertions.assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) + } //rad ikke låst lenger ved transaksjon slutt + + + //ny transaksjon finner raden da den ikke lenger er låst + transactionTemplate.execute { + Assertions.assertNotNull(barnetrygdmottakerRepository.finnNesteUprosesserte()) + } + } + + private fun lagreFullførtInnlesing(): BarnetrygdInnlesing { + val bestilt = innlesingRepository.bestilt( + BarnetrygdInnlesing.Bestilt( + id = InnlesingId.generate(), + år = 2023, + forespurtTidspunkt = Instant.now() + ) + ) + val startet = innlesingRepository.start(bestilt.startet()) + return innlesingRepository.fullført(startet.ferdig()) + } +} \ No newline at end of file