diff options
Diffstat (limited to 'src/main')
11 files changed, 271 insertions, 175 deletions
diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java b/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java index 6d0b160..51bfd50 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/ConsentDomain.java @@ -4,10 +4,10 @@ public enum ConsentDomain { /** * MII Broad consent */ - BroadConsent, + BROAD_CONSENT, /** - * GenomDe Modelvohaben §64e + * GenomDe Modellvorhaben §64e */ - Modelvorhaben64e + MODELLVORHABEN_64E } diff --git a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java index 6f3c987..95e8e8f 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java @@ -4,17 +4,9 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.DataFormatException; import dev.dnpm.etl.processor.config.AppFhirConfig; import dev.dnpm.etl.processor.config.GIcsConfigProperties; -import java.util.Date; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.DateType; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.OperationOutcome; -import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; -import org.hl7.fhir.r4.model.StringType; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,112 +14,152 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.retry.TerminatedRetryException; import org.springframework.retry.support.RetryTemplate; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import java.net.URI; +import java.util.Date; -public class GicsConsentService implements IGetConsent { +/** + * Service to request Consent from remote gICS installation + * + * @since 0.11 + */ +public class GicsConsentService implements IConsentService { private final Logger log = LoggerFactory.getLogger(GicsConsentService.class); public static final String IS_CONSENTED_ENDPOINT = "/$isConsented"; public static final String IS_POLICY_STATES_FOR_PERSON_ENDPOINT = "/$currentPolicyStatesForPerson"; + private final RetryTemplate retryTemplate; private final RestTemplate restTemplate; private final FhirContext fhirContext; - private final HttpHeaders httpHeader; private final GIcsConfigProperties gIcsConfigProperties; - private String url; - - public GicsConsentService(GIcsConfigProperties gIcsConfigProperties, - RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig) { + public GicsConsentService( + GIcsConfigProperties gIcsConfigProperties, + RetryTemplate retryTemplate, + RestTemplate restTemplate, + AppFhirConfig appFhirConfig + ) { this.retryTemplate = retryTemplate; this.restTemplate = restTemplate; this.fhirContext = appFhirConfig.fhirContext(); - httpHeader = buildHeader(gIcsConfigProperties.getUsername(), - gIcsConfigProperties.getPassword()); this.gIcsConfigProperties = gIcsConfigProperties; log.info("GicsConsentService initialized..."); } - public String getGicsUri(String endpoint) { - if (url == null) { - final String gIcsBaseUri = gIcsConfigProperties.getUri(); - if (StringUtils.isBlank(gIcsBaseUri)) { - throw new IllegalArgumentException( - "gICS base URL is empty - should call gICS with false configuration."); - } - url = UriComponentsBuilder.fromUriString(gIcsBaseUri).path(endpoint) - .toUriString(); - } - return url; - } - - @NotNull - private static HttpHeaders buildHeader(String gPasUserName, String gPasPassword) { - var headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_XML); - - if (StringUtils.isBlank(gPasUserName) || StringUtils.isBlank(gPasPassword)) { - return headers; - } - - headers.setBasicAuth(gPasUserName, gPasPassword); - return headers; - } - - protected static Parameters getIsConsentedRequestParam(GIcsConfigProperties configProperties, - String personIdentifierValue) { + protected Parameters getFhirRequestParameters( + String personIdentifierValue + ) { var result = new Parameters(); - result.addParameter(new ParametersParameterComponent().setName("personIdentifier").setValue( - new Identifier().setValue(personIdentifierValue) - .setSystem(configProperties.getPersonIdentifierSystem()))); - result.addParameter(new ParametersParameterComponent().setName("domain") - .setValue(new StringType().setValue(configProperties.getBroadConsentDomainName()))); - result.addParameter(new ParametersParameterComponent().setName("policy").setValue( - new Coding().setCode(configProperties.getBroadConsentPolicyCode()) - .setSystem(configProperties.getBroadConsentPolicySystem()))); + result.addParameter( + new ParametersParameterComponent() + .setName("personIdentifier") + .setValue( + new Identifier() + .setValue(personIdentifierValue) + .setSystem(this.gIcsConfigProperties.getPersonIdentifierSystem()) + ) + ); + result.addParameter( + new ParametersParameterComponent() + .setName("domain") + .setValue( + new StringType() + .setValue(this.gIcsConfigProperties.getBroadConsentDomainName()) + ) + ); + result.addParameter( + new ParametersParameterComponent() + .setName("policy") + .setValue( + new Coding() + .setCode(this.gIcsConfigProperties.getBroadConsentPolicyCode()) + .setSystem(this.gIcsConfigProperties.getBroadConsentPolicySystem()) + ) + ); /* * is mandatory parameter, but we ignore it via additional configuration parameter * 'ignoreVersionNumber'. */ - result.addParameter(new ParametersParameterComponent().setName("version") - .setValue(new StringType().setValue("1.1"))); + result.addParameter( + new ParametersParameterComponent() + .setName("version") + .setValue(new StringType().setValue("1.1") + ) + ); /* add config parameter with: * ignoreVersionNumber -> true ->> Reason is we cannot know which policy version each patient * has possibly signed or not, therefore we are happy with any version found. * unknownStateIsConsideredAsDecline -> true */ - var config = new ParametersParameterComponent().setName("config").addPart( - new ParametersParameterComponent().setName("ignoreVersionNumber") - .setValue(new BooleanType().setValue(true))).addPart( - new ParametersParameterComponent().setName("unknownStateIsConsideredAsDecline") - .setValue(new BooleanType().setValue(false))); + var config = new ParametersParameterComponent() + .setName("config") + .addPart( + new ParametersParameterComponent() + .setName("ignoreVersionNumber") + .setValue(new BooleanType().setValue(true)) + ) + .addPart( + new ParametersParameterComponent() + .setName("unknownStateIsConsideredAsDecline") + .setValue(new BooleanType().setValue(false)) + ); + result.addParameter(config); return result; } + private URI endpointUri(String endpoint) { + assert this.gIcsConfigProperties.getUri() != null; + return UriComponentsBuilder.fromUriString(this.gIcsConfigProperties.getUri()).path(endpoint).build().toUri(); + } + + private HttpHeaders headersWithHttpBasicAuth() { + assert this.gIcsConfigProperties.getUri() != null; + + var headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_XML); + + if ( + StringUtils.isBlank(this.gIcsConfigProperties.getUsername()) + || StringUtils.isBlank(this.gIcsConfigProperties.getPassword()) + ) { + return headers; + } + + headers.setBasicAuth(this.gIcsConfigProperties.getUsername(), this.gIcsConfigProperties.getPassword()); + return headers; + } + protected String callGicsApi(Parameters parameter, String endpoint) { var parameterAsXml = fhirContext.newXmlParser().encodeResourceToString(parameter); - - HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.httpHeader); - ResponseEntity<String> responseEntity; + HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.headersWithHttpBasicAuth()); try { - var url = getGicsUri(endpoint); - - responseEntity = retryTemplate.execute( - ctx -> restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class)); + var responseEntity = retryTemplate.execute( + ctx -> restTemplate.exchange(endpointUri(endpoint), HttpMethod.POST, requestEntity, String.class) + ); + + if (responseEntity.getStatusCode().is2xxSuccessful()) { + return responseEntity.getBody(); + } else { + var msg = String.format( + "Trusted party system reached but request failed! code: '%s' response: '%s'", + responseEntity.getStatusCode(), responseEntity.getBody()); + log.error(msg); + return null; + } } catch (RestClientException e) { var msg = String.format("Get consents status request failed reason: '%s", - e.getMessage()); + e.getMessage()); log.error(msg); return null; @@ -137,39 +169,32 @@ public class GicsConsentService implements IGetConsent { terminatedRetryException.getMessage()); log.error(msg); return null; - - } - if (responseEntity.getStatusCode().is2xxSuccessful()) { - return responseEntity.getBody(); - } else { - var msg = String.format( - "Trusted party system reached but request failed! code: '%s' response: '%s'", - responseEntity.getStatusCode(), responseEntity.getBody()); - log.error(msg); - return null; } } @Override public TtpConsentStatus getTtpBroadConsentStatus(String personIdentifierValue) { - var parameter = GicsConsentService.getIsConsentedRequestParam(gIcsConfigProperties, - personIdentifierValue); - - var consentStatusResponse = callGicsApi(parameter, - GicsConsentService.IS_CONSENTED_ENDPOINT); + var consentStatusResponse = callGicsApi( + getFhirRequestParameters(personIdentifierValue), + GicsConsentService.IS_CONSENTED_ENDPOINT + ); return evaluateConsentResponse(consentStatusResponse); } - protected Bundle currentConsentForPersonAndTemplate(String personIdentifierValue, - ConsentDomain targetConsentDomain, Date requestDate) { - - String consentDomain = getConsentDomain(targetConsentDomain); + protected Bundle currentConsentForPersonAndTemplate( + String personIdentifierValue, + ConsentDomain consentDomain, + Date requestDate + ) { - var requestParameter = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson( - gIcsConfigProperties, personIdentifierValue, requestDate, consentDomain); + var requestParameter = buildRequestParameterCurrentPolicyStatesForPerson( + personIdentifierValue, + requestDate, + consentDomain + ); var consentDataSerialized = callGicsApi(requestParameter, - GicsConsentService.IS_POLICY_STATES_FOR_PERSON_ENDPOINT); + GicsConsentService.IS_POLICY_STATES_FOR_PERSON_ENDPOINT); if (consentDataSerialized == null) { // error occurred - should not process further! @@ -177,15 +202,15 @@ public class GicsConsentService implements IGetConsent { "consent data request failed - stopping processing! - try again or fix other problems first."); } var iBaseResource = fhirContext.newJsonParser() - .parseResource(consentDataSerialized); + .parseResource(consentDataSerialized); if (iBaseResource instanceof OperationOutcome) { // log error - very likely a configuration error String errorMessage = "Consent request failed! Check outcome:\n " + consentDataSerialized; log.error(errorMessage); throw new IllegalStateException(errorMessage); - } else if (iBaseResource instanceof Bundle) { - return (Bundle) iBaseResource; + } else if (iBaseResource instanceof Bundle bundle) { + return bundle; } else { String errorMessage = "Consent request failed! Unexpected response received! -> " + consentDataSerialized; @@ -195,40 +220,52 @@ public class GicsConsentService implements IGetConsent { } @NotNull - private String getConsentDomain(ConsentDomain targetConsentDomain) { - String consentDomain; - switch (targetConsentDomain) { - case BroadConsent -> consentDomain = gIcsConfigProperties.getBroadConsentDomainName(); - case Modelvorhaben64e -> - consentDomain = gIcsConfigProperties.getGenomDeConsentDomainName(); - default -> throw new IllegalArgumentException( - "target ConsentDomain is missing but must be provided!"); - } - return consentDomain; + private String getConsentDomainName(ConsentDomain targetConsentDomain) { + return switch (targetConsentDomain) { + case BROAD_CONSENT -> gIcsConfigProperties.getBroadConsentDomainName(); + case MODELLVORHABEN_64E -> gIcsConfigProperties.getGenomDeConsentDomainName(); + }; } - protected static Parameters buildRequestParameterCurrentPolicyStatesForPerson( - GIcsConfigProperties gIcsConfigProperties, String personIdentifierValue, Date requestDate, - String targetDomain) { + protected Parameters buildRequestParameterCurrentPolicyStatesForPerson( + String personIdentifierValue, + Date requestDate, + ConsentDomain consentDomain + ) { var requestParameter = new Parameters(); - requestParameter.addParameter(new ParametersParameterComponent().setName("personIdentifier") - .setValue(new Identifier().setValue(personIdentifierValue) - .setSystem(gIcsConfigProperties.getPersonIdentifierSystem()))); - - requestParameter.addParameter(new ParametersParameterComponent().setName("domain") - .setValue(new StringType().setValue(targetDomain))); + requestParameter.addParameter( + new ParametersParameterComponent() + .setName("personIdentifier") + .setValue( + new Identifier() + .setValue(personIdentifierValue) + .setSystem(this.gIcsConfigProperties.getPersonIdentifierSystem()) + ) + ); + + requestParameter.addParameter( + new ParametersParameterComponent() + .setName("domain") + .setValue(new StringType().setValue(getConsentDomainName(consentDomain))) + ); Parameters nestedConfigParameters = new Parameters(); - nestedConfigParameters.addParameter( - new ParametersParameterComponent().setName("idMatchingType").setValue( - new Coding().setSystem( - "https://ths-greifswald.de/fhir/CodeSystem/gics/IdMatchingType") - .setCode("AT_LEAST_ONE"))).addParameter("ignoreVersionNumber", false) + nestedConfigParameters + .addParameter( + new ParametersParameterComponent() + .setName("idMatchingType") + .setValue(new Coding() + .setSystem("https://ths-greifswald.de/fhir/CodeSystem/gics/IdMatchingType") + .setCode("AT_LEAST_ONE") + ) + ) + .addParameter("ignoreVersionNumber", false) .addParameter("unknownStateIsConsideredAsDecline", false) .addParameter("requestDate", new DateType().setValue(requestDate)); - requestParameter.addParameter(new ParametersParameterComponent().setName("config").addPart() - .setResource(nestedConfigParameters)); + requestParameter.addParameter( + new ParametersParameterComponent().setName("config").addPart().setResource(nestedConfigParameters) + ); return requestParameter; } @@ -254,7 +291,7 @@ public class GicsConsentService implements IGetConsent { } } else if (response instanceof OperationOutcome outcome) { log.error("failed to get consent status from ttp. probably configuration error. " - + "outcome: '{}'", fhirContext.newJsonParser().encodeToString(outcome)); + + "outcome: '{}'", fhirContext.newJsonParser().encodeToString(outcome)); } } catch (DataFormatException dfe) { @@ -265,17 +302,6 @@ public class GicsConsentService implements IGetConsent { @Override public Bundle getConsent(String patientId, Date requestDate, ConsentDomain consentDomain) { - switch (consentDomain) { - case BroadConsent -> { - return currentConsentForPersonAndTemplate(patientId, ConsentDomain.BroadConsent, - requestDate); - } - case Modelvorhaben64e -> { - return currentConsentForPersonAndTemplate(patientId, - ConsentDomain.Modelvorhaben64e, requestDate); - } - } - - return new Bundle(); + return currentConsentForPersonAndTemplate(patientId, consentDomain, requestDate); } } diff --git a/src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java b/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java index 3482b9a..ded3515 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/IConsentService.java @@ -3,7 +3,7 @@ package dev.dnpm.etl.processor.consent; import java.util.Date; import org.hl7.fhir.r4.model.Bundle; -public interface IGetConsent { +public interface IConsentService { /** * Get broad consent status for a patient identifier diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java b/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java index f7ce39e..24cb8f7 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/MtbFileConsentService.java @@ -5,11 +5,11 @@ import org.hl7.fhir.r4.model.Bundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConsentByMtbFile implements IGetConsent { +public class MtbFileConsentService implements IConsentService { - private static final Logger log = LoggerFactory.getLogger(ConsentByMtbFile.class); + private static final Logger log = LoggerFactory.getLogger(MtbFileConsentService.class); - public ConsentByMtbFile() { + public MtbFileConsentService() { log.info("ConsentCheckFileBased initialized..."); } 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 +} |
