diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/clients/oppfolging/Oppfolgingsperiode.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/clients/oppfolging/Oppfolgingsperiode.kt index 23ba8f8e..f9eb6ce1 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/clients/oppfolging/Oppfolgingsperiode.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/clients/oppfolging/Oppfolgingsperiode.kt @@ -15,3 +15,9 @@ data class Oppfolgingsperiode ( return startetIPeriode && foerSluttDato } } + +data class AvsluttetOppfolgingsperiode ( + val uuid: UUID, + val startDato: ZonedDateTime, + val sluttDato: ZonedDateTime +) diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/db/ArenaDataUpsertInput.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/db/ArenaDataUpsertInput.kt index 2875d05f..6bf2fe4c 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/db/ArenaDataUpsertInput.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/db/ArenaDataUpsertInput.kt @@ -41,8 +41,8 @@ fun ArenaKafkaMessage<*>.toUpsertInputWithStatusHandled(arenaId: Long): ArenaDat return this.toUpsertInput(arenaId.toString(), IngestStatus.HANDLED, null) } -fun ArenaKafkaMessage<*>.toUpsertInputWithStatusHandled(arenaId: DeltakelseId): ArenaDataUpsertInput { - return this.toUpsertInput(arenaId.value.toString(), IngestStatus.HANDLED, null) +fun ArenaKafkaMessage<*>.toUpsertInputWithStatusHandled(arenaId: DeltakelseId, note: String? = null): ArenaDataUpsertInput { + return this.toUpsertInput(arenaId.value.toString(), IngestStatus.HANDLED, note) } fun ArenaKafkaMessage<*>.toUpsertInputWithStatusHandled(arenaId: String): ArenaDataUpsertInput { diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetKategori.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetKategori.kt index 8b3abe42..27d0ebd4 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetKategori.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetKategori.kt @@ -1,6 +1,7 @@ package no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet -enum class AktivitetKategori { - TILTAKSAKTIVITET, UTDANNINGSAKTIVITET, GRUPPEAKTIVITET - +enum class AktivitetKategori(val prefix: String) { + TILTAKSAKTIVITET("ARENATA"), + UTDANNINGSAKTIVITET("ARENAUA"), + GRUPPEAKTIVITET("ARENAGA") } diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetskortHeaders.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetskortHeaders.kt index 0514be89..019c3636 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetskortHeaders.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/domain/kafka/aktivitet/AktivitetskortHeaders.kt @@ -10,7 +10,7 @@ import java.util.* data class AktivitetskortHeaders( val arenaId: String, val tiltakKode: String, - val oppfolgingsperiode: UUID?, + val oppfolgingsperiode: UUID, val oppfolgingsSluttDato: ZonedDateTime?, ) { diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessor.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessor.kt index 7ef75ecb..4abb34f1 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessor.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessor.kt @@ -1,7 +1,6 @@ package no.nav.arena_tiltak_aktivitet_acl.processors import no.nav.arena_tiltak_aktivitet_acl.clients.oppfolging.Oppfolgingsperiode -import no.nav.arena_tiltak_aktivitet_acl.domain.db.DeltakerAktivitetMappingDbo import no.nav.arena_tiltak_aktivitet_acl.domain.db.IngestStatus import no.nav.arena_tiltak_aktivitet_acl.domain.db.toUpsertInputWithStatusHandled import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetKategori @@ -17,7 +16,6 @@ import no.nav.arena_tiltak_aktivitet_acl.exceptions.OppfolgingsperiodeNotFoundEx import no.nav.arena_tiltak_aktivitet_acl.exceptions.OutOfOrderException import no.nav.arena_tiltak_aktivitet_acl.processors.converters.ArenaDeltakerConverter import no.nav.arena_tiltak_aktivitet_acl.repositories.ArenaDataRepository -import no.nav.arena_tiltak_aktivitet_acl.repositories.DeltakerAktivitetMappingRepository import no.nav.arena_tiltak_aktivitet_acl.repositories.GjennomforingRepository import no.nav.arena_tiltak_aktivitet_acl.services.* import no.nav.arena_tiltak_aktivitet_acl.services.OppfolgingsperiodeService.Companion.defaultSlakk @@ -35,14 +33,12 @@ import java.util.* @Component open class DeltakerProcessor( private val arenaDataRepository: ArenaDataRepository, - private val arenaIdArenaIdTilAktivitetskortIdService: ArenaIdTilAktivitetskortIdService, private val kafkaProducerService: KafkaProducerService, private val gjennomforingRepository: GjennomforingRepository, private val aktivitetService: AktivitetService, private val tiltakService: TiltakService, private val personsporingService: PersonsporingService, private val oppfolgingsperiodeService: OppfolgingsperiodeService, - private val deltakerAktivitetMappingRepository: DeltakerAktivitetMappingRepository ) : ArenaMessageProcessor { companion object { @@ -88,28 +84,18 @@ open class DeltakerProcessor( hopper vi ut her, enten med retry eller ignored, siden handleOppfolgingsperiodeNull kaster exception alltid. Dette er viktig for å ikke opprette ny aktivitetsid før vi faktisk lagrer et aktivitetskort. */ - val oppfolgingsperiodePaaEndringsTidspunkt = getOppfolgingsPeriodeOrThrow(deltakelse, personIdent) - val endring = utledEndringsType(oppfolgingsperiodePaaEndringsTidspunkt, deltakelse.tiltakdeltakelseId, arenaDeltaker.DELTAKERSTATUSKODE, tiltak.administrasjonskode) + val periodeMatch = getOppfolgingsPeriodeOrThrow(deltakelse, personIdent) + val endring = utledEndringsType(periodeMatch, deltakelse.tiltakdeltakelseId, arenaDeltaker.DELTAKERSTATUSKODE, tiltak.administrasjonskode) when (endring) { is EndringsType.NyttAktivitetskortByttPeriode -> { - secureLog.info("Endring på deltakelse ${deltakelse.tiltakdeltakelseId} på deltakerId ${deltakelse.tiltakdeltakelseId} til ny aktivitetsid ${endring.aktivitetskortId} og oppfølgingsperiode ${oppfolgingsperiodePaaEndringsTidspunkt}. " + + secureLog.info("Endring på deltakelse ${deltakelse.tiltakdeltakelseId} på deltakerId ${deltakelse.tiltakdeltakelseId} til ny aktivitetsid ${endring.aktivitetskortId} og oppfølgingsperiode ${periodeMatch.oppfolgingsperiode.uuid}. " + "Oppretter nytt aktivitetskort for personIdent $personIdent og endrer eksisterende translation entry") - endring.oppdaterMappingMedNyId(deltakelse.tiltakdeltakelseId) - arenaIdArenaIdTilAktivitetskortIdService.setCurrentAktivitetskortIdForDeltakerId(deltakelse.tiltakdeltakelseId, endring.aktivitetskortId) - } - is EndringsType.NyttAktivitetskort -> { - arenaIdArenaIdTilAktivitetskortIdService.opprettAktivitetsId(endring.aktivitetskortId, deltakelse.tiltakdeltakelseId, AktivitetKategori.TILTAKSAKTIVITET) - endring.oppdaterMappingMedNyId(deltakelse.tiltakdeltakelseId) + syncOppfolgingsperioder(deltakelse.tiltakdeltakelseId, periodeMatch.allePerioder) } + is EndringsType.NyttAktivitetskort -> {} is EndringsType.OppdaterAktivitet -> {} } - if (endring.skalIgnoreres) { - log.info("Deltakeren har status=${arenaDeltaker.DELTAKERSTATUSKODE} og administrasjonskode=${tiltak.administrasjonskode} som ikke skal håndteres") - arenaDataRepository.upsert(message.toUpsertInputWithStatusHandled(deltakelse.tiltakdeltakelseId)) - return - } - val aktivitet = ArenaDeltakerConverter .convertToTiltaksaktivitet( deltaker = deltakelse, @@ -122,13 +108,19 @@ open class DeltakerProcessor( val aktivitetskortHeaders = AktivitetskortHeaders( arenaId = "${KafkaProducerService.TILTAK_ID_PREFIX}${deltakelse.tiltakdeltakelseId}", tiltakKode = tiltak.kode, - oppfolgingsperiode = oppfolgingsperiodePaaEndringsTidspunkt.uuid, - oppfolgingsSluttDato = oppfolgingsperiodePaaEndringsTidspunkt.sluttDato + oppfolgingsperiode = periodeMatch.oppfolgingsperiode.uuid, + oppfolgingsSluttDato = periodeMatch.oppfolgingsperiode.sluttDato ) - val outgoingMessage = aktivitet.toKafkaMessage() aktivitetService.upsert(aktivitet, aktivitetskortHeaders) - arenaDataRepository.upsert(message.toUpsertInputWithStatusHandled(deltakelse.tiltakdeltakelseId)) + if (endring.skalIgnoreres) { + log.info("Deltakeren har status=${arenaDeltaker.DELTAKERSTATUSKODE} og administrasjonskode=${tiltak.administrasjonskode} som ikke skal håndteres") + arenaDataRepository.upsert(message.toUpsertInputWithStatusHandled(deltakelse.tiltakdeltakelseId, "foreløpig ignorert")) + return + } + + arenaDataRepository.upsert(message.toUpsertInputWithStatusHandled(deltakelse.tiltakdeltakelseId)) + val outgoingMessage = aktivitet.toKafkaMessage() secureLog.info("Sender melding for aktivitetskort id=${endring.aktivitetskortId} arenaId=${deltakelse.tiltakdeltakelseId} personId=${deltakelse.personId} fnr=$personIdent") log.info("Sender medling messageId=${outgoingMessage.messageId} aktivitetskort id=$endring.aktivitetskortId arenaId=${deltakelse.tiltakdeltakelseId} type=${outgoingMessage.actionType}") kafkaProducerService.sendTilAktivitetskortTopic( @@ -158,42 +150,41 @@ open class DeltakerProcessor( } } - private fun getOppfolgingsPeriodeOrThrow(deltaker: TiltakDeltakelse, personIdent: String): Oppfolgingsperiode { - return deltaker.modDato?.let { modDato -> oppfolgingsperiodeService.finnOppfolgingsperiode(personIdent, modDato) } + private fun getOppfolgingsPeriodeOrThrow(deltaker: TiltakDeltakelse, personIdent: String): FinnOppfolgingResult.FunnetPeriodeResult { + val funnetPeriode = deltaker.modDato + ?.let { modDato -> oppfolgingsperiodeService.finnOppfolgingsperiode(personIdent, modDato) } ?: oppfolgingsperiodeService.finnOppfolgingsperiode(personIdent, deltaker.regDato) .also { log.info("arenaId: ${deltaker.tiltakdeltakelseId} Fant ikke oppfolgingsperiode på modDato, bruker fallback til regDato") } - ?: handleOppfolgingsperiodeNull(deltaker, personIdent, deltaker.modDato ?: deltaker.regDato, deltaker.tiltakdeltakelseId) + return when (funnetPeriode) { + is FinnOppfolgingResult.FunnetPeriodeResult -> funnetPeriode + is FinnOppfolgingResult.IngenPeriodeResult -> handleOppfolgingsperiodeNull(deltaker, personIdent, deltaker.modDato ?: deltaker.regDato, deltaker.tiltakdeltakelseId) + } } - private fun utledEndringsType(oppfolgingsperiode: Oppfolgingsperiode, deltakelseId: DeltakelseId, deltakerStatusKode: String, administrasjonskode: Tiltak.Administrasjonskode): EndringsType { + private fun utledEndringsType( + periodeMatch: FinnOppfolgingResult.FunnetPeriodeResult, + deltakelseId: DeltakelseId, + deltakerStatusKode: String, + administrasjonskode: Tiltak.Administrasjonskode, + ): EndringsType { val skalIgnoreres = skalIgnoreres(deltakerStatusKode, administrasjonskode) - val oppfolgingsperiodeTilAktivitetskortId = deltakerAktivitetMappingRepository.get(deltakelseId, AktivitetKategori.TILTAKSAKTIVITET) + val oppfolgingsperiodeTilAktivitetskortId = aktivitetService.getAllBy(deltakelseId, AktivitetKategori.TILTAKSAKTIVITET) val eksisterendeAktivitetsId = oppfolgingsperiodeTilAktivitetskortId - .firstOrNull { it.oppfolgingsperiodeUuid == oppfolgingsperiode.uuid }?.aktivitetId + .firstOrNull { it.oppfolgingsPeriode == periodeMatch.oppfolgingsperiode.uuid }?.id return when { // Har tidligere deltakelse på samme oppfolgingsperiode eksisterendeAktivitetsId != null -> EndringsType.OppdaterAktivitet(eksisterendeAktivitetsId, skalIgnoreres) // Har ingen tidligere aktivitetskort - oppfolgingsperiodeTilAktivitetskortId.isEmpty() -> EndringsType.NyttAktivitetskort(oppfolgingsperiode, skalIgnoreres) + oppfolgingsperiodeTilAktivitetskortId.isEmpty() -> EndringsType.NyttAktivitetskort(periodeMatch.oppfolgingsperiode, skalIgnoreres) // Har tidligere deltakelse men ikke på samme oppfølgingsperiode - else -> EndringsType.NyttAktivitetskortByttPeriode(oppfolgingsperiode, skalIgnoreres) + else -> { + EndringsType.NyttAktivitetskortByttPeriode(periodeMatch.oppfolgingsperiode, skalIgnoreres) + } } } - fun EndringsType.oppdaterMappingMedNyId(deltakelseId: DeltakelseId) { - when (this) { - is EndringsType.NyttAktivitetskort -> this.oppfolgingsperiode - is EndringsType.NyttAktivitetskortByttPeriode -> this.oppfolgingsperiode - is EndringsType.OppdaterAktivitet -> null - }?.let { - deltakerAktivitetMappingRepository.insert( - DeltakerAktivitetMappingDbo( - deltakelseId = deltakelseId, - aktivitetId = this.aktivitetskortId, - aktivitetKategori = AktivitetKategori.TILTAKSAKTIVITET, - oppfolgingsperiodeUuid = it.uuid) - ) - } + fun syncOppfolgingsperioder(deltakelseId: DeltakelseId, oppfolginsperioder: List) { + aktivitetService.closeClosedPerioder(deltakelseId, AktivitetKategori.TILTAKSAKTIVITET, oppfolginsperioder) } } diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetDbo.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetDbo.kt index aea741e7..f3001dc3 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetDbo.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetDbo.kt @@ -11,6 +11,6 @@ data class AktivitetDbo ( val data: String, val arenaId: String, val tiltakKode: String, - val oppfolgingsperiodeUUID: UUID?, + val oppfolgingsperiodeUUID: UUID, val oppfolgingsSluttTidspunkt: ZonedDateTime? ) diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetRepository.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetRepository.kt index 50f1f925..9179dc4d 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetRepository.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/AktivitetRepository.kt @@ -1,8 +1,12 @@ package no.nav.arena_tiltak_aktivitet_acl.repositories +import no.nav.arena_tiltak_aktivitet_acl.clients.oppfolging.AvsluttetOppfolgingsperiode import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetKategori +import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId import no.nav.arena_tiltak_aktivitet_acl.utils.* import org.intellij.lang.annotations.Language +import org.slf4j.LoggerFactory +import org.springframework.dao.IncorrectResultSizeDataAccessException import org.springframework.jdbc.core.RowMapper import org.springframework.jdbc.core.namedparam.MapSqlParameterSource import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate @@ -14,7 +18,9 @@ import java.util.* open class AktivitetRepository( private val template: NamedParameterJdbcTemplate ) { + private val log = LoggerFactory.getLogger(javaClass) fun upsert(aktivitet: AktivitetDbo) { + @Language("PostgreSQL") val sql = """ INSERT INTO aktivitet(id, person_ident, kategori_type, data, arena_id, tiltak_kode, oppfolgingsperiode_uuid, oppfolgingsperiode_slutt_tidspunkt) VALUES (:id, @@ -26,7 +32,9 @@ open class AktivitetRepository( :oppfolgingsperiode_uuid, :oppfolgingsperiode_slutt_tidspunkt) ON CONFLICT ON CONSTRAINT aktivitet_pkey - DO UPDATE SET data = :data::jsonb + DO UPDATE SET data = :data::jsonb, + oppfolgingsperiode_slutt_tidspunkt = :oppfolgingsperiode_slutt_tidspunkt, + oppfolgingsperiode_uuid = :oppfolgingsperiode_uuid """.trimIndent() val parameters = MapSqlParameterSource().addValues( @@ -56,6 +64,55 @@ open class AktivitetRepository( return template.query(sql, parameters, rowMapper).firstOrNull() } + + fun getCurrentAktivitetsId(deltakelseId: DeltakelseId, aktivitetKategori: AktivitetKategori): UUID? { + @Language("PostgreSQL") + val sql = """ + SELECT DISTINCT ON (arena_id) + arena_id, + aktivitet.id, + COALESCE(aktivitet.oppfolgingsperiode_slutt_tidspunkt, TO_TIMESTAMP('9999', 'YYYY')) slutt + FROM aktivitet + WHERE arena_id = :arenaId + ORDER BY arena_id, slutt DESC + """.trimIndent() + val parameters = mapOf("arenaId" to "${aktivitetKategori.prefix}${deltakelseId.value}") + return template.query(sql, parameters) { row, _ -> row.getUUID("id") } + .also { + if (it.size > 1) { + log.error("Got multiple results on currently active aktivitetskort: ${it.size}, deltakerId: ${deltakelseId.value}") + throw IncorrectResultSizeDataAccessException(1, it.size) + } + } + .firstOrNull() + } + + fun getAllBy(deltakelseId: DeltakelseId, aktivitetKategori: AktivitetKategori): List { + @Language("PostgreSQL") + val sql = """ + SELECT oppfolgingsperiode_uuid as oppfolgingsPeriode, id FROM aktivitet WHERE arena_id = :arenaId + """.trimIndent() + val params = mapOf("arenaId" to "${aktivitetKategori.prefix}${deltakelseId.value}") + return template.query(sql, params) { row, _ -> + AktivitetIdAndOppfolgingsPeriode(row.getUUID("id"), row.getUUID("oppfolgingsPeriode")) } + } + + fun closeClosedPerioder(deltakelseId: DeltakelseId, aktivitetKategori: AktivitetKategori, oppfolgingsperioder: List) { + @Language("PostgreSQL") + val sql = """ + UPDATE aktivitet SET oppfolgingsperiode_slutt_tidspunkt = :slutt + WHERE arena_id = :arenaId and oppfolgingsperiode_uuid = :oppfolgingsperiode + """.trimIndent() + val params = oppfolgingsperioder + .map { + mapOf( + "arenaId" to "${aktivitetKategori.prefix}${deltakelseId.value}", + "slutt" to it.sluttDato.toOffsetDateTime(), + "oppfolgingsperiode" to it.uuid + ) + }.toTypedArray() + template.batchUpdate(sql, params) + } } fun ResultSet.toAktivitetDbo() = @@ -66,6 +123,11 @@ fun ResultSet.toAktivitetDbo() = data = this.getString("data"), arenaId = this.getString("arena_id"), tiltakKode = this.getString("tiltak_kode"), - oppfolgingsperiodeUUID = this.getNullableUUID("oppfolgingsperiode_uuid"), + oppfolgingsperiodeUUID = this.getUUID("oppfolgingsperiode_uuid"), oppfolgingsSluttTidspunkt = this.getNullableZonedDateTime("oppfolgingsperiode_slutt_tidspunkt"), ) + +data class AktivitetIdAndOppfolgingsPeriode( + val id: UUID, + val oppfolgingsPeriode: UUID +) diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/ArenaIdTilAktivitetskortIdRepository.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/ArenaIdTilAktivitetskortIdRepository.kt deleted file mode 100644 index 24f31a68..00000000 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/ArenaIdTilAktivitetskortIdRepository.kt +++ /dev/null @@ -1,78 +0,0 @@ -package no.nav.arena_tiltak_aktivitet_acl.repositories - -import no.nav.arena_tiltak_aktivitet_acl.domain.db.TranslationDbo -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetKategori -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId -import no.nav.arena_tiltak_aktivitet_acl.utils.DatabaseUtils.sqlParameters -import no.nav.arena_tiltak_aktivitet_acl.utils.getUUID -import org.springframework.dao.DuplicateKeyException -import org.springframework.jdbc.core.RowMapper -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate -import org.springframework.stereotype.Component -import java.util.* - -@Component -open class ArenaIdTilAktivitetskortIdRepository( - private val template: NamedParameterJdbcTemplate -) { - - private val rowMapper = RowMapper { rs, _ -> - TranslationDbo( - aktivitetId = rs.getUUID("aktivitet_id"), - arenaId = DeltakelseId(rs.getLong("arena_id")), - aktivitetKategori = AktivitetKategori.valueOf(rs.getString("aktivitet_kategori")) - ) - } - - fun insert(entry: TranslationDbo) { - val sql = """ - INSERT INTO translation(aktivitet_id, arena_id, aktivitet_kategori) - VALUES (:aktivitet_id, :arena_id, :aktivitet_kategori) - """.trimIndent() - - try { - template.update(sql, entry.asParameterSource()) - } catch (e: DuplicateKeyException) { - throw IllegalStateException("Translation entry on table with id ${entry.arenaId} already exist.") - } - } - - /* - aktivitet_id er primærnøkkel, men har ingen fremmednøkler knyttet til seg, så update går fint. - */ - fun updateAktivitetId(arenaId: DeltakelseId, newAktivitetId: UUID) { - val sql = """ - UPDATE translation - SET aktivitet_id = :newAktivitetId where arena_id = :arenaId - """.trimIndent() - val parameters = sqlParameters( - "arenaId" to arenaId.value, - "newAktivitetId" to newAktivitetId) - template.update(sql, parameters) - } - - fun get(arenaId: DeltakelseId, aktivitetKategori: AktivitetKategori): TranslationDbo? { - val sql = """ - SELECT * - FROM translation - WHERE arena_id = :arena_id - AND aktivitet_kategori = :aktivitet_kategori - """.trimIndent() - - val parameters = sqlParameters( - "arena_id" to arenaId.value, - "aktivitet_kategori" to aktivitetKategori.name - ) - - return template.query(sql, parameters, rowMapper) - .firstOrNull() - } - - private fun TranslationDbo.asParameterSource() = sqlParameters( - "aktivitet_id" to aktivitetId, - "arena_id" to arenaId.value, - "aktivitet_kategori" to aktivitetKategori.name - ) - -} - diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/DeltakerAktivitetMappingRepository.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/DeltakerAktivitetMappingRepository.kt deleted file mode 100644 index fece5ca6..00000000 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/DeltakerAktivitetMappingRepository.kt +++ /dev/null @@ -1,66 +0,0 @@ -package no.nav.arena_tiltak_aktivitet_acl.repositories - -import no.nav.arena_tiltak_aktivitet_acl.domain.db.DeltakerAktivitetMappingDbo -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetKategori -import no.nav.arena_tiltak_aktivitet_acl.utils.DatabaseUtils.sqlParameters -import no.nav.arena_tiltak_aktivitet_acl.utils.getUUID -import org.springframework.dao.DuplicateKeyException -import org.springframework.jdbc.core.RowMapper -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate -import org.springframework.stereotype.Component -import java.util.* - -@Component -open class DeltakerAktivitetMappingRepository( - private val template: NamedParameterJdbcTemplate -) { - - private val rowMapper = RowMapper { rs, _ -> - DeltakerAktivitetMappingDbo( - deltakelseId = DeltakelseId(rs.getLong("deltaker_id")), - aktivitetId = rs.getUUID("aktivitet_id"), - aktivitetKategori = AktivitetKategori.valueOf(rs.getString("aktivitet_kategori")), - oppfolgingsperiodeUuid = rs.getUUID("oppfolgingsperiode_uuid"), - ) - } - - fun insert(entry: DeltakerAktivitetMappingDbo) { - val sql = """ - INSERT INTO deltaker_aktivitet_mapping(deltaker_id, aktivitet_id, aktivitet_kategori, oppfolgingsperiode_uuid) - VALUES (:deltaker_id, :aktivitet_id, :aktivitet_kategori, :oppfolgingsperiode_uuid) - """.trimIndent() - - try { - template.update(sql, entry.asParameterSource()) - } catch (e: DuplicateKeyException) { - throw IllegalStateException("DeltakerAktivitetMapping entry on table with deltaker_id=${entry.deltakelseId}, aktivitet_id=${entry.aktivitetId}, aktivitet_kategori=${entry.aktivitetKategori} oppfolgingsperiode_uuid=${entry.oppfolgingsperiodeUuid} already exist.") - } - } - - - fun get(deltakelseId: DeltakelseId, kategori: AktivitetKategori): List { - val sql = """ - SELECT * - FROM deltaker_aktivitet_mapping - WHERE deltaker_id = :deltaker_id and aktivitet_kategori = :aktivitet_kategori - """.trimIndent() - - val parameters = sqlParameters( - "deltaker_id" to deltakelseId.value, - "aktivitet_kategori" to kategori.name - ) - return template.query(sql, parameters, rowMapper) - } - - private fun DeltakerAktivitetMappingDbo.asParameterSource() = sqlParameters( - "deltaker_id" to deltakelseId.value, - "aktivitet_id" to aktivitetId, - "aktivitet_kategori" to aktivitetKategori.name, - "oppfolgingsperiode_uuid" to oppfolgingsperiodeUuid - ) - -} - -typealias OppfolginsPeriodeId = UUID - diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/rest/TranslationController.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/rest/TranslationController.kt index 71efd3fc..f387ac68 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/rest/TranslationController.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/rest/TranslationController.kt @@ -10,7 +10,7 @@ import no.nav.arena_tiltak_aktivitet_acl.auth.AuthService import no.nav.arena_tiltak_aktivitet_acl.auth.Issuer import no.nav.arena_tiltak_aktivitet_acl.domain.dto.TranslationQuery import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId -import no.nav.arena_tiltak_aktivitet_acl.services.ArenaIdTilAktivitetskortIdService +import no.nav.arena_tiltak_aktivitet_acl.repositories.AktivitetRepository import no.nav.security.token.support.core.api.Protected import no.nav.security.token.support.core.api.ProtectedWithClaims import org.springframework.http.HttpStatus @@ -28,7 +28,7 @@ import java.util.* @RequestMapping("/api/translation") class TranslationController( private val authService: AuthService, - private val arenaIdTilAktivitetskortIdService: ArenaIdTilAktivitetskortIdService + private val aktivitetRepository: AktivitetRepository, ) { @ProtectedWithClaims(issuer = Issuer.AZURE_AD) @@ -43,7 +43,7 @@ class TranslationController( @RequestBody query: TranslationQuery ): UUID { authService.validerErM2MToken() - return arenaIdTilAktivitetskortIdService.hentAktivitetIdForArenaId(DeltakelseId(query.arenaId), query.aktivitetKategori) + return aktivitetRepository.getCurrentAktivitetsId(DeltakelseId(query.arenaId), query.aktivitetKategori) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "No mapping found") } } diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/AktivitetService.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/AktivitetService.kt index bf20ebaf..5875b70d 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/AktivitetService.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/AktivitetService.kt @@ -1,7 +1,11 @@ package no.nav.arena_tiltak_aktivitet_acl.services +import no.nav.arena_tiltak_aktivitet_acl.clients.oppfolging.AvsluttetOppfolgingsperiode +import no.nav.arena_tiltak_aktivitet_acl.clients.oppfolging.Oppfolgingsperiode +import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetKategori import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.Aktivitetskort import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetskortHeaders +import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId import no.nav.arena_tiltak_aktivitet_acl.repositories.AktivitetRepository import org.springframework.stereotype.Service import java.util.UUID @@ -12,4 +16,15 @@ class AktivitetService( ) { fun upsert(aktivitet: Aktivitetskort, headers: AktivitetskortHeaders) = aktivitetRepository.upsert(aktivitet.toDbo(headers)) fun get(aktivitetId: UUID) = aktivitetRepository.getAktivitet(aktivitetId) + fun getAllBy(aktivitetId: DeltakelseId, aktivitetsKategori: AktivitetKategori) = + aktivitetRepository.getAllBy(aktivitetId, aktivitetsKategori) + + fun closeClosedPerioder(deltakelseId: DeltakelseId, aktivitetKategori: AktivitetKategori, oppfolgingsperioder: List) { + val avsluttedePerioder = oppfolgingsperioder + .mapNotNull { + it.sluttDato + ?.let { slutt -> AvsluttetOppfolgingsperiode(it.uuid, it.startDato, slutt) } + } + aktivitetRepository.closeClosedPerioder(deltakelseId, aktivitetKategori, avsluttedePerioder) + } } diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/ArenaIdTilAktivitetskortIdService.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/ArenaIdTilAktivitetskortIdService.kt deleted file mode 100644 index 49ca8765..00000000 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/ArenaIdTilAktivitetskortIdService.kt +++ /dev/null @@ -1,46 +0,0 @@ -package no.nav.arena_tiltak_aktivitet_acl.services - -import no.nav.arena_tiltak_aktivitet_acl.domain.db.TranslationDbo -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetKategori -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId -import no.nav.arena_tiltak_aktivitet_acl.repositories.ArenaIdTilAktivitetskortIdRepository -import org.slf4j.LoggerFactory -import org.springframework.stereotype.Service -import java.util.* - -@Service -open class ArenaIdTilAktivitetskortIdService( - private val arenaIdTilAktivitetskortIdRepository: ArenaIdTilAktivitetskortIdRepository -) { - private val log = LoggerFactory.getLogger(javaClass) - - fun setCurrentAktivitetskortIdForDeltakerId(deltakelseId: DeltakelseId, newAktivitetId: UUID) { - log.info("Oppdaterer gjeldende aktivitetsId på deltakerId: $deltakelseId til $newAktivitetId") - arenaIdTilAktivitetskortIdRepository.updateAktivitetId(deltakelseId, newAktivitetId) - } - - fun hentAktivitetIdForArenaId(arenaId: DeltakelseId, aktivitetType: AktivitetKategori): UUID? { - return arenaIdTilAktivitetskortIdRepository.get(arenaId, aktivitetType)?.aktivitetId - } - - private fun insertTranslation(arenaId: DeltakelseId, aktivitetId: UUID, kategori: AktivitetKategori) { - val translation = TranslationDbo( - aktivitetId = aktivitetId, - arenaId = arenaId, - aktivitetKategori = kategori - ) - - arenaIdTilAktivitetskortIdRepository.insert(translation) - } - - fun opprettAktivitetsId(nyAktivitetsId: UUID, deltakerArenaId: DeltakelseId, aktivitetType: AktivitetKategori): UUID { - log.info("Opprettet ny aktivitetsid for deltakelse, aktivitetsId=$nyAktivitetsId deltakerId=$deltakerArenaId") - insertTranslation( - deltakerArenaId, - nyAktivitetsId, - aktivitetType - ) - return nyAktivitetsId - } - -} diff --git a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeService.kt b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeService.kt index c264a5d7..5e2545a4 100644 --- a/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeService.kt +++ b/src/main/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeService.kt @@ -27,23 +27,27 @@ open class OppfolgingsperiodeService( val defaultSlakk = Duration.of(7, ChronoUnit.DAYS) } - - fun finnOppfolgingsperiode(fnr: String, tidspunkt: LocalDateTime): Oppfolgingsperiode? { - val oppfolgingsperioder = oppfolgingClient.hentOppfolgingsperioder(fnr) + fun hentAlleOppfolgingsperioder(fnr: String): List { + return oppfolgingClient.hentOppfolgingsperioder(fnr) .sortedByDescending { it.startDato } + } + + fun finnOppfolgingsperiode(fnr: String, tidspunkt: LocalDateTime): FinnOppfolgingResult { + val oppfolgingsperioder = hentAlleOppfolgingsperioder(fnr) if (oppfolgingsperioder.isEmpty()) { secureLog.info( "Arenatiltak finn oppfølgingsperiode - bruker har ingen oppfølgingsperioder - fnr={}, tidspunkt={}, oppfolgingsperioder={}", fnr, tidspunkt, listOf() ) - return null + return FinnOppfolgingResult.IngenPeriodeResult( emptyList()) } val tidspunktCZDT = ChronoZonedDateTime.from(tidspunkt.atZone(ZoneId.systemDefault())) val oppfolgingsperiode = oppfolgingsperioder .find {periode -> periode.tidspunktInnenforPeriode(tidspunktCZDT) } + if (oppfolgingsperiode != null) return FinnOppfolgingResult.FunnetPeriodeResult(oppfolgingsperiode, oppfolgingsperioder) - return oppfolgingsperiode ?: oppfolgingsperioder + return oppfolgingsperioder .filter { it.sluttDato == null || it.sluttDato.isAfter(tidspunktCZDT) } .minByOrNull { abs(ChronoUnit.MILLIS.between(tidspunktCZDT, it.startDato)) } .let { periodeMatch -> @@ -61,5 +65,14 @@ open class OppfolgingsperiodeService( periodeMatch } } + ?.let { FinnOppfolgingResult.FunnetPeriodeResult(it, oppfolgingsperioder) } + ?: FinnOppfolgingResult.IngenPeriodeResult(emptyList()) } } + +sealed class FinnOppfolgingResult( + val allePerioder: List +) { + class IngenPeriodeResult(allePerioder: List): FinnOppfolgingResult(allePerioder) + class FunnetPeriodeResult(val oppfolgingsperiode: Oppfolgingsperiode, allePerioder: List): FinnOppfolgingResult(allePerioder) +} diff --git a/src/main/resources/db/migration/V12__aktivitet_arenaid_ix.sql b/src/main/resources/db/migration/V12__aktivitet_arenaid_ix.sql new file mode 100644 index 00000000..cf363f54 --- /dev/null +++ b/src/main/resources/db/migration/V12__aktivitet_arenaid_ix.sql @@ -0,0 +1,2 @@ +CREATE INDEX IF NOT EXISTS + aktivitet_arena_id ON aktivitet (arena_id); \ No newline at end of file diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/AktivitetRepositoryIntegrationTest.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/AktivitetRepositoryIntegrationTest.kt index db3809ec..048afae2 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/AktivitetRepositoryIntegrationTest.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/AktivitetRepositoryIntegrationTest.kt @@ -8,6 +8,7 @@ import no.nav.arena_tiltak_aktivitet_acl.repositories.AktivitetDbo import no.nav.arena_tiltak_aktivitet_acl.repositories.AktivitetRepository import org.intellij.lang.annotations.Language import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate +import java.time.ZonedDateTime import java.util.* class AktivitetRepositoryIntegrationTest: StringSpec({ @@ -24,7 +25,7 @@ class AktivitetRepositoryIntegrationTest: StringSpec({ data = "{}", arenaId = "ARENATA-111", tiltakKode = "MIDLONNTIL", - oppfolgingsperiodeUUID = null, + oppfolgingsperiodeUUID = UUID.randomUUID(), oppfolgingsSluttTidspunkt = null, ) repository.upsert(aktivitet) @@ -37,9 +38,9 @@ class AktivitetRepositoryIntegrationTest: StringSpec({ personIdent = "123123123", kategori = AktivitetKategori.TILTAKSAKTIVITET, data = "{}", - arenaId = "ARENATA-111", + arenaId = "ARENATA-112", tiltakKode = "MIDLONNTIL", - oppfolgingsperiodeUUID = null, + oppfolgingsperiodeUUID = UUID.randomUUID(), oppfolgingsSluttTidspunkt = null, ) @@ -47,6 +48,43 @@ class AktivitetRepositoryIntegrationTest: StringSpec({ repository.upsert(aktivitet) } +/* "upsert should throw on multiple open perioder"() { + val aktivitet = AktivitetDbo( + id = UUID.randomUUID(), + personIdent = "123123123", + kategori = AktivitetKategori.TILTAKSAKTIVITET, + data = "{}", + arenaId = "ARENATA-114", + tiltakKode = "MIDLONNTIL", + oppfolgingsperiodeUUID = UUID.randomUUID(), + oppfolgingsSluttTidspunkt = null) + val nyAktivitetskortSammeDeltakelse = aktivitet.copy(id = UUID.randomUUID()) + repository.upsert(aktivitet) + shouldThrow { + repository.upsert(nyAktivitetskortSammeDeltakelse) + } + } + + */ + + "upsert should not throw on same arenaId" { + val aktivitet = AktivitetDbo( + id = UUID.randomUUID(), + personIdent = "123123123", + kategori = AktivitetKategori.TILTAKSAKTIVITET, + data = "{}", + arenaId = "ARENATA-116", + tiltakKode = "MIDLONNTIL", + oppfolgingsperiodeUUID = UUID.randomUUID(), + oppfolgingsSluttTidspunkt = ZonedDateTime.now().minusDays(2)) + val nyAktivitetskortForskjelligPeriode = aktivitet.copy( + id = UUID.randomUUID(), + oppfolgingsperiodeUUID = UUID.randomUUID(), + oppfolgingsSluttTidspunkt = null) + repository.upsert(aktivitet) + repository.upsert(nyAktivitetskortForskjelligPeriode) + } + "upsert should update data on duplicate key" { val id = UUID.randomUUID() val aktivitet = AktivitetDbo( @@ -54,9 +92,9 @@ class AktivitetRepositoryIntegrationTest: StringSpec({ personIdent = "123123123", kategori = AktivitetKategori.TILTAKSAKTIVITET, data = "{}", - arenaId = "ARENATA-111", + arenaId = "ARENATA-113", tiltakKode = "MIDLONNTIL", - oppfolgingsperiodeUUID = null, + oppfolgingsperiodeUUID = UUID.randomUUID(), oppfolgingsSluttTidspunkt = null, ) @Language("JSON") diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/DeltakerIntegrationTests.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/DeltakerIntegrationTests.kt index dceff468..26876089 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/DeltakerIntegrationTests.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/DeltakerIntegrationTests.kt @@ -9,6 +9,7 @@ import io.kotest.matchers.string.shouldMatch import no.nav.arena_tiltak_aktivitet_acl.clients.IdMappingClient import no.nav.arena_tiltak_aktivitet_acl.clients.oppfolging.Oppfolgingsperiode import no.nav.arena_tiltak_aktivitet_acl.domain.db.IngestStatus +import no.nav.arena_tiltak_aktivitet_acl.domain.db.TranslationDbo import no.nav.arena_tiltak_aktivitet_acl.domain.dto.TranslationQuery import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.* import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId @@ -28,7 +29,6 @@ import no.nav.arena_tiltak_aktivitet_acl.processors.converters.ArenaDeltakerConv import no.nav.arena_tiltak_aktivitet_acl.processors.converters.ArenaDeltakerConverter.JOBBKLUBB import no.nav.arena_tiltak_aktivitet_acl.repositories.AktivitetRepository import no.nav.arena_tiltak_aktivitet_acl.repositories.ArenaDataRepository -import no.nav.arena_tiltak_aktivitet_acl.repositories.ArenaIdTilAktivitetskortIdRepository import no.nav.arena_tiltak_aktivitet_acl.repositories.TiltakDbo import no.nav.arena_tiltak_aktivitet_acl.services.KafkaProducerService import no.nav.arena_tiltak_aktivitet_acl.services.KafkaProducerService.Companion.TILTAK_ID_PREFIX @@ -58,9 +58,6 @@ class DeltakerIntegrationTests : IntegrationTestBase() { @Autowired lateinit var arenaDataRepository: ArenaDataRepository - @Autowired - lateinit var arenaIdTilAktivitetskortIdRepository: ArenaIdTilAktivitetskortIdRepository - @SpyBean lateinit var kafkaProducerService: KafkaProducerService @@ -104,7 +101,7 @@ class DeltakerIntegrationTests : IntegrationTestBase() { handledResult.aktivitetskort { aktivitetId = it.id } - handledResult.deltakerAktivitetMapping.any { mapping -> mapping.aktivitetId == aktivitetId} shouldBe true + handledResult.deltakerAktivitetMapping.any { mapping -> mapping.id == aktivitetId} shouldBe true } val translation = hentTranslationMedRestClient(deltakerId) @@ -145,7 +142,7 @@ class DeltakerIntegrationTests : IntegrationTestBase() { result.expectHandled { it.output { it.actionType shouldBe ActionType.UPSERT_AKTIVITETSKORT_V1 } - it.deltakerAktivitetMapping.any { mapping -> mapping.aktivitetId == it.output.aktivitetskort.id} shouldBe true + it.deltakerAktivitetMapping.any { mapping -> mapping.id == it.output.aktivitetskort.id} shouldBe true it.aktivitetskort { it.isSame(deltakerInput, tiltak, gjennomforingInput) } it.headers.tiltakKode shouldBe gjennomforingInput.tiltakKode it.headers.arenaId shouldBe TILTAK_ID_PREFIX + deltakerInput.tiltakDeltakelseId @@ -233,7 +230,7 @@ class DeltakerIntegrationTests : IntegrationTestBase() { // Cron-job processMessages() - val aktivitetId = arenaIdTilAktivitetskortIdRepository.get(deltakerId, AktivitetKategori.TILTAKSAKTIVITET)?.aktivitetId + val aktivitetId = idMappingClient.hentMapping(TranslationQuery(deltakerId.value, AktivitetKategori.TILTAKSAKTIVITET)).second aktivitetId shouldNotBe null val mapper = ObjectMapper.get() @@ -291,7 +288,7 @@ class DeltakerIntegrationTests : IntegrationTestBase() { // Cron-job processFailedMessages() - val aktivitetId = arenaIdTilAktivitetskortIdRepository.get(deltakerId, AktivitetKategori.TILTAKSAKTIVITET)?.aktivitetId!! + val aktivitetId = idMappingClient.hentMapping(TranslationQuery(deltakerId.value, AktivitetKategori.TILTAKSAKTIVITET)).second!! fun String.toAktivitetskort() = ObjectMapper.get().readValue(this, Aktivitetskort::class.java) @@ -333,13 +330,13 @@ class DeltakerIntegrationTests : IntegrationTestBase() { result.expectHandled { r -> r.output { it.actionType shouldBe ActionType.UPSERT_AKTIVITETSKORT_V1 } - r.deltakerAktivitetMapping.any { mapping -> mapping.aktivitetId == r.output.aktivitetskort.id } shouldBe true + r.deltakerAktivitetMapping.any { mapping -> mapping.id == r.output.aktivitetskort.id } shouldBe true r.aktivitetskort { it.isSame(deltakerInput, tiltak, gjennomforingInput) } } updatedResult.expectHandled { r -> r.output { it.actionType shouldBe ActionType.UPSERT_AKTIVITETSKORT_V1 } - r.deltakerAktivitetMapping.any { mapping -> mapping.aktivitetId == r.output.aktivitetskort.id } shouldBe true + r.deltakerAktivitetMapping.any { mapping -> mapping.id == r.output.aktivitetskort.id } shouldBe true r.aktivitetskort { it.isSame(deltakerInputUpdated, tiltak, gjennomforingInput) } } } @@ -679,7 +676,7 @@ class DeltakerIntegrationTests : IntegrationTestBase() { } @Test - fun `skal takle tidligere ignorerte deltakelser uten å krasje på duplicate key i arenaId til aktivitetId mapping`() { + fun `skal lage id-mapping på tidligere ignorerte deltakelser men ikke publisere aktivitetskort før ikke-ignorert tilstand`() { val (gjennomforingId, deltakelseId, _) = setup(Tiltak.Administrasjonskode.INST) val deltakerInputIgnored = DeltakerInput( tiltakDeltakelseId = deltakelseId, @@ -691,6 +688,9 @@ class DeltakerIntegrationTests : IntegrationTestBase() { val deltakerCommandIgnored = NyDeltakerCommand(deltakerInputIgnored) val aktivitetResultIgnored = deltakerExecutor.execute(deltakerCommandIgnored, expectAktivitetskortOnTopic = false) aktivitetResultIgnored.arenaDataDbo.ingestStatus shouldBe IngestStatus.HANDLED + aktivitetResultIgnored.arenaDataDbo.note shouldBe "foreløpig ignorert" + + idMappingClient.hentMapping(TranslationQuery(deltakelseId.value, AktivitetKategori.TILTAKSAKTIVITET)) shouldNotBe null val deltakerInput = deltakerInputIgnored.copy(deltakerStatusKode = "GJENN") val deltakerCommand = OppdaterDeltakerCommand(deltakerInputIgnored, deltakerInput) @@ -777,14 +777,16 @@ class DeltakerIntegrationTests : IntegrationTestBase() { arenaData.ingestStatus shouldBe IngestStatus.RETRY arenaData.note shouldBe "LOL" } - arenaIdTilAktivitetskortIdRepository.get(deltakerId, AktivitetKategori.TILTAKSAKTIVITET) shouldBe null + idMappingClient.hentMapping(TranslationQuery(deltakerId.value, AktivitetKategori.TILTAKSAKTIVITET)).second shouldBe null } + private val idMappingClient: IdMappingClient by lazy { + val token = issueAzureAdM2MToken() + IdMappingClient(port!!) { token } + } private fun hentTranslationMedRestClient(deltakerId: DeltakelseId): UUID? { - val token = issueAzureAdM2MToken() - val client = IdMappingClient(port!!) { token } - return client.hentMapping(TranslationQuery(deltakerId.value, AktivitetKategori.TILTAKSAKTIVITET)) + return idMappingClient.hentMapping(TranslationQuery(deltakerId.value, AktivitetKategori.TILTAKSAKTIVITET)) .let { (response, result) -> response.isSuccessful shouldBe true result diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/IntegrationTestBase.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/IntegrationTestBase.kt index bdc6ba5b..1b0435d1 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/IntegrationTestBase.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/IntegrationTestBase.kt @@ -128,10 +128,9 @@ open class IntegrationTestConfiguration( open fun deltakerExecutor( kafkaProducer: KafkaProducerClientImpl, arenaDataRepository: ArenaDataRepository, - deltakerAktivitetMappingRepository: DeltakerAktivitetMappingRepository, aktivitetRepository: AktivitetRepository ): DeltakerTestExecutor { - return DeltakerTestExecutor(kafkaProducer, arenaDataRepository, deltakerAktivitetMappingRepository) + return DeltakerTestExecutor(kafkaProducer, arenaDataRepository, aktivitetRepository) } @Bean diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/commands/deltaker/AktivitetResult.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/commands/deltaker/AktivitetResult.kt index 2171ede1..0289cf31 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/commands/deltaker/AktivitetResult.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/commands/deltaker/AktivitetResult.kt @@ -5,13 +5,13 @@ import no.nav.arena_tiltak_aktivitet_acl.domain.db.DeltakerAktivitetMappingDbo import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.Aktivitetskort import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetskortHeaders import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.KafkaMessageDto -import no.nav.arena_tiltak_aktivitet_acl.repositories.OppfolginsPeriodeId +import no.nav.arena_tiltak_aktivitet_acl.repositories.AktivitetIdAndOppfolgingsPeriode import org.junit.jupiter.api.fail class HandledResult( position: String, arenaDataDbo: ArenaDataDbo, - deltakerAktivitetMapping: List, + deltakerAktivitetMapping: List, val output: KafkaMessageDto, val headers: AktivitetskortHeaders ): AktivitetResult(position, arenaDataDbo, deltakerAktivitetMapping) { @@ -28,13 +28,13 @@ class HandledResult( class HandledAndIgnored( position: String, arenaDataDbo: ArenaDataDbo, - deltakerAktivitetMapping: List, + deltakerAktivitetMapping: List, ): AktivitetResult(position, arenaDataDbo, deltakerAktivitetMapping) open class AktivitetResult( val position: String, val arenaDataDbo: ArenaDataDbo, - val deltakerAktivitetMapping: List + val deltakerAktivitetMapping: List ) { fun expectHandled(check: (data: HandledResult) -> Unit) { if (this !is HandledResult) fail("Expected arena message to have ingest status HANDLED but was ${this.arenaDataDbo.ingestStatus}") diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/executors/DeltakerTestExecutor.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/executors/DeltakerTestExecutor.kt index d16d8d26..b32b826f 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/executors/DeltakerTestExecutor.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/integration/executors/DeltakerTestExecutor.kt @@ -12,15 +12,15 @@ import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.ArenaKafkaMessageDto import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId import no.nav.arena_tiltak_aktivitet_acl.integration.commands.deltaker.* import no.nav.arena_tiltak_aktivitet_acl.integration.kafka.KafkaAktivitetskortIntegrationConsumer +import no.nav.arena_tiltak_aktivitet_acl.repositories.AktivitetRepository import no.nav.arena_tiltak_aktivitet_acl.repositories.ArenaDataRepository -import no.nav.arena_tiltak_aktivitet_acl.repositories.DeltakerAktivitetMappingRepository import no.nav.arena_tiltak_aktivitet_acl.utils.ArenaTableName import no.nav.common.kafka.producer.KafkaProducerClientImpl class DeltakerTestExecutor( kafkaProducer: KafkaProducerClientImpl, arenaDataRepository: ArenaDataRepository, - val deltakerAktivitetMappingRepository: DeltakerAktivitetMappingRepository + val aktivitetRepository: AktivitetRepository ) : TestExecutor( kafkaProducer = kafkaProducer, arenaDataRepository = arenaDataRepository, @@ -63,7 +63,7 @@ class DeltakerTestExecutor( ) val deltakelseId = DeltakelseId(arenaData.arenaId.toLong()) - var deltakerAktivitetMapping = deltakerAktivitetMappingRepository.get(deltakelseId, AktivitetKategori.TILTAKSAKTIVITET) + var deltakerAktivitetMapping = aktivitetRepository.getAllBy(deltakelseId, AktivitetKategori.TILTAKSAKTIVITET) // There is no ack for messages which are put in retry, // use translation-table for checking if record is processed <- GJELDER IKKE LENGER when (arenaData.ingestStatus) { @@ -81,8 +81,8 @@ class DeltakerTestExecutor( val message: TestRecord = runBlocking { waitForAktivitetskortOnOutgoingTopic { - deltakerAktivitetMapping = deltakerAktivitetMappingRepository.get(deltakelseId, AktivitetKategori.TILTAKSAKTIVITET) - deltakerAktivitetMapping.any { a -> it.melding.aktivitetskort.id == a.aktivitetId } + deltakerAktivitetMapping = aktivitetRepository.getAllBy(deltakelseId, AktivitetKategori.TILTAKSAKTIVITET) + deltakerAktivitetMapping.any { a -> it.melding.aktivitetskort.id == a.id } } } return HandledResult( diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/ArenaGjennomforingProcessorTest.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/ArenaGjennomforingProcessorTest.kt index 07d55228..355482e0 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/ArenaGjennomforingProcessorTest.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/ArenaGjennomforingProcessorTest.kt @@ -16,7 +16,6 @@ import no.nav.arena_tiltak_aktivitet_acl.exceptions.IgnoredException import no.nav.arena_tiltak_aktivitet_acl.exceptions.ValidationException import no.nav.arena_tiltak_aktivitet_acl.repositories.ArenaDataRepository import no.nav.arena_tiltak_aktivitet_acl.repositories.GjennomforingRepository -import no.nav.arena_tiltak_aktivitet_acl.repositories.ArenaIdTilAktivitetskortIdRepository import no.nav.arena_tiltak_aktivitet_acl.services.KafkaProducerService import no.nav.arena_tiltak_aktivitet_acl.services.TiltakService import no.nav.arena_tiltak_aktivitet_acl.utils.ArenaTableName @@ -35,7 +34,6 @@ import java.util.* class ArenaGjennomforingProcessorTest { private lateinit var jdbcTemplate: NamedParameterJdbcTemplate private lateinit var repository: ArenaDataRepository - private lateinit var arenaIdTilAktivitetskortIdRepository: ArenaIdTilAktivitetskortIdRepository private lateinit var tiltakService: TiltakService private lateinit var ordsClient: ArenaOrdsProxyClient private lateinit var kafkaProducerService: KafkaProducerService @@ -52,7 +50,6 @@ class ArenaGjennomforingProcessorTest { fun beforeAll() { jdbcTemplate = NamedParameterJdbcTemplate(dataSource) repository = ArenaDataRepository(jdbcTemplate) - arenaIdTilAktivitetskortIdRepository = ArenaIdTilAktivitetskortIdRepository(jdbcTemplate) tiltakService = mock(TiltakService::class.java) ordsClient = mock(ArenaOrdsProxyClient::class.java) kafkaProducerService = mock(KafkaProducerService::class.java) diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessorTest.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessorTest.kt index 87ba1142..e5997bb2 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessorTest.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/processors/DeltakerProcessorTest.kt @@ -4,6 +4,7 @@ import ArenaOrdsProxyClient import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldThrowExactly import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.reflection.beLateInit import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.mockk.* @@ -54,8 +55,8 @@ class DeltakerProcessorTest : FunSpec({ val kafkaProducerService = mockk(relaxUnitFun = true) lateinit var arenaDataRepository: ArenaDataRepository - lateinit var idArenaIdTilAktivitetskortIdRepository: ArenaIdTilAktivitetskortIdRepository lateinit var personSporingRepository: PersonSporingRepository + lateinit var aktivitetRepository: AktivitetRepository // Se SQL inserted før hver test val nonIgnoredGjennomforingArenaId = 1L @@ -64,8 +65,8 @@ class DeltakerProcessorTest : FunSpec({ beforeEach { val template = NamedParameterJdbcTemplate(dataSource) arenaDataRepository = ArenaDataRepository(template) - idArenaIdTilAktivitetskortIdRepository = ArenaIdTilAktivitetskortIdRepository(template) personSporingRepository = PersonSporingRepository(template) + aktivitetRepository = AktivitetRepository(template) clearMocks(kafkaProducerService) DatabaseTestUtils.cleanAndInitDatabase(dataSource, "/deltaker-processor_test-data.sql") @@ -79,14 +80,12 @@ class DeltakerProcessorTest : FunSpec({ return DeltakerProcessor( arenaDataRepository = arenaDataRepository, - arenaIdArenaIdTilAktivitetskortIdService = ArenaIdTilAktivitetskortIdService(idArenaIdTilAktivitetskortIdRepository), kafkaProducerService = kafkaProducerService, aktivitetService = AktivitetService(AktivitetRepository(template)), gjennomforingRepository = GjennomforingRepository(template), tiltakService = TiltakService(TiltakRepository(template)), oppfolgingsperiodeService = OppfolgingsperiodeService(oppfolgingClient), personsporingService = PersonsporingService(personSporingRepository, ordsClient), - deltakerAktivitetMappingRepository = DeltakerAktivitetMappingRepository(template) ) } @@ -127,7 +126,7 @@ class DeltakerProcessorTest : FunSpec({ ) createDeltakerProcessor().handleArenaMessage(newDeltaker) getAndCheckArenaDataRepositoryEntry(operation = Operation.CREATED, (operationPos).toString()) - val translationEntry = idArenaIdTilAktivitetskortIdRepository.get(DeltakelseId(1), AktivitetKategori.TILTAKSAKTIVITET) + val translationEntry = aktivitetRepository.getCurrentAktivitetsId(DeltakelseId(1), AktivitetKategori.TILTAKSAKTIVITET) translationEntry shouldNotBe null } @@ -183,7 +182,7 @@ class DeltakerProcessorTest : FunSpec({ registrertDato = opprettetTidspunkt) createDeltakerProcessor().handleArenaMessage(newDeltaker) getAndCheckArenaDataRepositoryEntry(operation = Operation.CREATED, (operationPos).toString()) - val translationEntry = idArenaIdTilAktivitetskortIdRepository.get(DeltakelseId(1), AktivitetKategori.TILTAKSAKTIVITET) + val translationEntry = aktivitetRepository.getCurrentAktivitetsId(DeltakelseId(1), AktivitetKategori.TILTAKSAKTIVITET) translationEntry shouldNotBe null } diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/ArenaIdTranslationRepositoryTest.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/ArenaIdTranslationRepositoryTest.kt deleted file mode 100644 index 8a5b415a..00000000 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/repositories/ArenaIdTranslationRepositoryTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package no.nav.arena_tiltak_aktivitet_acl.repositories - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import no.nav.arena_tiltak_aktivitet_acl.database.DatabaseTestUtils -import no.nav.arena_tiltak_aktivitet_acl.database.SingletonPostgresContainer -import no.nav.arena_tiltak_aktivitet_acl.domain.db.TranslationDbo -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.aktivitet.AktivitetKategori -import no.nav.arena_tiltak_aktivitet_acl.domain.kafka.arena.tiltak.DeltakelseId -import org.slf4j.LoggerFactory -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate -import java.util.* - -class ArenaIdTranslationRepositoryTest : FunSpec({ - - val dataSource = SingletonPostgresContainer.getDataSource() - - lateinit var repository: ArenaIdTilAktivitetskortIdRepository - - val testObject = TranslationDbo( - aktivitetId = UUID.randomUUID(), - arenaId = DeltakelseId(123L), - aktivitetKategori = AktivitetKategori.TILTAKSAKTIVITET - ) - - beforeEach { - val rootLogger: Logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger - rootLogger.level = Level.WARN - - repository = ArenaIdTilAktivitetskortIdRepository(NamedParameterJdbcTemplate(dataSource)) - - DatabaseTestUtils.cleanDatabase(dataSource) - } - - test("Insert and get should return inserted object") { - repository.insert(testObject) - - val stored = repository.get(testObject.arenaId, AktivitetKategori.TILTAKSAKTIVITET) - - stored shouldNotBe null - stored!!.aktivitetId shouldBe testObject.aktivitetId - stored.arenaId shouldBe testObject.arenaId - } -}) diff --git a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeServiceTest.kt b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeServiceTest.kt index 5104b33a..bbc3db0a 100644 --- a/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeServiceTest.kt +++ b/src/test/kotlin/no/nav/arena_tiltak_aktivitet_acl/services/OppfolgingsperiodeServiceTest.kt @@ -2,6 +2,7 @@ package no.nav.veilarbaktivitet.aktivitetskort import no.nav.arena_tiltak_aktivitet_acl.clients.oppfolging.OppfolgingClient import no.nav.arena_tiltak_aktivitet_acl.clients.oppfolging.Oppfolgingsperiode +import no.nav.arena_tiltak_aktivitet_acl.services.FinnOppfolgingResult import no.nav.arena_tiltak_aktivitet_acl.services.OppfolgingsperiodeService import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach @@ -148,7 +149,11 @@ class OppfolgingsperiodeServiceTest { Mockito.`when`(oppfolgingClient.hentOppfolgingsperioder(ArgumentMatchers.anyString())) .thenReturn(perioder) - return oppfolgingsperiodeService.finnOppfolgingsperiode(FNR, opprettetTidspunkt) + val result = oppfolgingsperiodeService.finnOppfolgingsperiode(FNR, opprettetTidspunkt) + return when (result) { + is FinnOppfolgingResult.FunnetPeriodeResult -> result.oppfolgingsperiode + else -> null + } } private fun oppfperiodeDTO(startDato: ZonedDateTime, sluttDato: ZonedDateTime?): Oppfolgingsperiode {