Skip to content

Commit ada76ee

Browse files
committed
Flyttet bekreftelse-tjeneste til intern
1 parent d19acb3 commit ada76ee

File tree

17 files changed

+763
-1
lines changed

17 files changed

+763
-1
lines changed

apps/bekreftelse-tjeneste/README.md

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
```mermaid
2+
sequenceDiagram
3+
Registeret->>PeriodeTopic: Periode startet
4+
PeriodeTopic-->>MeldepliktTjeneste: Periode startet
5+
MeldepliktTjeneste-->>EndringerTopic: Melding X dager før forfall
6+
MeldepliktTjeneste-->>EndringerTopic: Melding OK ved innsending
7+
MeldepliktTjeneste-->>EndringerTopic: Frist utløpt
8+
MeldepliktTjeneste-->>EndringerTopic: Graceperiode utløpt
9+
PeriodeTopic-->>MeldepliktTjeneste: Periode avsluttet
10+
MeldepliktTjeneste-->>EndringerTopic: Periode avsluttet
11+
```
12+
13+
```mermaid
14+
sequenceDiagram
15+
Registeret->>PeriodeTopic: Periode startet
16+
Dagpenger-->>AnsvarsTopic: Dagpenger startet
17+
AnsvarsTopic-->>MeldepliktTjeneste: Dagpenger tar over
18+
MeldepliktTjeneste-->>EndringerTopic:
19+
```
20+
21+
```mermaid
22+
graph LR
23+
periodeTopic((PeriodeTopic)) -- Startet --> Ansvar[Ansvar]
24+
```
25+
```
26+
periode topic:
27+
startet: lagre initiell tilstand
28+
avsluttet: send ok(avsluttet)
29+
:slett tilstand
30+
31+
ansvar topic:
32+
tar ansvar: lagre info om ansvar
33+
:sett tidspunkt for siste melding til record ts for ansvars endring
34+
avslutter ansvar: slett info om ansvar
35+
36+
melding topic:
37+
mottatt: lagre tidspunkt for siste melding
38+
:send OK(mottatt)
39+
dersom ikke ønsker å fortsette:
40+
:send VilAvsluttePerioden
41+
42+
43+
hver x time:
44+
for alle perioder ingen har ansvar for:
45+
dersom tid siden siste melding (eller periode start) > Y dager:
46+
send melding om frist nærmer seg
47+
dersom tid siden siste melding (eller periode start) > Z dager:
48+
send melding om frist utløpt
49+
dersom tid siden siste melding (eller periode start) > Z+Grace dager:
50+
send melding om graceperiode utløpt
51+
for alle perioder andre har ansvar for:
52+
dersom tid siden siste melding (eller periode start) > Z+G+1dag dager:
53+
send melding om graceperiode utløpt
54+
:slett ansvar, vi overtar
55+
```
56+
57+
Modul: endringer til frontend
58+
```
59+
meldings topic:
60+
- OK(mottatt) -> sett oppgave fullført
61+
- OK(avsluttet) -> slett oppgave kansellert
62+
- OK(ansvar) -> sett oppgave fullført
63+
- frist nærmer seg -> opprett oppgave
64+
- frist utløpt -> opprett oppgave
65+
- graceperiode utløpt -> ingenting
66+
```
67+
68+
Modul: endringer til eventlogg
69+
```
70+
meldings topic:
71+
- VilAvsluttePerioden -> send avslutt hendelse til eventlogg
72+
73+
```
74+
* OK (grunn: RapportMottatt, AnsvarFlyttet, PeriodeAvsluttet)
75+
*
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2+
3+
plugins {
4+
kotlin("jvm")
5+
id("com.google.cloud.tools.jib")
6+
}
7+
8+
dependencies {
9+
implementation(project(":lib:hoplite-config"))
10+
implementation(project(":lib:kafka-streams"))
11+
implementation(project(":lib:kafka-key-generator-client"))
12+
implementation(project(":domain:main-avro-schema"))
13+
implementation(project(":domain:rapportering-interne-hendelser"))
14+
implementation(project(":domain:rapporteringsansvar-schema"))
15+
implementation(project(":domain:rapporteringsmelding-schema"))
16+
implementation(orgApacheKafka.kafkaStreams)
17+
implementation(jackson.datatypeJsr310)
18+
implementation(jackson.kotlin)
19+
implementation(apacheAvro.kafkaStreamsAvroSerde)
20+
21+
testImplementation(orgApacheKafka.streamsTest)
22+
testImplementation(testLibs.runnerJunit5)
23+
testImplementation(testLibs.assertionsCore)
24+
}
25+
26+
//enable context receiver
27+
tasks.withType<KotlinCompile>().configureEach {
28+
compilerOptions {
29+
freeCompilerArgs.add("-Xcontext-receivers")
30+
}
31+
}
32+
33+
tasks.withType<Test>().configureEach {
34+
useJUnitPlatform()
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import no.nav.paw.config.kafka.streams.genericProcess
4+
import no.nav.paw.rapportering.ansvar.v1.AnsvarEndret
5+
import no.nav.paw.rapportering.internehendelser.RapporteringsHendelse
6+
import org.apache.kafka.streams.StreamsBuilder
7+
8+
context(ApplicationConfiguration, ApplicationContext)
9+
fun StreamsBuilder.processAnsvarTopic() {
10+
stream<Long, AnsvarEndret>(ansvarsTopic)
11+
.genericProcess<Long, AnsvarEndret, Long, RapporteringsHendelse>("ansvarEndret", statStoreName) { record ->
12+
13+
}
14+
}
15+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import java.time.Duration
4+
5+
data class ApplicationConfiguration(
6+
val periodeTopic: String,
7+
val ansvarsTopic: String,
8+
val rapporteringsTopic: String,
9+
val rapporteringsHendelsesloggTopic: String,
10+
val statStoreName: String,
11+
val punctuateInterval: Duration
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import no.nav.paw.meldeplikttjeneste.tilstand.InternTilstandSerde
4+
import no.nav.paw.rapportering.internehendelser.RapporteringsHendelseSerde
5+
6+
class ApplicationContext(
7+
val internTilstandSerde: InternTilstandSerde,
8+
val rapporteringsHendelseSerde: RapporteringsHendelseSerde
9+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import no.nav.paw.config.kafka.streams.Punctuation
4+
import no.nav.paw.meldeplikttjeneste.tilstand.InternTilstand
5+
import no.nav.paw.meldeplikttjeneste.tilstand.RapporteringsKonfigurasjon
6+
import org.apache.kafka.streams.KeyValue
7+
import org.apache.kafka.streams.processor.PunctuationType
8+
import org.apache.kafka.streams.state.KeyValueStore
9+
import java.time.Instant
10+
import java.util.*
11+
12+
13+
context(ApplicationConfiguration, RapporteringsKonfigurasjon)
14+
fun kontrollerFrister(): Punctuation<Long, Action> = TODO()
15+
16+
operator fun <K, V> KeyValue<K, V>.component1(): K = key
17+
operator fun <K, V> KeyValue<K, V>.component2(): V = value
18+
19+
context(Instant, RapporteringsKonfigurasjon)
20+
fun rapporteringSkalTilgjengeliggjoeres(tilstand: InternTilstand): Boolean {
21+
TODO()
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import no.nav.paw.arbeidssokerregisteret.api.v1.Periode
4+
import no.nav.paw.config.kafka.streams.genericProcess
5+
import no.nav.paw.config.kafka.streams.mapWithContext
6+
import no.nav.paw.kafkakeygenerator.client.KafkaKeysResponse
7+
import no.nav.paw.meldeplikttjeneste.tilstand.InternTilstand
8+
import no.nav.paw.meldeplikttjeneste.tilstand.InternTilstandSerde
9+
import no.nav.paw.meldeplikttjeneste.tilstand.initTilstand
10+
import no.nav.paw.rapportering.internehendelser.PeriodeAvsluttet
11+
import no.nav.paw.rapportering.internehendelser.RapporteringsHendelse
12+
import org.apache.kafka.common.serialization.Serdes
13+
import org.apache.kafka.streams.StreamsBuilder
14+
import org.apache.kafka.streams.kstream.Produced
15+
import org.apache.kafka.streams.state.KeyValueStore
16+
import java.util.*
17+
18+
19+
context(ApplicationConfiguration, ApplicationContext)
20+
fun StreamsBuilder.processPeriodeTopic(kafkaKeyFunction: (String) -> KafkaKeysResponse) {
21+
stream<Long, Periode>(periodeTopic)
22+
.mapWithContext("lagreEllerSlettPeriode", statStoreName) { periode ->
23+
val keyValueStore: KeyValueStore<UUID, InternTilstand> = getStateStore(statStoreName)
24+
val currentState = keyValueStore[periode.id]
25+
val (id, key) = currentState?.let { it.periode.kafkaKeysId to it.periode.recordKey } ?:
26+
kafkaKeyFunction(periode.identitetsnummer).let { it.id to it.key}
27+
when {
28+
currentState == null && periode.avsluttet() -> Action.DoNothing
29+
periode.avsluttet() -> Action.DeleteStateAndEmit(id, periode)
30+
currentState == null -> Action.UpdateState(initTilstand(id = id, key = key, periode = periode))
31+
else -> Action.DoNothing
32+
}
33+
}
34+
.genericProcess<Long, Action, Long, RapporteringsHendelse>(
35+
name = "executeAction",
36+
punctuation = null,
37+
stateStoreNames = arrayOf(statStoreName)
38+
) { record ->
39+
val keyValueStore: KeyValueStore<UUID, InternTilstand> = getStateStore(statStoreName)
40+
when (val action = record.value()) {
41+
is Action.DeleteStateAndEmit -> {
42+
keyValueStore.delete(action.periode.id)
43+
forward(
44+
record.withValue(
45+
PeriodeAvsluttet(
46+
UUID.randomUUID(),
47+
action.periode.id,
48+
action.periode.identitetsnummer,
49+
action.arbeidsoekerId
50+
) as RapporteringsHendelse
51+
)
52+
)
53+
}
54+
55+
Action.DoNothing -> {}
56+
is Action.UpdateState -> keyValueStore.put(action.state.periode.periodeId, action.state)
57+
}
58+
}.to(rapporteringsHendelsesloggTopic, Produced.with(Serdes.Long(), rapporteringsHendelseSerde))
59+
}
60+
61+
fun Periode.avsluttet(): Boolean = avsluttet != null
62+
63+
sealed interface Action {
64+
data object DoNothing : Action
65+
data class DeleteStateAndEmit(val arbeidsoekerId: Long, val periode: Periode) : Action
66+
data class UpdateState(val state: InternTilstand) : Action
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import no.nav.paw.config.kafka.streams.genericProcess
4+
import no.nav.paw.meldeplikttjeneste.tilstand.InternTilstand
5+
import no.nav.paw.rapportering.internehendelser.BaOmAaAvsluttePeriode
6+
import no.nav.paw.rapportering.internehendelser.RapporteringsHendelse
7+
import no.nav.paw.rapportering.internehendelser.RapporteringsMeldingMottatt
8+
import no.nav.paw.rapportering.melding.v1.Melding
9+
import org.apache.kafka.streams.StreamsBuilder
10+
import org.apache.kafka.streams.state.KeyValueStore
11+
import org.slf4j.LoggerFactory
12+
import java.util.*
13+
14+
context(ApplicationConfiguration, ApplicationContext)
15+
fun StreamsBuilder.processRapporteringsMeldingTopic() {
16+
stream<Long, Melding>(rapporteringsTopic)
17+
.genericProcess<Long, Melding, Long, RapporteringsHendelse>(
18+
name = "meldingMottatt",
19+
statStoreName
20+
) { record ->
21+
val gjeldeneTilstand: InternTilstand? = getStateStore<StateStore>(statStoreName)[record.value().periodeId]
22+
if (gjeldeneTilstand == null) {
23+
meldingsLogger.warn("Melding mottatt for periode som ikke er aktiv/eksisterer")
24+
} else {
25+
TODO()
26+
}
27+
28+
}
29+
30+
}
31+
32+
private val meldingsLogger = LoggerFactory.getLogger("meldingsLogger")
33+
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import io.confluent.kafka.streams.serdes.avro.SpecificAvroSerde
4+
import no.nav.paw.config.hoplite.loadNaisOrLocalConfiguration
5+
import no.nav.paw.config.kafka.KAFKA_STREAMS_CONFIG_WITH_SCHEME_REG
6+
import no.nav.paw.config.kafka.KafkaConfig
7+
import no.nav.paw.config.kafka.streams.KafkaStreamsFactory
8+
import no.nav.paw.meldeplikttjeneste.tilstand.InternTilstandSerde
9+
import org.apache.kafka.common.serialization.Serdes
10+
import org.apache.kafka.streams.StreamsBuilder
11+
import org.apache.kafka.streams.state.Stores
12+
13+
const val STATE_STORE = "state-store"
14+
const val APPLICATION_ID_SUFFIX = "beta"
15+
16+
fun main() {
17+
val kafkaConfig = loadNaisOrLocalConfiguration<KafkaConfig>(KAFKA_STREAMS_CONFIG_WITH_SCHEME_REG)
18+
val streamsFactory = KafkaStreamsFactory(APPLICATION_ID_SUFFIX, kafkaConfig)
19+
.withDefaultKeySerde(Serdes.Long()::class)
20+
.withDefaultValueSerde(SpecificAvroSerde::class)
21+
val steamsBuilder = StreamsBuilder()
22+
.addStateStore(
23+
Stores.keyValueStoreBuilder(
24+
Stores.persistentKeyValueStore(STATE_STORE),
25+
Serdes.UUID(),
26+
InternTilstandSerde()
27+
)
28+
)
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package no.nav.paw.meldeplikttjeneste
2+
3+
import no.nav.paw.kafkakeygenerator.client.KafkaKeysResponse
4+
import no.nav.paw.meldeplikttjeneste.tilstand.InternTilstand
5+
import no.nav.paw.rapportering.ansvar.v1.AnsvarEndret
6+
import org.apache.kafka.streams.StreamsBuilder
7+
import org.apache.kafka.streams.Topology
8+
import org.apache.kafka.streams.state.KeyValueStore
9+
import java.util.*
10+
11+
typealias StateStore = KeyValueStore<UUID, InternTilstand>
12+
13+
context(ApplicationConfiguration, ApplicationContext)
14+
fun StreamsBuilder.appTopology(
15+
kafkaKeyFunction: (String) -> KafkaKeysResponse
16+
): Topology {
17+
processPeriodeTopic(kafkaKeyFunction)
18+
processAnsvarTopic()
19+
processRapporteringsMeldingTopic()
20+
21+
return build()
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package no.nav.paw.meldeplikttjeneste.tilstand
2+
3+
import no.nav.paw.arbeidssokerregisteret.api.v1.Periode
4+
import no.nav.paw.rapportering.internehendelser.RapporteringsHendelse
5+
import java.time.Duration
6+
import java.time.Instant
7+
import java.util.UUID
8+
import kotlin.reflect.KClass
9+
10+
@JvmRecord
11+
data class InternTilstand(
12+
val periode: PeriodeInfo,
13+
val utestaaende: List<Rapportering>
14+
)
15+
16+
@JvmRecord
17+
data class Rapportering(
18+
val sisteHandling: KClass<RapporteringsHendelse>,
19+
val rapporteringsId: UUID,
20+
val gjelderFra: Instant,
21+
val gjelderTil: Instant
22+
)
23+
24+
25+
@JvmRecord
26+
data class PeriodeInfo(
27+
val periodeId: UUID,
28+
val identitetsnummer: String,
29+
val kafkaKeysId: Long,
30+
val recordKey: Long,
31+
val avsluttet: Boolean
32+
)
33+
34+
fun initTilstand(
35+
id: Long,
36+
key: Long,
37+
periode: Periode
38+
): InternTilstand =
39+
InternTilstand(
40+
periode = PeriodeInfo(
41+
periodeId = periode.id,
42+
identitetsnummer = periode.identitetsnummer,
43+
kafkaKeysId = id,
44+
recordKey = key,
45+
avsluttet = periode.avsluttet != null
46+
),
47+
utestaaende = emptyList()
48+
)

0 commit comments

Comments
 (0)