diff --git a/src/main/java/dev/dnpm/etl/processor/consent/BaseConsentService.java b/src/main/java/dev/dnpm/etl/processor/consent/BaseConsentService.java deleted file mode 100644 index 9af2aa3..0000000 --- a/src/main/java/dev/dnpm/etl/processor/consent/BaseConsentService.java +++ /dev/null @@ -1,112 +0,0 @@ -package dev.dnpm.etl.processor.consent; - -import ca.uhn.fhir.context.FhirContext; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import dev.dnpm.etl.processor.config.GIcsConfigProperties; -import dev.pcvolkmer.mv64e.mtb.ConsentProvision; -import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose; -import dev.pcvolkmer.mv64e.mtb.Mtb; -import dev.pcvolkmer.mv64e.mtb.Provision; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Consent; -import org.hl7.fhir.r4.model.Consent.ProvisionComponent; -import org.hl7.fhir.r4.model.Resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class BaseConsentService implements ICheckConsent { - - protected final GIcsConfigProperties gIcsConfigProperties; - - private final ObjectMapper objectMapper; - protected Logger logger = LoggerFactory.getLogger(BaseConsentService.class); - static FhirContext fhirCtx = FhirContext.forR4(); - - public BaseConsentService(GIcsConfigProperties gIcsConfigProperties, - ObjectMapper objectMapper) { - this.gIcsConfigProperties = gIcsConfigProperties; - this.objectMapper = objectMapper; - } - - public void embedBroadConsentResources(Mtb mtbFile, Bundle broadConsent) { - - for (Bundle.BundleEntryComponent entry : broadConsent.getEntry()) { - Resource resource = entry.getResource(); - if (resource instanceof Consent) { - // since jackson convertValue does not work here, - // we need another step to back to string, before we convert to object map - var asJsonString = fhirCtx.newJsonParser().encodeResourceToString(resource); - try { - var mapOfJson = objectMapper.readValue(asJsonString, - new TypeReference>() { - }); - mtbFile.getMetadata().getResearchConsents().add(mapOfJson); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - } - } - - public void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe) { - for (Bundle.BundleEntryComponent entry : consentGnomeDe.getEntry()) { - Resource resource = entry.getResource(); - if (!(resource instanceof Consent consentFhirResource)) { - continue; - } - - // We expect only one provision in collection, therefore get first or none - List provisions = consentFhirResource.getProvision().getProvision(); - if (provisions.isEmpty()) { - continue; - } - - var provisionComponent = provisions.getFirst(); - - String provisionCode = null; - if (provisionComponent.getCode() != null && !provisionComponent.getCode().isEmpty()) { - CodeableConcept codeableConcept = provisionComponent.getCode().getFirst(); - if (codeableConcept.getCoding() != null && !codeableConcept.getCoding().isEmpty()) { - provisionCode = codeableConcept.getCoding().getFirst().getCode(); - } - } - - if (provisionCode != null) { - try { - ModelProjectConsentPurpose modelProjectConsentPurpose = - ModelProjectConsentPurpose.forValue(provisionCode); - - if (ModelProjectConsentPurpose.SEQUENCING.equals(modelProjectConsentPurpose)) { - // CONVENTION: wrapping date is date of SEQUENCING consent - mtbFile.getMetadata().getModelProjectConsent() - .setDate(consentFhirResource.getDateTime()); - } - - Provision provision = Provision.builder() - .type(ConsentProvision.valueOf(provisionComponent.getType().name())) - .date(provisionComponent.getPeriod().getStart()) - .purpose(modelProjectConsentPurpose) - .build(); - - mtbFile.getMetadata().getModelProjectConsent().getProvisions().add(provision); - - } catch (IOException ioe) { - logger.error( - "Provision code '" + provisionCode + "' is unknown and cannot be mapped.", - ioe.toString()); - } - } - - if (!mtbFile.getMetadata().getModelProjectConsent().getProvisions().isEmpty()) { - mtbFile.getMetadata().getModelProjectConsent() - .setVersion(gIcsConfigProperties.getGenomeDeConsentVersion()); - } - } - } -} diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ConsentCheckFileBased.java b/src/main/java/dev/dnpm/etl/processor/consent/ConsentCheckFileBased.java index df1209b..331ea13 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/ConsentCheckFileBased.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/ConsentCheckFileBased.java @@ -42,14 +42,4 @@ public class ConsentCheckFileBased implements ICheckConsent { Date requestDate, ConsentDomain consentDomain) { return ConsentProvisionType.NULL; } - - @Override - public void embedBroadConsentResources(Mtb mtbFile, Bundle broadConsent) { - throw new NotImplementedException("not intended to be implemented here!"); - } - - @Override - public void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe) { - throw new NotImplementedException("not intended to be implemented here!"); - } } 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 40f1b84..e8f7e4b 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/GicsConsentService.java @@ -39,7 +39,7 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; -public class GicsConsentService extends BaseConsentService { +public class GicsConsentService implements ICheckConsent { private final Logger log = LoggerFactory.getLogger(GicsConsentService.class); @@ -49,17 +49,18 @@ public class GicsConsentService extends BaseConsentService { private final RestTemplate restTemplate; private final FhirContext fhirContext; private final HttpHeaders httpHeader; + private final GIcsConfigProperties gIcsConfigProperties; private String url; public GicsConsentService(GIcsConfigProperties gIcsConfigProperties, - RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig, ObjectMapper objectMapper) { - super(gIcsConfigProperties,objectMapper); + RetryTemplate retryTemplate, RestTemplate restTemplate, AppFhirConfig appFhirConfig) { this.retryTemplate = retryTemplate; this.restTemplate = restTemplate; this.fhirContext = appFhirConfig.fhirContext(); httpHeader = buildHeader(gIcsConfigProperties.getUsername(), gIcsConfigProperties.getPassword()); + this.gIcsConfigProperties = gIcsConfigProperties; log.info("GicsConsentService initialized..."); } @@ -207,7 +208,8 @@ public class GicsConsentService extends BaseConsentService { String consentDomain; switch (targetConsentDomain) { case BroadConsent -> consentDomain = gIcsConfigProperties.getBroadConsentDomainName(); - case Modelvorhaben64e -> consentDomain = gIcsConfigProperties.getGenomDeConsentDomainName(); + case Modelvorhaben64e -> + consentDomain = gIcsConfigProperties.getGenomDeConsentDomainName(); default -> throw new IllegalArgumentException( "target ConsentDomain is missing but must be provided!"); } diff --git a/src/main/java/dev/dnpm/etl/processor/consent/ICheckConsent.java b/src/main/java/dev/dnpm/etl/processor/consent/ICheckConsent.java index b230a3c..584eb4f 100644 --- a/src/main/java/dev/dnpm/etl/processor/consent/ICheckConsent.java +++ b/src/main/java/dev/dnpm/etl/processor/consent/ICheckConsent.java @@ -1,6 +1,5 @@ package dev.dnpm.etl.processor.consent; -import dev.pcvolkmer.mv64e.mtb.Mtb; import java.util.Date; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Consent.ConsentProvisionType; @@ -59,8 +58,4 @@ public interface ICheckConsent { ConsentProvisionType getProvisionTypeByPolicyCode(Bundle consentBundle, Date requestDate, ConsentDomain consentDomain); - - void embedBroadConsentResources(Mtb mtbFile, Bundle broadConsent); - - void addGenomeDbProvisions(Mtb mtbFile, Bundle consentGnomeDe); } diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt index 35eafd5..6beddb0 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt @@ -113,7 +113,7 @@ data class GIcsConfigProperties( * Consent version (fixed version) * */ - val genomeDeConsentVersion: String = "2.0" + val genomeDeConsentVersion: String = "2.0" ) { companion object { const val NAME = "app.consent.gics" 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 fd559a5..03be73d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt @@ -30,6 +30,7 @@ import dev.dnpm.etl.processor.pseudonym.GpasPseudonymGenerator import dev.dnpm.etl.processor.pseudonym.PseudonymizeService import dev.dnpm.etl.processor.security.TokenRepository import dev.dnpm.etl.processor.security.TokenService +import dev.dnpm.etl.processor.services.ConsentProcessor import dev.dnpm.etl.processor.services.Transformation import dev.dnpm.etl.processor.services.TransformationService import org.slf4j.LoggerFactory @@ -75,17 +76,26 @@ class AppConfiguration { } @Bean - fun appFhirConfig(): AppFhirConfig{ + fun appFhirConfig(): AppFhirConfig { return AppFhirConfig() } @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "GPAS") @Bean - fun gpasPseudonymGenerator(configProperties: GPasConfigProperties, retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig): Generator { + fun gpasPseudonymGenerator( + configProperties: GPasConfigProperties, + retryTemplate: RetryTemplate, + restTemplate: RestTemplate, + appFhirConfig: AppFhirConfig + ): Generator { return GpasPseudonymGenerator(configProperties, retryTemplate, restTemplate, appFhirConfig) } - @ConditionalOnProperty(value = ["app.pseudonymize.generator"], havingValue = "BUILDIN", matchIfMissing = true) + @ConditionalOnProperty( + value = ["app.pseudonymize.generator"], + havingValue = "BUILDIN", + matchIfMissing = true + ) @Bean fun buildinPseudonymGenerator(): Generator { return AnonymizingGenerator() @@ -105,7 +115,7 @@ class AppConfiguration { } @Bean - fun getObjectMapper () : ObjectMapper{ + fun getObjectMapper(): ObjectMapper { return JacksonConfig().objectMapper() } @@ -133,7 +143,11 @@ class AppConfiguration { callback: RetryCallback, throwable: Throwable ) { - logger.warn("Error occured: {}. Retrying {}", throwable.message, context.retryCount) + logger.warn( + "Error occured: {}. Retrying {}", + throwable.message, + context.retryCount + ) } }) .build() @@ -141,7 +155,11 @@ class AppConfiguration { @ConditionalOnProperty(value = ["app.security.enable-tokens"], havingValue = "true") @Bean - fun tokenService(userDetailsManager: InMemoryUserDetailsManager, passwordEncoder: PasswordEncoder, tokenRepository: TokenRepository): TokenService { + fun tokenService( + userDetailsManager: InMemoryUserDetailsManager, + passwordEncoder: PasswordEncoder, + tokenRepository: TokenRepository + ): TokenService { return TokenService(userDetailsManager, passwordEncoder, tokenRepository) } @@ -162,7 +180,11 @@ class AppConfiguration { gPasConfigProperties: GPasConfigProperties, connectionCheckUpdateProducer: Sinks.Many ): ConnectionCheckService { - return GPasConnectionCheckService(restTemplate, gPasConfigProperties, connectionCheckUpdateProducer) + return GPasConnectionCheckService( + restTemplate, + gPasConfigProperties, + connectionCheckUpdateProducer + ) } @ConditionalOnProperty(value = ["app.pseudonymizer"], havingValue = "GPAS") @@ -173,7 +195,11 @@ class AppConfiguration { gPasConfigProperties: GPasConfigProperties, connectionCheckUpdateProducer: Sinks.Many ): ConnectionCheckService { - return GPasConnectionCheckService(restTemplate, gPasConfigProperties, connectionCheckUpdateProducer) + return GPasConnectionCheckService( + restTemplate, + gPasConfigProperties, + connectionCheckUpdateProducer + ) } @Bean @@ -183,14 +209,31 @@ class AppConfiguration { @Bean @ConditionalOnProperty(name = ["app.consent.gics.enabled"], havingValue = "true") - fun gicsConsentService( gIcsConfigProperties: GIcsConfigProperties, - retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig, getObjectMapper: ObjectMapper): ICheckConsent { + fun gicsConsentService( + gIcsConfigProperties: GIcsConfigProperties, + retryTemplate: RetryTemplate, restTemplate: RestTemplate, appFhirConfig: AppFhirConfig + ): ICheckConsent { return GicsConsentService( gIcsConfigProperties, retryTemplate, restTemplate, - appFhirConfig, - getObjectMapper + appFhirConfig + ) + } + + @Bean + @ConditionalOnProperty(name = ["app.consent.gics.enabled"], havingValue = "true") + fun gicsConsentProcessor( + gIcsConfigProperties: GIcsConfigProperties, + getObjectMapper: ObjectMapper, + appFhirConfig: AppFhirConfig, + gicsConsentService: ICheckConsent + ): ConsentProcessor { + return ConsentProcessor( + gIcsConfigProperties, + getObjectMapper, + appFhirConfig.fhirContext(), + gicsConsentService ) } @@ -198,10 +241,14 @@ class AppConfiguration { @Bean fun gIcsConnectionCheckService( restTemplate: RestTemplate, - gIcsConfigProperties: GIcsConfigProperties, + gIcsConfigProperties: GIcsConfigProperties, connectionCheckUpdateProducer: Sinks.Many ): ConnectionCheckService { - return GIcsConnectionCheckService(restTemplate, gIcsConfigProperties, connectionCheckUpdateProducer) + return GIcsConnectionCheckService( + restTemplate, + gIcsConfigProperties, + connectionCheckUpdateProducer + ) } @Bean diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt new file mode 100644 index 0000000..1fd0c7b --- /dev/null +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ConsentProcessor.kt @@ -0,0 +1,186 @@ +package dev.dnpm.etl.processor.services + +import ca.uhn.fhir.context.FhirContext +import com.fasterxml.jackson.core.JsonProcessingException +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.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.hl7.fhir.r4.model.Consent.ProvisionComponent +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.io.IOException +import java.time.Clock +import java.time.Instant +import java.util.* + +@Service +class ConsentProcessor( + private val gIcsConfigProperties: GIcsConfigProperties, private val objectMapper: ObjectMapper, + private val fhirContext: FhirContext, + private val consentService: ICheckConsent? +) { + private var logger: Logger = LoggerFactory.getLogger("ConsentProcessor") + + /** + * In case an instance of {@link ICheckConsent} is active, consent will be embedded and checked. + * + * Logik: + * * true IF consent check is disabled. + * * true IF broad consent (BC) has been given. + * * true BC has been asked AND declined but genomDe consent has been consented. + * * ELSE false is returned. + * + * @param mtbFile File v2 (will be enriched with consent data) + * @return true if consent is given + * + */ + fun consentGatedCheckAndTryEmbedding(mtbFile: Mtb): Boolean { + if (consentService == null) { + // consent check seems to be disabled + return true + } + + mtbFile.ensureMetaDataIsInitialized() + + val personIdentifierValue = mtbFile.patient.id + val requestDate = Date.from(Instant.now(Clock.systemUTC())) + + // 1. Broad consent Entry exists? + // 1.1. -> yes and research consent is given -> send mtb file + // 1.2. -> no -> return status error - consent has not been asked + // 2. -> Broad consent found but rejected -> is GenomDe consent provision 'sequencing' given? + // 2.1 -> yes -> send mtb file + // 2.2 -> no -> warn/info no consent given + + /* + * broad consent + */ + val broadConsent = consentService.getBroadConsent(personIdentifierValue, requestDate) + val broadConsentHasBeenAsked = !broadConsent.entry.isEmpty() + + // fast exit - if patient has not been asked, we can skip and exit + if (!broadConsentHasBeenAsked) return false + + val genomeDeConsent = consentService.getGenomDeConsent( + personIdentifierValue, requestDate + ) + + addGenomeDbProvisions(mtbFile, genomeDeConsent) + + + if (!genomeDeConsent.entry.isEmpty()) setGenomDeSubmissionType(mtbFile) + + embedBroadConsentResources(mtbFile, broadConsent) + + val broadConsentStatus = consentService.getProvisionTypeByPolicyCode( + broadConsent, requestDate, ConsentDomain.BroadConsent + ) + + val genomDeSequencingStatus = consentService.getProvisionTypeByPolicyCode( + genomeDeConsent, requestDate, ConsentDomain.Modelvorhaben64e + ) + + if (Consent.ConsentProvisionType.NULL == broadConsentStatus) { + // bc not asked + return false + } + if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus || + Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus + ) return true + + return false + } + + public 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) + try { + val mapOfJson: HashMap? = + objectMapper.readValue?>( + asJsonString, + object : TypeReference?>() { + }) + mtbFile.metadata.researchConsents.add(mapOfJson) + } catch (e: JsonProcessingException) { + throw RuntimeException(e) + } + } + } + } + + public fun addGenomeDbProvisions(mtbFile: Mtb, consentGnomeDe: Bundle) { + for (entry in consentGnomeDe.getEntry()) { + val resource = entry.getResource() + if (resource !is Consent) { + continue + } + + // We expect only one provision in collection, therefore get first or none + val provisions = resource.getProvision().getProvision() + if (provisions.isEmpty()) { + continue + } + + val provisionComponent: ProvisionComponent = provisions.first() + + var provisionCode: String? = null + if (provisionComponent.getCode() != null && !provisionComponent.getCode().isEmpty()) { + val codableConcept: CodeableConcept = provisionComponent.getCode().first() + if (codableConcept.getCoding() != null && !codableConcept.getCoding().isEmpty()) { + provisionCode = codableConcept.getCoding().first().getCode() + } + } + + if (provisionCode != null) { + try { + val modelProjectConsentPurpose = + ModelProjectConsentPurpose.forValue(provisionCode) + + if (ModelProjectConsentPurpose.SEQUENCING == modelProjectConsentPurpose) { + // CONVENTION: wrapping date is date of SEQUENCING consent + mtbFile.metadata.modelProjectConsent.date = resource.getDateTime() + } + + val provision = Provision.builder() + .type(ConsentProvision.valueOf(provisionComponent.getType().name)) + .date(provisionComponent.getPeriod().getStart()) + .purpose(modelProjectConsentPurpose) + .build() + + mtbFile.metadata.modelProjectConsent.provisions.add(provision) + } catch (ioe: IOException) { + logger.error( + "Provision code '$provisionCode' is unknown and cannot be mapped.", + ioe.toString() + ) + } + } + + if (!mtbFile.metadata.modelProjectConsent.provisions.isEmpty()) { + mtbFile.metadata.modelProjectConsent.version = + gIcsConfigProperties.genomeDeConsentVersion + } + } + } + + /** + * fixme: currently we do not have information about submission type + */ + private fun setGenomDeSubmissionType(mtbFile: Mtb) { + mtbFile.metadata.type = MvhSubmissionType.INITIAL + } + +} \ 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 7965a6b..676310b 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt @@ -58,7 +58,7 @@ class RequestProcessor( private val objectMapper: ObjectMapper, private val applicationEventPublisher: ApplicationEventPublisher, private val appConfigProperties: AppConfigProperties, - private val consentService: ICheckConsent? + private val consentProcessor: ConsentProcessor? ) { private var logger: Logger = LoggerFactory.getLogger("RequestProcessor") @@ -78,80 +78,13 @@ class RequestProcessor( processMtbFile(mtbFile, randomRequestId()) } - /** - * In case an instance of {@link ICheckConsent} is active, consent will be embedded and checked. - * - * Logik: - * * true IF consent check is disabled. - * * true IF broad consent (BC) has been given. - * * true BC has been asked AND declined but genomDe consent has been consented. - * * ELSE false is returned. - * - * @param mtbFile File v2 (will be enriched with consent data) - * @return true if consent is given - * - */ - fun consentGatedCheckAndTryEmbedding(mtbFile: Mtb): Boolean { - if (consentService == null) { - // consent check seems to be disabled - return true - } - - mtbFile.ensureMetaDataIsInitialized() - - val personIdentifierValue = extractPatientIdentifier(mtbFile) - val requestDate = Date.from(Instant.now(Clock.systemUTC())) - - // 1. Broad consent Entry exists? - // 1.1. -> yes and research consent is given -> send mtb file - // 1.2. -> no -> return status error - consent has not been asked - // 2. -> Broad consent found but rejected -> is GenomDe consent provision 'sequencing' given? - // 2.1 -> yes -> send mtb file - // 2.2 -> no -> warn/info no consent given - - /* - * broad consent - */ - val broadConsent = consentService.getBroadConsent(personIdentifierValue, requestDate) - val broadConsentHasBeenAsked = !broadConsent.entry.isEmpty() - - // fast exit - if patient has not been asked, we can skip and exit - if (!broadConsentHasBeenAsked) return false - - val genomeDeConsent = consentService.getGenomDeConsent( - personIdentifierValue, requestDate - ) - - consentService.addGenomeDbProvisions(mtbFile, genomeDeConsent) - - // fixme: currently we do not have information about submission type - if (!genomeDeConsent.entry.isEmpty()) mtbFile.metadata.type = MvhSubmissionType.INITIAL - - consentService.embedBroadConsentResources(mtbFile, broadConsent) - - val broadConsentStatus = consentService.getProvisionTypeByPolicyCode( - broadConsent, requestDate, ConsentDomain.BroadConsent - ) - - val genomDeSequencingStatus = consentService.getProvisionTypeByPolicyCode( - genomeDeConsent, requestDate, ConsentDomain.Modelvorhaben64e - ) - - if (Consent.ConsentProvisionType.NULL == broadConsentStatus) { - // bc not asked - return false - } - if (Consent.ConsentProvisionType.PERMIT == broadConsentStatus || - Consent.ConsentProvisionType.PERMIT == genomDeSequencingStatus - ) return true - - return false - } fun processMtbFile(mtbFile: Mtb, requestId: RequestId) { val pid = PatientId(extractPatientIdentifier(mtbFile)) - if (consentGatedCheckAndTryEmbedding(mtbFile)) { + val isConsentOk = consentProcessor != null && + consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || consentProcessor == null; + if (isConsentOk) { mtbFile pseudonymizeWith pseudonymizeService mtbFile anonymizeContentWith pseudonymizeService val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile)) 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 dcbc4d5..aa667f4 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt @@ -19,14 +19,13 @@ package dev.dnpm.etl.processor.pseudonym +import ca.uhn.fhir.context.FhirContext import com.fasterxml.jackson.databind.ObjectMapper import de.ukw.ccc.bwhc.dto.* 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.consent.BaseConsentService -import dev.dnpm.etl.processor.consent.ConsentDomain -import dev.dnpm.etl.processor.consent.TtpConsentStatus +import dev.dnpm.etl.processor.services.ConsentProcessor import dev.dnpm.etl.processor.services.TransformationServiceTest import dev.pcvolkmer.mv64e.mtb.* import org.assertj.core.api.Assertions.assertThat @@ -46,7 +45,7 @@ import java.util.* @ExtendWith(MockitoExtension::class) class ExtensionsTest { - fun getObjectMapper() : ObjectMapper { + fun getObjectMapper(): ObjectMapper { return JacksonConfig().objectMapper() } @@ -251,35 +250,19 @@ class ExtensionsTest { private fun addConsentData(mtbFile: Mtb) { val gIcsConfigProperties = GIcsConfigProperties("", "", "", true) - val baseConsentService = object : BaseConsentService(gIcsConfigProperties, - JacksonConfig().objectMapper()) { - override fun getTtpBroadConsentStatus(personIdentifierValue: String?): TtpConsentStatus? { - throw NotImplementedError("dummy") - } - - override fun currentConsentForPersonAndTemplate( - personIdentifierValue: String?, - targetConsentDomain: ConsentDomain?, - requestDate: Date? - ): Bundle? { - throw NotImplementedError("dummy") - } - - override fun getProvisionTypeByPolicyCode( - consentBundle: Bundle?, - requestDate: Date?, - consentDomain: ConsentDomain? - ): org.hl7.fhir.r4.model.Consent.ConsentProvisionType? { - throw NotImplementedError("dummy") - } - } val bundle = Bundle() val dummyConsent = TransformationServiceTest.getDummyConsent() dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID" bundle.addEntry().resource = dummyConsent - baseConsentService.embedBroadConsentResources(mtbFile, bundle) + ConsentProcessor( + gIcsConfigProperties, + JacksonConfig().objectMapper(), + FhirContext.forR4(), + null + ).embedBroadConsentResources(mtbFile, bundle) + } @Test diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt index 34e6a66..b36c696 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt @@ -60,7 +60,7 @@ class RequestProcessorTest { private lateinit var requestService: RequestService private lateinit var applicationEventPublisher: ApplicationEventPublisher private lateinit var appConfigProperties: AppConfigProperties - private lateinit var gicsConsentService : GicsConsentService + private lateinit var consentProcessor: ConsentProcessor private lateinit var requestProcessor: RequestProcessor @BeforeEach @@ -70,7 +70,7 @@ class RequestProcessorTest { @Mock sender: RestMtbFileSender, @Mock requestService: RequestService, @Mock applicationEventPublisher: ApplicationEventPublisher, - @Mock gicsConsentService: GicsConsentService + @Mock consentProcessor: ConsentProcessor ) { this.pseudonymizeService = pseudonymizeService this.transformationService = transformationService @@ -78,7 +78,7 @@ class RequestProcessorTest { this.requestService = requestService this.applicationEventPublisher = applicationEventPublisher this.appConfigProperties = AppConfigProperties(null) - this.gicsConsentService = gicsConsentService + this.consentProcessor = consentProcessor val objectMapper = ObjectMapper() @@ -90,7 +90,7 @@ class RequestProcessorTest { objectMapper, applicationEventPublisher, appConfigProperties, - gicsConsentService + consentProcessor ) } @@ -348,7 +348,10 @@ class RequestProcessorTest { MtbFileSender.Response(status = RequestStatus.UNKNOWN) }.whenever(sender).send(any()) - this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE) + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE + ) val requestCaptor = argumentCaptor() verify(requestService, times(1)).save(requestCaptor.capture()) @@ -366,7 +369,10 @@ class RequestProcessorTest { MtbFileSender.Response(status = RequestStatus.SUCCESS) }.whenever(sender).send(any()) - this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE) + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE + ) val eventCaptor = argumentCaptor() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -384,7 +390,10 @@ class RequestProcessorTest { MtbFileSender.Response(status = RequestStatus.ERROR) }.whenever(sender).send(any()) - this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE) + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE + ) val eventCaptor = argumentCaptor() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -396,7 +405,10 @@ class RequestProcessorTest { fun testShouldSendDeleteRequestWithPseudonymErrorAndSaveErrorRequestStatus() { doThrow(RuntimeException()).whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - this.requestProcessor.processDeletion(TEST_PATIENT_ID, isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE) + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE + ) val requestCaptor = argumentCaptor() verify(requestService, times(1)).save(requestCaptor.capture())