mirror of
https://github.com/pcvolkmer/etl-processor.git
synced 2025-07-17 12:52:54 +00:00
refactor: moved consent evaluation to new ConsentProcessor.kt
This commit is contained in:
@ -23,9 +23,9 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import de.ukw.ccc.bwhc.dto.*
|
import de.ukw.ccc.bwhc.dto.*
|
||||||
import dev.dnpm.etl.processor.anyValueClass
|
import dev.dnpm.etl.processor.anyValueClass
|
||||||
import dev.dnpm.etl.processor.config.AppSecurityConfiguration
|
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.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.TokenRepository
|
||||||
import dev.dnpm.etl.processor.security.UserRoleRepository
|
import dev.dnpm.etl.processor.security.UserRoleRepository
|
||||||
import dev.dnpm.etl.processor.services.RequestProcessor
|
import dev.dnpm.etl.processor.services.RequestProcessor
|
||||||
@ -55,7 +55,7 @@ import org.springframework.test.web.servlet.post
|
|||||||
classes = [
|
classes = [
|
||||||
MtbFileRestController::class,
|
MtbFileRestController::class,
|
||||||
AppSecurityConfiguration::class,
|
AppSecurityConfiguration::class,
|
||||||
ConsentCheckFileBased::class, ICheckConsent::class
|
ConsentByMtbFile::class, IGetConsent::class
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@MockitoBean(types = [TokenRepository::class, RequestProcessor::class])
|
@MockitoBean(types = [TokenRepository::class, RequestProcessor::class])
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
package dev.dnpm.etl.processor.consent;
|
package dev.dnpm.etl.processor.consent;
|
||||||
|
|
||||||
import dev.pcvolkmer.mv64e.mtb.Mtb;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Consent.ConsentProvisionType;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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...");
|
log.info("ConsentCheckFileBased initialized...");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,12 +20,12 @@ public class ConsentCheckFileBased implements ICheckConsent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bundle getBroadConsent(String personIdentifierValue, Date requestDate) {
|
public Bundle getBroadConsent(String personIdentifierValue, Date requestDate) {
|
||||||
return ICheckConsent.super.getBroadConsent(personIdentifierValue, requestDate);
|
return IGetConsent.super.getBroadConsent(personIdentifierValue, requestDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
|
public Bundle getGenomDeConsent(String personIdentifierValue, Date requestDate) {
|
||||||
return ICheckConsent.super.getGenomDeConsent(personIdentifierValue, requestDate);
|
return IGetConsent.super.getGenomDeConsent(personIdentifierValue, requestDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -36,10 +33,4 @@ public class ConsentCheckFileBased implements ICheckConsent {
|
|||||||
ConsentDomain targetConsentDomain, Date requestDate) {
|
ConsentDomain targetConsentDomain, Date requestDate) {
|
||||||
return new Bundle();
|
return new Bundle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle,
|
|
||||||
Date requestDate, ConsentDomain consentDomain) {
|
|
||||||
return ConsentProvisionType.NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,27 +2,18 @@ package dev.dnpm.etl.processor.consent;
|
|||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
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.AppFhirConfig;
|
||||||
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
|
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Optional;
|
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
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.DateType;
|
||||||
import org.hl7.fhir.r4.model.Identifier;
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
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.hl7.fhir.r4.model.StringType;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -39,7 +30,7 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
|
|
||||||
public class GicsConsentService implements ICheckConsent {
|
public class GicsConsentService implements IGetConsent {
|
||||||
|
|
||||||
private final Logger log = LoggerFactory.getLogger(GicsConsentService.class);
|
private final Logger log = LoggerFactory.getLogger(GicsConsentService.class);
|
||||||
|
|
||||||
@ -272,83 +263,4 @@ public class GicsConsentService implements ICheckConsent {
|
|||||||
return TtpConsentStatus.FAILED_TO_ASK;
|
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<ConsentProvisionType> 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<ConsentProvisionType> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,8 @@ package dev.dnpm.etl.processor.consent;
|
|||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
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
|
* Get broad consent status for a patient identifier
|
||||||
@ -50,12 +49,4 @@ public interface ICheckConsent {
|
|||||||
Bundle currentConsentForPersonAndTemplate(String personIdentifierValue,
|
Bundle currentConsentForPersonAndTemplate(String personIdentifierValue,
|
||||||
ConsentDomain targetConsentDomain, Date requestDate);
|
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);
|
|
||||||
|
|
||||||
}
|
}
|
@ -20,8 +20,8 @@
|
|||||||
package dev.dnpm.etl.processor.config
|
package dev.dnpm.etl.processor.config
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import dev.dnpm.etl.processor.consent.ConsentCheckFileBased
|
import dev.dnpm.etl.processor.consent.ConsentByMtbFile
|
||||||
import dev.dnpm.etl.processor.consent.ICheckConsent
|
import dev.dnpm.etl.processor.consent.IGetConsent
|
||||||
import dev.dnpm.etl.processor.consent.GicsConsentService
|
import dev.dnpm.etl.processor.consent.GicsConsentService
|
||||||
import dev.dnpm.etl.processor.monitoring.*
|
import dev.dnpm.etl.processor.monitoring.*
|
||||||
import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator
|
import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator
|
||||||
@ -212,7 +212,7 @@ class AppConfiguration {
|
|||||||
fun gicsConsentService(
|
fun gicsConsentService(
|
||||||
gIcsConfigProperties: GIcsConfigProperties,
|
gIcsConfigProperties: GIcsConfigProperties,
|
||||||
retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig
|
retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig
|
||||||
): ICheckConsent {
|
): IGetConsent {
|
||||||
return GicsConsentService(
|
return GicsConsentService(
|
||||||
gIcsConfigProperties,
|
gIcsConfigProperties,
|
||||||
retryTemplate,
|
retryTemplate,
|
||||||
@ -227,7 +227,7 @@ class AppConfiguration {
|
|||||||
gIcsConfigProperties: GIcsConfigProperties,
|
gIcsConfigProperties: GIcsConfigProperties,
|
||||||
getObjectMapper: ObjectMapper,
|
getObjectMapper: ObjectMapper,
|
||||||
appFhirConfig: AppFhirConfig,
|
appFhirConfig: AppFhirConfig,
|
||||||
gicsConsentService: ICheckConsent
|
gicsConsentService: IGetConsent
|
||||||
): ConsentProcessor {
|
): ConsentProcessor {
|
||||||
return ConsentProcessor(
|
return ConsentProcessor(
|
||||||
gIcsConfigProperties,
|
gIcsConfigProperties,
|
||||||
@ -253,8 +253,8 @@ class AppConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
fun constService(): ICheckConsent {
|
fun iGetConsentService(): IGetConsent {
|
||||||
return ConsentCheckFileBased()
|
return ConsentByMtbFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import de.ukw.ccc.bwhc.dto.Consent
|
|||||||
import de.ukw.ccc.bwhc.dto.MtbFile
|
import de.ukw.ccc.bwhc.dto.MtbFile
|
||||||
import dev.dnpm.etl.processor.CustomMediaType
|
import dev.dnpm.etl.processor.CustomMediaType
|
||||||
import dev.dnpm.etl.processor.PatientId
|
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.consent.TtpConsentStatus
|
||||||
import dev.dnpm.etl.processor.services.RequestProcessor
|
import dev.dnpm.etl.processor.services.RequestProcessor
|
||||||
import dev.pcvolkmer.mv64e.mtb.Mtb
|
import dev.pcvolkmer.mv64e.mtb.Mtb
|
||||||
@ -35,7 +35,7 @@ import org.springframework.web.bind.annotation.*
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(path = ["mtbfile", "mtb"])
|
@RequestMapping(path = ["mtbfile", "mtb"])
|
||||||
class MtbFileRestController(
|
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)
|
private val logger = LoggerFactory.getLogger(MtbFileRestController::class.java)
|
||||||
@ -63,7 +63,7 @@ class MtbFileRestController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkConsentStatus(mtbFile: MtbFile): Pair<TtpConsentStatus, Boolean> {
|
private fun checkConsentStatus(mtbFile: MtbFile): Pair<TtpConsentStatus, Boolean> {
|
||||||
var ttpConsentStatus = iCheckConsent.getTtpBroadConsentStatus(mtbFile.patient.id)
|
var ttpConsentStatus = iGetConsent.getTtpBroadConsentStatus(mtbFile.patient.id)
|
||||||
|
|
||||||
val isConsentOK =
|
val isConsentOK =
|
||||||
(ttpConsentStatus.equals(TtpConsentStatus.UNKNOWN_CHECK_FILE) && mtbFile.consent.status == Consent.Status.ACTIVE) ||
|
(ttpConsentStatus.equals(TtpConsentStatus.UNKNOWN_CHECK_FILE) && mtbFile.consent.status == Consent.Status.ACTIVE) ||
|
||||||
|
@ -6,12 +6,14 @@ import com.fasterxml.jackson.core.type.TypeReference
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import dev.dnpm.etl.processor.config.GIcsConfigProperties
|
import dev.dnpm.etl.processor.config.GIcsConfigProperties
|
||||||
import dev.dnpm.etl.processor.consent.ConsentDomain
|
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.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized
|
||||||
import dev.pcvolkmer.mv64e.mtb.*
|
import dev.pcvolkmer.mv64e.mtb.*
|
||||||
import org.hl7.fhir.r4.model.Bundle
|
import org.apache.commons.lang3.NotImplementedException
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept
|
import org.hl7.fhir.r4.model.*
|
||||||
import org.hl7.fhir.r4.model.Consent
|
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.hl7.fhir.r4.model.Consent.ProvisionComponent
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@ -23,9 +25,10 @@ import java.util.*
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
class ConsentProcessor(
|
class ConsentProcessor(
|
||||||
private val gIcsConfigProperties: GIcsConfigProperties, private val objectMapper: ObjectMapper,
|
private val gIcsConfigProperties: GIcsConfigProperties,
|
||||||
|
private val objectMapper: ObjectMapper,
|
||||||
private val fhirContext: FhirContext,
|
private val fhirContext: FhirContext,
|
||||||
private val consentService: ICheckConsent?
|
private val consentService: IGetConsent?
|
||||||
) {
|
) {
|
||||||
private var logger: Logger = LoggerFactory.getLogger("ConsentProcessor")
|
private var logger: Logger = LoggerFactory.getLogger("ConsentProcessor")
|
||||||
|
|
||||||
@ -80,11 +83,11 @@ class ConsentProcessor(
|
|||||||
|
|
||||||
embedBroadConsentResources(mtbFile, broadConsent)
|
embedBroadConsentResources(mtbFile, broadConsent)
|
||||||
|
|
||||||
val broadConsentStatus = consentService.getProvisionTypeByPolicyCode(
|
val broadConsentStatus = getProvisionTypeByPolicyCode(
|
||||||
broadConsent, requestDate, ConsentDomain.BroadConsent
|
broadConsent, requestDate, ConsentDomain.BroadConsent
|
||||||
)
|
)
|
||||||
|
|
||||||
val genomDeSequencingStatus = consentService.getProvisionTypeByPolicyCode(
|
val genomDeSequencingStatus = getProvisionTypeByPolicyCode(
|
||||||
genomeDeConsent, requestDate, ConsentDomain.Modelvorhaben64e
|
genomeDeConsent, requestDate, ConsentDomain.Modelvorhaben64e
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -92,27 +95,22 @@ class ConsentProcessor(
|
|||||||
// bc not asked
|
// bc not asked
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus ||
|
if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus || Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus) return true
|
||||||
Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus
|
|
||||||
) return true
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun embedBroadConsentResources(mtbFile: Mtb, broadConsent: Bundle) {
|
fun embedBroadConsentResources(mtbFile: Mtb, broadConsent: Bundle) {
|
||||||
for (entry in broadConsent.getEntry()) {
|
for (entry in broadConsent.getEntry()) {
|
||||||
val resource = entry.getResource()
|
val resource = entry.getResource()
|
||||||
if (resource is Consent) {
|
if (resource is Consent) {
|
||||||
// since jackson convertValue does not work here,
|
// since jackson convertValue does not work here,
|
||||||
// we need another step to back to string, before we convert to object map
|
// we need another step to back to string, before we convert to object map
|
||||||
val asJsonString =
|
val asJsonString = fhirContext.newJsonParser().encodeResourceToString(resource)
|
||||||
fhirContext.newJsonParser().encodeResourceToString(resource)
|
|
||||||
try {
|
try {
|
||||||
val mapOfJson: HashMap<String?, Any?>? =
|
val mapOfJson: HashMap<String?, Any?>? =
|
||||||
objectMapper.readValue<HashMap<String?, Any?>?>(
|
objectMapper.readValue<HashMap<String?, Any?>?>(
|
||||||
asJsonString,
|
asJsonString, object : TypeReference<HashMap<String?, Any?>?>() {})
|
||||||
object : TypeReference<HashMap<String?, Any?>?>() {
|
|
||||||
})
|
|
||||||
mtbFile.metadata.researchConsents.add(mapOfJson)
|
mtbFile.metadata.researchConsents.add(mapOfJson)
|
||||||
} catch (e: JsonProcessingException) {
|
} catch (e: JsonProcessingException) {
|
||||||
throw RuntimeException(e)
|
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()) {
|
for (entry in consentGnomeDe.getEntry()) {
|
||||||
val resource = entry.getResource()
|
val resource = entry.getResource()
|
||||||
if (resource !is Consent) {
|
if (resource !is Consent) {
|
||||||
@ -157,8 +155,7 @@ class ConsentProcessor(
|
|||||||
val provision = Provision.builder()
|
val provision = Provision.builder()
|
||||||
.type(ConsentProvision.valueOf(provisionComponent.getType().name))
|
.type(ConsentProvision.valueOf(provisionComponent.getType().name))
|
||||||
.date(provisionComponent.getPeriod().getStart())
|
.date(provisionComponent.getPeriod().getStart())
|
||||||
.purpose(modelProjectConsentPurpose)
|
.purpose(modelProjectConsentPurpose).build()
|
||||||
.build()
|
|
||||||
|
|
||||||
mtbFile.metadata.modelProjectConsent.provisions.add(provision)
|
mtbFile.metadata.modelProjectConsent.provisions.add(provision)
|
||||||
} catch (ioe: IOException) {
|
} catch (ioe: IOException) {
|
||||||
@ -183,4 +180,89 @@ class ConsentProcessor(
|
|||||||
mtbFile.metadata.type = MvhSubmissionType.INITIAL
|
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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -23,8 +23,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import de.ukw.ccc.bwhc.dto.MtbFile
|
import de.ukw.ccc.bwhc.dto.MtbFile
|
||||||
import dev.dnpm.etl.processor.*
|
import dev.dnpm.etl.processor.*
|
||||||
import dev.dnpm.etl.processor.config.AppConfigProperties
|
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.consent.TtpConsentStatus
|
||||||
import dev.dnpm.etl.processor.monitoring.Report
|
import dev.dnpm.etl.processor.monitoring.Report
|
||||||
import dev.dnpm.etl.processor.monitoring.Request
|
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.output.*
|
||||||
import dev.dnpm.etl.processor.pseudonym.PseudonymizeService
|
import dev.dnpm.etl.processor.pseudonym.PseudonymizeService
|
||||||
import dev.dnpm.etl.processor.pseudonym.anonymizeContentWith
|
import dev.dnpm.etl.processor.pseudonym.anonymizeContentWith
|
||||||
import dev.dnpm.etl.processor.pseudonym.ensureMetaDataIsInitialized
|
|
||||||
import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith
|
import dev.dnpm.etl.processor.pseudonym.pseudonymizeWith
|
||||||
import dev.pcvolkmer.mv64e.mtb.Mtb
|
import dev.pcvolkmer.mv64e.mtb.Mtb
|
||||||
import dev.pcvolkmer.mv64e.mtb.MvhSubmissionType
|
|
||||||
import org.apache.commons.codec.binary.Base32
|
import org.apache.commons.codec.binary.Base32
|
||||||
import org.apache.commons.codec.digest.DigestUtils
|
import org.apache.commons.codec.digest.DigestUtils
|
||||||
import org.hl7.fhir.r4.model.Consent
|
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.context.ApplicationEventPublisher
|
import org.springframework.context.ApplicationEventPublisher
|
||||||
import org.springframework.stereotype.Service
|
import org.springframework.stereotype.Service
|
||||||
import java.lang.RuntimeException
|
import java.lang.RuntimeException
|
||||||
import java.time.Clock
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -83,7 +77,7 @@ class RequestProcessor(
|
|||||||
val pid = PatientId(extractPatientIdentifier(mtbFile))
|
val pid = PatientId(extractPatientIdentifier(mtbFile))
|
||||||
|
|
||||||
val isConsentOk = consentProcessor != null &&
|
val isConsentOk = consentProcessor != null &&
|
||||||
consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || consentProcessor == null;
|
consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || consentProcessor == null
|
||||||
if (isConsentOk) {
|
if (isConsentOk) {
|
||||||
mtbFile pseudonymizeWith pseudonymizeService
|
mtbFile pseudonymizeWith pseudonymizeService
|
||||||
mtbFile anonymizeContentWith pseudonymizeService
|
mtbFile anonymizeContentWith pseudonymizeService
|
||||||
@ -190,7 +184,7 @@ class RequestProcessor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
requestService.save(
|
requestService.save(
|
||||||
Request(
|
Request(
|
||||||
uuid = requestId,
|
uuid = requestId,
|
||||||
|
@ -3,20 +3,13 @@ package dev.dnpm.etl.processor.consent;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
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.match.MockRestRequestMatchers.requestTo;
|
||||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import dev.dnpm.etl.processor.config.AppConfiguration;
|
import dev.dnpm.etl.processor.config.AppConfiguration;
|
||||||
import dev.dnpm.etl.processor.config.AppFhirConfig;
|
import dev.dnpm.etl.processor.config.AppFhirConfig;
|
||||||
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
|
import dev.dnpm.etl.processor.config.GIcsConfigProperties;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
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.Identifier;
|
||||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
|
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.hl7.fhir.r4.model.StringType;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
|
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.TestPropertySource;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.web.client.MockRestServiceServer;
|
import org.springframework.test.web.client.MockRestServiceServer;
|
||||||
|
|
||||||
|
|
||||||
@ContextConfiguration(classes = {AppConfiguration.class, ObjectMapper.class})
|
@ContextConfiguration(classes = {AppConfiguration.class, ObjectMapper.class})
|
||||||
@TestPropertySource(properties = {"app.consent.gics.enabled=true",
|
@TestPropertySource(properties = {"app.consent.gics.enabled=true",
|
||||||
"app.consent.gics.uri=http://localhost:8090/ttp-fhir/fhir/gics"})
|
"app.consent.gics.uri=http://localhost:8090/ttp-fhir/fhir/gics"})
|
||||||
@ -63,7 +52,6 @@ public class GicsConsentServiceTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mockRestServiceServer = MockRestServiceServer.createServer(appConfiguration.restTemplate());
|
mockRestServiceServer = MockRestServiceServer.createServer(appConfiguration.restTemplate());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -72,12 +60,10 @@ public class GicsConsentServiceTest {
|
|||||||
new ParametersParameterComponent().setName("consented")
|
new ParametersParameterComponent().setName("consented")
|
||||||
.setValue(new BooleanType().setValue(true)));
|
.setValue(new BooleanType().setValue(true)));
|
||||||
|
|
||||||
mockRestServiceServer.expect(
|
mockRestServiceServer.expect(requestTo(
|
||||||
requestTo("http://localhost:8090/ttp-fhir/fhir/gics"
|
"http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT))
|
||||||
+ GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond(
|
.andRespond(withSuccess(appFhirConfig.fhirContext().newJsonParser()
|
||||||
withSuccess(appFhirConfig.fhirContext().newJsonParser()
|
.encodeResourceToString(responseConsented), MediaType.APPLICATION_JSON));
|
||||||
.encodeResourceToString(responseConsented),
|
|
||||||
MediaType.APPLICATION_JSON));
|
|
||||||
|
|
||||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.BROAD_CONSENT_GIVEN);
|
assertThat(consentStatus).isEqualTo(TtpConsentStatus.BROAD_CONSENT_GIVEN);
|
||||||
@ -89,11 +75,10 @@ public class GicsConsentServiceTest {
|
|||||||
new ParametersParameterComponent().setName("consented")
|
new ParametersParameterComponent().setName("consented")
|
||||||
.setValue(new BooleanType().setValue(false)));
|
.setValue(new BooleanType().setValue(false)));
|
||||||
|
|
||||||
mockRestServiceServer.expect(
|
mockRestServiceServer.expect(requestTo(
|
||||||
requestTo("http://localhost:8090/ttp-fhir/fhir/gics"
|
"http://localhost:8090/ttp-fhir/fhir/gics" + GicsConsentService.IS_CONSENTED_ENDPOINT))
|
||||||
+ GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond(
|
.andRespond(withSuccess(
|
||||||
withSuccess(appFhirConfig.fhirContext().newJsonParser()
|
appFhirConfig.fhirContext().newJsonParser().encodeResourceToString(responseRevoced),
|
||||||
.encodeResourceToString(responseRevoced),
|
|
||||||
MediaType.APPLICATION_JSON));
|
MediaType.APPLICATION_JSON));
|
||||||
|
|
||||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||||
@ -104,15 +89,13 @@ public class GicsConsentServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
void gicsParameterInvalid() {
|
void gicsParameterInvalid() {
|
||||||
final OperationOutcome responseErrorOutcome = new OperationOutcome().addIssue(
|
final OperationOutcome responseErrorOutcome = new OperationOutcome().addIssue(
|
||||||
new OperationOutcomeIssueComponent().setSeverity(
|
new OperationOutcomeIssueComponent().setSeverity(IssueSeverity.ERROR)
|
||||||
IssueSeverity.ERROR).setCode(IssueType.PROCESSING)
|
.setCode(IssueType.PROCESSING).setDiagnostics("Invalid policy parameter..."));
|
||||||
.setDiagnostics("Invalid policy parameter..."));
|
|
||||||
|
|
||||||
mockRestServiceServer.expect(
|
mockRestServiceServer.expect(
|
||||||
requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond(
|
requestTo(GICS_BASE_URI + GicsConsentService.IS_CONSENTED_ENDPOINT)).andRespond(
|
||||||
withSuccess(appFhirConfig.fhirContext().newJsonParser()
|
withSuccess(appFhirConfig.fhirContext().newJsonParser()
|
||||||
.encodeResourceToString(responseErrorOutcome),
|
.encodeResourceToString(responseErrorOutcome), MediaType.APPLICATION_JSON));
|
||||||
MediaType.APPLICATION_JSON));
|
|
||||||
|
|
||||||
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
var consentStatus = gicsConsentService.getTtpBroadConsentStatus("123456");
|
||||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
|
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
|
||||||
@ -122,51 +105,19 @@ public class GicsConsentServiceTest {
|
|||||||
void buildRequestParameterCurrentPolicyStatesForPersonTest() {
|
void buildRequestParameterCurrentPolicyStatesForPersonTest() {
|
||||||
|
|
||||||
String pid = "12345678";
|
String pid = "12345678";
|
||||||
var result = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson(gIcsConfigProperties,
|
var result = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson(
|
||||||
pid, Date.from(Instant.now()),gIcsConfigProperties.getGenomDeConsentDomainName());
|
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(((StringType) result.getParameter("domain").getValue()).getValue()).isEqualTo(
|
||||||
assertThat(((Identifier)result.getParameter("personIdentifier").getValue()).getValue()).isEqualTo(pid);
|
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import de.ukw.ccc.bwhc.dto.*
|
import de.ukw.ccc.bwhc.dto.*
|
||||||
import de.ukw.ccc.bwhc.dto.Consent.Status
|
import de.ukw.ccc.bwhc.dto.Consent.Status
|
||||||
import dev.dnpm.etl.processor.CustomMediaType
|
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.GicsConsentService
|
||||||
import dev.dnpm.etl.processor.consent.TtpConsentStatus
|
import dev.dnpm.etl.processor.consent.TtpConsentStatus
|
||||||
import dev.dnpm.etl.processor.services.RequestProcessor
|
import dev.dnpm.etl.processor.services.RequestProcessor
|
||||||
@ -68,7 +68,7 @@ class MtbFileRestControllerTest {
|
|||||||
) {
|
) {
|
||||||
this.requestProcessor = requestProcessor
|
this.requestProcessor = requestProcessor
|
||||||
val controller = MtbFileRestController(requestProcessor,
|
val controller = MtbFileRestController(requestProcessor,
|
||||||
ConsentCheckFileBased()
|
ConsentByMtbFile()
|
||||||
)
|
)
|
||||||
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
||||||
}
|
}
|
||||||
@ -220,7 +220,7 @@ class MtbFileRestControllerTest {
|
|||||||
) {
|
) {
|
||||||
this.requestProcessor = requestProcessor
|
this.requestProcessor = requestProcessor
|
||||||
val controller = MtbFileRestController(requestProcessor,
|
val controller = MtbFileRestController(requestProcessor,
|
||||||
ConsentCheckFileBased()
|
ConsentByMtbFile()
|
||||||
)
|
)
|
||||||
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import de.ukw.ccc.bwhc.dto.Patient
|
|||||||
import dev.dnpm.etl.processor.config.GIcsConfigProperties
|
import dev.dnpm.etl.processor.config.GIcsConfigProperties
|
||||||
import dev.dnpm.etl.processor.config.JacksonConfig
|
import dev.dnpm.etl.processor.config.JacksonConfig
|
||||||
import dev.dnpm.etl.processor.services.ConsentProcessor
|
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 dev.pcvolkmer.mv64e.mtb.*
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.hl7.fhir.r4.model.Bundle
|
import org.hl7.fhir.r4.model.Bundle
|
||||||
@ -252,7 +252,7 @@ class ExtensionsTest {
|
|||||||
|
|
||||||
|
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
val dummyConsent = TransformationServiceTest.getDummyConsent()
|
val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent()
|
||||||
dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID"
|
dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID"
|
||||||
bundle.addEntry().resource = dummyConsent
|
bundle.addEntry().resource = dummyConsent
|
||||||
|
|
||||||
|
@ -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>(Bundle::class.java, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -34,8 +34,6 @@ import dev.pcvolkmer.mv64e.mtb.Mtb
|
|||||||
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
|
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
|
||||||
import dev.pcvolkmer.mv64e.mtb.Provision
|
import dev.pcvolkmer.mv64e.mtb.Provision
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource
|
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.time.Instant
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@ -145,7 +143,7 @@ class TransformationServiceTest {
|
|||||||
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build()
|
.date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build()
|
||||||
)
|
)
|
||||||
).build()
|
).build()
|
||||||
val consent = getDummyConsent()
|
val consent = ConsentProcessorTest.getDummyGenomDeConsent()
|
||||||
|
|
||||||
mvhMetadata.researchConsents = mutableListOf()
|
mvhMetadata.researchConsents = mutableListOf()
|
||||||
mvhMetadata.researchConsents.add(mapOf(consent.id to consent as IBaseResource))
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user