Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

IS-2577: Add pdlClient to get name in api for journalforing #8

Merged
merged 1 commit into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Må legge til en "rule" ovenfor under accessPolicy.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ja, den lå der visst fra en tidligere commit, helt sikkert noe vi kopierte over da vi lagde repoet bare 👍🏼

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"
Comment on lines +70 to +73
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Har dobbeltsjekket at dette er riktig behandling for denne casen også 👍🏼


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