From b9d4773780c05c88f4a048c8462d39d9c17da51b Mon Sep 17 00:00:00 2001 From: Anna <100125713+anna-ritense@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:35:24 +0200 Subject: [PATCH] story: 117304 implement openklant 2.0 (#302) * wip * wip * wip * wip * improve gradle setup so that docker compose can be used for integration testing on macOS * * make filtering easier to apply to OpenKlant requests * fix integration tests not running on check/build * remove unnecessary Create* dto's * * separate contactgegevens api from klantinteracties api in OpenKlant Module * * more gradle test fixes * expand filtering with page * * added partijen path test * refactored some functions and class names * * wip PartijMutation * * add initial PartijMutation test * * add assertions to Partij Mutation test * * implement updatePartij mutation * * run ktlint * * refactor some tests * introduce initial DigitaleAdres related entities and queries * * change default loglevels for app to info * disable sql logging for app * fix DigitaleAdresType serialization * add authentication requirement to all openklant queries and mutations * add delete DigitaleAdres mutation * * revert splitting autoconfiguration * change base integration test image * * add missing queries * * changed return type of deleteUserDigitaleAdres mutation * integration tests for DigitaleAdres * fixed integration test ordering in test classe where it matters --- app/build.gradle.kts | 1 + app/src/main/resources/config/application.yml | 12 +- build.gradle.kts | 20 +- case/build.gradle.kts | 11 +- docker-resources/docker-compose-base-test.yml | 3 +- .../imports/openklant-2/database/init.sh | 5 + .../database/sql/1-add-authtoken.sql | 1 + .../database/sql/fill-data-on-startup.sh | 20 + .../imports/openklant-2/init/init.sh | 21 + form/build.gradle.kts | 11 +- gradle.properties | 2 +- gradle/testing.gradle.kts | 37 ++ settings.gradle.kts | 1 + zgw/openklant/build.gradle.kts | 50 ++ zgw/openklant/docker-compose-override.yml | 43 ++ zgw/openklant/gradle.properties | 17 + zgw/openklant/gradle/publishing.gradle.kts | 35 ++ zgw/openklant/licenseHeaderFile.template | 15 + .../OpenKlantAutoConfiguration.kt | 39 ++ .../OpenKlantGraphqlAutoConfiguration.kt | 54 ++ .../OpenKlantModuleConfiguration.kt | 42 ++ .../OpenKlant2KlantinteractiesClient.kt | 71 +++ .../openklant/client/domain/Actoren.kt | 50 ++ .../openklant/client/domain/Betrokkene.kt | 31 ++ .../client/domain/CategorieRelaties.kt | 33 ++ .../client/domain/DigitaleAdressen.kt | 55 ++ .../openklant/client/domain/KlantContacten.kt | 43 ++ .../client/domain/PartijIdentificatoren.kt | 55 ++ .../openklant/client/domain/Partijen.kt | 199 ++++++++ .../openklant/client/domain/ResultPage.kt | 35 ++ .../openklant/client/domain/Shared.kt | 478 ++++++++++++++++++ .../openklant/client/path/DigitaleAdressen.kt | 100 ++++ .../client/path/KlantInteractiesPath.kt | 29 ++ .../client/path/PartijIdentificatoren.kt | 99 ++++ .../openklant/client/path/Partijen.kt | 116 +++++ .../graphql/DigitaleAdresMutation.kt | 74 +++ .../openklant/graphql/DigitaleAdresQuery.kt | 38 ++ .../openklant/graphql/PartijMutation.kt | 57 +++ .../nlportal/openklant/graphql/PartijQuery.kt | 53 ++ .../graphql/domain/DigitaleAdresRequest.kt | 33 ++ .../graphql/domain/DigitaleAdresResponse.kt | 41 ++ .../graphql/domain/DigitaleAdresType.kt | 22 + .../openklant/graphql/domain/PartijRequest.kt | 58 +++ .../graphql/domain/PartijResponse.kt | 45 ++ .../openklant/graphql/domain/PartijType.kt | 29 ++ .../openklant/service/OpenKlant2Service.kt | 257 ++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../nl/nlportal/openklant/TestApplication.kt | 56 ++ .../nl/nlportal/openklant/TestHelper.kt | 262 ++++++++++ .../OpenKlant2KlantinteractiesClientTest.kt | 76 +++ .../client/path/OpenKlant2PartijenPathTest.kt | 108 ++++ .../OpenKlant2ModuleConfigurationTest.kt | 82 +++ .../OpenKlant2DigitaleAdresMutationIT.kt | 223 ++++++++ .../graphql/OpenKlant2DigitaleAdresQueryIT.kt | 157 ++++++ .../graphql/OpenKlant2PartijMutationIT.kt | 223 ++++++++ .../graphql/OpenKlant2PartijQueryIT.kt | 235 +++++++++ .../service/OpenKlant2ServiceTest.kt | 55 ++ .../src/test/resources/config/application.yml | 45 ++ .../graphql/createUserDigitaleAdres.gql | 8 + .../config/graphql/createUserPartij.gql | 16 + .../graphql/digitaleAdresIntrospection.gql | 7 + .../config/graphql/findUserPartij.gql | 89 ++++ .../config/graphql/getUserDigitaleAdresen.gql | 9 + .../config/graphql/getUserPartij.gql | 89 ++++ .../config/graphql/partijIntrospection.gql | 7 + .../config/graphql/updateUserPartij.gql | 16 + .../org.mockito.plugins.MockMaker | 1 + 67 files changed, 4279 insertions(+), 28 deletions(-) create mode 100755 docker-resources/imports/openklant-2/database/init.sh create mode 100644 docker-resources/imports/openklant-2/database/sql/1-add-authtoken.sql create mode 100755 docker-resources/imports/openklant-2/database/sql/fill-data-on-startup.sh create mode 100755 docker-resources/imports/openklant-2/init/init.sh create mode 100644 gradle/testing.gradle.kts create mode 100644 zgw/openklant/build.gradle.kts create mode 100644 zgw/openklant/docker-compose-override.yml create mode 100644 zgw/openklant/gradle.properties create mode 100644 zgw/openklant/gradle/publishing.gradle.kts create mode 100644 zgw/openklant/licenseHeaderFile.template create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantAutoConfiguration.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantGraphqlAutoConfiguration.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantModuleConfiguration.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClient.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Actoren.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Betrokkene.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/CategorieRelaties.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/DigitaleAdressen.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/KlantContacten.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/PartijIdentificatoren.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Partijen.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/ResultPage.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Shared.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/DigitaleAdressen.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/KlantInteractiesPath.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/PartijIdentificatoren.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/Partijen.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresMutation.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresQuery.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijMutation.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijQuery.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresRequest.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresResponse.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresType.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijRequest.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijResponse.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijType.kt create mode 100644 zgw/openklant/src/main/kotlin/nl/nlportal/openklant/service/OpenKlant2Service.kt create mode 100644 zgw/openklant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestApplication.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestHelper.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClientTest.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/path/OpenKlant2PartijenPathTest.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/configuration/OpenKlant2ModuleConfigurationTest.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresMutationIT.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresQueryIT.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijMutationIT.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijQueryIT.kt create mode 100644 zgw/openklant/src/test/kotlin/nl/nlportal/openklant/service/OpenKlant2ServiceTest.kt create mode 100644 zgw/openklant/src/test/resources/config/application.yml create mode 100644 zgw/openklant/src/test/resources/config/graphql/createUserDigitaleAdres.gql create mode 100644 zgw/openklant/src/test/resources/config/graphql/createUserPartij.gql create mode 100644 zgw/openklant/src/test/resources/config/graphql/digitaleAdresIntrospection.gql create mode 100644 zgw/openklant/src/test/resources/config/graphql/findUserPartij.gql create mode 100644 zgw/openklant/src/test/resources/config/graphql/getUserDigitaleAdresen.gql create mode 100644 zgw/openklant/src/test/resources/config/graphql/getUserPartij.gql create mode 100644 zgw/openklant/src/test/resources/config/graphql/partijIntrospection.gql create mode 100644 zgw/openklant/src/test/resources/config/graphql/updateUserPartij.gql create mode 100644 zgw/openklant/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 68dd71c6..d7e72ae2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { implementation(project(":product")) implementation(project(":form")) implementation(project(":zgw:taak")) + implementation(project(":zgw:openklant")) implementation(project(":zgw:zaken-api")) implementation(project(":zgw:catalogi-api")) implementation(project(":zgw:documenten-api")) diff --git a/app/src/main/resources/config/application.yml b/app/src/main/resources/config/application.yml index 1c6e87fe..abd30087 100644 --- a/app/src/main/resources/config/application.yml +++ b/app/src/main/resources/config/application.yml @@ -38,7 +38,7 @@ spring: jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect database: postgresql - show_sql: true + show_sql: false open-in-view: false properties: hibernate: @@ -69,9 +69,15 @@ graphql: logging: level: - org.springframework.amqp: DEBUG + root: INFO nl-portal: + config: + openklant: + enabled: true + properties: + klantinteracties-api-url: http://localhost:8007/klantinteracties/api/v1 + token: ac045222c9e7cde8120b48735560f9b920bb58cd zgw: catalogiapi: url: http://localhost:8001 @@ -169,4 +175,4 @@ nl-portal: shaInKey: de14f0e3-2ff0-45eb-95a6-1cdc35ca7a00 shaOutKey: de14f0e3-2ff0-45eb-95a6-1cdc35ca7a00 failureUrl: http://localhost:3000 - successUrl: http://localhost:3000 + successUrl: http://localhost:3000 \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e8f5a4b9..ed446bef 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,6 @@ import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension +import io.spring.gradle.dependencymanagement.org.codehaus.plexus.interpolation.os.Os.FAMILY_MAC +import org.apache.tools.ant.taskdefs.condition.Os import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile import java.net.URI @@ -122,6 +124,10 @@ subprojects { freeCompilerArgs.add("-Xjsr305=strict") freeCompilerArgs.add("-Xemit-jvm-type-annotations") } + val ktlintFormat: Task? by tasks + if (ktlintFormat != null) { + dependsOn(ktlintFormat) + } } println("Enabling Spring Boot Dependency Management in project ${project.name}...") @@ -134,10 +140,6 @@ subprojects { } } - tasks.withType { - useJUnitPlatform() - } - publishing { repositories { maven { @@ -208,6 +210,16 @@ subprojects { sign(publishing.publications["default"]) } } + + apply(from = "${rootProject.projectDir}/gradle/testing.gradle.kts") + + if (Os.isFamily(FAMILY_MAC)) { + println("Configure docker compose for macOs") + dockerCompose { + executable = "/usr/local/bin/docker-compose" + dockerExecutable = "/usr/local/bin/docker" + } + } } tasks.register("htmlDependencyReport") diff --git a/case/build.gradle.kts b/case/build.gradle.kts index ceabb618..22f1934a 100644 --- a/case/build.gradle.kts +++ b/case/build.gradle.kts @@ -1,6 +1,3 @@ -import io.spring.gradle.dependencymanagement.org.codehaus.plexus.interpolation.os.Os.FAMILY_MAC -import org.apache.tools.ant.taskdefs.condition.Os - /* * Copyright 2015-2023 Ritense BV, the Netherlands. * @@ -21,12 +18,8 @@ plugins { } dockerCompose { - if (Os.isFamily(FAMILY_MAC)) { - executable = "/usr/local/bin/docker-compose" - dockerExecutable = "/usr/local/bin/docker" - } - projectNamePrefix = "case" - isRequiredBy(tasks.getByName("test")) + setProjectName("$name-test") + isRequiredBy(tasks.getByName("integrationTest")) useComposeFiles.addAll("../docker-resources/docker-compose-base-test.yml", "docker-compose-override.yml") } diff --git a/docker-resources/docker-compose-base-test.yml b/docker-resources/docker-compose-base-test.yml index deaa43e7..ceb7784f 100644 --- a/docker-resources/docker-compose-base-test.yml +++ b/docker-resources/docker-compose-base-test.yml @@ -1,7 +1,6 @@ -version: '3.9' services: db: - image: "postgres" + image: postgres:15 environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=test diff --git a/docker-resources/imports/openklant-2/database/init.sh b/docker-resources/imports/openklant-2/database/init.sh new file mode 100755 index 00000000..26d2a8c1 --- /dev/null +++ b/docker-resources/imports/openklant-2/database/init.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo ">>>> Starting Open Klant data import script <<<<" + +sh /docker-entrypoint-initdb.d/sql/fill-data-on-startup.sh & \ No newline at end of file diff --git a/docker-resources/imports/openklant-2/database/sql/1-add-authtoken.sql b/docker-resources/imports/openklant-2/database/sql/1-add-authtoken.sql new file mode 100644 index 00000000..85907bc7 --- /dev/null +++ b/docker-resources/imports/openklant-2/database/sql/1-add-authtoken.sql @@ -0,0 +1 @@ +INSERT INTO public.token_tokenauth (id, token, contact_person, email, organization, last_modified, created, application, administration) VALUES (1, 'ac045222c9e7cde8120b48735560f9b920bb58cd', 'Admin', 'admin@example.com', '', '2024-09-06 07:26:53.703312 +00:00', '2024-09-06 07:26:53.703384 +00:00', '', ''); \ No newline at end of file diff --git a/docker-resources/imports/openklant-2/database/sql/fill-data-on-startup.sh b/docker-resources/imports/openklant-2/database/sql/fill-data-on-startup.sh new file mode 100755 index 00000000..6c346ba9 --- /dev/null +++ b/docker-resources/imports/openklant-2/database/sql/fill-data-on-startup.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +echo ">>>> Waiting until Open Klant has initialized the database <<<<" +while true +do + initiated=$(psql -U openklant -d openklant -t -A -c "SELECT EXISTS (SELECT table_name FROM information_schema.tables WHERE table_name = 'accounts_user');") + if [ "t" = "${initiated}" ] + then + echo "Running database setup scripts" + for file in /docker-entrypoint-initdb.d/sql/*.sql + do + echo "Running $file" + psql -U openklant -d openklant -f $file + done + break + else + echo "Open Klanten is not initiated yet" + sleep 5 + fi +done \ No newline at end of file diff --git a/docker-resources/imports/openklant-2/init/init.sh b/docker-resources/imports/openklant-2/init/init.sh new file mode 100755 index 00000000..4ab75166 --- /dev/null +++ b/docker-resources/imports/openklant-2/init/init.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Apply database migrations +>&2 echo "Apply database migrations" +python src/manage.py migrate + +exists=$(echo "from django.contrib.auth import get_user_model; User = get_user_model(); print(User.objects.filter(username='admin').exists())" | python src/manage.py shell) +if [ "False" = "${exists}" ] +then + echo "Creating user 'admin'" + python src/manage.py createsuperuser --username=admin --email=admin@example.com --noinput +else + echo "User 'admin' already exists" +fi +echo "Setting 'admin' password." +echo "from django.contrib.auth import get_user_model; User = get_user_model(); user = User.objects.get(username='admin'); user.set_password('admin'); user.save()" | python src/manage.py shell +echo "Loading fixtures" +python src/manage.py loaddata klantinteracties contactgegevens +echo "Finished setup" + +sh /start.sh \ No newline at end of file diff --git a/form/build.gradle.kts b/form/build.gradle.kts index 8165e798..bce27cd8 100644 --- a/form/build.gradle.kts +++ b/form/build.gradle.kts @@ -1,6 +1,3 @@ -import io.spring.gradle.dependencymanagement.org.codehaus.plexus.interpolation.os.Os.FAMILY_MAC -import org.apache.tools.ant.taskdefs.condition.Os - /* * Copyright 2015-2023 Ritense BV, the Netherlands. * @@ -21,12 +18,8 @@ plugins { } dockerCompose { - if (Os.isFamily(FAMILY_MAC)) { - executable = "/usr/local/bin/docker-compose" - dockerExecutable = "/usr/local/bin/docker" - } - projectNamePrefix = "form" - isRequiredBy(tasks.getByName("test")) + setProjectName("$name-test") + isRequiredBy(tasks.getByName("integrationTest")) useComposeFiles.addAll("../docker-resources/docker-compose-base-test.yml", "docker-compose-override.yml") } diff --git a/gradle.properties b/gradle.properties index 99528a88..ce3b27bb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,4 @@ dokkaVersion=1.9.20 org.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=2048m org.gradle.workers.max=10 -version=1.4.21-SNAPSHOT +version=1.4.21-SNAPSHOT \ No newline at end of file diff --git a/gradle/testing.gradle.kts b/gradle/testing.gradle.kts new file mode 100644 index 00000000..b99f0744 --- /dev/null +++ b/gradle/testing.gradle.kts @@ -0,0 +1,37 @@ +/* + * Copyright 2015-2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +tasks.register("integrationTest") { + group = "verification" + description = + """ + Composes docker containers and runs Tests tagged with "integration". + NB! Project root must contain a docker compose file with the following name: docker-compose-override.yml + """.trimIndent() + useJUnitPlatform { + includeTags("integration") + } +} + +tasks.named("test") { + useJUnitPlatform { + excludeTags("integration") + } +} + +tasks.named("check") { + dependsOn(tasks.getByName("integrationTest")) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index ebbd247f..d7185bfa 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -56,4 +56,5 @@ include( "zgw:taak", "zgw:zaken-api", "zgw:objectenapi", + "zgw:openklant", ) \ No newline at end of file diff --git a/zgw/openklant/build.gradle.kts b/zgw/openklant/build.gradle.kts new file mode 100644 index 00000000..f9fe885a --- /dev/null +++ b/zgw/openklant/build.gradle.kts @@ -0,0 +1,50 @@ +import org.springframework.boot.gradle.tasks.bundling.BootJar + +/* + * Copyright (c) 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +plugins { + kotlin("jvm") +} + +val isLib = true + +dockerCompose { + setProjectName("$name-test") + isRequiredBy(tasks.getByName("integrationTest")) + useComposeFiles.addAll("../../docker-resources/docker-compose-base-test.yml", "docker-compose-override.yml") +} + +dependencies { + api(project(":graphql")) + api(project(":portal-authentication")) + api(project(":zgw:common-ground-authentication")) + + testImplementation(project(":zgw:common-ground-authentication-test")) + testImplementation(TestDependencies.postgresql) + testImplementation("org.springframework.boot", "spring-boot-starter-test") + testImplementation("org.springframework.security", "spring-security-test") + testImplementation(TestDependencies.kotlinCoroutines) + testImplementation(TestDependencies.mockitoKotlin) + testImplementation(TestDependencies.okHttpMockWebserver) + testImplementation(TestDependencies.okHttp) +} + +val jar: Jar by tasks +val bootJar: BootJar by tasks +bootJar.enabled = false +jar.enabled = true + +apply(from = "gradle/publishing.gradle.kts") \ No newline at end of file diff --git a/zgw/openklant/docker-compose-override.yml b/zgw/openklant/docker-compose-override.yml new file mode 100644 index 00000000..efd4d53f --- /dev/null +++ b/zgw/openklant/docker-compose-override.yml @@ -0,0 +1,43 @@ +services: + + # openklant 2 + openklant-2: + image: maykinmedia/open-klant:2.1.0 + container_name: openklant-2-test + ports: + - "9007:8000" + depends_on: + - openklant-2-db + - redis + environment: + - DJANGO_SETTINGS_MODULE=openklant.conf.docker + - IS_HTTPS=no + - DB_NAME=openklant + - DB_USER=openklant + - DB_PASSWORD=openklant + - DB_HOST=openklant-2-db + - ALLOWED_HOSTS=* + - CACHE_DEFAULT=redis-test:6379/0 + - CACHE_AXES=redis-test:6379/0 + - SUBPATH=${SUBPATH:-/} + - SECRET_KEY=${SECRET_KEY:-django-insecure-f8s@b*ds4t84-q_2#c0j0506@!l2q6r5_pq5e!vm^_9c*#^66b} + - CELERY_BROKER_URL=redis://redis-test:6379/0 + - CELERY_RESULT_BACKEND=redis://redis-test:6379/0 + - DISABLE_2FA=true + volumes: + - ./imports/openklant-2/init:/app/init + command: sh /app/init/init.sh + + openklant-2-db: + image: postgres:15 + container_name: openklant-2-db-test + environment: + - POSTGRES_USER=openklant + - POSTGRES_PASSWORD=openklant + - POSTGRES_DB=openklant + volumes: + - ./imports/openklant-2/database:/docker-entrypoint-initdb.d/ + + redis: + image: redis:6.2.6 + container_name: redis-test \ No newline at end of file diff --git a/zgw/openklant/gradle.properties b/zgw/openklant/gradle.properties new file mode 100644 index 00000000..766b10fc --- /dev/null +++ b/zgw/openklant/gradle.properties @@ -0,0 +1,17 @@ +# +# Copyright (c) 2024 Ritense BV, the Netherlands. +# +# Licensed under EUPL, Version 1.2 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +isLib = true \ No newline at end of file diff --git a/zgw/openklant/gradle/publishing.gradle.kts b/zgw/openklant/gradle/publishing.gradle.kts new file mode 100644 index 00000000..faab44f7 --- /dev/null +++ b/zgw/openklant/gradle/publishing.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pluginManager.withPlugin("maven-publish") { + configure { + publications { + withType(MavenPublication::class.java) { + pom { + getName().set("OpenKlant module") + getDescription().set("This module enables NL Portal to interact with OpenKlant.") + developers { + developer { + getId().set("team-nl-portal") + getName().set("Team NL Portal") + getEmail().set("team-nl-portal@ritense.com") + } + } + } + } + } + } +} \ No newline at end of file diff --git a/zgw/openklant/licenseHeaderFile.template b/zgw/openklant/licenseHeaderFile.template new file mode 100644 index 00000000..156abaa9 --- /dev/null +++ b/zgw/openklant/licenseHeaderFile.template @@ -0,0 +1,15 @@ +/* + * Copyright $YEAR Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantAutoConfiguration.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantAutoConfiguration.kt new file mode 100644 index 00000000..5aaaa083 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantAutoConfiguration.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.autoconfigure + +import nl.nlportal.openklant.client.OpenKlant2KlantinteractiesClient +import nl.nlportal.openklant.service.OpenKlant2Service +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean + +@EnableConfigurationProperties( + OpenKlantModuleConfiguration::class, +) +class OpenKlantAutoConfiguration { + @Bean + @ConditionalOnMissingBean(OpenKlant2KlantinteractiesClient::class) + fun openKlant2Client(openklantModuleConfiguration: OpenKlantModuleConfiguration): OpenKlant2KlantinteractiesClient { + return OpenKlant2KlantinteractiesClient(openKlantConfigurationProperties = openklantModuleConfiguration.properties) + } + + @Bean + @ConditionalOnMissingBean(OpenKlant2Service::class) + fun openKlant2Service(openklant2Client: OpenKlant2KlantinteractiesClient): OpenKlant2Service { + return OpenKlant2Service(openklant2Client) + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantGraphqlAutoConfiguration.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantGraphqlAutoConfiguration.kt new file mode 100644 index 00000000..f7242e25 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantGraphqlAutoConfiguration.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.autoconfigure + +import com.expediagroup.graphql.server.operations.Mutation +import com.expediagroup.graphql.server.operations.Query +import nl.nlportal.openklant.graphql.DigitaleAdresMutation +import nl.nlportal.openklant.graphql.DigitaleAdresQuery +import nl.nlportal.openklant.graphql.PartijMutation +import nl.nlportal.openklant.graphql.PartijQuery +import nl.nlportal.openklant.service.OpenKlant2Service +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty +import org.springframework.context.annotation.Bean + +@ConditionalOnProperty(prefix = "nl-portal.config.openklant", name = ["enabled"], havingValue = "true") +class OpenKlantGraphqlAutoConfiguration { + @Bean + @ConditionalOnMissingBean(PartijQuery::class) + fun partijQuery(openKlant2Service: OpenKlant2Service): Query { + return PartijQuery(openKlant2Service) + } + + @Bean + @ConditionalOnMissingBean(PartijMutation::class) + fun partijMutation(openKlant2Service: OpenKlant2Service): Mutation { + return PartijMutation(openKlant2Service) + } + + @Bean + @ConditionalOnMissingBean(DigitaleAdresQuery::class) + fun digitaleAdresQuery(openKlant2Service: OpenKlant2Service): Query { + return DigitaleAdresQuery(openKlant2Service) + } + + @Bean + @ConditionalOnMissingBean(DigitaleAdresMutation::class) + fun digitaleAdresMutation(openKlant2Service: OpenKlant2Service): Mutation { + return DigitaleAdresMutation(openKlant2Service) + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantModuleConfiguration.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantModuleConfiguration.kt new file mode 100644 index 00000000..0ebb196e --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/autoconfigure/OpenKlantModuleConfiguration.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.autoconfigure + +import org.springframework.boot.context.properties.ConfigurationProperties +import java.net.URI + +@ConfigurationProperties(prefix = "nl-portal.config.openklant") +data class OpenKlantModuleConfiguration( + var enabled: Boolean = false, + var properties: OpenKlantConfigurationProperties, +) { + init { + if (enabled) { + requireNotNull(properties.klantinteractiesApiUrl) { + "OpenKlant Klantinteracties API URL not configured" + } + requireNotNull(properties.token) { + "OpenKlant token not configured" + } + } + } + + data class OpenKlantConfigurationProperties( + var contactgegevensApiUrl: URI? = null, + var klantinteractiesApiUrl: URI? = null, + var token: String? = null, + ) +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClient.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClient.kt new file mode 100644 index 00000000..b27f4854 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClient.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client + +import io.netty.handler.logging.LogLevel.TRACE +import nl.nlportal.core.util.Mapper +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration.OpenKlantConfigurationProperties +import nl.nlportal.openklant.client.path.KlantInteractiesPath +import org.springframework.http.client.reactive.ReactorClientHttpConnector +import org.springframework.http.codec.json.Jackson2JsonDecoder +import org.springframework.http.codec.json.Jackson2JsonEncoder +import org.springframework.web.reactive.function.client.ExchangeStrategies +import org.springframework.web.reactive.function.client.WebClient +import reactor.netty.http.client.HttpClient +import reactor.netty.transport.logging.AdvancedByteBufFormat.TEXTUAL +import kotlin.reflect.full.primaryConstructor + +class OpenKlant2KlantinteractiesClient(private val openKlantConfigurationProperties: OpenKlantConfigurationProperties) { + inline fun path(): P { + return P::class.primaryConstructor!!.call(this) + } + + fun webClient(): WebClient { + return webclientBuilder + .baseUrl(openKlantConfigurationProperties.klantinteractiesApiUrl.toString()) + .defaultHeader("Accept-Crs", "EPSG:4326") + .defaultHeader("Content-Crs", "EPSG:4326") + .defaultHeader("Authorization", "Token ${openKlantConfigurationProperties.token}") + .build() + } + + private val webclientBuilder = + WebClient.builder() + .clientConnector( + ReactorClientHttpConnector( + HttpClient.create().wiretap( + "reactor.netty.http.client.HttpClient", + TRACE, + TEXTUAL, + ), + ), + ) + .exchangeStrategies( + ExchangeStrategies.builder() + .codecs { configurer -> + with(configurer.defaultCodecs()) { + maxInMemorySize(16 * 1024 * 1024) + jackson2JsonEncoder( + Jackson2JsonEncoder(Mapper.get()), + ) + jackson2JsonDecoder( + Jackson2JsonDecoder(Mapper.get()), + ) + } + } + .build(), + ) +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Actoren.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Actoren.kt new file mode 100644 index 00000000..2b36acfc --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Actoren.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonValue +import java.util.UUID + +@JsonIgnoreProperties(ignoreUnknown = true) +data class Actor( + @JsonInclude(JsonInclude.Include.NON_NULL) + val uuid: UUID? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val url: String? = null, + val naam: String, + val soortActor: SoortActor, + val indicatieActief: Boolean? = null, + val actoridentificator: OpenKlant2Identificator? = null, +) { + init { + require(naam == null || naam.length in 1..200) { "Actor name has to be between 1 and 200 characters long" } + } +} + +enum class SoortActor( + @JsonValue private val value: String, +) { + MEDEWERKER("medewerker"), + GEAUTOMATISEERDE_ACTOR("geautomatiseerdeActor"), + ORGANISATORISCHE_EENHEID("organisatorischeEenheid"), + ; + + override fun toString(): String { + return this.value + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Betrokkene.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Betrokkene.kt new file mode 100644 index 00000000..a369267d --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Betrokkene.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +data class Betrokkene( + val bezoekadres: OpenKlant2Adres? = null, + val contactnaam: Contactnaam? = null, + val correspondentieadres: OpenKlant2Adres? = null, + val digitaleAdressen: List, + val hadKlantcontact: OpenKlant2ForeignKey, + val initiator: Boolean, + val organisatienaam: String, + val rol: String, + val url: String, + val uuid: String, + val volledigeNaam: String, + val wasPartij: OpenKlant2ForeignKey? = null, +) \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/CategorieRelaties.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/CategorieRelaties.kt new file mode 100644 index 00000000..0d9fbada --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/CategorieRelaties.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +import java.time.LocalDate + +data class CategorieRelatie( + val beginDatum: LocalDate? = null, + val categorie: Categorie? = null, + val eindDatum: LocalDate? = null, + val partij: OpenKlant2ForeignKey? = null, + val url: String, + val uuid: String, +) + +data class Categorie( + val naam: String, + val url: String, + val uuid: String, +) \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/DigitaleAdressen.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/DigitaleAdressen.kt new file mode 100644 index 00000000..c67f5047 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/DigitaleAdressen.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonValue +import java.util.UUID + +@JsonIgnoreProperties(ignoreUnknown = true) +data class OpenKlant2DigitaleAdres( + val adres: String, + val omschrijving: String, + val soortDigitaalAdres: String, + @JsonInclude(JsonInclude.Include.NON_NULL) + val url: String? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val uuid: UUID? = null, + val verstrektDoorBetrokkene: OpenKlant2UUID? = null, + val verstrektDoorPartij: OpenKlant2UUID? = null, +) { + init { + require(adres.length <= 80) { + "adres can't be longer than 10 characters." + } + require(omschrijving.length <= 40) { + "omschrijving can't be longer than 10 characters." + } + require(soortDigitaalAdres.length <= 255) { + "soortDigitaalAdres can't be longer than 10 characters." + } + } +} + +enum class OpenKlant2DigitaleAdressenFilters( + @JsonValue val value: String, +) : OpenKlant2Filters { + PAGE("page"), + ; + + override fun toString() = this.value +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/KlantContacten.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/KlantContacten.kt new file mode 100644 index 00000000..a8f9649f --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/KlantContacten.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +data class HadKlantcontact( + val gingOverOnderwerpobjecten: List, + val hadBetrokkenActoren: List, + val hadBetrokkenen: List, + val indicatieContactGelukt: Boolean, + val inhoud: String, + val kanaal: String, + val leiddeTotInterneTaken: List, + val nummer: String, + val omvatteBijlagen: List, + val onderwerp: String, + val plaatsgevondenOp: String, + val taal: String, + val url: String, + val uuid: String, + val vertrouwelijk: Boolean, +) + +data class HadBetrokkenActoren( + val actoridentificator: OpenKlant2Identificator, + val indicatieActief: Boolean, + val naam: String, + val soortActor: String, + val url: String, + val uuid: String, +) \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/PartijIdentificatoren.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/PartijIdentificatoren.kt new file mode 100644 index 00000000..2e45c03a --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/PartijIdentificatoren.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonValue +import java.util.UUID + +data class OpenKlant2PartijIdentificator( + @JsonInclude(JsonInclude.Include.NON_NULL) + val uuid: UUID? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val url: String? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val anderePartijIdentificator: String? = null, + val identificeerdePartij: OpenKlant2IdentificeerdePartij? = null, + val partijIdentificator: OpenKlant2Identificator? = null, +) { + init { + require(anderePartijIdentificator == null || anderePartijIdentificator.length <= 200) { + "Andere partij indetificator can't be longer than 200 characters" + } + } +} + +data class OpenKlant2IdentificeerdePartij( + val uuid: UUID, +) + +enum class OpenKlant2PartijIdentificatorenFilters( + @JsonValue val value: String, +) : OpenKlant2Filters { + ANDERE_PARTIJ_IDENTIFICATOR("andere_partij_identificator"), + PAGE("page"), + PARTIJ_IDENTIFICATOR_CODE_OBJECTTYPE("partij_identificator_code_objecttype"), + PARTIJ_IDENTIFICATOR_CODE_SOORT_OBJECT_ID("partij_identificator_code_soort_object_id"), + PARTIJ_IDENTIFICATOR_OBJECT_ID("partij_identificator_object_id"), + PARTIJ_IDENTIFICATOR_CODE_REGISTER("partij_identificator_code_register"), + ; + + override fun toString() = this.value +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Partijen.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Partijen.kt new file mode 100644 index 00000000..528da594 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Partijen.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +import com.expediagroup.graphql.generator.annotations.GraphQLDescription +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.annotation.JsonSubTypes +import com.fasterxml.jackson.annotation.JsonSubTypes.Type +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.JsonValue +import nl.nlportal.commonground.authentication.BedrijfAuthentication +import nl.nlportal.commonground.authentication.BurgerAuthentication +import nl.nlportal.commonground.authentication.CommonGroundAuthentication +import java.time.LocalDate +import java.util.Locale +import java.util.UUID + +@GraphQLDescription(value = "A Type that represents a Klantinteracties API Partij object") +@JsonIgnoreProperties(ignoreUnknown = true) +data class OpenKlant2Partij( + @JsonInclude(JsonInclude.Include.NON_NULL) + val betrokkenen: List? = null, + val bezoekadres: OpenKlant2Adres? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val categorieRelaties: List? = null, + val correspondentieadres: OpenKlant2Adres? = null, + val digitaleAdressen: List? = null, + val indicatieActief: Boolean, + val indicatieGeheimhouding: Boolean, + @JsonInclude(JsonInclude.Include.NON_NULL) + val interneNotitie: String? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val nummer: String? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val partijIdentificatoren: List? = null, + val rekeningnummers: List? = null, + val soortPartij: SoortPartij, + @JsonInclude(JsonInclude.Include.NON_NULL) + val url: String? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val uuid: UUID? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val vertegenwoordigden: List? = null, + val voorkeursDigitaalAdres: OpenKlant2ForeignKey? = null, + val voorkeursRekeningnummer: OpenKlant2ForeignKey? = null, + @JsonInclude(JsonInclude.Include.NON_NULL) + val voorkeurstaal: String? = null, + @JsonTypeInfo(include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "soortPartij", use = JsonTypeInfo.Id.NAME) + @JsonSubTypes( + Type(PersoonsIdentificatie::class, name = "persoon"), + Type(OrganisatieIdentificatie::class, name = "organisatie"), + Type(ContactpersoonIdentificatie::class, name = "contactpersoon"), + ) + val partijIdentificatie: PartijIdentificatie, + @JsonProperty("_expand") + @JsonInclude(JsonInclude.Include.NON_NULL) + val expand: PartijExpand? = null, +) { + init { + require(voorkeurstaal == null || voorkeurstaal in Locale.getAvailableLocales().map { it.isO3Language }) { + "Voorkeurstaal must be a valid Language in the ISO 639-2/B format" + } + require(interneNotitie == null || interneNotitie.length <= 1000) { + "Interne notitie can't be longer than 1000 characters." + } + require(nummer == null || nummer.length <= 10) { + "Nummer can't be longer than 10 characters." + } + } +} + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +open class PersoonsIdentificatie( + val contactnaam: Contactnaam? = null, + val volledigeNaam: String? = null, +) : PartijIdentificatie + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +data class ContactpersoonIdentificatie( + val uuid: UUID? = null, + val werkteVoorPartij: OpenKlant2ForeignKey? = null, + val contactnaam: Contactnaam? = null, + val volledigeNaam: String? = null, +) : PartijIdentificatie + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +data class OrganisatieIdentificatie( + val naam: String? = null, +) : PartijIdentificatie + +interface PartijIdentificatie + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +data class Contactnaam( + val voorletters: String? = null, + val voornaam: String? = null, + val voorvoegselAchternaam: String? = null, + val achternaam: String? = null, +) + +data class PartijExpand( + val betrokkenen: List? = null, + val hadKlantcontact: List? = null, + val categorieRelaties: List? = null, + val digitaleAdressen: List? = null, +) + +enum class PartijExpandOptions( + @JsonValue val value: String, +) : OpenKlant2Filters { + BETROKKENEN("betrokkenen"), + HAD_KLANTCONTACT("hadKlantcontact"), + CATEGORIE_RELATIES("categorieRelaties"), + DIGITALE_ADRESSEN("digitaleAdressen"), + ; + + override fun toString() = value +} + +data class CategorieRelatieForeignKey( + val beginDatum: LocalDate? = null, + val categorieNaam: String, + val eindDatum: LocalDate? = null, + val url: String, + val uuid: String, +) + +enum class OpenKlant2PartijenFilters( + @JsonValue val value: String, +) : OpenKlant2Filters { + VERTEGENWOORDIGDE_PARTIJ_UUID("vertegenwoordigde_partij__uuid"), + VERTEGENWOORDIGDE_PARTIJ_URL("vertegenwoordigde_partij__url"), + PARTIJ_IDENTIFICATOR_CODE_OBJECTTYPE("partij_identificator__code_objecttype"), + PARTIJ_IDENTIFICATOR_CODE_SOORT_OBJECT_ID("partij_identificator__code_soort_object_id"), + PARTIJ_IDENTIFICATOR_OBJECT_ID("partij_identificator__object_id"), + PARTIJ_IDENTIFICATOR_CODE_REGISTER("partij_identificator__code_register"), + CATEGORIERELATIE__CATEGORIE_NAAM("categorierelatie__categorie__naam"), + NUMMER("nummer"), + PAGE("page"), + EXPAND("expand"), + INDICATIE_GEHEIMHOUDING("indicatie_geheimhouding"), + INDICATIE_ACTIEF("indicatie_actief"), + SOORT_PARTIJ("soort_partij"), + BEZOEKADRES_NUMMERAANDUIDING_ID("bezoekadres_nummeraanduiding_id"), + BEZOEKADRES_ADRESREGEL1("bezoekadres_adresregel1"), + BEZOEKADRES_ADRESREGEL2("bezoekadres_adresregel2"), + BEZOEKADRES_ADRESREGEL3("bezoekadres_adresregel3"), + BEZOEKADRES_LAND("bezoekadres_land"), + CORRESPONDENTIEADRES_NUMMERAANDUIDING_ID("correspondentieadres_nummeraanduiding_id"), + CORRESPONDENTIEADRES_ADRESREGEL1("correspondentieadres_adresregel1"), + CORRESPONDENTIEADRES_ADRESREGEL2("correspondentieadres_adresregel2"), + CORRESPONDENTIEADRES_ADRESREGEL3("correspondentieadres_adresregel3"), + CORRESPONDENTIEADRES_LAND("correspondentieadres_land"), + ; + + override fun toString(): String { + return this.value + } +} + +enum class SoortPartij( + @JsonValue val value: String, +) { + PERSOON("persoon"), + ORGANISATIE("organisatie"), + CONTACTPERSOON("contactpersoon"), + ; + + override fun toString(): String { + return this.value + } +} + +fun CommonGroundAuthentication.asSoortPartij(): String { + return when (this) { + is BurgerAuthentication -> "persoon" + is BedrijfAuthentication -> "organisatie" + else -> "contactpersoon" + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/ResultPage.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/ResultPage.kt new file mode 100644 index 00000000..3d7337b2 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/ResultPage.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +import java.net.URI + +data class ResultPage( + val count: Int, + val next: URI? = null, + val previous: URI? = null, + val results: List, +) { + fun getNextPageNumber(): Int? { + return next + ?.query + ?.split("&") + ?.asSequence() + ?.map { Pair(it.substringBefore("="), it.substringAfter("=")) } + ?.firstOrNull { it.first == "page" } + ?.second?.toIntOrNull() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Shared.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Shared.kt new file mode 100644 index 00000000..89084202 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/domain/Shared.kt @@ -0,0 +1,478 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.domain + +import com.expediagroup.graphql.generator.annotations.GraphQLIgnore +import com.fasterxml.jackson.annotation.JsonValue +import java.util.UUID + +data class OpenKlant2ForeignKey( + val url: String, + val uuid: UUID, +) + +data class OpenKlant2UUID( + val uuid: UUID, +) + +data class OpenKlant2Identificator( + val objectId: String = "", + val codeObjecttype: String = "", + val codeRegister: String = "", + val codeSoortObjectId: String = "", +) { + init { + require(objectId.length <= 200) { + "ObjectID can't be longer than 200 characters" + } + require(codeObjecttype.length <= 200) { + "ObjectID can't be longer than 200 characters" + } + require(codeRegister.length <= 200) { + "ObjectID can't be longer than 200 characters" + } + require(codeSoortObjectId.length <= 200) { + "ObjectID can't be longer than 200 characters" + } + } +} + +data class OpenKlant2Adres( + val adresregel1: String? = null, + val adresregel2: String? = null, + val adresregel3: String? = null, + val land: OpenKlant2Landcode? = null, + val nummeraanduidingId: String? = null, +) { + init { + require(nummeraanduidingId == null || nummeraanduidingId.length <= 255) { + "Adresregel1 can't be more than 255 characters long." + } + require(adresregel1 == null || adresregel1.length <= 80) { + "Adresregel1 can't be more than 255 characters long." + } + require(adresregel2 == null || adresregel2.length <= 80) { + "Adresregel1 can't be more than 255 characters long." + } + require(adresregel3 == null || adresregel3.length <= 80) { + "Adresregel1 can't be more than 255 characters long." + } + } +} + +@GraphQLIgnore +interface OpenKlant2Filters + +enum class OpenKlant2Landcode( + @JsonValue val landcode: String, + val landnaam: String, +) { + CANADA("5001", "Canada"), + FRANKRIJK("5002", "Frankrijk"), + ZWITSERLAND("5003", "Zwitserland"), + RHODESIE("5004", "Rhodesië"), + MALAWI("5005", "Malawi"), + CUBA("5006", "Cuba"), + SURINAME("5007", "Suriname"), + TUNESIE("5008", "Tunesië"), + OOSTENRIJK("5009", "Oostenrijk"), + BELGIE("5010", "België"), + BOTSWANA("5011", "Botswana"), + IRAN("5012", "Iran"), + NIEUWZEELAND("5013", "Nieuw-Zeeland"), + ZUIDAFRIKA("5014", "Zuid-Afrika"), + DENEMARKEN("5015", "Denemarken"), + NOORDJEMEN("5016", "Noord-Jemen"), + HONGARIJE("5017", "Hongarije"), + SAOEDIARABIE("5018", "Saoedi-Arabië"), + LIBERIA("5019", "Liberia"), + ETHIOPIE("5020", "Ethiopië"), + CHILI("5021", "Chili"), + MAROKKO("5022", "Marokko"), + TOGO("5023", "Togo"), + GHANA("5024", "Ghana"), + LAOS("5025", "Laos"), + ANGOLA("5026", "Angola"), + FILIPIJNEN("5027", "Filipijnen"), + ZAMBIA("5028", "Zambia"), + MALI("5029", "Mali"), + IVOORKUST("5030", "Ivoorkust"), + BURMA("5031", "Burma"), + MONACO("5032", "Monaco"), + COLOMBIA("5033", "Colombia"), + ALBANIE("5034", "Albanië"), + KAMEROEN("5035", "Kameroen"), + ZUIDVIETNAM("5036", "Zuid-Vietnam"), + SINGAPORE("5037", "Singapore"), + PARAGUAY("5038", "Paraguay"), + ZWEDEN("5039", "Zweden"), + CYPRUS("5040", "Cyprus"), + AUSTRALISCH_NIEUWGUINEA("5041", "Australisch Nieuw-Guinea"), + BRUNEI("5042", "Brunei"), + IRAK("5043", "Irak"), + MAURITIUS("5044", "Mauritius"), + VATICAANSTAD("5045", "Vaticaanstad"), + KASHMIR("5046", "Kashmir"), + MYANMAR("5047", "Myanmar"), + JEMEN("5048", "Jemen"), + SLOVENIE("5049", "Slovenië"), + ZAIRE("5050", "Zaïre"), + KROATIE("5051", "Kroatië"), + TAIWAN("5052", "Taiwan"), + RUSLAND("5053", "Rusland"), + ARMENIE("5054", "Armenië"), + ASCENSION("5055", "Ascension"), + AZOREN("5056", "Azoren"), + BAHREIN("5057", "Bahrein"), + BHUTAN("5058", "Bhutan"), + BRITSE_ANTILLEN("5059", "Britse Antillen"), + COMOREN("5060", "Comoren"), + FALKLANDEILANDEN("5061", "Falklandeilanden"), + FRANSGUYANA("5062", "Frans-Guyana"), + FRANSSOMALILAND("5063", "Frans-Somaliland"), + GILBERT_EN_ELLICEEILANDEN("5064", "Gilbert- en Ellice-eilanden"), + GROENLAND("5065", "Groenland"), + GUADELOUPE("5066", "Guadeloupe"), + KAAPVERDISCHE_EILANDEN("5067", "Kaapverdische Eilanden"), + MACAU("5068", "Macau"), + MARTINIQUE("5069", "Martinique"), + MOZAMBIQUE("5070", "Mozambique"), + PITCAIRNEILANDEN("5071", "Pitcairneilanden"), + GUINEEBISSAU("5072", "Guinee-Bissau"), + REUNION("5073", "Réunion"), + SAINT_PIERRE_EN_MIQUELON("5074", "Saint Pierre en Miquelon"), + SEYCHELLEN_EN_AMIRANTEN("5075", "Seychellen en Amiranten"), + TONGA("5076", "Tonga"), + WALLIS_EN_FUTUNA("5077", "Wallis en Futuna"), + ZUIDWESTAFRIKA("5078", "Zuidwest-Afrika"), + FRANSINDIE("5079", "Frans-Indië"), + JOHNSTON("5080", "Johnston"), + KEDAH("5081", "Kedah"), + KELANTAN("5082", "Kelantan"), + MALAKKA("5083", "Malakka"), + MAYOTTE("5084", "Mayotte"), + NEGRI_SEMBILAN("5085", "Negri Sembilan"), + PAHANG("5086", "Pahang"), + PERAK("5087", "Perak"), + PERLIS("5088", "Perlis"), + PORTUGEESINDIE("5089", "Portugees-Indië"), + SELANGOR("5090", "Selangor"), + SIKKIM("5091", "Sikkim"), + SAINT_VINCENT_EN_DE_GRENADINES("5092", "Saint Vincent en de Grenadines"), + SPITSBERGEN("5093", "Spitsbergen"), + TRENGGANU("5094", "Trengganu"), + ARUBA("5095", "Aruba"), + BURKINA_FASO("5096", "Burkina Faso"), + AZERBEIDZJAN("5097", "Azerbeidzjan"), + BELARUS("5098", "Belarus"), + KAZACHSTAN("5099", "Kazachstan"), + MACEDONIE("5100", "Macedonië"), + TIMOR_LESTE("5101", "Timor Leste"), + SERVIE_EN_MONTENEGRO("5102", "Servië en Montenegro"), + SERVIE("5103", "Servië"), + MONTENEGRO("5104", "Montenegro"), + KOSOVO("5105", "Kosovo"), + BONAIRE("5106", "Bonaire"), + CURACAO("5107", "Curaçao"), + SABA("5108", "Saba"), + SINT_EUSTATIUS("5109", "Sint Eustatius"), + SINT_MAARTEN("5110", "Sint Maarten"), + ZUIDSOEDAN("5111", "Zuid-Soedan"), + GAZASTROOK_EN_WESTELIJKE_JORDAANOEVER("5112", "Gazastrook en Westelijke Jordaanoever"), + REPUBLIEK_NOORDMACEDONIE("5113", "Republiek Noord-Macedonië"), + MOLDAVIE("6000", "Moldavië"), + BURUNDI("6001", "Burundi"), + FINLAND("6002", "Finland"), + GRIEKENLAND("6003", "Griekenland"), + GUATEMALA("6004", "Guatemala"), + NIGERIA("6005", "Nigeria"), + LIBIE("6006", "Libië"), + IERLAND("6007", "Ierland"), + BRAZILIE("6008", "Brazilië"), + RWANDA("6009", "Rwanda"), + VENEZUELA("6010", "Venezuela"), + IJSLAND("6011", "IJsland"), + LIECHTENSTEIN("6012", "Liechtenstein"), + SOMALIE("6013", "Somalië"), + VERENIGDE_STATEN_VAN_AMERIKA("6014", "Verenigde Staten van Amerika"), + BOLIVIA("6015", "Bolivia"), + AUSTRALIE("6016", "Australië"), + JAMAICA("6017", "Jamaica"), + LUXEMBURG("6018", "Luxemburg"), + TSJAAD("6019", "Tsjaad"), + MAURITANIE("6020", "Mauritanië"), + KIRGIZIE("6021", "Kirgizië"), + CHINA("6022", "China"), + AFGHANISTAN("6023", "Afghanistan"), + INDONESIE("6024", "Indonesië"), + GUYANA("6025", "Guyana"), + NOORDVIETNAM("6026", "Noord-Vietnam"), + NOORWEGEN("6027", "Noorwegen"), + SAN_MARINO("6028", "San Marino"), + DUITSLAND("6029", "Duitsland"), + NEDERLAND("6030", "Nederland"), + CAMBODJA("6031", "Cambodja"), + FIJI("6032", "Fiji"), + BAHAMAS("6033", "Bahama's"), + ISRAEL("6034", "Israël"), + NEPAL("6035", "Nepal"), + ZUIDKOREA("6036", "Zuid-Korea"), + SPANJE("6037", "Spanje"), + OEKRAINE("6038", "Oekraïne"), + VERENIGD_KONINKRIJK("6039", "Verenigd Koninkrijk"), + NIGER("6040", "Niger"), + HAITI("6041", "Haïti"), + JORDANIE("6042", "Jordanië"), + TURKIJE("6043", "Turkije"), + TRINIDAD_EN_TOBAGO("6044", "Trinidad en Tobago"), + JOEGOSLAVIE("6045", "Joegoslavië"), + OPPERVOLTA("6046", "Opper-Volta"), + ALGERIJE("6047", "Algerije"), + GABON("6048", "Gabon"), + NOORDKOREA("6049", "Noord-Korea"), + OEZBEKISTAN("6050", "Oezbekistan"), + SIERRA_LEONE("6051", "Sierra Leone"), + BRITSHONDURAS("6052", "Brits-Honduras"), + CANARISCHE_EILANDEN("6053", "Canarische Eilanden"), + FRANSPOLYNESIE("6054", "Frans-Polynesië"), + GIBRALTAR("6055", "Gibraltar"), + PORTUGEESTIMOR("6056", "Portugees-Timor"), + TADZJIKISTAN("6057", "Tadzjikistan"), + BRITSE_SALOMONSEILANDEN("6058", "Britse Salomonseilanden"), + SAO_TOME_EN_PRINCIPE("6059", "São Tomé en Principe"), + SINTHELENA("6060", "Sint-Helena"), + TRISTAN_DA_CUNHA("6061", "Tristan Da Cunha"), + WESTSAMOA("6062", "West-Samoa"), + TURKMENISTAN("6063", "Turkmenistan"), + GEORGIE("6064", "Georgië"), + BOSNIEHERZEGOVINA("6065", "Bosnië-Herzegovina"), + TSJECHIE("6066", "Tsjechië"), + SLOWAKIJE("6067", "Slowakije"), + FEDERALE_REPUBLIEK_JOEGOSLAVIE("6068", "Federale Republiek Joegoslavië"), + DEMOCRATISCHE_REPUBLIEK_CONGO("6069", "Democratische Republiek Congo"), + UGANDA("7001", "Uganda"), + KENYA("7002", "Kenya"), + MALTA("7003", "Malta"), + BARBADOS("7004", "Barbados"), + ANDORRA("7005", "Andorra"), + MEXICO("7006", "Mexico"), + COSTA_RICA("7007", "Costa Rica"), + GAMBIA("7008", "Gambia"), + SYRIE("7009", "Syrië"), + NEDERLANDSE_ANTILLEN("7011", "Nederlandse Antillen"), + ZUIDJEMEN("7012", "Zuid-Jemen"), + EGYPTE("7014", "Egypte"), + ARGENTINIE("7015", "Argentinië"), + LESOTHO("7016", "Lesotho"), + HONDURAS("7017", "Honduras"), + NICARAGUA("7018", "Nicaragua"), + PAKISTAN("7020", "Pakistan"), + SENEGAL("7021", "Senegal"), + DAHOMEY("7023", "Dahomey"), + BULGARIJE("7024", "Bulgarije"), + MALEISIE("7026", "Maleisië"), + DOMINICAANSE_REPUBLIEK("7027", "Dominicaanse Republiek"), + POLEN("7028", "Polen"), + RUSLAND_OUD("7029", "Rusland (oud)"), + BRITSE_MAAGDENEILANDEN("7030", "Britse Maagdeneilanden"), + TANZANIA("7031", "Tanzania"), + EL_SALVADOR("7032", "El Salvador"), + SRI_LANKA("7033", "Sri Lanka"), + SOEDAN("7034", "Soedan"), + JAPAN("7035", "Japan"), + HONGKONG("7036", "Hongkong"), + PANAMA("7037", "Panama"), + URUGUAY("7038", "Uruguay"), + ECUADOR("7039", "Ecuador"), + GUINEE("7040", "Guinee"), + MALDIVEN("7041", "Maldiven"), + THAILAND("7042", "Thailand"), + LIBANON("7043", "Libanon"), + ITALIE("7044", "Italië"), + KOEWEIT("7045", "Koeweit"), + INDIA("7046", "India"), + ROEMENIE("7047", "Roemenië"), + TSJECHOSLOWAKIJE("7048", "Tsjecho-Slowakije"), + PERU("7049", "Peru"), + PORTUGAL("7050", "Portugal"), + OMAN("7051", "Oman"), + MONGOLIE("7052", "Mongolië"), + SAMOA("7053", "Samoa"), + VERENIGDE_ARABISCHE_EMIRATEN("7054", "Verenigde Arabische Emiraten"), + TIBET("7055", "Tibet"), + NAURU("7057", "Nauru"), + NEDERLANDS_NIEUWGUINEA("7058", "Nederlands Nieuw-Guinea"), + TANGANYIKA("7059", "Tanganyika"), + PALESTINA("7060", "Palestina"), + BRITS_WESTINDIE("7062", "Brits West-Indië"), + PORTUGEESAFRIKA("7063", "Portugees-Afrika"), + LETLAND("7064", "Letland"), + ESTLAND("7065", "Estland"), + LITOUWEN("7066", "Litouwen"), + BRITSAFRIKA("7067", "Brits-Afrika"), + BELGISCHCONGO("7068", "Belgisch-Congo"), + BRITSINDIE("7070", "Brits-Indië"), + NOORDRHODESIE("7071", "Noord-Rhodesië"), + ZUIDRHODESIE("7072", "Zuid-Rhodesië"), + SAARLAND("7073", "Saarland"), + FRANS_INDOCHINA("7074", "Frans Indochina"), + BRITS_WESTBORNEO("7075", "Brits West-Borneo"), + GOUDKUST("7076", "Goudkust"), + RAS_ALKHAIMAH("7077", "Ras al-Khaimah"), + FRANSCONGO("7079", "Frans-Congo"), + SIAM("7080", "Siam"), + BRITS_OOSTAFRIKA("7082", "Brits Oost-Afrika"), + BRITS_NOORDBORNEO("7083", "Brits Noord-Borneo"), + BANGLADESH("7084", "Bangladesh"), + DUITSE_DEMOCRATISCHE_REPUBLIEK("7085", "Duitse Democratische Republiek"), + MADEIRAEILANDEN("7087", "Madeira-eilanden"), + AMERIKAANSE_MAAGDENEILANDEN("7088", "Amerikaanse Maagdeneilanden"), + AUSTRALISCHE_SALOMONSEILANDEN("7089", "Australische Salomonseilanden"), + SPAANSE_SAHARA("7091", "Spaanse Sahara"), + CAYMANEILANDEN("7092", "Caymaneilanden"), + CAICOSEILANDEN("7093", "Caicoseilanden"), + TURKSEILANDEN("7094", "Turkseilanden"), + BRITS_ANTARCTISCH_TERRITORIUM("7095", "Brits Antarctisch Territorium"), + BRITS_INDISCHE_OCEAANTERRITORIUM("7096", "Brits Indische Oceaanterritorium"), + COOKEILANDEN("7097", "Cookeilanden"), + TOKELAU("7098", "Tokelau"), + NIEUWCALEDONIE("7099", "Nieuw-Caledonië"), + HAWAIIEILANDEN("8000", "Hawaii-eilanden"), + GUAM("8001", "Guam"), + AMERIKAANSSAMOA("8002", "Amerikaans-Samoa"), + MIDWAY("8003", "Midway"), + RIUKIUEILANDEN("8004", "Riukiu-eilanden"), + WAKE("8005", "Wake"), + PACIFICEILANDEN("8006", "Pacific-eilanden"), + GRENADA("8008", "Grenada"), + MARIANEN("8009", "Marianen"), + CABINDA("8010", "Cabinda"), + CANTON_EN_ENDERBURY("8011", "Canton en Enderbury"), + CHRISTMASEILAND("8012", "Christmaseiland"), + COCOSEILANDEN("8013", "Cocoseilanden"), + FAEROER("8014", "Faeröer"), + MONTSERRAT("8015", "Montserrat"), + NORFOLK("8016", "Norfolk"), + BELIZE("8017", "Belize"), + TASMANIE("8018", "Tasmanië"), + TURKS_EN_CAICOSEILANDEN("8019", "Turks- en Caicoseilanden"), + PUERTO_RICO("8020", "Puerto Rico"), + PAPOEANIEUWGUINEA("8021", "Papoea-Nieuw-Guinea"), + SALOMONSEILANDEN("8022", "Salomonseilanden"), + BENIN("8023", "Benin"), + VIETNAM("8024", "Vietnam"), + KAAPVERDIE("8025", "Kaapverdië"), + SEYCHELLEN("8026", "Seychellen"), + KIRIBATI("8027", "Kiribati"), + TUVALU("8028", "Tuvalu"), + SAINT_LUCIA("8029", "Saint Lucia"), + DOMINICA("8030", "Dominica"), + ZIMBABWE("8031", "Zimbabwe"), + DUBAI("8032", "Dubai"), + NIEUWE_HEBRIDEN("8033", "Nieuwe Hebriden"), + KANAALEILANDEN("8034", "Kanaaleilanden"), + MAN("8035", "Man"), + ANGUILLA("8036", "Anguilla"), + SAINT_KITTS_EN_NEVIS("8037", "Saint Kitts en Nevis"), + ANTIGUA("8038", "Antigua"), + SAINT_VINCENT("8039", "Saint Vincent"), + GILBERTEILANDEN("8040", "Gilberteilanden"), + PANAMAKANAALZONE("8041", "Panamakanaalzone"), + SAINT_KITTS_NEVIS_EN_ANGUILLA("8042", "Saint Kitts, Nevis en Anguilla"), + BELAU("8043", "Belau"), + PALAU("8044", "Palau"), + ANTIGUA_EN_BARBUDA("8045", "Antigua en Barbuda"), + NEWFOUNDLAND("9000", "Newfoundland"), + NYASALAND("9001", "Nyasaland"), + ERITREA("9003", "Eritrea"), + IFNI("9005", "Ifni"), + BRITSKAMEROEN("9006", "Brits-Kameroen"), + KEIZER_WILHELMSLAND("9007", "Keizer Wilhelmsland"), + CONGO("9008", "Congo"), + CONGOKINSHASA("9009", "Congo-Kinshasa"), + MADAGASKAR("9010", "Madagaskar"), + CONGOBRAZZAVILLE("9013", "Congo-Brazzaville"), + LEEWARDEILANDEN("9014", "Leewardeilanden"), + WINDWARDEILANDEN("9015", "Windwardeilanden"), + FRANS_TERRITORIUM_VOOR_AFARS_EN_ISSAS("9016", "Frans Territorium voor Afars en Issa's"), + PHOENIXEILANDEN("9017", "Phoenixeilanden"), + PORTUGEESGUINEE("9020", "Portugees-Guinee"), + DUITS_ZUIDWESTAFRIKA("9022", "Duits Zuidwest-Afrika"), + NAMIBIE("9023", "Namibië"), + BRITSSOMALILAND("9027", "Brits-Somaliland"), + ITALIAANSSOMALILAND("9028", "Italiaans-Somaliland"), + NEDERLANDSINDIE("9030", "Nederlands-Indië"), + BRITSGUYANA("9031", "Brits-Guyana"), + SWAZILAND("9036", "Swaziland"), + QATAR("9037", "Qatar"), + ESWATINI("9038", "Eswatini"), + ADEN("9041", "Aden"), + ZUIDARABISCHE_FEDERATIE("9042", "Zuid-Arabische Federatie"), + EQUATORIAALGUINEA("9043", "Equatoriaal-Guinea"), + SPAANSGUINEE("9044", "Spaans-Guinee"), + VERENIGDE_ARABISCHE_REPUBLIEK("9047", "Verenigde Arabische Republiek"), + BERMUDA("9048", "Bermuda"), + SOVJETUNIE("9049", "Sovjet-Unie"), + DUITS_OOSTAFRIKA("9050", "Duits Oost-Afrika"), + ZANZIBAR("9051", "Zanzibar"), + CEYLON("9052", "Ceylon"), + MUSCAT_EN_OMAN("9053", "Muscat en Oman"), + TRUCIAL_OMAN("9054", "Trucial Oman"), + INDOCHINA("9055", "Indochina"), + MARSHALLEILANDEN("9056", "Marshalleilanden"), + SARAWAK("9057", "Sarawak"), + BRITSBORNEO("9058", "Brits-Borneo"), + SABAH("9060", "Sabah"), + ABU_DHABI("9061", "Abu Dhabi"), + AJMAN("9062", "Ajman"), + BASUTOLAND("9063", "Basutoland"), + BECHUANALAND("9064", "Bechuanaland"), + FUJAIRAH("9065", "Fujairah"), + FRANSKAMEROEN("9066", "Frans-Kameroen"), + JOHORE("9067", "Johore"), + KOREA("9068", "Korea"), + LABUAN("9069", "Labuan"), + UMM_ALQAIWAIN("9070", "Umm Al-Qaiwain"), + OOSTENRIJKHONGARIJE("9071", "Oostenrijk-Hongarije"), + PORTUGEES_OOSTAFRIKA("9072", "Portugees Oost-Afrika"), + PORTUGEES_WESTAFRIKA("9073", "Portugees West-Afrika"), + SHARJAH("9074", "Sharjah"), + STRAITS_SETTLEMENTS("9075", "Straits Settlements"), + ABESSINIE("9076", "Abessinië"), + FRANS_WESTAFRIKA("9077", "Frans West-Afrika"), + FRANS_EQUATORIAALAFRIKA("9078", "Frans Equatoriaal-Afrika"), + URUNDI("9081", "Urundi"), + RUANDAURUNDI("9082", "Ruanda-Urundi"), + GOA("9084", "Goa"), + DANTZIG("9085", "Dantzig"), + CENTRAALAFRIKAANSE_REPUBLIEK("9086", "Centraal-Afrikaanse Republiek"), + DJIBOUTI("9087", "Djibouti"), + TRANSJORDANIE("9088", "Transjordanië"), + BONDSREPUBLIEK_DUITSLAND("9089", "Bondsrepubliek Duitsland"), + VANUATU("9090", "Vanuatu"), + NIUE("9091", "Niue"), + SPAANS_NOORDAFRIKA("9092", "Spaans Noord-Afrika"), + WESTELIJKE_SAHARA("9093", "Westelijke Sahara"), + MICRONESIA("9094", "Micronesia"), + SVALBARDEILANDEN("9095", "Svalbardeilanden"), + INTERNATIONAAL_GEBIED("9999", "Internationaal gebied"), + NONE("", ""), + ; + + override fun toString(): String { + return "$landcode: $landnaam" + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/DigitaleAdressen.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/DigitaleAdressen.kt new file mode 100644 index 00000000..8ecc7dfc --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/DigitaleAdressen.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.path + +import nl.nlportal.openklant.client.OpenKlant2KlantinteractiesClient +import nl.nlportal.openklant.client.domain.OpenKlant2DigitaleAdres +import nl.nlportal.openklant.client.domain.OpenKlant2DigitaleAdressenFilters +import nl.nlportal.openklant.client.domain.OpenKlant2PartijIdentificator +import nl.nlportal.openklant.client.domain.ResultPage +import org.springframework.http.MediaType +import org.springframework.web.reactive.function.BodyInserters +import org.springframework.web.reactive.function.client.awaitBodilessEntity +import org.springframework.web.reactive.function.client.awaitBody +import org.springframework.web.reactive.function.client.awaitBodyOrNull +import java.util.UUID + +class DigitaleAdressen(val client: OpenKlant2KlantinteractiesClient) : KlantInteractiesPath() { + override val path: String = "/digitaleadressen" + + suspend fun get(searchFilters: List>? = null): List { + val response: ResultPage = + client + .webClient() + .get() + .uri { uriBuilder -> + uriBuilder + .path(path) + .applyFilters(searchFilters) + uriBuilder.build() + } + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBody() + + return response.results + } + + suspend fun create(digitaleAdres: OpenKlant2DigitaleAdres): OpenKlant2DigitaleAdres? { + val response: OpenKlant2DigitaleAdres? = + client + .webClient() + .post() + .uri { uriBuilder -> + uriBuilder + .path(path) + .build() + } + .body(BodyInserters.fromValue(digitaleAdres)) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodyOrNull() + + return response + } + + suspend fun put(digitaleAdres: OpenKlant2DigitaleAdres): OpenKlant2DigitaleAdres? { + val response: OpenKlant2DigitaleAdres? = + client + .webClient() + .put() + .uri { uriBuilder -> + uriBuilder + .path("$path/${digitaleAdres.uuid}") + .build() + } + .body(BodyInserters.fromValue(digitaleAdres)) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodyOrNull() + + return response + } + + suspend fun delete(uuid: UUID) { + client + .webClient() + .delete() + .uri { uriBuilder -> + uriBuilder + .path("$path/$uuid") + .build() + } + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodilessEntity() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/KlantInteractiesPath.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/KlantInteractiesPath.kt new file mode 100644 index 00000000..3bb5a9da --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/KlantInteractiesPath.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.path + +import nl.nlportal.openklant.client.domain.OpenKlant2Filters +import org.springframework.web.util.UriBuilder + +open class KlantInteractiesPath { + open val path: String = "/" + + fun UriBuilder.applyFilters(filters: List>? = null): UriBuilder { + return apply { + filters?.forEach { queryParam(it.first.toString(), it.second.toString()) } + } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/PartijIdentificatoren.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/PartijIdentificatoren.kt new file mode 100644 index 00000000..30478ce8 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/PartijIdentificatoren.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.path + +import nl.nlportal.openklant.client.OpenKlant2KlantinteractiesClient +import nl.nlportal.openklant.client.domain.OpenKlant2PartijIdentificator +import nl.nlportal.openklant.client.domain.OpenKlant2PartijIdentificatorenFilters +import nl.nlportal.openklant.client.domain.ResultPage +import org.springframework.http.MediaType +import org.springframework.web.reactive.function.BodyInserters +import org.springframework.web.reactive.function.client.awaitBodilessEntity +import org.springframework.web.reactive.function.client.awaitBody +import org.springframework.web.reactive.function.client.awaitBodyOrNull +import java.util.UUID + +class PartijIdentificatoren(val client: OpenKlant2KlantinteractiesClient) : KlantInteractiesPath() { + override val path: String = "/partij-identificatoren" + + suspend fun get(searchFilters: List>? = null): List { + val response: ResultPage = + client + .webClient() + .get() + .uri { uriBuilder -> + uriBuilder + .path(path) + .applyFilters(searchFilters) + uriBuilder.build() + } + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBody() + + return response.results + } + + suspend fun create(partijIdentificatorRequest: OpenKlant2PartijIdentificator): OpenKlant2PartijIdentificator { + val response: OpenKlant2PartijIdentificator = + client + .webClient() + .post() + .uri { uriBuilder -> + uriBuilder + .path(path) + .build() + } + .body(BodyInserters.fromValue(partijIdentificatorRequest)) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBody() + + return response + } + + suspend fun put(partijIdentificatorRequest: OpenKlant2PartijIdentificator): OpenKlant2PartijIdentificator? { + val response: OpenKlant2PartijIdentificator? = + client + .webClient() + .put() + .uri { uriBuilder -> + uriBuilder + .path("$path/${partijIdentificatorRequest.uuid}") + .build() + } + .body(BodyInserters.fromValue(partijIdentificatorRequest)) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodyOrNull() + + return response + } + + suspend fun delete(uuid: UUID) { + client + .webClient() + .delete() + .uri { uriBuilder -> + uriBuilder + .path("$path/$uuid") + .build() + } + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodilessEntity() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/Partijen.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/Partijen.kt new file mode 100644 index 00000000..c83c3591 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/client/path/Partijen.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.path + +import nl.nlportal.openklant.client.OpenKlant2KlantinteractiesClient +import nl.nlportal.openklant.client.domain.OpenKlant2Partij +import nl.nlportal.openklant.client.domain.OpenKlant2PartijenFilters +import nl.nlportal.openklant.client.domain.ResultPage +import org.springframework.http.MediaType +import org.springframework.web.reactive.function.BodyInserters +import org.springframework.web.reactive.function.client.awaitBodilessEntity +import org.springframework.web.reactive.function.client.awaitBody +import org.springframework.web.reactive.function.client.awaitBodyOrNull +import java.util.UUID + +class Partijen(val client: OpenKlant2KlantinteractiesClient) : KlantInteractiesPath() { + override val path = "/partijen" + + suspend fun get(searchFilters: List>? = null): List? { + val response: ResultPage? = + client + .webClient() + .get() + .uri { uriBuilder -> + uriBuilder + .path(path) + .applyFilters(filters = searchFilters) + .build() + } + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodyOrNull() + + return response?.results + } + + suspend fun get(partijId: UUID): OpenKlant2Partij? { + val response: OpenKlant2Partij? = + client + .webClient() + .get() + .uri { uriBuilder -> + uriBuilder + .path("$path/$partijId") + .build() + } + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodyOrNull() + + return response + } + + suspend fun create(partijRequest: OpenKlant2Partij): OpenKlant2Partij { + val response: OpenKlant2Partij = + client + .webClient() + .post() + .uri { uriBuilder -> + uriBuilder + .path(path) + .build() + } + .body(BodyInserters.fromValue(partijRequest)) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBody() + + return response + } + + suspend fun put(partij: OpenKlant2Partij): OpenKlant2Partij? { + val response: OpenKlant2Partij? = + client + .webClient() + .put() + .uri { uriBuilder -> + uriBuilder + .path("$path/${partij.uuid}") + .build() + } + .body(BodyInserters.fromValue(partij)) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodyOrNull() + + return response + } + + suspend fun delete(uuid: UUID) { + client + .webClient() + .delete() + .uri { uriBuilder -> + uriBuilder + .path("$path/$uuid") + .build() + } + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .awaitBodilessEntity() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresMutation.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresMutation.kt new file mode 100644 index 00000000..0c8bc8fb --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresMutation.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.expediagroup.graphql.generator.annotations.GraphQLDescription +import com.expediagroup.graphql.generator.federation.directives.AuthenticatedDirective +import com.expediagroup.graphql.server.operations.Mutation +import graphql.schema.DataFetchingEnvironment +import nl.nlportal.graphql.security.SecurityConstants.AUTHENTICATION_KEY +import nl.nlportal.openklant.graphql.domain.DigitaleAdresRequest +import nl.nlportal.openklant.graphql.domain.DigitaleAdresResponse +import nl.nlportal.openklant.service.OpenKlant2Service +import java.util.UUID + +@AuthenticatedDirective +class DigitaleAdresMutation( + private val openklant2Service: OpenKlant2Service, +) : Mutation { + @GraphQLDescription("Create DigitaleAdres for User") + suspend fun createUserDigitaleAdres( + dfe: DataFetchingEnvironment, + digitaleAdresRequest: DigitaleAdresRequest, + ): DigitaleAdresResponse? { + val digitaleAdres = + openklant2Service.createDigitaleAdres( + authentication = dfe.graphQlContext.get(AUTHENTICATION_KEY), + digitaleAdres = digitaleAdresRequest.asOpenKlant2DigitaleAdres(), + ) + return digitaleAdres?.let { DigitaleAdresResponse.fromOpenKlant2DigitaleAdres(digitaleAdres) } + } + + @GraphQLDescription("Update DigitaleAdres of User") + suspend fun updateUserDigitaleAdres( + dfe: DataFetchingEnvironment, + digitaleAdresId: UUID, + digitaleAdresRequest: DigitaleAdresRequest, + ): DigitaleAdresResponse? { + val digitaleAdres = + openklant2Service + .updateDigitaleAdresById( + authentication = dfe.graphQlContext.get(AUTHENTICATION_KEY), + digitaleAdresId = digitaleAdresId, + digitaleAdres = digitaleAdresRequest.asOpenKlant2DigitaleAdres(), + ) + + return digitaleAdres?.let { DigitaleAdresResponse.fromOpenKlant2DigitaleAdres(digitaleAdres) } + } + + @GraphQLDescription("Delete DigitaleAdres of User by Id") + suspend fun deleteUserDigitaleAdres( + dfe: DataFetchingEnvironment, + digitaleAdresId: UUID, + ): Boolean? { + openklant2Service + .deleteDigitaleAdresById( + dfe.graphQlContext.get(AUTHENTICATION_KEY), + digitaleAdresId, + ) + return null + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresQuery.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresQuery.kt new file mode 100644 index 00000000..f4482199 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/DigitaleAdresQuery.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.expediagroup.graphql.generator.annotations.GraphQLDescription +import com.expediagroup.graphql.generator.federation.directives.AuthenticatedDirective +import com.expediagroup.graphql.server.operations.Query +import graphql.schema.DataFetchingEnvironment +import nl.nlportal.commonground.authentication.CommonGroundAuthentication +import nl.nlportal.graphql.security.SecurityConstants.AUTHENTICATION_KEY +import nl.nlportal.openklant.graphql.domain.DigitaleAdresResponse +import nl.nlportal.openklant.service.OpenKlant2Service + +@AuthenticatedDirective +class DigitaleAdresQuery( + private val openklant2Service: OpenKlant2Service, +) : Query { + @GraphQLDescription("Get DigitaleAdressen of authenticated user.") + suspend fun getUserDigitaleAdresen(dfe: DataFetchingEnvironment): List? { + val authentication: CommonGroundAuthentication = dfe.graphQlContext.get(AUTHENTICATION_KEY) + val userDigitaleAdressen = openklant2Service.findDigitaleAdressen(authentication) + + return userDigitaleAdressen?.map { DigitaleAdresResponse.fromOpenKlant2DigitaleAdres(it) } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijMutation.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijMutation.kt new file mode 100644 index 00000000..83ef4b2f --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijMutation.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.expediagroup.graphql.generator.annotations.GraphQLDescription +import com.expediagroup.graphql.generator.federation.directives.AuthenticatedDirective +import com.expediagroup.graphql.server.operations.Mutation +import graphql.schema.DataFetchingEnvironment +import nl.nlportal.graphql.security.SecurityConstants.AUTHENTICATION_KEY +import nl.nlportal.openklant.graphql.domain.PartijRequest +import nl.nlportal.openklant.graphql.domain.PartijResponse +import nl.nlportal.openklant.service.OpenKlant2Service + +@AuthenticatedDirective +class PartijMutation( + private val openklant2Service: OpenKlant2Service, +) : Mutation { + @GraphQLDescription("Create Partij for user") + suspend fun createUserPartij( + dfe: DataFetchingEnvironment, + partijRequest: PartijRequest, + ): PartijResponse? { + val partij = + openklant2Service.createPartijWithIdentificator( + authentication = dfe.graphQlContext.get(AUTHENTICATION_KEY), + partij = partijRequest.asOpenKlant2Partij(), + ) + return partij?.let { PartijResponse.fromOpenKlant2Partij(partij) } + } + + @GraphQLDescription("Update user Partij") + suspend fun updateUserPartij( + dfe: DataFetchingEnvironment, + partijRequest: PartijRequest, + ): PartijResponse? { + val partij = + openklant2Service.updatePartij( + dfe.graphQlContext.get(AUTHENTICATION_KEY), + partijRequest.asOpenKlant2Partij(), + ) + + return partij?.let { PartijResponse.fromOpenKlant2Partij(partij) } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijQuery.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijQuery.kt new file mode 100644 index 00000000..9ae60b9e --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/PartijQuery.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.expediagroup.graphql.generator.annotations.GraphQLDescription +import com.expediagroup.graphql.generator.federation.directives.AuthenticatedDirective +import com.expediagroup.graphql.server.operations.Query +import graphql.schema.DataFetchingEnvironment +import nl.nlportal.commonground.authentication.CommonGroundAuthentication +import nl.nlportal.graphql.security.SecurityConstants.AUTHENTICATION_KEY +import nl.nlportal.openklant.client.domain.OpenKlant2Partij +import nl.nlportal.openklant.service.OpenKlant2Service +import java.util.UUID + +@AuthenticatedDirective +class PartijQuery( + private val openklant2Service: OpenKlant2Service, +) : Query { + @GraphQLDescription("Find the Partij of the authenticated user.") + suspend fun findUserPartij(dfe: DataFetchingEnvironment): OpenKlant2Partij? { + return openklant2Service.findPartijByAuthentication(dfe.graphQlContext.get(AUTHENTICATION_KEY)) + } + + @GraphQLDescription("Get Partij by Id for authenticated user.") + suspend fun getUserPartij( + dfe: DataFetchingEnvironment, + partijId: UUID, + ): OpenKlant2Partij? { + val authentication: CommonGroundAuthentication = dfe.graphQlContext.get(AUTHENTICATION_KEY) + val userPartijen = + openklant2Service + .findPartijIdentificatoren(authentication) + ?.mapNotNull { it.identificeerdePartij?.uuid } + + if (userPartijen == null || partijId !in userPartijen) return null + val partijResponse = openklant2Service.getPartij(partijId = partijId) + + return partijResponse + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresRequest.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresRequest.kt new file mode 100644 index 00000000..514c4965 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresRequest.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql.domain + +import nl.nlportal.openklant.client.domain.OpenKlant2DigitaleAdres +import java.util.UUID + +data class DigitaleAdresRequest( + val uuid: UUID? = null, + val waarde: String, + val type: DigitaleAdresType, + val omschrijving: String, +) { + fun asOpenKlant2DigitaleAdres(): OpenKlant2DigitaleAdres = + OpenKlant2DigitaleAdres( + adres = waarde, + omschrijving = omschrijving, + soortDigitaalAdres = type.name, + ) +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresResponse.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresResponse.kt new file mode 100644 index 00000000..c3a76898 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresResponse.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql.domain + +import nl.nlportal.openklant.client.domain.OpenKlant2DigitaleAdres +import nl.nlportal.openklant.graphql.domain.DigitaleAdresType.ANDERS +import java.util.UUID + +data class DigitaleAdresResponse( + val uuid: UUID, + val waarde: String, + val type: DigitaleAdresType, + val omschrijving: String, +) { + companion object { + fun fromOpenKlant2DigitaleAdres(openKlant2DigitaleAdres: OpenKlant2DigitaleAdres): DigitaleAdresResponse = + DigitaleAdresResponse( + uuid = openKlant2DigitaleAdres.uuid!!, + waarde = openKlant2DigitaleAdres.adres, + omschrijving = openKlant2DigitaleAdres.omschrijving, + type = + DigitaleAdresType + .entries + .singleOrNull { it.name == openKlant2DigitaleAdres.soortDigitaalAdres } + ?: ANDERS, + ) + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresType.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresType.kt new file mode 100644 index 00000000..1c95c1b6 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/DigitaleAdresType.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql.domain + +enum class DigitaleAdresType { + TELEFOONNUMMER, + EMAIL, + ANDERS, +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijRequest.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijRequest.kt new file mode 100644 index 00000000..8c70ab24 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijRequest.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql.domain + +import nl.nlportal.openklant.client.domain.ContactpersoonIdentificatie +import nl.nlportal.openklant.client.domain.OpenKlant2Partij +import nl.nlportal.openklant.client.domain.OrganisatieIdentificatie +import nl.nlportal.openklant.client.domain.PartijIdentificatie +import nl.nlportal.openklant.client.domain.PersoonsIdentificatie +import nl.nlportal.openklant.graphql.domain.PartijType.CONTACTPERSOON +import nl.nlportal.openklant.graphql.domain.PartijType.ORGANISATIE +import nl.nlportal.openklant.graphql.domain.PartijType.PERSOON + +data class PartijRequest( + val indicatieGeheimhouding: Boolean, + val indicatieActief: Boolean, + val type: PartijType, + val persoonsIdentificatie: PersoonsIdentificatie? = null, + val organisatieIdentificatie: OrganisatieIdentificatie? = null, + val contactpersoonIdentificatie: ContactpersoonIdentificatie? = null, +) { + private val identificatie: PartijIdentificatie = + when (type) { + PERSOON -> + requireNotNull(persoonsIdentificatie) { + "{persoonIdentification} can not be null when is $type" + } + ORGANISATIE -> + requireNotNull(organisatieIdentificatie) { + "{organisatieIdentificatie} can not be null when is $type" + } + CONTACTPERSOON -> + requireNotNull(contactpersoonIdentificatie) { + "{contactpersoonIdentificatie} can not be null when is $type" + } + } + + fun asOpenKlant2Partij(): OpenKlant2Partij = + OpenKlant2Partij( + indicatieGeheimhouding = indicatieGeheimhouding, + indicatieActief = indicatieActief, + soortPartij = type.asSoortPartij(), + partijIdentificatie = identificatie, + ) +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijResponse.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijResponse.kt new file mode 100644 index 00000000..4b05b4a7 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijResponse.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql.domain + +import nl.nlportal.openklant.client.domain.ContactpersoonIdentificatie +import nl.nlportal.openklant.client.domain.OpenKlant2DigitaleAdres +import nl.nlportal.openklant.client.domain.OpenKlant2Partij +import nl.nlportal.openklant.client.domain.OrganisatieIdentificatie +import nl.nlportal.openklant.client.domain.PersoonsIdentificatie + +data class PartijResponse( + val indicatieGeheimhouding: Boolean, + val indicatieActief: Boolean, + val type: PartijType, + val persoonsIdentificatie: PersoonsIdentificatie? = null, + val organisatieIdentificatie: OrganisatieIdentificatie? = null, + val contactpersoonIdentificatie: ContactpersoonIdentificatie? = null, + val digitaleAdressen: List? = null, +) { + companion object { + fun fromOpenKlant2Partij(openKlant2Partij: OpenKlant2Partij): PartijResponse = + PartijResponse( + indicatieGeheimhouding = openKlant2Partij.indicatieGeheimhouding, + indicatieActief = openKlant2Partij.indicatieActief, + type = PartijType.valueOf(openKlant2Partij.soortPartij.name), + persoonsIdentificatie = openKlant2Partij.partijIdentificatie as? PersoonsIdentificatie, + organisatieIdentificatie = openKlant2Partij.partijIdentificatie as? OrganisatieIdentificatie, + contactpersoonIdentificatie = openKlant2Partij.partijIdentificatie as? ContactpersoonIdentificatie, + digitaleAdressen = openKlant2Partij.expand?.digitaleAdressen, + ) + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijType.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijType.kt new file mode 100644 index 00000000..00e66d57 --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/graphql/domain/PartijType.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql.domain + +import nl.nlportal.openklant.client.domain.SoortPartij + +enum class PartijType { + PERSOON, + ORGANISATIE, + CONTACTPERSOON, + ; + + fun asSoortPartij(): SoortPartij { + return SoortPartij.valueOf(name) + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/service/OpenKlant2Service.kt b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/service/OpenKlant2Service.kt new file mode 100644 index 00000000..ddd90dba --- /dev/null +++ b/zgw/openklant/src/main/kotlin/nl/nlportal/openklant/service/OpenKlant2Service.kt @@ -0,0 +1,257 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.service + +import mu.KotlinLogging +import nl.nlportal.commonground.authentication.CommonGroundAuthentication +import nl.nlportal.openklant.client.OpenKlant2KlantinteractiesClient +import nl.nlportal.openklant.client.domain.OpenKlant2DigitaleAdres +import nl.nlportal.openklant.client.domain.OpenKlant2Identificator +import nl.nlportal.openklant.client.domain.OpenKlant2IdentificeerdePartij +import nl.nlportal.openklant.client.domain.OpenKlant2Partij +import nl.nlportal.openklant.client.domain.OpenKlant2PartijIdentificator +import nl.nlportal.openklant.client.domain.OpenKlant2PartijIdentificatorenFilters +import nl.nlportal.openklant.client.domain.OpenKlant2PartijenFilters +import nl.nlportal.openklant.client.domain.OpenKlant2UUID +import nl.nlportal.openklant.client.domain.PartijExpandOptions.DIGITALE_ADRESSEN +import nl.nlportal.openklant.client.domain.asSoortPartij +import nl.nlportal.openklant.client.path.DigitaleAdressen +import nl.nlportal.openklant.client.path.PartijIdentificatoren +import nl.nlportal.openklant.client.path.Partijen +import org.springframework.web.reactive.function.client.WebClientResponseException +import java.util.UUID + +class OpenKlant2Service( + private val openKlant2Client: OpenKlant2KlantinteractiesClient, +) { + suspend fun findPartijByAuthentication(authentication: CommonGroundAuthentication): OpenKlant2Partij? { + val searchVariables = + listOf( + OpenKlant2PartijenFilters.SOORT_PARTIJ to authentication.asSoortPartij(), + OpenKlant2PartijenFilters.PARTIJ_IDENTIFICATOR_OBJECT_ID to authentication.userId, + ) + + try { + return openKlant2Client.path().get(searchVariables)?.singleOrNull() + } catch (ex: WebClientResponseException) { + logger.debug("Failed to find Partij: ${ex.responseBodyAsString}", ex) + return null + } + } + + suspend fun getPartij(partijId: UUID): OpenKlant2Partij? { + try { + return openKlant2Client.path().get(partijId) + } catch (ex: WebClientResponseException) { + logger.debug("Failed to find Partij: ${ex.responseBodyAsString}", ex) + return null + } + } + + suspend fun createPartijWithIdentificator( + authentication: CommonGroundAuthentication, + partij: OpenKlant2Partij, + ): OpenKlant2Partij? { + val partijIdentificator = + OpenKlant2PartijIdentificator( + partijIdentificator = + OpenKlant2Identificator( + objectId = authentication.userId, + ), + ) + val partijResponse = + try { + openKlant2Client.path().create(partij) + .also { + try { + openKlant2Client + .path() + .create( + partijIdentificator + .copy( + identificeerdePartij = OpenKlant2IdentificeerdePartij(it.uuid!!), + ), + ) + } catch (ex: WebClientResponseException) { + logger.debug("Failed to create PartijIdentificator") + openKlant2Client.path().delete(it.uuid!!) + throw ex + } + } + } catch (ex: WebClientResponseException) { + logger.debug("Failed to create Partij: ${ex.responseBodyAsString}", ex) + return null + } + + return partijResponse + } + + suspend fun updatePartij( + authentication: CommonGroundAuthentication, + partij: OpenKlant2Partij, + ): OpenKlant2Partij? { + val previousPartij = findPartijByAuthentication(authentication) + if (previousPartij != null) { + val updatedPartij = + previousPartij.copy( + indicatieGeheimhouding = partij.indicatieGeheimhouding, + indicatieActief = partij.indicatieActief, + soortPartij = partij.soortPartij, + partijIdentificatie = partij.partijIdentificatie, + ) + val partijResponse = + try { + openKlant2Client + .path() + .put(updatedPartij) + } catch (ex: WebClientResponseException) { + logger.debug("Failed to update Partij: ${ex.responseBodyAsString}", ex) + return null + } + + return partijResponse + } + + logger.debug("Failed to update Partij: No existing Partij found. Creating new Partij") + return createPartijWithIdentificator(authentication, partij) + } + + suspend fun findPartijIdentificatoren(authentication: CommonGroundAuthentication): List? { + val searchFilters: List> = + listOf( + OpenKlant2PartijIdentificatorenFilters.PARTIJ_IDENTIFICATOR_OBJECT_ID to authentication.userId, + ) + + try { + return openKlant2Client.path().get(searchFilters) + } catch (ex: WebClientResponseException) { + logger.debug("Failed to find Partij Identificatoren: ${ex.responseBodyAsString}", ex) + return null + } + } + + suspend fun findDigitaleAdressen(authentication: CommonGroundAuthentication): List? { + val searchVariables = + listOf( + OpenKlant2PartijenFilters.SOORT_PARTIJ to authentication.asSoortPartij(), + OpenKlant2PartijenFilters.PARTIJ_IDENTIFICATOR_OBJECT_ID to authentication.userId, + OpenKlant2PartijenFilters.EXPAND to DIGITALE_ADRESSEN, + ) + + val response = + try { + openKlant2Client.path().get(searchVariables)?.singleOrNull() + } catch (ex: WebClientResponseException) { + logger.debug("Failed to get Partij with DigitaleAdressen: ${ex.responseBodyAsString}", ex) + return null + } + + return response?.expand?.digitaleAdressen + } + + suspend fun createDigitaleAdres( + authentication: CommonGroundAuthentication, + digitaleAdres: OpenKlant2DigitaleAdres, + ): OpenKlant2DigitaleAdres? { + val userPartijId = + findPartijIdentificatoren(authentication) + ?.singleOrNull { it.partijIdentificator?.objectId == authentication.userId } + ?.identificeerdePartij + ?.uuid + + if (userPartijId == null) { + logger.debug("Failed to create Digitale Adres: Authenticated User does not have a Partij") + return null + } + + val digitaleAdresResponse = + try { + openKlant2Client + .path() + .create( + digitaleAdres + .copy(verstrektDoorPartij = OpenKlant2UUID(userPartijId)), + ) + } catch (ex: WebClientResponseException) { + logger.debug("Failed to create DigitaleAdres: ${ex.responseBodyAsString}", ex) + return null + } + + return digitaleAdresResponse + } + + suspend fun updateDigitaleAdresById( + authentication: CommonGroundAuthentication, + digitaleAdresId: UUID, + digitaleAdres: OpenKlant2DigitaleAdres, + ): OpenKlant2DigitaleAdres? { + val previousDigitaleAdres = findDigitaleAdressen(authentication)?.singleOrNull { it.uuid == digitaleAdresId } + if (previousDigitaleAdres == null) { + logger.debug("Failed to update DigitaleAdres: No DigitaleAdres exists with provided Id") + return null + } + val updatedDigitaleAdres = + previousDigitaleAdres.copy( + adres = digitaleAdres.adres, + omschrijving = digitaleAdres.omschrijving, + soortDigitaalAdres = digitaleAdres.soortDigitaalAdres, + verstrektDoorBetrokkene = digitaleAdres.verstrektDoorBetrokkene, + verstrektDoorPartij = digitaleAdres.verstrektDoorPartij, + ) + val digitaleAdresResponse = + try { + openKlant2Client + .path() + .put(updatedDigitaleAdres) + } catch (ex: WebClientResponseException) { + logger.debug("Failed to update DigitaleAdres: ${ex.responseBodyAsString}", ex) + return null + } + + return digitaleAdresResponse + } + + suspend fun deleteDigitaleAdresById( + authentication: CommonGroundAuthentication, + digitaleAdresId: UUID, + ) { + val userPartijId = + findPartijIdentificatoren(authentication) + ?.singleOrNull { it.partijIdentificator?.objectId == authentication.userId } + ?.identificeerdePartij + ?.uuid + + if (userPartijId == null) { + logger.debug("Failed to delete Digitale Adres: Given DigitaleAdres does not belong to Authenticated User") + return + } + + try { + openKlant2Client + .path() + .delete(digitaleAdresId) + } catch (ex: WebClientResponseException) { + logger.debug("Failed to delete DigitaleAdres: ${ex.responseBodyAsString}", ex) + return + } + + return + } + + companion object { + private val logger = KotlinLogging.logger {} + } +} \ No newline at end of file diff --git a/zgw/openklant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/zgw/openklant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..6f148d10 --- /dev/null +++ b/zgw/openklant/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +nl.nlportal.openklant.autoconfigure.OpenKlantAutoConfiguration +nl.nlportal.openklant.autoconfigure.OpenKlantGraphqlAutoConfiguration \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestApplication.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestApplication.kt new file mode 100644 index 00000000..32c941a7 --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestApplication.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant + +import com.expediagroup.graphql.server.operations.Query +import nl.nlportal.core.security.OauthSecurityAutoConfiguration +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.context.annotation.Bean +import org.springframework.security.config.web.server.ServerHttpSecurity +import org.springframework.security.web.server.SecurityWebFilterChain + +@SpringBootApplication( + exclude = [ + OauthSecurityAutoConfiguration::class, + ], +) +class TestApplication { + fun main(args: Array) { + runApplication(*args) + } + + @Bean + fun springSecurityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { + return http + .csrf { it.disable() } + .authorizeExchange { + it.anyExchange().permitAll() + } + .build() + } + + @Bean + fun testQuery(): Query { + return TestQuery() + } + + class TestQuery : Query { + fun runTest(): String { + return "Test" + } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestHelper.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestHelper.kt new file mode 100644 index 00000000..15a34426 --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/TestHelper.kt @@ -0,0 +1,262 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant + +object TestHelper { + val emptyPage = + """ + { + "count": 0, + "next": null, + "previous": null, + "results": [] + } + """.trimIndent() + + object Partijen { + val createPartijRequest = + """ + { + "digitaleAdressen": null, + "voorkeursDigitaalAdres": null, + "vertegenwoordigden": null, + "rekeningnummers": null, + "voorkeursRekeningnummer": null, + "partijIdentificatoren": null, + "soortPartij": "persoon", + "indicatieGeheimhouding": true, + "indicatieActief": true, + "partijIdentificatie": { + "contactnaam": { + "voorletters": "A.", + "voornaam": "Anna", + "voorvoegselAchternaam": "", + "achternaam": "Vissart" + }, + "volledigeNaam": "Anna Vissart" + } + } + """.trimIndent() + val persoonPartijResponse = + """ + { + "count": 1, + "next": null, + "previous": null, + "results": [ + { + "uuid": "9f8e8009-2d1c-40ae-931e-c08861ba7207", + "url": "http://localhost:8007/klantinteracties/api/v1/partijen/9f8e8009-2d1c-40ae-931e-c08861ba7207", + "nummer": "0000000001", + "interneNotitie": "", + "betrokkenen": [ + { + "uuid": "d5de314b-58a4-4d02-9d66-4e68e8927a90", + "url": "http://localhost:8007/klantinteracties/api/v1/betrokkenen/d5de314b-58a4-4d02-9d66-4e68e8927a90" + } + ], + "categorieRelaties": [], + "digitaleAdressen": [ + { + "uuid": "1300d2ab-13cb-4c12-9818-3b76e2a5d993", + "url": "http://localhost:8007/klantinteracties/api/v1/digitaleadressen/1300d2ab-13cb-4c12-9818-3b76e2a5d993" + }, + { + "uuid": "2596d218-05fa-4d8f-ab29-8a5c3029dcf2", + "url": "http://localhost:8007/klantinteracties/api/v1/digitaleadressen/2596d218-05fa-4d8f-ab29-8a5c3029dcf2" + } + ], + "voorkeursDigitaalAdres": { + "uuid": "1300d2ab-13cb-4c12-9818-3b76e2a5d993", + "url": "http://localhost:8007/klantinteracties/api/v1/digitaleadressen/1300d2ab-13cb-4c12-9818-3b76e2a5d993" + }, + "vertegenwoordigden": [], + "rekeningnummers": [], + "voorkeursRekeningnummer": null, + "partijIdentificatoren": [ + { + "uuid": "dd328a2e-7b8e-4809-be28-f9f894c11c34", + "url": "http://localhost:8007/klantinteracties/api/v1/partij-identificatoren/dd328a2e-7b8e-4809-be28-f9f894c11c34" + } + ], + "soortPartij": "persoon", + "indicatieGeheimhouding": true, + "voorkeurstaal": "nld", + "indicatieActief": true, + "bezoekadres": { + "nummeraanduidingId": "", + "adresregel1": "", + "adresregel2": "", + "adresregel3": "", + "land": "" + }, + "correspondentieadres": { + "nummeraanduidingId": "", + "adresregel1": "", + "adresregel2": "", + "adresregel3": "", + "land": "" + }, + "partijIdentificatie": { + "contactnaam": { + "voorletters": "L.", + "voornaam": "Lucas", + "voorvoegselAchternaam": "", + "achternaam": "Boom" + }, + "volledigeNaam": "Lucas Boom" + }, + "_expand": { + "betrokkenen": [ + { + "uuid": "d5de314b-58a4-4d02-9d66-4e68e8927a90", + "url": "http://localhost:8007/klantinteracties/api/v1/betrokkenen/d5de314b-58a4-4d02-9d66-4e68e8927a90", + "wasPartij": { + "uuid": "9f8e8009-2d1c-40ae-931e-c08861ba7207", + "url": "http://localhost:8007/klantinteracties/api/v1/partijen/9f8e8009-2d1c-40ae-931e-c08861ba7207" + }, + "hadKlantcontact": { + "uuid": "33549ba5-95f0-44d2-9c63-776ec126bc55", + "url": "http://localhost:8007/klantinteracties/api/v1/klantcontacten/33549ba5-95f0-44d2-9c63-776ec126bc55" + }, + "digitaleAdressen": [ + { + "uuid": "1300d2ab-13cb-4c12-9818-3b76e2a5d993", + "url": "http://localhost:8007/klantinteracties/api/v1/digitaleadressen/1300d2ab-13cb-4c12-9818-3b76e2a5d993" + }, + { + "uuid": "2596d218-05fa-4d8f-ab29-8a5c3029dcf2", + "url": "http://localhost:8007/klantinteracties/api/v1/digitaleadressen/2596d218-05fa-4d8f-ab29-8a5c3029dcf2" + } + ], + "bezoekadres": { + "nummeraanduidingId": "", + "adresregel1": "", + "adresregel2": "", + "adresregel3": "", + "land": "" + }, + "correspondentieadres": { + "nummeraanduidingId": "", + "adresregel1": "", + "adresregel2": "", + "adresregel3": "", + "land": "" + }, + "contactnaam": { + "voorletters": "L.", + "voornaam": "Lucas", + "voorvoegselAchternaam": "", + "achternaam": "Boom" + }, + "volledigeNaam": "Lucas Boom", + "rol": "klant", + "organisatienaam": "", + "initiator": true, + "_expand": { + "hadKlantcontact": { + "uuid": "33549ba5-95f0-44d2-9c63-776ec126bc55", + "url": "http://localhost:8007/klantinteracties/api/v1/klantcontacten/33549ba5-95f0-44d2-9c63-776ec126bc55", + "gingOverOnderwerpobjecten": [ + { + "uuid": "896821a7-0e35-45a8-84d1-0f4f033ee7c5", + "url": "http://localhost:8007/klantinteracties/api/v1/onderwerpobjecten/896821a7-0e35-45a8-84d1-0f4f033ee7c5" + } + ], + "hadBetrokkenActoren": [ + { + "uuid": "02d4cea3-ebf4-4826-956e-7a5ca1be85ca", + "url": "http://localhost:8007/klantinteracties/api/v1/actoren/02d4cea3-ebf4-4826-956e-7a5ca1be85ca", + "naam": "Jasmijn", + "soortActor": "medewerker", + "indicatieActief": true, + "actoridentificator": { + "objectId": "", + "codeObjecttype": "", + "codeRegister": "", + "codeSoortObjectId": "" + }, + "actorIdentificatie": { + "functie": "receptioniste", + "emailadres": "jasmijn@email.com", + "telefoonnummer": "693624816" + } + } + ], + "omvatteBijlagen": [], + "hadBetrokkenen": [ + { + "uuid": "d5de314b-58a4-4d02-9d66-4e68e8927a90", + "url": "http://localhost:8007/klantinteracties/api/v1/betrokkenen/d5de314b-58a4-4d02-9d66-4e68e8927a90" + } + ], + "leiddeTotInterneTaken": [ + { + "uuid": "58d26043-0cdc-4a46-9110-6acca6e200f2", + "url": "http://localhost:8007/klantinteracties/api/v1/internetaken/58d26043-0cdc-4a46-9110-6acca6e200f2" + } + ], + "nummer": "0000000001", + "kanaal": "E-mail", + "onderwerp": "Vraag over vergunningsaanvraag", + "inhoud": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras ut aliquam velit. Vestibulum tempus purus vitae vehicula blandit. Aliquam erat volutpat. Suspendisse potenti. Maecenas ultrices condimentum lorem, sit amet aliquet sem sagittis at. Aenean lorem neque, tincidunt at ultrices ut, condimentum sed dui. Quisque sagittis eros eget sapien tempor lobortis. Aliquam magna nisi, ultrices vitae condimentum quis, ullamcorper luctus nulla. Proin condimentum diam lobortis lacinia accumsan. Etiam gravida neque quis lectus facilisis eleifend. Proin finibus non sapien a feugiat. Vestibulum consequat felis vitae felis aliquam faucibus. Morbi pellentesque quam velit, sed interdum quam cursus sed. Curabitur suscipit nunc eu cursus cursus. Etiam suscipit massa vel mauris tristique, non tristique tortor eleifend.", + "indicatieContactGelukt": true, + "taal": "nld", + "vertrouwelijk": true, + "plaatsgevondenOp": "2024-03-06T11:02:24Z" + } + } + } + ], + "digitaleAdressen": [ + { + "uuid": "1300d2ab-13cb-4c12-9818-3b76e2a5d993", + "url": "http://localhost:8007/klantinteracties/api/v1/digitaleadressen/1300d2ab-13cb-4c12-9818-3b76e2a5d993", + "verstrektDoorBetrokkene": { + "uuid": "d5de314b-58a4-4d02-9d66-4e68e8927a90", + "url": "http://localhost:8007/klantinteracties/api/v1/betrokkenen/d5de314b-58a4-4d02-9d66-4e68e8927a90" + }, + "verstrektDoorPartij": { + "uuid": "9f8e8009-2d1c-40ae-931e-c08861ba7207", + "url": "http://localhost:8007/klantinteracties/api/v1/partijen/9f8e8009-2d1c-40ae-931e-c08861ba7207" + }, + "adres": "lucas@boom.nl", + "soortDigitaalAdres": "Email", + "omschrijving": "Persoonlijke email adres" + }, + { + "uuid": "2596d218-05fa-4d8f-ab29-8a5c3029dcf2", + "url": "http://localhost:8007/klantinteracties/api/v1/digitaleadressen/2596d218-05fa-4d8f-ab29-8a5c3029dcf2", + "verstrektDoorBetrokkene": { + "uuid": "d5de314b-58a4-4d02-9d66-4e68e8927a90", + "url": "http://localhost:8007/klantinteracties/api/v1/betrokkenen/d5de314b-58a4-4d02-9d66-4e68e8927a90" + }, + "verstrektDoorPartij": { + "uuid": "9f8e8009-2d1c-40ae-931e-c08861ba7207", + "url": "http://localhost:8007/klantinteracties/api/v1/partijen/9f8e8009-2d1c-40ae-931e-c08861ba7207" + }, + "adres": "0611111111", + "soortDigitaalAdres": "Telefoon", + "omschrijving": "Telefoonnummer van de klant" + } + ] + } + } + ] + } + """.trimIndent() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClientTest.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClientTest.kt new file mode 100644 index 00000000..f5a634df --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/OpenKlant2KlantinteractiesClientTest.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client + +import kotlinx.coroutines.test.runTest +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration.OpenKlantConfigurationProperties +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.web.reactive.function.client.awaitBodilessEntity + +class OpenKlant2KlantinteractiesClientTest { + private lateinit var openklantModuleConfiguration: OpenKlantModuleConfiguration + private lateinit var mockServer: MockWebServer + private lateinit var openKlant2Client: OpenKlant2KlantinteractiesClient + private lateinit var hostUrl: String + private lateinit var apiUrl: String + + @BeforeEach + fun setUp() { + mockServer = MockWebServer() + mockServer.start() + + hostUrl = "http://${mockServer.hostName}:${mockServer.port}/" + apiUrl = "http://${mockServer.hostName}:${mockServer.port}/myapi/v1" + openklantModuleConfiguration = + OpenKlantModuleConfiguration( + enabled = true, + properties = + OpenKlantConfigurationProperties( + klantinteractiesApiUrl = mockServer.url("/myapi/v1").toUri(), + token = "SuperSecretToken1234", + ), + ) + openKlant2Client = OpenKlant2KlantinteractiesClient(openklantModuleConfiguration.properties) + } + + @AfterEach + internal fun tearDown() { + mockServer.shutdown() + } + + @Test + fun `should provide configured webclient`() = + runTest { + // when + mockServer.enqueue(MockResponse().setResponseCode(200)) + + // given + openKlant2Client.webClient().get().uri("mypath").retrieve().awaitBodilessEntity() + val request = mockServer.takeRequest() + + // then + assertEquals("${apiUrl}mypath", request.requestUrl.toString()) + assertEquals("Token SuperSecretToken1234", request.getHeader("Authorization")) + assertEquals("EPSG:4326", request.getHeader("Accept-Crs")) + assertEquals("EPSG:4326", request.getHeader("Content-Crs")) + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/path/OpenKlant2PartijenPathTest.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/path/OpenKlant2PartijenPathTest.kt new file mode 100644 index 00000000..396a2bd1 --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/client/path/OpenKlant2PartijenPathTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.client.path + +import kotlinx.coroutines.test.runTest +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration.OpenKlantConfigurationProperties +import nl.nlportal.openklant.client.OpenKlant2KlantinteractiesClient +import nl.nlportal.openklant.client.domain.OpenKlant2PartijenFilters +import nl.nlportal.openklant.client.domain.OpenKlant2PartijenFilters.PARTIJ_IDENTIFICATOR_OBJECT_ID +import nl.nlportal.openklant.client.domain.OpenKlant2PartijenFilters.SOORT_PARTIJ +import nl.nlportal.openklant.client.domain.SoortPartij +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class OpenKlant2PartijenPathTest { + private lateinit var openklantModuleConfiguration: OpenKlantModuleConfiguration + private lateinit var mockServer: MockWebServer + private lateinit var openKlant2Client: OpenKlant2KlantinteractiesClient + + @BeforeEach + fun setUp() { + mockServer = MockWebServer() + mockServer.start() + + openklantModuleConfiguration = + OpenKlantModuleConfiguration( + enabled = true, + properties = + OpenKlantConfigurationProperties( + klantinteractiesApiUrl = mockServer.url(API_PATH).toUri(), + token = "SuperSecretToken1234", + ), + ) + openKlant2Client = OpenKlant2KlantinteractiesClient(openklantModuleConfiguration.properties) + } + + @AfterEach + internal fun tearDown() { + mockServer.shutdown() + } + + @Test + fun `should apply path to request`() = + runTest { + // when + mockServer.enqueue( + MockResponse() + .setResponseCode(200) + .addHeader("Content-Type", "application/json"), + ) + + // given + openKlant2Client.path().get() + val request = mockServer.takeRequest() + + // then + assertEquals("$API_PATH$PATH", request.requestUrl?.encodedPath) + } + + @Test + fun `get - should apply query parameters to request`() = + runTest { + // when + val filters = + listOf( + OpenKlant2PartijenFilters.PAGE to 1, + SOORT_PARTIJ to SoortPartij.PERSOON, + PARTIJ_IDENTIFICATOR_OBJECT_ID to "999990755", + ) + mockServer.enqueue( + MockResponse() + .setResponseCode(200) + .addHeader("Content-Type", "application/json"), + ) + + // given + openKlant2Client.path().get(filters) + val request = mockServer.takeRequest() + + // then + assertEquals("1", request.requestUrl?.queryParameter("page")) + assertEquals("persoon", request.requestUrl?.queryParameter(SOORT_PARTIJ.toString())) + assertEquals("999990755", request.requestUrl?.queryParameter(PARTIJ_IDENTIFICATOR_OBJECT_ID.toString())) + } + + companion object { + const val PATH = "/partijen" + const val API_PATH = "/myapi/v1" + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/configuration/OpenKlant2ModuleConfigurationTest.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/configuration/OpenKlant2ModuleConfigurationTest.kt new file mode 100644 index 00000000..a83d833a --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/configuration/OpenKlant2ModuleConfigurationTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.configuration + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.node.NullNode +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.test.runTest +import nl.nlportal.core.util.Mapper +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.core.io.ClassPathResource +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.web.reactive.function.BodyInserters +import java.nio.charset.Charset + +@SpringBootTest +@ActiveProfiles("openklant-disabled") +@AutoConfigureWebTestClient(timeout = "36000") +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +class OpenKlant2ModuleConfigurationTest( + @Autowired private val webTestClient: WebTestClient, + @Autowired private val openKlantModuleConfiguration: OpenKlantModuleConfiguration, +) { + @Test + fun `should not expose openklant type when module is disabled`() = + runTest { + // when + val responseBodyContent = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/partijIntrospection.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val typeResponse = + objectMapper + .readValue(responseBodyContent!!) + .get("data") + ?.get("__type") + + // then + assertFalse(openKlantModuleConfiguration.enabled) + assertTrue(typeResponse is NullNode) + } + + companion object { + private val objectMapper = Mapper.get() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresMutationIT.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresMutationIT.kt new file mode 100644 index 00000000..9225af36 --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresMutationIT.kt @@ -0,0 +1,223 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.node.NullNode +import com.fasterxml.jackson.databind.node.ObjectNode +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.test.runTest +import nl.nlportal.commonground.authentication.WithBurgerUser +import nl.nlportal.core.util.Mapper +import nl.nlportal.openklant.graphql.domain.DigitaleAdresType +import nl.nlportal.openklant.service.OpenKlant2Service +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation +import org.junit.jupiter.api.Order +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestMethodOrder +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.SpyBean +import org.springframework.core.io.ClassPathResource +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.web.reactive.function.BodyInserters +import java.nio.charset.Charset + +@SpringBootTest +@Tag("integration") +@TestMethodOrder(OrderAnnotation::class) +@AutoConfigureWebTestClient(timeout = "36000") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OpenKlant2DigitaleAdresMutationIT( + @Autowired private val webTestClient: WebTestClient, +) { + @SpyBean + lateinit var openKlant2Service: OpenKlant2Service + + lateinit var testdigitaleAdresUUID: String + + @Test + @Order(1) + @WithBurgerUser("123456788") + fun `should create DigitaleAdres for burger`() = + runTest { + // when + val createResponse = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/createUserDigitaleAdres.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val createResult = + objectMapper + .readValue(createResponse!!) + .get("data") + ?.get("createUserDigitaleAdres") + // then + verify(openKlant2Service, times(1)).createDigitaleAdres(any(), any()) + + assertTrue(createResult is ObjectNode) + assertEquals(DigitaleAdresType.TELEFOONNUMMER.name, createResult!!.get("type").textValue()) + assertEquals("0611111111", createResult.get("waarde").textValue()) + assertEquals("Privè telefoonnummer", createResult.get("omschrijving").textValue()) + + testdigitaleAdresUUID = createResult.get("uuid").textValue() + } + + @Test + @Order(2) + @WithBurgerUser("123456788") + fun `should update existing DigitaleAdres for burger`() = + runTest { + // when + val createResponse = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .bodyValue( + """ + mutation { + updateUserDigitaleAdres( + digitaleAdresId: "$testdigitaleAdresUUID", + digitaleAdresRequest: { + waarde: "0611111112", type: TELEFOONNUMMER, omschrijving: "Modified" + } + ) { + uuid + waarde + type + omschrijving + } + } + """.trimIndent(), + ) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val createResult = + objectMapper + .readValue(createResponse!!) + .get("data") + ?.get("updateUserDigitaleAdres") + + // then + verify(openKlant2Service, times(1)).updateDigitaleAdresById(any(), any(), any()) + + assertTrue(createResult is ObjectNode) + assertTrue(createResult is ObjectNode) + assertEquals(DigitaleAdresType.TELEFOONNUMMER.name, createResult!!.get("type").textValue()) + assertEquals("0611111112", createResult.get("waarde").textValue()) + assertEquals("Modified", createResult.get("omschrijving").textValue()) + } + + @Test + @Order(3) + @WithBurgerUser("123456788") + fun `should delete existing DigitaleAdres for burger`() = + runTest { + // when + val createResponse = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .bodyValue( + """ + mutation { + deleteUserDigitaleAdres(digitaleAdresId: "$testdigitaleAdresUUID") + } + """.trimIndent(), + ) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val createResult = + objectMapper + .readValue(createResponse!!) + .get("data") + ?.get("deleteUserDigitaleAdres") + + // then + verify(openKlant2Service, times(1)).deleteDigitaleAdresById(any(), any()) + + assertTrue(createResult is NullNode) + + val userAdressen = + objectMapper.readTree( + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/getUserDigitaleAdresen.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent, + ) + .get("data") + ?.get("getUserDigitaleAdresen") + + assertFalse(testdigitaleAdresUUID in userAdressen!!.mapNotNull { it?.get("uuid")?.textValue() }) + } + + companion object { + private val objectMapper = Mapper.get() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresQueryIT.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresQueryIT.kt new file mode 100644 index 00000000..16dcd694 --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2DigitaleAdresQueryIT.kt @@ -0,0 +1,157 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.test.runTest +import nl.nlportal.commonground.authentication.WithBurgerUser +import nl.nlportal.core.util.Mapper +import nl.nlportal.openklant.service.OpenKlant2Service +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.SpyBean +import org.springframework.core.io.ClassPathResource +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.web.reactive.function.BodyInserters +import java.nio.charset.Charset + +@SpringBootTest +@Tag("integration") +@AutoConfigureWebTestClient(timeout = "36000") +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +class OpenKlant2DigitaleAdresQueryIT( + @Autowired private val webTestClient: WebTestClient, +) { + @SpyBean + lateinit var openKlant2Service: OpenKlant2Service + + @Test + fun `should introspect DigitaleAdres type`() = + runTest { + // when + val responseBodyContent = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/digitaleAdresIntrospection.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val typeResponse = + objectMapper + .readValue(responseBodyContent!!) + .get("data") + ?.get("__type") + + // then + assertEquals("OBJECT", typeResponse?.get("kind")?.textValue()) + assertEquals("DigitaleAdresResponse", typeResponse?.get("name")?.textValue()) + } + + @Test + @WithBurgerUser("123456788") + fun `should find DigitaleAdressen for authenticated user`() = + runTest { + // when + val responseBody = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/getUserDigitaleAdresen.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val response = + objectMapper + .readValue(responseBody!!) + .get("data") + ?.get("getUserDigitaleAdresen") + + // then + verify(openKlant2Service, times(1)).findDigitaleAdressen(any()) + + assertNotNull(response) + assertEquals("ANDERS", response?.get(0)?.get("type")?.textValue()) + } + + @Test + @WithBurgerUser("111111110") + fun `should return empty list when no DigitaleAdres was found for authenticated user`() = + runTest { + // when + val responseBody = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/getUserDigitaleAdresen.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val responsePartij = + objectMapper + .readValue(responseBody!!) + .get("data") + ?.get("getUserDigitaleAdresen") + + // then + verify(openKlant2Service, times(1)).findDigitaleAdressen(any()) + assertTrue(responsePartij!!.isEmpty) + } + + companion object { + private val objectMapper = Mapper.get() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijMutationIT.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijMutationIT.kt new file mode 100644 index 00000000..e5500a30 --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijMutationIT.kt @@ -0,0 +1,223 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.node.ObjectNode +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.test.runTest +import nl.nlportal.commonground.authentication.WithBurgerUser +import nl.nlportal.core.util.Mapper +import nl.nlportal.openklant.graphql.domain.PartijType.PERSOON +import nl.nlportal.openklant.service.OpenKlant2Service +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation +import org.junit.jupiter.api.Order +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestMethodOrder +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.SpyBean +import org.springframework.core.io.ClassPathResource +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.web.reactive.function.BodyInserters +import java.nio.charset.Charset + +@SpringBootTest +@Tag("integration") +@TestMethodOrder(OrderAnnotation::class) +@AutoConfigureWebTestClient(timeout = "36000") +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OpenKlant2PartijMutationIT( + @Autowired private val webTestClient: WebTestClient, +) { + @SpyBean + lateinit var openKlant2Service: OpenKlant2Service + + @Test + @Order(1) + @WithBurgerUser("999990755") + fun `should create Partij for burger`() = + runTest { + // when + val createPartijResponse = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/createUserPartij.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val createPartijResult = + objectMapper + .readValue(createPartijResponse!!) + .get("data") + ?.get("createUserPartij") + // then + verify(openKlant2Service, times(1)).createPartijWithIdentificator(any(), any()) + + assertTrue(createPartijResult is ObjectNode) + assertEquals(PERSOON.name, createPartijResult!!.requiredAt("/type")?.textValue()) + assertTrue(createPartijResult.requiredAt("/indicatieActief").booleanValue()) + assertTrue(createPartijResult.requiredAt("/indicatieGeheimhouding").booleanValue()) + assertEquals( + "Bob de Bouwer", + createPartijResult.requiredAt("/persoonsIdentificatie/volledigeNaam").textValue(), + ) + assertEquals( + "Bob", + createPartijResult.requiredAt("/persoonsIdentificatie/contactnaam/voornaam").textValue(), + ) + assertEquals( + "de", + createPartijResult.requiredAt("/persoonsIdentificatie/contactnaam/voorvoegselAchternaam").textValue(), + ) + assertEquals( + "Bouwer", + createPartijResult.requiredAt("/persoonsIdentificatie/contactnaam/achternaam").textValue(), + ) + } + + @Test + @Order(2) + @WithBurgerUser("999990755") + fun `should update existing Partij for burger`() = + runTest { + // when + val updatePartijResponse = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/updateUserPartij.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val updatePartijResult = + objectMapper + .readValue(updatePartijResponse!!) + .get("data") + ?.get("updateUserPartij") + // then + verify(openKlant2Service, times(1)).updatePartij(any(), any()) + + assertTrue(updatePartijResult is ObjectNode) + assertEquals(PERSOON.name, updatePartijResult!!.requiredAt("/type")?.textValue()) + assertTrue(updatePartijResult.requiredAt("/indicatieActief").booleanValue()) + assertFalse(updatePartijResult.requiredAt("/indicatieGeheimhouding").booleanValue()) + assertEquals( + "Kees de Boer", + updatePartijResult.requiredAt("/persoonsIdentificatie/volledigeNaam").textValue(), + ) + assertEquals( + "Kees", + updatePartijResult.requiredAt("/persoonsIdentificatie/contactnaam/voornaam").textValue(), + ) + assertEquals( + "de", + updatePartijResult.requiredAt("/persoonsIdentificatie/contactnaam/voorvoegselAchternaam").textValue(), + ) + assertEquals( + "Boer", + updatePartijResult.requiredAt("/persoonsIdentificatie/contactnaam/achternaam").textValue(), + ) + } + + @Test + @Order(3) + @WithBurgerUser("11111110") + fun `should create Partij when update fails due to missing Partij`() = + runTest { + // when + val updatePartijResponse = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/updateUserPartij.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val updatePartijResult = + objectMapper + .readValue(updatePartijResponse!!) + .get("data") + ?.get("updateUserPartij") + // then + verify(openKlant2Service, times(1)).updatePartij(any(), any()) + verify(openKlant2Service, times(1)).createPartijWithIdentificator(any(), any()) + + assertTrue(updatePartijResult is ObjectNode) + assertEquals(PERSOON.name, updatePartijResult!!.requiredAt("/type")?.textValue()) + assertTrue(updatePartijResult.requiredAt("/indicatieActief").booleanValue()) + assertFalse(updatePartijResult.requiredAt("/indicatieGeheimhouding").booleanValue()) + assertEquals( + "Kees de Boer", + updatePartijResult.requiredAt("/persoonsIdentificatie/volledigeNaam").textValue(), + ) + assertEquals( + "Kees", + updatePartijResult.requiredAt("/persoonsIdentificatie/contactnaam/voornaam").textValue(), + ) + assertEquals( + "de", + updatePartijResult.requiredAt("/persoonsIdentificatie/contactnaam/voorvoegselAchternaam").textValue(), + ) + assertEquals( + "Boer", + updatePartijResult.requiredAt("/persoonsIdentificatie/contactnaam/achternaam").textValue(), + ) + } + + companion object { + private val objectMapper = Mapper.get() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijQueryIT.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijQueryIT.kt new file mode 100644 index 00000000..21a84578 --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/graphql/OpenKlant2PartijQueryIT.kt @@ -0,0 +1,235 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.graphql + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.module.kotlin.readValue +import com.fasterxml.jackson.module.kotlin.treeToValue +import kotlinx.coroutines.test.runTest +import nl.nlportal.commonground.authentication.WithBurgerUser +import nl.nlportal.core.util.Mapper +import nl.nlportal.openklant.client.domain.PersoonsIdentificatie +import nl.nlportal.openklant.client.domain.SoortPartij +import nl.nlportal.openklant.service.OpenKlant2Service +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.assertDoesNotThrow +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.SpyBean +import org.springframework.core.io.ClassPathResource +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.test.web.reactive.server.WebTestClient +import org.springframework.web.reactive.function.BodyInserters +import java.nio.charset.Charset + +@SpringBootTest +@Tag("integration") +@AutoConfigureWebTestClient(timeout = "36000") +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +class OpenKlant2PartijQueryIT( + @Autowired private val webTestClient: WebTestClient, +) { + @SpyBean + lateinit var openKlant2Service: OpenKlant2Service + + @Test + fun `should introspect Partij type`() = + runTest { + // when + val responseBodyContent = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/partijIntrospection.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val typeResponse = + objectMapper + .readValue(responseBodyContent!!) + .get("data") + ?.get("__type") + + // then + assertEquals("OBJECT", typeResponse?.get("kind")?.textValue()) + assertEquals("PartijResponse", typeResponse?.get("name")?.textValue()) + } + + @Test + @WithBurgerUser("123456788") + fun `should find Partij for authenticated user`() = + runTest { + // when + val responseBody = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/findUserPartij.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val responsePartij = + objectMapper + .readValue(responseBody!!) + .get("data") + ?.get("findUserPartij") + + // then + verify(openKlant2Service, times(1)).findPartijByAuthentication(any()) + + assertNotNull(responsePartij) + assertEquals(SoortPartij.PERSOON.name, responsePartij?.get("soortPartij")?.textValue()) + assertDoesNotThrow { objectMapper.treeToValue(responsePartij!!.get("partijIdentificatie")) } + assertEquals("Lucas Boom", responsePartij?.requiredAt("/partijIdentificatie/volledigeNaam")?.textValue()) + } + + @Test + @WithBurgerUser("123456788") + fun `should get Partij by Id for authenticated user`() = + runTest { + // when + val responseBody = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/getUserPartij.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val responsePartij = + objectMapper + .readValue(responseBody!!) + .get("data") + ?.get("getUserPartij") + + // then + verify(openKlant2Service, times(1)).getPartij(any()) + + assertNotNull(responsePartij) + assertEquals(SoortPartij.PERSOON.name, responsePartij?.get("soortPartij")?.textValue()) + assertDoesNotThrow { objectMapper.treeToValue(responsePartij!!.get("partijIdentificatie")) } + assertEquals("Lucas Boom", responsePartij?.requiredAt("/partijIdentificatie/volledigeNaam")?.textValue()) + } + + @Test + @WithBurgerUser("99990755") + fun `should return null when user is not allowed to request Partij`() = + runTest { + // when + val responseBody = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/getUserPartij.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val responsePartij = + objectMapper + .readValue(responseBody!!) + .get("data") + ?.get("getUserPartij") + + // then + verify(openKlant2Service, times(1)).findPartijIdentificatoren(any()) + verify(openKlant2Service, times(0)).getPartij(any()) + + assertTrue(responsePartij!!.isNull) + } + + @Test + @WithBurgerUser("111111110") + fun `should return null when no Partij was found for authenticated user`() = + runTest { + // when + val responseBody = + webTestClient + .post() + .uri { builder -> + builder + .path("/graphql") + .build() + } + .header(HttpHeaders.CONTENT_TYPE, MediaType("application", "graphql").toString()) + .body(BodyInserters.fromResource(ClassPathResource("/config/graphql/findUserPartij.gql"))) + .exchange() + .expectStatus().isOk + .expectBody() + .returnResult() + .responseBodyContent + ?.toString(Charset.defaultCharset()) + + val responsePartij = + objectMapper + .readValue(responseBody!!) + .get("data") + ?.get("findUserPartij") + + // then + verify(openKlant2Service, times(1)).findPartijByAuthentication(any()) + assertTrue(responsePartij!!.isNull) + } + + companion object { + private val objectMapper = Mapper.get() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/service/OpenKlant2ServiceTest.kt b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/service/OpenKlant2ServiceTest.kt new file mode 100644 index 00000000..0d5faedf --- /dev/null +++ b/zgw/openklant/src/test/kotlin/nl/nlportal/openklant/service/OpenKlant2ServiceTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2024 Ritense BV, the Netherlands. + * + * Licensed under EUPL, Version 1.2 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.nlportal.openklant.service + +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration +import nl.nlportal.openklant.autoconfigure.OpenKlantModuleConfiguration.OpenKlantConfigurationProperties +import nl.nlportal.openklant.client.OpenKlant2KlantinteractiesClient +import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach + +class OpenKlant2ServiceTest() { + private lateinit var openklantModuleConfiguration: OpenKlantModuleConfiguration + private lateinit var mockServer: MockWebServer + private lateinit var openKlant2Client: OpenKlant2KlantinteractiesClient + private lateinit var hostUrl: String + private lateinit var apiUrl: String + + @BeforeEach + fun setUp() { + mockServer = MockWebServer() + mockServer.start() + + hostUrl = "http://${mockServer.hostName}:${mockServer.port}/" + apiUrl = "http://${mockServer.hostName}:${mockServer.port}/myapi/v1" + openklantModuleConfiguration = + OpenKlantModuleConfiguration( + enabled = true, + properties = + OpenKlantConfigurationProperties( + klantinteractiesApiUrl = mockServer.url("/myapi/v1").toUri(), + token = "SuperSecretToken1234", + ), + ) + openKlant2Client = OpenKlant2KlantinteractiesClient(openklantModuleConfiguration.properties) + } + + @AfterEach + internal fun tearDown() { + mockServer.shutdown() + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/application.yml b/zgw/openklant/src/test/resources/config/application.yml new file mode 100644 index 00000000..ce1caab3 --- /dev/null +++ b/zgw/openklant/src/test/resources/config/application.yml @@ -0,0 +1,45 @@ +graphql: + packages: + - "nl.nlportal" + +nl-portal: + config: + openklant: + enabled: true + properties: + klantinteracties-api-url: http://localhost:9007/klantinteracties/api/v1 + token: ac045222c9e7cde8120b48735560f9b920bb58cd + +spring: + security: + oauth2: + resourceserver: + jwt: + issuer-uri: http://localhost:8082/auth/realms/nlportal + +--- + +spring: + config: + activate: + on-profile: openklant-disabled + +nl-portal: + config: + openklant: + enabled: false + +--- + +spring: + config: + activate: + on-profile: openklant-misconfigured + +nl-portal: + config: + openklant: + enabled: true + properties: + klantinteracties-api-url: + token: \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/createUserDigitaleAdres.gql b/zgw/openklant/src/test/resources/config/graphql/createUserDigitaleAdres.gql new file mode 100644 index 00000000..9dc102fd --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/createUserDigitaleAdres.gql @@ -0,0 +1,8 @@ +mutation { + createUserDigitaleAdres(digitaleAdresRequest: {waarde: "0611111111", type: TELEFOONNUMMER, omschrijving: "Privè telefoonnummer"}) { + uuid + waarde + type + omschrijving + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/createUserPartij.gql b/zgw/openklant/src/test/resources/config/graphql/createUserPartij.gql new file mode 100644 index 00000000..f3da9a0a --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/createUserPartij.gql @@ -0,0 +1,16 @@ +mutation { + createUserPartij(partijRequest: { type: PERSOON, indicatieActief: true, indicatieGeheimhouding: true, persoonsIdentificatie: {contactnaam: { voornaam: "Bob", voorvoegselAchternaam: "de", achternaam: "Bouwer" }, volledigeNaam: "Bob de Bouwer" } }) { + type + indicatieActief + indicatieGeheimhouding + persoonsIdentificatie { + contactnaam { + voorletters + voornaam + voorvoegselAchternaam + achternaam + } + volledigeNaam + } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/digitaleAdresIntrospection.gql b/zgw/openklant/src/test/resources/config/graphql/digitaleAdresIntrospection.gql new file mode 100644 index 00000000..78445473 --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/digitaleAdresIntrospection.gql @@ -0,0 +1,7 @@ +query { + __type(name: "DigitaleAdresResponse") { + kind + name + description + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/findUserPartij.gql b/zgw/openklant/src/test/resources/config/graphql/findUserPartij.gql new file mode 100644 index 00000000..37539244 --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/findUserPartij.gql @@ -0,0 +1,89 @@ +query { + findUserPartij { + betrokkenen { + uuid + url + } + bezoekadres { + adresregel1 + adresregel2 + adresregel3 + land + nummeraanduidingId + } + categorieRelaties { + beginDatum + categorieNaam + eindDatum + url + uuid + } + correspondentieadres { + adresregel1 + adresregel2 + adresregel3 + land + nummeraanduidingId + } + digitaleAdressen { + uuid + url + } + indicatieActief + indicatieGeheimhouding + interneNotitie + nummer + partijIdentificatoren { + uuid + url + } + rekeningnummers { + uuid + url + } + soortPartij + url + uuid + vertegenwoordigden { + uuid + url + } + voorkeursDigitaalAdres { + uuid + url + } + voorkeursRekeningnummer { + uuid + url + } + voorkeurstaal + partijIdentificatie { + ... on PersoonsIdentificatie { + contactnaam { + voorletters + voornaam + voorvoegselAchternaam + achternaam + } + volledigeNaam + } + ... on ContactpersoonIdentificatie { + uuid + werkteVoorPartij { + uuid + url + } + contactnaam { + voorletters + voornaam + voorvoegselAchternaam + achternaam + } + volledigeNaam + } + ... on OrganisatieIdentificatie { + naam + } + } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/getUserDigitaleAdresen.gql b/zgw/openklant/src/test/resources/config/graphql/getUserDigitaleAdresen.gql new file mode 100644 index 00000000..8e436af6 --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/getUserDigitaleAdresen.gql @@ -0,0 +1,9 @@ + +query { + getUserDigitaleAdresen { + uuid + waarde + type + omschrijving + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/getUserPartij.gql b/zgw/openklant/src/test/resources/config/graphql/getUserPartij.gql new file mode 100644 index 00000000..5405dfc6 --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/getUserPartij.gql @@ -0,0 +1,89 @@ +query { + getUserPartij(partijId: "9f8e8009-2d1c-40ae-931e-c08861ba7207") { + betrokkenen { + uuid + url + } + bezoekadres { + adresregel1 + adresregel2 + adresregel3 + land + nummeraanduidingId + } + categorieRelaties { + beginDatum + categorieNaam + eindDatum + url + uuid + } + correspondentieadres { + adresregel1 + adresregel2 + adresregel3 + land + nummeraanduidingId + } + digitaleAdressen { + uuid + url + } + indicatieActief + indicatieGeheimhouding + interneNotitie + nummer + partijIdentificatoren { + uuid + url + } + rekeningnummers { + uuid + url + } + soortPartij + url + uuid + vertegenwoordigden { + uuid + url + } + voorkeursDigitaalAdres { + uuid + url + } + voorkeursRekeningnummer { + uuid + url + } + voorkeurstaal + partijIdentificatie { + ... on PersoonsIdentificatie { + contactnaam { + voorletters + voornaam + voorvoegselAchternaam + achternaam + } + volledigeNaam + } + ... on ContactpersoonIdentificatie { + uuid + werkteVoorPartij { + uuid + url + } + contactnaam { + voorletters + voornaam + voorvoegselAchternaam + achternaam + } + volledigeNaam + } + ... on OrganisatieIdentificatie { + naam + } + } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/partijIntrospection.gql b/zgw/openklant/src/test/resources/config/graphql/partijIntrospection.gql new file mode 100644 index 00000000..b78dde7f --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/partijIntrospection.gql @@ -0,0 +1,7 @@ +query { + __type(name: "PartijResponse") { + kind + name + description + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/config/graphql/updateUserPartij.gql b/zgw/openklant/src/test/resources/config/graphql/updateUserPartij.gql new file mode 100644 index 00000000..bc451cb1 --- /dev/null +++ b/zgw/openklant/src/test/resources/config/graphql/updateUserPartij.gql @@ -0,0 +1,16 @@ +mutation { + updateUserPartij(partijRequest: { type: PERSOON, indicatieActief: true, indicatieGeheimhouding: false, persoonsIdentificatie: {contactnaam: { voornaam: "Kees", voorvoegselAchternaam: "de", achternaam: "Boer" }, volledigeNaam: "Kees de Boer" } }) { + type + indicatieActief + indicatieGeheimhouding + persoonsIdentificatie { + contactnaam { + voorletters + voornaam + voorvoegselAchternaam + achternaam + } + volledigeNaam + } + } +} \ No newline at end of file diff --git a/zgw/openklant/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/zgw/openklant/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 00000000..ca6ee9ce --- /dev/null +++ b/zgw/openklant/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file