diff --git a/build.gradle.kts b/build.gradle.kts index c29c08a18..c3e54d932 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,6 +37,7 @@ object Versions { const val junitJupiter = "5.7.0" const val mockk = "1.10.3" const val springmockk = "2.0.0" + const val mockwebserver = "5.0.0-alpha.2" } plugins { @@ -147,6 +148,7 @@ dependencies { testImplementation("no.nav.security:token-validation-test-support:${Versions.tokenValidation}") testImplementation("org.jetbrains.kotlin:kotlin-test:${Versions.kotlin}") testImplementation("com.ninja-squad:springmockk:${Versions.springmockk}") + testImplementation("com.squareup.okhttp3:mockwebserver3-junit5:${Versions.mockwebserver}") // spesifikke versjoner oppgradert etter ønske fra snyk constraints { diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientImpl.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientImpl.kt index 2e22c4eb9..ad4f316db 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientImpl.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientImpl.kt @@ -1,8 +1,6 @@ package no.nav.sosialhjelp.innsyn.client.digisosapi -import com.fasterxml.jackson.module.kotlin.readValue import no.nav.sosialhjelp.api.fiks.exceptions.FiksClientException -import no.nav.sosialhjelp.api.fiks.exceptions.FiksException import no.nav.sosialhjelp.api.fiks.exceptions.FiksServerException import no.nav.sosialhjelp.innsyn.client.fiks.FiksClientImpl import no.nav.sosialhjelp.innsyn.client.fiks.VedleggMetadata @@ -16,17 +14,17 @@ import no.nav.sosialhjelp.innsyn.utils.IntegrationUtils.HEADER_INTEGRASJON_PASSO import no.nav.sosialhjelp.innsyn.utils.IntegrationUtils.forwardHeaders import no.nav.sosialhjelp.innsyn.utils.logger import no.nav.sosialhjelp.innsyn.utils.objectMapper +import no.nav.sosialhjelp.innsyn.utils.typeRef import org.springframework.context.annotation.Profile -import org.springframework.http.HttpEntity import org.springframework.http.HttpHeaders import org.springframework.http.HttpHeaders.AUTHORIZATION -import org.springframework.http.HttpMethod +import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.stereotype.Component import org.springframework.util.LinkedMultiValueMap -import org.springframework.web.client.HttpClientErrorException -import org.springframework.web.client.HttpServerErrorException -import org.springframework.web.client.RestTemplate +import org.springframework.web.reactive.function.BodyInserters +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.bodyToMono import java.util.Collections /** @@ -35,109 +33,106 @@ import java.util.Collections @Profile("!(prod-sbs|mock)") @Component class DigisosApiClientImpl( - clientProperties: ClientProperties, - private val restTemplate: RestTemplate, - private val idPortenService: IdPortenService, - private val fiksClientImpl: FiksClientImpl + private val clientProperties: ClientProperties, + private val fiksWebClient: WebClient, + private val idPortenService: IdPortenService, + private val fiksClientImpl: FiksClientImpl, ) : DigisosApiClient { private val testbrukerNatalie = System.getenv("TESTBRUKER_NATALIE") ?: "11111111111" - private val baseUrl = clientProperties.fiksDigisosEndpointUrl - private val fiksIntegrasjonIdKommune = clientProperties.fiksIntegrasjonIdKommune - private val fiksIntegrasjonPassordKommune = clientProperties.fiksIntegrasjonPassordKommune - override fun oppdaterDigisosSak(fiksDigisosId: String?, digisosApiWrapper: DigisosApiWrapper): String? { var id = fiksDigisosId if (fiksDigisosId == null || fiksDigisosId == "001" || fiksDigisosId == "002" || fiksDigisosId == "003") { id = opprettDigisosSak() log.info("Laget ny digisossak: $id") } - val httpEntity = HttpEntity(objectMapper.writeValueAsString(digisosApiWrapper), headers()) - try { - restTemplate.exchange("$baseUrl/digisos/api/v1/11415cd1-e26d-499a-8421-751457dfcbd5/$id", HttpMethod.POST, httpEntity, String::class.java) - log.info("Postet DigisosSak til Fiks") - return id - } catch (e: HttpClientErrorException) { - log.warn(e.responseBodyAsString) - log.warn("Fiks - oppdaterDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksClientException(e.rawStatusCode, e.message, e) - } catch (e: HttpServerErrorException) { - log.warn(e.responseBodyAsString) - log.warn("Fiks - oppdaterDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksServerException(e.rawStatusCode, e.message, e) - } catch (e: Exception) { - log.error(e.message, e) - throw FiksException(e.message, e) - } + + return fiksWebClient.post() + .uri("/digisos/api/v1/11415cd1-e26d-499a-8421-751457dfcbd5/$id") + .headers { it.addAll(headers()) } + .body(BodyInserters.fromValue(objectMapper.writeValueAsString(digisosApiWrapper))) + .retrieve() + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - oppdaterDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) + FiksClientException(e.rawStatusCode, e.message, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - oppdaterDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) + FiksServerException(e.rawStatusCode, e.message, e) + } + } + .bodyToMono() + .block() + .also { log.info("Postet DigisosSak til Fiks") } } // Brukes for å laste opp Pdf-er fra test-fagsystem i q-miljø override fun lastOppNyeFilerTilFiks(files: List, soknadId: String): List { - val headers = forwardHeaders() - headers.accept = Collections.singletonList(MediaType.APPLICATION_JSON) - headers.set(AUTHORIZATION, BEARER + idPortenService.getToken().token) - headers.set(HEADER_INTEGRASJON_ID, fiksIntegrasjonIdKommune) - headers.set(HEADER_INTEGRASJON_PASSORD, fiksIntegrasjonPassordKommune) - headers.contentType = MediaType.MULTIPART_FORM_DATA - val body = LinkedMultiValueMap() - files.forEachIndexed { fileId, file -> val vedleggMetadata = VedleggMetadata(file.filnavn, file.mimetype, file.storrelse) body.add("vedleggSpesifikasjon:$fileId", fiksClientImpl.createHttpEntityOfString(fiksClientImpl.serialiser(vedleggMetadata), "vedleggSpesifikasjon:$fileId")) body.add("dokument:$fileId", fiksClientImpl.createHttpEntityOfFile(file, "dokument:$fileId")) } - val requestEntity = HttpEntity(body, headers) - try { - val path = "$baseUrl/digisos/api/v1/11415cd1-e26d-499a-8421-751457dfcbd5/$soknadId/filer" - val response = restTemplate.exchange(path, HttpMethod.POST, requestEntity, String::class.java) - - val opplastingResponse: List = objectMapper.readValue(response.body!!) - log.info("Filer sendt til Fiks") - return opplastingResponse.map { filOpplastingResponse -> filOpplastingResponse.dokumentlagerDokumentId } - - } catch (e: HttpClientErrorException) { - log.warn(e.responseBodyAsString) - log.warn("Opplasting av filer feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksClientException(e.rawStatusCode, e.message, e) - } catch (e: HttpServerErrorException) { - log.warn(e.responseBodyAsString) - log.warn("Opplasting av filer feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksServerException(e.rawStatusCode, e.message, e) - } catch (e: Exception) { - log.warn("Opplasting av filer feilet", e) - throw FiksException(e.message, e) - } - + val opplastingResponseList = fiksWebClient.post() + .uri("/digisos/api/v1/11415cd1-e26d-499a-8421-751457dfcbd5/$soknadId/filer") + .headers { it.addAll(headers()) } + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(BodyInserters.fromMultipartData(body)) + .retrieve() + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - Opplasting av filer feilet - ${e.statusCode} ${e.statusText}", e) + FiksClientException(e.rawStatusCode, e.message, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - Opplasting av filer feilet - ${e.statusCode} ${e.statusText}", e) + FiksServerException(e.rawStatusCode, e.message, e) + } + } + .bodyToMono(typeRef>()) + .block() + log.info("Filer sendt til Fiks") + return opplastingResponseList!!.map { it.dokumentlagerDokumentId } } fun opprettDigisosSak(): String? { - val httpEntity = HttpEntity("", headers()) - try { - val response = restTemplate.exchange("$baseUrl/digisos/api/v1/11415cd1-e26d-499a-8421-751457dfcbd5/ny?sokerFnr=$testbrukerNatalie", HttpMethod.POST, httpEntity, String::class.java) - log.info("Opprettet sak hos Fiks. Digisosid: ${response.body}") - return response.body?.replace("\"", "") - } catch (e: HttpClientErrorException) { - log.warn("Fiks - opprettDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksClientException(e.rawStatusCode, e.message, e) - } catch (e: HttpServerErrorException) { - log.warn("Fiks - opprettDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksServerException(e.rawStatusCode, e.message, e) - } catch (e: Exception) { - log.error(e.message, e) - throw FiksException(e.message, e) - } + val response = fiksWebClient.post() + .uri("/digisos/api/v1/11415cd1-e26d-499a-8421-751457dfcbd5/ny?sokerFnr=$testbrukerNatalie") + .headers { it.addAll(headers()) } + .body(BodyInserters.fromValue("")) + .retrieve() + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - opprettDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) + FiksClientException(e.rawStatusCode, e.message, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - opprettDigisosSak feilet - ${e.statusCode} ${e.statusText}", e) + FiksServerException(e.rawStatusCode, e.message, e) + } + } + .bodyToMono() + .block() + log.info("Opprettet sak hos Fiks. Digisosid: $response") + return response?.replace("\"", "") } private fun headers(): HttpHeaders { val headers = forwardHeaders() - val accessToken = idPortenService.getToken() - headers.accept = Collections.singletonList(MediaType.ALL) - headers.set(HEADER_INTEGRASJON_ID, fiksIntegrasjonIdKommune) - headers.set(HEADER_INTEGRASJON_PASSORD, fiksIntegrasjonPassordKommune) - headers.set(AUTHORIZATION, BEARER + accessToken.token) + headers.accept = Collections.singletonList(MediaType.APPLICATION_JSON) + headers.set(HEADER_INTEGRASJON_ID, clientProperties.fiksIntegrasjonIdKommune) + headers.set(HEADER_INTEGRASJON_PASSORD, clientProperties.fiksIntegrasjonPassordKommune) + headers.set(AUTHORIZATION, BEARER + idPortenService.getToken().token) headers.contentType = MediaType.APPLICATION_JSON return headers } @@ -148,7 +143,7 @@ class DigisosApiClientImpl( } data class FilOpplastingResponse( - val filnavn: String, - val dokumentlagerDokumentId: String, - val storrelse: Long + val filnavn: String, + val dokumentlagerDokumentId: String, + val storrelse: Long, ) diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/DokumentlagerClient.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/DokumentlagerClient.kt index 6ca5de425..72d231b6d 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/DokumentlagerClient.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/DokumentlagerClient.kt @@ -1,24 +1,19 @@ package no.nav.sosialhjelp.innsyn.client.fiks import no.nav.sosialhjelp.api.fiks.exceptions.FiksClientException -import no.nav.sosialhjelp.api.fiks.exceptions.FiksException import no.nav.sosialhjelp.api.fiks.exceptions.FiksServerException import no.nav.sosialhjelp.innsyn.config.ClientProperties import no.nav.sosialhjelp.innsyn.utils.IntegrationUtils import no.nav.sosialhjelp.innsyn.utils.logger import org.springframework.context.annotation.Profile -import org.springframework.http.HttpEntity -import org.springframework.http.HttpHeaders -import org.springframework.http.MediaType +import org.springframework.http.HttpStatus import org.springframework.stereotype.Component -import org.springframework.web.client.HttpClientErrorException -import org.springframework.web.client.HttpServerErrorException -import org.springframework.web.client.RestTemplate +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.bodyToMono import java.io.ByteArrayInputStream import java.security.cert.CertificateException import java.security.cert.CertificateFactory import java.security.cert.X509Certificate -import java.util.Collections interface DokumentlagerClient { @@ -28,41 +23,37 @@ interface DokumentlagerClient { @Profile("!mock") @Component class DokumentlagerClientImpl( - clientProperties: ClientProperties, - private val restTemplate: RestTemplate + private val clientProperties: ClientProperties, + private val fiksWebClient: WebClient, ) : DokumentlagerClient { - private val baseUrl = clientProperties.fiksDigisosEndpointUrl - private val fiksIntegrasjonid = clientProperties.fiksIntegrasjonId - private val fiksIntegrasjonpassord = clientProperties.fiksIntegrasjonpassord - override fun getDokumentlagerPublicKeyX509Certificate(token: String): X509Certificate { - val headers = IntegrationUtils.forwardHeaders() - headers.accept = Collections.singletonList(MediaType.APPLICATION_JSON) - headers.set(HttpHeaders.AUTHORIZATION, token) - headers.set(IntegrationUtils.HEADER_INTEGRASJON_ID, fiksIntegrasjonid) - headers.set(IntegrationUtils.HEADER_INTEGRASJON_PASSORD, fiksIntegrasjonpassord) - - try { - val response = restTemplate.exchange("$baseUrl/digisos/api/v1/dokumentlager-public-key", org.springframework.http.HttpMethod.GET, HttpEntity(headers), ByteArray::class.java) - log.info("Hentet public key for dokumentlager") - val publicKey = response.body - try { - val certificateFactory = CertificateFactory.getInstance("X.509") + val publicKey = fiksWebClient.get() + .uri(FiksPaths.PATH_DOKUMENTLAGER_PUBLICKEY) + .headers { it.addAll(IntegrationUtils.fiksHeaders(clientProperties, token)) } + .retrieve() + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - getDokumentlagerPublicKey feilet - ${e.statusCode} ${e.statusText}", e) + FiksClientException(e.rawStatusCode, e.message, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - getDokumentlagerPublicKey feilet - ${e.statusCode} ${e.statusText}", e) + FiksServerException(e.rawStatusCode, e.message, e) + } + } + .bodyToMono() + .block() - return certificateFactory.generateCertificate(ByteArrayInputStream(publicKey)) as X509Certificate + log.info("Hentet public key for dokumentlager") - } catch (e: CertificateException) { - throw RuntimeException(e) - } - } catch (e: HttpClientErrorException) { - log.warn("Fiks - getDokumentlagerPublicKey feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksClientException(e.rawStatusCode, e.message, e) - } catch (e: HttpServerErrorException) { - log.warn("Fiks - getDokumentlagerPublicKey feilet - ${e.statusCode} ${e.statusText}", e) - throw FiksServerException(e.rawStatusCode, e.message, e) - } catch (e: Exception) { - throw FiksException(e.message, e) + try { + val certificateFactory = CertificateFactory.getInstance("X.509") + return certificateFactory.generateCertificate(ByteArrayInputStream(publicKey!!)) as X509Certificate + } catch (e: CertificateException) { + throw RuntimeException(e) } } diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientImpl.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientImpl.kt index 19ca6b88d..c210af46f 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientImpl.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientImpl.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.runBlocking import no.nav.sbl.soknadsosialhjelp.vedlegg.JsonVedleggSpesifikasjon import no.nav.sosialhjelp.api.fiks.DigisosSak import no.nav.sosialhjelp.api.fiks.exceptions.FiksClientException -import no.nav.sosialhjelp.api.fiks.exceptions.FiksException import no.nav.sosialhjelp.api.fiks.exceptions.FiksNotFoundException import no.nav.sosialhjelp.api.fiks.exceptions.FiksServerException import no.nav.sosialhjelp.innsyn.config.ClientProperties @@ -15,8 +14,8 @@ import no.nav.sosialhjelp.innsyn.utils.IntegrationUtils.fiksHeaders import no.nav.sosialhjelp.innsyn.utils.feilmeldingUtenFnr import no.nav.sosialhjelp.innsyn.utils.lagNavEksternRefId import no.nav.sosialhjelp.innsyn.utils.logger +import no.nav.sosialhjelp.innsyn.utils.messageUtenFnr import no.nav.sosialhjelp.innsyn.utils.objectMapper -import no.nav.sosialhjelp.innsyn.utils.toFiksErrorMessage import no.nav.sosialhjelp.innsyn.utils.typeRef import no.nav.sosialhjelp.kotlin.utils.retry import org.springframework.context.annotation.Profile @@ -24,29 +23,27 @@ import org.springframework.core.io.InputStreamResource import org.springframework.http.ContentDisposition import org.springframework.http.HttpEntity import org.springframework.http.HttpHeaders -import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus import org.springframework.http.MediaType -import org.springframework.http.ResponseEntity import org.springframework.lang.NonNull import org.springframework.stereotype.Component import org.springframework.util.LinkedMultiValueMap -import org.springframework.web.client.HttpClientErrorException -import org.springframework.web.client.HttpServerErrorException -import org.springframework.web.client.RestTemplate +import org.springframework.web.reactive.function.BodyInserters +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.bodyToMono +import org.springframework.web.reactive.function.client.toEntity +import java.util.function.Predicate @Profile("!mock") @Component class FiksClientImpl( - private val clientProperties: ClientProperties, - private val restTemplate: RestTemplate, - private val retryProperties: FiksRetryProperties, - private val redisService: RedisService + private val clientProperties: ClientProperties, + private val fiksWebClient: WebClient, + private val retryProperties: FiksRetryProperties, + private val redisService: RedisService, ) : FiksClient { - private val baseUrl = clientProperties.fiksDigisosEndpointUrl - override fun hentDigisosSak(digisosId: String, token: String, useCache: Boolean): DigisosSak { return when { useCache -> hentDigisosSakFraCache(digisosId) ?: hentDigisosSakFraFiks(digisosId, token) @@ -55,114 +52,157 @@ class FiksClientImpl( } private fun hentDigisosSakFraCache(digisosId: String): DigisosSak? = - redisService.get(digisosId, DigisosSak::class.java) as DigisosSak? + redisService.get(digisosId, DigisosSak::class.java) as DigisosSak? private fun hentDigisosSakFraFiks(digisosId: String, token: String): DigisosSak { - log.debug("Forsøker å hente digisosSak fra $baseUrl/digisos/api/v1/soknader/$digisosId") - - try { - val headers = fiksHeaders(clientProperties, token) - val urlTemplate = baseUrl + FiksPaths.PATH_DIGISOSSAK - - val response: ResponseEntity = withRetry { - restTemplate.exchange(urlTemplate, HttpMethod.GET, HttpEntity(headers), String::class.java, digisosId) - } - - log.debug("Hentet DigisosSak fra Fiks") - val body = response.body!! - return objectMapper.readValue(body, DigisosSak::class.java) - .also { lagreTilCache(digisosId, it) } - } catch (e: HttpClientErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Fiks - hentDigisosSak feilet - $message - $fiksErrorMessage", e) - if (e.statusCode == HttpStatus.NOT_FOUND) { - throw FiksNotFoundException(message, e) - } - throw FiksClientException(e.rawStatusCode, e.message, e) - } catch (e: HttpServerErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Fiks - hentDigisosSak feilet - $message - $fiksErrorMessage", e) - throw FiksServerException(e.rawStatusCode, message, e) - } catch (e: Exception) { - log.warn("Fiks - hentDigisosSak feilet", e) - throw FiksException(e.message?.feilmeldingUtenFnr, e) + log.debug("Forsøker å hente digisosSak fra /digisos/api/v1/soknader/$digisosId") + + val digisosSak: DigisosSak? = withRetry { + fiksWebClient.get() + .uri(FiksPaths.PATH_DIGISOSSAK, digisosId) + .headers { it.addAll(fiksHeaders(clientProperties, token)) } + .retrieve() + .onStatus(Predicate.isEqual(HttpStatus.NOT_FOUND)) { + it.createException().map { e -> + log.warn("Fiks - hentDigisosSak feilet - ${messageUtenFnr(e)}", e) + FiksNotFoundException(e.message?.feilmeldingUtenFnr, e) + } + } + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - hentDigisosSak feilet - ${messageUtenFnr(e)}", e) + FiksClientException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - hentDigisosSak feilet - ${messageUtenFnr(e)}", e) + FiksServerException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .bodyToMono() + .block() } + log.debug("Hentet DigisosSak fra Fiks") + return digisosSak!! + .also { lagreTilCache(digisosId, it) } } private fun lagreTilCache(id: String, digisosSakEllerDokument: Any) = - redisService.put(id, objectMapper.writeValueAsBytes(digisosSakEllerDokument)) - - override fun hentDokument(digisosId: String, dokumentlagerId: String, requestedClass: Class, token: String): Any { + redisService.put(id, objectMapper.writeValueAsBytes(digisosSakEllerDokument)) + + override fun hentDokument( + digisosId: String, + dokumentlagerId: String, + requestedClass: Class, + token: String, + ): Any { return hentDokumentFraCache(dokumentlagerId, requestedClass) - ?: hentDokumentFraFiks(digisosId, dokumentlagerId, requestedClass, token) + ?: hentDokumentFraFiks(digisosId, dokumentlagerId, requestedClass, token) } private fun hentDokumentFraCache(dokumentlagerId: String, requestedClass: Class): Any? = - redisService.get(dokumentlagerId, requestedClass) - - private fun hentDokumentFraFiks(digisosId: String, dokumentlagerId: String, requestedClass: Class, token: String): Any { - log.debug("Forsøker å hente dokument fra $baseUrl/digisos/api/v1/soknader/nav/$digisosId/dokumenter/$dokumentlagerId") - - try { - val headers = fiksHeaders(clientProperties, token) - val urlTemplate = baseUrl + FiksPaths.PATH_DOKUMENT - val vars = mapOf("digisosId" to digisosId, "dokumentlagerId" to dokumentlagerId) - - val response: ResponseEntity = withRetry { - restTemplate.exchange(urlTemplate, HttpMethod.GET, HttpEntity(headers), String::class.java, vars) - } - - log.debug("Hentet dokument (${requestedClass.simpleName}) fra Fiks, dokumentlagerId=$dokumentlagerId") - return objectMapper.readValue(response.body!!, requestedClass) - .also { lagreTilCache(dokumentlagerId, it) } - } catch (e: HttpClientErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Fiks - hentDokument feilet - $message - $fiksErrorMessage", e) - throw FiksClientException(e.rawStatusCode, message, e) - } catch (e: HttpServerErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Fiks - hentDokument feilet - $message - $fiksErrorMessage", e) - throw FiksServerException(e.rawStatusCode, message, e) - } catch (e: Exception) { - log.warn("Fiks - hentDokument feilet", e) - throw FiksException(e.message?.feilmeldingUtenFnr, e) + redisService.get(dokumentlagerId, requestedClass) + + private fun hentDokumentFraFiks( + digisosId: String, + dokumentlagerId: String, + requestedClass: Class, + token: String, + ): Any { + log.debug("Forsøker å hente dokument fra /digisos/api/v1/soknader/nav/$digisosId/dokumenter/$dokumentlagerId") + + val dokument: Any? = withRetry { + fiksWebClient.get() + .uri(FiksPaths.PATH_DOKUMENT, digisosId, dokumentlagerId) + .headers { it.addAll(fiksHeaders(clientProperties, token)) } + .retrieve() + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - hentDokument feilet - ${messageUtenFnr(e)}", e) + FiksClientException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - hentDokument feilet - ${messageUtenFnr(e)}", e) + FiksServerException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .bodyToMono(requestedClass) + .block() } + log.debug("Hentet dokument (${requestedClass.simpleName}) fra Fiks, dokumentlagerId=$dokumentlagerId") + return dokument!! + .also { lagreTilCache(dokumentlagerId, it) } } override fun hentAlleDigisosSaker(token: String): List { - try { - val headers = fiksHeaders(clientProperties, token) - val url = baseUrl + FiksPaths.PATH_ALLE_DIGISOSSAKER - - val response: ResponseEntity> = withRetry { - restTemplate.exchange(url, HttpMethod.GET, HttpEntity(headers), typeRef>()) - } - return response.body.orEmpty() - } catch (e: HttpClientErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Fiks - hentAlleDigisosSaker feilet - $message - $fiksErrorMessage", e) - throw FiksClientException(e.rawStatusCode, message, e) - } catch (e: HttpServerErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Fiks - hentAlleDigisosSaker feilet - $message - $fiksErrorMessage", e) - throw FiksServerException(e.rawStatusCode, message, e) - } catch (e: Exception) { - log.warn("Fiks - hentAlleDigisosSaker feilet", e) - throw FiksException(e.message?.feilmeldingUtenFnr, e) + val digisosSaker: List? = withRetry { + fiksWebClient.get() + .uri(FiksPaths.PATH_ALLE_DIGISOSSAKER) + .headers { it.addAll(fiksHeaders(clientProperties, token)) } + .retrieve() + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - hentAlleDigisosSaker feilet - ${messageUtenFnr(e)}", e) + FiksClientException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - hentAlleDigisosSaker feilet - ${messageUtenFnr(e)}", e) + FiksServerException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .bodyToMono(typeRef>()) + .block() } + return digisosSaker!! } - override fun lastOppNyEttersendelse(files: List, vedleggJson: JsonVedleggSpesifikasjon, digisosId: String, token: String) { + override fun lastOppNyEttersendelse( + files: List, + vedleggJson: JsonVedleggSpesifikasjon, + digisosId: String, + token: String, + ) { log.info("Starter sending til FIKS for ettersendelse med ${files.size} filer (inkludert ettersendelse.pdf). Validering, filnavn-endring, generering av ettersendelse.pdf og kryptering er OK.") - val headers = fiksHeaders(clientProperties, token) - headers.contentType = MediaType.MULTIPART_FORM_DATA + val body = createBodyForUpload(vedleggJson, files) + + val digisosSak = hentDigisosSakFraFiks(digisosId, token) + val kommunenummer = digisosSak.kommunenummer + val navEksternRefId = lagNavEksternRefId(digisosSak) + + val responseEntity = fiksWebClient.post() + .uri(FiksPaths.PATH_LAST_OPP_ETTERSENDELSE, kommunenummer, digisosId, navEksternRefId) + .headers { it.addAll(fiksHeaders(clientProperties, token)) } + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(BodyInserters.fromMultipartData(body)) + .retrieve() + .onStatus(HttpStatus::is4xxClientError) { + it.createException().map { e -> + log.warn("Fiks - Opplasting av ettersendelse på $digisosId feilet - ${messageUtenFnr(e)}", e) + FiksClientException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .onStatus(HttpStatus::is5xxServerError) { + it.createException().map { e -> + log.warn("Fiks - Opplasting av ettersendelse på $digisosId feilet - ${messageUtenFnr(e)}", e) + FiksServerException(e.rawStatusCode, e.message?.feilmeldingUtenFnr, e) + } + } + .toEntity() + .block() + + log.info("Sendte ettersendelse til kommune $kommunenummer i Fiks, fikk navEksternRefId $navEksternRefId (statusCode: ${responseEntity!!.statusCodeValue})") + } + + fun createBodyForUpload( + vedleggJson: JsonVedleggSpesifikasjon, + files: List, + ): LinkedMultiValueMap { val body = LinkedMultiValueMap() body.add("vedlegg.json", createHttpEntityOfString(serialiser(vedleggJson), "vedlegg.json")) @@ -171,33 +211,7 @@ class FiksClientImpl( body.add("vedleggSpesifikasjon:$fileId", createHttpEntityOfString(serialiser(vedleggMetadata), "vedleggSpesifikasjon:$fileId")) body.add("dokument:$fileId", createHttpEntityOfFile(file, "dokument:$fileId")) } - - val digisosSak = hentDigisosSakFraFiks(digisosId, token) - val kommunenummer = digisosSak.kommunenummer - val navEksternRefId = lagNavEksternRefId(digisosSak) - - val requestEntity = HttpEntity(body, headers) - try { - val urlTemplate = "$baseUrl/digisos/api/v1/soknader/{kommunenummer}/{digisosId}/{navEksternRefId}" - val vars = mapOf("kommunenummer" to kommunenummer, "digisosId" to digisosId, "navEksternRefId" to navEksternRefId) - val response: ResponseEntity = restTemplate.exchange(urlTemplate, HttpMethod.POST, requestEntity, String::class.java, vars) - - log.info("Sendte ettersendelse til kommune $kommunenummer i Fiks, fikk navEksternRefId $navEksternRefId (statusCode: ${response.statusCodeValue})") - - } catch (e: HttpClientErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Opplasting av ettersendelse på $digisosId feilet - $message - $fiksErrorMessage", e) - throw FiksClientException(e.rawStatusCode, message, e) - } catch (e: HttpServerErrorException) { - val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr - val message = e.message?.feilmeldingUtenFnr - log.warn("Opplasting av ettersendelse på $digisosId feilet - $message - $fiksErrorMessage", e) - throw FiksServerException(e.rawStatusCode, message, e) - } catch (e: Exception) { - log.warn("Opplasting av ettersendelse på $digisosId feilet", e) - throw FiksException(e.message?.feilmeldingUtenFnr, e) - } + return body } fun createHttpEntityOfString(body: String, name: String): HttpEntity { @@ -211,8 +225,8 @@ class FiksClientImpl( private fun createHttpEntity(body: Any, name: String, filename: String?, contentType: String): HttpEntity { val headerMap = LinkedMultiValueMap() val builder: ContentDisposition.Builder = ContentDisposition - .builder("form-data") - .name(name) + .builder("form-data") + .name(name) val contentDisposition: ContentDisposition = if (filename == null) builder.build() else builder.filename(filename).build() headerMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString()) @@ -228,13 +242,13 @@ class FiksClientImpl( } } - private fun withRetry(block: () -> ResponseEntity): ResponseEntity { + private fun withRetry(block: () -> T): T { return runBlocking { retry( - attempts = retryProperties.attempts, - initialDelay = retryProperties.initialDelay, - maxDelay = retryProperties.maxDelay, - retryableExceptions = arrayOf(HttpServerErrorException::class) + attempts = retryProperties.attempts, + initialDelay = retryProperties.initialDelay, + maxDelay = retryProperties.maxDelay, + retryableExceptions = arrayOf(FiksServerException::class) ) { block() } @@ -247,7 +261,7 @@ class FiksClientImpl( } data class VedleggMetadata( - val filnavn: String?, - val mimetype: String?, - val storrelse: Long + val filnavn: String?, + val mimetype: String?, + val storrelse: Long, ) diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksConfig.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksConfig.kt new file mode 100644 index 000000000..023ac1235 --- /dev/null +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksConfig.kt @@ -0,0 +1,28 @@ +package no.nav.sosialhjelp.innsyn.client.fiks + +import no.nav.sosialhjelp.innsyn.config.ClientProperties +import no.nav.sosialhjelp.innsyn.utils.objectMapper +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.http.codec.json.Jackson2JsonDecoder +import org.springframework.http.codec.json.Jackson2JsonEncoder +import org.springframework.web.reactive.function.client.WebClient + +@Configuration +class FiksConfig( + private val proxiedWebClient: WebClient, + private val clientProperties: ClientProperties, +) { + + @Bean + fun fiksWebClient(): WebClient = + proxiedWebClient + .mutate() + .baseUrl(clientProperties.fiksDigisosEndpointUrl) + .codecs { + it.defaultCodecs().maxInMemorySize(16 * 1024 * 1024) + it.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(objectMapper)) + it.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(objectMapper)) + } + .build() +} \ No newline at end of file diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksPaths.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksPaths.kt index 57ce54181..592947833 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksPaths.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksPaths.kt @@ -7,5 +7,7 @@ object FiksPaths { const val PATH_DOKUMENT = "/digisos/api/v1/soknader/{digisosId}/dokumenter/{dokumentlagerId}" const val PATH_KOMMUNEINFO = "/digisos/api/v1/nav/kommuner/{kommunenummer}" const val PATH_ALLE_KOMMUNEINFO = "/digisos/api/v1/nav/kommuner" + const val PATH_LAST_OPP_ETTERSENDELSE = "/digisos/api/v1/soknader/{kommunenummer}/{digisosId}/{navEksternRefId}" + const val PATH_DOKUMENTLAGER_PUBLICKEY = "/digisos/api/v1/dokumentlager-public-key" } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/KommuneInfoClientConfig.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/KommuneInfoClientConfig.kt index 2e09b5d78..780a96153 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/KommuneInfoClientConfig.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/KommuneInfoClientConfig.kt @@ -12,14 +12,14 @@ import org.springframework.web.reactive.function.client.WebClient @Profile("!mock") @Configuration class KommuneInfoClientConfig( - private val proxiedWebClientBuilder: WebClient.Builder, + private val proxiedWebClient: WebClient, private val clientProperties: ClientProperties ) { @Bean fun kommuneInfoClient(): KommuneInfoClient { return KommuneInfoClientImpl( - proxiedWebClientBuilder.build(), + proxiedWebClient, fiksProperties() ) } diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/idporten/IdPortenClientConfig.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/idporten/IdPortenClientConfig.kt index 07b7093bc..3242d552f 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/idporten/IdPortenClientConfig.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/idporten/IdPortenClientConfig.kt @@ -12,7 +12,7 @@ import org.springframework.web.reactive.function.client.WebClient @Profile("!mock") @Configuration class IdPortenClientConfig( - private val proxiedWebClientBuilder: WebClient.Builder, + private val proxiedWebClient: WebClient, @Value("\${no.nav.sosialhjelp.idporten.token_url}") private val tokenUrl: String, @Value("\${no.nav.sosialhjelp.idporten.client_id}") private val clientId: String, @Value("\${no.nav.sosialhjelp.idporten.scope}") private val scope: String, @@ -24,7 +24,7 @@ class IdPortenClientConfig( @Bean fun idPortenClient(): IdPortenClient { return IdPortenClient( - webClient = proxiedWebClientBuilder.build(), + webClient = proxiedWebClient, idPortenProperties = idPortenProperties() ) } diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/norg/NorgConfig.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/norg/NorgConfig.kt index 287352ee2..a5eb5ec61 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/norg/NorgConfig.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/norg/NorgConfig.kt @@ -10,11 +10,10 @@ import reactor.netty.http.client.HttpClient @Configuration class NorgConfig( private val clientProperties: ClientProperties, - private val webClientBuilder: WebClient.Builder ) { @Bean - fun norgWebClient(): WebClient = + fun norgWebClient(webClientBuilder: WebClient.Builder): WebClient = webClientBuilder .baseUrl(clientProperties.norgEndpointUrl) .clientConnector(ReactorClientHttpConnector(HttpClient.newConnection())) diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/sts/StsConfig.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/sts/StsConfig.kt index 3aca1a396..0af272a4a 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/sts/StsConfig.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/client/sts/StsConfig.kt @@ -15,12 +15,11 @@ import java.util.Base64 @Profile("!(mock | local)") @Configuration class StsConfig ( - private val clientProperties: ClientProperties, - private val webClientBuilder: WebClient.Builder + private val clientProperties: ClientProperties ) { @Bean - fun stsWebClient(): WebClient = + fun stsWebClient(webClientBuilder: WebClient.Builder): WebClient = webClientBuilder .baseUrl(clientProperties.stsTokenEndpointUrl) .defaultHeader(HttpHeaders.AUTHORIZATION, "Basic ${credentials()}") diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/common/InnsynExceptionHandler.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/common/InnsynExceptionHandler.kt index c56ec6f10..81db54f68 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/common/InnsynExceptionHandler.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/common/InnsynExceptionHandler.kt @@ -43,28 +43,28 @@ class InnsynExceptionHandler : ResponseEntityExceptionHandler() { @ExceptionHandler(FiksNotFoundException::class) fun handleFiksNotFoundError(e: FiksNotFoundException): ResponseEntity { - log.error("DigisosSak finnes ikke i FIKS ", e) + log.error("DigisosSak finnes ikke i FIKS ") val error = FrontendErrorMessage(FIKS_ERROR, "DigisosSak finnes ikke") return ResponseEntity(error, HttpStatus.NOT_FOUND) } @ExceptionHandler(FiksException::class) fun handleFiksError(e: FiksException): ResponseEntity { - log.error("Noe feilet ved kall til Fiks", e) + log.error("Noe feilet ved kall til Fiks") val error = FrontendErrorMessage(FIKS_ERROR, NOE_UVENTET_FEILET) return ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR) } @ExceptionHandler(FiksClientException::class) fun handleFiksClientError(e: FiksClientException): ResponseEntity { - log.error("Client-feil ved kall til Fiks", e) + log.error("Client-feil ved kall til Fiks") val error = FrontendErrorMessage(FIKS_ERROR, NOE_UVENTET_FEILET) return ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR) } @ExceptionHandler(FiksServerException::class) fun handleFiksServerError(e: FiksServerException): ResponseEntity { - log.error("Server-feil ved kall til Fiks", e) + log.error("Server-feil ved kall til Fiks") val error = FrontendErrorMessage(FIKS_ERROR, NOE_UVENTET_FEILET) return ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR) } diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/ProxiedWebClientConfig.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/ProxiedWebClientConfig.kt index ae647c846..25e10d8f8 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/ProxiedWebClientConfig.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/ProxiedWebClientConfig.kt @@ -17,9 +17,10 @@ class ProxiedWebClientConfig { private lateinit var proxyUrl: String @Bean - fun proxiedWebClientBuilder(): WebClient.Builder = - WebClient.builder() + fun proxiedWebClient(webClientBuilder: WebClient.Builder): WebClient = + webClientBuilder .clientConnector(getReactorClientHttpConnector(proxyUrl)) + .build() } @@ -28,8 +29,9 @@ class ProxiedWebClientConfig { class MockProxiedWebClientConfig { @Bean - fun proxiedWebClientBuilder(): WebClient.Builder = - WebClient.builder() + fun proxiedWebClient(webClientBuilder: WebClient.Builder): WebClient = + webClientBuilder .clientConnector(ReactorClientHttpConnector(HttpClient.newConnection())) + .build() } \ No newline at end of file diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/WebClientConfig.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/WebClientConfig.kt index e4aa19fae..7711b6ca2 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/WebClientConfig.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/config/WebClientConfig.kt @@ -7,11 +7,10 @@ import org.springframework.web.reactive.function.client.WebClient import reactor.netty.http.client.HttpClient @Configuration -class WebClientConfig( - private val webClientBuilder: WebClient.Builder, -) { +class WebClientConfig { + @Bean - fun webClient(): WebClient = + fun webClient(webClientBuilder: WebClient.Builder): WebClient = webClientBuilder .clientConnector(ReactorClientHttpConnector(HttpClient.newConnection())) .build() diff --git a/src/main/kotlin/no/nav/sosialhjelp/innsyn/utils/Utils.kt b/src/main/kotlin/no/nav/sosialhjelp/innsyn/utils/Utils.kt index 01bc95236..14ff18d4f 100644 --- a/src/main/kotlin/no/nav/sosialhjelp/innsyn/utils/Utils.kt +++ b/src/main/kotlin/no/nav/sosialhjelp/innsyn/utils/Utils.kt @@ -8,14 +8,15 @@ import no.nav.sbl.soknadsosialhjelp.digisos.soker.filreferanse.JsonDokumentlager import no.nav.sbl.soknadsosialhjelp.digisos.soker.filreferanse.JsonSvarUtFilreferanse import no.nav.sosialhjelp.api.fiks.DigisosSak import no.nav.sosialhjelp.api.fiks.ErrorMessage +import no.nav.sosialhjelp.client.kommuneinfo.feilmeldingUtenFnr +import no.nav.sosialhjelp.client.kommuneinfo.toFiksErrorMessage import no.nav.sosialhjelp.innsyn.config.ClientProperties import no.nav.sosialhjelp.innsyn.utils.mdc.MDCUtils import org.slf4j.Logger import org.slf4j.LoggerFactory import org.slf4j.MDC import org.springframework.core.ParameterizedTypeReference -import org.springframework.web.client.HttpStatusCodeException -import java.io.IOException +import org.springframework.web.reactive.function.client.WebClientResponseException import java.sql.Timestamp import java.time.Instant import java.time.LocalDate @@ -53,7 +54,7 @@ fun hentDokumentlagerUrl(clientProperties: ClientProperties, dokumentlagerId: St fun String.toLocalDateTime(): LocalDateTime { return ZonedDateTime.parse(this, ISO_DATE_TIME) - .withZoneSameInstant(ZoneId.of("Europe/Oslo")).toLocalDateTime() + .withZoneSameInstant(ZoneId.of("Europe/Oslo")).toLocalDateTime() } fun String.toLocalDate(): LocalDate = LocalDate.parse(this, ISO_LOCAL_DATE) @@ -71,7 +72,7 @@ fun formatLocalDateTime(dato: LocalDateTime): String { return dato.format(datoFormatter) } -fun soknadsalderIMinutter(tidspunktSendt : LocalDateTime?) : Long { +fun soknadsalderIMinutter(tidspunktSendt: LocalDateTime?): Long { return tidspunktSendt?.until(LocalDateTime.now(), ChronoUnit.MINUTES) ?: -1 } @@ -87,9 +88,9 @@ fun enumNameToLowercase(string: String): String { */ fun lagNavEksternRefId(digisosSak: DigisosSak): String { val previousId: String = digisosSak.ettersendtInfoNAV?.ettersendelser - ?.map { it.navEksternRefId }?.maxByOrNull { it.takeLast(COUNTER_SUFFIX_LENGTH).toLong() } - ?: digisosSak.originalSoknadNAV?.navEksternRefId?.plus("0000") - ?: digisosSak.fiksDigisosId.plus("0000") + ?.map { it.navEksternRefId }?.maxByOrNull { it.takeLast(COUNTER_SUFFIX_LENGTH).toLong() } + ?: digisosSak.originalSoknadNAV?.navEksternRefId?.plus("0000") + ?: digisosSak.fiksDigisosId.plus("0000") val nesteSuffix = lagIdSuffix(previousId) return (previousId.dropLast(COUNTER_SUFFIX_LENGTH).plus(nesteSuffix)) @@ -119,24 +120,17 @@ fun isRunningInProd(): Boolean { return clusterName != null && clusterName.contains("prod") } -fun T.toFiksErrorMessage(): ErrorMessage? { - return try { - objectMapper.readValue(this.responseBodyAsByteArray, ErrorMessage::class.java) - } catch (e: IOException) { - null - } +fun messageUtenFnr(e: WebClientResponseException): String { + val fiksErrorMessage = e.toFiksErrorMessage()?.feilmeldingUtenFnr + val message = e.message?.feilmeldingUtenFnr + return "$message - $fiksErrorMessage" } - -val String.feilmeldingUtenFnr: String? - get() { - return this.replace(Regex("""\b[0-9]{11}\b"""), "[FNR]") - } +val String.feilmeldingUtenFnr: String + get() = this.replace(Regex("""\b[0-9]{11}\b"""), "[FNR]") val ErrorMessage.feilmeldingUtenFnr: String? - get() { - return this.message?.feilmeldingUtenFnr - } + get() = this.message?.feilmeldingUtenFnr fun runAsyncWithMDC(runnable: Runnable, executor: ExecutorService): CompletableFuture { val previous: Map = MDC.getCopyOfContextMap() diff --git a/src/test/kotlin/no/nav/sosialhjelp/innsyn/ApplicationContextTest.kt b/src/test/kotlin/no/nav/sosialhjelp/innsyn/ApplicationContextTest.kt index 4c376cf19..48c95d76a 100644 --- a/src/test/kotlin/no/nav/sosialhjelp/innsyn/ApplicationContextTest.kt +++ b/src/test/kotlin/no/nav/sosialhjelp/innsyn/ApplicationContextTest.kt @@ -16,8 +16,8 @@ class ApplicationContextTest { @MockkBean private lateinit var idPortenClient: IdPortenClient - @MockkBean(name = "proxiedWebClientBuilder", relaxed = true) - private lateinit var proxiedWebClientBuilder: WebClient.Builder + @MockkBean(name = "proxiedWebClient", relaxed = true) + private lateinit var proxiedWebClient: WebClient @MockkBean private lateinit var proxiedWebClientConfig: ProxiedWebClientConfig diff --git a/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientTest.kt b/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientTest.kt index 682488652..c6ac557f4 100644 --- a/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientTest.kt +++ b/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/digisosapi/DigisosApiClientTest.kt @@ -1,8 +1,9 @@ package no.nav.sosialhjelp.innsyn.client.digisosapi import io.mockk.coEvery -import io.mockk.every import io.mockk.mockk +import mockwebserver3.MockResponse +import mockwebserver3.MockWebServer import no.nav.sbl.soknadsosialhjelp.digisos.soker.JsonDigisosSoker import no.nav.sosialhjelp.innsyn.client.fiks.FiksClientImpl import no.nav.sosialhjelp.innsyn.config.ClientProperties @@ -10,9 +11,9 @@ import no.nav.sosialhjelp.innsyn.domain.DigisosApiWrapper import no.nav.sosialhjelp.innsyn.domain.SakWrapper import no.nav.sosialhjelp.innsyn.responses.ok_komplett_jsondigisossoker_response import no.nav.sosialhjelp.innsyn.service.idporten.IdPortenService +import no.nav.sosialhjelp.innsyn.utils.objectMapper import org.junit.jupiter.api.Test -import org.springframework.http.ResponseEntity -import org.springframework.web.client.RestTemplate +import org.springframework.web.reactive.function.client.WebClient internal class DigisosApiClientTest { @@ -20,23 +21,24 @@ internal class DigisosApiClientTest { @Test fun `Post digisos sak til mock`() { - val restTemplate: RestTemplate = mockk() + val mockWebServer = MockWebServer() + val fiksWebClient = WebClient.create(mockWebServer.url("/").toString()) val idPortenService: IdPortenService = mockk() val fiksClientImpl: FiksClientImpl = mockk() - val digisosApiClient = DigisosApiClientImpl(clientProperties, restTemplate, idPortenService, fiksClientImpl) + val digisosApiClient = DigisosApiClientImpl(clientProperties, fiksWebClient, idPortenService, fiksClientImpl) - val mockResponse: ResponseEntity = mockk() - every { mockResponse.body } returns ok_komplett_jsondigisossoker_response coEvery { idPortenService.getToken().token } returns "Token" - every { - restTemplate.exchange( - any(), - any(), - any(), - String::class.java) - } returns mockResponse - - digisosApiClient.oppdaterDigisosSak("123123", DigisosApiWrapper(SakWrapper(JsonDigisosSoker()), "")) + + mockWebServer.enqueue( + MockResponse() + .setResponseCode(202) + .setBody("ok") + ) + + val jsonDigisosSoker = + objectMapper.readValue(ok_komplett_jsondigisossoker_response, JsonDigisosSoker::class.java) + + digisosApiClient.oppdaterDigisosSak("123123", DigisosApiWrapper(SakWrapper(jsonDigisosSoker), "")) } } \ No newline at end of file diff --git a/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientTest.kt b/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientTest.kt index 02753063d..dfba67cd3 100644 --- a/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientTest.kt +++ b/src/test/kotlin/no/nav/sosialhjelp/innsyn/client/fiks/FiksClientTest.kt @@ -6,8 +6,9 @@ import io.mockk.clearAllMocks import io.mockk.every import io.mockk.just import io.mockk.mockk -import io.mockk.slot import io.mockk.verify +import mockwebserver3.MockResponse +import mockwebserver3.MockWebServer import no.nav.sbl.soknadsosialhjelp.digisos.soker.JsonDigisosSoker import no.nav.sbl.soknadsosialhjelp.vedlegg.JsonVedleggSpesifikasjon import no.nav.sosialhjelp.api.fiks.DigisosSak @@ -21,37 +22,39 @@ import no.nav.sosialhjelp.innsyn.service.pdf.EttersendelsePdfGenerator import no.nav.sosialhjelp.innsyn.service.vedlegg.FilForOpplasting import no.nav.sosialhjelp.innsyn.service.vedlegg.KrypteringService import no.nav.sosialhjelp.innsyn.utils.objectMapper -import no.nav.sosialhjelp.innsyn.utils.typeRef import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatCode import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.springframework.http.HttpEntity -import org.springframework.http.HttpMethod +import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus +import org.springframework.http.MediaType import org.springframework.http.ResponseEntity -import org.springframework.util.LinkedMultiValueMap -import org.springframework.web.client.HttpClientErrorException -import org.springframework.web.client.HttpServerErrorException -import org.springframework.web.client.RestTemplate +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.bodyToMono +import org.springframework.web.reactive.function.client.toEntity import java.io.InputStream internal class FiksClientTest { + private val mockWebServer = MockWebServer() + private val clientProperties: ClientProperties = mockk(relaxed = true) - private val restTemplate: RestTemplate = mockk() + private val fiksWebClient = WebClient.create(mockWebServer.url("/").toString()) private val redisService: RedisService = mockk() private val retryProperties: FiksRetryProperties = mockk() private val ettersendelsePdfGenerator: EttersendelsePdfGenerator = mockk() private val krypteringService: KrypteringService = mockk() - private val fiksClient = FiksClientImpl(clientProperties, restTemplate, retryProperties, redisService) + private val fiksClient = FiksClientImpl(clientProperties, fiksWebClient, retryProperties, redisService) private val id = "123" @BeforeEach fun init() { clearAllMocks() + mockWebServer.start() every { redisService.get(any(), any()) } returns null every { redisService.put(any(), any(), any()) } just Runs @@ -62,18 +65,19 @@ internal class FiksClientTest { every { retryProperties.maxDelay } returns 10 } + @AfterEach + internal fun tearDown() { + mockWebServer.shutdown() + } + @Test fun `GET eksakt 1 DigisosSak`() { - val mockResponse: ResponseEntity = mockk() - every { mockResponse.body } returns ok_digisossak_response - every { - restTemplate.exchange( - any(), - any(), - any(), - String::class.java, - id) - } returns mockResponse + mockWebServer.enqueue( + MockResponse() + .setResponseCode(200) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(ok_digisossak_response) + ) val result = fiksClient.hentDigisosSak(id, "Token", false) @@ -94,16 +98,12 @@ internal class FiksClientTest { @Test fun `GET digisosSak fra cache etter put`() { - val mockResponse: ResponseEntity = mockk() - every { mockResponse.body } returns ok_digisossak_response - every { - restTemplate.exchange( - any(), - any(), - any(), - String::class.java, - id) - } returns mockResponse + mockWebServer.enqueue( + MockResponse() + .setResponseCode(200) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(ok_digisossak_response) + ) val result1 = fiksClient.hentDigisosSak(id, "Token", true) @@ -111,7 +111,7 @@ internal class FiksClientTest { verify(exactly = 1) { redisService.put(any(), any(), any()) } verify(exactly = 1) { redisService.get(any(), DigisosSak::class.java) } - val digisosSak: DigisosSak = objectMapper.readValue(ok_digisossak_response) + val digisosSak: DigisosSak = objectMapper.readValue(ok_digisossak_response) every { redisService.get(id, DigisosSak::class.java) } returns digisosSak val result = fiksClient.hentDigisosSak(id, "Token", true) @@ -124,61 +124,54 @@ internal class FiksClientTest { @Test fun `GET DigisosSak feiler hvis Fiks gir 500`() { - every { - restTemplate.exchange( - any(), - any(), - any(), - String::class.java, - id) - } throws HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "some error") - assertThatExceptionOfType(FiksServerException::class.java) - .isThrownBy { fiksClient.hentDigisosSak(id, "Token", true) } + every { retryProperties.attempts } returns 1 + + mockWebServer.enqueue( + MockResponse() + .setResponseCode(500) + ) + assertThatExceptionOfType(FiksServerException::class.java) + .isThrownBy { fiksClient.hentDigisosSak(id, "Token", true) } } @Test fun `GET alle DigisosSaker skal bruke retry hvis Fiks gir 5xx-feil`() { - every { - restTemplate.exchange( - any(), - any(), - any(), - typeRef>()) - } throws HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR, "some error") + mockWebServer.enqueue( + MockResponse() + .setResponseCode(500) + ) + mockWebServer.enqueue( + MockResponse() + .setResponseCode(500) + ) assertThatExceptionOfType(FiksServerException::class.java).isThrownBy { fiksClient.hentAlleDigisosSaker("Token") } - - verify(atLeast = 2) { restTemplate.exchange(any(), any(), any(), typeRef>()) } + assertThat(mockWebServer.requestCount).isEqualTo(2) } @Test fun `GET alle DigisosSaker skal ikke bruke retry hvis Fiks gir 4xx-feil`() { - every { - restTemplate.exchange( - any(), - any(), - any(), - typeRef>()) - } throws HttpClientErrorException(HttpStatus.BAD_REQUEST, "some error") + mockWebServer.enqueue( + MockResponse() + .setResponseCode(400) + ) assertThatExceptionOfType(FiksClientException::class.java).isThrownBy { fiksClient.hentAlleDigisosSaker("Token") } - verify(exactly = 1) { restTemplate.exchange(any(), any(), any(), typeRef>()) } + assertThat(mockWebServer.requestCount).isEqualTo(1) } @Test fun `GET alle DigisosSaker`() { - val mockListResponse: ResponseEntity> = mockk() val digisosSakOk = objectMapper.readValue(ok_digisossak_response, DigisosSak::class.java) - every { mockListResponse.body } returns listOf(digisosSakOk, digisosSakOk) - every { - restTemplate.exchange( - any(), - any(), - any(), - typeRef>()) - } returns mockListResponse + + mockWebServer.enqueue( + MockResponse() + .setResponseCode(200) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(objectMapper.writeValueAsString(listOf(digisosSakOk, digisosSakOk))) + ) val result = fiksClient.hentAlleDigisosSaker("Token") @@ -188,16 +181,12 @@ internal class FiksClientTest { @Test fun `GET dokument`() { - val mockResponse: ResponseEntity = mockk() - every { mockResponse.body } returns ok_minimal_jsondigisossoker_response - every { - restTemplate.exchange( - any(), - any(), - any(), - String::class.java, - any()) - } returns mockResponse + mockWebServer.enqueue( + MockResponse() + .setResponseCode(200) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(ok_minimal_jsondigisossoker_response) + ) val result = fiksClient.hentDokument(id, "dokumentlagerId", JsonDigisosSoker::class.java, "Token") @@ -218,16 +207,12 @@ internal class FiksClientTest { @Test fun `GET dokument fra cache etter put`() { - val mockResponse: ResponseEntity = mockk() - every { mockResponse.body } returns ok_digisossak_response - every { - restTemplate.exchange( - any(), - any(), - any(), - String::class.java, - any()) - } returns mockResponse + mockWebServer.enqueue( + MockResponse() + .setResponseCode(200) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(ok_minimal_jsondigisossoker_response) + ) val result1 = fiksClient.hentDokument(id, "dokumentlagerId", JsonDigisosSoker::class.java, "Token") @@ -251,16 +236,12 @@ internal class FiksClientTest { // cache returnerer jsonsoknad, men vi forventer jsondigisossoker every { redisService.get(any(), JsonDigisosSoker::class.java) } returns null - val mockResponse: ResponseEntity = mockk() - every { mockResponse.body } returns ok_minimal_jsondigisossoker_response - every { - restTemplate.exchange( - any(), - any(), - any(), - String::class.java, - any()) - } returns mockResponse + mockWebServer.enqueue( + MockResponse() + .setResponseCode(200) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .setBody(ok_minimal_jsondigisossoker_response) + ) val result2 = fiksClient.hentDokument(id, "dokumentlagerId", JsonDigisosSoker::class.java, "Token") @@ -269,8 +250,11 @@ internal class FiksClientTest { verify(exactly = 1) { redisService.put(any(), any(), any()) } } - @Test + @Test // fikk ikke mockWebServer til å funke her uten å skjønner hvorfor (InputStream-relatert), så gikk for "klassisk" mockk stil fun `POST ny ettersendelse`() { + val webClient: WebClient = mockk() + val clientForPost = FiksClientImpl(clientProperties, webClient, retryProperties, redisService) + val fil1: InputStream = mockk() val fil2: InputStream = mockk() every { fil1.readAllBytes() } returns "test-fil".toByteArray() @@ -280,33 +264,63 @@ internal class FiksClientTest { every { ettersendelsePdfGenerator.generate(any(), any()) } returns ettersendelsPdf every { krypteringService.krypter(any(), any(), any()) } returns fil1 - val mockDigisosSakResponse: ResponseEntity = mockk() - every { mockDigisosSakResponse.body } returns ok_digisossak_response - every { restTemplate.exchange(any(), HttpMethod.GET, any(), String::class.java, id) } returns mockDigisosSakResponse + val files = listOf(FilForOpplasting("filnavn0", "image/png", 1L, fil1), + FilForOpplasting("filnavn1", "image/jpg", 1L, fil2)) + + every { + webClient.get() + .uri(any(), any()) + .headers(any()) + .retrieve() + .onStatus(any(), any()) + .onStatus(any(), any()) + .onStatus(any(), any()) + .bodyToMono() + .block() + } returns objectMapper.readValue(ok_digisossak_response, DigisosSak::class.java) - val slot = slot>>() - val mockFiksResponse: ResponseEntity = mockk() - every { mockFiksResponse.statusCodeValue } returns 202 - every { restTemplate.exchange(any(), HttpMethod.POST, capture(slot), String::class.java, any()) } returns mockFiksResponse + every { + webClient.post() + .uri(any(), any(), any(), any()) + .headers(any()) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(any()) + .retrieve() + .onStatus(any(), any()) + .onStatus(any(), any()) + .toEntity() + .block() + } returns ResponseEntity(HttpStatus.ACCEPTED) + + assertThatCode { + clientForPost.lastOppNyEttersendelse(files, + JsonVedleggSpesifikasjon(), + id, + "token") + }.doesNotThrowAnyException() + } + + @Test + internal fun `should produce body for upload`() { + val fil1: InputStream = mockk() + val fil2: InputStream = mockk() + every { fil1.readAllBytes() } returns "test-fil".toByteArray() + every { fil2.readAllBytes() } returns "div".toByteArray() val files = listOf(FilForOpplasting("filnavn0", "image/png", 1L, fil1), - FilForOpplasting("filnavn1", "image/jpg", 1L, fil2)) - - assertThatCode { fiksClient.lastOppNyEttersendelse(files, JsonVedleggSpesifikasjon(), id, "token") }.doesNotThrowAnyException() - - val httpEntity = slot.captured - - assertThat(httpEntity.body!!.size == 5) - assertThat(httpEntity.headers["Content-Type"]!![0] == "multipart/form-data") - assertThat(httpEntity.body!!.keys.contains("vedlegg.json")) - assertThat(httpEntity.body!!.keys.contains("vedleggSpesifikasjon:0")) - assertThat(httpEntity.body!!.keys.contains("dokument:0")) - assertThat(httpEntity.body!!.keys.contains("vedleggSpesifikasjon:1")) - assertThat(httpEntity.body!!.keys.contains("dokument:1")) - assertThat(httpEntity.body!!["dokument:0"].toString().contains("InputStream resource")) - assertThat(httpEntity.body!!["dokument:1"].toString().contains("InputStream resource")) - assertThat(httpEntity.body!!["vedlegg.json"].toString().contains("text/plain;charset=UTF-8")) - assertThat(httpEntity.body!!["vedleggSpesifikasjon:0"].toString().contains("text/plain;charset=UTF-8")) - assertThat(httpEntity.body!!["vedleggSpesifikasjon:1"].toString().contains("text/plain;charset=UTF-8")) + FilForOpplasting("filnavn1", "image/jpg", 1L, fil2)) + val body = fiksClient.createBodyForUpload(JsonVedleggSpesifikasjon(), files) + + assertThat(body.size == 5) + assertThat(body.keys.contains("vedlegg.json")) + assertThat(body.keys.contains("vedleggSpesifikasjon:0")) + assertThat(body.keys.contains("dokument:0")) + assertThat(body.keys.contains("vedleggSpesifikasjon:1")) + assertThat(body.keys.contains("dokument:1")) + assertThat(body["dokument:0"].toString().contains("InputStream resource")) + assertThat(body["dokument:1"].toString().contains("InputStream resource")) + assertThat(body["vedlegg.json"].toString().contains("text/plain;charset=UTF-8")) + assertThat(body["vedleggSpesifikasjon:0"].toString().contains("text/plain;charset=UTF-8")) + assertThat(body["vedleggSpesifikasjon:1"].toString().contains("text/plain;charset=UTF-8")) } } \ No newline at end of file