summaryrefslogtreecommitdiff
path: root/src/main/kotlin/dev/dnpm
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/dev/dnpm')
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt4
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt18
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppKafkaConfiguration.kt8
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/consent/ConsentEvaluator.kt66
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt6
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt16
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt22
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
+}