Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/pek 504 klp tjenestepensjon simulering #146

Merged
merged 13 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .nais/nais-dev-gcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ 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
maskinporten:
enabled: true
scopes:
consumes:
- name: "klp:oftp/simulering"
- name: "klp:pensjonsimulering"
- name: "nav:pensjon/v1/tpregisteret"
liveness:
Expand Down
3 changes: 3 additions & 0 deletions .nais/nais-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -60,6 +62,7 @@ spec:
scopes:
consumes:
- name: "klp:pensjonsimulering"
- name: "klp:oftp/simulering"
- name: "nav:pensjon/v1/tpregisteret"
liveness:
path: actuator/health/liveness
Expand Down
2 changes: 2 additions & 0 deletions .nais/nais-prod-gcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@ 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
maskinporten:
enabled: true
scopes:
consumes:
- name: "klp:oftp/simulering"
- name: "klp:pensjonsimulering"
- name: "nav:pensjon/v1/tpregisteret"
liveness:
Expand Down
3 changes: 3 additions & 0 deletions .nais/nais-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ spec:
- application: pensjon-pen
namespace: pensjondeployer
cluster: prod-fss
external:
- host: api.klp.no
azure:
application:
enabled: true
Expand All @@ -46,6 +48,7 @@ spec:
scopes:
consumes:
- name: "klp:pensjonsimulering"
- name: "klp:oftp/simulering"
- name: "nav:pensjon/v1/tpregisteret"
liveness:
path: actuator/health/liveness
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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,
Expand Down Expand Up @@ -99,18 +124,19 @@ class WebClientConfig {
.build()
}

private fun addCorrelationId(
next: ExchangeFunction,
request: ClientRequest
): Mono<ClientResponse> = 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<ClientResponse> = next.exchange(
ClientRequest.from(request)
.header(CORRELATION_ID_HTTP_HEADER, MDC.get(CORRELATION_ID))
.header(CONSUMER_ID_HTTP_HEADER, MDC.get(CONSUMER_ID))
.build()
)
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -34,7 +35,7 @@ class MaskinportenToken(
JWTClaimsSet.Builder()
.audience(issuer)
.issuer(clientId)
.claim("scope", scopes)
.claim("scope", scope)
.issueTime(Date())
.expirationTime(twoMinutesFromDate(Date()))
.build()
Expand All @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<SimulerOffentligTjenestepensjonResponse>().block()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package no.nav.tjenestepensjon.simulering.v2025.tjenestepensjon.v1.domain

data class SimulertTjenestepensjon(
val fnr: String,
var ordningsListe: List<Ordning> = emptyList(),
var utbetalingsperioder: List<Utbetalingsperiode> = emptyList(),
var aarsakIngenUtbetaling: List<String> = emptyList(),
Expand Down
Original file line number Diff line number Diff line change
@@ -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 ?: ""}"
}
Original file line number Diff line number Diff line change
@@ -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<SimulertTjenestepensjon>
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<SimulertTjenestepensjon> {
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)
}
}
}
Loading
Loading