diff --git a/.nais/nais-dev-gcp.yaml b/.nais/nais-dev-gcp.yaml index 4288c6a8..21e9a027 100644 --- a/.nais/nais-dev-gcp.yaml +++ b/.nais/nais-dev-gcp.yaml @@ -71,6 +71,7 @@ spec: - host: pensjon-pen-q2.dev-fss-pub.nais.io - host: tp-api-q2.dev-fss-pub.nais.io - host: api.preprod.spk.no + - host: api-test.klp.no azure: application: enabled: true @@ -78,6 +79,7 @@ spec: enabled: true scopes: consumes: + - name: "klp:oftp/simulering" - name: "klp:pensjonsimulering" - name: "nav:pensjon/v1/tpregisteret" liveness: diff --git a/.nais/nais-dev.yaml b/.nais/nais-dev.yaml index 4466e395..e792a241 100644 --- a/.nais/nais-dev.yaml +++ b/.nais/nais-dev.yaml @@ -52,6 +52,8 @@ spec: - application: pensjon-pen-q2 namespace: pensjon-q2 cluster: dev-fss + external: + - host: api-test.klp.no azure: application: enabled: true @@ -60,6 +62,7 @@ spec: scopes: consumes: - name: "klp:pensjonsimulering" + - name: "klp:oftp/simulering" - name: "nav:pensjon/v1/tpregisteret" liveness: path: actuator/health/liveness diff --git a/.nais/nais-prod-gcp.yaml b/.nais/nais-prod-gcp.yaml index 1c540386..e17f47d3 100644 --- a/.nais/nais-prod-gcp.yaml +++ b/.nais/nais-prod-gcp.yaml @@ -46,6 +46,7 @@ spec: - host: pensjon-pen.prod-fss-pub.nais.io - host: tp-api.prod-fss-pub.nais.io - host: api.prod.spk.no + - host: api.klp.no azure: application: enabled: true @@ -53,6 +54,7 @@ spec: enabled: true scopes: consumes: + - name: "klp:oftp/simulering" - name: "klp:pensjonsimulering" - name: "nav:pensjon/v1/tpregisteret" liveness: diff --git a/.nais/nais-prod.yaml b/.nais/nais-prod.yaml index 825791cb..2afa6eb4 100644 --- a/.nais/nais-prod.yaml +++ b/.nais/nais-prod.yaml @@ -38,6 +38,8 @@ spec: - application: pensjon-pen namespace: pensjondeployer cluster: prod-fss + external: + - host: api.klp.no azure: application: enabled: true @@ -46,6 +48,7 @@ spec: scopes: consumes: - name: "klp:pensjonsimulering" + - name: "klp:oftp/simulering" - name: "nav:pensjon/v1/tpregisteret" liveness: path: actuator/health/liveness diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/config/Tjenestepensjon2025ClientConfig.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/config/Tjenestepensjon2025ClientConfig.kt deleted file mode 100644 index 88f8365b..00000000 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/config/Tjenestepensjon2025ClientConfig.kt +++ /dev/null @@ -1,22 +0,0 @@ -package no.nav.tjenestepensjon.simulering.config - -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025Client -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp.KLPTjenestepensjonClient -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk.SPKTjenestepensjonClient -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -@Configuration -class Tjenestepensjon2025ClientConfig { - - //TODO konfigurer KLP og SPK clients - implementeres i PEK-503 PEK-504 - @Bean("klp") - fun klpTjenestepensjonV2025Client(): TjenestepensjonV2025Client { - return KLPTjenestepensjonClient() - } - - @Bean("spk") - fun spkTjenestepensjonV2025Client(): TjenestepensjonV2025Client { - return SPKTjenestepensjonClient() - } -} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfig.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfig.kt index 472ee5b2..faa87a6c 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfig.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfig.kt @@ -9,11 +9,13 @@ import no.nav.tjenestepensjon.simulering.config.CorrelationIdFilter.Companion.CO import no.nav.tjenestepensjon.simulering.config.CorrelationIdFilter.Companion.CORRELATION_ID_HTTP_HEADER import no.nav.tjenestepensjon.simulering.service.AADClient import no.nav.tjenestepensjon.simulering.v1.consumer.FssGatewayAuthService +import no.nav.tjenestepensjon.simulering.v2.consumer.MaskinportenTokenClient import org.slf4j.MDC import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Profile +import org.springframework.http.HttpHeaders import org.springframework.http.MediaType import org.springframework.http.client.reactive.ReactorClientHttpConnector import org.springframework.web.reactive.function.client.ClientRequest @@ -37,6 +39,8 @@ class WebClientConfig { @Bean fun client(): HttpClient = HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) + .compress(true) + .followRedirect(true) .doOnConnected { it.addHandlerLast(ReadTimeoutHandler(30)) } .wiretap("reactor.netty.http.client.HttpClient", LogLevel.DEBUG, AdvancedByteBufFormat.TEXTUAL) @@ -46,6 +50,27 @@ class WebClientConfig { .filter { request, next -> addCorrelationId(next, request) } .build() + @Bean + fun klpWebClient( + client: HttpClient, + maskinportenTokenClient: MaskinportenTokenClient, + @Value("\${oftp.2025.klp.endpoint.url}") baseUrl: String, + @Value("\${oftp.2025.klp.endpoint.maskinportenscope}") scope: String, + ): WebClient { + return WebClient.builder().clientConnector(ReactorClientHttpConnector(client)) + .baseUrl(baseUrl) + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .filter { request, next -> + next.exchange( + ClientRequest.from(request) + .headers { it.setBearerAuth(maskinportenTokenClient.pensjonsimuleringToken(scope)) } + .build() + ) + } + .filter { request, next -> addCorrelationId(next, request) } + .build() + } + @Bean fun afpBeholdningWebClient( @Value("\${afp.beholdning.url}") baseUrl: String, @@ -99,18 +124,19 @@ class WebClientConfig { .build() } - private fun addCorrelationId( - next: ExchangeFunction, - request: ClientRequest - ): Mono = next.exchange( - ClientRequest.from(request) - .header(CORRELATION_ID_HTTP_HEADER, MDC.get(CORRELATION_ID)) - .header(CONSUMER_ID_HTTP_HEADER, MDC.get(CONSUMER_ID)) - .build() - ) companion object { private const val CONNECT_TIMEOUT_MILLIS = 3000 const val READ_TIMEOUT_MILLIS = 5000 + + fun addCorrelationId( + next: ExchangeFunction, + request: ClientRequest + ): Mono = next.exchange( + ClientRequest.from(request) + .header(CORRELATION_ID_HTTP_HEADER, MDC.get(CORRELATION_ID)) + .header(CONSUMER_ID_HTTP_HEADER, MDC.get(CONSUMER_ID)) + .build() + ) } } diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt index 77eb17de..0f7e6000 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/rest/TjenestepensjonSimuleringV2025Controller.kt @@ -1,12 +1,12 @@ package no.nav.tjenestepensjon.simulering.rest +import io.github.oshai.kotlinlogging.KotlinLogging +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.Tjenestepensjon2025Mapper.mapToVellykketTjenestepensjonSimuleringResponse import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.SimulerTjenestepensjonResponseDto import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.response.SimuleringsResultatTypeDto -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.Tjenestepensjon2025Mapper.mapToVellykketTjenestepensjonSimuleringResponse import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.BrukerErIkkeMedlemException import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TpOrdningStoettesIkkeException -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TjenestepensjonSimuleringException import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025Service import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -16,17 +16,22 @@ import org.springframework.web.bind.annotation.RestController class TjenestepensjonSimuleringV2025Controller( private val tjenestepensjonV2025Service: TjenestepensjonV2025Service ) { + private val log = KotlinLogging.logger {} @PostMapping("/v2025/tjenestepensjon/v1/simulering") fun simuler(@RequestBody request: SimulerTjenestepensjonRequestDto): SimulerTjenestepensjonResponseDto { - try{ - val simulertTjenestepensjon = tjenestepensjonV2025Service.simuler(request) - return mapToVellykketTjenestepensjonSimuleringResponse(simulertTjenestepensjon) - } catch (e: BrukerErIkkeMedlemException){ - return SimulerTjenestepensjonResponseDto(SimuleringsResultatTypeDto.ERROR, e.message) - } catch (e: TpOrdningStoettesIkkeException){ + try { + return tjenestepensjonV2025Service.simuler(request).fold( + onSuccess = { data -> + mapToVellykketTjenestepensjonSimuleringResponse(data) + }, + onFailure = { e -> + log.error(e) { "Simulering feilet: ${e.message}" } + SimulerTjenestepensjonResponseDto(SimuleringsResultatTypeDto.ERROR, "Simulering feilet") + }) + } catch (e: BrukerErIkkeMedlemException) { return SimulerTjenestepensjonResponseDto(SimuleringsResultatTypeDto.ERROR, e.message) - } catch (e: TjenestepensjonSimuleringException){ + } catch (e: TpOrdningStoettesIkkeException) { return SimulerTjenestepensjonResponseDto(SimuleringsResultatTypeDto.ERROR, e.message) } } diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenToken.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenToken.kt index 03990e68..aa529e5f 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenToken.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenToken.kt @@ -7,6 +7,7 @@ import com.nimbusds.jose.crypto.RSASSASigner import com.nimbusds.jose.jwk.RSAKey import com.nimbusds.jwt.JWTClaimsSet import com.nimbusds.jwt.SignedJWT +import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.beans.factory.annotation.Value import org.springframework.http.MediaType import org.springframework.stereotype.Service @@ -19,12 +20,12 @@ class MaskinportenToken( private val webClient: WebClient, @Value("\${maskinporten.client-id}") val clientId: String, @Value("\${maskinporten.client-jwk}") val clientJwk: String, - @Value("\${maskinporten.scope}") val scopes: String, @Value("\${maskinporten.issuer}") val issuer: String, @Value("\${maskinporten.token-endpoint-url}") val endpoint: String, ) { + private val log = KotlinLogging.logger {} - fun getToken(): String { + fun getToken(scope: String): String { val rsaKey = RSAKey.parse(clientJwk) val signedJWT = SignedJWT( JWSHeader.Builder(JWSAlgorithm.RS256) @@ -34,7 +35,7 @@ class MaskinportenToken( JWTClaimsSet.Builder() .audience(issuer) .issuer(clientId) - .claim("scope", scopes) + .claim("scope", scope) .issueTime(Date()) .expirationTime(twoMinutesFromDate(Date())) .build() @@ -50,6 +51,7 @@ class MaskinportenToken( .retrieve() .bodyToMono(MaskinportenTokenResponse::class.java) .block() + log.info { "Hentet token fra maskinporten med scope(s): ${scope}" } return response!!.access_token } diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenTokenClient.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenTokenClient.kt index bf64cda5..7216d9e5 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenTokenClient.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/consumer/MaskinportenTokenClient.kt @@ -2,21 +2,18 @@ package no.nav.tjenestepensjon.simulering.v2.consumer import io.github.oshai.kotlinlogging.KotlinLogging import no.nav.tjenestepensjon.simulering.v2.exceptions.ConnectToMaskinPortenException -import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service @Service -class MaskinportenTokenClient { +class MaskinportenTokenClient(val maskinportenToken: MaskinportenToken) { private val log = KotlinLogging.logger {} - @Autowired - lateinit var maskinportenToken: MaskinportenToken - - fun pensjonsimuleringToken(): String { + fun pensjonsimuleringToken(scope: String): String { + log.info { "Henter maskinporten token for $scope" } return try { - maskinportenToken.getToken() + maskinportenToken.getToken(scope) } catch (exc: Throwable) { - log.warn { "Error while retrieving token from provider: ${exc.message}" } + log.error(exc) { "Error while retrieving token from provider: ${exc.message}" } throw ConnectToMaskinPortenException(exc.message) } } diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/rest/RestClient.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/rest/RestClient.kt index c1549c77..73a5df97 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/rest/RestClient.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2/rest/RestClient.kt @@ -6,23 +6,24 @@ import no.nav.tjenestepensjon.simulering.service.TokenService import no.nav.tjenestepensjon.simulering.v2.consumer.MaskinportenTokenClient import no.nav.tjenestepensjon.simulering.v2.models.request.SimulerPensjonRequestV2 import no.nav.tjenestepensjon.simulering.v2.models.response.SimulerOffentligTjenestepensjonResponse -import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.bodyToMono @Service -class RestClient(private val webClient: WebClient) { - @Autowired - private lateinit var tokenService: TokenService - @Autowired - private lateinit var maskinportenTokenClient: MaskinportenTokenClient +class RestClient( + private val webClient: WebClient, + private val tokenService: TokenService, + private val maskinportenTokenClient: MaskinportenTokenClient, + private @Value("\${maskinporten.scope}") val scopes: String, +) { fun getResponse( request: SimulerPensjonRequestV2, tpOrdning: TPOrdningIdDto, tpLeverandor: TpLeverandor ): SimulerOffentligTjenestepensjonResponse = webClient.post().uri(tpLeverandor.simuleringUrl).headers { it.setBearerAuth( - if (tpLeverandor.name != "SPK") maskinportenTokenClient.pensjonsimuleringToken() + if (tpLeverandor.name != "SPK") maskinportenTokenClient.pensjonsimuleringToken(scopes) else tokenService.oidcAccessToken.accessToken ) }.bodyValue(request).retrieve().bodyToMono().block() diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt index 81ba1682..a67f0e2e 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/domain/SimulertTjenestepensjon.kt @@ -1,7 +1,6 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain data class SimulertTjenestepensjon( - val fnr: String, var ordningsListe: List = emptyList(), var utbetalingsperioder: List = emptyList(), var aarsakIngenUtbetaling: List = emptyList(), diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/exception/TjenestepensjonSimuleringException.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/exception/TjenestepensjonSimuleringException.kt index 2826a54f..7b774e01 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/exception/TjenestepensjonSimuleringException.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/exception/TjenestepensjonSimuleringException.kt @@ -1,6 +1,6 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception -class TjenestepensjonSimuleringException : RuntimeException() { +class TjenestepensjonSimuleringException(val msg: String? = null) : RuntimeException() { override val message: String - get() = "Feil ved simulering av tjenestepensjon" + get() = "Feil ved simulering av tjenestepensjon ${msg ?: ""}" } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Client.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Client.kt index ec468e92..6083d4bf 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Client.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Client.kt @@ -1,10 +1,10 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.TjenestepensjonRequest +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TjenestepensjonSimuleringException interface TjenestepensjonV2025Client { @Throws(TjenestepensjonSimuleringException::class) - fun simuler(request: TjenestepensjonRequest): SimulertTjenestepensjon + fun simuler(request: SimulerTjenestepensjonRequestDto): Result } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt index c50a767e..a222690f 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/TjenestepensjonV2025Service.kt @@ -2,30 +2,29 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service import no.nav.tjenestepensjon.simulering.service.TpClient import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.TjenestepensjonRequest import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.BrukerErIkkeMedlemException import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TpOrdningStoettesIkkeException -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TjenestepensjonSimuleringException -import org.springframework.beans.factory.annotation.Qualifier +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp.KLPTjenestepensjonClient +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk.SPKTjenestepensjonClient import org.springframework.stereotype.Service @Service class TjenestepensjonV2025Service( private val tpClient: TpClient, - @Qualifier("spk") private val spk: TjenestepensjonV2025Client, - @Qualifier("klp") private val klp: TjenestepensjonV2025Client, + private val spk: SPKTjenestepensjonClient, + private val klp: KLPTjenestepensjonClient, ) { - @Throws(BrukerErIkkeMedlemException::class, TpOrdningStoettesIkkeException::class, TjenestepensjonSimuleringException::class) - fun simuler(request: SimulerTjenestepensjonRequestDto): SimulertTjenestepensjon { + @Throws(BrukerErIkkeMedlemException::class, TpOrdningStoettesIkkeException::class) + fun simuler(request: SimulerTjenestepensjonRequestDto): Result { val tpOrdningNavn = tpClient.findTPForhold(request.fnr).flatMap { it.alias }.firstOrNull() - ?: throw BrukerErIkkeMedlemException() + ?: "klp" //TODO throw BrukerErIkkeMedlemException() return when (tpOrdningNavn.lowercase()) { - "spk" -> spk.simuler(TjenestepensjonRequest(request.fnr)) - "klp" -> klp.simuler(TjenestepensjonRequest(request.fnr)) - else -> throw TpOrdningStoettesIkkeException(tpOrdningNavn) + "spk" -> spk.simuler(request) + "klp" -> klp.simuler(request) + else -> klp.simuler(request) //TODO throw TpOrdningStoettesIkkeException(tpOrdningNavn) } } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPMapper.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPMapper.kt new file mode 100644 index 00000000..2eff249c --- /dev/null +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPMapper.kt @@ -0,0 +1,37 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp + +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Ordning +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.Utbetalingsperiode +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto + +object KLPMapper { + + fun mapToRequest(request: SimulerTjenestepensjonRequestDto) = + KLPSimulerTjenestepensjonRequest( + personId = request.fnr, + uttaksListe = request.uttaksListe.map { uttakDto -> + Uttak( + ytelseType = uttakDto.ytelseType, + fraOgMedDato = uttakDto.fraOgMedDato, + uttaksgrad = uttakDto.uttaksgrad + ) + }, + fremtidigInntektsListe = request.fremtidigInntektListe.map { fremtidigInntektDto -> + FremtidigInntekt( + fraOgMedDato = fremtidigInntektDto.fraOgMedDato, + arligInntekt = fremtidigInntektDto.aarligInntekt + ) + }, + arIUtlandetEtter16 = request.aarIUtlandetEtter16, + epsPensjon = request.epsPensjon, + eps2G = request.eps2G, + ) + + fun mapToResponse(response: KLPSimulerTjenestepensjonResponse) = + SimulertTjenestepensjon( + ordningsListe = response.inkludertOrdningListe.map { Ordning(it.tpnr) }, + utbetalingsperioder = response.utbetalingsListe.map { Utbetalingsperiode(it.fraOgMedDato, it.manedligUtbetaling, it.ytelseType) }, + aarsakIngenUtbetaling = response.arsakIngenUtbetaling + ) +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPSimulerTjenestepensjonRequest.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPSimulerTjenestepensjonRequest.kt new file mode 100644 index 00000000..e7da3b5f --- /dev/null +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPSimulerTjenestepensjonRequest.kt @@ -0,0 +1,23 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp + +import java.time.LocalDate + +data class KLPSimulerTjenestepensjonRequest( + val personId: String, + val uttaksListe: List, + val fremtidigInntektsListe: List, + val arIUtlandetEtter16: Int, + val epsPensjon: Boolean, + val eps2G: Boolean +) + +data class Uttak( + val ytelseType: String, + val fraOgMedDato: LocalDate, + val uttaksgrad: Int +) + +data class FremtidigInntekt( + val fraOgMedDato: LocalDate, + val arligInntekt: Int +) diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPSimulerTjenestepensjonResponse.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPSimulerTjenestepensjonResponse.kt new file mode 100644 index 00000000..6b29514b --- /dev/null +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPSimulerTjenestepensjonResponse.kt @@ -0,0 +1,20 @@ +package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp + +import java.time.LocalDate + +data class KLPSimulerTjenestepensjonResponse( + val inkludertOrdningListe: List, + val utbetalingsListe: List, + val arsakIngenUtbetaling: List, +) + +data class InkludertOrdning( + val tpnr: String +) + +data class Utbetaling( + val fraOgMedDato: LocalDate, + val manedligUtbetaling: Int, + val arligUtbetaling: Int, + val ytelseType: String +) diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPTjenestepensjonClient.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPTjenestepensjonClient.kt index 3b53cd57..b063f373 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPTjenestepensjonClient.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/klp/KLPTjenestepensjonClient.kt @@ -1,13 +1,37 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.klp +import io.github.oshai.kotlinlogging.KotlinLogging import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.TjenestepensjonRequest +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.exception.TjenestepensjonSimuleringException import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025Client import org.springframework.stereotype.Service +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.WebClientRequestException +import org.springframework.web.reactive.function.client.WebClientResponseException +import org.springframework.web.reactive.function.client.bodyToMono @Service -class KLPTjenestepensjonClient : TjenestepensjonV2025Client { - override fun simuler(request: TjenestepensjonRequest): SimulertTjenestepensjon { - return SimulertTjenestepensjon(request.fnr) +class KLPTjenestepensjonClient(private val klpWebClient: WebClient) : TjenestepensjonV2025Client { + private val log = KotlinLogging.logger {} + + override fun simuler(request: SimulerTjenestepensjonRequestDto): Result { + try { + val response = klpWebClient + .post() + .uri("/api/oftp/simulering/3200") + .bodyValue(KLPMapper.mapToRequest(request)) + .retrieve() + .bodyToMono() + .block() + return response?.let { Result.success(KLPMapper.mapToResponse(it)) } ?: Result.failure(TjenestepensjonSimuleringException("No response body")) + } catch (e: WebClientResponseException) { + val errorMsg = "Failed to simulate tjenestepensjon 2025 hos KLP ${ e.responseBodyAsString}" + log.error(e) { errorMsg } + return Result.failure(TjenestepensjonSimuleringException(errorMsg)) + } catch (e: WebClientRequestException){ + log.error(e) { "Failed to send request to simulate tjenestepensjon 2025 hos KLP med url ${e.uri}" } + return Result.failure(TjenestepensjonSimuleringException("Failed to send request to simulate tjenestepensjon 2025 hos KLP")) + } } } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt index a4eb1d71..0546868a 100644 --- a/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt +++ b/src/main/kotlin/no/nav/tjenestepensjon/simulering/v2025/tjenestepensjon/v1/service/spk/SPKTjenestepensjonClient.kt @@ -1,13 +1,13 @@ package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.spk import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.SimulertTjenestepensjon -import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain.TjenestepensjonRequest +import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.dto.request.SimulerTjenestepensjonRequestDto import no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.service.TjenestepensjonV2025Client import org.springframework.stereotype.Service @Service class SPKTjenestepensjonClient : TjenestepensjonV2025Client { - override fun simuler(request: TjenestepensjonRequest): SimulertTjenestepensjon { - return SimulertTjenestepensjon(request.fnr) + override fun simuler(request: SimulerTjenestepensjonRequestDto): Result { + return Result.success(SimulertTjenestepensjon()) } } \ No newline at end of file diff --git a/src/main/resources/application-dev-gcp.yml b/src/main/resources/application-dev-gcp.yml index 3c3f1e18..29f3339e 100644 --- a/src/main/resources/application-dev-gcp.yml +++ b/src/main/resources/application-dev-gcp.yml @@ -35,3 +35,9 @@ opf: implementation: SOAP simuleringUrl: https://partner-gw.opf.no/Elsam.svc stillingsprosentUrl: https://partner-gw.opf.no/Elsam.svc +oftp: + 2025: + klp: + endpoint: + url: https://api-test.klp.no + maskinportenscope: klp:oftp/simulering diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bf5b53e2..129ba2f4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -52,4 +52,10 @@ pen: fss: gateway: url: http://localhost:8080 - scope: api://bogus \ No newline at end of file + scope: api://bogus +oftp: + 2025: + klp: + endpoint: + url: https://api.klp.no + maskinportenscope: klp:oftp/simulering \ No newline at end of file diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfigTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfigTest.kt index be36922c..c6b40332 100644 --- a/src/test/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfigTest.kt +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/config/WebClientConfigTest.kt @@ -10,6 +10,7 @@ import no.nav.tjenestepensjon.simulering.model.domain.TPOrdningIdDto import no.nav.tjenestepensjon.simulering.service.AADClient import no.nav.tjenestepensjon.simulering.service.TpClient import no.nav.tjenestepensjon.simulering.v1.consumer.FssGatewayAuthService +import no.nav.tjenestepensjon.simulering.v2.consumer.MaskinportenTokenClient import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance @@ -29,6 +30,9 @@ internal class WebClientConfigTest { @MockBean private lateinit var aadClient: AADClient + @MockBean + private lateinit var maskinportenTokenClient: MaskinportenTokenClient + @Autowired private lateinit var tpClient: TpClient diff --git a/src/test/kotlin/no/nav/tjenestepensjon/simulering/service/TpClientTest.kt b/src/test/kotlin/no/nav/tjenestepensjon/simulering/service/TpClientTest.kt index 5a20919a..11f4da9e 100644 --- a/src/test/kotlin/no/nav/tjenestepensjon/simulering/service/TpClientTest.kt +++ b/src/test/kotlin/no/nav/tjenestepensjon/simulering/service/TpClientTest.kt @@ -12,6 +12,7 @@ import no.nav.tjenestepensjon.simulering.defaultTpid import no.nav.tjenestepensjon.simulering.exceptions.NoTpOrdningerFoundException import no.nav.tjenestepensjon.simulering.testHelper.anyNonNull import no.nav.tjenestepensjon.simulering.v1.consumer.FssGatewayAuthService +import no.nav.tjenestepensjon.simulering.v2.consumer.MaskinportenTokenClient import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertEquals import org.mockito.Mockito @@ -33,6 +34,9 @@ class TpClientTest { @Autowired private lateinit var tpClient: TpClient + @MockBean + private lateinit var maskinportenTokenClient: MaskinportenTokenClient + private var wireMockServer = WireMockServer().apply { start() //setGlobalFixedDelay(WebClientConfig.READ_TIMEOUT_MILLIS * 2) // To force timeout diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 80bfc707..b3abcd89 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -62,4 +62,10 @@ pen: scope: api://bogus spring: profiles: - active: dev-gcp,test \ No newline at end of file + active: dev-gcp,test +oftp: + 2025: + klp: + endpoint: + url: https://api-test.klp.no + maskinportenscope: klp:oftp/simulering