Skip to content

Commit

Permalink
Utbedret felles feilhåndtering, la til bekreftelse-api i kafka-keys i…
Browse files Browse the repository at this point in the history
…nbound
  • Loading branch information
naviktthomas committed Sep 17, 2024
1 parent e4ae96a commit 166652c
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 41 deletions.
1 change: 1 addition & 0 deletions apps/kafka-key-generator/nais/nais-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ spec:
- application: paw-arbeidssoekerregisteret-utgang-pdl
- application: paw-microfrontend-toggler
- application: paw-arbeidssoekerregisteret-hendelselogg-backup
- application: paw-arbeidssoeker-bekreftelse-api
1 change: 1 addition & 0 deletions lib/error-handling/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {

dependencies {
compileOnly(ktorServer.core)
compileOnly(ktor.serializationJackson)
compileOnly(orgApacheKafka.kafkaStreams)
compileOnly(loggingLibs.logbackClassic)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package no.nav.paw.error.handler
import io.ktor.server.application.ApplicationCall
import io.ktor.server.plugins.BadRequestException
import io.ktor.server.plugins.ContentTransformationException
import io.ktor.server.request.ApplicationRequest
import io.ktor.server.request.RequestAlreadyConsumedException
import io.ktor.server.request.uri
import io.ktor.server.response.respond
import no.nav.paw.error.exception.ClientResponseException
import no.nav.paw.error.exception.ServerResponseException
import no.nav.paw.error.model.ProblemDetails
import no.nav.paw.error.model.build400Error
import no.nav.paw.error.model.build500Error
import no.nav.paw.error.model.buildError
Expand All @@ -17,69 +19,62 @@ import org.slf4j.LoggerFactory
private const val ERROR_TYPE_PREFIX = "PAW_"
private val logger: Logger = LoggerFactory.getLogger("no.nav.paw.logger.error.http")

suspend fun <T : Throwable> ApplicationCall.handleException(throwable: T) {
suspend fun ApplicationCall.handleException(throwable: Throwable) {
val problemDetails = resolveProblemDetails(request, throwable)
logger.error(problemDetails.detail, throwable)
respond(problemDetails.status, problemDetails)
}

fun resolveProblemDetails(request: ApplicationRequest, throwable: Throwable): ProblemDetails {
when (throwable) {
is BadRequestException -> {
return build400Error(
"${ERROR_TYPE_PREFIX}KUNNE_IKKE_TOLKE_FORESPOERSEL",
"Kunne ikke tolke innhold i forespørsel",
request.uri
)
}

is ContentTransformationException -> {
val error = build400Error(
return build400Error(
"${ERROR_TYPE_PREFIX}KUNNE_IKKE_TOLKE_INNHOLD",
"Kunne ikke tolke innhold i kall",
this.request.uri
request.uri
)
logger.debug(error.detail, throwable)
this.respond(error.status, error)
}

is ClientResponseException -> {
val error = buildError(
"${ERROR_TYPE_PREFIX}${throwable.code}",
throwable.message,
throwable.status,
this.request.uri
is RequestAlreadyConsumedException -> {
return build500Error(
"${ERROR_TYPE_PREFIX}FORESPOERSEL_ALLEREDE_MOTTATT",
"Forespørsel er allerede mottatt. Dette er en kodefeil",
request.uri
)
logger.warn(error.detail, throwable)
this.respond(error.status, error)
}

is ServerResponseException -> {
val error = buildError(
return buildError(
"${ERROR_TYPE_PREFIX}${throwable.code}",
throwable.message,
throwable.status,
this.request.uri
request.uri
)
logger.error(error.detail, throwable)
this.respond(error.status, error)
}

is BadRequestException -> {
val error =
build400Error(
"${ERROR_TYPE_PREFIX}ULOVLIG_FORESPOERSEL",
"Kunne ikke tolke innhold i forespørsel",
this.request.uri
)
logger.error(error.detail, throwable)
this.respond(error.status, error)
}

is RequestAlreadyConsumedException -> {
val error = build500Error(
"${ERROR_TYPE_PREFIX}FORESPOERSEL_ALLEREDE_MOTTATT",
"Forespørsel er allerede mottatt. Dette er en kodefeil",
this.request.uri
is ClientResponseException -> {
return buildError(
"${ERROR_TYPE_PREFIX}${throwable.code}",
throwable.message,
throwable.status,
request.uri
)
logger.error(error.detail, throwable)
this.respond(error.status, error)
}

else -> {
val error = build500Error(
return build500Error(
"${ERROR_TYPE_PREFIX}UKJENT_FEIL",
"Forespørsel feilet med ukjent feil",
this.request.uri
request.uri
)
logger.error(error.detail, throwable)
this.respond(error.status, error)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package no.nav.paw.error.model

import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import io.ktor.http.HttpStatusCode
import no.nav.paw.error.serialize.HttpStatusCodeDeserializer
import no.nav.paw.error.serialize.HttpStatusCodeSerializer

/**
* Object som inneholder detaljer om en oppstått feilsituasjon, basert på RFC 7807.
Expand All @@ -9,13 +13,13 @@ import io.ktor.http.HttpStatusCode
data class ProblemDetails(
val type: String,
val title: String,
val status: HttpStatusCode,
@JsonSerialize(using = HttpStatusCodeSerializer::class) @JsonDeserialize(using = HttpStatusCodeDeserializer::class) val status: HttpStatusCode,
val detail: String,
val instance: String
) {
constructor(
title: String,
status: HttpStatusCode,
@JsonSerialize(using = HttpStatusCodeSerializer::class) @JsonDeserialize(using = HttpStatusCodeDeserializer::class) status: HttpStatusCode,
detail: String,
instance: String
) : this("about:blank", title, status, detail, instance)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package no.nav.paw.error.serialize

import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import io.ktor.http.HttpStatusCode

class HttpStatusCodeDeserializer : JsonDeserializer<HttpStatusCode>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): HttpStatusCode {
return HttpStatusCode.fromValue(parser.numberValue.toInt())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package no.nav.paw.error.serialize

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import io.ktor.http.HttpStatusCode

class HttpStatusCodeSerializer : JsonSerializer<HttpStatusCode>() {
override fun serialize(value: HttpStatusCode, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeNumber(value.value)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package no.nav.paw.error.handler

import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.shouldBe
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.http.HttpStatusCode
import io.ktor.serialization.jackson.jackson
import io.ktor.server.application.ApplicationCall
import io.ktor.server.plugins.BadRequestException
import io.ktor.server.plugins.statuspages.StatusPages
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
import io.ktor.server.testing.testApplication
import no.nav.paw.error.model.ProblemDetails
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation as ClientContentNegotiation
import io.ktor.server.application.install as serverInstall
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation as ServerContentNegotiation

class HttpExceptionHandlerTest : FreeSpec({
"Skal håndtere exceptions og returnere ProblemDetails response" {
testApplication {
application {
serverInstall(IgnoreTrailingSlash)
serverInstall(StatusPages) {
exception<Throwable> { call: ApplicationCall, cause: Throwable ->
call.handleException(cause)
}
}
serverInstall(ServerContentNegotiation) {
jackson {}
}
routing {
get("/api/400") {
throw BadRequestException("It's bad")
}
}
}

val client = createClient {
install(ClientContentNegotiation) {
jackson {}
}
}

val response400 = client.get("/api/400")
val responseBody404 = response400.body<ProblemDetails>()
response400.status shouldBe HttpStatusCode.BadRequest
responseBody404.status shouldBe HttpStatusCode.BadRequest
responseBody404.type shouldBe "PAW_KUNNE_IKKE_TOLKE_FORESPOERSEL"
responseBody404.title shouldBe HttpStatusCode.BadRequest.description
}
}
})

0 comments on commit 166652c

Please sign in to comment.