Skip to content

Commit

Permalink
Første utkast til API for bestilling av brev
Browse files Browse the repository at this point in the history
Inkludert integrasjon mot brev-sanity-proxy
  • Loading branch information
matiasvinjevoll committed Sep 23, 2024
1 parent d130690 commit fc81dea
Show file tree
Hide file tree
Showing 18 changed files with 209 additions and 32 deletions.
4 changes: 4 additions & 0 deletions .nais/app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ spec:
value: api://dev-gcp.aap.behandlingsflyt/.default
- name: INTEGRASJON_BEHANDLINGSFLYT_AZP
value: dev-gcp:aap:behandlingsflyt
- name: INTEGRASJON_BREV_SANITY_PROXY_URL
value: http://brev-sanity-proxy
- name: INTEGRASJON_BREV_SANITY_PROXY_SCOPE
value: api://dev-gcp.aap.brev-sanity-proxy/.default
33 changes: 29 additions & 4 deletions app/src/main/kotlin/no/nav/aap/brev/App.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package no.nav.aap.brev

import com.papsign.ktor.openapigen.route.apiRouting
import com.papsign.ktor.openapigen.route.path.normal.post
import com.papsign.ktor.openapigen.route.response.respond
import com.papsign.ktor.openapigen.route.route
import com.zaxxer.hikari.HikariConfig
Expand All @@ -17,6 +16,12 @@ import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.micrometer.prometheusmetrics.PrometheusConfig
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
import no.nav.aap.brev.api.BestillBrevRequest
import no.nav.aap.brev.api.BestillBrevResponse
import no.nav.aap.brev.api.ErrorRespons
import no.nav.aap.brev.domene.BrevbestillingReferanse
import no.nav.aap.brev.innhold.BrevinnholdService
import no.nav.aap.brev.innhold.SanityBrevinnholdGateway
import no.nav.aap.komponenter.commonKtorModule
import no.nav.aap.komponenter.config.requiredConfigForKey
import no.nav.aap.komponenter.dbmigrering.Migrering
Expand All @@ -33,10 +38,22 @@ class App

fun main() {
Thread.currentThread().setUncaughtExceptionHandler { _, e -> SECURE_LOGGER.error("Uhåndtert feil", e) }
embeddedServer(Netty, port = 8080) { server(DbConfig()) }.start(wait = true)

val brevinnholdGateway = SanityBrevinnholdGateway()
val brevinnholdService = BrevinnholdService(brevinnholdGateway)

embeddedServer(Netty, port = 8080) {
server(
DbConfig(),
brevinnholdService,
)
}.start(wait = true)
}

internal fun Application.server(dbConfig: DbConfig) {
internal fun Application.server(
dbConfig: DbConfig,
brevinnholdService: BrevinnholdService,
) {
val prometheus = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)

commonKtorModule(prometheus, AzureConfig(), "AAP - Brev")
Expand Down Expand Up @@ -65,7 +82,15 @@ internal fun Application.server(dbConfig: DbConfig) {
route("/api") {
route("/bestill") {
authorizedPostWithApprovedList<Unit, BestillBrevResponse, BestillBrevRequest>(behandlingsflytAzp) { _, request ->
respond(BestillBrevResponse(UUID.randomUUID()), HttpStatusCode.Created)
brevinnholdService.behandleBrevbestilling(
request.behandlingReferanse,
request.brevtype,
request.language,
)
respond(
response = BestillBrevResponse(BrevbestillingReferanse(UUID.randomUUID())),
statusCode = HttpStatusCode.Created
)
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions app/src/main/kotlin/no/nav/aap/brev/BestillBrevRequest.kt

This file was deleted.

5 changes: 0 additions & 5 deletions app/src/main/kotlin/no/nav/aap/brev/BestillBrevResponse.kt

This file was deleted.

11 changes: 11 additions & 0 deletions app/src/main/kotlin/no/nav/aap/brev/api/BestillBrevRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package no.nav.aap.brev.api

import no.nav.aap.brev.domene.BehandlingReferanse
import no.nav.aap.brev.domene.Brevtype
import no.nav.aap.brev.domene.Språk

data class BestillBrevRequest(
val behandlingReferanse: BehandlingReferanse,
val brevtype: Brevtype,
val language: Språk,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package no.nav.aap.brev.api

import no.nav.aap.brev.domene.BrevbestillingReferanse


data class BestillBrevResponse(val brevbestillingReferanse: BrevbestillingReferanse)
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package no.nav.aap.brev
package no.nav.aap.brev.api

data class ErrorRespons(val message: String?)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package no.nav.aap.brev.domene

@JvmInline
value class BehandlingReferanse(val referanse: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package no.nav.aap.brev.domene

import java.util.UUID

@JvmInline
value class BrevbestillingReferanse(val referanse: UUID)
3 changes: 3 additions & 0 deletions app/src/main/kotlin/no/nav/aap/brev/domene/Brevinnhold.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package no.nav.aap.brev.domene

data class Brevinnhold(val innhold: String)
6 changes: 6 additions & 0 deletions app/src/main/kotlin/no/nav/aap/brev/domene/Brevtype.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package no.nav.aap.brev.domene

enum class Brevtype {
INNVILGELSE,
AVSLAG,
}
5 changes: 5 additions & 0 deletions app/src/main/kotlin/no/nav/aap/brev/domene/Språk.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package no.nav.aap.brev.domene

enum class Språk {
EN, NB, NN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package no.nav.aap.brev.innhold

import no.nav.aap.brev.domene.Brevinnhold
import no.nav.aap.brev.domene.Brevtype
import no.nav.aap.brev.domene.Språk

interface BrevinnholdGateway {
fun hentBrev(brevtype: Brevtype, språk: Språk): Brevinnhold
}
21 changes: 21 additions & 0 deletions app/src/main/kotlin/no/nav/aap/brev/innhold/BrevinnholdService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package no.nav.aap.brev.innhold

import no.nav.aap.brev.domene.BehandlingReferanse
import no.nav.aap.brev.domene.Brevtype
import no.nav.aap.brev.domene.Språk
import org.slf4j.LoggerFactory

class BrevinnholdService(private val brevinnholdGateway: BrevinnholdGateway) {

private val log = LoggerFactory.getLogger(BrevinnholdService::class.java)

fun behandleBrevbestilling(
behandlingReferanse: BehandlingReferanse,
brevtype: Brevtype,
språk: Språk,
) {
log.info("Henter brevinnhold for behandlingReferanse=$behandlingReferanse brevtype=$brevtype språk=$språk")
val brevinnhold = brevinnholdGateway.hentBrev(brevtype, språk)
log.info("Hentet brevinnhold: ${brevinnhold.innhold}")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package no.nav.aap.brev.innhold

import no.nav.aap.brev.domene.Brevinnhold
import no.nav.aap.brev.domene.Brevtype
import no.nav.aap.brev.domene.Språk
import no.nav.aap.komponenter.config.requiredConfigForKey
import no.nav.aap.komponenter.httpklient.httpclient.ClientConfig
import no.nav.aap.komponenter.httpklient.httpclient.Header
import no.nav.aap.komponenter.httpklient.httpclient.RestClient
import no.nav.aap.komponenter.httpklient.httpclient.request.GetRequest
import no.nav.aap.komponenter.httpklient.httpclient.tokenprovider.azurecc.ClientCredentialsTokenProvider
import no.nav.aap.komponenter.httpklient.json.DefaultJsonMapper
import java.net.URI

class SanityBrevinnholdGateway : BrevinnholdGateway {

private val baseUri = URI.create(requiredConfigForKey("integrasjon.brev_sanity_proxy.url"))
val config = ClientConfig(scope = requiredConfigForKey("integrasjon.brev_sanity_proxy.scope"))
private val client = RestClient.withDefaultResponseHandler(
config = config,
tokenProvider = ClientCredentialsTokenProvider
)

override fun hentBrev(
brevtype: Brevtype,
språk: Språk
): Brevinnhold {
val uri = baseUri.resolve("/api/brev?brevtype=$brevtype&language=$språk")
val httpRequest = GetRequest(
additionalHeaders = listOf(
Header("Accept", "application/json")
)
)

return requireNotNull(client.get(uri = uri, request = httpRequest, mapper = { body, _ ->
DefaultJsonMapper.fromJson(body)
}))
}
}
45 changes: 28 additions & 17 deletions app/src/test/kotlin/no/nav/aap/brev/AppTest.kt
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
package no.nav.aap.brev

import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import no.nav.aap.brev.api.BestillBrevRequest
import no.nav.aap.brev.api.BestillBrevResponse
import no.nav.aap.brev.domene.BehandlingReferanse
import no.nav.aap.brev.domene.Brevtype
import no.nav.aap.brev.domene.Språk
import no.nav.aap.brev.innhold.BrevinnholdService
import no.nav.aap.brev.innhold.SanityBrevinnholdGateway
import no.nav.aap.brev.no.nav.aap.brev.test.Fakes
import no.nav.aap.komponenter.httpklient.httpclient.ClientConfig
import no.nav.aap.komponenter.httpklient.httpclient.RestClient
import no.nav.aap.komponenter.httpklient.httpclient.error.DefaultResponseHandler
import no.nav.aap.komponenter.httpklient.httpclient.request.PostRequest
import no.nav.aap.komponenter.httpklient.httpclient.tokenprovider.azurecc.ClientCredentialsTokenProvider
import no.nav.aap.komponenter.httpklient.json.DefaultJsonMapper
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Test
import java.net.URI
import org.assertj.core.api.Assertions.assertThat
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.net.URI
import java.time.Duration
import java.time.temporal.ChronoUnit
import kotlin.test.assertNotNull

class AppTest {

Expand All @@ -30,7 +35,9 @@ class AppTest {
jdbcUrl = "${postgres.jdbcUrl}&user=${postgres.username}&password=${postgres.password}",
)

val httpClient = HttpClient.newBuilder().build()
val brevinnholdGateway = SanityBrevinnholdGateway()
val brevinnholdService = BrevinnholdService(brevinnholdGateway)

private val restClient = RestClient(
config = ClientConfig(scope = "brev"),
tokenProvider = ClientCredentialsTokenProvider,
Expand All @@ -39,7 +46,10 @@ class AppTest {

// Starter server
private val server = embeddedServer(Netty, port = 8080) {
server(dbConfig = dbConfig)
server(
dbConfig = dbConfig,
brevinnholdService = brevinnholdService,
)
module(fakes)
}.start()

Expand All @@ -53,16 +63,17 @@ class AppTest {
}

@Test
fun `appen er ready`() {
val res = httpClient.send(
HttpRequest.newBuilder(
URI.create("http://localhost:8080/")
.resolve("/actuator/ready"),
).GET()
.build(),
HttpResponse.BodyHandlers.ofString()
fun `bestiller brev`() {
val res: BestillBrevResponse? = restClient.post(
uri = URI.create("http://localhost:8080/").resolve("/api/bestill"),
request = PostRequest(
body = BestillBrevRequest(BehandlingReferanse("123"), Brevtype.INNVILGELSE, Språk.NB)
),
mapper = { body, _ ->
DefaultJsonMapper.fromJson(body)
}
)
assertThat(res.statusCode()).isEqualTo(HttpStatusCode.OK.value)
assertNotNull(res)
}
}

Expand Down
8 changes: 7 additions & 1 deletion app/src/test/kotlin/no/nav/aap/brev/TestApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import io.ktor.server.application.Application
import io.ktor.server.application.ApplicationStopped
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import no.nav.aap.brev.innhold.BrevinnholdService
import no.nav.aap.brev.innhold.SanityBrevinnholdGateway
import no.nav.aap.brev.no.nav.aap.brev.test.Fakes
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy
Expand All @@ -19,11 +21,15 @@ fun main() {
val dbConfig = DbConfig(
jdbcUrl = "${postgres.jdbcUrl}&user=${postgres.username}&password=${postgres.password}",
)
val brevinnholdGateway = SanityBrevinnholdGateway()
val brevinnholdService = BrevinnholdService(brevinnholdGateway)

// Useful for connecting to the test database locally
// jdbc URL contains the host and port and database name.
println("jdbcUrl: ${postgres.jdbcUrl}. Password: ${postgres.password}. Username: ${postgres.username}.")
server(
dbConfig
dbConfig = dbConfig,
brevinnholdService = brevinnholdService,
)
module(fakes)
}.start(wait = true)
Expand Down
31 changes: 30 additions & 1 deletion lib-test/src/main/kotlin/no/nav/aap/brev/test/Fakes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import io.ktor.server.request.receive
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.coroutines.runBlocking
import no.nav.aap.brev.ErrorRespons
import no.nav.aap.brev.api.ErrorRespons
import no.nav.aap.brev.domene.Brevinnhold
import no.nav.aap.brev.test.AZURE_JWKS
import no.nav.aap.brev.test.AzureTokenGen
import org.slf4j.Logger
Expand All @@ -24,6 +25,7 @@ class Fakes(azurePort: Int = 0) : AutoCloseable {
private val azure = embeddedServer(Netty, port = azurePort, module = { azureFake() }).start()
private val behandlingsflyt = embeddedServer(Netty, port = 0, module = { behandlingsflytFake() }).apply { start() }
private val tilgang = embeddedServer(Netty, port = 0, module = { tilgangFake() }).apply { start() }
private val brevSanityProxy = embeddedServer(Netty, port = 0, module = { brevSanityProxyFake() }).apply { start() }

init {
Thread.currentThread().setUncaughtExceptionHandler { _, e -> log.error("Uhåndtert feil", e) }
Expand All @@ -43,12 +45,18 @@ class Fakes(azurePort: Int = 0) : AutoCloseable {
System.setProperty("integrasjon.tilgang.url", "http://localhost:${tilgang.port()}")
System.setProperty("integrasjon.tilgang.scope", "scope")
System.setProperty("integrasjon.tilgang.azp", "azp")

// Brev sanity proxy
System.setProperty("integrasjon.brev_sanity_proxy.url", "http://localhost:${brevSanityProxy.port()}")
System.setProperty("integrasjon.brev_sanity_proxy.scope", "scope")
System.setProperty("integrasjon.brev_sanity_proxy.azp", "azp")
}

override fun close() {
azure.stop(0L, 0L)
behandlingsflyt.stop(0L, 0L)
tilgang.stop(0L, 0L)
brevSanityProxy.stop(0L, 0L)
}

private fun NettyApplicationEngine.port(): Int =
Expand Down Expand Up @@ -117,6 +125,27 @@ class Fakes(azurePort: Int = 0) : AutoCloseable {
}
}

fun Application.brevSanityProxyFake() {
install(ContentNegotiation) {
jackson()
}
install(StatusPages) {
exception<Throwable> { call, cause ->
this@brevSanityProxyFake.log.info(
"BREV_SANITY_PROXY :: Ukjent feil ved kall til '{}'",
call.request.local.uri,
cause
)
call.respond(status = HttpStatusCode.InternalServerError, message = ErrorRespons(cause.message))
}
}
routing {
get("/api/brev") {
call.respond(Brevinnhold("brevinnhold"))
}
}
}


internal data class TestToken(
val access_token: String,
Expand Down

0 comments on commit fc81dea

Please sign in to comment.