From 6c50e2b76e78b44d65de9d68b842a9f77bab85c3 Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Tue, 12 May 2026 09:51:22 +0200 Subject: feat: use Jackson 3 for JSON serde (#286) --- build.gradle.kts | 2 +- .../etl/processor/AbstractTestcontainerTest.kt | 3 +- .../etl/processor/EtlProcessorApplicationTests.kt | 13 +- .../dev/dnpm/etl/processor/EtlProcessorArchTest.kt | 33 ++ .../etl/processor/config/AppConfigurationTest.kt | 6 +- .../processor/config/GPasConfigPropertiesTest.kt | 20 + .../kotlin/dev/dnpm/etl/processor/helpers.kt | 3 +- .../processor/input/MtbFileRestControllerTest.kt | 19 +- .../processor/monitoring/RequestRepositoryTest.kt | 3 +- .../processor/output/RestDipMtbFileSenderTest.kt | 14 +- .../pseudonym/GpasPseudonymGeneratorTest.kt | 3 +- .../services/RequestServiceIntegrationTest.kt | 3 +- .../dnpm/etl/processor/web/ConfigControllerTest.kt | 3 +- .../dnpm/etl/processor/web/HomeControllerTest.kt | 3 +- .../dnpm/etl/processor/web/LoginControllerTest.kt | 3 +- .../etl/processor/web/StatisticsControllerTest.kt | 3 +- .../processor/web/StatisticsRestControllerTest.kt | 3 +- .../processor/consent/AbstractConsentService.java | 2 +- .../dnpm/etl/processor/consent/ConsentDomain.java | 20 + .../etl/processor/consent/GicsConsentService.java | 20 + .../consent/GicsGetBroadConsentService.java | 20 + .../etl/processor/consent/IConsentService.java | 20 + .../consent/MiiBroadConsentEvaluator.java | 2 +- .../processor/consent/MtbFileConsentService.java | 20 + .../etl/processor/consent/TtpConsentStatus.java | 20 + .../dnpm/etl/processor/pseudonym/Generator.java | 3 +- .../pseudonym/GpasPseudonymGenerator.java | 3 +- .../pseudonym/PseudonymRequestFailed.java | 20 + .../etl/processor/pseudonym/PsnDomainType.java | 20 + .../dnpm/etl/processor/EtlProcessorApplication.kt | 3 +- .../kotlin/dev/dnpm/etl/processor/Exceptions.kt | 3 +- .../dev/dnpm/etl/processor/ServletInitializer.kt | 3 +- .../etl/processor/config/AppConfigProperties.kt | 3 +- .../dnpm/etl/processor/config/AppConfiguration.kt | 525 ++++++++++----------- .../dev/dnpm/etl/processor/config/AppFhirConfig.kt | 20 + .../etl/processor/config/AppJdbcConfiguration.kt | 20 + .../etl/processor/config/AppKafkaConfiguration.kt | 17 +- .../etl/processor/config/AppRestConfiguration.kt | 3 +- .../processor/config/AppSecurityConfiguration.kt | 3 +- .../dev/dnpm/etl/processor/config/AppWebConfig.kt | 20 + .../etl/processor/config/AuditConfiguration.kt | 20 + .../dnpm/etl/processor/config/Jackson2Config.kt | 85 ++++ .../dev/dnpm/etl/processor/config/JacksonConfig.kt | 67 ++- .../dnpm/etl/processor/consent/ConsentEvaluator.kt | 3 +- .../kotlin/dev/dnpm/etl/processor/functions.kt | 3 +- .../dnpm/etl/processor/input/KafkaInputListener.kt | 9 +- .../etl/processor/input/MtbFileRestController.kt | 3 +- .../processor/monitoring/ConnectionCheckService.kt | 3 +- .../dnpm/etl/processor/monitoring/ReportService.kt | 96 ++-- .../dev/dnpm/etl/processor/monitoring/Request.kt | 3 +- .../dnpm/etl/processor/monitoring/RequestStatus.kt | 3 +- .../dnpm/etl/processor/monitoring/RequestType.kt | 3 +- .../etl/processor/monitoring/SubmissionType.kt | 20 + .../etl/processor/output/KafkaMtbFileSender.kt | 11 +- .../dev/dnpm/etl/processor/output/MtbFileSender.kt | 3 +- .../dev/dnpm/etl/processor/output/MtbRequest.kt | 3 +- .../etl/processor/output/RestDipMtbFileSender.kt | 3 +- .../dnpm/etl/processor/output/RestMtbFileSender.kt | 2 +- .../processor/pseudonym/AnonymizingGenerator.kt | 3 +- .../pseudonym/GpasSoapPseudonymGenerator.kt | 20 + .../etl/processor/pseudonym/GpasSoapService.kt | 20 + .../etl/processor/pseudonym/PseudonymizeService.kt | 3 +- .../dev/dnpm/etl/processor/pseudonym/extensions.kt | 3 +- .../dnpm/etl/processor/security/TokenService.kt | 3 +- .../dev/dnpm/etl/processor/security/UserRole.kt | 4 +- .../dnpm/etl/processor/security/UserRoleService.kt | 3 +- .../etl/processor/services/ConsentProcessor.kt | 37 +- .../etl/processor/services/RequestProcessor.kt | 8 +- .../dnpm/etl/processor/services/RequestService.kt | 3 +- .../etl/processor/services/ResponseProcessor.kt | 3 +- .../processor/services/TransformationService.kt | 13 +- .../services/kafka/KafkaResponseProcessor.kt | 98 ++-- src/main/kotlin/dev/dnpm/etl/processor/types.kt | 3 +- .../processor/web/ApplicationControllerAdvice.kt | 3 +- .../dev/dnpm/etl/processor/web/ConfigController.kt | 3 +- .../dev/dnpm/etl/processor/web/HomeController.kt | 3 +- .../dev/dnpm/etl/processor/web/LoginController.kt | 3 +- .../dnpm/etl/processor/web/StatisticsController.kt | 3 +- .../etl/processor/web/StatisticsRestController.kt | 10 +- src/main/resources/static/icon.svg | 2 - src/main/resources/static/user.svg | 1 - src/main/resources/templates/report.html | 2 +- .../processor/consent/GicsConsentServiceTest.java | 20 + .../consent/GicsGetBroadConsentServiceTest.java | 20 + .../consent/MiiBroadConsentEvaluatorTest.java | 20 + .../kotlin/dev/dnpm/etl/processor/FunctionsTest.kt | 3 +- .../etl/processor/config/Jackson3ConfigTest.kt | 92 ++++ .../dnpm/etl/processor/config/JacksonConfigTest.kt | 50 +- .../etl/processor/consent/ConsentProcessorTest.kt | 28 +- .../consent/Dnpm21BasedConsentEvaluatorTest.kt | 3 +- src/test/kotlin/dev/dnpm/etl/processor/helpers.kt | 3 +- .../etl/processor/input/KafkaInputListenerTest.kt | 22 +- .../processor/input/MtbFileRestControllerTest.kt | 3 +- .../monitoring/ConnectionCheckServiceTest.kt | 20 + .../etl/processor/monitoring/ReportServiceTest.kt | 24 +- .../etl/processor/output/KafkaMtbFileSenderTest.kt | 31 +- .../processor/output/RestDipMtbFileSenderTest.kt | 490 +++++++++---------- .../dnpm/etl/processor/pseudonym/ExtensionsTest.kt | 451 +++++++++--------- .../processor/pseudonym/PseudonymizeServiceTest.kt | 3 +- .../etl/processor/security/TokenServiceTest.kt | 3 +- .../etl/processor/security/UserRoleServiceTest.kt | 3 +- .../etl/processor/services/ConsentProcessorTest.kt | 32 +- .../etl/processor/services/ReportServiceTest.kt | 40 +- .../etl/processor/services/RequestProcessorTest.kt | 33 +- .../etl/processor/services/RequestServiceTest.kt | 3 +- .../processor/services/ResponseProcessorTest.kt | 3 +- .../services/TransformationServiceTest.kt | 9 +- .../services/kafka/KafkaResponseProcessorTest.kt | 15 +- 108 files changed, 1887 insertions(+), 1058 deletions(-) create mode 100644 src/main/kotlin/dev/dnpm/etl/processor/config/Jackson2Config.kt create mode 100644 src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index e428580..afa84ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -74,7 +74,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-flyway") implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6") implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8") - implementation("com.fasterxml.jackson.module:jackson-module-kotlin") + implementation("tools.jackson.module:jackson-module-kotlin") implementation("org.springframework.kafka:spring-kafka") implementation("org.springframework.retry:spring-retry:${springRetryVersion}") implementation("org.flywaydb:flyway-database-postgresql") diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt index 136691e..d8564c0 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/AbstractTestcontainerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt index cc1ff2b..248f889 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,7 +20,6 @@ package dev.dnpm.etl.processor -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.monitoring.RequestRepository import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.output.DnpmV2MtbFileRequest @@ -43,6 +43,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.post import org.testcontainers.junit.jupiter.Testcontainers +import tools.jackson.databind.json.JsonMapper @Testcontainers @ExtendWith(SpringExtension::class) @@ -84,16 +85,16 @@ class EtlProcessorApplicationTests : AbstractTestcontainerTest() { @MockitoBean private lateinit var mtbFileSender: MtbFileSender private lateinit var mockMvc: MockMvc - private lateinit var objectMapper: ObjectMapper + private lateinit var jsonMapper: JsonMapper @BeforeEach fun setup( @Autowired mockMvc: MockMvc, - @Autowired objectMapper: ObjectMapper, + @Autowired jsonMapper: JsonMapper, @Autowired requestRepository: RequestRepository ) { this.mockMvc = mockMvc - this.objectMapper = objectMapper + this.jsonMapper = jsonMapper requestRepository.deleteAll() } @@ -135,7 +136,7 @@ class EtlProcessorApplicationTests : AbstractTestcontainerTest() { mockMvc .post("/mtbfile") { - content = objectMapper.writeValueAsString(mtbFile) + content = jsonMapper.writeValueAsString(mtbFile) contentType = MediaType.APPLICATION_JSON with(user("admin").roles("ADMIN")) } diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt index 1ebf458..eeca93f 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorArchTest.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor import com.tngtech.archunit.core.domain.JavaClasses @@ -84,4 +104,17 @@ class EtlProcessorArchTest { rule.check(noTestClasses) } + + @Test + fun noClassesDependOnJackson2() { + val rule = + noClasses() + .that() + .haveSimpleNameNotStartingWith("Jackson") + .should() + .dependOnClassesThat() + .resideInAPackage("com.fasterxml.jackson.*") + + rule.check(noTestClasses) + } } diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt index b0fa70f..b4d6e06 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -49,6 +50,7 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.TestPropertySource import org.springframework.test.context.bean.override.mockito.MockitoBean +import tools.jackson.databind.json.JsonMapper @SpringBootTest @ContextConfiguration( @@ -62,7 +64,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoBean ConsentEvaluator::class, ], ) -@MockitoBean(types = [ObjectMapper::class]) +@MockitoBean(types = [ObjectMapper::class, JsonMapper::class]) @TestPropertySource( properties = [ diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt index 7ba171e..609e4c4 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/GPasConfigPropertiesTest.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.config import org.assertj.core.api.Assertions.assertThat diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt index 5ee6922..5f50bed 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/helpers.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt index e0e8162..ba793f2 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,7 +20,6 @@ package dev.dnpm.etl.processor.input -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.config.AppSecurityConfiguration import dev.dnpm.etl.processor.consent.ConsentEvaluation @@ -36,8 +36,6 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import org.mockito.ArgumentCaptor -import org.mockito.Mockito import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.* import org.springframework.beans.factory.annotation.Autowired @@ -53,6 +51,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.delete import org.springframework.test.web.servlet.post +import tools.jackson.databind.json.JsonMapper import java.time.Instant import java.util.* @@ -119,7 +118,7 @@ class MtbFileRestControllerTest { .post(url) { with(user("onkostarserver").roles("MTBFILE")) contentType = MediaType.APPLICATION_JSON - content = ObjectMapper().writeValueAsString(mtbFile) + content = JsonMapper().writeValueAsString(mtbFile) } .andExpect { status { isAccepted() } } @@ -147,7 +146,7 @@ class MtbFileRestControllerTest { .post(url) { with(user("onkostarserver").roles("ADMIN")) contentType = MediaType.APPLICATION_JSON - content = ObjectMapper().writeValueAsString(mtbFile) + content = JsonMapper().writeValueAsString(mtbFile) } .andExpect { status { isAccepted() } } @@ -175,7 +174,7 @@ class MtbFileRestControllerTest { .post(url) { with(user("testuser").roles("USER")) contentType = MediaType.APPLICATION_JSON - content = ObjectMapper().writeValueAsString(mtbFile) + content = JsonMapper().writeValueAsString(mtbFile) } .andExpect { status { isAccepted() } } @@ -200,7 +199,7 @@ class MtbFileRestControllerTest { mockMvc .post(url) { contentType = MediaType.APPLICATION_JSON - content = ObjectMapper().writeValueAsString(mtbFile) + content = JsonMapper().writeValueAsString(mtbFile) } .andExpect { status { isUnauthorized() } } @@ -308,7 +307,7 @@ class MtbFileRestControllerTest { .post(url) { with(user("onkostarserver").roles("ADMIN")) contentType = MediaType.APPLICATION_JSON - content = ObjectMapper().writeValueAsString(mtbFile) + content = JsonMapper().writeValueAsString(mtbFile) } .andExpect { status { isAccepted() } } @@ -336,7 +335,7 @@ class MtbFileRestControllerTest { .post(url) { with(user("onkostarserver").roles("USER")) contentType = MediaType.APPLICATION_JSON - content = ObjectMapper().writeValueAsString(mtbFile) + content = JsonMapper().writeValueAsString(mtbFile) } .andExpect { status { isAccepted() } } diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt index 17b49eb..a712ee7 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt index d27aa4c..bb24ea0 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt @@ -2,7 +2,7 @@ * This file is part of ETL-Processor * * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken - * Copyright (c) 2023-2025 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -20,19 +20,16 @@ package dev.dnpm.etl.processor.output -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.config.AppConfiguration import dev.dnpm.etl.processor.config.AppRestConfiguration import dev.dnpm.etl.processor.config.AppSecurityConfiguration +import dev.dnpm.etl.processor.config.JacksonConfig import dev.dnpm.etl.processor.config.RestTargetProperties import dev.dnpm.etl.processor.consent.ConsentEvaluator import dev.dnpm.etl.processor.monitoring.ReportService import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.pcvolkmer.mv64e.mtb.* -import java.time.Instant -import java.util.* import org.assertj.core.api.Assertions.assertThat import org.hamcrest.CoreMatchers.not import org.hamcrest.Matchers.containsString @@ -53,6 +50,10 @@ import org.springframework.test.web.client.MockRestServiceServer import org.springframework.test.web.client.match.MockRestRequestMatchers.* import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus import org.springframework.web.client.RestTemplate +import tools.jackson.databind.json.JsonMapper +import tools.jackson.module.kotlin.KotlinModule +import java.time.Instant +import java.util.* @SpringBootTest @MockitoBean(types = [ReportService::class]) @@ -78,8 +79,7 @@ class RestDipMtbFileSenderTest { private lateinit var restMtbFileSender: RestMtbFileSender private var reportService = - ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) - + ReportService(JsonMapper.builder().addModule(KotlinModule.Builder().build()).build()) @BeforeEach fun setup(@Autowired restTemplate: RestTemplate) { val restTemplate = restTemplate diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt index cccd614..78089d6 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGeneratorTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt index 63dd5dc..bbade1c 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt index b946da8..3978b33 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt index fdc2066..65aa0d8 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt index efe36ae..d37cc24 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt index f375f2c..20cffe8 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt index cb19cf4..c7440f8 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java index 5c9878c..1dcb74e 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/AbstractConsentService.java @@ -2,7 +2,7 @@ * This file is part of ETL-Processor * * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken - * Copyright (c) 2023-2025 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java b/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java index e339ba4..9c0359a 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; public enum ConsentDomain { diff --git a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java index d445182..20fa3c4 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; import dev.dnpm.etl.processor.config.AppFhirConfig; diff --git a/src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java index d657306..2439e44 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentService.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; import dev.dnpm.etl.processor.config.AppFhirConfig; diff --git a/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java index f9230ec..43c000c 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; import java.util.Date; diff --git a/src/main/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluator.java b/src/main/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluator.java index c815f7a..28ee6be 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluator.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluator.java @@ -2,7 +2,7 @@ * This file is part of ETL-Processor * * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken - * Copyright (c) 2023-2025 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java index cc8107f..db9d690 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; import java.util.Date; diff --git a/src/main/java/dev/dnpm/etl/processor/consent/TtpConsentStatus.java b/src/main/java/dev/dnpm/etl/processor/consent/TtpConsentStatus.java index b92f58d..7aacb20 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/TtpConsentStatus.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/TtpConsentStatus.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; public enum TtpConsentStatus { diff --git a/src/main/java/dev/dnpm/etl/processor/pseudonym/Generator.java b/src/main/java/dev/dnpm/etl/processor/pseudonym/Generator.java index e72a8f8..79e90db 100644 --- a/src/main/java/dev/dnpm/etl/processor/pseudonym/Generator.java +++ b/src/main/java/dev/dnpm/etl/processor/pseudonym/Generator.java @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/java/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGenerator.java b/src/main/java/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGenerator.java index 478c5c9..b4641aa 100644 --- a/src/main/java/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGenerator.java +++ b/src/main/java/dev/dnpm/etl/processor/pseudonym/GpasPseudonymGenerator.java @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/java/dev/dnpm/etl/processor/pseudonym/PseudonymRequestFailed.java b/src/main/java/dev/dnpm/etl/processor/pseudonym/PseudonymRequestFailed.java index 397455a..b1a215e 100644 --- a/src/main/java/dev/dnpm/etl/processor/pseudonym/PseudonymRequestFailed.java +++ b/src/main/java/dev/dnpm/etl/processor/pseudonym/PseudonymRequestFailed.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.pseudonym; public class PseudonymRequestFailed extends RuntimeException { diff --git a/src/main/java/dev/dnpm/etl/processor/pseudonym/PsnDomainType.java b/src/main/java/dev/dnpm/etl/processor/pseudonym/PsnDomainType.java index 55cb212..b1cec26 100644 --- a/src/main/java/dev/dnpm/etl/processor/pseudonym/PsnDomainType.java +++ b/src/main/java/dev/dnpm/etl/processor/pseudonym/PsnDomainType.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.pseudonym; public enum PsnDomainType { diff --git a/src/main/kotlin/dev/dnpm/etl/processor/EtlProcessorApplication.kt b/src/main/kotlin/dev/dnpm/etl/processor/EtlProcessorApplication.kt index 75147ea..26d87da 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/EtlProcessorApplication.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/EtlProcessorApplication.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt b/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt index 1c590fc..92eff9a 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/Exceptions.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt b/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt index e35cddf..1963bd3 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/ServletInitializer.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt index 63f50a6..01bab32 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt index 61f3c40..1c18aab 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt @@ -2,7 +2,7 @@ * This file is part of ETL-Processor * * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken - * Copyright (c) 2023-2025 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -45,7 +45,7 @@ import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.ConfigurationCondition import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration import org.springframework.http.converter.StringHttpMessageConverter -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter +import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter import org.springframework.retry.RetryCallback import org.springframework.retry.RetryContext import org.springframework.retry.RetryListener @@ -58,6 +58,7 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager import org.springframework.web.client.HttpClientErrorException import org.springframework.web.client.RestTemplate import reactor.core.publisher.Sinks +import tools.jackson.databind.json.JsonMapper import kotlin.time.Duration.Companion.seconds import kotlin.time.toJavaDuration @@ -75,280 +76,276 @@ import kotlin.time.toJavaDuration @EnableScheduling class AppConfiguration { - private val logger = LoggerFactory.getLogger(AppConfiguration::class.java) - - fun stringHttpMessageConverter(): StringHttpMessageConverter { - return StringHttpMessageConverter() - } - - @Bean - fun mappingJacksonHttpMessageConverter( - objectMapper: ObjectMapper - ): MappingJackson2HttpMessageConverter { - val converter = MappingJackson2HttpMessageConverter() - converter.setObjectMapper(objectMapper) - return converter - } - - @Bean - fun restTemplate(objectMapper: ObjectMapper): RestTemplate { - return RestTemplateBuilder() - .messageConverters( - stringHttpMessageConverter(), - mappingJacksonHttpMessageConverter(objectMapper), + private val logger = LoggerFactory.getLogger(AppConfiguration::class.java) + + fun stringHttpMessageConverter(): StringHttpMessageConverter { + return StringHttpMessageConverter() + } + + @Bean + fun jacksonJsonHttpMapperConverter(jsonMapper: JsonMapper): JacksonJsonHttpMessageConverter { + return JacksonJsonHttpMessageConverter(jsonMapper) + } + + @Bean + fun restTemplate(jsonMapper: JsonMapper): RestTemplate { + return RestTemplateBuilder() + .messageConverters( + stringHttpMessageConverter(), + jacksonJsonHttpMapperConverter(jsonMapper), + ) + .build() + } + + @Bean + fun appFhirConfig(): AppFhirConfig { + return AppFhirConfig() + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) + @Bean + fun gpasSoapProxyFactoryBean(gpasConfigProperties: GPasConfigProperties): JaxWsProxyFactoryBean { + val proxyFactory = JaxWsProxyFactoryBean() + proxyFactory.serviceClass = GpasSoapService::class.java + proxyFactory.address = gpasConfigProperties.soapEndpoint + return proxyFactory + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) + @Bean + fun gpasSoapProxy(gpasConfigProperties: GPasConfigProperties): GpasSoapService { + return gpasSoapProxyFactoryBean(gpasConfigProperties).create() as GpasSoapService + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) + @Bean + fun gpasSoapPseudonymGenerator( + configProperties: GPasConfigProperties, + retryTemplate: RetryTemplate, + gpasSoapService: GpasSoapService, + appFhirConfig: AppFhirConfig, + ): Generator { + logger.info("Selected 'GpasSoapPseudonym Generator'") + return GpasSoapPseudonymGenerator( + configProperties, + retryTemplate, + gpasSoapService, + appFhirConfig, ) - .build() - } - - @Bean - fun appFhirConfig(): AppFhirConfig { - return AppFhirConfig() - } - - @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") - @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) - @Bean - fun gpasSoapProxyFactoryBean(gpasConfigProperties: GPasConfigProperties): JaxWsProxyFactoryBean { - val proxyFactory = JaxWsProxyFactoryBean() - proxyFactory.serviceClass = GpasSoapService::class.java - proxyFactory.address = gpasConfigProperties.soapEndpoint - return proxyFactory - } - - @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") - @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) - @Bean - fun gpasSoapProxy(gpasConfigProperties: GPasConfigProperties): GpasSoapService { - return gpasSoapProxyFactoryBean(gpasConfigProperties).create() as GpasSoapService - } - - @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") - @ConditionalOnProperty(value = ["app.pseudonymize.gpas.soap-endpoint"]) - @Bean - fun gpasSoapPseudonymGenerator( - configProperties: GPasConfigProperties, - retryTemplate: RetryTemplate, - gpasSoapService: GpasSoapService, - appFhirConfig: AppFhirConfig, - ): Generator { - logger.info("Selected 'GpasSoapPseudonym Generator'") - return GpasSoapPseudonymGenerator( - configProperties, - retryTemplate, - gpasSoapService, - appFhirConfig, + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @ConditionalOnProperty(value = ["app.pseudonymize.gpas.uri"]) + @Bean + fun gpasPseudonymGenerator( + configProperties: GPasConfigProperties, + retryTemplate: RetryTemplate, + restTemplate: RestTemplate, + appFhirConfig: AppFhirConfig, + ): Generator { + logger.info("Selected 'GpasPseudonym Generator'") + return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate, appFhirConfig) + } + + @ConditionalOnProperty( + value = ["app.pseudonymize.generator"], + havingValue = "BUILDIN", + matchIfMissing = true, ) - } - - @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") - @ConditionalOnProperty(value = ["app.pseudonymize.gpas.uri"]) - @Bean - fun gpasPseudonymGenerator( - configProperties: GPasConfigProperties, - retryTemplate: RetryTemplate, - restTemplate: RestTemplate, - appFhirConfig: AppFhirConfig, - ): Generator { - logger.info("Selected 'GpasPseudonym Generator'") - return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate, appFhirConfig) - } - - @ConditionalOnProperty( - value = ["app.pseudonymize.generator"], - havingValue = "BUILDIN", - matchIfMissing = true, - ) - @Bean - fun buildinPseudonymGenerator(): Generator { - logger.info("Selected 'BUILDIN Pseudonym Generator'") - return AnonymizingGenerator() - } - - @Bean - fun pseudonymizeService( - generator: Generator, - pseudonymizeConfigProperties: PseudonymizeConfigProperties, - ): PseudonymizeService { - return PseudonymizeService(generator, pseudonymizeConfigProperties) - } - - @Bean - fun reportService(): ReportService { - return ReportService(getObjectMapper()) - } - - @Bean - fun getObjectMapper(): ObjectMapper { - return JacksonConfig().objectMapper() - } - - @Bean - fun transformationService(configProperties: AppConfigProperties): TransformationService { - logger.info("Apply ${configProperties.transformations.size} transformation rules") - return TransformationService( - getObjectMapper(), - configProperties.transformations.map { Transformation.of(it.path) from it.from to it.to }, - ) - } - - @Bean - fun retryTemplate(configProperties: AppConfigProperties): RetryTemplate { - return RetryTemplateBuilder() - .notRetryOn(IllegalArgumentException::class.java) - .notRetryOn(HttpClientErrorException.BadRequest::class.java) - .notRetryOn(HttpClientErrorException.UnprocessableContent::class.java) - .exponentialBackoff(2.seconds.toJavaDuration(), 1.25, 5.seconds.toJavaDuration()) - .customPolicy(SimpleRetryPolicy(configProperties.maxRetryAttempts)) - .withListener( - object : RetryListener { - override fun onError( - context: RetryContext, - callback: RetryCallback, - throwable: Throwable, - ) { - logger.warn("Error occured: {}. Retrying {}", throwable.message, context.retryCount) - } - } + @Bean + fun buildinPseudonymGenerator(): Generator { + logger.info("Selected 'BUILDIN Pseudonym Generator'") + return AnonymizingGenerator() + } + + @Bean + fun pseudonymizeService( + generator: Generator, + pseudonymizeConfigProperties: PseudonymizeConfigProperties, + ): PseudonymizeService { + return PseudonymizeService(generator, pseudonymizeConfigProperties) + } + + @Bean + fun reportService(): ReportService { + return ReportService(getJsonMapper()) + } + + @Bean + fun getJsonMapper(): JsonMapper { + return JacksonConfig().jsonMapper() + } + + @Bean + fun transformationService(configProperties: AppConfigProperties): TransformationService { + logger.info("Apply ${configProperties.transformations.size} transformation rules") + return TransformationService( + getJsonMapper(), + configProperties.transformations.map { Transformation.of(it.path) from it.from to it.to }, ) - .build() - } - - @ConditionalOnProperty(value = ["app.security.enable-tokens"], havingValue = "true") - @Bean - fun tokenService( - userDetailsManager: InMemoryUserDetailsManager, - passwordEncoder: PasswordEncoder, - tokenRepository: TokenRepository, - ): TokenService { - return TokenService(userDetailsManager, passwordEncoder, tokenRepository) - } - - @Bean - fun statisticsUpdateProducer(): Sinks.Many { - return Sinks.many().multicast().directBestEffort() - } - - @Bean - fun connectionCheckUpdateProducer(): Sinks.Many { - return Sinks.many().multicast().onBackpressureBuffer() - } - - @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") - @Bean - fun gPasConnectionCheckService( - restTemplate: RestTemplate, - gPasConfigProperties: GPasConfigProperties, - connectionCheckUpdateProducer: Sinks.Many, - ): ConnectionCheckService { - return GPasConnectionCheckService( - restTemplate, - gPasConfigProperties, - connectionCheckUpdateProducer, - ) - } - - @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "GPAS") - @ConditionalOnMissingBean - @Bean - fun gPasConnectionCheckServiceOnDeprecatedProperty( - restTemplate: RestTemplate, - gPasConfigProperties: GPasConfigProperties, - connectionCheckUpdateProducer: Sinks.Many, - ): ConnectionCheckService { - return GPasConnectionCheckService( - restTemplate, - gPasConfigProperties, - connectionCheckUpdateProducer, - ) - } - - @Bean - fun jdbcConfiguration(): AbstractJdbcConfiguration { - return AppJdbcConfiguration() - } - - @Conditional(GicsEnabledCondition::class) - @Bean - fun gicsConsentService( - gIcsConfigProperties: GIcsConfigProperties, - retryTemplate: RetryTemplate, - restTemplate: RestTemplate, - appFhirConfig: AppFhirConfig, - ): IConsentService { - return GicsConsentService(gIcsConfigProperties, retryTemplate, restTemplate, appFhirConfig) - } - - @Conditional(GicsGetBroadConsentEnabledCondition::class) - @Bean - fun gicsGetBroadConsentService( - gIcsConfigProperties: GIcsConfigProperties, - retryTemplate: RetryTemplate, - restTemplate: RestTemplate, - appFhirConfig: AppFhirConfig, - ): IConsentService { - return GicsGetBroadConsentService( - gIcsConfigProperties, - retryTemplate, - restTemplate, - appFhirConfig, - ) - } - - @Conditional(GicsEnabledCondition::class) - @Bean - fun consentProcessor( - configProperties: AppConfigProperties, - gIcsConfigProperties: GIcsConfigProperties, - getObjectMapper: ObjectMapper, - appFhirConfig: AppFhirConfig, - gicsConsentService: IConsentService, - ): ConsentProcessor { - return ConsentProcessor( - configProperties, - gIcsConfigProperties, - getObjectMapper, - appFhirConfig.fhirContext(), - gicsConsentService, - ) - } - - @Conditional(GicsEnabledCondition::class) - @Bean - fun gIcsConnectionCheckService( - restTemplate: RestTemplate, - gIcsConfigProperties: GIcsConfigProperties, - connectionCheckUpdateProducer: Sinks.Many, - ): ConnectionCheckService { - return GIcsConnectionCheckService( - restTemplate, - gIcsConfigProperties, - connectionCheckUpdateProducer, - ) - } + } + + @Bean + fun retryTemplate(configProperties: AppConfigProperties): RetryTemplate { + return RetryTemplateBuilder() + .notRetryOn(IllegalArgumentException::class.java) + .notRetryOn(HttpClientErrorException.BadRequest::class.java) + .notRetryOn(HttpClientErrorException.UnprocessableContent::class.java) + .exponentialBackoff(2.seconds.toJavaDuration(), 1.25, 5.seconds.toJavaDuration()) + .customPolicy(SimpleRetryPolicy(configProperties.maxRetryAttempts)) + .withListener( + object : RetryListener { + override fun onError( + context: RetryContext, + callback: RetryCallback, + throwable: Throwable, + ) { + logger.warn("Error occured: {}. Retrying {}", throwable.message, context.retryCount) + } + } + ) + .build() + } + + @ConditionalOnProperty(value = ["app.security.enable-tokens"], havingValue = "true") + @Bean + fun tokenService( + userDetailsManager: InMemoryUserDetailsManager, + passwordEncoder: PasswordEncoder, + tokenRepository: TokenRepository, + ): TokenService { + return TokenService(userDetailsManager, passwordEncoder, tokenRepository) + } + + @Bean + fun statisticsUpdateProducer(): Sinks.Many { + return Sinks.many().multicast().directBestEffort() + } + + @Bean + fun connectionCheckUpdateProducer(): Sinks.Many { + return Sinks.many().multicast().onBackpressureBuffer() + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @Bean + fun gPasConnectionCheckService( + restTemplate: RestTemplate, + gPasConfigProperties: GPasConfigProperties, + connectionCheckUpdateProducer: Sinks.Many, + ): ConnectionCheckService { + return GPasConnectionCheckService( + restTemplate, + gPasConfigProperties, + connectionCheckUpdateProducer, + ) + } + + @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "GPAS") + @ConditionalOnMissingBean + @Bean + fun gPasConnectionCheckServiceOnDeprecatedProperty( + restTemplate: RestTemplate, + gPasConfigProperties: GPasConfigProperties, + connectionCheckUpdateProducer: Sinks.Many, + ): ConnectionCheckService { + return GPasConnectionCheckService( + restTemplate, + gPasConfigProperties, + connectionCheckUpdateProducer, + ) + } + + @Bean + fun jdbcConfiguration(): AbstractJdbcConfiguration { + return AppJdbcConfiguration() + } + + @Conditional(GicsEnabledCondition::class) + @Bean + fun gicsConsentService( + gIcsConfigProperties: GIcsConfigProperties, + retryTemplate: RetryTemplate, + restTemplate: RestTemplate, + appFhirConfig: AppFhirConfig, + ): IConsentService { + return GicsConsentService(gIcsConfigProperties, retryTemplate, restTemplate, appFhirConfig) + } + + @Conditional(GicsGetBroadConsentEnabledCondition::class) + @Bean + fun gicsGetBroadConsentService( + gIcsConfigProperties: GIcsConfigProperties, + retryTemplate: RetryTemplate, + restTemplate: RestTemplate, + appFhirConfig: AppFhirConfig, + ): IConsentService { + return GicsGetBroadConsentService( + gIcsConfigProperties, + retryTemplate, + restTemplate, + appFhirConfig, + ) + } + + @Conditional(GicsEnabledCondition::class) + @Bean + fun consentProcessor( + configProperties: AppConfigProperties, + gIcsConfigProperties: GIcsConfigProperties, + getObjectMapper: JsonMapper, + appFhirConfig: AppFhirConfig, + gicsConsentService: IConsentService, + ): ConsentProcessor { + return ConsentProcessor( + configProperties, + gIcsConfigProperties, + getObjectMapper, + appFhirConfig.fhirContext(), + gicsConsentService, + ) + } + + @Conditional(GicsEnabledCondition::class) + @Bean + fun gIcsConnectionCheckService( + restTemplate: RestTemplate, + gIcsConfigProperties: GIcsConfigProperties, + connectionCheckUpdateProducer: Sinks.Many, + ): ConnectionCheckService { + return GIcsConnectionCheckService( + restTemplate, + gIcsConfigProperties, + connectionCheckUpdateProducer, + ) + } - @Bean - @ConditionalOnMissingBean - fun iGetConsentService(): IConsentService { - return MtbFileConsentService() - } + @Bean + @ConditionalOnMissingBean + fun iGetConsentService(): IConsentService { + return MtbFileConsentService() + } } class GicsEnabledCondition : AnyNestedCondition(ConfigurationCondition.ConfigurationPhase.REGISTER_BEAN) { - @ConditionalOnProperty(name = ["app.consent.service"], havingValue = "gics") - @ConditionalOnProperty(name = ["app.consent.gics.uri"]) - class OnGicsServiceSelected { - // Just for Condition - } + @ConditionalOnProperty(name = ["app.consent.service"], havingValue = "gics") + @ConditionalOnProperty(name = ["app.consent.gics.uri"]) + class OnGicsServiceSelected { + // Just for Condition + } } class GicsGetBroadConsentEnabledCondition : AnyNestedCondition(ConfigurationCondition.ConfigurationPhase.REGISTER_BEAN) { - @ConditionalOnProperty(name = ["app.consent.service"], havingValue = "gics_get_bc") - @ConditionalOnProperty(name = ["app.consent.gics.uri"]) - class OnGicsGetBroadConsentServiceSelected { - // Just for Condition - } + @ConditionalOnProperty(name = ["app.consent.service"], havingValue = "gics_get_bc") + @ConditionalOnProperty(name = ["app.consent.gics.uri"]) + class OnGicsGetBroadConsentServiceSelected { + // Just for Condition + } } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt index 052822e..a7e6378 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppFhirConfig.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.config import ca.uhn.fhir.context.FhirContext diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt index 769faf3..a5bf1ae 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppJdbcConfiguration.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.config import dev.dnpm.etl.processor.Fingerprint diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt index 24fc58c..aa3e6cb 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,7 +20,6 @@ package dev.dnpm.etl.processor.config -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.consent.ConsentEvaluator import dev.dnpm.etl.processor.input.KafkaInputListener import dev.dnpm.etl.processor.monitoring.ConnectionCheckResult @@ -43,6 +43,7 @@ import org.springframework.kafka.listener.ContainerProperties import org.springframework.kafka.listener.KafkaMessageListenerContainer import org.springframework.retry.support.RetryTemplate import reactor.core.publisher.Sinks +import tools.jackson.databind.json.JsonMapper @Configuration @EnableConfigurationProperties(value = [KafkaProperties::class]) @@ -57,10 +58,10 @@ class AppKafkaConfiguration { kafkaTemplate: KafkaTemplate, kafkaProperties: KafkaProperties, retryTemplate: RetryTemplate, - objectMapper: ObjectMapper, + jsonMapper: JsonMapper, ): MtbFileSender { logger.info("Selected 'KafkaMtbFileSender'") - return KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper) + return KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, jsonMapper) } @Bean @@ -77,8 +78,8 @@ class AppKafkaConfiguration { @Bean fun kafkaResponseProcessor( applicationEventPublisher: ApplicationEventPublisher, - objectMapper: ObjectMapper, - ): KafkaResponseProcessor = KafkaResponseProcessor(applicationEventPublisher, objectMapper) + jsonMapper: JsonMapper, + ): KafkaResponseProcessor = KafkaResponseProcessor(applicationEventPublisher, jsonMapper) @Bean @ConditionalOnProperty(value = ["app.kafka.input-topic"]) @@ -99,9 +100,9 @@ class AppKafkaConfiguration { @ConditionalOnProperty(value = ["app.kafka.input-topic"]) fun kafkaInputListener( requestProcessor: RequestProcessor, - objectMapper: ObjectMapper, + jsonMapper: JsonMapper, consentEvaluator: ConsentEvaluator, - ): KafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, objectMapper) + ): KafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, jsonMapper) @Bean fun kafkaConnectionCheckService( diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt index 565209e..d10a32f 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt index 60b1a9c..b87a1bd 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppWebConfig.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppWebConfig.kt index 3aa50f2..7efa5c8 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppWebConfig.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppWebConfig.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.config import org.springframework.boot.convert.ApplicationConversionService diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AuditConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AuditConfiguration.kt index 45763ee..1f392c0 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AuditConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AuditConfiguration.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.config import org.springframework.context.annotation.Bean diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/Jackson2Config.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/Jackson2Config.kt new file mode 100644 index 0000000..27204d0 --- /dev/null +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/Jackson2Config.kt @@ -0,0 +1,85 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.dnpm.etl.processor.config + +import ca.uhn.fhir.context.FhirContext +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import org.hl7.fhir.r4.model.Consent +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * @deprecated Use JacksonConfig instead + * @since 0.16 + */ +@Deprecated("Use JacksonConfig instead") +@Configuration +class Jackson2Config { + companion object { + var fhirContext: FhirContext = FhirContext.forR4() + + @JvmStatic fun fhirContext(): FhirContext = fhirContext + } + + @Bean + fun objectMapper(): com.fasterxml.jackson.databind.ObjectMapper = + com.fasterxml.jackson.databind + .ObjectMapper() + .registerModule(Jackson2FhirResourceModule()) + .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .registerModule(JavaTimeModule()) + .registerModule(Jdk8Module()) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) +} + +class Jackson2FhirResourceModule : com.fasterxml.jackson.databind.module.SimpleModule() { + init { + addSerializer(Consent::class.java, Jackson2ConsentResourceSerializer()) + addDeserializer(Consent::class.java, Jackson2ConsentResourceDeserializer()) + } +} + +class Jackson2ConsentResourceSerializer : com.fasterxml.jackson.databind.JsonSerializer() { + override fun serialize( + value: Consent, + gen: JsonGenerator, + serializers: com.fasterxml.jackson.databind.SerializerProvider, + ) { + val json = Jackson2Config.fhirContext().newJsonParser().encodeResourceToString(value) + gen.writeRawValue(json) + } +} + +class Jackson2ConsentResourceDeserializer : com.fasterxml.jackson.databind.JsonDeserializer() { + override fun deserialize( + p: JsonParser?, + ctxt: com.fasterxml.jackson.databind.DeserializationContext?, + ): Consent { + val jsonNode = p?.readValueAsTree() + val json = jsonNode?.toString() + + return Jackson2Config.fhirContext().newJsonParser().parseResource(json) as Consent + } +} diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt index 847880d..94f08c7 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/JacksonConfig.kt @@ -1,19 +1,38 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.config import ca.uhn.fhir.context.FhirContext import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.* -import com.fasterxml.jackson.databind.module.SimpleModule -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import org.hl7.fhir.r4.model.Consent import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import tools.jackson.databind.* +import tools.jackson.databind.json.JsonMapper +import tools.jackson.databind.module.SimpleModule +import tools.jackson.module.kotlin.KotlinModule @Configuration class JacksonConfig { + companion object { var fhirContext: FhirContext = FhirContext.forR4() @@ -21,36 +40,38 @@ class JacksonConfig { } @Bean - fun objectMapper(): ObjectMapper = - ObjectMapper() - .registerModule(Jackson2FhirResourceModule()) - .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - .registerModule(JavaTimeModule()) - .registerModule(Jdk8Module()) - .setSerializationInclusion(JsonInclude.Include.NON_NULL) + fun jsonMapper(): JsonMapper = + JsonMapper + .builder() + .addModule(JacksonFhirResourceModule()) + .addModule(KotlinModule.Builder().build()) + .changeDefaultPropertyInclusion { + it.withContentInclusion(JsonInclude.Include.NON_NULL) + it.withValueInclusion(JsonInclude.Include.NON_NULL) + }.build() } -class Jackson2FhirResourceModule : SimpleModule() { +class JacksonFhirResourceModule : SimpleModule() { init { - addSerializer(Consent::class.java, Jackson2ConsentResourceSerializer()) - addDeserializer(Consent::class.java, Jackson2ConsentResourceDeserializer()) + addSerializer(Consent::class.java, JacksonConsentResourceSerializer()) + addDeserializer(Consent::class.java, JacksonConsentResourceDeserializer()) } } -class Jackson2ConsentResourceSerializer : JsonSerializer() { +class JacksonConsentResourceSerializer : ValueSerializer() { override fun serialize( - value: Consent, - gen: JsonGenerator, - serializers: SerializerProvider, + value: Consent?, + gen: tools.jackson.core.JsonGenerator?, + ctxt: SerializationContext?, ) { val json = JacksonConfig.fhirContext().newJsonParser().encodeResourceToString(value) - gen.writeRawValue(json) + gen?.writeRawValue(json) } } -class Jackson2ConsentResourceDeserializer : JsonDeserializer() { +class JacksonConsentResourceDeserializer : ValueDeserializer() { override fun deserialize( - p: JsonParser?, + p: tools.jackson.core.JsonParser?, ctxt: DeserializationContext?, ): Consent { val jsonNode = p?.readValueAsTree() diff --git a/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt b/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt index 58f647f..a8b8fdb 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/functions.kt b/src/main/kotlin/dev/dnpm/etl/processor/functions.kt index aa4fc75..8326ad0 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/functions.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/functions.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2026 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt b/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt index 03cd03d..a126e07 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,7 +20,6 @@ package dev.dnpm.etl.processor.input -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.PatientId import dev.dnpm.etl.processor.RequestId @@ -31,12 +31,13 @@ import org.apache.kafka.clients.consumer.ConsumerRecord import org.slf4j.LoggerFactory import org.springframework.http.MediaType import org.springframework.kafka.listener.MessageListener +import tools.jackson.databind.json.JsonMapper import java.nio.charset.Charset class KafkaInputListener( private val requestProcessor: RequestProcessor, private val consentEvaluator: ConsentEvaluator, - private val objectMapper: ObjectMapper, + private val jsonMapper: JsonMapper, ) : MessageListener { private val logger = LoggerFactory.getLogger(KafkaInputListener::class.java) @@ -71,7 +72,7 @@ class KafkaInputListener( private fun handleDnpmV2Message(record: ConsumerRecord) { try { - val mtbFile = objectMapper.readValue(record.value(), Mtb::class.java) + val mtbFile = jsonMapper.readValue(record.value(), Mtb::class.java) val patientId = PatientId(mtbFile.patient.id) val firstRequestIdHeader = record.headers().headers("requestId")?.firstOrNull() val requestId = diff --git a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt index c9825c7..47f0766 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt index a88cf10..c9c73a2 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt index c54aa7a..b9359fc 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ReportService.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,64 +20,61 @@ package dev.dnpm.etl.processor.monitoring -import com.fasterxml.jackson.annotation.JsonAlias -import com.fasterxml.jackson.annotation.JsonIgnoreProperties -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.annotation.JsonValue -import com.fasterxml.jackson.core.JsonParseException -import com.fasterxml.jackson.databind.JsonMappingException -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.monitoring.ReportService.Issue import dev.dnpm.etl.processor.monitoring.ReportService.Severity +import tools.jackson.core.JacksonException +import tools.jackson.databind.EnumNamingStrategies +import tools.jackson.databind.annotation.EnumNaming +import tools.jackson.databind.json.JsonMapper import java.util.* -class ReportService(private val objectMapper: ObjectMapper) { +class ReportService(private val jsonMapper: JsonMapper) { - fun deserialize(dataQualityReport: String?): List { - if (dataQualityReport.isNullOrBlank()) { - return listOf() + fun deserialize(dataQualityReport: String?): List { + if (dataQualityReport.isNullOrBlank()) { + return listOf() + } + return try { + jsonMapper.readValue(dataQualityReport, DataQualityReport::class.java).issues.sortedBy { + it.severity + } + } catch (_: JacksonException) { + val otherIssue = + Issue(Severity.ERROR, Optional.of("Not parsable data quality report '$dataQualityReport'")) + return listOf(otherIssue) + } catch (e: Exception) { + throw e + } } - return try { - objectMapper.readValue(dataQualityReport, DataQualityReport::class.java).issues.sortedBy { - it.severity - } - } catch (e: Exception) { - val otherIssue = - Issue(Severity.ERROR, "Not parsable data quality report '$dataQualityReport'") - return when (e) { - is JsonMappingException -> listOf(otherIssue) - is JsonParseException -> listOf(otherIssue) - else -> throw e - } - } - } - @JsonIgnoreProperties(ignoreUnknown = true) - private data class DataQualityReport( - @param:JsonProperty(value = "issues") val issues: List - ) + private data class DataQualityReport( + val issues: List + ) - @JsonIgnoreProperties(ignoreUnknown = true) - data class Issue( - @param:JsonProperty(value = "severity") val severity: Severity, - @param:JsonProperty(value = "message") @param:JsonAlias("details") val message: String, - @param:JsonProperty(value = "path") val path: Optional = Optional.empty(), - ) + data class Issue( + val severity: Severity, + val message: Optional = Optional.empty(), + val details: Optional = Optional.empty(), + val path: Optional = Optional.empty(), + ) { + fun getMessage() = message.orElse(details.orElse("No details available")) + } - enum class Severity(@JsonValue val value: String) { - FATAL("fatal"), - ERROR("error"), - WARNING("warning"), - INFO("info"), - } + @EnumNaming(EnumNamingStrategies.LowerCaseStrategy::class) + enum class Severity(val value: String) { + FATAL("fatal"), + ERROR("error"), + WARNING("warning"), + INFO("info"), + } } fun List.asRequestStatus(): RequestStatus { - val severity = this.minOfOrNull { it.severity } - return when (severity) { - Severity.FATAL, - Severity.ERROR -> RequestStatus.ERROR - Severity.WARNING -> RequestStatus.WARNING - else -> RequestStatus.SUCCESS - } + val severity = this.minOfOrNull { it.severity } + return when (severity) { + Severity.FATAL, + Severity.ERROR -> RequestStatus.ERROR + Severity.WARNING -> RequestStatus.WARNING + else -> RequestStatus.SUCCESS + } } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt index 8b2776a..a1f5c3d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt index a0cd4ad..f1bf304 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt index ef7f1e3..9611232 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestType.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/SubmissionType.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/SubmissionType.kt index 351281e..4001b7e 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/SubmissionType.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/SubmissionType.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.monitoring enum class SubmissionType( diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt index bcd532f..8c20b7c 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,7 +20,6 @@ package dev.dnpm.etl.processor.output -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.config.KafkaProperties import dev.dnpm.etl.processor.monitoring.RequestStatus @@ -29,12 +29,13 @@ import org.apache.kafka.clients.producer.ProducerRecord import org.slf4j.LoggerFactory import org.springframework.kafka.core.KafkaTemplate import org.springframework.retry.support.RetryTemplate +import tools.jackson.databind.json.JsonMapper class KafkaMtbFileSender( private val kafkaTemplate: KafkaTemplate, private val kafkaProperties: KafkaProperties, private val retryTemplate: RetryTemplate, - private val objectMapper: ObjectMapper, + private val jsonMapper: JsonMapper, ) : MtbFileSender { private val logger = LoggerFactory.getLogger(KafkaMtbFileSender::class.java) @@ -45,7 +46,7 @@ class KafkaMtbFileSender( ProducerRecord( kafkaProperties.outputTopic, key(request), - objectMapper.writeValueAsString(request.content), + jsonMapper.writeValueAsString(request.content), ) record.headers().add("requestId", request.requestId.value.toByteArray()) record.headers().add("requestMethod", "POST".toByteArray()) @@ -82,7 +83,7 @@ class KafkaMtbFileSender( ProducerRecord( kafkaProperties.outputTopic, key(request), - objectMapper.writeValueAsString( + jsonMapper.writeValueAsString( DnpmV2MtbFileRequest(request.requestId, dummyMtbFile), ), ) diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt index c81b572..3d0a8dd 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt index b228c4c..75401e6 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt index 5aad133..89232bd 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSender.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt index 0d8ed7b..7e25827 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt @@ -2,7 +2,7 @@ * This file is part of ETL-Processor * * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken - * Copyright (c) 2023-2025 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt index 90d867f..b3c80e2 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/AnonymizingGenerator.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt index 089736c..589a2d4 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapPseudonymGenerator.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.pseudonym import dev.dnpm.etl.processor.config.AppFhirConfig diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt index f1121b8..4daba3a 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/GpasSoapService.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.pseudonym import jakarta.jws.WebMethod diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt index 77ab87d..9eec9f3 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt index d8969c1..6615b35 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt index fd75446..74f3127 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt index bfe966a..c67c89d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (C) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -15,7 +16,6 @@ * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . - * */ package dev.dnpm.etl.processor.security diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt index bf46b84..0c1fe11 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt index 4a9a6bd..a95420a 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt @@ -1,9 +1,27 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.services import ca.uhn.fhir.context.FhirContext import com.fasterxml.jackson.core.JsonProcessingException -import com.fasterxml.jackson.core.type.TypeReference -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.config.GIcsConfigProperties import dev.dnpm.etl.processor.consent.ConsentDomain @@ -11,10 +29,6 @@ import dev.dnpm.etl.processor.consent.IConsentService import dev.dnpm.etl.processor.consent.MtbFileConsentService import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized import dev.pcvolkmer.mv64e.mtb.* -import java.io.IOException -import java.time.Clock -import java.time.Instant -import java.util.* import org.apache.commons.lang3.NotImplementedException import org.hl7.fhir.r4.model.* import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent @@ -23,12 +37,17 @@ import org.hl7.fhir.r4.model.Consent.ProvisionComponent import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.stereotype.Service +import tools.jackson.databind.json.JsonMapper +import java.io.IOException +import java.time.Clock +import java.time.Instant +import java.util.* @Service class ConsentProcessor( private val appConfigProperties: AppConfigProperties, private val gIcsConfigProperties: GIcsConfigProperties, - private val objectMapper: ObjectMapper, + private val jsonMapper: JsonMapper, private val fhirContext: FhirContext, private val consentService: IConsentService, ) { @@ -119,9 +138,9 @@ class ConsentProcessor( val asJsonString = fhirContext.newJsonParser().encodeResourceToString(resource) try { val mapOfJson: MvhMetadata.ResearchConsent? = - objectMapper.readValue( + jsonMapper.readValue( asJsonString, - object : TypeReference() {}, + MvhMetadata.ResearchConsent::class.java, ) mtbFile.metadata.researchConsents.add(mapOfJson) } catch (e: JsonProcessingException) { diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt index f0eed7f..b94fbbc 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt @@ -2,7 +2,7 @@ * This file is part of ETL-Processor * * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken - * Copyright (c) 2023-2026 Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -20,7 +20,6 @@ package dev.dnpm.etl.processor.services -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.* import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.consent.TtpConsentStatus @@ -41,6 +40,7 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service +import tools.jackson.databind.json.JsonMapper import java.time.Instant import java.util.* @@ -50,7 +50,7 @@ class RequestProcessor( private val transformationService: TransformationService, private val sender: MtbFileSender, private val requestService: RequestService, - private val objectMapper: ObjectMapper, + private val jsonMapper: JsonMapper, private val applicationEventPublisher: ApplicationEventPublisher, private val appConfigProperties: AppConfigProperties, private val consentProcessor: ConsentProcessor?, @@ -292,7 +292,7 @@ class RequestProcessor( private fun fingerprint(request: MtbFileRequest): Fingerprint { return when (request) { - is DnpmV2MtbFileRequest -> fingerprint(objectMapper.writeValueAsString(request.content)) + is DnpmV2MtbFileRequest -> fingerprint(jsonMapper.writeValueAsString(request.content)) } } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt index eb57a6c..b967f2d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt index 3ecac71..947ec17 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt index df8ac3d..755e6b3 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,18 +20,18 @@ package dev.dnpm.etl.processor.services -import com.fasterxml.jackson.databind.ObjectMapper import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.PathNotFoundException import dev.pcvolkmer.mv64e.mtb.Mtb +import tools.jackson.databind.json.JsonMapper class TransformationService( - private val objectMapper: ObjectMapper, + private val jsonMapper: JsonMapper, private val transformations: List, ) { fun transform(mtbFile: Mtb): Mtb { - val json = transform(objectMapper.writeValueAsString(mtbFile)) - return objectMapper.readValue(json, Mtb::class.java) + val json = transform(jsonMapper.writeValueAsString(mtbFile)) + return jsonMapper.readValue(json, Mtb::class.java) } private fun transform(content: String): String { @@ -61,7 +62,7 @@ class TransformationService( newValue, { it.item(HashMap::class.java)[last] == existingValue }, ) - } catch (e: PathNotFoundException) { + } catch (_: PathNotFoundException) { // Ignore } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt index e70f1e7..41f7720 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,71 +20,70 @@ package dev.dnpm.etl.processor.services.kafka -import com.fasterxml.jackson.annotation.JsonAlias -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.output.asRequestStatus import dev.dnpm.etl.processor.services.ResponseEvent -import java.time.Instant -import java.util.* import org.apache.kafka.clients.consumer.ConsumerRecord import org.slf4j.LoggerFactory import org.springframework.context.ApplicationEventPublisher import org.springframework.kafka.listener.MessageListener +import tools.jackson.databind.PropertyNamingStrategies +import tools.jackson.databind.annotation.JsonNaming +import tools.jackson.databind.json.JsonMapper +import java.time.Instant +import java.util.* class KafkaResponseProcessor( private val eventPublisher: ApplicationEventPublisher, - private val objectMapper: ObjectMapper, + private val jsonMapper: JsonMapper, ) : MessageListener { - private val logger = LoggerFactory.getLogger(KafkaResponseProcessor::class.java) + private val logger = LoggerFactory.getLogger(KafkaResponseProcessor::class.java) - override fun onMessage(data: ConsumerRecord) { - try { - Optional.of(objectMapper.readValue(data.value(), ResponseBody::class.java)) + override fun onMessage(data: ConsumerRecord) { + try { + Optional.of(jsonMapper.readValue(data.value(), ResponseBody::class.java)) } catch (e: Exception) { - logger.error("Cannot process Kafka response", e) - Optional.empty() + logger.error("Cannot process Kafka response", e) + Optional.empty() } - .ifPresentOrElse( - { responseBody -> - val event = - ResponseEvent( - RequestId(responseBody.requestId), - Instant.ofEpochMilli(data.timestamp()), - responseBody.statusCode.asRequestStatus(), - when (responseBody.statusCode.asRequestStatus()) { - RequestStatus.SUCCESS -> { - Optional.empty() - } + .ifPresentOrElse( + { responseBody -> + val event = + ResponseEvent( + RequestId(responseBody.requestId), + Instant.ofEpochMilli(data.timestamp()), + responseBody.statusCode.asRequestStatus(), + when (responseBody.statusCode.asRequestStatus()) { + RequestStatus.SUCCESS -> { + Optional.empty() + } - RequestStatus.WARNING, - RequestStatus.ERROR -> { - Optional.of(objectMapper.writeValueAsString(responseBody.statusBody)) - } + RequestStatus.WARNING, + RequestStatus.ERROR -> { + Optional.of(jsonMapper.writeValueAsString(responseBody.statusBody)) + } - else -> { - logger.error( - "Kafka response: Unknown response code '{}'!", - responseBody.statusCode, - ) - Optional.empty() - } - }, - ) - eventPublisher.publishEvent(event) - }, - { logger.error("No requestId in Kafka response") }, - ) - } + else -> { + logger.error( + "Kafka response: Unknown response code '{}'!", + responseBody.statusCode, + ) + Optional.empty() + } + }, + ) + eventPublisher.publishEvent(event) + }, + { logger.error("No requestId in Kafka response") }, + ) + } - data class ResponseBody( - @param:JsonProperty("request_id") @param:JsonAlias("requestId") val requestId: String, - @param:JsonProperty("status_code") @param:JsonAlias("statusCode") val statusCode: Int, - @param:JsonProperty("status_body") - @param:JsonAlias("statusBody") - val statusBody: Map, - ) + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) + data class ResponseBody( + val requestId: String, + val statusCode: Int, + val statusBody: Map, + ) } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/types.kt b/src/main/kotlin/dev/dnpm/etl/processor/types.kt index 9244297..9abd25d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/types.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/types.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt index e81b348..508d12b 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/ApplicationControllerAdvice.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt index b77bdf9..5bd25b7 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt index 6622348..6347735 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt index 9b49f5a..c62553a 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/LoginController.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt index 8dfe595..4cb99f1 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsController.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt index 8843d8e..3ea9667 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/StatisticsRestController.kt @@ -1,7 +1,7 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify @@ -20,8 +20,6 @@ package dev.dnpm.etl.processor.web -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.annotation.JsonPropertyOrder import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.monitoring.RequestType import dev.dnpm.etl.processor.services.RequestService @@ -34,6 +32,8 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import reactor.core.publisher.Flux import reactor.core.publisher.Sinks +import tools.jackson.databind.PropertyNamingStrategies +import tools.jackson.databind.annotation.JsonNaming import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter @@ -201,15 +201,13 @@ data class DateNameValues( val nameValues: NameValues, ) -@JsonPropertyOrder(value = ["error", "warning", "success", "no_consent", "duplication", "blocked_initial", "unknown"]) +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) data class NameValues( val error: Int = 0, val warning: Int = 0, val success: Int = 0, - @field:JsonProperty("no_consent") val noConsent: Int = 0, val duplication: Int = 0, - @field:JsonProperty("blocked_initial") val blockedInitial: Int = 0, val unknown: Int = 0, ) diff --git a/src/main/resources/static/icon.svg b/src/main/resources/static/icon.svg index 90c3a65..7b36666 100644 --- a/src/main/resources/static/icon.svg +++ b/src/main/resources/static/icon.svg @@ -1,6 +1,4 @@ - - - diff --git a/src/main/resources/templates/report.html b/src/main/resources/templates/report.html index bad8ca2..bdc7457 100644 --- a/src/main/resources/templates/report.html +++ b/src/main/resources/templates/report.html @@ -34,7 +34,7 @@ [[ ${issue.severity} ]] [[ ${issue.severity} ]] -
[[ ${issue.message} ]]
+
[[ ${issue.getMessage()} ]]
[[ ${issue.path.get()} ]]
Keine Angabe
diff --git a/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java b/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java index 22f676b..1f9e698 100644 --- a/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java +++ b/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; import static dev.dnpm.etl.processor.consent.GicsConsentService.IS_CONSENTED_ENDPOINT; diff --git a/src/test/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentServiceTest.java b/src/test/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentServiceTest.java index 46da158..e97a8d4 100644 --- a/src/test/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentServiceTest.java +++ b/src/test/java/dev/dnpm/etl/processor/consent/GicsGetBroadConsentServiceTest.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluatorTest.java b/src/test/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluatorTest.java index e63dddc..ebaf043 100644 --- a/src/test/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluatorTest.java +++ b/src/test/java/dev/dnpm/etl/processor/consent/MiiBroadConsentEvaluatorTest.java @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent; import static org.assertj.core.api.Assertions.assertThat; diff --git a/src/test/kotlin/dev/dnpm/etl/processor/FunctionsTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/FunctionsTest.kt index 716529b..95ef0d4 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/FunctionsTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/FunctionsTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2026 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt new file mode 100644 index 0000000..b940537 --- /dev/null +++ b/src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt @@ -0,0 +1,92 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.dnpm.etl.processor.config + +import com.fasterxml.jackson.databind.node.ObjectNode +import dev.pcvolkmer.mv64e.mtb.Mtb +import dev.pcvolkmer.mv64e.mtb.MvhMetadata +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import java.util.* + +class Jackson3ConfigTest { + + lateinit var jacksonConfig: JacksonConfig + + @BeforeEach + fun setup() { + this.jacksonConfig = JacksonConfig() + } + + @ParameterizedTest + @ValueSource(strings = [ + "mv64e-mtb-fake-patient.json", + "fake_broadConsent_mii_response_deny.json", + "fake_broadConsent_mii_response_permit.json", + ]) + fun shouldSerializeJsonWithoutNulledOutFields(filename: String) { + val inputJson = + Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream(filename))?.readAllBytes()?.decodeToString() + + val jsonNode = this.jacksonConfig.jsonMapper().readTree(inputJson) + val actual = this.jacksonConfig.jsonMapper().writeValueAsString(jsonNode) + + assertThat(actual).doesNotContain("null") + } + + @ParameterizedTest + @ValueSource(strings = [ + "fake_broadConsent_mii_response_deny.json", + "fake_broadConsent_mii_response_permit.json", + ]) + fun shouldSerializeConsentWithoutWithoutDatesAsTimestamps(filename: String) { + val inputJson = + Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream(filename))?.readAllBytes()?.decodeToString() + + val json = this.jacksonConfig.jsonMapper().readTree(inputJson) + val actual = this.jacksonConfig.jsonMapper().writeValueAsString(json) + + assertThat(actual).contains(""""lastUpdated":"2025-08-15T11:13:59.143+02:00"""") + } + + @Test + fun shouldSerializeJsonWithBroadConsent() { + val inputMtbFileJson = + Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream("mv64e-mtb-fake-patient.json"))?.readAllBytes()?.decodeToString() + + val inputConsentJson = + Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream("fake_broadConsent_mii_response_permit.json"))?.readAllBytes()?.decodeToString() + + val mtb = this.jacksonConfig.jsonMapper().readValue(inputMtbFileJson, Mtb::class.java) + // Still use Jackson2 ObjectMapper since MTB DTO requires Jackson2 ObjectNode + val consentJsonNode = Jackson2Config().objectMapper().readTree(inputConsentJson) + mtb.metadata = MvhMetadata.builder().researchConsents(listOf(MvhMetadata.ResearchConsent.from(consentJsonNode as ObjectNode))).build() + + val actual = this.jacksonConfig.jsonMapper().writeValueAsString(mtb) + + assertThat(actual).doesNotContain("null") + assertThat(actual).contains(""""lastUpdated":"2025-08-15T11:13:59.143+02:00"""") + assertThat(actual).contains("""{"entry":[{"fullUrl":"http://localhost:8080/ttp-fhir/fhir/gics/Consent/7d3456c2-79b1-11f0-ab27-6ed0ed82d0fd"""") + } +} diff --git a/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt index 9042d8c..e734c6f 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt @@ -1,5 +1,28 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.config +import com.fasterxml.jackson.databind.node.ObjectNode +import dev.pcvolkmer.mv64e.mtb.Mtb +import dev.pcvolkmer.mv64e.mtb.MvhMetadata import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -9,11 +32,11 @@ import java.util.* class JacksonConfigTest { - lateinit var jacksonConfig: JacksonConfig + lateinit var jacksonConfig: Jackson2Config @BeforeEach fun setup() { - this.jacksonConfig = JacksonConfig() + this.jacksonConfig = Jackson2Config() } @ParameterizedTest @@ -26,8 +49,8 @@ class JacksonConfigTest { val inputJson = Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream(filename))?.readAllBytes()?.decodeToString() - val json = this.jacksonConfig.objectMapper().readTree(inputJson) - val actual = this.jacksonConfig.objectMapper().writeValueAsString(json) + val jsonNode = this.jacksonConfig.objectMapper().readTree(inputJson) + val actual = this.jacksonConfig.objectMapper().writeValueAsString(jsonNode) assertThat(actual).doesNotContain("null") } @@ -47,4 +70,23 @@ class JacksonConfigTest { assertThat(actual).contains(""""lastUpdated":"2025-08-15T11:13:59.143+02:00"""") } + @Test + fun shouldSerializeJsonWithBroadConsent() { + val inputMtbFileJson = + Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream("mv64e-mtb-fake-patient.json"))?.readAllBytes()?.decodeToString() + + val inputConsentJson = + Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream("fake_broadConsent_mii_response_permit.json"))?.readAllBytes()?.decodeToString() + + val mtb = this.jacksonConfig.objectMapper().readValue(inputMtbFileJson, Mtb::class.java) + val consentJsonNode = this.jacksonConfig.objectMapper().readTree(inputConsentJson) + mtb.metadata = MvhMetadata.builder().researchConsents(listOf(MvhMetadata.ResearchConsent.from(consentJsonNode as ObjectNode))).build() + + val actual = this.jacksonConfig.objectMapper().writeValueAsString(mtb) + + assertThat(actual).doesNotContain("null") + assertThat(actual).contains(""""lastUpdated":"2025-08-15T11:13:59.143+02:00"""") + assertThat(actual).contains("""{"entry":[{"fullUrl":"http://localhost:8080/ttp-fhir/fhir/gics/Consent/7d3456c2-79b1-11f0-ab27-6ed0ed82d0fd"""") + } + } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt index 1140425..30e3823 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt @@ -1,11 +1,29 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.consent import ca.uhn.fhir.context.FhirContext -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.config.GIcsConfigProperties import dev.dnpm.etl.processor.services.ConsentProcessor -import java.util.* import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.Consent @@ -15,13 +33,15 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension +import tools.jackson.databind.json.JsonMapper +import java.util.* @ExtendWith(MockitoExtension::class) class ConsentProcessorTest { lateinit var consentProcessor: ConsentProcessor - val objectMapper = ObjectMapper() + val jsonMapper = JsonMapper() val fhirContext = FhirContext.forR4() @BeforeEach @@ -33,7 +53,7 @@ class ConsentProcessorTest { ConsentProcessor( appConfigProperties, gIcsConfigProperties, - objectMapper, + jsonMapper, fhirContext, consentService, ) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt index 85a8b3e..284c552 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt index b7bf436..192bf90 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt index 0d3f275..eeb6288 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -43,13 +44,14 @@ import org.mockito.kotlin.anyValueClass import org.mockito.kotlin.firstValue import org.mockito.kotlin.times import org.mockito.kotlin.verify +import tools.jackson.databind.json.JsonMapper import java.util.* @ExtendWith(MockitoExtension::class) class KafkaInputListenerTest { private lateinit var requestProcessor: RequestProcessor - private lateinit var objectMapper: ObjectMapper + private lateinit var jsonMapper: JsonMapper private lateinit var kafkaInputListener: KafkaInputListener @@ -59,9 +61,9 @@ class KafkaInputListenerTest { @Mock consentEvaluator: ConsentEvaluator, ) { this.requestProcessor = requestProcessor - this.objectMapper = ObjectMapper() + this.jsonMapper = JsonMapper() - this.kafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, objectMapper) + this.kafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, jsonMapper) } @Test @@ -88,7 +90,7 @@ class KafkaInputListenerTest { .build() kafkaInputListener.onMessage( - ConsumerRecord("testtopic", 0, 0, "", this.objectMapper.writeValueAsString(mtbFile)) + ConsumerRecord("testtopic", 0, 0, "", this.jsonMapper.writeValueAsString(mtbFile)) ) verify(requestProcessor, times(1)).processMtbFile(any()) @@ -113,7 +115,7 @@ class KafkaInputListenerTest { .build() kafkaInputListener.onMessage( - ConsumerRecord("testtopic", 0, 0, "", this.objectMapper.writeValueAsString(mtbFile)) + ConsumerRecord("testtopic", 0, 0, "", this.jsonMapper.writeValueAsString(mtbFile)) ) verify(requestProcessor, times(1)).processMtbFile(any()) } @@ -153,7 +155,7 @@ class KafkaInputListenerTest { -1, -1, "", - this.objectMapper.writeValueAsString(mtbFile), + this.jsonMapper.writeValueAsString(mtbFile), headers, Optional.empty(), ) @@ -197,7 +199,7 @@ class KafkaInputListenerTest { -1, -1, "", - this.objectMapper.writeValueAsString(mtbFile), + this.jsonMapper.writeValueAsString(mtbFile), headers, Optional.empty(), ) @@ -248,7 +250,7 @@ class KafkaInputListenerTest { -1, -1, "", - this.objectMapper.writeValueAsString(mtbFile), + this.jsonMapper.writeValueAsString(mtbFile), headers, Optional.empty(), ) @@ -303,7 +305,7 @@ class KafkaInputListenerTest { -1, -1, "", - this.objectMapper.writeValueAsString(mtbFile), + this.jsonMapper.writeValueAsString(mtbFile), headers, Optional.empty(), ) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt index c8e5804..9ee4b2b 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt index a380d2a..62ece6c 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.monitoring import dev.dnpm.etl.processor.config.GIcsConfigProperties diff --git a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt index 74d1138..83c2354 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt @@ -1,3 +1,23 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.monitoring import dev.dnpm.etl.processor.config.JacksonConfig @@ -12,8 +32,8 @@ class ReportServiceTest { @BeforeEach fun setUp() { - val jacksonConfig = JacksonConfig() - service = ReportService(jacksonConfig.objectMapper()) + val jackson3Config = JacksonConfig() + service = ReportService(jackson3Config.jsonMapper()) } @Test diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt index ac14366..ee840a8 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,17 +20,12 @@ package dev.dnpm.etl.processor.output -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.PatientPseudonym import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.config.KafkaProperties import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.pcvolkmer.mv64e.mtb.* -import java.time.Instant -import java.util.* -import java.util.concurrent.CompletableFuture.completedFuture -import java.util.concurrent.ExecutionException import org.apache.kafka.clients.producer.ProducerRecord import org.apache.kafka.clients.producer.RecordMetadata import org.apache.kafka.common.TopicPartition @@ -47,6 +43,11 @@ import org.springframework.kafka.core.KafkaTemplate import org.springframework.kafka.support.SendResult import org.springframework.retry.policy.SimpleRetryPolicy import org.springframework.retry.support.RetryTemplateBuilder +import tools.jackson.databind.json.JsonMapper +import java.time.Instant +import java.util.* +import java.util.concurrent.CompletableFuture.completedFuture +import java.util.concurrent.ExecutionException @ExtendWith(MockitoExtension::class) class KafkaMtbFileSenderTest { @@ -58,18 +59,18 @@ class KafkaMtbFileSenderTest { private lateinit var kafkaMtbFileSender: KafkaMtbFileSender - private lateinit var objectMapper: ObjectMapper + private lateinit var jsonMapper: JsonMapper @BeforeEach fun setup(@Mock kafkaTemplate: KafkaTemplate) { val kafkaProperties = KafkaProperties("testtopic") val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() - this.objectMapper = ObjectMapper() + this.jsonMapper = JsonMapper.builder().build() this.kafkaTemplate = kafkaTemplate this.kafkaMtbFileSender = - KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper) + KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, jsonMapper) } @ParameterizedTest @@ -94,7 +95,7 @@ class KafkaMtbFileSenderTest { val kafkaProperties = KafkaProperties("testtopic") val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build() this.kafkaMtbFileSender = - KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.objectMapper) + KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.jsonMapper) doAnswer { if (null != testData.exception) { @@ -126,18 +127,18 @@ class KafkaMtbFileSenderTest { private lateinit var kafkaMtbFileSender: KafkaMtbFileSender - private lateinit var objectMapper: ObjectMapper + private lateinit var jsonMapper: JsonMapper @BeforeEach fun setup(@Mock kafkaTemplate: KafkaTemplate) { val kafkaProperties = KafkaProperties("testtopic") val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() - this.objectMapper = ObjectMapper() + this.jsonMapper = JsonMapper() this.kafkaTemplate = kafkaTemplate this.kafkaMtbFileSender = - KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper) + KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, jsonMapper) } @ParameterizedTest @@ -179,7 +180,7 @@ class KafkaMtbFileSenderTest { .isEqualTo("POST".toByteArray()) assertThat(captor.firstValue.value()).isNotNull assertThat(captor.firstValue.value()) - .isEqualTo(objectMapper.writeValueAsString(dnmpV2kafkaRecordData(TEST_REQUEST_ID))) + .isEqualTo(jsonMapper.writeValueAsString(dnmpV2kafkaRecordData(TEST_REQUEST_ID))) } @Test @@ -212,7 +213,7 @@ class KafkaMtbFileSenderTest { val kafkaProperties = KafkaProperties("testtopic") val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build() this.kafkaMtbFileSender = - KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.objectMapper) + KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.jsonMapper) doAnswer { if (null != testData.exception) { diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt index 2ab0218..d95ced0 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,8 +20,6 @@ package dev.dnpm.etl.processor.output -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.PatientPseudonym import dev.dnpm.etl.processor.RequestId @@ -30,8 +29,6 @@ import dev.dnpm.etl.processor.config.RestTargetProperties import dev.dnpm.etl.processor.monitoring.ReportService import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.pcvolkmer.mv64e.mtb.* -import java.time.Instant -import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -48,252 +45,255 @@ import org.springframework.test.web.client.MockRestServiceServer import org.springframework.test.web.client.match.MockRestRequestMatchers.* import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus import org.springframework.web.client.RestTemplate +import tools.jackson.databind.json.JsonMapper +import java.time.Instant +import java.util.* class RestDipMtbFileSenderTest { - @Nested - inner class DnpmV2ContentRequest { - - private lateinit var mockRestServiceServer: MockRestServiceServer - - private lateinit var restMtbFileSender: RestMtbFileSender - - private var reportService = - ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) - - @BeforeEach - fun setup() { - val restTemplate = RestTemplate() - val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() - - this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) - - this.restMtbFileSender = - RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) - } + @Nested + inner class DnpmV2ContentRequest { + + private lateinit var mockRestServiceServer: MockRestServiceServer + + private lateinit var restMtbFileSender: RestMtbFileSender + + private var reportService = + ReportService(JsonMapper()) + + @BeforeEach + fun setup() { + val restTemplate = RestTemplate() + val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() + + this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) + + this.restMtbFileSender = + RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) + } + + @ParameterizedTest + @MethodSource( + "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#mtbFileRequestWithResponseSource" + ) + fun shouldReturnExpectedResponseForDnpmV2MtbFilePost(requestWithResponse: RequestWithResponse) { + this.mockRestServiceServer + .expect(method(HttpMethod.POST)) + .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record")) + .andExpect( + header( + HttpHeaders.CONTENT_TYPE, + CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE, + ) + ) + .andRespond { + withStatus(requestWithResponse.httpStatus) + .body(requestWithResponse.body) + .createResponse(it) + } - @ParameterizedTest - @MethodSource( - "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#mtbFileRequestWithResponseSource" - ) - fun shouldReturnExpectedResponseForDnpmV2MtbFilePost(requestWithResponse: RequestWithResponse) { - this.mockRestServiceServer - .expect(method(HttpMethod.POST)) - .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record")) - .andExpect( - header( - HttpHeaders.CONTENT_TYPE, - CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE, - ) - ) - .andRespond { - withStatus(requestWithResponse.httpStatus) - .body(requestWithResponse.body) - .createResponse(it) - } - - val response = restMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) - assertThat(response.status).isEqualTo(requestWithResponse.response.status) - assertThat(response.body).isEqualTo(requestWithResponse.response.body) + val response = restMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) + assertThat(response.status).isEqualTo(requestWithResponse.response.status) + assertThat(response.body).isEqualTo(requestWithResponse.response.body) + } } - } - - @Nested - inner class DeleteRequest { - private lateinit var mockRestServiceServer: MockRestServiceServer - - private lateinit var restMtbFileSender: RestMtbFileSender - - private var reportService = - ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) - - @BeforeEach - fun setup() { - val restTemplate = RestTemplate() - val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() + @Nested + inner class DeleteRequest { + + private lateinit var mockRestServiceServer: MockRestServiceServer + + private lateinit var restMtbFileSender: RestMtbFileSender + + private var reportService = + ReportService(JsonMapper()) + + @BeforeEach + fun setup() { + val restTemplate = RestTemplate() + val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() + + this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) + + this.restMtbFileSender = + RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) + } + + @ParameterizedTest + @MethodSource( + "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource" + ) + fun shouldReturnExpectedResponseForDelete(requestWithResponse: RequestWithResponse) { + this.mockRestServiceServer + .expect(method(HttpMethod.DELETE)) + .andExpect( + requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}") + ) + .andRespond { + withStatus(requestWithResponse.httpStatus) + .body(requestWithResponse.body) + .createResponse(it) + } - this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) + val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) + assertThat(response.status).isEqualTo(requestWithResponse.response.status) + assertThat(response.body).isEqualTo(requestWithResponse.response.body) + } + + @ParameterizedTest + @MethodSource( + "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource" + ) + fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) { + val restTemplate = RestTemplate() + val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) + val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties()) + retryTemplate.setBackOffPolicy(NoBackOffPolicy()) + + this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) + this.restMtbFileSender = + RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) + + val expectedCount = + when (requestWithResponse.httpStatus) { + // OK - No Retry + HttpStatus.OK, + HttpStatus.CREATED, + HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.BAD_REQUEST -> ExpectedCount.max(1) + // Request failed - Retry max 3 times + else -> ExpectedCount.max(3) + } - this.restMtbFileSender = - RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) - } + this.mockRestServiceServer + .expect(expectedCount, method(HttpMethod.DELETE)) + .andExpect( + requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}") + ) + .andRespond { + withStatus(requestWithResponse.httpStatus) + .body(requestWithResponse.body) + .createResponse(it) + } - @ParameterizedTest - @MethodSource( - "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource" - ) - fun shouldReturnExpectedResponseForDelete(requestWithResponse: RequestWithResponse) { - this.mockRestServiceServer - .expect(method(HttpMethod.DELETE)) - .andExpect( - requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}") - ) - .andRespond { - withStatus(requestWithResponse.httpStatus) - .body(requestWithResponse.body) - .createResponse(it) - } - - val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) - assertThat(response.status).isEqualTo(requestWithResponse.response.status) - assertThat(response.body).isEqualTo(requestWithResponse.response.body) + val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) + assertThat(response.status).isEqualTo(requestWithResponse.response.status) + assertThat(response.body).isEqualTo(requestWithResponse.response.body) + } } - @ParameterizedTest - @MethodSource( - "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource" - ) - fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) { - val restTemplate = RestTemplate() - val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) - val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties()) - retryTemplate.setBackOffPolicy(NoBackOffPolicy()) - - this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) - this.restMtbFileSender = - RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) - - val expectedCount = - when (requestWithResponse.httpStatus) { - // OK - No Retry - HttpStatus.OK, - HttpStatus.CREATED, - HttpStatus.UNPROCESSABLE_ENTITY, - HttpStatus.BAD_REQUEST -> ExpectedCount.max(1) - // Request failed - Retry max 3 times - else -> ExpectedCount.max(3) - } - - this.mockRestServiceServer - .expect(expectedCount, method(HttpMethod.DELETE)) - .andExpect( - requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}") - ) - .andRespond { - withStatus(requestWithResponse.httpStatus) - .body(requestWithResponse.body) - .createResponse(it) - } - - val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) - assertThat(response.status).isEqualTo(requestWithResponse.response.status) - assertThat(response.body).isEqualTo(requestWithResponse.response.body) - } - } - - companion object { - data class RequestWithResponse( - val httpStatus: HttpStatus, - val body: String, - val response: MtbFileSender.Response, - ) - - val TEST_REQUEST_ID = RequestId("TestId") - val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") - - fun dnpmV2MtbFile(): Mtb { - return Mtb().apply { - this.patient = - dev.pcvolkmer.mv64e.mtb.Patient().apply { - this.id = "PID" - this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + companion object { + data class RequestWithResponse( + val httpStatus: HttpStatus, + val body: String, + val response: MtbFileSender.Response, + ) + + val TEST_REQUEST_ID = RequestId("TestId") + val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") + + fun dnpmV2MtbFile(): Mtb { + return Mtb().apply { + this.patient = + Patient().apply { + this.id = "PID" + this.birthDate = Date.from(Instant.now()) + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + } + this.episodesOfCare = + listOf( + MtbEpisodeOfCare().apply { + this.id = "1" + this.patient = Reference().apply { this.id = "PID" } + this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) } + } + ) } - this.episodesOfCare = - listOf( - MtbEpisodeOfCare().apply { - this.id = "1" - this.patient = Reference().apply { this.id = "PID" } - this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) } - } + } + + private const val ERROR_RESPONSE_BODY = "Sonstiger Fehler bei der Übertragung" + + /** + * Synthetic http responses with related request status Also see: + * https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API + */ + @JvmStatic + fun mtbFileRequestWithResponseSource(): Set { + return setOf( + RequestWithResponse( + HttpStatus.OK, + responseBodyWithMaxSeverity(ReportService.Severity.INFO), + MtbFileSender.Response( + RequestStatus.SUCCESS, + responseBodyWithMaxSeverity(ReportService.Severity.INFO), + ), + ), + RequestWithResponse( + HttpStatus.CREATED, + responseBodyWithMaxSeverity(ReportService.Severity.WARNING), + MtbFileSender.Response( + RequestStatus.WARNING, + responseBodyWithMaxSeverity(ReportService.Severity.WARNING), + ), + ), + RequestWithResponse( + HttpStatus.BAD_REQUEST, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + MtbFileSender.Response( + RequestStatus.ERROR, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + ), + ), + RequestWithResponse( + HttpStatus.UNPROCESSABLE_ENTITY, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + MtbFileSender.Response( + RequestStatus.ERROR, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + ), + ), + // Some more errors not mentioned in documentation + RequestWithResponse( + HttpStatus.NOT_FOUND, + ERROR_RESPONSE_BODY, + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), + RequestWithResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + ERROR_RESPONSE_BODY, + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), ) - } - } - - private const val ERROR_RESPONSE_BODY = "Sonstiger Fehler bei der Übertragung" - - /** - * Synthetic http responses with related request status Also see: - * https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API - */ - @JvmStatic - fun mtbFileRequestWithResponseSource(): Set { - return setOf( - RequestWithResponse( - HttpStatus.OK, - responseBodyWithMaxSeverity(ReportService.Severity.INFO), - MtbFileSender.Response( - RequestStatus.SUCCESS, - responseBodyWithMaxSeverity(ReportService.Severity.INFO), - ), - ), - RequestWithResponse( - HttpStatus.CREATED, - responseBodyWithMaxSeverity(ReportService.Severity.WARNING), - MtbFileSender.Response( - RequestStatus.WARNING, - responseBodyWithMaxSeverity(ReportService.Severity.WARNING), - ), - ), - RequestWithResponse( - HttpStatus.BAD_REQUEST, - responseBodyWithMaxSeverity(ReportService.Severity.ERROR), - MtbFileSender.Response( - RequestStatus.ERROR, - responseBodyWithMaxSeverity(ReportService.Severity.ERROR), - ), - ), - RequestWithResponse( - HttpStatus.UNPROCESSABLE_ENTITY, - responseBodyWithMaxSeverity(ReportService.Severity.ERROR), - MtbFileSender.Response( - RequestStatus.ERROR, - responseBodyWithMaxSeverity(ReportService.Severity.ERROR), - ), - ), - // Some more errors not mentioned in documentation - RequestWithResponse( - HttpStatus.NOT_FOUND, - ERROR_RESPONSE_BODY, - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), - ), - RequestWithResponse( - HttpStatus.INTERNAL_SERVER_ERROR, - ERROR_RESPONSE_BODY, - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), - ), - ) - } - - /** - * Synthetic http responses with related request status Also see: - * https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API - */ - @JvmStatic - fun deleteRequestWithResponseSource(): Set { - return setOf( - RequestWithResponse(HttpStatus.OK, "", MtbFileSender.Response(RequestStatus.SUCCESS)), - // Some more errors not mentioned in documentation - RequestWithResponse( - HttpStatus.NOT_FOUND, - "what????", - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), - ), - RequestWithResponse( - HttpStatus.INTERNAL_SERVER_ERROR, - "what????", - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), - ), - ) - } + } + + /** + * Synthetic http responses with related request status Also see: + * https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API + */ + @JvmStatic + fun deleteRequestWithResponseSource(): Set { + return setOf( + RequestWithResponse(HttpStatus.OK, "", MtbFileSender.Response(RequestStatus.SUCCESS)), + // Some more errors not mentioned in documentation + RequestWithResponse( + HttpStatus.NOT_FOUND, + "what????", + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), + RequestWithResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "what????", + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), + ) + } - fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String { - return when (severity) { - ReportService.Severity.INFO -> - """ + fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String { + return when (severity) { + ReportService.Severity.INFO -> + """ { "patient": "PID", "issues": [ @@ -302,8 +302,8 @@ class RestDipMtbFileSenderTest { } """ - ReportService.Severity.WARNING -> - """ + ReportService.Severity.WARNING -> + """ { "patient": "PID", "issues": [ @@ -313,8 +313,8 @@ class RestDipMtbFileSenderTest { } """ - ReportService.Severity.ERROR -> - """ + ReportService.Severity.ERROR -> + """ { "patient": "PID", "issues": [ @@ -325,8 +325,8 @@ class RestDipMtbFileSenderTest { } """ - ReportService.Severity.FATAL -> - """ + ReportService.Severity.FATAL -> + """ { "patient": "PID", "issues": [ @@ -337,7 +337,7 @@ class RestDipMtbFileSenderTest { ] } """ - } + } + } } - } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt index 84b081a..abc17c6 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2025-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -20,7 +21,6 @@ package dev.dnpm.etl.processor.pseudonym import ca.uhn.fhir.context.FhirContext -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.config.GIcsConfigProperties import dev.dnpm.etl.processor.config.JacksonConfig @@ -28,8 +28,6 @@ import dev.dnpm.etl.processor.consent.MtbFileConsentService import dev.dnpm.etl.processor.services.ConsentProcessor import dev.dnpm.etl.processor.services.ConsentProcessorTest import dev.pcvolkmer.mv64e.mtb.* -import java.time.Instant -import java.util.* import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle import org.junit.jupiter.api.Nested @@ -42,253 +40,256 @@ import org.mockito.kotlin.anyValueClass import org.mockito.kotlin.doAnswer import org.mockito.kotlin.whenever import org.springframework.core.io.ClassPathResource +import tools.jackson.databind.json.JsonMapper +import java.time.Instant +import java.util.* @ExtendWith(MockitoExtension::class) class ExtensionsTest { - fun getObjectMapper(): ObjectMapper { - return JacksonConfig().objectMapper() - } + fun getJsonMapper(): JsonMapper { + return JacksonConfig().jsonMapper() + } - @Nested - inner class UsingDnpmV2Datamodel { + @Nested + inner class UsingDnpmV2Datamodel { - val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json" - val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1" + val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json" + val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1" - private fun fakeMtbFile(): Mtb { - val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream - return getObjectMapper().readValue(mtbFile, Mtb::class.java) - } + private fun fakeMtbFile(): Mtb { + val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream + return getJsonMapper().readValue(mtbFile, Mtb::class.java) + } - private fun Mtb.serialized(): String { - return getObjectMapper().writeValueAsString(this) - } + private fun Mtb.serialized(): String { + return getJsonMapper().writeValueAsString(this) + } - @Test - fun shouldNotContainCleanPatientId(@Mock pseudonymizeService: PseudonymizeService) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - } - .whenever(pseudonymizeService) - .patientPseudonym(anyValueClass()) + @Test + fun shouldNotContainCleanPatientId(@Mock pseudonymizeService: PseudonymizeService) { + doAnswer { + it.arguments[0] + "PSEUDO-ID" + } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) - val mtbFile = fakeMtbFile() - mtbFile.ensureMetaDataIsInitialized() - addConsentData(mtbFile) + val mtbFile = fakeMtbFile() + mtbFile.ensureMetaDataIsInitialized() + addConsentData(mtbFile) - mtbFile.pseudonymizeWith(pseudonymizeService) + mtbFile.pseudonymizeWith(pseudonymizeService) - assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID") - assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID) - } + assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID") + assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID) + } - private fun addConsentData(mtbFile: Mtb) { - val gIcsConfigProperties = GIcsConfigProperties("", "", "") - val appConfigProperties = AppConfigProperties(emptyList()) - - val bundle = Bundle() - val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent() - dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID" - bundle.addEntry().resource = dummyConsent - - ConsentProcessor( - appConfigProperties, - gIcsConfigProperties, - JacksonConfig().objectMapper(), - FhirContext.forR4(), - MtbFileConsentService(), - ) - .embedBroadConsentResources(mtbFile, bundle) - } + private fun addConsentData(mtbFile: Mtb) { + val gIcsConfigProperties = GIcsConfigProperties("", "", "") + val appConfigProperties = AppConfigProperties(emptyList()) + + val bundle = Bundle() + val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent() + dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID" + bundle.addEntry().resource = dummyConsent + + ConsentProcessor( + appConfigProperties, + gIcsConfigProperties, + JacksonConfig().jsonMapper(), + FhirContext.forR4(), + MtbFileConsentService(), + ) + .embedBroadConsentResources(mtbFile, bundle) + } - @Test - fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - } - .whenever(pseudonymizeService) - .patientPseudonym(anyValueClass()) - - doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() - - val mtbFile = - Mtb().apply { - this.patient = - Patient().apply { - this.id = "PID" - this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + @Test + fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) { + doAnswer { + it.arguments[0] + "PSEUDO-ID" + } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() + + val mtbFile = + Mtb().apply { + this.patient = + Patient().apply { + this.id = "PID" + this.birthDate = Date.from(Instant.now()) + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + } + this.episodesOfCare = + listOf( + MtbEpisodeOfCare().apply { + this.id = "1" + this.patient = Reference().apply { this.id = "PID" } + this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) } + } + ) } - this.episodesOfCare = - listOf( - MtbEpisodeOfCare().apply { - this.id = "1" - this.patient = Reference().apply { this.id = "PID" } - this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) } - } - ) - } - mtbFile.pseudonymizeWith(pseudonymizeService) - mtbFile.anonymizeContentWith(pseudonymizeService) + mtbFile.pseudonymizeWith(pseudonymizeService) + mtbFile.anonymizeContentWith(pseudonymizeService) - assertThat(mtbFile.episodesOfCare).hasSize(1) - assertThat(mtbFile.episodesOfCare.map { it.id }).isNotNull - } + assertThat(mtbFile.episodesOfCare).hasSize(1) + assertThat(mtbFile.episodesOfCare.map { it.id }).isNotNull + } - @Test - fun shouldNotContainAnyUuidAfterRehashingOfIds(@Mock pseudonymizeService: PseudonymizeService) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - } - .whenever(pseudonymizeService) - .patientPseudonym(anyValueClass()) + @Test + fun shouldNotContainAnyUuidAfterRehashingOfIds(@Mock pseudonymizeService: PseudonymizeService) { + doAnswer { + it.arguments[0] + "PSEUDO-ID" + } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) - doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() + doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() - val mtbFile = fakeMtbFile() + val mtbFile = fakeMtbFile() - /** replace hex values with random long, so our test does not match false positives */ - mtbFile.ngsReports.forEach { report -> - report.results.simpleVariants.forEach { simpleVariant -> - simpleVariant.externalIds.forEach { extIdValue -> - extIdValue.value = Math.random().toLong().toString() - } - } - } - mtbFile.ngsReports.forEach { report -> - report.results.rnaFusions.forEach { simpleVariant -> - simpleVariant.externalIds.forEach { extIdValue -> - extIdValue.value = Math.random().toLong().toString() - } - simpleVariant.fusionPartner3Prime?.transcriptId?.value = Math.random().toLong().toString() - simpleVariant.fusionPartner5Prime?.transcriptId?.value = Math.random().toLong().toString() - simpleVariant.externalIds?.forEach { it?.value = Math.random().toLong().toString() } + /** replace hex values with random long, so our test does not match false positives */ + mtbFile.ngsReports.forEach { report -> + report.results.simpleVariants.forEach { simpleVariant -> + simpleVariant.externalIds.forEach { extIdValue -> + extIdValue.value = Math.random().toLong().toString() + } + } + } + mtbFile.ngsReports.forEach { report -> + report.results.rnaFusions.forEach { simpleVariant -> + simpleVariant.externalIds.forEach { extIdValue -> + extIdValue.value = Math.random().toLong().toString() + } + simpleVariant.fusionPartner3Prime?.transcriptId?.value = Math.random().toLong().toString() + simpleVariant.fusionPartner5Prime?.transcriptId?.value = Math.random().toLong().toString() + simpleVariant.externalIds?.forEach { it?.value = Math.random().toLong().toString() } + } + } + + mtbFile.pseudonymizeWith(pseudonymizeService) + mtbFile.anonymizeContentWith(pseudonymizeService) + + val pattern = + "\"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\"".toRegex().toPattern() + val input = mtbFile.serialized() + val matcher = pattern.matcher(input) + + assertThrows { + matcher.find() + val posSt = "check at pos: " + matcher.start().toString() + ", " + matcher.end() + println(posSt + " with " + matcher.group()) + } + .also { assertThat(it.message).isEqualTo("No match found") } } - } - - mtbFile.pseudonymizeWith(pseudonymizeService) - mtbFile.anonymizeContentWith(pseudonymizeService) - - val pattern = - "\"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\"".toRegex().toPattern() - val input = mtbFile.serialized() - val matcher = pattern.matcher(input) - - assertThrows { - matcher.find() - val posSt = "check at pos: " + matcher.start().toString() + ", " + matcher.end() - println(posSt + " with " + matcher.group()) - } - .also { assertThat(it.message).isEqualTo("No match found") } } - } - @Test - fun shouldUseSameAnonymIdForDiagnosisAndDiagnosisReferences( - @Mock pseudonymizeService: PseudonymizeService - ) { + @Test + fun shouldUseSameAnonymIdForDiagnosisAndDiagnosisReferences( + @Mock pseudonymizeService: PseudonymizeService + ) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - } - .whenever(pseudonymizeService) - .patientPseudonym(anyValueClass()) - - doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() - - val mtbFile = - Mtb().apply { - this.patient = - Patient().apply { - this.id = "PID" - this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } - } - this.diagnoses = listOf(MtbDiagnosis().apply { this.id = "Diagnosis-1" }) - this.episodesOfCare = - listOf( - MtbEpisodeOfCare().apply { - this.id = "Episode-1" - this.diagnoses = listOf(Reference().apply { this.id = "Diagnosis-1" }) - } - ) - this.guidelineTherapies = - listOf( - MtbSystemicTherapy().apply { - this.id = "Systemic-Therapy-1" - this.reason = Reference().apply { this.id = "Diagnosis-1" } - } - ) - this.guidelineProcedures = - listOf( - OncoProcedure().apply { - this.id = "Onco-Procedure-1" - this.reason = Reference().apply { this.id = "Diagnosis-1" } - } - ) - this.specimens = - listOf( - TumorSpecimen().apply { - this.id = "Specimen-1" - this.diagnosis = Reference().apply { this.id = "Diagnosis-1" } - } - ) + doAnswer { + it.arguments[0] + "PSEUDO-ID" } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() + + val mtbFile = + Mtb().apply { + this.patient = + Patient().apply { + this.id = "PID" + this.birthDate = Date.from(Instant.now()) + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + } + this.diagnoses = listOf(MtbDiagnosis().apply { this.id = "Diagnosis-1" }) + this.episodesOfCare = + listOf( + MtbEpisodeOfCare().apply { + this.id = "Episode-1" + this.diagnoses = listOf(Reference().apply { this.id = "Diagnosis-1" }) + } + ) + this.guidelineTherapies = + listOf( + MtbSystemicTherapy().apply { + this.id = "Systemic-Therapy-1" + this.reason = Reference().apply { this.id = "Diagnosis-1" } + } + ) + this.guidelineProcedures = + listOf( + OncoProcedure().apply { + this.id = "Onco-Procedure-1" + this.reason = Reference().apply { this.id = "Diagnosis-1" } + } + ) + this.specimens = + listOf( + TumorSpecimen().apply { + this.id = "Specimen-1" + this.diagnosis = Reference().apply { this.id = "Diagnosis-1" } + } + ) + } + + mtbFile.pseudonymizeWith(pseudonymizeService) + mtbFile.anonymizeContentWith(pseudonymizeService) + + assertThat(mtbFile.diagnoses.first().id) + .isEqualTo(mtbFile.episodesOfCare.first().diagnoses.first().id) + assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.guidelineTherapies.first().reason.id) + assertThat(mtbFile.diagnoses.first().id) + .isEqualTo(mtbFile.guidelineProcedures.first().reason.id) + assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.specimens.first().diagnosis.id) + } - mtbFile.pseudonymizeWith(pseudonymizeService) - mtbFile.anonymizeContentWith(pseudonymizeService) - - assertThat(mtbFile.diagnoses.first().id) - .isEqualTo(mtbFile.episodesOfCare.first().diagnoses.first().id) - assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.guidelineTherapies.first().reason.id) - assertThat(mtbFile.diagnoses.first().id) - .isEqualTo(mtbFile.guidelineProcedures.first().reason.id) - assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.specimens.first().diagnosis.id) - } - - @Test - fun shouldNotThrowAnyExceptionOnMissingMsiId(@Mock pseudonymizeService: PseudonymizeService) { + @Test + fun shouldNotThrowAnyExceptionOnMissingMsiId(@Mock pseudonymizeService: PseudonymizeService) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - } - .whenever(pseudonymizeService) - .patientPseudonym(anyValueClass()) - - doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() - - val mtbFile = - Mtb().apply { - this.patient = - Patient().apply { - this.id = "PID" - this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } - } - this.msiFindings = - listOf( - null, - Msi.builder().id("1").build(), - Msi.builder().build(), - Msi.builder().specimen(null).build(), - Msi.builder().specimen(Reference.builder().build()).build(), - ) + doAnswer { + it.arguments[0] + "PSEUDO-ID" } - - mtbFile.pseudonymizeWith(pseudonymizeService) - mtbFile.anonymizeContentWith(pseudonymizeService) - - assertThat(mtbFile.msiFindings).isNotNull - assertThat(mtbFile.msiFindings[1]) - .satisfiesAnyOf( - { assertThat(it.id).isNull() }, - { assertThat(it.id).isEqualTo("TESTDOMAIN44e20a53bbbf9f3ae39626d05df7014dcd77d6098") }, - ) - } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() + + val mtbFile = + Mtb().apply { + this.patient = + Patient().apply { + this.id = "PID" + this.birthDate = Date.from(Instant.now()) + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + } + this.msiFindings = + listOf( + null, + Msi.builder().id("1").build(), + Msi.builder().build(), + Msi.builder().specimen(null).build(), + Msi.builder().specimen(Reference.builder().build()).build(), + ) + } + + mtbFile.pseudonymizeWith(pseudonymizeService) + mtbFile.anonymizeContentWith(pseudonymizeService) + + assertThat(mtbFile.msiFindings).isNotNull + assertThat(mtbFile.msiFindings[1]) + .satisfiesAnyOf( + { assertThat(it.id).isNull() }, + { assertThat(it.id).isEqualTo("TESTDOMAIN44e20a53bbbf9f3ae39626d05df7014dcd77d6098") }, + ) + } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt index 7da0247..7c6d6c9 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt index e9a1650..18e0bb5 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt index 7743069..2a5a481 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2024-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt index 331a816..a85d555 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt @@ -1,7 +1,26 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package dev.dnpm.etl.processor.services import ca.uhn.fhir.context.FhirContext -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.config.GIcsConfigProperties import dev.dnpm.etl.processor.config.JacksonConfig @@ -29,6 +48,7 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.eq import org.mockito.kotlin.whenever import org.springframework.core.io.ClassPathResource +import tools.jackson.databind.json.JsonMapper import java.io.IOException import java.io.InputStream import java.time.Instant @@ -40,7 +60,7 @@ class ConsentProcessorTest { private lateinit var appConfigProperties: AppConfigProperties private lateinit var gicsConsentService: GicsConsentService - private lateinit var objectMapper: ObjectMapper + private lateinit var jsonMapper: JsonMapper private lateinit var gIcsConfigProperties: GIcsConfigProperties private lateinit var fhirContext: FhirContext private lateinit var consentProcessor: ConsentProcessor @@ -52,7 +72,7 @@ class ConsentProcessorTest { this.gIcsConfigProperties = GIcsConfigProperties(uri = "https://gics.example.com", genomDeConsentDomainName = "GenomDE_MV") val jacksonConfig = JacksonConfig() - this.objectMapper = jacksonConfig.objectMapper() + this.jsonMapper = jacksonConfig.jsonMapper() this.fhirContext = JacksonConfig.fhirContext() this.gicsConsentService = gicsConsentService this.appConfigProperties = AppConfigProperties(emptyList()) @@ -60,7 +80,7 @@ class ConsentProcessorTest { ConsentProcessor( appConfigProperties, gIcsConfigProperties, - objectMapper, + jsonMapper, fhirContext, gicsConsentService, ) @@ -94,7 +114,7 @@ class ConsentProcessorTest { ConsentProcessor( appConfigProperties, gIcsConfigProperties, - objectMapper, + jsonMapper, fhirContext, MtbFileConsentService(), ) @@ -220,7 +240,7 @@ class ConsentProcessorTest { ConsentProcessor( appConfigProperties, gIcsConfigProperties, - objectMapper, + jsonMapper, fhirContext, gicsConsentService, ) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt index 4308fed..e9c3107 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,8 +20,6 @@ package dev.dnpm.etl.processor.services -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule import dev.dnpm.etl.processor.monitoring.ReportService import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.monitoring.asRequestStatus @@ -30,6 +29,8 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import tools.jackson.databind.json.JsonMapper +import tools.jackson.module.kotlin.KotlinModule class ReportServiceTest { private lateinit var reportService: ReportService @@ -37,7 +38,7 @@ class ReportServiceTest { @BeforeEach fun setup() { this.reportService = - ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) + ReportService(JsonMapper.builder().addModule(KotlinModule.Builder().build()).build()) } @Test @@ -59,13 +60,34 @@ class ReportServiceTest { assertThat(actual).hasSize(4) assertThat(actual[0].severity).isEqualTo(ReportService.Severity.FATAL) - assertThat(actual[0].message).isEqualTo("Fatal Message") + assertThat(actual[0].getMessage()).isEqualTo("Fatal Message") assertThat(actual[1].severity).isEqualTo(ReportService.Severity.ERROR) - assertThat(actual[1].message).isEqualTo("Error Message") + assertThat(actual[1].getMessage()).isEqualTo("Error Message") assertThat(actual[2].severity).isEqualTo(ReportService.Severity.WARNING) - assertThat(actual[2].message).isEqualTo("Warning Message") + assertThat(actual[2].getMessage()).isEqualTo("Warning Message") assertThat(actual[3].severity).isEqualTo(ReportService.Severity.INFO) - assertThat(actual[3].message).isEqualTo("Info Message") + assertThat(actual[3].getMessage()).isEqualTo("Info Message") + + assertThat(actual.asRequestStatus()).isEqualTo(RequestStatus.ERROR) + } + + @Test + fun shouldParseDataQualityReportWithMissingPathError() { + val json = + """ + { + "patient": "4711", + "issues": [ + { "severity": "error", "details": "/specimens(0)/type/code: error.path.missing" } + ] + } + """.trimIndent() + + val actual = this.reportService.deserialize(json) + + assertThat(actual).hasSize(1) + assertThat(actual[0].severity).isEqualTo(ReportService.Severity.ERROR) + assertThat(actual[0].getMessage()).isEqualTo("/specimens(0)/type/code: error.path.missing") assertThat(actual.asRequestStatus()).isEqualTo(RequestStatus.ERROR) } @@ -88,7 +110,7 @@ class ReportServiceTest { assertThat(actual).hasSize(1) assertThat(actual[0].severity).isEqualTo(ReportService.Severity.ERROR) - assertThat(actual[0].message).isEqualTo("Not parsable data quality report '$invalidResponse'") + assertThat(actual[0].getMessage()).isEqualTo("Not parsable data quality report '$invalidResponse'") } companion object { diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt index b51aed2..5a89c03 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -34,6 +35,7 @@ import dev.dnpm.etl.processor.output.RestMtbFileSender import dev.dnpm.etl.processor.pseudonym.PseudonymizeService import dev.pcvolkmer.mv64e.mtb.* import org.assertj.core.api.Assertions.assertThat +import org.hl7.fhir.r4.model.Consent import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -46,6 +48,7 @@ import org.mockito.kotlin.anyValueClass import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.whenever import org.springframework.context.ApplicationEventPublisher +import tools.jackson.databind.json.JsonMapper import java.time.Instant import java.util.* @@ -60,6 +63,7 @@ class RequestProcessorTest { private lateinit var appConfigProperties: AppConfigProperties private lateinit var consentProcessor: ConsentProcessor private lateinit var requestProcessor: RequestProcessor + private lateinit var jsonMapper: JsonMapper @BeforeEach fun setup( @@ -77,8 +81,7 @@ class RequestProcessorTest { this.applicationEventPublisher = applicationEventPublisher this.appConfigProperties = AppConfigProperties() this.consentProcessor = consentProcessor - - val objectMapper = ObjectMapper() + this.jsonMapper = JsonMapper() requestProcessor = RequestProcessor( @@ -86,7 +89,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - objectMapper, + jsonMapper, applicationEventPublisher, appConfigProperties, consentProcessor, @@ -101,7 +104,7 @@ class RequestProcessorTest { randomRequestId(), PatientPseudonym("TEST_12345678901"), PatientId("P1"), - Fingerprint("6vkiti5bk6ikwifpajpt7cygmd3dvw54d6lwfhzlynb3pqtzferq"), + Fingerprint("syehahte4oyqd5m2rbd5imnth4rx6md32g2msb7sztnayxoc4kaq"), RequestType.MTB_FILE, SubmissionType.TEST, RequestStatus.SUCCESS, @@ -158,7 +161,7 @@ class RequestProcessorTest { randomRequestId(), PatientPseudonym("TEST_12345678901"), PatientId("P1"), - Fingerprint("4gcjwtjjtcczybsljxepdfpkaeusvd7g3vogfqpmphyffyzfx7dq"), + Fingerprint("me6ockoru4boi4ypghfia5myfqtuffwlbszwhtop2rtltb3ycjva"), RequestType.MTB_FILE, SubmissionType.TEST, RequestStatus.SUCCESS, @@ -413,7 +416,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - ObjectMapper(), + jsonMapper, applicationEventPublisher, AppConfigProperties(postInitialSubmissionBlock = true), consentProcessor, @@ -637,7 +640,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - ObjectMapper(), + jsonMapper, applicationEventPublisher, AppConfigProperties(postInitialSubmissionBlock = true), consentProcessor, @@ -681,6 +684,7 @@ class RequestProcessorTest { private lateinit var appConfigProperties: AppConfigProperties private lateinit var consentProcessor: ConsentProcessor private lateinit var requestProcessor: RequestProcessor + private lateinit var jsonMapper: JsonMapper @BeforeEach fun setup( @@ -698,8 +702,7 @@ class RequestProcessorTest { this.applicationEventPublisher = applicationEventPublisher this.appConfigProperties = AppConfigProperties() this.consentProcessor = consentProcessor - - val objectMapper = ObjectMapper() + this.jsonMapper = JsonMapper() requestProcessor = RequestProcessor( @@ -707,7 +710,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - objectMapper, + jsonMapper, applicationEventPublisher, appConfigProperties, consentProcessor, @@ -779,7 +782,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - ObjectMapper(), + jsonMapper, applicationEventPublisher, AppConfigProperties(postInitialSubmissionBlock = true), consentProcessor, @@ -883,7 +886,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - ObjectMapper(), + jsonMapper, applicationEventPublisher, AppConfigProperties(postInitialSubmissionBlock = true), consentProcessor, @@ -994,7 +997,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - ObjectMapper(), + jsonMapper, applicationEventPublisher, AppConfigProperties(postInitialSubmissionBlock = true), consentProcessor, @@ -1092,7 +1095,7 @@ class RequestProcessorTest { transformationService, sender, requestService, - ObjectMapper(), + jsonMapper, applicationEventPublisher, AppConfigProperties(postInitialSubmissionBlock = true), consentProcessor, diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt index 9b103ba..54b4dce 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt index 3b09cc7..441f3a9 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt index 3af5097..66e45f8 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -24,11 +25,11 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ObjectNode import dev.dnpm.etl.processor.config.JacksonConfig import dev.pcvolkmer.mv64e.mtb.* -import java.time.Instant -import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import java.time.Instant +import java.util.* class TransformationServiceTest { @@ -38,7 +39,7 @@ class TransformationServiceTest { fun setup() { this.service = TransformationService( - JacksonConfig().objectMapper(), + JacksonConfig().jsonMapper(), listOf( Transformation.of("diagnoses[*].code.version") from "2013" to "2014", ), diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt index 8d5024a..96c9a82 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt @@ -1,7 +1,8 @@ /* * This file is part of ETL-Processor * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors + * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken + * Copyright (c) 2023-2026 Paul-Christian Volkmer, Datenintegrationszentrum Philipps-Universität Marburg and Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published @@ -19,8 +20,6 @@ package dev.dnpm.etl.processor.services.kafka -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule import dev.dnpm.etl.processor.services.ResponseEvent import org.apache.kafka.clients.consumer.ConsumerRecord import org.junit.jupiter.api.BeforeEach @@ -36,11 +35,13 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.springframework.context.ApplicationEventPublisher import org.springframework.http.HttpStatus +import tools.jackson.databind.json.JsonMapper +import tools.jackson.module.kotlin.KotlinModule @ExtendWith(MockitoExtension::class) class KafkaResponseProcessorTest { private lateinit var eventPublisher: ApplicationEventPublisher - private lateinit var objectMapper: ObjectMapper + private lateinit var jsonMapper: JsonMapper private lateinit var kafkaResponseProcessor: KafkaResponseProcessor @@ -57,7 +58,7 @@ class KafkaResponseProcessorTest { if (statusBody == null) { "" } else { - this.objectMapper.writeValueAsString( + this.jsonMapper.writeValueAsString( KafkaResponseProcessor.ResponseBody(requestId, statusCode, statusBody), ) }, @@ -68,9 +69,9 @@ class KafkaResponseProcessorTest { @Mock eventPublisher: ApplicationEventPublisher, ) { this.eventPublisher = eventPublisher - this.objectMapper = ObjectMapper().registerModule(KotlinModule.Builder().build()) + this.jsonMapper = JsonMapper.builder().addModule(KotlinModule.Builder().build()).build() - this.kafkaResponseProcessor = KafkaResponseProcessor(eventPublisher, objectMapper) + this.kafkaResponseProcessor = KafkaResponseProcessor(eventPublisher, jsonMapper) } @Test -- cgit v1.2.3