diff options
Diffstat (limited to 'src/main/kotlin/dev/dnpm/etl/processor')
7 files changed, 105 insertions, 35 deletions
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 1f3c650..5dea8ab 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt @@ -73,8 +73,8 @@ data class GIcsConfigProperties( * */ val uri: String?, - val username: String?, - val password: String?, + val username: String? = null, + val password: String? = null, /** * gICS specific system 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 8f90947..f32ecaa 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt @@ -20,9 +20,9 @@ package dev.dnpm.etl.processor.config import com.fasterxml.jackson.databind.ObjectMapper -import dev.dnpm.etl.processor.consent.ConsentByMtbFile +import dev.dnpm.etl.processor.consent.MtbFileConsentService import dev.dnpm.etl.processor.consent.GicsConsentService -import dev.dnpm.etl.processor.consent.IGetConsent +import dev.dnpm.etl.processor.consent.IConsentService import dev.dnpm.etl.processor.monitoring.* import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator import dev.dnpm.etl.processor.pseudonym.Generator @@ -218,7 +218,7 @@ class AppConfiguration { retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig - ): IGetConsent { + ): IConsentService { return GicsConsentService( gIcsConfigProperties, retryTemplate, @@ -234,7 +234,7 @@ class AppConfiguration { gIcsConfigProperties: GIcsConfigProperties, getObjectMapper: ObjectMapper, appFhirConfig: AppFhirConfig, - gicsConsentService: IGetConsent + gicsConsentService: IConsentService ): ConsentProcessor { return ConsentProcessor( configProperties, @@ -261,8 +261,8 @@ class AppConfiguration { @Bean @ConditionalOnMissingBean - fun iGetConsentService(): IGetConsent { - return ConsentByMtbFile() + fun iGetConsentService(): IConsentService { + return MtbFileConsentService() } } @@ -271,13 +271,9 @@ 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.gics.enabled"], havingValue = "true") - class OnGicsEnabled { - // Just for Condition - } - } 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 de11cbb..6551713 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt @@ -20,6 +20,7 @@ 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 import dev.dnpm.etl.processor.monitoring.ConnectionCheckService @@ -100,9 +101,10 @@ class AppKafkaConfiguration { @ConditionalOnProperty(value = ["app.kafka.input-topic"]) fun kafkaInputListener( requestProcessor: RequestProcessor, - objectMapper: ObjectMapper + objectMapper: ObjectMapper, + consentEvaluator: ConsentEvaluator ): KafkaInputListener { - return KafkaInputListener(requestProcessor, objectMapper) + return KafkaInputListener(requestProcessor, consentEvaluator, objectMapper) } @Bean @@ -113,4 +115,4 @@ class AppKafkaConfiguration { return KafkaConnectionCheckService(consumerFactory.createConsumer(), connectionCheckUpdateProducer) } -}
\ No newline at end of file +} diff --git a/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt b/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt new file mode 100644 index 0000000..195346d --- /dev/null +++ b/src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt @@ -0,0 +1,66 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, 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.consent + +import dev.pcvolkmer.mv64e.mtb.ConsentProvision +import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose +import dev.pcvolkmer.mv64e.mtb.Mtb +import org.springframework.stereotype.Service + +/** + * Evaluates consent using provided consent service and file based consent information + */ +@Service +class ConsentEvaluator( + private val consentService: IConsentService +) { + fun check(mtbFile: Mtb): ConsentEvaluation { + val ttpConsentStatus = consentService.getTtpBroadConsentStatus(mtbFile.patient.id) + val consentGiven = ttpConsentStatus == TtpConsentStatus.BROAD_CONSENT_GIVEN + || ttpConsentStatus == TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT + // Aktuell nur Modellvorhaben Consent im File + || ttpConsentStatus == TtpConsentStatus.UNKNOWN_CHECK_FILE && mtbFile.metadata?.modelProjectConsent?.provisions?.any { + it.purpose == ModelProjectConsentPurpose.SEQUENCING + && it.type == ConsentProvision.PERMIT + } == true + + return ConsentEvaluation(ttpConsentStatus, consentGiven) + } +} + +data class ConsentEvaluation(private val ttpConsentStatus: TtpConsentStatus, private val consentGiven: Boolean) { + /** + * Checks if any required consent is present + */ + fun hasConsent(): Boolean { + return consentGiven + } + + /** + * Returns the consent status + */ + fun getStatus(): TtpConsentStatus { + if (ttpConsentStatus == TtpConsentStatus.UNKNOWN_CHECK_FILE) { + // in case ttp check is disabled - we propagate rejected status anyway + return TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED + } + return ttpConsentStatus + } +} 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 47615be..4ac9f2d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt @@ -23,9 +23,9 @@ import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.PatientId import dev.dnpm.etl.processor.RequestId +import dev.dnpm.etl.processor.consent.ConsentEvaluator import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.services.RequestProcessor -import dev.pcvolkmer.mv64e.mtb.ConsentProvision import dev.pcvolkmer.mv64e.mtb.Mtb import org.apache.kafka.clients.consumer.ConsumerRecord import org.slf4j.LoggerFactory @@ -34,6 +34,7 @@ import org.springframework.kafka.listener.MessageListener class KafkaInputListener( private val requestProcessor: RequestProcessor, + private val consentEvaluator: ConsentEvaluator, private val objectMapper: ObjectMapper ) : MessageListener<String, String> { private val logger = LoggerFactory.getLogger(KafkaInputListener::class.java) @@ -70,8 +71,7 @@ class KafkaInputListener( RequestId("") } - // TODO: Use MV Consent for now - needs to be replaced with proper consent evaluation - if (mtbFile.metadata.modelProjectConsent.provisions.filter { it.type == ConsentProvision.PERMIT }.isNotEmpty()) { + if (consentEvaluator.check(mtbFile).hasConsent()) { logger.debug("Accepted MTB File for processing") if (requestId.isBlank()) { requestProcessor.processMtbFile(mtbFile) 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 d00ad25..e154536 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt @@ -21,7 +21,7 @@ package dev.dnpm.etl.processor.input import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.PatientId -import dev.dnpm.etl.processor.consent.IGetConsent +import dev.dnpm.etl.processor.consent.ConsentEvaluator import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.services.RequestProcessor import dev.pcvolkmer.mv64e.mtb.Mtb @@ -34,9 +34,8 @@ import org.springframework.web.bind.annotation.* @RequestMapping(path = ["mtbfile", "mtb"]) class MtbFileRestController( private val requestProcessor: RequestProcessor, - private val iGetConsent: IGetConsent + private val consentEvaluator: ConsentEvaluator ) { - private val logger = LoggerFactory.getLogger(MtbFileRestController::class.java) @GetMapping @@ -46,8 +45,15 @@ class MtbFileRestController( @PostMapping(consumes = [MediaType.APPLICATION_JSON_VALUE, CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE]) fun mtbFile(@RequestBody mtbFile: Mtb): ResponseEntity<Unit> { - logger.debug("Accepted MTB File (DNPM V2) for processing") - requestProcessor.processMtbFile(mtbFile) + val consentEvaluation = consentEvaluator.check(mtbFile) + if (consentEvaluation.hasConsent()) { + logger.debug("Accepted MTB File (DNPM V2) for processing") + requestProcessor.processMtbFile(mtbFile) + } else { + logger.debug("Accepted MTB File (DNPM V2) and process deletion") + val patientId = PatientId(mtbFile.patient.id) + requestProcessor.processDeletion(patientId, consentEvaluation.getStatus()) + } return ResponseEntity.accepted().build() } 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 11aff57..6688087 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt @@ -6,9 +6,9 @@ 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.ConsentByMtbFile +import dev.dnpm.etl.processor.consent.MtbFileConsentService import dev.dnpm.etl.processor.consent.ConsentDomain -import dev.dnpm.etl.processor.consent.IGetConsent +import dev.dnpm.etl.processor.consent.IConsentService import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized import dev.pcvolkmer.mv64e.mtb.* import org.apache.commons.lang3.NotImplementedException @@ -31,7 +31,7 @@ class ConsentProcessor( private val gIcsConfigProperties: GIcsConfigProperties, private val objectMapper: ObjectMapper, private val fhirContext: FhirContext, - private val consentService: IGetConsent + private val consentService: IConsentService ) { private var logger: Logger = LoggerFactory.getLogger("ConsentProcessor") @@ -49,7 +49,7 @@ class ConsentProcessor( * */ fun consentGatedCheckAndTryEmbedding(mtbFile: Mtb): Boolean { - if (consentService is ConsentByMtbFile) { + if (consentService is MtbFileConsentService) { // consent check is disabled return true } @@ -70,7 +70,7 @@ class ConsentProcessor( * broad consent */ val broadConsent = consentService.getConsent( - personIdentifierValue, requestDate, ConsentDomain.BroadConsent + personIdentifierValue, requestDate, ConsentDomain.BROAD_CONSENT ) val broadConsentHasBeenAsked = !broadConsent.entry.isEmpty() @@ -78,7 +78,7 @@ class ConsentProcessor( if (!broadConsentHasBeenAsked) return false val genomeDeConsent = consentService.getConsent( - personIdentifierValue, requestDate, ConsentDomain.Modelvorhaben64e + personIdentifierValue, requestDate, ConsentDomain.MODELLVORHABEN_64E ) addGenomeDbProvisions(mtbFile, genomeDeConsent) @@ -88,11 +88,11 @@ class ConsentProcessor( embedBroadConsentResources(mtbFile, broadConsent) val broadConsentStatus = getProvisionTypeByPolicyCode( - broadConsent, requestDate, ConsentDomain.BroadConsent + broadConsent, requestDate, ConsentDomain.BROAD_CONSENT ) val genomDeSequencingStatus = getProvisionTypeByPolicyCode( - genomeDeConsent, requestDate, ConsentDomain.Modelvorhaben64e + genomeDeConsent, requestDate, ConsentDomain.MODELLVORHABEN_64E ) if (Consent.ConsentProvisionType.NULL == broadConsentStatus) { @@ -204,10 +204,10 @@ class ConsentProcessor( ): Consent.ConsentProvisionType { val code: String? val system: String? - if (ConsentDomain.BroadConsent == consentDomain) { + if (ConsentDomain.BROAD_CONSENT == consentDomain) { code = gIcsConfigProperties.broadConsentPolicyCode system = gIcsConfigProperties.broadConsentPolicySystem - } else if (ConsentDomain.Modelvorhaben64e == consentDomain) { + } else if (ConsentDomain.MODELLVORHABEN_64E == consentDomain) { code = gIcsConfigProperties.genomeDePolicyCode system = gIcsConfigProperties.genomeDePolicySystem } else { @@ -279,4 +279,4 @@ class ConsentProcessor( return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0 } -}
\ No newline at end of file +} |
