From 8746e73315292318a73b710dc0da986fdaca3cb7 Mon Sep 17 00:00:00 2001 From: Johanne Tronstad <43403066+johatr@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:38:44 +0100 Subject: [PATCH] Send udelte samtalereferat til oversikten (#897) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * legg til funksjonalitet for melding til oversikten * Legg til TODOs på steder der vi må agere * Legg til sql-fil for oversikt-tabell * Test at når udelte referater opprettes sendes melding til oversikten * Fiks testoppsett * Legg til TODOs på steder i koden der vi må sende melding * WIP: Send startmelding * WIP: Test at vi putter melding i utboksen * Test sending av startmelding * Send melding til oversikten Signed-off-by: Henrik uran * Rensk opp ny databasetabell mellom hver test * WIP: Lagre sendingsUuid * oppdater flyway script for meldinger til oversikten for udelt samtalereferat * legg til operasjon for melding til oversikten, og rename repository til dao * legg til melding key for møte * legg til håndtering av caser for melding til oversikten for møte og samtalereferat * Set ny meldinguuid * cleanup håndtering av caser for melding til oversikten og bigquery * Implementer lagret oversikten melding * WIP: Ha egen mappingtabell for oversiktenmeldinger og aktiviteter * Lag ID på ny tabell * Lagre meldingKeyUuid i koblingstabell * Legg til composite key opå meldingstabell * Tilpass at hverken melding_key eller aktivitet_id er unike * Fiks småting for at koden skal kjøre * Fjern test som ikke er relevant * Legg til enhetstest av oversiktenMelding * legg til producer for sending av melding på kafka * legg til funksjonalitet for å lagre stoppmelding om udelt samtalereferat * Logg hvis meldingKey ikke eksisterer * når samtalereferat deles med bruker sendes top melding til oversikten test Signed-off-by: Henrik uran * Test stoppmelding * Legg til nytt testcase * Bruk producer * WIP: Test oversiktenService * Fiks service tester * Fiks navn på topic * Test at melding blir markert som sendt * Legg publisering av meldinger bak feature toggle * Errorhåndtering * endre schedule til hver time * Legg til topic i application properties for test * Legg til tabell i ryddeliste * cleanup imports --------- Signed-off-by: Henrik uran Co-authored-by: erikb Co-authored-by: Mads Lee Giil Co-authored-by: Henrik uran --- .../aktivitet/AktivitetAppService.java | 64 ++++++-- .../aktivitet/domain/MoteData.java | 2 +- .../aktivitet/AktivitetsplanController.kt | 4 - .../config/HttpExceptionHandler.kt | 11 ++ .../eventsLogger/BigQueryClient.kt | 1 + .../oversikten/OversiktenMelding.kt | 44 ++++++ .../OversiktenMeldingAktivitetMappingDAO.kt | 41 ++++++ .../OversiktenMeldingMedMetadata.kt | 45 ++++++ .../OversiktenMeldingMedMetadataDAO.kt | 88 +++++++++++ .../oversikten/OversiktenProducer.kt | 21 +++ .../oversikten/OversiktenService.kt | 85 +++++++++++ src/main/resources/application.properties | 1 + .../V10__oversikten_melding_med_metadata.sql | 39 +++++ .../nav/veilarbaktivitet/db/DbTestUtils.java | 4 +- .../util/AktivitetTestService.java | 31 ++++ .../aktivitet/AktivitetsplanControllerTest.kt | 139 +++++++++++++++++- .../oversikten/OversiktenMeldingTest.kt | 38 +++++ .../oversikten/OversiktenServiceTest.kt | 105 +++++++++++++ src/test/resources/application.properties | 1 + 19 files changed, 743 insertions(+), 21 deletions(-) create mode 100644 src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMelding.kt create mode 100644 src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingAktivitetMappingDAO.kt create mode 100644 src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadata.kt create mode 100644 src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadataDAO.kt create mode 100644 src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenProducer.kt create mode 100644 src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenService.kt create mode 100644 src/main/resources/db/migration/V10__oversikten_melding_med_metadata.sql create mode 100644 src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingTest.kt create mode 100644 src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenServiceTest.kt diff --git a/src/main/java/no/nav/veilarbaktivitet/aktivitet/AktivitetAppService.java b/src/main/java/no/nav/veilarbaktivitet/aktivitet/AktivitetAppService.java index c8bc62700..2601e6174 100644 --- a/src/main/java/no/nav/veilarbaktivitet/aktivitet/AktivitetAppService.java +++ b/src/main/java/no/nav/veilarbaktivitet/aktivitet/AktivitetAppService.java @@ -9,6 +9,7 @@ import no.nav.veilarbaktivitet.aktivitet.feil.EndringAvHistoriskAktivitetException; import no.nav.veilarbaktivitet.eventsLogger.BigQueryClient; import no.nav.veilarbaktivitet.eventsLogger.EventType; +import no.nav.veilarbaktivitet.oversikten.OversiktenService; import no.nav.veilarbaktivitet.person.Person; import no.nav.veilarbaktivitet.person.PersonService; import org.slf4j.Logger; @@ -31,6 +32,7 @@ public class AktivitetAppService { private final MetricService metricService; private final PersonService personService; private final BigQueryClient bigQueryClient; + private final OversiktenService oversiktenService; private static final Set TYPER_SOM_KAN_ENDRES_EKSTERNT = new HashSet<>(Arrays.asList( AktivitetTypeData.EGENAKTIVITET, @@ -102,6 +104,16 @@ private static boolean erReferatetEndretForDetErPublisert(AktivitetData aktivite return !aktivitetData.getMoteData().isReferatPublisert() && referatEndret; } + private boolean referatErDeltMedBruker(AktivitetData nyAktivitet) { + if(nyAktivitet.getMoteData() == null) return false; + return nyAktivitet.getMoteData().isReferatPublisert(); + } + + private boolean nyopprettetAktivitetKanHaReferat(AktivitetData nyAktivitet) { + var aktivitetstyperSomKanHaReferatNårAktivitetOpprettes = List.of(AktivitetTypeData.SAMTALEREFERAT); + return aktivitetstyperSomKanHaReferatNårAktivitetOpprettes.contains(nyAktivitet.getAktivitetType()); + } + @Transactional public AktivitetData opprettNyAktivitet(AktivitetData aktivitetData) { @@ -113,8 +125,14 @@ public AktivitetData opprettNyAktivitet(AktivitetData aktivitetData) { } AktivitetData nyAktivitet = aktivitetService.opprettAktivitet(aktivitetData); - if (nyAktivitet.getAktivitetType() == AktivitetTypeData.SAMTALEREFERAT || nyAktivitet.getAktivitetType() == AktivitetTypeData.MOTE) { - bigQueryClient.logEvent(nyAktivitet, EventType.SAMTALEREFERAT_OPPRETTET); + + if (nyopprettetAktivitetKanHaReferat(nyAktivitet)) { + if (referatErDeltMedBruker(nyAktivitet)) { + bigQueryClient.logEvent(nyAktivitet, EventType.SAMTALEREFERAT_OPPRETTET_OG_DELT_MED_BRUKER); + } else { + bigQueryClient.logEvent(nyAktivitet, EventType.SAMTALEREFERAT_OPPRETTET); + oversiktenService.lagreStartMeldingOmUdeltSamtalereferatIUtboks(nyAktivitet.getAktorId(), nyAktivitet.getId()); + } } // dette er gjort på grunn av KVP @@ -229,21 +247,39 @@ public AktivitetData oppdaterReferat(AktivitetData aktivitet) { final var originalAktivitet = hentAktivitet(aktivitet.getId()); kanEndreAktivitetGuard(originalAktivitet, aktivitet.getVersjon(), aktivitet.getAktorId()); - var oppdatertAktivtiet = aktivitetService.oppdaterReferat( - originalAktivitet, - aktivitet - ); + var oppdatertAktivitet = aktivitetService.oppdaterReferat(originalAktivitet, aktivitet); + + var maybeEventType = hentEventTypePåSamtalereferat(originalAktivitet, aktivitet); + maybeEventType.ifPresent(eventType -> { + bigQueryClient.logEvent(oppdatertAktivitet, eventType); + sendMeldingTilOversikten(oppdatertAktivitet, eventType); + }); + return oppdatertAktivitet; + } - if(!originalAktivitet.getMoteData().isReferatPublisert() && oppdatertAktivtiet.getMoteData().isReferatPublisert()) { - bigQueryClient.logEvent(oppdatertAktivtiet, EventType.SAMTALEREFERAT_DELT_MED_BRUKER); + private void sendMeldingTilOversikten(AktivitetData aktivitet, EventType eventType) { + if (eventType == EventType.SAMTALEREFERAT_OPPRETTET) { // Kan kun skje for aktivitetstype "Møte" + oversiktenService.lagreStartMeldingOmUdeltSamtalereferatIUtboks(aktivitet.getAktorId(), aktivitet.getId()); + } else if (eventType == EventType.SAMTALEREFERAT_DELT_MED_BRUKER) { + oversiktenService.lagreStoppMeldingOmUdeltSamtalereferatIUtboks(aktivitet.getAktorId(), aktivitet.getId()); } + } + + private Optional hentEventTypePåSamtalereferat(AktivitetData originalAktivitet, AktivitetData oppdatertAktivitet) { var forrigeReferat = Optional.ofNullable(originalAktivitet.getMoteData()).map(it -> it.getReferat()).orElse(""); - var nesteReferat = Optional.ofNullable(oppdatertAktivtiet.getMoteData()).map(it -> it.getReferat()).orElse(""); - if (forrigeReferat.isEmpty() && !nesteReferat.isEmpty() && oppdatertAktivtiet.getAktivitetType() == AktivitetTypeData.MOTE ) { - bigQueryClient.logEvent(oppdatertAktivtiet, EventType.SAMTALEREFERAT_FIKK_INNHOLD); - } + var nesteReferat = Optional.ofNullable(oppdatertAktivitet.getMoteData()).map(it -> it.getReferat()).orElse(""); - return oppdatertAktivtiet; - } + var referatHarNåFåttInnhold = forrigeReferat.isEmpty() && !nesteReferat.isEmpty(); + var referatHarNåBlittDeltMedBruker = !originalAktivitet.getMoteData().isReferatPublisert() && oppdatertAktivitet.getMoteData().isReferatPublisert(); + if (referatHarNåFåttInnhold && !referatHarNåBlittDeltMedBruker) { + return Optional.of(EventType.SAMTALEREFERAT_OPPRETTET); + } else if (referatHarNåFåttInnhold && referatHarNåBlittDeltMedBruker) { + return Optional.of(EventType.SAMTALEREFERAT_OPPRETTET_OG_DELT_MED_BRUKER); + } else if (!referatHarNåFåttInnhold && referatHarNåBlittDeltMedBruker) { + return Optional.of(EventType.SAMTALEREFERAT_DELT_MED_BRUKER); + } else { + return Optional.empty(); + } + } } diff --git a/src/main/java/no/nav/veilarbaktivitet/aktivitet/domain/MoteData.java b/src/main/java/no/nav/veilarbaktivitet/aktivitet/domain/MoteData.java index 89a8e9741..b46f0bc5f 100644 --- a/src/main/java/no/nav/veilarbaktivitet/aktivitet/domain/MoteData.java +++ b/src/main/java/no/nav/veilarbaktivitet/aktivitet/domain/MoteData.java @@ -15,4 +15,4 @@ public class MoteData { String referat; boolean referatPublisert; -} \ No newline at end of file +} diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanController.kt b/src/main/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanController.kt index c9f18f131..8eeddf9d0 100644 --- a/src/main/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanController.kt +++ b/src/main/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanController.kt @@ -80,10 +80,6 @@ class AktivitetsplanController( @RequestBody aktivitet: AktivitetDTO, @RequestParam(required = false, defaultValue = "false") automatisk: Boolean ): AktivitetDTO { - return internOpprettAktivitet(aktivitet, automatisk) - } - - fun internOpprettAktivitet(aktivitet: AktivitetDTO, automatisk: Boolean): AktivitetDTO { return aktivitetDataMapperService.mapTilAktivitetData(aktivitet) .withAutomatiskOpprettet(automatisk) .let { aktivitetData -> appService.opprettNyAktivitet(aktivitetData) } diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/config/HttpExceptionHandler.kt b/src/main/kotlin/no/nav/veilarbaktivitet/config/HttpExceptionHandler.kt index 6d87faec5..abb77840c 100644 --- a/src/main/kotlin/no/nav/veilarbaktivitet/config/HttpExceptionHandler.kt +++ b/src/main/kotlin/no/nav/veilarbaktivitet/config/HttpExceptionHandler.kt @@ -2,6 +2,7 @@ package no.nav.veilarbaktivitet.config import com.fasterxml.jackson.annotation.JsonInclude import no.nav.veilarbaktivitet.aktivitet.feil.* +import org.slf4j.LoggerFactory import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.ControllerAdvice @@ -13,6 +14,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep @ControllerAdvice class HttpExceptionHandler : ResponseEntityExceptionHandler() { + val log = LoggerFactory.getLogger(HttpExceptionHandler::class.java) @ExceptionHandler(value = [EndringAvAktivitetException::class]) fun handleException(e: EndringAvAktivitetException, request: WebRequest): ResponseEntity { @@ -32,6 +34,15 @@ class HttpExceptionHandler : ResponseEntityExceptionHandler() { .body(Response(statusCode = e.statusCode.value(), message = e.reason)) } + @ExceptionHandler(value = [RuntimeException::class]) + fun handleRuntimeException(e: RuntimeException, request: WebRequest): ResponseEntity { + val statusKode = HttpStatus.INTERNAL_SERVER_ERROR.value() + log.error("Feil i håndtering av kall", e) + return ResponseEntity + .status(statusKode) + .body(Response(statusCode = statusKode, message = "Noe gikk galt")) + } + @JsonInclude(JsonInclude.Include.NON_NULL) data class Response( val message: String?, diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/eventsLogger/BigQueryClient.kt b/src/main/kotlin/no/nav/veilarbaktivitet/eventsLogger/BigQueryClient.kt index af52a789c..8d3742a9c 100644 --- a/src/main/kotlin/no/nav/veilarbaktivitet/eventsLogger/BigQueryClient.kt +++ b/src/main/kotlin/no/nav/veilarbaktivitet/eventsLogger/BigQueryClient.kt @@ -14,6 +14,7 @@ enum class EventType { SAMTALEREFERAT_OPPRETTET, SAMTALEREFERAT_FIKK_INNHOLD, SAMTALEREFERAT_DELT_MED_BRUKER, + SAMTALEREFERAT_OPPRETTET_OG_DELT_MED_BRUKER, } data class SamtalereferatPublisertFordeling( diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMelding.kt b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMelding.kt new file mode 100644 index 000000000..27a517070 --- /dev/null +++ b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMelding.kt @@ -0,0 +1,44 @@ +package no.nav.veilarbaktivitet.oversikten + +import java.time.LocalDateTime + +data class OversiktenMelding( + val personID: String, + val avsender: String = "veilarbaktivitet", + val kategori: Kategori, + val operasjon: Operasjon, + val hendelse: Hendelse +) { + companion object { + private fun baseUrlVeilarbpersonflate(erProd: Boolean) = + if (erProd) "https://veilarbpersonflate.intern.nav.no" else "https://veilarbpersonflate.ansatt.dev.nav.no" + + fun forUdeltSamtalereferat(fnr: String, operasjon: Operasjon, erProd: Boolean) = OversiktenMelding( + personID = fnr, + kategori = Kategori.UDELT_SAMTALEREFERAT, + operasjon = operasjon, + hendelse = Hendelse( + beskrivelse = "Bruker har et udelt samtalereferat", + dato = LocalDateTime.now(), + lenke = "${baseUrlVeilarbpersonflate(erProd)}/aktivitetsplan", + ) + ) + } + + data class Hendelse ( + val beskrivelse: String, + val dato: LocalDateTime, + val lenke: String, + val detaljer: String? = null, + ) + + enum class Kategori { + UDELT_SAMTALEREFERAT + } + + enum class Operasjon { + START, + OPPDATER, + STOPP + } +} diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingAktivitetMappingDAO.kt b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingAktivitetMappingDAO.kt new file mode 100644 index 000000000..e1bc49e31 --- /dev/null +++ b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingAktivitetMappingDAO.kt @@ -0,0 +1,41 @@ +package no.nav.veilarbaktivitet.oversikten + +import no.nav.veilarbaktivitet.aktivitet.AktivitetId +import org.springframework.jdbc.core.RowMapper +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate +import org.springframework.stereotype.Repository +import java.sql.ResultSet +import java.util.* + +@Repository +open class OversiktenMeldingAktivitetMappingDAO(private val template: NamedParameterJdbcTemplate) { + + open fun lagreKoblingMellomOversiktenMeldingOgAktivitet(oversiktenMeldingKey: MeldingKey, aktivitetId: AktivitetId, kategori: OversiktenMelding.Kategori) { + val sql = """ + insert into oversikten_melding_aktivitet_mapping (oversikten_melding_key, aktivitet_id, kategori) + values (:oversiktenMeldingKey, :aktivitetId, :kategori::OVERSIKTEN_KATEGORI) + """.trimIndent() + val params = MapSqlParameterSource() + .addValue("oversiktenMeldingKey", oversiktenMeldingKey) + .addValue("aktivitetId", aktivitetId) + .addValue("kategori", kategori.name) + template.update(sql, params) + } + + open fun hentMeldingKeyForAktivitet(aktivitetId: AktivitetId, kategori: OversiktenMelding.Kategori) : UUID? { + val sql = """ + select oversikten_melding_key + from oversikten_melding_aktivitet_mapping + where aktivitet_id = :aktivitetId and kategori = :kategori::OVERSIKTEN_KATEGORI + """.trimIndent() + + val params = mapOf("aktivitetId" to aktivitetId, "kategori" to kategori.name) + + return template.queryForObject(sql, params, rowMapper) + } + + open val rowMapper = RowMapper { rs: ResultSet, rowNum: Int -> + rs.getObject("oversikten_melding_key", UUID::class.java) + } +} diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadata.kt b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadata.kt new file mode 100644 index 000000000..47afaa51b --- /dev/null +++ b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadata.kt @@ -0,0 +1,45 @@ +package no.nav.veilarbaktivitet.oversikten + +import no.nav.common.types.identer.Fnr +import java.time.ZonedDateTime +import java.util.* + +open class OversiktenMeldingMedMetadata( + val meldingKey: MeldingKey, + val fnr: Fnr, + val opprettet: ZonedDateTime = ZonedDateTime.now(), + val tidspunktSendt: ZonedDateTime? = null, + val utsendingStatus: UtsendingStatus = UtsendingStatus.SKAL_SENDES, + val meldingSomJson: String, + val kategori: OversiktenMelding.Kategori, + val operasjon: OversiktenMelding.Operasjon, +) + +class LagretOversiktenMeldingMedMetadata( + val id: Long, + meldingKey: MeldingKey, + fnr: Fnr, + opprettet: ZonedDateTime, + tidspunktSendt: ZonedDateTime?, + utsendingStatus: UtsendingStatus, + meldingSomJson: String, + kategori: OversiktenMelding.Kategori, + operasjon: OversiktenMelding.Operasjon +) : OversiktenMeldingMedMetadata( + meldingKey = meldingKey, + fnr = fnr, + opprettet = opprettet, + tidspunktSendt = tidspunktSendt, + utsendingStatus = utsendingStatus, + meldingSomJson = meldingSomJson, + kategori = kategori, + operasjon = operasjon +) + +typealias MeldingKey = UUID + +enum class UtsendingStatus { + SKAL_SENDES, + SENDT, + SKAL_IKKE_SENDES +} diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadataDAO.kt b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadataDAO.kt new file mode 100644 index 000000000..622e6b329 --- /dev/null +++ b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingMedMetadataDAO.kt @@ -0,0 +1,88 @@ +package no.nav.veilarbaktivitet.oversikten + +import no.nav.common.types.identer.Fnr +import no.nav.veilarbaktivitet.config.database.Database +import no.nav.veilarbaktivitet.veilarbdbutil.VeilarbAktivitetSqlParameterSource +import org.springframework.jdbc.core.RowMapper +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate +import org.springframework.jdbc.support.GeneratedKeyHolder +import org.springframework.stereotype.Repository +import java.sql.ResultSet +import java.util.* + +@Repository +open class OversiktenMeldingMedMetadataDAO( + private val jdbc: NamedParameterJdbcTemplate +) { + open fun lagre(oversiktenMeldingMedMetadata: OversiktenMeldingMedMetadata): Long { + val sql = """ + INSERT INTO oversikten_melding_med_metadata ( + fnr, opprettet, utsending_status, melding, kategori, melding_key, operasjon) + VALUES ( :fnr, :opprettet, :utsending_status::OVERSIKTEN_UTSENDING_STATUS, :melding::json, :kategori::OVERSIKTEN_KATEGORI, :melding_key, :operasjon::OVERSIKTEN_OPERASJON) + """.trimIndent() + + val params = VeilarbAktivitetSqlParameterSource().apply { + addValue("fnr", oversiktenMeldingMedMetadata.fnr.get()) + addValue("opprettet", oversiktenMeldingMedMetadata.opprettet) + addValue("utsending_status", oversiktenMeldingMedMetadata.utsendingStatus.name) + addValue("melding", oversiktenMeldingMedMetadata.meldingSomJson) + addValue("kategori", oversiktenMeldingMedMetadata.kategori.name) + addValue("melding_key", oversiktenMeldingMedMetadata.meldingKey) + addValue("operasjon", oversiktenMeldingMedMetadata.operasjon.name) + } + + val keyHolder = GeneratedKeyHolder() + jdbc.update(sql, params, keyHolder, arrayOf("id")) + + return keyHolder.key?.toLong() ?: throw IllegalStateException("Kunne ikke hente ut nøkkel til lagret melding") + } + + open fun hentAlleSomSkalSendes(): List { + val sql = """ + SELECT * FROM oversikten_melding_med_metadata WHERE utsending_status = 'SKAL_SENDES' + """.trimIndent() + + return jdbc.query(sql, rowMapper) + } + + open fun markerSomSendt(id: Long) { + val sql = """ + UPDATE oversikten_melding_med_metadata + SET utsending_status = 'SENDT', + tidspunkt_sendt = now() + WHERE id = :id + """.trimIndent() + + val params = VeilarbAktivitetSqlParameterSource().apply { + addValue("id", id) + } + + jdbc.update(sql, params) + } + + open fun hent(id : Long) : LagretOversiktenMeldingMedMetadata? { + val sql = """ + SELECT * FROM oversikten_melding_med_metadata WHERE id = :id + """.trimIndent() + + val params = VeilarbAktivitetSqlParameterSource().apply { + addValue("id", id) + } + + return jdbc.queryForObject(sql, params, rowMapper) + } + + open val rowMapper = RowMapper { rs: ResultSet, rowNum: Int -> + LagretOversiktenMeldingMedMetadata( + id = rs.getLong("id"), + fnr = Fnr.of(rs.getString("fnr")), + opprettet = Database.hentZonedDateTime(rs, "opprettet"), + tidspunktSendt = Database.hentZonedDateTime(rs, "tidspunkt_sendt"), + utsendingStatus = UtsendingStatus.valueOf(rs.getString("utsending_status")), + meldingSomJson = rs.getString("melding"), + kategori = OversiktenMelding.Kategori.valueOf(rs.getString("kategori")), + meldingKey = UUID.fromString(rs.getString("melding_key")), + operasjon = OversiktenMelding.Operasjon.valueOf(rs.getString("operasjon")), + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenProducer.kt b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenProducer.kt new file mode 100644 index 000000000..be9d713f3 --- /dev/null +++ b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenProducer.kt @@ -0,0 +1,21 @@ +package no.nav.veilarbaktivitet.oversikten + +import no.nav.common.kafka.producer.KafkaProducerClient +import org.apache.kafka.clients.producer.ProducerRecord +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service + +@Service +open class OversiktenProducer( + @Autowired + val aivenProducerClient: KafkaProducerClient, + @Value("\${topic.ut.oversikten}") + private val topic: String, +) { + + open fun sendMelding(key: String, melding: String) { + val producerRecord = ProducerRecord(topic, key, melding) + aivenProducerClient.sendSync(producerRecord) + } +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenService.kt b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenService.kt new file mode 100644 index 000000000..5ae0ff879 --- /dev/null +++ b/src/main/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenService.kt @@ -0,0 +1,85 @@ +package no.nav.veilarbaktivitet.oversikten + +import net.javacrumbs.shedlock.spring.annotation.SchedulerLock +import no.nav.common.client.aktoroppslag.AktorOppslagClient +import no.nav.common.json.JsonUtils +import no.nav.common.utils.EnvironmentUtils +import no.nav.veilarbaktivitet.aktivitet.AktivitetId +import no.nav.veilarbaktivitet.oversikten.OversiktenMelding.Kategori.UDELT_SAMTALEREFERAT +import no.nav.veilarbaktivitet.person.Person.AktorId +import org.slf4j.LoggerFactory +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service +import java.util.* +import kotlin.jvm.optionals.getOrElse + +@Service +open class OversiktenService( + private val aktorOppslagClient: AktorOppslagClient, + private val oversiktenMeldingMedMetadataRepository: OversiktenMeldingMedMetadataDAO, + private val oversiktenMeldingAktivitetMappingDao: OversiktenMeldingAktivitetMappingDAO, + private val oversiktenProducer: OversiktenProducer + +) { + private val log = LoggerFactory.getLogger(OversiktenService::class.java) + private val erProd = EnvironmentUtils.isProduction().orElse(false) + + @Scheduled(cron = "0 0 * * * *") // Hvert minutt + @SchedulerLock(name = "oversikten_melding_med_metadata_scheduledTask", lockAtMostFor = "PT3M") + open fun sendUsendteMeldingerTilOversikten() { + val kanPublisereMeldinger = !EnvironmentUtils.isProduction().getOrElse { false } && !EnvironmentUtils.isDevelopment().getOrElse { false } + + if (kanPublisereMeldinger) { + val meldingerMedMetadata = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes() + if(meldingerMedMetadata.isNotEmpty()) { + log.info("Sender ${meldingerMedMetadata.size} meldinger til oversikten") + } + meldingerMedMetadata.forEach { meldingMedMetadata -> + oversiktenProducer.sendMelding(meldingMedMetadata.meldingKey.toString(), meldingMedMetadata.meldingSomJson) + oversiktenMeldingMedMetadataRepository.markerSomSendt(meldingMedMetadata.id) + meldingMedMetadata.fnr + } + } else { + log.info("OBO er ikke klare til å ta imot meldinger om udelte samtalereferat") + } + } + + open fun lagreStartMeldingOmUdeltSamtalereferatIUtboks(aktorId: AktorId, aktivitetId: AktivitetId) { + val fnr = aktorOppslagClient.hentFnr(no.nav.common.types.identer.AktorId.of(aktorId.get())) + val melding = + OversiktenMelding.forUdeltSamtalereferat(fnr.toString(), OversiktenMelding.Operasjon.START, erProd) + val oversiktenMeldingMedMetadata = OversiktenMeldingMedMetadata( + meldingSomJson = JsonUtils.toJson(melding), + fnr = fnr, + kategori = melding.kategori, + meldingKey = UUID.randomUUID(), + operasjon = melding.operasjon, + ) + oversiktenMeldingMedMetadataRepository.lagre(oversiktenMeldingMedMetadata) + oversiktenMeldingAktivitetMappingDao.lagreKoblingMellomOversiktenMeldingOgAktivitet( + aktivitetId = aktivitetId, + oversiktenMeldingKey = oversiktenMeldingMedMetadata.meldingKey, + kategori = UDELT_SAMTALEREFERAT + ) + } + + open fun lagreStoppMeldingOmUdeltSamtalereferatIUtboks(aktorId: AktorId, aktivitetId: AktivitetId) { + val fnr = aktorOppslagClient.hentFnr(no.nav.common.types.identer.AktorId.of(aktorId.get())) + val sluttmelding = OversiktenMelding.forUdeltSamtalereferat(fnr.toString(), OversiktenMelding.Operasjon.STOPP, erProd) + val meldingKey = oversiktenMeldingAktivitetMappingDao.hentMeldingKeyForAktivitet(aktivitetId, UDELT_SAMTALEREFERAT) + + if (meldingKey == null) { + log.warn("Finner ikke meldingKey for aktivitet med id $aktivitetId og kategori $UDELT_SAMTALEREFERAT") + return + } + + val oversiktenMeldingMedMetadata = OversiktenMeldingMedMetadata( + meldingSomJson = JsonUtils.toJson(sluttmelding), + fnr = fnr, + kategori = sluttmelding.kategori, + meldingKey = meldingKey, + operasjon = sluttmelding.operasjon, + ) + oversiktenMeldingMedMetadataRepository.lagre(oversiktenMeldingMedMetadata) + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 898de30ac..2cabece3d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -82,6 +82,7 @@ topic.ut.brukernotifikasjon.brukervarsel=min-side.aapen-brukervarsel-v1 topic.inn.brukernotifikasjon.brukervarselHendelse=min-side.aapen-varsel-hendelse-v1 topic.inn.oppfolgingsperiode=pto.oppfolgingsperiode-v1 topic.inn.kvpAvsluttet=pto.kvp-avsluttet-v1 +topic.ut.oversikten=obo.portefolje-hendelsesfilter-v1 ############################################### # KAFKA AIVEN # ############################################### diff --git a/src/main/resources/db/migration/V10__oversikten_melding_med_metadata.sql b/src/main/resources/db/migration/V10__oversikten_melding_med_metadata.sql new file mode 100644 index 000000000..3cb0c84a6 --- /dev/null +++ b/src/main/resources/db/migration/V10__oversikten_melding_med_metadata.sql @@ -0,0 +1,39 @@ +CREATE TYPE OVERSIKTEN_UTSENDING_STATUS AS ENUM ('SKAL_SENDES', 'SENDT', 'SKAL_IKKE_SENDES'); +CREATE TYPE OVERSIKTEN_OPERASJON AS ENUM ('START', 'OPPDATER', 'STOPP'); +CREATE TYPE OVERSIKTEN_KATEGORI AS ENUM ('UDELT_SAMTALEREFERAT'); + +create table oversikten_melding_med_metadata +( + id SERIAL PRIMARY KEY, + melding_key uuid not null, + fnr varchar(11) not null, + opprettet timestamp not null, + tidspunkt_sendt timestamp, + utsending_status OVERSIKTEN_UTSENDING_STATUS not null, + melding json not null, + kategori OVERSIKTEN_KATEGORI not null, + operasjon OVERSIKTEN_OPERASJON not null +); +create index oversikten_melding_med_metadata_melding_key_pk on oversikten_melding_med_metadata (melding_key); +create index oversikten_melding_med_metadata_utsending_status_idx on oversikten_melding_med_metadata (utsending_status); + +create function melding_key_eksisterer(key uuid) returns boolean as +$$ +select exists (select from oversikten_melding_med_metadata where melding_key = key); +$$ language sql; + +create function aktivitet_id_eksisterer(akt_id bigint) returns boolean as +$$ +select exists (select from aktivitet where aktivitet_id = akt_id); +$$ language sql; + + +create table oversikten_melding_aktivitet_mapping +( + oversikten_melding_key uuid not null check (melding_key_eksisterer(oversikten_melding_key)), + aktivitet_id bigint not null check (aktivitet_id_eksisterer(aktivitet_id)), + kategori oversikten_kategori not null, + PRIMARY KEY (aktivitet_id, kategori) +); +CREATE INDEX oversikten_melding_aktivitet_mapping_melding_key ON oversikten_melding_aktivitet_mapping (oversikten_melding_key); +CREATE INDEX oversikten_melding_aktivitet_mapping_aktivitet_id ON oversikten_melding_aktivitet_mapping (aktivitet_id); diff --git a/src/test/java/no/nav/veilarbaktivitet/db/DbTestUtils.java b/src/test/java/no/nav/veilarbaktivitet/db/DbTestUtils.java index 072cd076e..2420d7db2 100644 --- a/src/test/java/no/nav/veilarbaktivitet/db/DbTestUtils.java +++ b/src/test/java/no/nav/veilarbaktivitet/db/DbTestUtils.java @@ -30,7 +30,9 @@ public class DbTestUtils { "AKTIVITET", "FORHAANDSORIENTERING", "SHEDLOCK", - "ID_MAPPINGER" + "ID_MAPPINGER", + "OVERSIKTEN_MELDING_AKTIVITET_MAPPING", + "OVERSIKTEN_MELDING_MED_METADATA" ); public static void cleanupTestDb(JdbcTemplate db) { diff --git a/src/test/java/no/nav/veilarbaktivitet/util/AktivitetTestService.java b/src/test/java/no/nav/veilarbaktivitet/util/AktivitetTestService.java index 7ea3956f3..2cdc020f4 100644 --- a/src/test/java/no/nav/veilarbaktivitet/util/AktivitetTestService.java +++ b/src/test/java/no/nav/veilarbaktivitet/util/AktivitetTestService.java @@ -527,6 +527,37 @@ public SendResult opprettEksterntAktivitetsKort(ProducerRecord p return sendResult; } + @SneakyThrows + public AktivitetDTO publiserReferat(AktivitetDTO aktivitetDTO, MockVeileder veileder) { + aktivitetDTO.setErReferatPublisert(true); + Response response = veileder + .createRequest() + .and() + .body(JsonUtils.toJson(aktivitetDTO)) + .when() + .put("http://localhost:" + port + "/veilarbaktivitet/api/aktivitet/" + aktivitetDTO.getId() + "/referat/publiser") + .then() + .assertThat().statusCode(HttpStatus.OK.value()) + .extract().response(); + + return response.as(AktivitetDTO.class); + } + + @SneakyThrows + public AktivitetDTO oppdaterReferat(AktivitetDTO aktivitetDTO, MockVeileder veileder) { + Response response = veileder + .createRequest() + .and() + .body(JsonUtils.toJson(aktivitetDTO)) + .when() + .put("http://localhost:" + port + "/veilarbaktivitet/api/aktivitet/" + aktivitetDTO.getId() + "/referat") + .then() + .assertThat().statusCode(HttpStatus.OK.value()) + .extract().response(); + + return response.as(AktivitetDTO.class); + } + @SneakyThrows public void kasserEskterntAktivitetskort(KasseringsBestilling kasseringsBestilling) { var record = new ProducerRecord<>(aktivitetsKortV1Topic, kasseringsBestilling.getAktivitetskortId().toString(), JsonUtils.toJson(kasseringsBestilling)); diff --git a/src/test/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanControllerTest.kt b/src/test/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanControllerTest.kt index 7e988b7e6..042b36074 100644 --- a/src/test/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanControllerTest.kt +++ b/src/test/kotlin/no/nav/veilarbaktivitet/aktivitet/AktivitetsplanControllerTest.kt @@ -3,12 +3,24 @@ package no.nav.veilarbaktivitet.aktivitet import no.nav.common.json.JsonUtils import no.nav.veilarbaktivitet.SpringBootTestBase import no.nav.veilarbaktivitet.aktivitet.dto.AktivitetTypeDTO +import no.nav.veilarbaktivitet.oversikten.OversiktenMelding +import no.nav.veilarbaktivitet.oversikten.OversiktenMeldingMedMetadataDAO +import no.nav.veilarbaktivitet.oversikten.UtsendingStatus import no.nav.veilarbaktivitet.testutils.AktivitetDtoTestBuilder +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.within import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus +import java.time.ZonedDateTime +import java.time.temporal.ChronoUnit + +internal class AktivitetsplanControllerTest: SpringBootTestBase() { + + @Autowired + lateinit var oversiktenMeldingMedMetadataRepository: OversiktenMeldingMedMetadataDAO; -internal class AktivitetsplanControllerTest : SpringBootTestBase() { @Test fun veileder_skal_kunne_oprette_aktivitet() { val happyBruker = navMockService.createBruker() @@ -176,4 +188,129 @@ internal class AktivitetsplanControllerTest : SpringBootTestBase() { .assertThat() .statusCode(HttpStatus.FORBIDDEN.value()) } + + @Test + fun når_udelt_referat_opprettes_så_sendes_melding_til_oversikten() { + val happyBruker = navMockService.createBruker() + val veileder = navMockService.createVeileder(happyBruker) + val aktivitet = aktivitetTestService.opprettAktivitet( + happyBruker, + veileder, + AktivitetDtoTestBuilder.nyAktivitet(AktivitetTypeDTO.MOTE).setErReferatPublisert(false).setReferat("") + ) + val oppdatertAktivitet = aktivitet + oppdatertAktivitet.setReferat("Et referat") + val aktivitetPayloadJson = JsonUtils.toJson(oppdatertAktivitet) + + veileder + .createRequest(happyBruker) + .body(aktivitetPayloadJson) + .put("http://localhost:$port/veilarbaktivitet/api/aktivitet/${aktivitet.id}/referat") + .then() + .assertThat() + .statusCode(HttpStatus.OK.value()) + + val meldingerTilOversikten = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes() + assertThat(meldingerTilOversikten).hasSize(1) + val melding = meldingerTilOversikten.first() + assertThat(melding.fnr.get()).isEqualTo(happyBruker.fnr) + assertThat(melding.kategori).isEqualTo(OversiktenMelding.Kategori.UDELT_SAMTALEREFERAT) + assertThat(melding.opprettet).isCloseTo(ZonedDateTime.now(), within(1, ChronoUnit.SECONDS)) + assertThat(melding.tidspunktSendt).isNull() + assertThat(melding.utsendingStatus).isEqualTo(UtsendingStatus.SKAL_SENDES) + } + + @Test + fun skal_ikke_sende_startmelding_når_referat_oppdateres() { + val happyBruker = navMockService.createBruker() + val veileder = navMockService.createVeileder(happyBruker) + val aktivitet = aktivitetTestService.opprettAktivitet( + happyBruker, + veileder, + AktivitetDtoTestBuilder.nyAktivitet(AktivitetTypeDTO.MOTE).setErReferatPublisert(false).setReferat("Et referat") + ) + val oppdatertAktivitet = aktivitet + oppdatertAktivitet.setReferat("Et oppdatert referat") + val aktivitetPayloadJson = JsonUtils.toJson(oppdatertAktivitet) + val antallMeldingerTilOversiktenFørOppdatering = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes().size + + veileder + .createRequest(happyBruker) + .body(aktivitetPayloadJson) + .put("http://localhost:$port/veilarbaktivitet/api/aktivitet/${aktivitet.id}/referat") + .then() + .assertThat() + .statusCode(HttpStatus.OK.value()) + + val antallMeldingerTilOversiktenEtterOppdatering = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes().size + assertThat(antallMeldingerTilOversiktenEtterOppdatering).isEqualTo(antallMeldingerTilOversiktenFørOppdatering) + } + + @Test + fun når_samtalereferat_opprettes_med_referat_sendes_melding_til_oversikten() { + val happyBruker = navMockService.createBruker() + val veileder = navMockService.createVeileder(happyBruker) + + aktivitetTestService.opprettAktivitet( + happyBruker, + veileder, + AktivitetDtoTestBuilder.nyAktivitet(AktivitetTypeDTO.SAMTALEREFERAT).setErReferatPublisert(false).setReferat("Et referat") + ) + + val antallMeldingerTilOversikten = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes().size + assertThat(antallMeldingerTilOversikten).isEqualTo(1) + } + + @Test + fun når_samtalereferat_deles_med_bruker_sendes_stoppmelding_til_oversikten(){ + val happyBruker = navMockService.createBruker() + val veileder = navMockService.createVeileder(happyBruker) + val aktivitet = aktivitetTestService.opprettAktivitet( + happyBruker, + veileder, + AktivitetDtoTestBuilder.nyAktivitet(AktivitetTypeDTO.SAMTALEREFERAT).setErReferatPublisert(false).setReferat("Et referat") + ) + assertThat(oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes().size).isEqualTo(1) + + aktivitetTestService.publiserReferat(aktivitet, veileder) + + val meldingerTilOversikten = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes() + assertThat(meldingerTilOversikten.size).isEqualTo(2) + val sisteMelding = meldingerTilOversikten.maxBy { it.opprettet } + assertThat(sisteMelding.operasjon).isEqualTo(OversiktenMelding.Operasjon.STOPP) + } + + @Test + fun når_referat_på_møte_opprettes_sendes_melding_til_oversikten() { + val happyBruker = navMockService.createBruker() + val veileder = navMockService.createVeileder(happyBruker) + val aktivitet = aktivitetTestService.opprettAktivitet( + happyBruker, + veileder, + AktivitetDtoTestBuilder.nyAktivitet(AktivitetTypeDTO.MOTE).setReferat(null) + ) + + aktivitet.setReferat("Et referat") + aktivitetTestService.oppdaterReferat(aktivitet, veileder) + + val meldingerTilOversikten = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes() + assertThat(meldingerTilOversikten.size).isEqualTo(1) + val sisteMelding = meldingerTilOversikten.maxBy { it.opprettet } + assertThat(sisteMelding.operasjon).isEqualTo(OversiktenMelding.Operasjon.START) + } + + @Test + fun `Ikke send melding til oversikten når samtalereferat deles når det opprettes`() { + val happyBruker = navMockService.createBruker() + val veileder = navMockService.createVeileder(happyBruker) + + aktivitetTestService.opprettAktivitet( + happyBruker, + veileder, + AktivitetDtoTestBuilder.nyAktivitet(AktivitetTypeDTO.SAMTALEREFERAT).setErReferatPublisert(true).setReferat("Et referat") + ) + + val meldingerTilOversikten = oversiktenMeldingMedMetadataRepository.hentAlleSomSkalSendes() + assertThat(meldingerTilOversikten).isEmpty() + } } diff --git a/src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingTest.kt b/src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingTest.kt new file mode 100644 index 000000000..7a06e514e --- /dev/null +++ b/src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenMeldingTest.kt @@ -0,0 +1,38 @@ +package no.nav.veilarbaktivitet.oversikten + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.within +import org.junit.jupiter.api.Test +import java.time.LocalDateTime +import java.time.temporal.ChronoUnit + +class OversiktenMeldingTest { + + @Test + fun `Melding for udelt samtalereferat skal ha riktige verdier`() { + val fnr = "1234567891234" + val melding = OversiktenMelding.forUdeltSamtalereferat(fnr = fnr, operasjon = OversiktenMelding.Operasjon.START, erProd = false) + assertThat(melding.kategori).isEqualTo(OversiktenMelding.Kategori.UDELT_SAMTALEREFERAT) + assertThat(melding.avsender).isEqualTo("veilarbaktivitet") + assertThat(melding.personID).isEqualTo(fnr) + assertThat(melding.operasjon).isEqualTo(OversiktenMelding.Operasjon.START) + assertThat(melding.hendelse.beskrivelse).isEqualTo("Bruker har et udelt samtalereferat") + assertThat(melding.hendelse.lenke).isEqualTo("https://veilarbpersonflate.ansatt.dev.nav.no/aktivitetsplan") + assertThat(melding.hendelse.dato).isCloseTo(LocalDateTime.now(), within(100, ChronoUnit.MILLIS)) + } + + @Test + fun `Melding om udelt samtalereferat skal ha riktig URL for prod`() { + val fnr = "1234567891234" + val melding = OversiktenMelding.forUdeltSamtalereferat(fnr = fnr, operasjon = OversiktenMelding.Operasjon.START, erProd = true) + assertThat(melding.hendelse.lenke).isEqualTo("https://veilarbpersonflate.intern.nav.no/aktivitetsplan") + } + + @Test + fun `Stoppmelding for udelt samtalereferat skal ha riktig URL for prod`() { + val fnr = "1234567891234" + val melding = OversiktenMelding.forUdeltSamtalereferat(fnr = fnr, operasjon = OversiktenMelding.Operasjon.STOPP, erProd = true) + assertThat(melding.hendelse.lenke).isEqualTo("https://veilarbpersonflate.intern.nav.no/aktivitetsplan") + assertThat(melding.operasjon).isEqualTo(OversiktenMelding.Operasjon.STOPP) + } +} \ No newline at end of file diff --git a/src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenServiceTest.kt b/src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenServiceTest.kt new file mode 100644 index 000000000..dc60a4560 --- /dev/null +++ b/src/test/kotlin/no/nav/veilarbaktivitet/oversikten/OversiktenServiceTest.kt @@ -0,0 +1,105 @@ +package no.nav.veilarbaktivitet.oversikten + +import no.nav.common.types.identer.Fnr +import no.nav.veilarbaktivitet.SpringBootTestBase +import no.nav.veilarbaktivitet.mock_nav_modell.MockBruker +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.within +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.mockito.Mockito.* +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.context.bean.override.mockito.MockitoBean +import java.time.ZonedDateTime +import java.time.temporal.ChronoUnit +import java.util.* + +open class OversiktenServiceTest: SpringBootTestBase() { + + @MockitoBean + private lateinit var oversiktenProducer: OversiktenProducer + + @Autowired + private lateinit var oversiktenMeldingMedMetadataDAO: OversiktenMeldingMedMetadataDAO + + @Autowired + private lateinit var oversiktenService: OversiktenService + + @BeforeEach + fun beforeEach() { + jdbcTemplate.execute("TRUNCATE TABLE oversikten_melding_med_metadata") + } + + @Test + fun `Skal sende usendte meldinger`() { + val bruker = navMockService.createHappyBruker() + val melding = melding(bruker, utsendingStatus = UtsendingStatus.SKAL_SENDES) + val alleredeSendtMelding = melding(bruker, utsendingStatus = UtsendingStatus.SENDT) + val meldingId = oversiktenMeldingMedMetadataDAO.lagre(melding) + oversiktenMeldingMedMetadataDAO.lagre(alleredeSendtMelding) + + oversiktenService.sendUsendteMeldingerTilOversikten() + + verify(oversiktenProducer, times(1)) + .sendMelding(melding.meldingKey.toString(), melding.meldingSomJson) + verifyNoMoreInteractions(oversiktenProducer) + val sendtMelding = oversiktenMeldingMedMetadataDAO.hent(meldingId)!! + assertThat(sendtMelding.tidspunktSendt).isCloseTo(ZonedDateTime.now(), within(500, ChronoUnit.MILLIS)) + assertThat(sendtMelding.utsendingStatus).isEqualTo(UtsendingStatus.SENDT) + } + + @Test + fun `Skal ikke sende melding som er markert som SENDT`() { + val bruker = navMockService.createHappyBruker() + val melding = melding(bruker, utsendingStatus = UtsendingStatus.SENDT) + oversiktenMeldingMedMetadataDAO.lagre(melding) + + oversiktenService.sendUsendteMeldingerTilOversikten() + + verifyNoInteractions(oversiktenProducer) + } + + @Test + fun `Skal ikke sende melding som er markert som SKAL_IKKE_SENDES`() { + val bruker = navMockService.createHappyBruker() + val melding = melding(bruker, utsendingStatus = UtsendingStatus.SKAL_IKKE_SENDES) + oversiktenMeldingMedMetadataDAO.lagre(melding) + + oversiktenService.sendUsendteMeldingerTilOversikten() + + verifyNoInteractions(oversiktenProducer) + } + + @Test + fun `Nye meldinger skal ikke påvirke andre meldinger`() { + val bruker = navMockService.createHappyBruker() + val førsteMelding = melding(bruker, utsendingStatus = UtsendingStatus.SENDT) + val førsteMeldingID = oversiktenMeldingMedMetadataDAO.lagre(førsteMelding) + val andreMelding = melding(meldingKey = førsteMelding.meldingKey, bruker = bruker, utsendingStatus = UtsendingStatus.SKAL_SENDES) + oversiktenMeldingMedMetadataDAO.lagre(andreMelding) + + oversiktenService.sendUsendteMeldingerTilOversikten() + + val førsteMeldingEtterAndreMeldingErSendt = oversiktenMeldingMedMetadataDAO.hent(førsteMeldingID)!! + assertThat(førsteMelding.tidspunktSendt).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.tidspunktSendt) + assertThat(førsteMelding.fnr).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.fnr) + assertThat(førsteMelding.meldingSomJson).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.meldingSomJson) + assertThat(førsteMelding.kategori).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.kategori) + assertThat(førsteMelding.operasjon).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.operasjon) + assertThat(førsteMelding.opprettet.truncatedTo(ChronoUnit.MILLIS)).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.opprettet.truncatedTo( + ChronoUnit.MILLIS)) + assertThat(førsteMelding.utsendingStatus).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.utsendingStatus) + assertThat(førsteMelding.meldingKey).isEqualTo(førsteMeldingEtterAndreMeldingErSendt.meldingKey) + } + + private fun melding(bruker: MockBruker, meldingKey: UUID = UUID.randomUUID(), utsendingStatus: UtsendingStatus = UtsendingStatus.SKAL_SENDES) = + OversiktenMeldingMedMetadata( + fnr = Fnr.of(bruker.fnr), + meldingSomJson = "{}", + kategori = OversiktenMelding.Kategori.UDELT_SAMTALEREFERAT, + meldingKey = meldingKey, + utsendingStatus = utsendingStatus, + operasjon = OversiktenMelding.Operasjon.START + ) + +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 0bb988bb4..92d599cd9 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -54,6 +54,7 @@ topic.inn.aktivitetskort=dab.aktivitetskort-v1.1 topic.ut.aktivitetskort-feil=dab.aktivitetskort-feil-v1 topic.ut.aktivitetskort-idmapping=dab.aktivitetskort-idmapping-v1 topic.inn.kvpAvsluttet=pto.kvp-avsluttet-v1 +topic.ut.oversikten=obo.portefolje-hendelsesfilter-v1 ############################################### # Consumer # ###############################################