Skip to content

Commit

Permalink
IS-2577: Add pdlClient to get name in api for journalforing
Browse files Browse the repository at this point in the history
  • Loading branch information
eirikdahlen committed Aug 8, 2024
1 parent 50ae3f9 commit 06f0098
Show file tree
Hide file tree
Showing 15 changed files with 381 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .nais/naiserator-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ spec:
value: "dev-gcp.teamsykefravr.istilgangskontroll"
- name: ISTILGANGSKONTROLL_URL
value: "http://istilgangskontroll"
- name: PDL_CLIENT_ID
value: "dev-fss.pdl.pdl-api"
- name: PDL_URL
value: "https://pdl-api.dev-fss-pub.nais.io/graphql"
4 changes: 4 additions & 0 deletions .nais/naiserator-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ spec:
value: "prod-gcp.teamsykefravr.istilgangskontroll"
- name: ISTILGANGSKONTROLL_URL
value: "http://istilgangskontroll"
- name: PDL_CLIENT_ID
value: "prod-fss.pdl.pdl-api"
- name: PDL_URL
value: "https://pdl-api.prod-fss-pub.nais.io/graphql"
4 changes: 4 additions & 0 deletions src/main/kotlin/no/nav/syfo/ApplicationEnvironment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ data class Environment(
baseUrl = getEnvVar("ISTILGANGSKONTROLL_URL"),
clientId = getEnvVar("ISTILGANGSKONTROLL_CLIENT_ID")
),
pdl = ClientEnvironment(
baseUrl = getEnvVar("PDL_URL"),
clientId = getEnvVar("PDL_CLIENT_ID")
),
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package no.nav.syfo.infrastructure.clients

data class ClientsEnvironment(
val istilgangskontroll: ClientEnvironment,
val pdl: ClientEnvironment,
)

data class ClientEnvironment(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package no.nav.syfo.infrastructure.clients.pdl

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.micrometer.core.instrument.Counter
import no.nav.syfo.domain.Personident
import no.nav.syfo.infrastructure.clients.ClientEnvironment
import no.nav.syfo.infrastructure.clients.azuread.AzureAdClient
import no.nav.syfo.infrastructure.bearerHeader
import no.nav.syfo.infrastructure.clients.pdl.dto.*
import no.nav.syfo.infrastructure.clients.httpClientDefault
import no.nav.syfo.infrastructure.metric.METRICS_NS
import no.nav.syfo.infrastructure.metric.METRICS_REGISTRY
import org.slf4j.LoggerFactory

class PdlClient(
private val azureAdClient: AzureAdClient,
private val pdlEnvironment: ClientEnvironment,
private val httpClient: HttpClient = httpClientDefault(),
) {

suspend fun getPerson(personident: Personident): PdlPerson {
val token = azureAdClient.getSystemToken(pdlEnvironment.clientId)
?: throw RuntimeException("Failed to send request to PDL: No token was found")
val request = PdlHentPersonRequest(getPdlQuery(), PdlHentPersonRequestVariables(personident.value))

val response: HttpResponse = httpClient.post(pdlEnvironment.baseUrl) {
setBody(request)
header(HttpHeaders.ContentType, "application/json")
header(HttpHeaders.Authorization, bearerHeader(token.accessToken))
header(BEHANDLINGSNUMMER_HEADER_KEY, BEHANDLINGSNUMMER_HEADER_VALUE)
}

val person = when (response.status) {
HttpStatusCode.OK -> {
val pdlPersonReponse = response.body<PdlPersonResponse>()
if (!pdlPersonReponse.errors.isNullOrEmpty()) {
Metrics.COUNT_CALL_PDL_PERSON_FAIL.increment()
pdlPersonReponse.errors.forEach {
logger.error("Error while requesting person from PersonDataLosningen: ${it.errorMessage()}")
}
null
} else {
Metrics.COUNT_CALL_PDL_PERSON_SUCCESS.increment()
pdlPersonReponse.data?.hentPerson
}
}

else -> {
Metrics.COUNT_CALL_PDL_PERSON_FAIL.increment()
logger.error("Request with url: ${pdlEnvironment.baseUrl} failed with reponse code ${response.status.value}")
null
}
}

return person ?: throw RuntimeException("PDL did not return a person for given fnr")
}

private fun getPdlQuery(): String =
this::class.java.getResource(PDL_QUERY_PATH)!!
.readText()
.replace("[\n\r]", "")

companion object {
private const val PDL_QUERY_PATH = "/pdl/hentPerson.graphql"

// Se behandlingskatalog https://behandlingskatalog.intern.nav.no/
// Behandling: Sykefraværsoppfølging: Vurdere behov for oppfølging og rett til sykepenger etter §§ 8-4 og 8-8
private const val BEHANDLINGSNUMMER_HEADER_KEY = "behandlingsnummer"
private const val BEHANDLINGSNUMMER_HEADER_VALUE = "B426"

private val logger = LoggerFactory.getLogger(PdlClient::class.java)
}
}

private class Metrics {
companion object {
const val CALL_PDL_PERSON_BASE = "${METRICS_NS}_call_pdl_person"
const val CALL_PDL_PERSON_SUCCESS = "${CALL_PDL_PERSON_BASE}_success_count"
const val CALL_PDL_PERSON_FAIL = "${CALL_PDL_PERSON_BASE}_fail_count"

val COUNT_CALL_PDL_PERSON_SUCCESS: Counter = Counter.builder(CALL_PDL_PERSON_SUCCESS)
.description("Counts the number of successful calls to pdl - person")
.register(METRICS_REGISTRY)
val COUNT_CALL_PDL_PERSON_FAIL: Counter = Counter.builder(CALL_PDL_PERSON_FAIL)
.description("Counts the number of failed calls to pdl - person")
.register(METRICS_REGISTRY)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package no.nav.syfo.infrastructure.clients.pdl.dto

data class PdlError(
val message: String,
val locations: List<PdlErrorLocation>,
val path: List<String>?,
val extensions: PdlErrorExtension,
)

data class PdlErrorLocation(
val line: Int?,
val column: Int?,
)

data class PdlErrorExtension(
val code: String?,
val classification: String,
)

fun PdlError.errorMessage(): String {
return "${this.message} with code: ${extensions.code} and classification: ${extensions.classification}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package no.nav.syfo.infrastructure.clients.pdl.dto

data class PdlHentPersonRequest(
val query: String,
val variables: PdlHentPersonRequestVariables
)

data class PdlHentPersonRequestVariables(
val ident: String,
val navnHistorikk: Boolean = false
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package no.nav.syfo.infrastructure.clients.pdl.dto

import java.util.*

data class PdlPersonResponse(
val errors: List<PdlError>?,
val data: PdlHentPerson?
)

data class PdlHentPerson(
val hentPerson: PdlPerson?
)

data class PdlPerson(
val navn: List<PdlPersonNavn>,
) {
val fullName: String = navn.firstOrNull()?.fullName()
?: throw RuntimeException("PDL returned empty navn for given fnr")
}

data class PdlPersonNavn(
val fornavn: String,
val mellomnavn: String?,
val etternavn: String
) {
fun fullName(): String {
val fornavn = fornavn.lowerCapitalize()
val etternavn = etternavn.lowerCapitalize()

return if (mellomnavn.isNullOrBlank()) {
"$fornavn $etternavn"
} else {
"$fornavn ${mellomnavn.lowerCapitalize()} $etternavn"
}
}
}

fun String.lowerCapitalize() =
this.split(" ").joinToString(" ") { name ->
val nameWithDash = name.split("-")
if (nameWithDash.size > 1) {
nameWithDash.joinToString("-") { it.capitalizeName() }
} else {
name.capitalizeName()
}
}

private fun String.capitalizeName() =
this.lowercase(Locale.getDefault()).replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
}
15 changes: 15 additions & 0 deletions src/main/resources/pdl/hentPerson.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
query($ident: ID!, $navnHistorikk: Boolean!){
hentPerson(ident: $ident) {
navn(historikk: $navnHistorikk) {
fornavn
mellomnavn
etternavn
forkortetNavn
originaltNavn {
fornavn
mellomnavn
etternavn
}
}
}
}
4 changes: 4 additions & 0 deletions src/test/kotlin/no/nav/syfo/TestEnvironment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ fun testEnvironment() = Environment(
baseUrl = "isTilgangskontrollUrl",
clientId = "dev-gcp.teamsykefravr.istilgangskontroll",
),
pdl = ClientEnvironment(
baseUrl = "pdlUrl",
clientId = "pdlClientId",
),
),
electorPath = "electorPath",
)
Expand Down
10 changes: 10 additions & 0 deletions src/test/kotlin/no/nav/syfo/UserConstants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,15 @@ import no.nav.syfo.domain.Personident
object UserConstants {
val ARBEIDSTAKER_PERSONIDENT = Personident("12345678910")
val ARBEIDSTAKER_PERSONIDENT_VEILEDER_NO_ACCESS = Personident("11111111111")
val ARBEIDSTAKER_PERSONIDENT_NAME_WITH_DASH = Personident("11111111234")
val ARBEIDSTAKER_PERSONIDENT_NO_NAME = Personident("11111111222")
val ARBEIDSTAKER_PERSONIDENT_PDL_FAILS = Personident("11111111666")
const val VEILEDER_IDENT = "Z999999"

const val PERSON_FORNAVN = "Fornavn"
const val PERSON_MELLOMNAVN = "Mellomnavn"
const val PERSON_ETTERNAVN = "Etternavnesen"
const val PERSON_FULLNAME = "Fornavn Mellomnavn Etternavnesen"
const val PERSON_FORNAVN_DASH = "For-Navn"
const val PERSON_FULLNAME_DASH = "For-Navn Mellomnavn Etternavnesen"
}
35 changes: 35 additions & 0 deletions src/test/kotlin/no/nav/syfo/generator/PdlGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package no.nav.syfo.generator

import no.nav.syfo.UserConstants
import no.nav.syfo.infrastructure.clients.pdl.dto.*

fun generatePdlPersonResponse(pdlPersonNavn: PdlPersonNavn? = null, errors: List<PdlError>? = null) = PdlPersonResponse(
errors = errors,
data = generatePdlHentPerson(pdlPersonNavn)
)

fun generatePdlPersonNavn(): PdlPersonNavn = PdlPersonNavn(
fornavn = UserConstants.PERSON_FORNAVN,
mellomnavn = UserConstants.PERSON_MELLOMNAVN,
etternavn = UserConstants.PERSON_ETTERNAVN,
)

fun generatePdlHentPerson(
pdlPersonNavn: PdlPersonNavn?,
): PdlHentPerson = PdlHentPerson(
hentPerson = PdlPerson(
navn = if (pdlPersonNavn != null) listOf(pdlPersonNavn) else emptyList(),
)
)

fun generatePdlError() = listOf(
PdlError(
message = "Error in PDL",
locations = emptyList(),
path = null,
extensions = PdlErrorExtension(
code = null,
classification = "",
)
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fun mockHttpClient(environment: Environment) = HttpClient(MockEngine) {
requestUrl.startsWith("/${environment.clients.istilgangskontroll.baseUrl}") -> tilgangskontrollResponse(
request
)
requestUrl.startsWith("/${environment.clients.pdl.baseUrl}") -> pdlMockResponse(request)
else -> error("Unhandled ${request.url.encodedPath}")
}
}
Expand Down
29 changes: 29 additions & 0 deletions src/test/kotlin/no/nav/syfo/infrastructure/mock/PdlMock.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package no.nav.syfo.infrastructure.mock

import io.ktor.client.engine.mock.*
import io.ktor.client.request.*
import no.nav.syfo.UserConstants
import no.nav.syfo.domain.Personident
import no.nav.syfo.generator.generatePdlError
import no.nav.syfo.generator.generatePdlPersonNavn
import no.nav.syfo.generator.generatePdlPersonResponse
import no.nav.syfo.infrastructure.clients.pdl.dto.PdlHentPersonRequest
import no.nav.syfo.infrastructure.clients.pdl.dto.PdlPersonNavn

suspend fun MockRequestHandleScope.pdlMockResponse(request: HttpRequestData): HttpResponseData {
val pdlRequest = request.receiveBody<PdlHentPersonRequest>()
return when (Personident(pdlRequest.variables.ident)) {
UserConstants.ARBEIDSTAKER_PERSONIDENT_NO_NAME -> respond(generatePdlPersonResponse(pdlPersonNavn = null))
UserConstants.ARBEIDSTAKER_PERSONIDENT_NAME_WITH_DASH -> respond(
generatePdlPersonResponse(
PdlPersonNavn(
fornavn = UserConstants.PERSON_FORNAVN_DASH,
mellomnavn = UserConstants.PERSON_MELLOMNAVN,
etternavn = UserConstants.PERSON_ETTERNAVN,
)
)
)
UserConstants.ARBEIDSTAKER_PERSONIDENT_PDL_FAILS -> respond(generatePdlPersonResponse(errors = generatePdlError()))
else -> respond(generatePdlPersonResponse(generatePdlPersonNavn()))
}
}
Loading

0 comments on commit 06f0098

Please sign in to comment.