diff --git a/apps/api-start-stopp-perioder/build.gradle.kts b/apps/api-start-stopp-perioder/build.gradle.kts index 53c7fd3d..1cdcd08a 100644 --- a/apps/api-start-stopp-perioder/build.gradle.kts +++ b/apps/api-start-stopp-perioder/build.gradle.kts @@ -1,6 +1,4 @@ -import org.gradle.configurationcache.extensions.capitalized import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.openapitools.generator.gradle.plugin.tasks.GenerateTask plugins { @@ -23,6 +21,7 @@ dependencies { implementation(project(":lib:kafka")) implementation(project(":lib:hoplite-config")) implementation(project(":lib:kafka-key-generator-client")) + implementation(project(":lib:pdl-client")) implementation(project(":domain:arbeidssoeker-regler")) implementation(libs.bundles.ktorServerWithNettyAndMicrometer) implementation(libs.micrometer.registryPrometheus) @@ -36,7 +35,6 @@ dependencies { implementation(libs.nav.common.auditLog) implementation(libs.nav.common.log) implementation(libs.poao.tilgangClient) - implementation(libs.paw.pdl.client) implementation(libs.logbackClassic) implementation(libs.logstashLogbackEncoder) implementation(libs.kafka.clients) @@ -93,8 +91,8 @@ jib { environment = mapOf( "IMAGE_WITH_VERSION" to "${image ?: project.name}:${project.version}", "OTEL_INSTRUMENTATION_METHODS_INCLUDE" to ("io.ktor.server.routing.Routing[interceptor,executeResult];" + - "io.ktor.server.netty.NettyApplicationCallHandler[handleRequest,exceptionCaught];") + - "io.ktor.serialization.jackson.JacksonConverter[deserialize,serializeNullable]" + "io.ktor.server.netty.NettyApplicationCallHandler[handleRequest,exceptionCaught];") + + "io.ktor.serialization.jackson.JacksonConverter[deserialize,serializeNullable]" ) jvmFlags = listOf("-XX:ActiveProcessorCount=4", "-XX:+UseZGC", "-XX:+ZGenerational") } @@ -107,7 +105,7 @@ mapOf( "${layout.projectDirectory}/src/main/resources/openapi/opplysninger.yaml" to "${generatedCodePackageName}.opplysningermottatt", "${layout.projectDirectory}/src/main/resources/openapi/startstopp.yaml" to "${generatedCodePackageName}.startstopp" ).map { (openApiDocFile, pkgName) -> - val taskName = "generate${pkgName.capitalized()}" + val taskName = "generate${pkgName.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }}" tasks.register(taskName, GenerateTask::class) { generatorName.set("kotlin-server") library = "ktor" diff --git a/apps/bekreftelse-api/build.gradle.kts b/apps/bekreftelse-api/build.gradle.kts index d9bfb148..5167a589 100644 --- a/apps/bekreftelse-api/build.gradle.kts +++ b/apps/bekreftelse-api/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { // Serialization implementation(libs.ktor.serialization.jackson) - implementation(libs.ktor.serialization.json) + implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.jackson.datatypeJsr310) // Authentication diff --git a/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/model/AccessToken.kt b/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/model/AccessToken.kt index 1c0cbc23..21e4d0cf 100644 --- a/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/model/AccessToken.kt +++ b/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/model/AccessToken.kt @@ -1,9 +1,6 @@ package no.nav.paw.bekreftelse.api.model import no.nav.paw.bekreftelse.api.exception.UgyldigBearerTokenException -import no.nav.paw.bekreftelse.api.model.ResolveToken.AzureToken -import no.nav.paw.bekreftelse.api.model.ResolveToken.IdPortenToken -import no.nav.paw.bekreftelse.api.model.ResolveToken.TokenXToken import no.nav.security.token.support.core.context.TokenValidationContext import no.nav.security.token.support.core.jwt.JwtToken import java.util.* @@ -39,14 +36,11 @@ data object OID : Claim("oid", UUID::fromString) data object Name : Claim("name", { it }) data object NavIdent : Claim("NAVident", { it }) -private sealed class ResolveToken( - val issuer: Issuer, - val claims: List> -) { - data object IdPortenToken : ResolveToken(IdPorten, listOf(PID)) - data object TokenXToken : ResolveToken(TokenX, listOf(PID)) - data object AzureToken : ResolveToken(Azure, listOf(OID, Name, NavIdent)) -} +sealed class ResolveToken(val issuer: Issuer, val claims: List>) + +data object IdPortenToken : ResolveToken(IdPorten, listOf(PID)) +data object TokenXToken : ResolveToken(TokenX, listOf(PID)) +data object AzureToken : ResolveToken(Azure, listOf(OID, Name, NavIdent)) private val validTokens: List = listOf(IdPortenToken, TokenXToken, AzureToken) diff --git a/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/plugins/custom/KafkaConsumerPlugin.kt b/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/plugins/custom/KafkaConsumerPlugin.kt index 6e7c9583..499f33e7 100644 --- a/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/plugins/custom/KafkaConsumerPlugin.kt +++ b/apps/bekreftelse-api/src/main/kotlin/no/nav/paw/bekreftelse/api/plugins/custom/KafkaConsumerPlugin.kt @@ -64,6 +64,7 @@ val KafkaConsumerPlugin: ApplicationPlugin = application.log.info("Stopper Kafka Consumer") pollingFlag.set(false) consumeJob?.cancel() + consumer.unsubscribe() consumer.close(shutDownTimeout) } diff --git a/apps/bekreftelse-tjeneste/build.gradle.kts b/apps/bekreftelse-tjeneste/build.gradle.kts index 46209f64..2df5503c 100644 --- a/apps/bekreftelse-tjeneste/build.gradle.kts +++ b/apps/bekreftelse-tjeneste/build.gradle.kts @@ -26,7 +26,7 @@ dependencies { // Serialization implementation(libs.ktor.serialization.jackson) - implementation(libs.ktor.serialization.json) + implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.jackson.datatypeJsr310) // Tooling diff --git a/apps/bekreftelse-utgang/build.gradle.kts b/apps/bekreftelse-utgang/build.gradle.kts index 184dbf02..17e810f8 100644 --- a/apps/bekreftelse-utgang/build.gradle.kts +++ b/apps/bekreftelse-utgang/build.gradle.kts @@ -25,7 +25,7 @@ dependencies { // Serialization implementation(libs.ktor.serialization.jackson) - implementation(libs.ktor.serialization.json) + implementation(libs.ktor.serialization.kotlinx.json) implementation(libs.jackson.datatypeJsr310) // Tooling diff --git a/apps/hendelselogg-backup/build.gradle.kts b/apps/hendelselogg-backup/build.gradle.kts index 5c07e06a..9dafa297 100644 --- a/apps/hendelselogg-backup/build.gradle.kts +++ b/apps/hendelselogg-backup/build.gradle.kts @@ -1,4 +1,3 @@ -import org.gradle.configurationcache.extensions.capitalized import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.openapitools.generator.gradle.plugin.tasks.GenerateTask @@ -107,7 +106,7 @@ val generatedCodeOutputDir = "${layout.buildDirectory.get()}/generated/" mapOf( "${layout.projectDirectory}/src/main/resources/openapi/Brukerstoette.yaml" to "${generatedCodePackageName}.brukerstoette" ).map { (openApiDocFile, pkgName) -> - val taskName = "generate${pkgName.capitalized()}" + val taskName = "generate${pkgName.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }}" tasks.register(taskName, GenerateTask::class) { generatorName.set("kotlin-server") library = "ktor" @@ -142,7 +141,7 @@ mapOf( mapOf( "${layout.projectDirectory}/src/main/resources/openapi/oppslags-api.yaml" to "${generatedCodePackageName}.oppslagsapi" ).map { (openApiDocFile, pkgName) -> - val taskName = "generate${pkgName.capitalized()}" + val taskName = "generate${pkgName.replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }}" tasks.register(taskName, GenerateTask::class) { generatorName.set("kotlin") library = "jvm-ktor" diff --git a/apps/kafka-key-generator/build.gradle.kts b/apps/kafka-key-generator/build.gradle.kts index c72fccaa..1b8cd089 100644 --- a/apps/kafka-key-generator/build.gradle.kts +++ b/apps/kafka-key-generator/build.gradle.kts @@ -13,7 +13,7 @@ val jvmMajorVersion: String by project dependencies { // PAW implementation(project(":lib:hoplite-config")) - implementation(libs.paw.pdl.client) + implementation(project(":lib:pdl-client")) // NAV implementation(libs.nav.common.log) diff --git a/apps/utgang-pdl/build.gradle.kts b/apps/utgang-pdl/build.gradle.kts index 43849d55..df0a953a 100644 --- a/apps/utgang-pdl/build.gradle.kts +++ b/apps/utgang-pdl/build.gradle.kts @@ -20,15 +20,15 @@ dependencies { implementation(project(":domain:arbeidssoekerregisteret-kotlin")) implementation(project(":domain:arbeidssoeker-regler")) - implementation(project(":lib:kafka-key-generator-client")) + implementation(project(":lib:hoplite-config")) implementation(project(":lib:kafka")) implementation(project(":lib:kafka-streams")) - implementation(project(":lib:hoplite-config")) + implementation(project(":lib:kafka-key-generator-client")) + implementation(project(":lib:pdl-client")) api(libs.arrow.core.core) implementation(libs.kafka.streams.core) - implementation(libs.paw.pdl.client) implementation(libs.bundles.ktorServerWithNettyAndMicrometer) implementation(libs.micrometer.registryPrometheus) diff --git a/domain/arbeidssoeker-regler/build.gradle.kts b/domain/arbeidssoeker-regler/build.gradle.kts index ff3acf54..d2ae55f6 100644 --- a/domain/arbeidssoeker-regler/build.gradle.kts +++ b/domain/arbeidssoeker-regler/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { kotlin("jvm") id("org.openapi.generator") @@ -11,8 +9,8 @@ val baseImage: String by project val jvmMajorVersion: String by project dependencies { + implementation(project(":lib:pdl-client")) implementation(project(":domain:interne-hendelser")) - implementation(libs.paw.pdl.client) api(libs.micrometer.registryPrometheus) api(libs.arrow.core.core) testImplementation(libs.ktor.server.testJvm) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index de4edb13..6ab64a9b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,13 +1,12 @@ [versions] -pawPdlClientVersion = "24.10.18.41-1" -pawAaregClientVersion = "24.07.04.18-1" +noNavSecurityVersion = "5.0.5" +noNavCommonVersion = "3.2024.05.23_05.46-2b29fa343e8e" arbeidssokerregisteretVersion = "1.9348086045.48-1" bekreftelseSchemaVersion = "24.10.21.12-1" arrowVersion = "1.2.4" arrowJacksonIntegrationVersion = "0.14.1" -noNavCommonVersion = "3.2024.05.23_05.46-2b29fa343e8e" -noNavSecurityVersion = "5.0.5" comSksamuelHopliteVersion = "2.8.2" +graphqlClientVersion = "7.1.1" orgApacheKafkaVersion = "3.7.0" ioConfluentKafkaVersion = "7.7.1" orgApacheAvroVersion = "1.12.0" @@ -27,6 +26,8 @@ coroutinesVersion = "1.9.0" postgresDriverVersion = "42.7.4" flywayVersion = "10.20.0" hikariVersion = "6.0.0" +kotlinxCoroutinesVersion = "1.8.1" +kotlinxSerializationJsonVersion = "1.7.3" ktorVersion = "2.3.12" [libraries] @@ -36,6 +37,8 @@ arrow-core-serialization = { group = "io.arrow-kt", name = "arrow-core-serializa arrow-integration-jackson = { group = "io.arrow-kt", name = "arrow-integrations-jackson-module", version.ref = "arrowJacksonIntegrationVersion" } logbackClassic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logbackVersion" } logstashLogbackEncoder = { group = "net.logstash.logback", name = "logstash-logback-encoder", version.ref = "logstashVersion" } +kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesVersion" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJsonVersion" } ktor-client-contentNegotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktorVersion" } ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktorVersion" } ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktorVersion" } @@ -57,14 +60,12 @@ ktor-server-testJvm = { group = "io.ktor", name = "ktor-server-tests-jvm", versi ktor-serialization-core = { group = "io.ktor", name = "ktor-serialization", version.ref = "ktorVersion" } ktor-serialization-jvm = { group = "io.ktor", name = "ktor-serialization-jvm", version.ref = "ktorVersion" } ktor-serialization-jackson = { group = "io.ktor", name = "ktor-serialization-jackson", version.ref = "ktorVersion" } -ktor-serialization-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktorVersion" } +ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktorVersion" } opentelemetry-api = { group = "io.opentelemetry", name = "opentelemetry-api", version.ref = "otelTargetSdkVersion" } opentelemetry-ktor = { group = "io.opentelemetry.instrumentation", name = "opentelemetry-ktor-2.0", version.ref = "otelInstrumentationKtorVersion" } opentelemetry-annotations = { group = "io.opentelemetry.instrumentation", name = "opentelemetry-instrumentation-annotations", version.ref = "otelInstrumentationVersion" } micrometerCore = { group = "io.micrometer", name = "micrometer-core", version.ref = "micrometerVersion" } micrometer-registryPrometheus = { group = "io.micrometer", name = "micrometer-registry-prometheus", version.ref = "micrometerVersion" } -paw-pdl-client = { group = "no.nav.paw", name = "pdl-client", version.ref = "pawPdlClientVersion" } -paw-aareg-client = { group = "no.nav.paw", name = "aareg-client", version.ref = "pawAaregClientVersion" } paw-schema-main = { group = "no.nav.paw.arbeidssokerregisteret.api", name = "main-avro-schema", version.ref = "arbeidssokerregisteretVersion" } kafka-clients = { group = "org.apache.kafka", name = "kafka-clients", version.ref = "orgApacheKafkaVersion" } kafka-streams-core = { group = "org.apache.kafka", name = "kafka-streams", version.ref = "orgApacheKafkaVersion" } @@ -80,6 +81,8 @@ nav-common-log = { group = "no.nav.common", name = "log", version.ref = "noNavCo nav-common-auditLog = { group = "no.nav.common", name = "audit-log", version.ref = "noNavCommonVersion" } nav-security-tokenValidationKtorV2 = { group = "no.nav.security", name = "token-validation-ktor-v2", version.ref = "noNavSecurityVersion" } nav-security-tokenClientCore = { group = "no.nav.security", name = "token-client-core", version.ref = "noNavSecurityVersion" } +graphql-client = { group = "com.expediagroup", name = "graphql-kotlin-client", version.ref = "graphqlClientVersion" } +graphql-ktor-client = { group = "com.expediagroup", name = "graphql-kotlin-ktor-client", version.ref = "graphqlClientVersion" } hoplite-core = { group = "com.sksamuel.hoplite", name = "hoplite-core", version.ref = "comSksamuelHopliteVersion" } hoplite-toml = { group = "com.sksamuel.hoplite", name = "hoplite-toml", version.ref = "comSksamuelHopliteVersion" } hoplite-yaml = { group = "com.sksamuel.hoplite", name = "hoplite-yaml", version.ref = "comSksamuelHopliteVersion" } diff --git a/lib/aareg-client/README.md b/lib/aareg-client/README.md new file mode 100644 index 00000000..0edf2934 --- /dev/null +++ b/lib/aareg-client/README.md @@ -0,0 +1,41 @@ +# paw-aareg-client + +Henter arbeidsforhold fra Arbeidsgiver- og arbeidstakerregisteret ([aareg](https://navikt.github.io/aareg/)). + +Se URL-er på https://github.com/navikt/aareg-services + +Se dokumentasjon for aareg på https://aareg-services.dev.intern.nav.no/swagger-ui/index.html + +### Bruk av paw-aareg-client + +**_gradle.build.kts_** + +```kts +val tokenproviderVersion: String by project +val aaregClientVersion: String by project + +dependencies { + implementation("no.nav.paw:tokenprovider:$tokenproviderVersion") + implementation("no.nav.paw:aareg-client:$aaregClientVersion") +} +``` + +### Klienten instansieres slik + +```kt +import kotlinx.coroutines.runBlocking +import no.nav.paw.tokenprovider.OAuth2TokenProvider +import no.nav.paw.aareg.AaregClient + +fun main() { + val url = "https://modapp-q1.adeo.no/aareg-services" + val tokenProvider = OAuth2TokenProvider( + // Token config + ) + + val aaregClient = AaregClient(url) { tokenProvider.getToken() } + + val arbeidsforhold = runBlocking { aaregClient.hentArbeidsforhold("fnr", "callId") } + println(arbeidsforhold) +} +``` diff --git a/lib/aareg-client/build.gradle.kts b/lib/aareg-client/build.gradle.kts new file mode 100644 index 00000000..69f076e3 --- /dev/null +++ b/lib/aareg-client/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + kotlin("jvm") + kotlin("plugin.serialization") +} + +val jvmMajorVersion: String by project + +dependencies { + api(libs.kotlinx.serialization.json) + + implementation(libs.ktor.client.contentNegotiation) + implementation(libs.ktor.client.okhttp) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.ktor.client.logging) + + testImplementation(libs.bundles.testLibsWithUnitTesting) + testImplementation(libs.ktor.client.mock) +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(jvmMajorVersion)) + } +} + +tasks.withType().configureEach { + useJUnitPlatform() +} diff --git a/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/AaregClient.kt b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/AaregClient.kt new file mode 100644 index 00000000..7ec7a162 --- /dev/null +++ b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/AaregClient.kt @@ -0,0 +1,53 @@ +package no.nav.paw.aareg + +import io.ktor.client.call.body +import io.ktor.client.plugins.ResponseException +import io.ktor.client.request.bearerAuth +import io.ktor.client.request.get +import io.ktor.client.request.header +import io.ktor.client.statement.bodyAsText +import io.ktor.http.ContentType +import io.ktor.http.contentType +import io.ktor.serialization.JsonConvertException +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.net.ConnectException + +/** + * klient for å hente ut aktive arbeidsforhold på en person + */ + +class AaregClient( + private val url: String, + private val getAccessToken: () -> String +) { + private val sikkerLogger: Logger = LoggerFactory.getLogger("tjenestekall") + private val logger: Logger = LoggerFactory.getLogger("paw-aareg-client") + private val httpClient = createHttpClient() + + suspend fun hentArbeidsforhold(ident: String, callId: String): List { + val token = getAccessToken() + try { + val payload = httpClient.get(url) { + contentType(ContentType.Application.Json) + bearerAuth(token) + header("Nav-Call-Id", callId) + header("Nav-Personident", ident) + }.also { + logger.info("Hentet arbeidsforhold fra aareg med status=${it.status}") + sikkerLogger.debug("Svar fra aareg-API: " + it.bodyAsText()) + }.body>() + return payload + } catch (responseException: ResponseException) { + logger.error("Hente arbeidsforhold callId=[$callId] feilet med http-kode ${responseException.response.status}") + throw responseException + } catch (connectException: ConnectException) { + logger.error("Hente arbeidsforhold callId=[$callId] feilet:", connectException) + throw connectException + } catch (jsonConvertException: JsonConvertException) { + logger.error("Hente arbeidsforhold callId=[$callId] feilet, kunne ikke lese JSON") + sikkerLogger.error("Hente arbeidsforhold callId=[$callId] feilet", jsonConvertException) + throw jsonConvertException + } + } +} diff --git a/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/Arbeidsforhold.kt b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/Arbeidsforhold.kt new file mode 100644 index 00000000..31ac11db --- /dev/null +++ b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/Arbeidsforhold.kt @@ -0,0 +1,46 @@ +package no.nav.paw.aareg + +import kotlinx.serialization.Serializable +import java.time.LocalDate +import java.time.LocalDateTime + +@Serializable +data class Arbeidsforhold( + val arbeidsgiver: Arbeidsgiver, + val opplysningspliktig: Opplysningspliktig, + val arbeidsavtaler: List, + val ansettelsesperiode: Ansettelsesperiode, + @Serializable(with = LocalDateTimeSerializer::class) + val registrert: LocalDateTime +) + +@Serializable +data class Arbeidsavtale( + val stillingsprosent: Double?, + val gyldighetsperiode: Periode +) + +@Serializable +data class Ansettelsesperiode( + val periode: Periode +) + +@Serializable +data class Arbeidsgiver( + val type: String, + val organisasjonsnummer: String? +) + +@Serializable +data class Periode( + @Serializable(with = LocalDateSerializer::class) + val fom: LocalDate, + @Serializable(with = LocalDateSerializer::class) + val tom: LocalDate? = null +) + +@Serializable +data class Opplysningspliktig( + val type: String, + val organisasjonsnummer: String? +) diff --git a/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/LocalDateSerializer.kt b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/LocalDateSerializer.kt new file mode 100644 index 00000000..b0ef6980 --- /dev/null +++ b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/LocalDateSerializer.kt @@ -0,0 +1,15 @@ +package no.nav.paw.aareg + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.time.LocalDate + +object LocalDateSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: LocalDate) = encoder.encodeString(value.toString()) + override fun deserialize(decoder: Decoder): LocalDate = LocalDate.parse(decoder.decodeString()) +} diff --git a/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/LocalDateTimeSerializer.kt b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/LocalDateTimeSerializer.kt new file mode 100644 index 00000000..f1ee25a3 --- /dev/null +++ b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/LocalDateTimeSerializer.kt @@ -0,0 +1,15 @@ +package no.nav.paw.aareg + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.time.LocalDateTime + +object LocalDateTimeSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: LocalDateTime) = encoder.encodeString(value.toString()) + override fun deserialize(decoder: Decoder): LocalDateTime = LocalDateTime.parse(decoder.decodeString()) +} diff --git a/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/Utils.kt b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/Utils.kt new file mode 100644 index 00000000..22ff109b --- /dev/null +++ b/lib/aareg-client/src/main/kotlin/no/nav/paw/aareg/Utils.kt @@ -0,0 +1,28 @@ +package no.nav.paw.aareg + +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.okhttp.OkHttp +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.serialization.kotlinx.json.json +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json + +internal fun createHttpClient(): HttpClient = + HttpClient(OkHttp) { configureJsonHandler() } + +@OptIn(ExperimentalSerializationApi::class) +internal fun HttpClientConfig<*>.configureJsonHandler() { + install(ContentNegotiation) { + json( + Json { + ignoreUnknownKeys = true + explicitNulls = false + } + ) + } + expectSuccess = true +} + +internal fun String.readResource(): String = + ClassLoader.getSystemResource(this).readText() diff --git a/lib/aareg-client/src/test/kotlin/no/nav/paw/aareg/AaregClientTest.kt b/lib/aareg-client/src/test/kotlin/no/nav/paw/aareg/AaregClientTest.kt new file mode 100644 index 00000000..cab5633d --- /dev/null +++ b/lib/aareg-client/src/test/kotlin/no/nav/paw/aareg/AaregClientTest.kt @@ -0,0 +1,21 @@ +package no.nav.paw.aareg + +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.collections.shouldContain +import kotlinx.coroutines.runBlocking + +class AaregClientTest : FreeSpec({ + + /* + API Description: + https://navikt.github.io/aareg/tjenester/integrasjon/api/ + */ + "Returnerer gyldig objekt når alt er ok" { + val response = runBlocking { + mockAaregClient(MockResponse.arbeidsforhold) + .hentArbeidsforhold("ident", "call-id") + } + + response.map { it.arbeidsgiver.organisasjonsnummer } shouldContain "896929119" + } +}) diff --git a/lib/aareg-client/src/test/kotlin/no/nav/paw/aareg/MockUtils.kt b/lib/aareg-client/src/test/kotlin/no/nav/paw/aareg/MockUtils.kt new file mode 100644 index 00000000..757a12bb --- /dev/null +++ b/lib/aareg-client/src/test/kotlin/no/nav/paw/aareg/MockUtils.kt @@ -0,0 +1,44 @@ +package no.nav.paw.aareg + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headersOf +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import kotlin.reflect.KFunction + +object MockResponse { + val arbeidsforhold = "aareg-arbeidsforhold.json".readResource() + + val error = "error.json".readResource() +} +fun mockAaregClient(content: String, statusCode: HttpStatusCode = HttpStatusCode.OK): AaregClient { + val mockEngine = MockEngine { + respond( + content = content, + status = statusCode, + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString()) + ) + } + + val mockHttpClient = HttpClient(mockEngine) { configureJsonHandler() } + + return mockFn(::createHttpClient) { + every { createHttpClient() } returns mockHttpClient + AaregClient("url") { "fake token" } + } +} + +private fun mockFn(fn: KFunction<*>, block: () -> T): T { + mockkStatic(fn) + return try { + block() + } finally { + unmockkStatic(fn) + } +} diff --git a/lib/aareg-client/src/test/resources/aareg-arbeidsforhold.json b/lib/aareg-client/src/test/resources/aareg-arbeidsforhold.json new file mode 100644 index 00000000..f677ff7f --- /dev/null +++ b/lib/aareg-client/src/test/resources/aareg-arbeidsforhold.json @@ -0,0 +1,164 @@ +[ + { + "navArbeidsforholdId": 51505573, + "arbeidsforholdId": "1", + "arbeidstaker": { + "type": "Person", + "offentligIdent": "22018520056", + "aktoerId": "2884609777831" + }, + "arbeidsgiver": { + "type": "Organisasjon", + "organisasjonsnummer": "896929119" + }, + "opplysningspliktig": { + "type": "Organisasjon", + "organisasjonsnummer": "928497704" + }, + "type": "ordinaertArbeidsforhold", + "ansettelsesperiode": { + "periode": { + "fom": "2001-01-22" + }, + "bruksperiode": { + "fom": "2021-01-22T12:07:35.844" + }, + "sporingsinformasjon": { + "opprettetTidspunkt": "2021-01-22T12:07:35.844", + "opprettetAv": "srvtestnorge-aareg", + "opprettetKilde": "AAREG", + "opprettetKildereferanse": "Dolly 92e7cef8-7097-4cf5-8ffa-b9960d0dd3f8", + "endretTidspunkt": "2021-01-22T12:07:35.844", + "endretAv": "srvtestnorge-aareg", + "endretKilde": "AAREG", + "endretKildereferanse": "Dolly 92e7cef8-7097-4cf5-8ffa-b9960d0dd3f8" + } + }, + "arbeidsavtaler": [ + { + "type": "Ordinaer", + "arbeidstidsordning": "ikkeSkift", + "yrke": "3310101", + "stillingsprosent": 100.0, + "antallTimerPrUke": 37.5, + "beregnetAntallTimerPrUke": 37.5, + "bruksperiode": { + "fom": "2021-01-22T12:07:35.844" + }, + "gyldighetsperiode": { + "fom": "2001-01-01" + }, + "sporingsinformasjon": { + "opprettetTidspunkt": "2021-01-22T12:07:35.844", + "opprettetAv": "srvtestnorge-aareg", + "opprettetKilde": "AAREG", + "opprettetKildereferanse": "Dolly 92e7cef8-7097-4cf5-8ffa-b9960d0dd3f8", + "endretTidspunkt": "2021-01-22T12:07:35.844", + "endretAv": "srvtestnorge-aareg", + "endretKilde": "AAREG", + "endretKildereferanse": "Dolly 92e7cef8-7097-4cf5-8ffa-b9960d0dd3f8" + } + } + ], + "varsler": [ + { + "entitet": "ARBEIDSFORHOLD", + "varslingskode": "NAVEND" + } + ], + "innrapportertEtterAOrdningen": true, + "registrert": "2021-01-22T12:07:35.785", + "sistBekreftet": "2021-01-22T12:07:35", + "sporingsinformasjon": { + "opprettetTidspunkt": "2021-01-22T12:07:35.844", + "opprettetAv": "srvtestnorge-aareg", + "opprettetKilde": "AAREG", + "opprettetKildereferanse": "Dolly 92e7cef8-7097-4cf5-8ffa-b9960d0dd3f8", + "endretTidspunkt": "2021-01-22T12:07:35.844", + "endretAv": "srvtestnorge-aareg", + "endretKilde": "AAREG", + "endretKildereferanse": "Dolly 92e7cef8-7097-4cf5-8ffa-b9960d0dd3f8" + } + }, + { + "navArbeidsforholdId": 51507953, + "arbeidsforholdId": "2", + "arbeidstaker": { + "type": "Person", + "offentligIdent": "22018520056", + "aktoerId": "2884609777831" + }, + "arbeidsgiver": { + "type": "Organisasjon", + "organisasjonsnummer": "896929119" + }, + "opplysningspliktig": { + "type": "Organisasjon", + "organisasjonsnummer": "928497704" + }, + "type": "ordinaertArbeidsforhold", + "ansettelsesperiode": { + "periode": { + "fom": "2001-03-15" + }, + "bruksperiode": { + "fom": "2021-03-15T12:45:29.413" + }, + "sporingsinformasjon": { + "opprettetTidspunkt": "2021-03-15T12:45:29.413", + "opprettetAv": "srvtestnorge-aareg", + "opprettetKilde": "AAREG", + "opprettetKildereferanse": "DOLLY", + "endretTidspunkt": "2021-03-15T12:45:29.413", + "endretAv": "srvtestnorge-aareg", + "endretKilde": "AAREG", + "endretKildereferanse": "DOLLY" + } + }, + "arbeidsavtaler": [ + { + "type": "Ordinaer", + "arbeidstidsordning": "ikkeSkift", + "yrke": "5221126", + "stillingsprosent": 100.0, + "antallTimerPrUke": 37.5, + "beregnetAntallTimerPrUke": 37.5, + "bruksperiode": { + "fom": "2021-03-15T12:45:29.413" + }, + "gyldighetsperiode": { + "fom": "2001-03-01" + }, + "sporingsinformasjon": { + "opprettetTidspunkt": "2021-03-15T12:45:29.413", + "opprettetAv": "srvtestnorge-aareg", + "opprettetKilde": "AAREG", + "opprettetKildereferanse": "DOLLY", + "endretTidspunkt": "2021-03-15T12:45:29.413", + "endretAv": "srvtestnorge-aareg", + "endretKilde": "AAREG", + "endretKildereferanse": "DOLLY" + } + } + ], + "varsler": [ + { + "entitet": "ARBEIDSFORHOLD", + "varslingskode": "NAVEND" + } + ], + "innrapportertEtterAOrdningen": true, + "registrert": "2021-03-15T12:45:29.319", + "sistBekreftet": "2021-03-15T12:45:29", + "sporingsinformasjon": { + "opprettetTidspunkt": "2021-03-15T12:45:29.413", + "opprettetAv": "srvtestnorge-aareg", + "opprettetKilde": "AAREG", + "opprettetKildereferanse": "DOLLY", + "endretTidspunkt": "2021-03-15T12:45:29.413", + "endretAv": "srvtestnorge-aareg", + "endretKilde": "AAREG", + "endretKildereferanse": "DOLLY" + } + } +] \ No newline at end of file diff --git a/lib/aareg-client/src/test/resources/error.json b/lib/aareg-client/src/test/resources/error.json new file mode 100644 index 00000000..55860e82 --- /dev/null +++ b/lib/aareg-client/src/test/resources/error.json @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "En ukjent feil oppstod", + "locations": [], + "extensions": { + "errormessages": { + "code": "AA-000", + "message": "Det oppsto en feil under behandlingen av forespøselen", + "details": null + }, + "classification": "ExecutionAborted" + } + } + ], + "data": { + "finnArbeidsforhold": null + } +} \ No newline at end of file diff --git a/lib/pdl-client/README.md b/lib/pdl-client/README.md new file mode 100644 index 00000000..3db91210 --- /dev/null +++ b/lib/pdl-client/README.md @@ -0,0 +1,74 @@ +# paw-pdl-client + +Klient for å gjøre spørringer mot Persondataløsningen [PDL](https://pdldocs-navno.msappproxy.net/ekstern/index.html). + +### Bruk av paw-pdl-client + +**_gradle.build.kts_** + +```kts +dependencies { + implementation("no.nav.paw.pdl-client:${Versions.pdlClient}") +} +``` + +### Klienten instansieres slik + +For mer informasjon om tema-koder [sjekk her](https://confluence.adeo.no/pages/viewpage.action?pageId=309311397). + +```kt +import io.ktor.client.HttpClient +import io.ktor.client.engine.okhttp.OkHttp +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import no.nav.common.token_client.builder.AzureAdTokenClientBuilder +import no.nav.paw.pdl-client.PdlCLient + +fun main() { + val url = "http://pdl-api.default.svc.nais.local/graphql" + val httpClient = HttpClient(OkHttp) { + install(ContentNegotiation) { + jackson() + } + } + val pdlClient = pdlClient(url, "tema", httpClient) { getAccessToken() } + val result = runBlocking { pdlClient.hentAktorId("fnr") } + println(result) +} + +private val aadMachineToMachineTokenClient = AzureAdTokenClientBuilder.builder() + .withNaisDefaults() + .buildMachineToMachineTokenClient() + +fun getAccessToken(): String { + return aadMachineToMachineTokenClient.createMachineToMachineToken( + "api://${pdlCluster}.$namespace.$appName/.default" + ) +} +``` + +### Lokal utvikling + +For å teste klienten-endringer i en annen applikasjon uten å publisere remote, kjør: + +```sh +./gradlew publishToMavenLocal +``` + +Pakken blir da publisert til lokalt repository, husk at du må legge til `mavenLocal()` i applikasjonen: + +```dsl +repositories { + mavenLocal() + mavenCentral() +} +``` + +### Henvendelser + +Spørsmål knyttet til koden eller prosjektet kan rettes mot: + +- Jonas Enge + +### For NAV-ansatte + +Interne henvendelser kan sendes via Slack i kanalen #team-paw-dev. diff --git a/lib/pdl-client/build.gradle.kts b/lib/pdl-client/build.gradle.kts new file mode 100644 index 00000000..8d8945b3 --- /dev/null +++ b/lib/pdl-client/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + kotlin("jvm") + kotlin("plugin.serialization") + id("com.expediagroup.graphql") +} + +val jvmMajorVersion: String by project + +dependencies { + api(libs.kotlinx.serialization.json) + + implementation(libs.ktor.client.contentNegotiation) + implementation(libs.ktor.client.okhttp) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.ktor.client.logging) + api(libs.graphql.ktor.client) + + testImplementation(libs.bundles.testLibsWithUnitTesting) + testImplementation(libs.ktor.client.mock) + testImplementation(libs.kotlinx.coroutines.core) +} + +java { + withSourcesJar() + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } +} + + +graphql { + client { + packageName = "no.nav.paw.pdl.graphql.generated" + schemaFile = File("src/main/resources/pdl-schema.graphql") + queryFiles = file("src/main/resources").listFiles()?.toList()?.filter { it.name.endsWith(".graphql") }.orEmpty() + serializer = com.expediagroup.graphql.plugin.gradle.config.GraphQLSerializer.KOTLINX + } +} + +tasks.withType().configureEach { + useJUnitPlatform() +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentFoedsel.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentFoedsel.kt new file mode 100644 index 00000000..de22aadd --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentFoedsel.kt @@ -0,0 +1,40 @@ +package no.nav.paw.pdl + +import no.nav.paw.pdl.graphql.generated.HentFoedsel +import no.nav.paw.pdl.graphql.generated.hentfoedsel.Foedsel + +suspend fun PdlClient.hentFoedsel( + ident: String, + callId: String?, + traceparent: String? = null, + navConsumerId: String?, + behandlingsnummer: String, +): Foedsel? { + val query = + HentFoedsel( + HentFoedsel.Variables(ident), + ) + + logger.trace("Henter 'hentFoedsel' fra PDL") + + val respons = + execute( + query = query, + callId = callId, + navConsumerId = navConsumerId, + traceparent = traceparent, + behandlingsnummer = behandlingsnummer, + ) + + respons.errors?.let { + throw PdlException("'hentPerson' feilet", it) + } + + logger.trace("Hentet 'hentFoedsel' fra PDL") + + return respons + .data + ?.hentPerson + ?.foedsel + ?.firstOrNull() +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentForenkletStatus.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentForenkletStatus.kt new file mode 100644 index 00000000..431194ec --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentForenkletStatus.kt @@ -0,0 +1,36 @@ +package no.nav.paw.pdl + +import no.nav.paw.pdl.graphql.generated.HentForenkletStatus +import no.nav.paw.pdl.graphql.generated.hentforenkletstatus.Person + +suspend fun PdlClient.hentForenkletStatus( + ident: String, + callId: String?, + traceparent: String? = null, + navConsumerId: String?, + behandlingsnummer: String, +): Person? { + val query = + HentForenkletStatus( + HentForenkletStatus.Variables(ident), + ) + + val response = + execute( + query = query, + callId = callId, + navConsumerId = navConsumerId, + traceparent = traceparent, + behandlingsnummer = behandlingsnummer, + ) + + response.errors?.let { + throw PdlException("'hentForenkletStatus' feilet", it) + } + + logger.trace("Hentet 'hentForenkletStatus' fra PDL") + + return response + .data + ?.hentPerson +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentForenkletStatusBolk.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentForenkletStatusBolk.kt new file mode 100644 index 00000000..6aa05fb0 --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentForenkletStatusBolk.kt @@ -0,0 +1,36 @@ +package no.nav.paw.pdl + +import no.nav.paw.pdl.graphql.generated.HentForenkletStatusBolk +import no.nav.paw.pdl.graphql.generated.hentforenkletstatusbolk.HentPersonBolkResult + +suspend fun PdlClient.hentForenkletStatusBolk( + ident: List, + callId: String?, + traceparent: String? = null, + navConsumerId: String?, + behandlingsnummer: String, +): List? { + val query = + HentForenkletStatusBolk( + HentForenkletStatusBolk.Variables(ident), + ) + + val response = + execute( + query = query, + callId = callId, + navConsumerId = navConsumerId, + traceparent = traceparent, + behandlingsnummer = behandlingsnummer, + ) + + response.errors?.let { + throw PdlException("'hentForenkletStatusBolk' feilet", it) + } + + logger.trace("Hentet 'hentForenkletStatusBolk' fra PDL") + + return response + .data + ?.hentPersonBolk +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentIdenter.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentIdenter.kt new file mode 100644 index 00000000..18bbb7eb --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentIdenter.kt @@ -0,0 +1,52 @@ +package no.nav.paw.pdl + +import no.nav.paw.pdl.graphql.generated.HentIdenter +import no.nav.paw.pdl.graphql.generated.enums.IdentGruppe +import no.nav.paw.pdl.graphql.generated.hentidenter.IdentInformasjon + +suspend fun PdlClient.hentAktorId( + ident: String, + callId: String?, + navConsumerId: String?, + behandlingsnummer: String, +): String? = + hentIdenter( + ident = ident, + callId = callId, + navConsumerId = navConsumerId, + behandlingsnummer = behandlingsnummer, + ) + ?.firstOrNull { it.gruppe == IdentGruppe.AKTORID } + ?.ident + +suspend fun PdlClient.hentIdenter( + ident: String, + historikk: Boolean = false, + callId: String?, + navConsumerId: String?, + behandlingsnummer: String, +): List? { + val query = HentIdenter(HentIdenter.Variables(ident = ident, historisk = historikk)) + + logger.info("Henter 'hentIdenter' fra PDL") + + val respons = + execute( + query = query, + callId = callId, + navConsumerId = navConsumerId, + behandlingsnummer = behandlingsnummer, + ) + + respons.errors?.let { + logger.error("Henter 'hentIdenter' fra PDL feilet med: ${respons.errors}") + throw PdlException("'hentIdenter' fra pdl feilet", it) + } + + logger.info("Hentet 'hentIdenter' fra PDL") + + return respons + .data + ?.hentIdenter + ?.identer +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentOpphold.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentOpphold.kt new file mode 100644 index 00000000..a553eabb --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentOpphold.kt @@ -0,0 +1,35 @@ +package no.nav.paw.pdl + +import no.nav.paw.pdl.graphql.generated.HentOpphold +import no.nav.paw.pdl.graphql.generated.hentopphold.Opphold + +suspend fun PdlClient.hentOpphold( + ident: String, + callId: String?, + navConsumerId: String?, + behandlingsnummer: String, +): List? { + val query = HentOpphold(HentOpphold.Variables(ident)) + + logger.info("Henter 'hentPerson' fra PDL") + + val respons = + execute( + query = query, + callId = callId, + navConsumerId = navConsumerId, + behandlingsnummer = behandlingsnummer, + ) + + respons.errors?.let { + logger.error("Henter 'hentPerson' fra PDL feilet med: ${respons.errors}") + throw PdlException("'hentPerson' feilet", it) + } + + logger.info("Hentet 'hentPerson' fra PDL") + + return respons + .data + ?.hentPerson + ?.opphold +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentPerson.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentPerson.kt new file mode 100644 index 00000000..16f8be00 --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentPerson.kt @@ -0,0 +1,39 @@ +package no.nav.paw.pdl + +import no.nav.paw.pdl.graphql.generated.HentPerson +import no.nav.paw.pdl.graphql.generated.hentperson.Person + +suspend fun PdlClient.hentPerson( + ident: String, + callId: String?, + traceparent: String? = null, + navConsumerId: String?, + historisk: Boolean = false, + behandlingsnummer: String, +): Person? { + val query = + HentPerson( + HentPerson.Variables(ident, historisk), + ) + + logger.trace("Henter 'hentPerson' fra PDL") + + val respons = + execute( + query = query, + callId = callId, + navConsumerId = navConsumerId, + traceparent = traceparent, + behandlingsnummer = behandlingsnummer, + ) + + respons.errors?.let { + throw PdlException("'hentPerson' feilet", it) + } + + logger.trace("Hentet 'hentPerson' fra PDL") + + return respons + .data + ?.hentPerson +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentPersonBolk.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentPersonBolk.kt new file mode 100644 index 00000000..82b88cf0 --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/HentPersonBolk.kt @@ -0,0 +1,39 @@ +package no.nav.paw.pdl + +import no.nav.paw.pdl.graphql.generated.HentPersonBolk +import no.nav.paw.pdl.graphql.generated.hentpersonbolk.HentPersonBolkResult + +suspend fun PdlClient.hentPersonBolk( + ident: List, + callId: String?, + traceparent: String? = null, + navConsumerId: String?, + historisk: Boolean = false, + behandlingsnummer: String, +): List? { + val query = + HentPersonBolk( + HentPersonBolk.Variables(ident, historisk), + ) + + logger.trace("Henter 'hentPerson' fra PDL") + + val respons = + execute( + query = query, + callId = callId, + navConsumerId = navConsumerId, + traceparent = traceparent, + behandlingsnummer = behandlingsnummer, + ) + + respons.errors?.let { + throw PdlException("'hentPerson' feilet", it) + } + + logger.trace("Hentet 'hentPerson' fra PDL") + + return respons + .data + ?.hentPersonBolk +} diff --git a/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/PdlClient.kt b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/PdlClient.kt new file mode 100644 index 00000000..eddfdf77 --- /dev/null +++ b/lib/pdl-client/src/main/kotlin/no/nav/paw/pdl/PdlClient.kt @@ -0,0 +1,54 @@ +package no.nav.paw.pdl + +import com.expediagroup.graphql.client.ktor.GraphQLKtorClient +import com.expediagroup.graphql.client.types.GraphQLClientError +import com.expediagroup.graphql.client.types.GraphQLClientRequest +import com.expediagroup.graphql.client.types.GraphQLClientResponse +import io.ktor.client.HttpClient +import io.ktor.client.request.bearerAuth +import io.ktor.client.request.header +import org.slf4j.LoggerFactory +import java.io.IOException +import java.net.URI + +// Se https://pdldocs-navno.msappproxy.net/ for dokumentasjon av PDL API-et +class PdlClient( + url: String, + // Tema: https://confluence.adeo.no/pages/viewpage.action?pageId=309311397 + private val tema: String, + httpClient: HttpClient, + private val getAccessToken: () -> String, +) { + internal val logger = LoggerFactory.getLogger(this::class.java) + + private val graphQLClient = + GraphQLKtorClient( + url = URI.create(url).toURL(), + httpClient = httpClient, + ) + + internal suspend fun execute( + query: GraphQLClientRequest, + behandlingsnummer: String, + callId: String?, + traceparent: String? = null, + navConsumerId: String?, + ): GraphQLClientResponse { + if (behandlingsnummer.isBlank()) { + throw IllegalArgumentException("Behandlingsnummer kan ikke være tom") + } + return graphQLClient.execute(query) { + bearerAuth(getAccessToken()) + header("Tema", tema) + header("Nav-Call-Id", callId) + header("Nav-Consumer-Id", navConsumerId) + header("Behandlingsnummer", behandlingsnummer) + traceparent?.let { header("traceparent", it) } + } + } +} + +class PdlException( + message: String? = null, + val errors: List?, +) : IOException(message) diff --git a/lib/pdl-client/src/main/resources/.graphqlconfig b/lib/pdl-client/src/main/resources/.graphqlconfig new file mode 100644 index 00000000..f44e17f5 --- /dev/null +++ b/lib/pdl-client/src/main/resources/.graphqlconfig @@ -0,0 +1,4 @@ +{ + "name": "PDL GraphQL Schema", + "schemaPath": "pdl-schema.graphql" +} \ No newline at end of file diff --git a/lib/pdl-client/src/main/resources/hentFoedsel.graphql b/lib/pdl-client/src/main/resources/hentFoedsel.graphql new file mode 100644 index 00000000..abd56026 --- /dev/null +++ b/lib/pdl-client/src/main/resources/hentFoedsel.graphql @@ -0,0 +1,8 @@ +query($ident: ID!) { + hentPerson(ident: $ident) { + foedsel { + foedselsdato + foedselsaar + } + } +} diff --git a/lib/pdl-client/src/main/resources/hentForenkletStatus.graphql b/lib/pdl-client/src/main/resources/hentForenkletStatus.graphql new file mode 100644 index 00000000..5e1856b3 --- /dev/null +++ b/lib/pdl-client/src/main/resources/hentForenkletStatus.graphql @@ -0,0 +1,7 @@ +query($ident: ID!) { + hentPerson(ident: $ident) { + folkeregisterpersonstatus { + forenkletStatus + } + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/main/resources/hentForenkletStatusBolk.graphql b/lib/pdl-client/src/main/resources/hentForenkletStatusBolk.graphql new file mode 100644 index 00000000..29af5b2a --- /dev/null +++ b/lib/pdl-client/src/main/resources/hentForenkletStatusBolk.graphql @@ -0,0 +1,11 @@ +query($identer: [ID!]!) { + hentPersonBolk(identer: $identer) { + ident + person { + folkeregisterpersonstatus { + forenkletStatus + } + } + code + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/main/resources/hentIdenter.graphql b/lib/pdl-client/src/main/resources/hentIdenter.graphql new file mode 100644 index 00000000..25ab35a8 --- /dev/null +++ b/lib/pdl-client/src/main/resources/hentIdenter.graphql @@ -0,0 +1,8 @@ +query($ident: ID!, $historisk: Boolean) { + hentIdenter(ident: $ident, historikk: $historisk) { + identer { + ident + gruppe + } + } +} diff --git a/lib/pdl-client/src/main/resources/hentOpphold.graphql b/lib/pdl-client/src/main/resources/hentOpphold.graphql new file mode 100644 index 00000000..a4255d86 --- /dev/null +++ b/lib/pdl-client/src/main/resources/hentOpphold.graphql @@ -0,0 +1,9 @@ +query($ident: ID!) { + hentPerson(ident: $ident) { + opphold { + type + oppholdFra + oppholdTil + } + } +} diff --git a/lib/pdl-client/src/main/resources/hentPerson.graphql b/lib/pdl-client/src/main/resources/hentPerson.graphql new file mode 100644 index 00000000..96d883e5 --- /dev/null +++ b/lib/pdl-client/src/main/resources/hentPerson.graphql @@ -0,0 +1,70 @@ +query($ident: ID!, $historisk: Boolean) { + hentPerson(ident: $ident) { + foedsel { + foedselsdato + foedselsaar + } + statsborgerskap(historikk: $historisk) { + land + metadata { + endringer { + type + registrert + kilde + } + } + } + opphold(historikk: $historisk) { + oppholdFra + oppholdTil + type + metadata { + endringer { + type + registrert + kilde + } + } + } + folkeregisterpersonstatus(historikk: $historisk) { + forenkletStatus + metadata { + endringer { + type + registrert + kilde + } + } + } + bostedsadresse(historikk: $historisk) { + angittFlyttedato + gyldigFraOgMed + gyldigTilOgMed + vegadresse { + kommunenummer + } + matrikkeladresse { + kommunenummer + } + ukjentBosted { + bostedskommune + } + utenlandskAdresse { + landkode + } + } + innflyttingTilNorge { + folkeregistermetadata { + gyldighetstidspunkt + ajourholdstidspunkt + } + } + utflyttingFraNorge { + utflyttingsdato + folkeregistermetadata { + gyldighetstidspunkt + ajourholdstidspunkt + } + } + } +} diff --git a/lib/pdl-client/src/main/resources/hentPersonBolk.graphql b/lib/pdl-client/src/main/resources/hentPersonBolk.graphql new file mode 100644 index 00000000..7f50882c --- /dev/null +++ b/lib/pdl-client/src/main/resources/hentPersonBolk.graphql @@ -0,0 +1,74 @@ +query($identer: [ID!]!, $historisk: Boolean) { + hentPersonBolk(identer: $identer) { + ident + person { + foedsel { + foedselsdato + foedselsaar + } + statsborgerskap(historikk: $historisk) { + land + metadata { + endringer { + type + registrert + kilde + } + } + } + opphold(historikk: $historisk) { + oppholdFra + oppholdTil + type + metadata { + endringer { + type + registrert + kilde + } + } + } + folkeregisterpersonstatus(historikk: $historisk) { + forenkletStatus + metadata { + endringer { + type + registrert + kilde + } + } + } + bostedsadresse(historikk: $historisk) { + angittFlyttedato + gyldigFraOgMed + gyldigTilOgMed + vegadresse { + kommunenummer + } + matrikkeladresse { + kommunenummer + } + ukjentBosted { + bostedskommune + } + utenlandskAdresse { + landkode + } + } + innflyttingTilNorge { + folkeregistermetadata { + gyldighetstidspunkt + ajourholdstidspunkt + } + } + utflyttingFraNorge { + utflyttingsdato + folkeregistermetadata { + gyldighetstidspunkt + ajourholdstidspunkt + } + } + } + code + } +} diff --git a/lib/pdl-client/src/main/resources/pdl-schema.graphql b/lib/pdl-client/src/main/resources/pdl-schema.graphql new file mode 100644 index 00000000..f44649a5 --- /dev/null +++ b/lib/pdl-client/src/main/resources/pdl-schema.graphql @@ -0,0 +1,910 @@ +# Hentet fra https://github.com/navikt/pdl/blob/master/apps/api/src/main/resources/schemas/pdl.graphqls +# ISO-8601 representasjon for en kalenderdato. YYYY-MM-DD. Eksempel: 2018-01-01. +scalar Date + +# ISO-8601 representasjon for en kalenderdato med tid, trunkert til nærmeste sekund. YYYY-MM-DD'T'hh:mm:ss. Eksempel: 2018-01-01T12:00:00. +scalar DateTime + +# Schema parser kjenner ikke til Typen long men den er støttet av graphql-java, så definerer den her for at schema skal validere. +scalar Long + +schema { + query: Query +} + +type Query { + hentPerson(ident: ID!): Person + hentPersonBolk(identer: [ID!]!): [HentPersonBolkResult!]! + hentIdenter(ident: ID!, grupper: [IdentGruppe!], historikk: Boolean = false): Identliste + hentIdenterBolk(identer: [ID!]!, grupper: [IdentGruppe!], historikk: Boolean = false): [HentIdenterBolkResult!]! + hentGeografiskTilknytning(ident: ID!): GeografiskTilknytning + hentGeografiskTilknytningBolk(identer: [ID!]!): [hentGeografiskTilknytningBolkResult!]! + sokPerson(criteria:[Criterion], paging:Paging): PersonSearchResult + sokAdresse(criteria:[Criterion], paging:Paging): AdresseSearchResult + forslagAdresse(parameters:CompletionParameters): AdresseCompletionResult + hentAdresse(matrikkelId: ID!): KartverketAdresse +} + +type KartverketAdresse { + id: Long! + vegadresse: KartverketVegadresse + matrikkeladresse: KartverketMatrikkeladresse +} + +type KartverketMatrikkeladresse { + undernummer: Int + representasjonspunkt: KartverketRepresentasjonspunkt + adressetilleggsnavn: String + kortnavn: String + postnummeromraade: KartverketPostnummeromraade + grunnkrets: KartverketGrunnkrets + bydel: KartverketBydel + matrikkelnummer: KartverketMatrikkelnummer +} + +type KartverketMatrikkelnummer { + kommunenummer: String + gaardsnummer: Int + bruksnummer: Int + festenummer: Int + seksjonsnummer: Int +} + +type KartverketVegadresse { + nummer: Int + bokstav: String + representasjonspunkt: KartverketRepresentasjonspunkt + adressetilleggsnavn: String + kortnavn: String + postnummeromraade: KartverketPostnummeromraade + grunnkrets: KartverketGrunnkrets + veg: KartverketVeg + bydel: KartverketBydel +} + +type KartverketPostnummeromraade { + poststed: String + postnummer: String +} + +type KartverketGrunnkrets { + grunnkretsnavn: String + grunnkretsnummer: String +} + +type KartverketRepresentasjonspunkt { + posisjonskvalitet: Int + x: Float + y: Float + z: Float +} + +type KartverketBydel { + bydelsnavn: String + bydelsnummer: String +} + +type KartverketVeg { + adressekode: Int + adressenavn: String + kortnavn: String + stedsnummer: String + kommune: KartverketKommune +} + +type KartverketKommune { + nummer: String + navn: String + fylke: KartverketFylke +} + +type KartverketFylke { + nummer: String + navn: String +} + +type Identliste { + identer: [IdentInformasjon!]! +} + +type HentIdenterBolkResult { + ident: String! + identer: [IdentInformasjon!] + code: String! +} + +type IdentInformasjon { + ident: String! + gruppe: IdentGruppe! + historisk: Boolean! +} + +enum IdentGruppe { + AKTORID, + FOLKEREGISTERIDENT, + NPID +} + +type HentPersonBolkResult { + ident: String! + person: Person + code: String! +} + +type Person { + adressebeskyttelse(historikk: Boolean = false): [Adressebeskyttelse!]! + bostedsadresse(historikk: Boolean = false): [Bostedsadresse!]! + deltBosted(historikk: Boolean = false): [DeltBosted!]! + doedfoedtBarn: [DoedfoedtBarn!]! + doedsfall: [Doedsfall!]! + falskIdentitet: FalskIdentitet + foedsel: [Foedsel!]! + foedselsdato: [Foedselsdato!]! + foedested: [Foedested!]! + folkeregisteridentifikator(historikk: Boolean = false): [Folkeregisteridentifikator!]! + folkeregisterpersonstatus(historikk: Boolean = false): [Folkeregisterpersonstatus!]! + forelderBarnRelasjon: [ForelderBarnRelasjon!]! + foreldreansvar(historikk: Boolean = false): [Foreldreansvar!]! + fullmakt(historikk: Boolean = false): [Fullmakt!]! + identitetsgrunnlag(historikk: Boolean = false): [Identitetsgrunnlag!]! + kjoenn(historikk: Boolean = false): [Kjoenn!]! + kontaktadresse(historikk: Boolean = false): [Kontaktadresse!]! + kontaktinformasjonForDoedsbo(historikk: Boolean = false): [KontaktinformasjonForDoedsbo!]! + navn(historikk: Boolean = false): [Navn!]! + opphold(historikk: Boolean = false):[Opphold!]! + oppholdsadresse(historikk: Boolean = false):[Oppholdsadresse!]! + sikkerhetstiltak:[Sikkerhetstiltak!]! + sivilstand(historikk: Boolean = false):[Sivilstand!]! + statsborgerskap(historikk: Boolean = false): [Statsborgerskap!]! + telefonnummer(historikk: Boolean = false): [Telefonnummer!]! + tilrettelagtKommunikasjon:[TilrettelagtKommunikasjon!]! + utenlandskIdentifikasjonsnummer(historikk: Boolean = false): [UtenlandskIdentifikasjonsnummer!]! + innflyttingTilNorge: [InnflyttingTilNorge!]! + utflyttingFraNorge: [UtflyttingFraNorge!]! + vergemaalEllerFremtidsfullmakt(historikk: Boolean = false): [VergemaalEllerFremtidsfullmakt!]! + rettsligHandleevne(historikk: Boolean = false): [RettsligHandleevne!]! +} + +type DeltBosted { + startdatoForKontrakt: Date! + sluttdatoForKontrakt: Date + + coAdressenavn: String + vegadresse: Vegadresse + matrikkeladresse: Matrikkeladresse + utenlandskAdresse: UtenlandskAdresse + ukjentBosted: UkjentBosted + + folkeregistermetadata: Folkeregistermetadata! + metadata: Metadata! +} + +type Bostedsadresse { + angittFlyttedato: Date + gyldigFraOgMed: DateTime + gyldigTilOgMed: DateTime + + coAdressenavn: String + vegadresse: Vegadresse + matrikkeladresse: Matrikkeladresse + utenlandskAdresse: UtenlandskAdresse + ukjentBosted: UkjentBosted + + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Oppholdsadresse { + gyldigFraOgMed: DateTime + gyldigTilOgMed: DateTime + + coAdressenavn: String + utenlandskAdresse: UtenlandskAdresse + vegadresse: Vegadresse + matrikkeladresse: Matrikkeladresse + oppholdAnnetSted: String + + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Kontaktadresse { + gyldigFraOgMed: DateTime + gyldigTilOgMed: DateTime + type: KontaktadresseType! + + coAdressenavn: String + postboksadresse: Postboksadresse + vegadresse: Vegadresse + postadresseIFrittFormat: PostadresseIFrittFormat + utenlandskAdresse: UtenlandskAdresse + utenlandskAdresseIFrittFormat: UtenlandskAdresseIFrittFormat + + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +enum KontaktadresseType { + Innland, + Utland +} + +type Vegadresse { + matrikkelId: Long + husnummer: String + husbokstav: String + bruksenhetsnummer: String + adressenavn: String + kommunenummer: String + bydelsnummer: String + tilleggsnavn: String + postnummer: String + koordinater: Koordinater +} + +type Matrikkeladresse { + matrikkelId: Long + bruksenhetsnummer: String + tilleggsnavn: String + postnummer: String + kommunenummer: String + koordinater: Koordinater +} + +type UkjentBosted { + bostedskommune: String +} + +type UtenlandskAdresse { + adressenavnNummer: String + bygningEtasjeLeilighet: String + postboksNummerNavn: String + postkode: String + bySted: String + regionDistriktOmraade: String + landkode: String! +} + +type UtenlandskAdresseIFrittFormat { + adresselinje1: String + adresselinje2: String + adresselinje3: String + postkode: String + byEllerStedsnavn: String + landkode: String! +} + +type Postboksadresse { + postbokseier: String + postboks: String! + postnummer: String +} + +type PostadresseIFrittFormat { + adresselinje1: String + adresselinje2: String + adresselinje3: String + postnummer: String +} + +type Koordinater { + x: Float + y: Float + z: Float + kvalitet: Int +} + +type FalskIdentitet { + erFalsk: Boolean! + rettIdentitetVedIdentifikasjonsnummer: String + rettIdentitetErUkjent: Boolean + rettIdentitetVedOpplysninger: FalskIdentitetIdentifiserendeInformasjon + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type FalskIdentitetIdentifiserendeInformasjon { + personnavn: Personnavn! + foedselsdato: Date + statsborgerskap: [String!]! + kjoenn: KjoennType +} + +type KontaktinformasjonForDoedsbo { + skifteform: KontaktinformasjonForDoedsboSkifteform! + attestutstedelsesdato: Date! + personSomKontakt: KontaktinformasjonForDoedsboPersonSomKontakt + advokatSomKontakt: KontaktinformasjonForDoedsboAdvokatSomKontakt + organisasjonSomKontakt: KontaktinformasjonForDoedsboOrganisasjonSomKontakt + adresse: KontaktinformasjonForDoedsboAdresse! + folkeregistermetadata: Folkeregistermetadata! + metadata: Metadata! +} + +enum KontaktinformasjonForDoedsboSkifteform { + OFFENTLIG + ANNET +} + +type KontaktinformasjonForDoedsboPersonSomKontakt { + foedselsdato: Date + personnavn: Personnavn + identifikasjonsnummer: String +} + +type KontaktinformasjonForDoedsboAdvokatSomKontakt { + personnavn: Personnavn! + organisasjonsnavn: String + organisasjonsnummer: String +} + +type KontaktinformasjonForDoedsboOrganisasjonSomKontakt { + kontaktperson: Personnavn + organisasjonsnavn: String! + organisasjonsnummer: String +} + +type KontaktinformasjonForDoedsboAdresse { + adresselinje1: String! + adresselinje2: String + poststedsnavn: String! + postnummer: String! + landkode: String +} + +type UtenlandskIdentifikasjonsnummer { + identifikasjonsnummer: String! + utstederland: String! + opphoert: Boolean! + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Adressebeskyttelse { + gradering: AdressebeskyttelseGradering! + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +enum AdressebeskyttelseGradering { + STRENGT_FORTROLIG_UTLAND, + STRENGT_FORTROLIG, + FORTROLIG, + UGRADERT +} + +type Foedsel { + foedselsaar: Int + foedselsdato: Date + foedeland: String + foedested: String + foedekommune: String + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Foedested { + foedeland: String + foedested: String + foedekommune: String + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Foedselsdato { + foedselsdato: Date + foedselsaar: Int + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Kjoenn { + kjoenn: KjoennType + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Doedsfall { + doedsdato: Date + metadata: Metadata! + folkeregistermetadata: Folkeregistermetadata +} + +type ForelderBarnRelasjon { + relatertPersonsIdent: String + relatertPersonsRolle: ForelderBarnRelasjonRolle! + minRolleForPerson: ForelderBarnRelasjonRolle + relatertPersonUtenFolkeregisteridentifikator: RelatertBiPerson + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type DoedfoedtBarn { + dato: Date + folkeregistermetadata: Folkeregistermetadata! + metadata: Metadata! +} + +enum Familierelasjonsrolle { + BARN, + MOR, + FAR, + MEDMOR +} + +enum ForelderBarnRelasjonRolle { + BARN, + MOR, + FAR, + MEDMOR +} + +type Folkeregisterpersonstatus { + status: String! + forenkletStatus: String! + folkeregistermetadata: Folkeregistermetadata! + metadata: Metadata! +} + +type GeografiskTilknytning { + gtType: GtType! + gtKommune: String + gtBydel: String + gtLand: String + regel: String! +} + +type hentGeografiskTilknytningBolkResult { + ident: String! + geografiskTilknytning: GeografiskTilknytning + code: String! +} + +enum GtType { + KOMMUNE, + BYDEL, + UTLAND, + UDEFINERT +} + +type Navn { + fornavn: String! + mellomnavn: String + etternavn: String! + forkortetNavn: String @deprecated + originaltNavn: OriginaltNavn + gyldigFraOgMed: Date + + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type OriginaltNavn { + fornavn: String + mellomnavn: String + etternavn: String +} + +type Personnavn { + fornavn: String! + mellomnavn: String + etternavn: String! +} + +enum KjoennType { + MANN, KVINNE, UKJENT +} + +type Identitetsgrunnlag { + status: Identitetsgrunnlagsstatus! + folkeregistermetadata: Folkeregistermetadata! + metadata: Metadata! +} + +enum Identitetsgrunnlagsstatus { + IKKE_KONTROLLERT + KONTROLLERT, + INGEN_STATUS +} + +type Folkeregistermetadata { + ajourholdstidspunkt: DateTime + gyldighetstidspunkt: DateTime + opphoerstidspunkt: DateTime + kilde: String + aarsak: String + sekvens: Int +} + +type Telefonnummer { + landskode: String! + nummer: String! + prioritet: Int! + metadata: Metadata! +} + +type TilrettelagtKommunikasjon { + talespraaktolk: Tolk + tegnspraaktolk: Tolk + metadata: Metadata! +} + +type Tolk { + spraak: String +} + +enum FullmaktsRolle { + FULLMAKTSGIVER, + FULLMEKTIG +} + +type Fullmakt { + motpartsPersonident: String! + motpartsRolle: FullmaktsRolle! + omraader: [String!]! + gyldigFraOgMed: Date! + gyldigTilOgMed: Date! + metadata: Metadata! +} + +type Folkeregisteridentifikator { + identifikasjonsnummer: String! + status: String! + type: String! + folkeregistermetadata: Folkeregistermetadata! + metadata: Metadata! +} + +type SikkerhetstiltakKontaktperson { + personident: String! + enhet: String! +} + +type Sikkerhetstiltak { + tiltakstype: String! + beskrivelse: String! + kontaktperson: SikkerhetstiltakKontaktperson + gyldigFraOgMed: Date! + gyldigTilOgMed: Date! + metadata: Metadata! +} + +type Statsborgerskap { + land: String! + bekreftelsesdato: Date + gyldigFraOgMed: Date + gyldigTilOgMed: Date + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Opphold { + type: Oppholdstillatelse! + oppholdFra: Date + oppholdTil: Date + folkeregistermetadata: Folkeregistermetadata! + metadata: Metadata! +} + +enum Oppholdstillatelse { + MIDLERTIDIG + PERMANENT + OPPLYSNING_MANGLER +} + +type Sivilstand { + type: Sivilstandstype! + gyldigFraOgMed: Date + relatertVedSivilstand: String + bekreftelsesdato: Date + + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +enum Sivilstandstype { + UOPPGITT + UGIFT + GIFT + ENKE_ELLER_ENKEMANN + SKILT + SEPARERT + REGISTRERT_PARTNER + SEPARERT_PARTNER + SKILT_PARTNER + GJENLEVENDE_PARTNER +} + +type InnflyttingTilNorge { + fraflyttingsland: String + fraflyttingsstedIUtlandet: String + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type UtflyttingFraNorge { + tilflyttingsland: String + tilflyttingsstedIUtlandet: String + utflyttingsdato: Date + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type VergeEllerFullmektig { + navn: Personnavn @deprecated(reason:"Erstattes av navn iidentifiserendeInformasjon") + identifiserendeInformasjon: IdentifiserendeInformasjon + motpartsPersonident: String + omfang: String + omfangetErInnenPersonligOmraade: Boolean + tjenesteomraade:[Tjenesteomraade!] +} +type IdentifiserendeInformasjon { + navn: Personnavn + kjoenn: String + foedselsdato:Date + statsborgerskap: [String!] + +} +type Tjenesteomraade { + tjenesteoppgave: String + tjenestevirksomhet: String +} + +type VergemaalEllerFremtidsfullmakt { + type: String + embete: String + vergeEllerFullmektig: VergeEllerFullmektig! + + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + + +type RettsligHandleevne { + rettsligHandleevneomfang: String + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type Foreldreansvar { + ansvar: String + ansvarlig: String + ansvarssubjekt: String + ansvarligUtenIdentifikator: RelatertBiPerson + + folkeregistermetadata: Folkeregistermetadata + metadata: Metadata! +} + +type RelatertBiPerson { + navn: Personnavn + foedselsdato: Date + statsborgerskap: String + kjoenn: KjoennType +} + +type Metadata { + # I PDL så får alle forekomster av en opplysning en ID som representerer dens unike forekomst. + # F.eks, så vil en Opprett ha ID X, korriger ID Y (der hvor den spesifiserer at den korrigerer X). + # Dersom en opplysning ikke er lagret i PDL, så vil denne verdien ikke være utfylt. + opplysningsId: String + + # Master refererer til hvem som eier opplysningen, f.eks så har PDL en kopi av Folkeregisteret, da vil master være FREG og eventuelle endringer på dette må gå via Folkeregisteret (API mot dem eller andre rutiner). + master: String! + + # En liste over alle endringer som har blitt utført over tid. + # Vær obs på at denne kan endre seg og man burde takle at det finnes flere korrigeringer i listen, så dersom man ønsker å kun vise den siste, så må man selv filtrere ut dette. + # Det kan også ved svært få tilfeller skje at opprett blir fjernet. F.eks ved splitt tilfeller av identer. Dette skal skje i svært få tilfeller. Dersom man ønsker å presentere opprettet tidspunktet, så blir det tidspunktet på den første endringen. + endringer: [Endring!]! + + # Feltet betegner hvorvidt dette er en funksjonelt historisk opplysning, for eksempel en tidligere fraflyttet adresse eller et foreldreansvar som er utløpt fordi barnet har fylt 18 år. + # I de fleste tilfeller kan dette utledes ved å se på de andre feltene i opplysningen. Dette er imidlertid ikke alltid tilfellet, blant annet for foreldreansvar. + # Feltet bør brukes av konsumenter som henter informasjon fra GraphQL med historikk, men som også trenger å utlede gjeldende informasjon. + historisk: Boolean! +} + +# Endring som har blitt utført på opplysningen. F.eks: Opprett -> Korriger -> Korriger +type Endring { + # Hvilke type endring som har blitt utført. + type: Endringstype! + # Tidspunktet for registrering. + registrert: DateTime! + # Hvem endringen har blitt utført av, ofte saksbehandler (f.eks Z990200), men kan også være system (f.eks srvXXXX). Denne blir satt til "Folkeregisteret" for det vi får fra dem. + registrertAv: String! + # Hvilke system endringen har kommet fra (f.eks srvXXX). Denne blir satt til "FREG" for det vi får fra Folkeregisteret. + systemkilde: String! + # Opphavet til informasjonen. I NAV blir dette satt i forbindelse med registrering (f.eks: Sykehuskassan). + # Fra Folkeregisteret får vi opphaven til dems opplysning, altså NAV, UDI, Politiet, Skatteetaten o.l.. Fra Folkeregisteret kan det også være tekniske navn som: DSF_MIGRERING, m.m.. + kilde: String! + + hendelseId:String! +} + +enum Endringstype { + OPPRETT + KORRIGER + OPPHOER +} + +input Criterion { + # Feltnavn inkludert sti til ønsket felt (Eksempel: person.navn.fornavn) + fieldName:String + + and:[Criterion] + or:[Criterion] + not:[Criterion] + + searchRule:SearchRule + # Søk i historiske data + # true = søker kun i historiske data. + # false = søker kun i gjeldende data. + # null = søke i både historiske og gjeldende data. + searchHistorical:Boolean +} + + +input SearchRule { + # Sjekker om feltet finnes / at det ikke har en null verdi. + exists:String + # Filtrerer bort treff hvor felt inneholder input verdi + notEquals:String + # Begrenser treff til kun de hvor felt har input verdi + equals:String + # Gir treff når opgitt felt inneholder en eller flere ord fra input verdien. + contains:String + # Søk som gir treff også for små variasjoner i skrivemåte + fuzzy:String + # Søk som gir tilfeldig poengsum til hvert treff (kun ment til generering av testdata) + random:String + # Bruk "?" som wildcard for enkelt tegn, og "*" som wildcard for 0 eller flere tegn. + wildcard:String + # Gir treff når opgitt feltstarter med opgitt verdi. + startsWith:String + # Regex søk for spesielle situasjoner (Dette er en treg opprasjon og bør ikke brukes) + regex:String + # Brukes til søke etter datoer som kommer etter opgitt dato. + after:String + # Brukes til søke etter datoer som kommer før opgitt dato. + before:String + # Brukes til å søke i tall og finner verdier som er mindre en input verdi. + lessThan:String + # Brukes til å søke i tall og finner verdier som er størren en input verdi. + greaterThan:String + # Søk fra og med (se fromExcluding for bare fra men ikke med) + # kan benyttes på tall og dato + from:String + # Søk til og med (se toExcluding for bare til men ikke med) + # kan benyttes på tall og dato + to:String + # Søk fra men ikke med oppgitt verdi + # kan benyttes på tall og dato + fromExcluding:String, + # Søk til men ikke med oppgitt verdi + # kan benyttes på tall og dato + toExcluding:String + + # [Flag] Kan brukes til å overstyre standard oppførsellen for søk i felter (standard er case insensitive) + caseSensitive:Boolean + # [Flag] Brukes til å deaktivere fonetisk søk feltene som har dette som standard (Navn) + disablePhonetic:Boolean + # Boost brukes til å gi ett søkekriterie høyere eller lavere vektlegging en de andre søke kriteriene. + boost:Float +} + +input Paging { + # Hvilken side i resultatsettet man ønsker vist. + pageNumber:Int = 1 + # antall treff per side (maks 100) + resultsPerPage:Int = 10 + # Liste over felter man ønsker resultatene sortert etter + # Standard er "score". Score er poengsummen Elasticsearch tildeler hvert resultat. + sortBy:[SearchSorting] +} + +input SearchSorting { + # Feltnavn ikludert sti til ønsket felt (eksepmel: person.navn.fornavn) + fieldName:String! + direction:Direction! +} +enum Direction { + ASC, + DESC +} +type PersonSearchResult { + # treff liste + hits : [PersonSearchHit!]! + # Side nummer for siden som vises + pageNumber:Int + # Totalt antall sider + totalPages:Int + # Totalt antall treff (øvre grense er satt til 10 000) + totalHits:Int + +} +type PersonSearchHit { + # forespurte data + person :Person + # forespurte data + identer(historikk: Boolean = false): [IdentInformasjon!]! + # Poengsummen elasticsearch har gitt dette resultatet (brukt til feilsøking, og tuning av søk) + score :Float + # Infromasjon om hva som ga treff i søke resultatet. + highlights:[PersonSearchHighlight] +} +type PersonSearchHighlight { + # Navn/Sti til opplysningen som ga treff. Merk at dette ikke er feltet som ga treff men opplysningen. + # F.eks. hvis du søker på person.navn.fornavn så vil opplysingen være person.navn. + opplysning:String + # Gitt att opplysningen som ga treff har en opplysningsId så vil den returneres her. + # alle søk under person skal ha opplysningsId, men søk i identer vil kunne returnere treff uten opplysningsId. + opplysningsId: String + # Forteller hvorvidt opplysningen som ga treff er markert som historisk. + historisk: Boolean + # liste med feltene og verdiene som ga treff. + # Merk at for fritekst søk så vil disse kunne referere til hjelpe felter som ikke er synelig i resultatene. + matches:[SearchMatch] +} +type SearchMatch { + # feltnavn med sti til feltet so ga treff. + field:String! + type: String + # Verdien som ga treff + fragments: [String] +} +type AdresseSearchResult { + hits : [AdresseSearchHit!]! + pageNumber:Int, + totalPages:Int + totalHits:Int + +} +type AdresseSearchHit { + vegadresse : VegadresseResult + matrikkeladresse : MatrikkeladresseResult + score :Float +} +type VegadresseResult { + matrikkelId:String + husnummer:Int + husbokstav:String + adressenavn:String + adressekode:String + tilleggsnavn:String + fylkesnavn:String + fylkesnummer:String + kommunenavn:String + kommunenummer:String + postnummer:String + poststed:String + bydelsnavn:String + bydelsnummer:String +} +type MatrikkeladresseResult { + matrikkelId:String + tilleggsnavn:String + kommunenummer:String + gaardsnummer:String + bruksnummer:String + postnummer:String + poststed:String +} + +type AdresseCompletionResult { + suggestions: [String!]! + addressFound : CompletionAdresse +} +type CompletionAdresse { + vegadresse : VegadresseResult + matrikkeladresse : MatrikkeladresseResult +} + +input CompletionParameters { + + completionField: String! + maxSuggestions: Int + fieldValues: [CompletionFieldValue]! +} +input CompletionFieldValue { + fieldName: String! + fieldValue: String +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/kotlin/no/nav/paw/pdl/MockPdlClient.kt b/lib/pdl-client/src/test/kotlin/no/nav/paw/pdl/MockPdlClient.kt new file mode 100644 index 00000000..3a02813e --- /dev/null +++ b/lib/pdl-client/src/test/kotlin/no/nav/paw/pdl/MockPdlClient.kt @@ -0,0 +1,21 @@ +package no.nav.paw.pdl + +import io.ktor.client.HttpClient +import io.ktor.client.engine.mock.MockEngine +import io.ktor.client.engine.mock.respond +import io.ktor.http.ContentType +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.headersOf + +fun mockPdlClient(content: String): PdlClient { + val mockEngine = + MockEngine { + respond( + content = content, + status = HttpStatusCode.OK, + headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString()), + ) + } + return PdlClient("https://url", "tema", HttpClient(mockEngine)) { "fake token" } +} diff --git a/lib/pdl-client/src/test/kotlin/no/nav/paw/pdl/PdlClientTest.kt b/lib/pdl-client/src/test/kotlin/no/nav/paw/pdl/PdlClientTest.kt new file mode 100644 index 00000000..7725b0b7 --- /dev/null +++ b/lib/pdl-client/src/test/kotlin/no/nav/paw/pdl/PdlClientTest.kt @@ -0,0 +1,142 @@ +package no.nav.paw.pdl + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FreeSpec +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.runBlocking +import no.nav.paw.pdl.graphql.generated.enums.Oppholdstillatelse +import java.util.* + +class PdlClientTest : FreeSpec({ + + val callId = UUID.randomUUID().toString() + val navConsumerId = "nav-consumer-id" + + "Forventer gyldig respons fra hentAktorId" { + val respons = readResource("hentIdenter-response.json") + val pdlClient = mockPdlClient(respons) + + val resultat = runBlocking { + pdlClient.hentAktorId("2649500819544", callId, navConsumerId, "B123") + } + val forventet = "2649500819544" + + resultat shouldBe forventet + } + + "Forventer feilmelding fra hentIdenter" { + val respons = readResource("error-response.json") + val pdlClient = mockPdlClient(respons) + + shouldThrow( + block = { + runBlocking { + pdlClient.hentIdenter( + ident = "2649500819544", + callId = callId, + navConsumerId = navConsumerId, + behandlingsnummer = "B123", + ) + } + }, + ) + } + + "Forventer gyldig respons fra hentIdenter" { + val respons = readResource("hentIdenter-response.json") + val pdlClient = mockPdlClient(respons) + + val resultat = runBlocking { + pdlClient.hentIdenter( + ident = "2649500819544", + callId = callId, + navConsumerId = navConsumerId, + behandlingsnummer = "B123", + ) + } + val forventet = "09127821914" + + resultat!!.first().ident shouldBe forventet + } + + "Forventer gyldig respons fra hentOpphold" { + val respons = readResource("hentOpphold-response.json") + val pdlClient = mockPdlClient(respons) + + val resultat = runBlocking { + pdlClient.hentOpphold("2649500819544", callId, navConsumerId, "B123") + } + val forventet = Oppholdstillatelse.PERMANENT + + resultat!!.first().type shouldBe forventet + } + + "Forventer gyldig respons fra hentPerson" { + val respons = readResource("hentPerson-response.json") + val pdlClient = mockPdlClient(respons) + + val resultat = runBlocking { + pdlClient.hentPerson( + "2649500819544", + callId, + null, + navConsumerId, + behandlingsnummer = "B123" + ) + } + val forventet = Oppholdstillatelse.PERMANENT + + resultat!!.opphold.first().type shouldBe forventet + } + + "Forventer gyldig respons fra hentPersonBolk" { + val respons = readResource("hentPersonBolk-response.json") + val pdlClient = mockPdlClient(respons) + + val resultat = runBlocking { + pdlClient.hentPersonBolk( + listOf("2649500819544"), + callId, + null, + navConsumerId, + false, + "B123" + ) + } + val forventet = Oppholdstillatelse.PERMANENT + + resultat!!.first().person!!.opphold.first().type shouldBe forventet + } + + "Forventer gyldig respons fra hentForenkletStatus" { + val respons = readResource("hentForenkletStatus-response.json") + val pdlClient = mockPdlClient(respons) + + val resultat = + runBlocking { pdlClient.hentForenkletStatus("2649500819544", callId, null, navConsumerId, "B123") } + val forventet = "bosattEtterFolkeregisterloven" + + resultat!!.folkeregisterpersonstatus.map { it.forenkletStatus } shouldContain forventet + } + + "Forventer gyldig respons fra hentForenkletStatusBolk" { + val respons = readResource("hentForenkletStatusBolk-response.json") + val pdlClient = mockPdlClient(respons) + + val resultat = runBlocking { + pdlClient.hentForenkletStatusBolk( + listOf("2649500819544"), + callId, + null, + navConsumerId, + "B123" + ) + } + val forventet = "bosattEtterFolkeregisterloven" + + resultat!!.first().person!!.folkeregisterpersonstatus.map { it.forenkletStatus } shouldContain forventet + } +}) + +private fun readResource(filename: String) = ClassLoader.getSystemResource(filename).readText() diff --git a/lib/pdl-client/src/test/resources/error-response.json b/lib/pdl-client/src/test/resources/error-response.json new file mode 100644 index 00000000..e9b63be1 --- /dev/null +++ b/lib/pdl-client/src/test/resources/error-response.json @@ -0,0 +1,14 @@ +{ + "data": null, + "errors": [ + { + "message": "error message", + "locations": null, + "path": null, + "extensions": { + "code": null, + "classification": "Error" + } + } + ] +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/resources/hentForenkletStatus-response.json b/lib/pdl-client/src/test/resources/hentForenkletStatus-response.json new file mode 100644 index 00000000..8086a5e4 --- /dev/null +++ b/lib/pdl-client/src/test/resources/hentForenkletStatus-response.json @@ -0,0 +1,11 @@ +{ + "data": { + "hentPerson": { + "folkeregisterpersonstatus": [ + { + "forenkletStatus": "bosattEtterFolkeregisterloven" + } + ] + } + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/resources/hentForenkletStatusBolk-response.json b/lib/pdl-client/src/test/resources/hentForenkletStatusBolk-response.json new file mode 100644 index 00000000..fd97a945 --- /dev/null +++ b/lib/pdl-client/src/test/resources/hentForenkletStatusBolk-response.json @@ -0,0 +1,17 @@ +{ + "data": { + "hentPersonBolk": [ + { + "ident": "2649500819544", + "person": { + "folkeregisterpersonstatus": [ + { + "forenkletStatus": "bosattEtterFolkeregisterloven" + } + ] + }, + "code": "ok" + } + ] + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/resources/hentFullPerson-response.json b/lib/pdl-client/src/test/resources/hentFullPerson-response.json new file mode 100644 index 00000000..8dd9be41 --- /dev/null +++ b/lib/pdl-client/src/test/resources/hentFullPerson-response.json @@ -0,0 +1,97 @@ +{ + "data": { + "hentIdenter": { + "identer": [ + { + "ident": "09127821914", + "gruppe": "FOLKEREGISTERIDENT" + }, + { + "ident": "2649500819544", + "gruppe": "AKTORID" + } + ] + }, + + "hentGeografiskTilknytning": { + "gtType": "KOMMUNE", + "gtKommune": "1851", + "gtBydel": null, + "gtLand": null + }, + + "hentPerson": { + "navn": [ + { + "fornavn": "NILS", + "etternavn": "FALSKESEN", + "metadata": { + "master": "Freg" + } + } + ], + "bostedsadresse": [ + { + "gyldigFraOgMed": "2014-07-29T00:00", + "angittFlyttedato": "2014-07-29", + "vegadresse": { + "kommunenummer": "1221" + } + }, + { + "gyldigFraOgMed": "2016-02-05T00:00", + "angittFlyttedato": "2016-01-26", + "gyldigTilOgMed": null, + "vegadresse": { + "kommunenummer": "1106" + } + }, + { + "gyldigFraOgMed": "2014-02-05T00:00", + "angittFlyttedato": "2014-02-02", + "vegadresse": { + "kommunenummer": "1106" + } + }, + { + "gyldigFraOgMed": "2017-05-03T00:00", + "angittFlyttedato": "2017-05-03", + "ukjentBosted": { + "bostedskommune": "1106" + } + }, + { + "gyldigFraOgMed": "2014-05-09T00:00", + "angittFlyttedato": "2014-05-08", + "vegadresse": { + "kommunenummer": "1221" + } + }, + { + "gyldigFraOgMed": "2015-08-13T00:00", + "angittFlyttedato": "2015-08-13", + "vegadresse": { + "kommunenummer": "0626" + } + } + ], + "statsborgerskap": [ + { + "land": "NOR" + } + ], + "foedsel": [ + { + "foedselsdato": "1984-01-31" + } + ], + "doedsfall": [], + "adressebeskyttelse": [], + "kjoenn": [ + { + "kjoenn": "MANN" + } + ] + } + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/resources/hentIdenter-response.json b/lib/pdl-client/src/test/resources/hentIdenter-response.json new file mode 100644 index 00000000..7478c75f --- /dev/null +++ b/lib/pdl-client/src/test/resources/hentIdenter-response.json @@ -0,0 +1,16 @@ +{ + "data": { + "hentIdenter": { + "identer": [ + { + "ident": "09127821914", + "gruppe": "FOLKEREGISTERIDENT" + }, + { + "ident": "2649500819544", + "gruppe": "AKTORID" + } + ] + } + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/resources/hentOpphold-response.json b/lib/pdl-client/src/test/resources/hentOpphold-response.json new file mode 100644 index 00000000..ae95b68a --- /dev/null +++ b/lib/pdl-client/src/test/resources/hentOpphold-response.json @@ -0,0 +1,13 @@ +{ + "data": { + "hentPerson": { + "opphold": [ + { + "type": "PERMANENT", + "oppholdFra": "2010-01-20", + "oppholdTil": "2022-01-20" + } + ] + } + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/resources/hentPerson-response.json b/lib/pdl-client/src/test/resources/hentPerson-response.json new file mode 100644 index 00000000..11ca6176 --- /dev/null +++ b/lib/pdl-client/src/test/resources/hentPerson-response.json @@ -0,0 +1,69 @@ +{ + "data": { + "hentPerson": { + "statsborgerskap": [ + { + "land": "NOR", + "metadata": { + "endringer": [{ + "type:": "OPPRETT", + "registrert": "2010-01-20", + "kilde": "FOLKEREGISTERET" + }] + } + } + ], + "foedsel": [ + { + "foedselsdato": "1986-11-26", + "foedselsaar": "1986" + } + ], + "opphold": [ + { + "oppholdFra": "2010-01-20", + "oppholdTil": "2022-01-20", + "type": "PERMANENT", + "metadata": { + "endringer": [ + { + "type:": "OPPRETT", + "registrert": "2010-01-20", + "kilde": "FOLKEREGISTERET" + } + ] + } + } + ], + "folkeregisterpersonstatus": [ + { + "forenkletStatus": "bosattEtterFolkeregisterloven", + "metadata": { + "endringer": [ + { + "type:": "OPPRETT", + "registrert": "2010-01-20", + "kilde": "FOLKEREGISTERET" + } + ] + } + } + ], + "bostedsadresse": [ + { + "angittFlyttedato": "2010-01-20", + "gyldigFraOgMed": "2010-01-20", + "gyldigTilOgMed": "2022-01-20", + "vegadresse": { + "kommunenummer": "0301" + }, + "matrikkeladresse": null, + "ukjentBosted": null, + "utenlandskAdresse": null + } + ], + "innflyttingTilNorge": [], + "utflyttingFraNorge": [] + } + } +} \ No newline at end of file diff --git a/lib/pdl-client/src/test/resources/hentPersonBolk-response.json b/lib/pdl-client/src/test/resources/hentPersonBolk-response.json new file mode 100644 index 00000000..815e2f77 --- /dev/null +++ b/lib/pdl-client/src/test/resources/hentPersonBolk-response.json @@ -0,0 +1,75 @@ +{ + "data": { + "hentPersonBolk": [ + { + "ident": "2649500819544", + "person": { + "statsborgerskap": [ + { + "land": "NOR", + "metadata": { + "endringer": [{ + "type:": "OPPRETT", + "registrert": "2010-01-20", + "kilde": "FOLKEREGISTERET" + }] + } + } + ], + "foedsel": [ + { + "foedselsdato": "1986-11-26", + "foedselsaar": "1986" + } + ], + "opphold": [ + { + "oppholdFra": "2010-01-20", + "oppholdTil": "2022-01-20", + "type": "PERMANENT", + "metadata": { + "endringer": [ + { + "type:": "OPPRETT", + "registrert": "2010-01-20", + "kilde": "FOLKEREGISTERET" + } + ] + } + } + ], + "folkeregisterpersonstatus": [ + { + "forenkletStatus": "bosattEtterFolkeregisterloven", + "metadata": { + "endringer": [ + { + "type:": "OPPRETT", + "registrert": "2010-01-20", + "kilde": "FOLKEREGISTERET" + } + ] + } + } + ], + "bostedsadresse": [ + { + "angittFlyttedato": "2010-01-20", + "gyldigFraOgMed": "2010-01-20", + "gyldigTilOgMed": "2022-01-20", + "vegadresse": { + "kommunenummer": "0301" + }, + "matrikkeladresse": null, + "ukjentBosted": null, + "utenlandskAdresse": null + } + ], + "innflyttingTilNorge": [], + "utflyttingFraNorge": [] + }, + "code": "ok" + } + ] + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 35e9e66f..d01decaf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,9 +1,11 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" kotlin("jvm") version "2.0.21" apply false + kotlin("plugin.serialization") version "2.0.21" apply false id("com.google.cloud.tools.jib") version "3.4.4" apply false id("org.openapi.generator") version "7.9.0" apply false id("com.github.davidmc24.gradle.plugin.avro") version "1.9.1" apply false + id("com.expediagroup.graphql") version "7.1.1" apply false } rootProject.name = "paw-arbeidssoekerregisteret-monorepo-intern" @@ -14,6 +16,8 @@ include( "lib:kafka", "lib:kafka-streams", "lib:kafka-key-generator-client", + "lib:pdl-client", + "lib:aareg-client", "test:test-data-lib", "test:kafka-streams-test-functions", "domain:bekreftelse-interne-hendelser",