From dc6a4c6cb9a31ca534c02575b5ec7ab8de795884 Mon Sep 17 00:00:00 2001 From: Jakub Lidke Date: Wed, 16 Jul 2025 15:20:26 +0200 Subject: [PATCH] refactor: moved consent evaluation to new ConsentProcessor.kt --- .../input/MtbFileRestControllerTest.kt | 6 +- ...ckFileBased.java => ConsentByMtbFile.java} | 19 +-- .../processor/consent/GicsConsentService.java | 90 +--------- .../{ICheckConsent.java => IGetConsent.java} | 11 +- .../etl/processor/config/AppConfiguration.kt | 12 +- .../processor/input/MtbFileRestController.kt | 6 +- .../processor/services/ConsentProcessor.kt | 122 ++++++++++--- .../processor/services/RequestProcessor.kt | 10 +- .../consent/GicsConsentServiceTest.java | 91 +++------- .../input/MtbFileRestControllerTest.kt | 6 +- .../etl/processor/pseudonym/ExtensionsTest.kt | 4 +- .../services/ConsentProcessorTest.kt | 160 ++++++++++++++++++ .../services/TransformationServiceTest.kt | 50 +----- 13 files changed, 310 insertions(+), 277 deletions(-) rename src/main/java/dev/dnpm/etl/processor/consent/{ConsentCheckFileBased.java => ConsentByMtbFile.java} (58%) rename src/main/java/dev/dnpm/etl/processor/consent/{ICheckConsent.java => IGetConsent.java} (82%) create mode 100644 src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt index c463fd0..8aa8ba0 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt @@ -23,9 +23,9 @@ import com.fasterxml.jackson.databind.ObjectMapper import de.ukw.ccc.bwhc.dto.* import dev.dnpm.etl.processor.anyValueClass import dev.dnpm.etl.processor.config.AppSecurityConfiguration -import dev.dnpm.etl.processor.consent.ConsentCheckFileBased +import dev.dnpm.etl.processor.consent.ConsentByMtbFile import dev.dnpm.etl.processor.consent.TtpConsentStatus -import dev.dnpm.etl.processor.consent.ICheckConsent +import dev.dnpm.etl.processor.consent.IGetConsent import dev.dnpm.etl.processor.security.TokenRepository import dev.dnpm.etl.processor.security.UserRoleRepository import dev.dnpm.etl.processor.services.RequestProcessor @@ -55,7 +55,7 @@ import org.springframework.test.web.servlet.post classes = [ MtbFileRestController::class, AppSecurityConfiguration::class, - ConsentCheckFileBased::class, ICheckConsent::class + ConsentByMtbFile::class, IGetConsent::class ] ) @MockitoBean(types = [TokenRepository::class, RequestProcessor::class]) diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ConsentCheckFileBased.java b/src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java similarity index 58% rename from src/main/java/dev/dnpm/etl/processor/consent/ConsentCheckFileBased.java rename to src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java index 331ea13..a1107ae 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/ConsentCheckFileBased.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/ConsentByMtbFile.java @@ -1,18 +1,15 @@ package dev.dnpm.etl.processor.consent; -import dev.pcvolkmer.mv64e.mtb.Mtb; import java.util.Date; -import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Consent.ConsentProvisionType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConsentCheckFileBased implements ICheckConsent { +public class ConsentByMtbFile implements IGetConsent { - private static final Logger log = LoggerFactory.getLogger(ConsentCheckFileBased.class); + private static final Logger log = LoggerFactory.getLogger(ConsentByMtbFile.class); - public ConsentCheckFileBased() { + public ConsentByMtbFile() { log.info("ConsentCheckFileBased initialized..."); } @@ -23,12 +20,12 @@ public class ConsentCheckFileBased implements ICheckConsent { @Override public Bundle getBroadConsent(String personIdentifierValue, Date requestDate) { - return ICheckConsent.super.getBroadConsent(personIdentifierValue, requestDate); + return IGetConsent.super.getBroadConsent(personIdentifierValue, requestDate); } @Override public Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) { - return ICheckConsent.super.getGenomDeConsent(personIdentifierValue, requestDate); + return IGetConsent.super.getGenomDeConsent(personIdentifierValue, requestDate); } @Override @@ -36,10 +33,4 @@ public class ConsentCheckFileBased implements ICheckConsent { ConsentDomain targetConsentDomain, Date requestDate) { return new Bundle(); } - - @Override - public ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle, - Date requestDate, ConsentDomain consentDomain) { - return ConsentProvisionType.NULL; - } } 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 e8f7e4b..352cbb3 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java @@ -2,27 +2,18 @@ package dev.dnpm.etl.processor.consent; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.DataFormatException; -import com.fasterxml.jackson.databind.ObjectMapper; import dev.dnpm.etl.processor.config.AppFhirConfig; import dev.dnpm.etl.processor.config.GIcsConfigProperties; import java.util.Date; -import java.util.Optional; -import org.apache.commons.lang3.NotImplementedException; 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.Consent; -import org.hl7.fhir.r4.model.Consent.ConsentProvisionType; -import org.hl7.fhir.r4.model.Consent.ConsentState; -import org.hl7.fhir.r4.model.Consent.ProvisionComponent; 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.Parameters.ParametersParameterComponent; -import org.hl7.fhir.r4.model.Period; -import org.hl7.fhir.r4.model.ResourceType; import org.hl7.fhir.r4.model.StringType; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -39,7 +30,7 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; -public class GicsConsentService implements ICheckConsent { +public class GicsConsentService implements IGetConsent { private final Logger log = LoggerFactory.getLogger(GicsConsentService.class); @@ -272,83 +263,4 @@ public class GicsConsentService implements ICheckConsent { return TtpConsentStatus.FAILED_TO_ASK; } - /** - * @param consentBundle consent resource - * @param requestDate date which must be within validation period of provision - * @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found. - */ - public ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle, - Date requestDate, ConsentDomain consentDomain) { - String code; - String system; - if (ConsentDomain.BroadConsent == consentDomain) { - code = gIcsConfigProperties.getBroadConsentPolicyCode(); - system = gIcsConfigProperties.getBroadConsentPolicySystem(); - } else if (ConsentDomain.Modelvorhaben64e == consentDomain) { - code = gIcsConfigProperties.getGenomeDePolicyCode(); - system = gIcsConfigProperties.getGenomeDePolicySystem(); - } else { - throw new NotImplementedException("unknown consent domain " + consentDomain.name()); - } - - Optional provisionTypeByPolicyCode = getProvisionTypeByPolicyCode( - consentBundle, code, - system, requestDate); - return provisionTypeByPolicyCode.orElse(ConsentProvisionType.NULL); - } - - /** - * @param consentBundle consent resource - * @param policyAndProvisionCode policyRule and provision code value - * @param policyAndProvisionSystem policyRule and provision system value - * @param requestDate date which must be within validation period of provision - * @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found. - */ - public Optional getProvisionTypeByPolicyCode(Bundle consentBundle, - String policyAndProvisionCode, String policyAndProvisionSystem, Date requestDate) { - return consentBundle.getEntry().stream().filter(entry -> { - if (entry.getResource().getResourceType() != ResourceType.Consent) { - // no consent in bundle - return false; - } - - Consent consent = (Consent) entry.getResource(); - - // consent ist active and its policy rule must fits search criteria - return consent.getStatus() == ConsentState.ACTIVE && checkCoding( - policyAndProvisionCode, policyAndProvisionSystem, - consent.getPolicyRule().getCodingFirstRep()) && isIsRequestDateInRange(requestDate, - consent.getProvision().getPeriod()); - - }).map(consentWithTargetPolicy -> { - ProvisionComponent provision = ((Consent) consentWithTargetPolicy.getResource()).getProvision(); - var provisionComponentByCode = provision.getProvision().stream().filter(prov -> - - checkCoding(policyAndProvisionCode, policyAndProvisionSystem, - prov.getCodeFirstRep().getCodingFirstRep()) && isIsRequestDateInRange( - requestDate, prov.getPeriod()) - - ).findFirst(); - - if (provisionComponentByCode.isPresent()) { - // actual provision we search for - return provisionComponentByCode.get().getType(); - } - // no fitting nested provision found - fall back to wrapping provision with default value - return provision.getType(); - }).findFirst().or(() -> Optional.of(ConsentProvisionType.NULL)); - } - - protected static boolean checkCoding(String researchAllowedPolicyOid, - String researchAllowedPolicySystem, Coding coding) { - - return coding.getSystem().equals(researchAllowedPolicySystem) && coding.getCode() - .equals(researchAllowedPolicyOid); - } - - protected static boolean isIsRequestDateInRange(Date requestdate, Period provPeriod) { - var isRequestDateAfterOrEqualStart = provPeriod.getStart().compareTo(requestdate); - var isRequestDateBeforeOrEqualEnd = provPeriod.getEnd().compareTo(requestdate); - return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0; - } } diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ICheckConsent.java b/src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java similarity index 82% rename from src/main/java/dev/dnpm/etl/processor/consent/ICheckConsent.java rename to src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java index 584eb4f..48bcef3 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/ICheckConsent.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/IGetConsent.java @@ -2,9 +2,8 @@ package dev.dnpm.etl.processor.consent; import java.util.Date; import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Consent.ConsentProvisionType; -public interface ICheckConsent { +public interface IGetConsent { /** * Get broad consent status for a patient identifier @@ -50,12 +49,4 @@ public interface ICheckConsent { Bundle currentConsentForPersonAndTemplate(String personIdentifierValue, ConsentDomain targetConsentDomain, Date requestDate); - /** - * @param consentBundle consent resource - * @param requestDate date which must be within validation period of provision - * @return type of provision, will be {@link ConsentProvisionType#NULL} if none is found. - */ - ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle, Date requestDate, - ConsentDomain consentDomain); - } 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 03be73d..91edd8c 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt @@ -20,8 +20,8 @@ package dev.dnpm.etl.processor.config import com.fasterxml.jackson.databind.ObjectMapper -import dev.dnpm.etl.processor.consent.ConsentCheckFileBased -import dev.dnpm.etl.processor.consent.ICheckConsent +import dev.dnpm.etl.processor.consent.ConsentByMtbFile +import dev.dnpm.etl.processor.consent.IGetConsent import dev.dnpm.etl.processor.consent.GicsConsentService import dev.dnpm.etl.processor.monitoring.* import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator @@ -212,7 +212,7 @@ class AppConfiguration { fun gicsConsentService( gIcsConfigProperties: GIcsConfigProperties, retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig - ): ICheckConsent { + ): IGetConsent { return GicsConsentService( gIcsConfigProperties, retryTemplate, @@ -227,7 +227,7 @@ class AppConfiguration { gIcsConfigProperties: GIcsConfigProperties, getObjectMapper: ObjectMapper, appFhirConfig: AppFhirConfig, - gicsConsentService: ICheckConsent + gicsConsentService: IGetConsent ): ConsentProcessor { return ConsentProcessor( gIcsConfigProperties, @@ -253,8 +253,8 @@ class AppConfiguration { @Bean @ConditionalOnMissingBean - fun constService(): ICheckConsent { - return ConsentCheckFileBased() + fun iGetConsentService(): IGetConsent { + return ConsentByMtbFile() } } 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 4905ab9..44c74e3 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt @@ -23,7 +23,7 @@ import de.ukw.ccc.bwhc.dto.Consent import de.ukw.ccc.bwhc.dto.MtbFile import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.PatientId -import dev.dnpm.etl.processor.consent.ICheckConsent +import dev.dnpm.etl.processor.consent.IGetConsent import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.services.RequestProcessor import dev.pcvolkmer.mv64e.mtb.Mtb @@ -35,7 +35,7 @@ import org.springframework.web.bind.annotation.* @RestController @RequestMapping(path = ["mtbfile", "mtb"]) class MtbFileRestController( - private val requestProcessor: RequestProcessor, private val iCheckConsent: ICheckConsent + private val requestProcessor: RequestProcessor, private val iGetConsent: IGetConsent ) { private val logger = LoggerFactory.getLogger(MtbFileRestController::class.java) @@ -63,7 +63,7 @@ class MtbFileRestController( } private fun checkConsentStatus(mtbFile: MtbFile): Pair { - var ttpConsentStatus = iCheckConsent.getTtpBroadConsentStatus(mtbFile.patient.id) + var ttpConsentStatus = iGetConsent.getTtpBroadConsentStatus(mtbFile.patient.id) val isConsentOK = (ttpConsentStatus.equals(TtpConsentStatus.UNKNOWN_CHECK_FILE) && mtbFile.consent.status == Consent.Status.ACTIVE) || 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 1fd0c7b..d315f85 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt @@ -6,12 +6,14 @@ import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.config.GIcsConfigProperties import dev.dnpm.etl.processor.consent.ConsentDomain -import dev.dnpm.etl.processor.consent.ICheckConsent +import dev.dnpm.etl.processor.consent.IGetConsent import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized import dev.pcvolkmer.mv64e.mtb.* -import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.CodeableConcept -import org.hl7.fhir.r4.model.Consent +import org.apache.commons.lang3.NotImplementedException +import org.hl7.fhir.r4.model.* +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent +import org.hl7.fhir.r4.model.Coding +import org.hl7.fhir.r4.model.Consent.ConsentState import org.hl7.fhir.r4.model.Consent.ProvisionComponent import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -23,9 +25,10 @@ import java.util.* @Service class ConsentProcessor( - private val gIcsConfigProperties: GIcsConfigProperties, private val objectMapper: ObjectMapper, + private val gIcsConfigProperties: GIcsConfigProperties, + private val objectMapper: ObjectMapper, private val fhirContext: FhirContext, - private val consentService: ICheckConsent? + private val consentService: IGetConsent? ) { private var logger: Logger = LoggerFactory.getLogger("ConsentProcessor") @@ -80,11 +83,11 @@ class ConsentProcessor( embedBroadConsentResources(mtbFile, broadConsent) - val broadConsentStatus = consentService.getProvisionTypeByPolicyCode( + val broadConsentStatus = getProvisionTypeByPolicyCode( broadConsent, requestDate, ConsentDomain.BroadConsent ) - val genomDeSequencingStatus = consentService.getProvisionTypeByPolicyCode( + val genomDeSequencingStatus = getProvisionTypeByPolicyCode( genomeDeConsent, requestDate, ConsentDomain.Modelvorhaben64e ) @@ -92,27 +95,22 @@ class ConsentProcessor( // bc not asked return false } - if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus || - Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus - ) return true + if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus || Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus) return true return false } - public fun embedBroadConsentResources(mtbFile: Mtb, broadConsent: Bundle) { + fun embedBroadConsentResources(mtbFile: Mtb, broadConsent: Bundle) { for (entry in broadConsent.getEntry()) { val resource = entry.getResource() if (resource is Consent) { // since jackson convertValue does not work here, // we need another step to back to string, before we convert to object map - val asJsonString = - fhirContext.newJsonParser().encodeResourceToString(resource) + val asJsonString = fhirContext.newJsonParser().encodeResourceToString(resource) try { val mapOfJson: HashMap? = objectMapper.readValue?>( - asJsonString, - object : TypeReference?>() { - }) + asJsonString, object : TypeReference?>() {}) mtbFile.metadata.researchConsents.add(mapOfJson) } catch (e: JsonProcessingException) { throw RuntimeException(e) @@ -121,7 +119,7 @@ class ConsentProcessor( } } - public fun addGenomeDbProvisions(mtbFile: Mtb, consentGnomeDe: Bundle) { + fun addGenomeDbProvisions(mtbFile: Mtb, consentGnomeDe: Bundle) { for (entry in consentGnomeDe.getEntry()) { val resource = entry.getResource() if (resource !is Consent) { @@ -157,8 +155,7 @@ class ConsentProcessor( val provision = Provision.builder() .type(ConsentProvision.valueOf(provisionComponent.getType().name)) .date(provisionComponent.getPeriod().getStart()) - .purpose(modelProjectConsentPurpose) - .build() + .purpose(modelProjectConsentPurpose).build() mtbFile.metadata.modelProjectConsent.provisions.add(provision) } catch (ioe: IOException) { @@ -183,4 +180,89 @@ class ConsentProcessor( mtbFile.metadata.type = MvhSubmissionType.INITIAL } + /** + * @param consentBundle consent resource + * @param requestDate date which must be within validation period of provision + * @return type of provision, will be [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if none is found. + */ + fun getProvisionTypeByPolicyCode( + consentBundle: Bundle, requestDate: Date?, consentDomain: ConsentDomain + ): Consent.ConsentProvisionType { + val code: String? + val system: String? + if (ConsentDomain.BroadConsent == consentDomain) { + code = gIcsConfigProperties.broadConsentPolicyCode + system = gIcsConfigProperties.broadConsentPolicySystem + } else if (ConsentDomain.Modelvorhaben64e == consentDomain) { + code = gIcsConfigProperties.genomeDePolicyCode + system = gIcsConfigProperties.genomeDePolicySystem + } else { + throw NotImplementedException("unknown consent domain " + consentDomain.name) + } + + val provisionTypeByPolicyCode = getProvisionTypeByPolicyCode( + consentBundle, code, system, requestDate + ) + return provisionTypeByPolicyCode + } + + /** + * @param consentBundle consent resource + * @param policyAndProvisionCode policyRule and provision code value + * @param policyAndProvisionSystem policyRule and provision system value + * @param requestDate date which must be within validation period of provision + * @return type of provision, will be [org.hl7.fhir.r4.model.Consent.ConsentProvisionType.NULL] if none is found. + */ + fun getProvisionTypeByPolicyCode( + consentBundle: Bundle, + policyAndProvisionCode: String?, + policyAndProvisionSystem: String?, + requestDate: Date? + ): Consent.ConsentProvisionType { + val entriesOfInterest = consentBundle.entry.filter { entry -> + entry.resource.isResource && entry.resource.resourceType == ResourceType.Consent && (entry.resource as Consent).status == ConsentState.ACTIVE && checkCoding( + policyAndProvisionCode, + policyAndProvisionSystem, + (entry.resource as Consent).policyRule.codingFirstRep + ) && isIsRequestDateInRange( + requestDate, (entry.resource as Consent).provision.period + ) + }.map { consentWithTargetPolicy: BundleEntryComponent -> + val provision = (consentWithTargetPolicy.getResource() as Consent).getProvision() + val provisionComponentByCode = + provision.getProvision().stream().filter { prov: ProvisionComponent? -> + checkCoding( + policyAndProvisionCode, + policyAndProvisionSystem, + prov!!.getCodeFirstRep().getCodingFirstRep() + ) && isIsRequestDateInRange( + requestDate, prov.getPeriod() + ) + }.findFirst() + if (provisionComponentByCode.isPresent) { + // actual provision we search for + return@map provisionComponentByCode.get().getType() + } else { + if (provision.type != null) return provision.type + + } + return Consent.ConsentProvisionType.NULL + }.firstOrNull() + + if (entriesOfInterest == null) return Consent.ConsentProvisionType.NULL + return entriesOfInterest + } + + fun checkCoding( + researchAllowedPolicyOid: String?, researchAllowedPolicySystem: String?, coding: Coding + ): Boolean { + return coding.getSystem() == researchAllowedPolicySystem && (coding.getCode() == researchAllowedPolicyOid) + } + + fun isIsRequestDateInRange(requestdate: Date?, provPeriod: Period): Boolean { + val isRequestDateAfterOrEqualStart = provPeriod.getStart().compareTo(requestdate) + val isRequestDateBeforeOrEqualEnd = provPeriod.getEnd().compareTo(requestdate) + return isRequestDateAfterOrEqualStart <= 0 && isRequestDateBeforeOrEqualEnd >= 0 + } + } \ No newline at end of file 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 676310b..bb226c0 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt @@ -23,8 +23,6 @@ import com.fasterxml.jackson.databind.ObjectMapper import de.ukw.ccc.bwhc.dto.MtbFile import dev.dnpm.etl.processor.* import dev.dnpm.etl.processor.config.AppConfigProperties -import dev.dnpm.etl.processor.consent.ConsentDomain -import dev.dnpm.etl.processor.consent.ICheckConsent import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.monitoring.Report import dev.dnpm.etl.processor.monitoring.Request @@ -33,19 +31,15 @@ import dev.dnpm.etl.processor.monitoring.RequestType import dev.dnpm.etl.processor.output.* import dev.dnpm.etl.processor.pseudonym.PseudonymizeService import dev.dnpm.etl.processor.pseudonym.anonymizeContentWith -import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith import dev.pcvolkmer.mv64e.mtb.Mtb -import dev.pcvolkmer.mv64e.mtb.MvhSubmissionType import org.apache.commons.codec.binary.Base32 import org.apache.commons.codec.digest.DigestUtils -import org.hl7.fhir.r4.model.Consent import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service import java.lang.RuntimeException -import java.time.Clock import java.time.Instant import java.util.* @@ -83,7 +77,7 @@ class RequestProcessor( val pid = PatientId(extractPatientIdentifier(mtbFile)) val isConsentOk = consentProcessor != null && - consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || consentProcessor == null; + consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || consentProcessor == null if (isConsentOk) { mtbFile pseudonymizeWith pseudonymizeService mtbFile anonymizeContentWith pseudonymizeService @@ -190,7 +184,7 @@ class RequestProcessor( ) ) - } catch (e: Exception) { + } catch (_: Exception) { requestService.save( Request( uuid = requestId, diff --git a/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java b/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java index bb26312..d26eca2 100644 --- a/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java +++ b/src/test/java/dev/dnpm/etl/processor/consent/GicsConsentServiceTest.java @@ -3,20 +3,13 @@ package dev.dnpm.etl.processor.consent; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; - -import ca.uhn.fhir.context.FhirContext; import com.fasterxml.jackson.databind.ObjectMapper; import dev.dnpm.etl.processor.config.AppConfiguration; import dev.dnpm.etl.processor.config.AppFhirConfig; import dev.dnpm.etl.processor.config.GIcsConfigProperties; -import java.io.IOException; -import java.io.InputStream; import java.time.Instant; -import java.time.OffsetDateTime; import java.util.Date; import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Consent.ConsentProvisionType; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; @@ -27,17 +20,13 @@ import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; -import org.springframework.core.io.ClassPathResource; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.client.MockRestServiceServer; - @ContextConfiguration(classes = {AppConfiguration.class, ObjectMapper.class}) @TestPropertySource(properties = {"app.consent.gics.enabled=true", "app.consent.gics.uri=http://localhost:8090/ttp-fhir/fhir/gics"}) @@ -63,7 +52,6 @@ public class GicsConsentServiceTest { @BeforeEach public void setUp() { mockRestServiceServer = MockRestServiceServer.createServer(appConfiguration.restTemplate()); - } @Test @@ -72,12 +60,10 @@ public class GicsConsentServiceTest { new ParametersParameterComponent().setName("consented") .setValue(new BooleanType().setValue(true))); - mockRestServiceServer.expect( - requestTo("http://localhost:8090/ttp-fhir/fhir/gics" - + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond( - withSuccess(appFhirConfig.fhirContext().newJsonParser() - .encodeResourceToString(responseConsented), - MediaType.APPLICATION_JSON)); + mockRestServiceServer.expect(requestTo( + "http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)) + .andRespond(withSuccess(appFhirConfig.fhirContext().newJsonParser() + .encodeResourceToString(responseConsented), MediaType.APPLICATION_JSON)); var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456"); assertThat(consentStatus).isEqualTo(TtpConsentStatus.BROAD_CONSENT_GIVEN); @@ -89,11 +75,10 @@ public class GicsConsentServiceTest { new ParametersParameterComponent().setName("consented") .setValue(new BooleanType().setValue(false))); - mockRestServiceServer.expect( - requestTo("http://localhost:8090/ttp-fhir/fhir/gics" - + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond( - withSuccess(appFhirConfig.fhirContext().newJsonParser() - .encodeResourceToString(responseRevoced), + mockRestServiceServer.expect(requestTo( + "http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT)) + .andRespond(withSuccess( + appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(responseRevoced), MediaType.APPLICATION_JSON)); var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456"); @@ -104,15 +89,13 @@ public class GicsConsentServiceTest { @Test void gicsParameterInvalid() { final OperationOutcome responseErrorOutcome = new OperationOutcome().addIssue( - new OperationOutcomeIssueComponent().setSeverity( - IssueSeverity.ERROR).setCode(IssueType.PROCESSING) - .setDiagnostics("Invalid policy parameter...")); + new OperationOutcomeIssueComponent().setSeverity(IssueSeverity.ERROR) + .setCode(IssueType.PROCESSING).setDiagnostics("Invalid policy parameter...")); mockRestServiceServer.expect( requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond( withSuccess(appFhirConfig.fhirContext().newJsonParser() - .encodeResourceToString(responseErrorOutcome), - MediaType.APPLICATION_JSON)); + .encodeResourceToString(responseErrorOutcome), MediaType.APPLICATION_JSON)); var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456"); assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK); @@ -122,51 +105,19 @@ public class GicsConsentServiceTest { void buildRequestParameterCurrentPolicyStatesForPersonTest() { String pid = "12345678"; - var result = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson(gIcsConfigProperties, - pid, Date.from(Instant.now()),gIcsConfigProperties.getGenomDeConsentDomainName()); + var result = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson( + gIcsConfigProperties, pid, Date.from(Instant.now()), + gIcsConfigProperties.getGenomDeConsentDomainName()); - assertThat(result.getParameter().size()).as("should contain 3 parameter resources").isEqualTo(3); + assertThat(result.getParameter().size()).as("should contain 3 parameter resources") + .isEqualTo(3); - assertThat(((StringType)result.getParameter("domain").getValue()).getValue()).isEqualTo(gIcsConfigProperties.getGenomDeConsentDomainName()); - assertThat(((Identifier)result.getParameter("personIdentifier").getValue()).getValue()).isEqualTo(pid); + assertThat(((StringType) result.getParameter("domain").getValue()).getValue()).isEqualTo( + gIcsConfigProperties.getGenomDeConsentDomainName()); + assertThat( + ((Identifier) result.getParameter("personIdentifier").getValue()).getValue()).isEqualTo( + pid); } - @ParameterizedTest - @CsvSource({ - "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,PERMIT,expect permit", - "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on start", - "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2055-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on end", - "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2021-06-23T00:00:00+02:00,NULL,date is before start", - "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2060-06-23T00:00:00+02:00,NULL,date is after end", - "2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-07-23T00:00:00+02:00,NULL,system not found - therefore expect NULL", - "2.16.840.1.113883.3.1937.777.24.5.3.27,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,DENY,provision is denied"}) - void getProvisionTypeByPolicyCode(String code, String system, String timeStamp, String expected, - String desc) { - - var testData = getDummyBroadConsent(); - - Date requestDate = Date.from(OffsetDateTime.parse(timeStamp).toInstant()); - - var result = gicsConsentService.getProvisionTypeByPolicyCode(testData, code, system, requestDate); - assertThat(result).isNotNull(); - assertThat(result).isNotEmpty(); - - assertThat(result.get()).as(desc).isEqualTo(ConsentProvisionType.valueOf(expected)); - } - - private Bundle getDummyBroadConsent() { - - InputStream bundle; - try { - bundle = new ClassPathResource( - "fake_broadConsent_gics_response_permit.json").getInputStream(); - } catch (IOException e) { - throw new RuntimeException(e); - } - - return FhirContext.forR4().newJsonParser().parseResource(Bundle.class, bundle); - - } - } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt index df5ed8d..eb7e0b6 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt @@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import de.ukw.ccc.bwhc.dto.* import de.ukw.ccc.bwhc.dto.Consent.Status import dev.dnpm.etl.processor.CustomMediaType -import dev.dnpm.etl.processor.consent.ConsentCheckFileBased +import dev.dnpm.etl.processor.consent.ConsentByMtbFile import dev.dnpm.etl.processor.consent.GicsConsentService import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.services.RequestProcessor @@ -68,7 +68,7 @@ class MtbFileRestControllerTest { ) { this.requestProcessor = requestProcessor val controller = MtbFileRestController(requestProcessor, - ConsentCheckFileBased() + ConsentByMtbFile() ) this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build() } @@ -220,7 +220,7 @@ class MtbFileRestControllerTest { ) { this.requestProcessor = requestProcessor val controller = MtbFileRestController(requestProcessor, - ConsentCheckFileBased() + ConsentByMtbFile() ) this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build() } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt index aa667f4..d436a8e 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt @@ -26,7 +26,7 @@ import de.ukw.ccc.bwhc.dto.Patient import dev.dnpm.etl.processor.config.GIcsConfigProperties import dev.dnpm.etl.processor.config.JacksonConfig import dev.dnpm.etl.processor.services.ConsentProcessor -import dev.dnpm.etl.processor.services.TransformationServiceTest +import dev.dnpm.etl.processor.services.ConsentProcessorTest import dev.pcvolkmer.mv64e.mtb.* import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle @@ -252,7 +252,7 @@ class ExtensionsTest { val bundle = Bundle() - val dummyConsent = TransformationServiceTest.getDummyConsent() + val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent() dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID" bundle.addEntry().resource = dummyConsent diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt new file mode 100644 index 0000000..15e4904 --- /dev/null +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt @@ -0,0 +1,160 @@ +package dev.dnpm.etl.processor.services + +import ca.uhn.fhir.context.FhirContext +import com.fasterxml.jackson.databind.ObjectMapper +import dev.dnpm.etl.processor.config.GIcsConfigProperties +import dev.dnpm.etl.processor.config.JacksonConfig +import dev.dnpm.etl.processor.consent.GicsConsentService +import dev.pcvolkmer.mv64e.mtb.* +import org.assertj.core.api.Assertions.assertThat +import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.CodeableConcept +import org.hl7.fhir.r4.model.Coding +import org.hl7.fhir.r4.model.Consent +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.CsvSource +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.whenever +import org.springframework.core.io.ClassPathResource +import java.io.IOException +import java.io.InputStream +import java.time.Instant +import java.time.OffsetDateTime +import java.util.* + +@ExtendWith(MockitoExtension::class) +class ConsentProcessorTest { + + private lateinit var gicsConsentService: GicsConsentService + private lateinit var objectMapper: ObjectMapper + private lateinit var gIcsConfigProperties: GIcsConfigProperties + private lateinit var fhirContext: FhirContext + private lateinit var consentProcessor: ConsentProcessor + + @BeforeEach + fun setups( + @Mock gicsConsentService: GicsConsentService, + ) { + + this.gIcsConfigProperties = GIcsConfigProperties(null, null, null, true) + val jacksonConfig = JacksonConfig() + this.objectMapper = jacksonConfig.objectMapper() + this.fhirContext = JacksonConfig.fhirContext() + this.gicsConsentService = gicsConsentService + + this.consentProcessor = + ConsentProcessor(gIcsConfigProperties, objectMapper, fhirContext, gicsConsentService) + } + + @Test + fun consentOk() { + assertThat(consentProcessor.toString()).isNotNull + // prep gICS response + doAnswer { getDummyBroadConsentBundle() }.whenever(gicsConsentService) + .getBroadConsent(any(), any()) + doAnswer { Bundle() }.whenever(gicsConsentService) + .getGenomDeConsent(any(), any()) + + val inputMtb = Mtb.builder() + .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()).build() + val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb) + + assertThat(checkResult).isTrue + assertThat(inputMtb.metadata.researchConsents).hasSize(13) + } + + companion object { + fun getDummyGenomDeConsent(): Consent { + val consent = Consent() + consent.id = "consent 1 id" + consent.patient.reference = "Patient/1234-pat1" + + consent.provision.setType( + Consent.ConsentProvisionType.fromCode( + "deny" + ) + ) + consent.provision.period.start = + Date.from(Instant.parse("2025-06-23T00:00:00.00Z")) + consent.provision.period.end = + Date.from(Instant.parse("3000-01-01T00:00:00.00Z")) + + val addProvision1 = consent.provision.addProvision() + addProvision1.setType(Consent.ConsentProvisionType.fromCode("permit")) + addProvision1.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z")) + addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z")) + addProvision1.code.addLast( + CodeableConcept( + Coding( + "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV", + "Teilnahme", + "Teilnahme am Modellvorhaben und Einwilligung zur Genomsequenzierung" + ) + ) + ) + + val addProvision2 = consent.provision.addProvision() + addProvision2.setType(Consent.ConsentProvisionType.fromCode("deny")) + addProvision2.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z")) + addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z")) + addProvision2.code.addLast( + CodeableConcept( + Coding( + "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV", + "Rekontaktierung", + "Re-Identifizierung meiner Daten über die Vertrauensstelle beim Robert Koch-Institut und in die erneute Kontaktaufnahme durch meine behandelnde Ärztin oder meinen behandelnden Arzt" + ) + ) + ) + return consent + } + } + + @ParameterizedTest + @CsvSource( + "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,PERMIT,expect permit", + "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on start", + "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2055-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on end", + "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2021-06-23T00:00:00+02:00,NULL,date is before start", + "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2060-06-23T00:00:00+02:00,NULL,date is after end", + "2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-07-23T00:00:00+02:00,NULL,system not found - therefore expect NULL", + "2.16.840.1.113883.3.1937.777.24.5.3.27,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,DENY,provision is denied" + ) + fun getProvisionTypeByPolicyCode( + code: String?, system: String?, timeStamp: String, expected: String?, + desc: String? + ) { + val testData = getDummyBroadConsentBundle() + + val requestDate = Date.from(OffsetDateTime.parse(timeStamp).toInstant()) + + val result: Consent.ConsentProvisionType = + consentProcessor.getProvisionTypeByPolicyCode(testData, code, system, requestDate) + assertThat(result).isNotNull() + + + assertThat(result).`as`(desc) + .isEqualTo(Consent.ConsentProvisionType.valueOf(expected!!)) + } + + fun getDummyBroadConsentBundle(): Bundle { + val bundle: InputStream? + try { + bundle = ClassPathResource( + "fake_broadConsent_gics_response_permit.json" + ).getInputStream() + } catch (e: IOException) { + throw RuntimeException(e) + } + + return FhirContext.forR4().newJsonParser() + .parseResource(Bundle::class.java, bundle) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt index 10be51e..113245a 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt @@ -34,8 +34,6 @@ import dev.pcvolkmer.mv64e.mtb.Mtb import dev.pcvolkmer.mv64e.mtb.MvhMetadata import dev.pcvolkmer.mv64e.mtb.Provision import org.hl7.fhir.instance.model.api.IBaseResource -import org.hl7.fhir.r4.model.CodeableConcept -import org.hl7.fhir.r4.model.Coding import java.time.Instant import java.util.Date @@ -145,7 +143,7 @@ class TransformationServiceTest { .date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build() ) ).build() - val consent = getDummyConsent() + val consent = ConsentProcessorTest.getDummyGenomDeConsent() mvhMetadata.researchConsents = mutableListOf() mvhMetadata.researchConsents.add(mapOf(consent.id to consent as IBaseResource)) @@ -157,51 +155,5 @@ class TransformationServiceTest { } - companion object { - fun getDummyConsent(): org.hl7.fhir.r4.model.Consent { - val modelVorhabenConsent = org.hl7.fhir.r4.model.Consent() - modelVorhabenConsent.id = "consent 1 id" - modelVorhabenConsent.patient.reference = "Patient/1234-pat1" - modelVorhabenConsent.provision.setType( - org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode( - "deny" - ) - ) - modelVorhabenConsent.provision.period.start = - Date.from(Instant.parse("2025-06-23T00:00:00.00Z")) - modelVorhabenConsent.provision.period.end = - Date.from(Instant.parse("3000-01-01T00:00:00.00Z")) - - - val addProvision1 = modelVorhabenConsent.provision.addProvision() - addProvision1.setType(org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode("permit")) - addProvision1.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z")) - addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z")) - addProvision1.code.addLast( - CodeableConcept( - Coding( - "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV", - "Teilnahme", - "Teilnahme am Modellvorhaben und Einwilligung zur Genomsequenzierung" - ) - ) - ) - - val addProvision2 = modelVorhabenConsent.provision.addProvision() - addProvision2.setType(org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode("deny")) - addProvision2.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z")) - addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z")) - addProvision2.code.addLast( - CodeableConcept( - Coding( - "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV", - "Rekontaktierung", - "Re-Identifizierung meiner Daten über die Vertrauensstelle beim Robert Koch-Institut und in die erneute Kontaktaufnahme durch meine behandelnde Ärztin oder meinen behandelnden Arzt" - ) - ) - ) - return modelVorhabenConsent - } - } } \ No newline at end of file