diff options
Diffstat (limited to 'src/main/kotlin')
50 files changed, 750 insertions, 459 deletions
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 <T : Any, E : Throwable> onError( - context: RetryContext, - callback: RetryCallback<T, E>, - 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<Any> { - return Sinks.many().multicast().directBestEffort() - } - - @Bean - fun connectionCheckUpdateProducer(): Sinks.Many<ConnectionCheckResult> { - return Sinks.many().multicast().onBackpressureBuffer() - } - - @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") - @Bean - fun gPasConnectionCheckService( - restTemplate: RestTemplate, - gPasConfigProperties: GPasConfigProperties, - connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>, - ): ConnectionCheckService { - return GPasConnectionCheckService( - restTemplate, - gPasConfigProperties, - connectionCheckUpdateProducer, - ) - } - - @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "GPAS") - @ConditionalOnMissingBean - @Bean - fun gPasConnectionCheckServiceOnDeprecatedProperty( - restTemplate: RestTemplate, - gPasConfigProperties: GPasConfigProperties, - connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>, - ): 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<ConnectionCheckResult>, - ): 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 <T : Any, E : Throwable> onError( + context: RetryContext, + callback: RetryCallback<T, E>, + 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<Any> { + return Sinks.many().multicast().directBestEffort() + } + + @Bean + fun connectionCheckUpdateProducer(): Sinks.Many<ConnectionCheckResult> { + return Sinks.many().multicast().onBackpressureBuffer() + } + + @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") + @Bean + fun gPasConnectionCheckService( + restTemplate: RestTemplate, + gPasConfigProperties: GPasConfigProperties, + connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>, + ): ConnectionCheckService { + return GPasConnectionCheckService( + restTemplate, + gPasConfigProperties, + connectionCheckUpdateProducer, + ) + } + + @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "GPAS") + @ConditionalOnMissingBean + @Bean + fun gPasConnectionCheckServiceOnDeprecatedProperty( + restTemplate: RestTemplate, + gPasConfigProperties: GPasConfigProperties, + connectionCheckUpdateProducer: Sinks.Many<ConnectionCheckResult>, + ): 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<ConnectionCheckResult>, + ): 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 <https://www.gnu.org/licenses/>. + */ + 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 <https://www.gnu.org/licenses/>. + */ + 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<String, String>, 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 <https://www.gnu.org/licenses/>. + */ + 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 <https://www.gnu.org/licenses/>. + */ + 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 <https://www.gnu.org/licenses/>. + */ + +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<Consent>() { + 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<Consent>() { + override fun deserialize( + p: JsonParser?, + ctxt: com.fasterxml.jackson.databind.DeserializationContext?, + ): Consent { + val jsonNode = p?.readValueAsTree<com.fasterxml.jackson.databind.JsonNode>() + 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 <https://www.gnu.org/licenses/>. + */ + 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<Consent>() { +class JacksonConsentResourceSerializer : ValueSerializer<Consent>() { 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<Consent>() { +class JacksonConsentResourceDeserializer : ValueDeserializer<Consent>() { override fun deserialize( - p: JsonParser?, + p: tools.jackson.core.JsonParser?, ctxt: DeserializationContext?, ): Consent { val jsonNode = p?.readValueAsTree<JsonNode>() 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<String, String> { private val logger = LoggerFactory.getLogger(KafkaInputListener::class.java) @@ -71,7 +72,7 @@ class KafkaInputListener( private fun handleDnpmV2Message(record: ConsumerRecord<String, String>) { 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<Issue> { - if (dataQualityReport.isNullOrBlank()) { - return listOf() + fun deserialize(dataQualityReport: String?): List<Issue> { + 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<Issue> - ) + private data class DataQualityReport( + val issues: List<Issue> + ) - @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<String> = Optional.empty(), - ) + data class Issue( + val severity: Severity, + val message: Optional<String> = Optional.empty(), + val details: Optional<String> = Optional.empty(), + val path: Optional<String> = 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<Issue>.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 <https://www.gnu.org/licenses/>. + */ + 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<String, String>, 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 <https://www.gnu.org/licenses/>. + */ + 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 <https://www.gnu.org/licenses/>. + */ + 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 <https://www.gnu.org/licenses/>. - * */ 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 <https://www.gnu.org/licenses/>. + */ + 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<MvhMetadata.ResearchConsent?>( + jsonMapper.readValue( asJsonString, - object : TypeReference<MvhMetadata.ResearchConsent?>() {}, + 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 <T> fingerprint(request: MtbFileRequest<T>): 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<Transformation>, ) { 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<String, String> { - private val logger = LoggerFactory.getLogger(KafkaResponseProcessor::class.java) + private val logger = LoggerFactory.getLogger(KafkaResponseProcessor::class.java) - override fun onMessage(data: ConsumerRecord<String, String>) { - try { - Optional.of(objectMapper.readValue(data.value(), ResponseBody::class.java)) + override fun onMessage(data: ConsumerRecord<String, String>) { + 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<String, Any>, - ) + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) + data class ResponseBody( + val requestId: String, + val statusCode: Int, + val statusBody: Map<String, Any>, + ) } 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, ) |
