From 563118a853e17fdb40815442ce49ad83e7be5216 Mon Sep 17 00:00:00 2001 From: Nils Martin Sande Date: Mon, 28 Oct 2024 07:30:12 +0100 Subject: [PATCH] =?UTF-8?q?La=20inn=20st=C3=B8tte=20for=20=C3=A5=20oppdage?= =?UTF-8?q?=20id=20merges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nav/paw/kafkakeygenerator/Applikasjon.kt | 40 +++++++++++++---- .../no/nav/paw/kafkakeygenerator/Either.kt | 29 ++++++++++++- .../no/nav/paw/kafkakeygenerator/Result.kt | 13 +++++- .../api/v2/ApiEndepunktV2.kt | 2 +- .../kafkakeygenerator/api/v2/InfoResponse.kt | 22 +++------- .../mergedetector/FailureBuilder.kt | 14 ++++++ .../mergedetector/FindMerge.kt | 20 +++++++++ .../mergedetector/HentLagretData.kt | 43 +++++++++++++++++++ .../mergedetector/vo/LagretData.kt | 9 ++++ .../mergedetector/vo/MergeDetected.kt | 17 ++++++++ .../no/nav/paw/kafkakeygenerator/vo/Info.kt | 22 ++++++++++ .../main/resources/openapi/documentation.yaml | 18 ++++++++ 12 files changed, 219 insertions(+), 30 deletions(-) create mode 100644 apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FailureBuilder.kt create mode 100644 apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FindMerge.kt create mode 100644 apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/HentLagretData.kt create mode 100644 apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/LagretData.kt create mode 100644 apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/MergeDetected.kt create mode 100644 apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/vo/Info.kt diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Applikasjon.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Applikasjon.kt index 2ac43814..6552a5df 100644 --- a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Applikasjon.kt +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Applikasjon.kt @@ -4,19 +4,36 @@ import io.opentelemetry.instrumentation.annotations.WithSpan import no.nav.paw.kafkakeygenerator.FailureCode.CONFLICT import no.nav.paw.kafkakeygenerator.FailureCode.DB_NOT_FOUND import no.nav.paw.kafkakeygenerator.api.v2.* +import no.nav.paw.kafkakeygenerator.mergedetector.findMerge +import no.nav.paw.kafkakeygenerator.mergedetector.hentLagretData +import no.nav.paw.kafkakeygenerator.mergedetector.vo.MergeDetected import no.nav.paw.kafkakeygenerator.pdl.PdlIdentitesTjeneste -import no.nav.paw.kafkakeygenerator.vo.ArbeidssoekerId -import no.nav.paw.kafkakeygenerator.vo.CallId -import no.nav.paw.kafkakeygenerator.vo.Identitetsnummer -import kotlin.math.absoluteValue +import no.nav.paw.kafkakeygenerator.vo.* class Applikasjon( private val kafkaKeys: KafkaKeys, private val identitetsTjeneste: PdlIdentitesTjeneste ) { + @WithSpan + suspend fun validerLagretData(callId: CallId, identitet: Identitetsnummer): Either { + return hentInfo(callId, identitet) + .flatMap { info -> + hentLagretData( + hentArbeidssoekerId = kafkaKeys::hent, + info = info + ).map { info to it } + } + .map { (info, lagretDatra ) -> info to findMerge(lagretDatra) } + .map { (info, merge) -> + InfoResponse( + info = info, + mergeDetected = merge as? MergeDetected + ) + } + } @WithSpan - suspend fun hentInfo(callId: CallId, identitet: Identitetsnummer): Either { + suspend fun hentInfo(callId: CallId, identitet: Identitetsnummer): Either { val pdlIdInfo = identitetsTjeneste.hentIdentInformasjon( callId = callId, identitet = identitet, @@ -29,11 +46,16 @@ class Applikasjon( recordKey = publicTopicKeyFunction(arbeidssoekerId).value ) }.map { lokalIdData -> - InfoResponse( + Info( + identitetsnummer = identitet.value, lagretData = lokalIdData, pdlData = pdlIdInfo.fold( { PdlData(error = it.code.name, id = null) }, - { PdlData(error = null, id = it.map { identInfo -> PdlId(identInfo.gruppe.name, identInfo.ident) })} + { + PdlData( + error = null, + id = it.map { identInfo -> PdlId(identInfo.gruppe.name, identInfo.ident) }) + } ) ) } @@ -42,7 +64,7 @@ class Applikasjon( @WithSpan suspend fun hent(callId: CallId, identitet: Identitetsnummer): Either { return kafkaKeys.hent(identitet) - .recover(DB_NOT_FOUND) { + .suspendingRecover(DB_NOT_FOUND) { sjekkMotAliaser(callId, identitet) } } @@ -50,7 +72,7 @@ class Applikasjon( @WithSpan suspend fun hentEllerOpprett(callId: CallId, identitet: Identitetsnummer): Either { return hent(callId, identitet) - .recover(DB_NOT_FOUND) { + .suspendingRecover(DB_NOT_FOUND) { kafkaKeys.opprett(identitet) }.recover(CONFLICT) { kafkaKeys.hent(identitet) diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Either.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Either.kt index 1c6c678c..3b0d85bf 100644 --- a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Either.kt +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Either.kt @@ -15,6 +15,7 @@ fun Either.flatMap(f: (R1) -> Either): Either f(right) is Left -> this } + data class Right(val right: R) : Either { override fun map(f: (R) -> R2): Right = Right(f(right)) override fun mapLeft(f: (Nothing) -> L2): Right = this @@ -24,10 +25,34 @@ data class Right(val right: R) : Either { override fun fold(onLeft: (Nothing) -> V, onRight: (R) -> V): V = onRight(right) } -data class Left(val left: L): Either { +data class Left(val left: L) : Either { override fun map(f: (Nothing) -> R2): Left = this override fun mapLeft(f: (L) -> L2): Left = Left(f(left)) override fun onLeft(f: (L) -> Unit) = this.also { f(left) } override fun onRight(f: (Nothing) -> Unit) = this override fun fold(onLeft: (L) -> V, onRight: (Nothing) -> V): V = onLeft(left) -} \ No newline at end of file +} + +fun >> Either.flatten(): Either> { + return flatMap { list -> + val lefts = list.filterIsInstance>() + if (lefts.isNotEmpty()) { + lefts.first() + } else { + list.filterIsInstance>() + .map { it.right } + .let(::right) + } + } +} + +fun >> C.flatten(): Either> { + val lefts = filterIsInstance>() + return if (lefts.isNotEmpty()) { + lefts.first() + } else { + filterIsInstance>() + .map { it.right } + .let(::right) + } +} diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Result.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Result.kt index 2260aa7c..64d905de 100644 --- a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Result.kt +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/Result.kt @@ -40,7 +40,18 @@ fun Either.mapToFailure( } } -suspend fun Either.recover(failureCode: FailureCode, f: suspend () -> Either): Either { +suspend fun Either.suspendingRecover(failureCode: FailureCode, f: suspend () -> Either): Either { + return when (this) { + is Right -> this + is Left -> if (left.code == failureCode) { + f() + } else { + this + } + } +} + +fun Either.recover(failureCode: FailureCode, f: () -> Either): Either { return when (this) { is Right -> this is Left -> if (left.code == failureCode) { diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/ApiEndepunktV2.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/ApiEndepunktV2.kt index 23cd0a86..4d271240 100644 --- a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/ApiEndepunktV2.kt +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/ApiEndepunktV2.kt @@ -45,7 +45,7 @@ suspend fun PipelineContext.hentInfo( ?.let { CallId(it) } ?: CallId(UUID.randomUUID().toString()) val request = call.receive() - when (val resultat = applikasjon.hentInfo(callId, Identitetsnummer(request.ident))) { + when (val resultat = applikasjon.validerLagretData(callId, Identitetsnummer(request.ident))) { is Left -> call.respond( status = InternalServerError, message = resultat.left.code.name diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/InfoResponse.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/InfoResponse.kt index c9b091fd..893bcea7 100644 --- a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/InfoResponse.kt +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/api/v2/InfoResponse.kt @@ -1,21 +1,9 @@ package no.nav.paw.kafkakeygenerator.api.v2 -data class InfoResponse( - val lagretData: LokalIdData?, - val pdlData: PdlData -) - -data class LokalIdData( - val arbeidsoekerId: Long, - val recordKey: Long -) +import no.nav.paw.kafkakeygenerator.mergedetector.vo.MergeDetected +import no.nav.paw.kafkakeygenerator.vo.Info -data class PdlData( - val error: String?, - val id: List? -) - -data class PdlId( - val gruppe: String, - val id: String +data class InfoResponse( + val info: Info, + val mergeDetected: MergeDetected? ) \ No newline at end of file diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FailureBuilder.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FailureBuilder.kt new file mode 100644 index 00000000..a1dcd5f6 --- /dev/null +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FailureBuilder.kt @@ -0,0 +1,14 @@ +package no.nav.paw.kafkakeygenerator.mergedetector + +import no.nav.paw.kafkakeygenerator.Failure +import no.nav.paw.kafkakeygenerator.FailureCode + +fun failure(code: FailureCode) : Failure = + Failure( + system = when (code) { + FailureCode.PDL_NOT_FOUND -> "pdl" + FailureCode.DB_NOT_FOUND -> "database" + else -> "other" + }, + code = code + ) \ No newline at end of file diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FindMerge.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FindMerge.kt new file mode 100644 index 00000000..79dbb089 --- /dev/null +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/FindMerge.kt @@ -0,0 +1,20 @@ +package no.nav.paw.kafkakeygenerator.mergedetector + +import no.nav.paw.kafkakeygenerator.mergedetector.vo.LagretData +import no.nav.paw.kafkakeygenerator.mergedetector.vo.MergeDetected +import no.nav.paw.kafkakeygenerator.mergedetector.vo.MergeDetectorResult +import no.nav.paw.kafkakeygenerator.mergedetector.vo.NoMergeDetected + +fun findMerge(lagretData: LagretData): MergeDetectorResult { + val etterArbeidssoekerId = lagretData.lagretData + .toList() + .mapNotNull { (id, arbeidssoekerId) -> arbeidssoekerId?.let { it to id } } + .groupBy { it.first } + .toMap() + .mapValues { (_, value) -> value.map { it.second } } + return if (etterArbeidssoekerId.size > 1) { + MergeDetected(lagretData.identitetsnummer, etterArbeidssoekerId) + } else { + NoMergeDetected(lagretData.identitetsnummer) + } +} \ No newline at end of file diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/HentLagretData.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/HentLagretData.kt new file mode 100644 index 00000000..09519d79 --- /dev/null +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/HentLagretData.kt @@ -0,0 +1,43 @@ +package no.nav.paw.kafkakeygenerator.mergedetector + +import io.opentelemetry.instrumentation.annotations.WithSpan +import kotlinx.coroutines.yield +import no.nav.paw.kafkakeygenerator.* +import no.nav.paw.kafkakeygenerator.FailureCode.DB_NOT_FOUND +import no.nav.paw.kafkakeygenerator.FailureCode.PDL_NOT_FOUND +import no.nav.paw.kafkakeygenerator.vo.Info +import no.nav.paw.kafkakeygenerator.vo.PdlId +import no.nav.paw.kafkakeygenerator.mergedetector.vo.LagretData +import no.nav.paw.kafkakeygenerator.vo.ArbeidssoekerId +import no.nav.paw.kafkakeygenerator.vo.Identitetsnummer +import kotlin.coroutines.Continuation +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +import kotlin.coroutines.createCoroutine + +@WithSpan +fun hentLagretData( + hentArbeidssoekerId: (Identitetsnummer) -> Either, + info: Info +): Either { + if (info.lagretData == null) return left(failure(DB_NOT_FOUND)) // Er ikke noe lagret kan vi ikke ha en merge + val pdlData = info.pdlData.id + if (pdlData.isNullOrEmpty()) return left(failure(PDL_NOT_FOUND)) // Er ikke noe pdl data kan vi ikke ha en merge + return pdlData + .asSequence() + .filter { it.gruppe == "FOLKEREGISTERIDENT" } + .map(PdlId::id) + .map(::Identitetsnummer) + .map { identitetsnummer -> + hentArbeidssoekerId(identitetsnummer) + .recover(DB_NOT_FOUND) { right(null) } + .map { identitetsnummer to it } + } + .toList() + .flatten() + .map { it.toMap() } + .map { LagretData( + identitetsnummer = Identitetsnummer(info.identitetsnummer), + lagretData = it + ) } +} diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/LagretData.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/LagretData.kt new file mode 100644 index 00000000..8da8ce4a --- /dev/null +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/LagretData.kt @@ -0,0 +1,9 @@ +package no.nav.paw.kafkakeygenerator.mergedetector.vo + +import no.nav.paw.kafkakeygenerator.vo.ArbeidssoekerId +import no.nav.paw.kafkakeygenerator.vo.Identitetsnummer + +data class LagretData( + val identitetsnummer: Identitetsnummer, + val lagretData: Map +) \ No newline at end of file diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/MergeDetected.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/MergeDetected.kt new file mode 100644 index 00000000..a2da5eb1 --- /dev/null +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/mergedetector/vo/MergeDetected.kt @@ -0,0 +1,17 @@ +package no.nav.paw.kafkakeygenerator.mergedetector.vo + +import no.nav.paw.kafkakeygenerator.vo.ArbeidssoekerId +import no.nav.paw.kafkakeygenerator.vo.Identitetsnummer + +interface MergeDetectorResult { + val id: Identitetsnummer +} + +data class NoMergeDetected( + override val id: Identitetsnummer +) : MergeDetectorResult + +data class MergeDetected( + override val id: Identitetsnummer, + val map: Map> +) : MergeDetectorResult \ No newline at end of file diff --git a/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/vo/Info.kt b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/vo/Info.kt new file mode 100644 index 00000000..7d2b1127 --- /dev/null +++ b/apps/kafka-key-generator/src/main/kotlin/no/nav/paw/kafkakeygenerator/vo/Info.kt @@ -0,0 +1,22 @@ +package no.nav.paw.kafkakeygenerator.vo + +data class Info( + val identitetsnummer: String, + val lagretData: LokalIdData?, + val pdlData: PdlData +) + +data class LokalIdData( + val arbeidsoekerId: Long, + val recordKey: Long +) + +data class PdlData( + val error: String?, + val id: List? +) + +data class PdlId( + val gruppe: String, + val id: String +) \ No newline at end of file diff --git a/apps/kafka-key-generator/src/main/resources/openapi/documentation.yaml b/apps/kafka-key-generator/src/main/resources/openapi/documentation.yaml index e8fb57fc..b4e30d82 100644 --- a/apps/kafka-key-generator/src/main/resources/openapi/documentation.yaml +++ b/apps/kafka-key-generator/src/main/resources/openapi/documentation.yaml @@ -103,6 +103,24 @@ components: InfoResponse: type: "object" properties: + info: + $ref: "#/components/schemas/Info" + mergeDetected: + type: "object" + properties: + id: + type: "object" + properties: + value: + type: "string" + map: + type: "object" + additionalProperties: true + Info: + type: "object" + properties: + identitetsnummer: + type: "string" lagretData: type: "object" properties: