mirror of
https://github.com/pcvolkmer/etl-processor.git
synced 2025-07-01 14:12:55 +00:00
chore: add HL7 Fhir serialization to ObjectMapper, so we can add FHIR consent resources to mtb file
This commit is contained in:
@ -38,6 +38,7 @@ public class GicsConsentService implements ICheckConsent {
|
||||
private final GIcsConfigProperties gIcsConfigProperties;
|
||||
|
||||
public static final String IS_CONSENTED_ENDPOINT = "/$isConsented";
|
||||
public static final String IS_POLICY_STATES_FOR_PERSON_ENDPOINT = "/$currentPolicyStatesForPerson";
|
||||
private final RetryTemplate retryTemplate;
|
||||
private final RestTemplate restTemplate;
|
||||
private final FhirContext fhirContext;
|
||||
@ -55,7 +56,7 @@ public class GicsConsentService implements ICheckConsent {
|
||||
log.info("GicsConsentService initialized...");
|
||||
}
|
||||
|
||||
public String getGicsUri() {
|
||||
public String getGicsUri(String endpoint) {
|
||||
if (url == null) {
|
||||
final String gIcsBaseUri = gIcsConfigProperties.getUri();
|
||||
if (StringUtils.isBlank(gIcsBaseUri)) {
|
||||
@ -81,7 +82,7 @@ public class GicsConsentService implements ICheckConsent {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static Parameters getIsConsentedRequestParam(GIcsConfigProperties configProperties,
|
||||
protected static Parameters getIsConsentedRequestParam(GIcsConfigProperties configProperties,
|
||||
String personIdentifierValue) {
|
||||
var result = new Parameters();
|
||||
result.addParameter(new ParametersParameterComponent().setName("personIdentifier").setValue(
|
||||
@ -115,13 +116,13 @@ public class GicsConsentService implements ICheckConsent {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected String callGicsApi(Parameters parameter) {
|
||||
protected String callGicsApi(Parameters parameter, String endpoint) {
|
||||
var parameterAsXml = fhirContext.newXmlParser().encodeResourceToString(parameter);
|
||||
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(parameterAsXml, this.httpHeader);
|
||||
ResponseEntity<String> responseEntity;
|
||||
try {
|
||||
var url = getGicsUri();
|
||||
var url = getGicsUri(endpoint);
|
||||
|
||||
responseEntity = retryTemplate.execute(
|
||||
ctx -> restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class));
|
||||
@ -155,7 +156,8 @@ public class GicsConsentService implements ICheckConsent {
|
||||
var parameter = GicsConsentService.getIsConsentedRequestParam(gIcsConfigProperties,
|
||||
personIdentifierValue);
|
||||
|
||||
var consentStatusResponse = callGicsApi(parameter);
|
||||
var consentStatusResponse = callGicsApi(parameter,
|
||||
GicsConsentService.IS_CONSENTED_ENDPOINT);
|
||||
return evaluateConsentResponse(consentStatusResponse);
|
||||
}
|
||||
|
||||
@ -167,7 +169,8 @@ public class GicsConsentService implements ICheckConsent {
|
||||
var requestParameter = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson(
|
||||
gIcsConfigProperties, personIdentifierValue, requestDate, consentDomain);
|
||||
|
||||
var consentDataSerialized = callGicsApi(requestParameter);
|
||||
var consentDataSerialized = callGicsApi(requestParameter,
|
||||
GicsConsentService.IS_POLICY_STATES_FOR_PERSON_ENDPOINT);
|
||||
|
||||
if (consentDataSerialized == null) {
|
||||
// error occurred - should not process further!
|
||||
@ -222,7 +225,7 @@ public class GicsConsentService implements ICheckConsent {
|
||||
ConsentDomain.Modelvorhaben64e, requestDate);
|
||||
}
|
||||
|
||||
private static Parameters buildRequestParameterCurrentPolicyStatesForPerson(
|
||||
protected static Parameters buildRequestParameterCurrentPolicyStatesForPerson(
|
||||
GIcsConfigProperties gIcsConfigProperties, String personIdentifierValue, Date requestDate,
|
||||
String targetDomain) {
|
||||
var requestParameter = new Parameters();
|
||||
|
@ -87,7 +87,7 @@ data class GIcsConfigProperties(
|
||||
/**
|
||||
* Domain of Modelvorhaben 64e consent resources
|
||||
**/
|
||||
val gnomDeConsentDomainName: String = "GenomeDE_MV",
|
||||
val gnomDeConsentDomainName: String = "GenomDE_MV",
|
||||
|
||||
/**
|
||||
* Value to expect in case of positiv consent
|
||||
|
@ -100,17 +100,21 @@ class AppConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun reportService(objectMapper: ObjectMapper): ReportService {
|
||||
return ReportService(objectMapper)
|
||||
fun reportService(): ReportService {
|
||||
return ReportService(getObjectMapper())
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun getObjectMapper () : ObjectMapper{
|
||||
return JacksonConfig().objectMapper()
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun transformationService(
|
||||
objectMapper: ObjectMapper,
|
||||
configProperties: AppConfigProperties
|
||||
): TransformationService {
|
||||
logger.info("Apply ${configProperties.transformations.size} transformation rules")
|
||||
return TransformationService(objectMapper, configProperties.transformations.map {
|
||||
return TransformationService(getObjectMapper(), configProperties.transformations.map {
|
||||
Transformation.of(it.path) from it.from to it.to
|
||||
})
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package dev.dnpm.etl.processor.config
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource
|
||||
|
||||
class FhirResourceModule : SimpleModule() {
|
||||
init {
|
||||
addSerializer(IBaseResource::class.java, IBaseResourceSerializer())
|
||||
addDeserializer(IBaseResource::class.java, IBaseResourceDeserializer())
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package dev.dnpm.etl.processor.config
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource
|
||||
|
||||
class IBaseResourceDeserializer : JsonDeserializer<IBaseResource>() {
|
||||
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): IBaseResource {
|
||||
val fhirContext = FhirContext.forR4()
|
||||
|
||||
val jsonNode = p?.readValueAsTree<JsonNode>()
|
||||
val json = jsonNode?.toString()
|
||||
|
||||
return fhirContext.newJsonParser().parseResource(json) as IBaseResource
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package dev.dnpm.etl.processor.config
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.databind.JsonSerializer
|
||||
import com.fasterxml.jackson.databind.SerializerProvider
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource
|
||||
|
||||
class IBaseResourceSerializer : JsonSerializer<IBaseResource>() {
|
||||
override fun serialize(
|
||||
value: IBaseResource,
|
||||
gen: JsonGenerator,
|
||||
serializers: SerializerProvider
|
||||
) {
|
||||
val fhirContext = FhirContext.forR4()
|
||||
val json = fhirContext.newJsonParser().encodeResourceToString(value)
|
||||
gen.writeRawValue(json)
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package dev.dnpm.etl.processor.config
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
|
||||
|
||||
@Configuration
|
||||
class JacksonConfig {
|
||||
|
||||
@Bean
|
||||
fun objectMapper(): ObjectMapper =
|
||||
ObjectMapper()
|
||||
.registerModule(FhirResourceModule())
|
||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).registerModule(
|
||||
JavaTimeModule()
|
||||
);
|
||||
}
|
@ -7,13 +7,18 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
|
||||
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.time.Instant;
|
||||
import java.util.Date;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Identifier;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome.IssueType;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
@ -43,6 +48,9 @@ public class GicsConsentServiceTest {
|
||||
@Autowired
|
||||
AppFhirConfig appFhirConfig;
|
||||
|
||||
@Autowired
|
||||
GIcsConfigProperties gIcsConfigProperties;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
mockRestServiceServer = MockRestServiceServer.createServer(appConfiguration.restTemplate());
|
||||
@ -100,4 +108,17 @@ public class GicsConsentServiceTest {
|
||||
var consentStatus = gicsConsentService.getTtpConsentStatus("123456");
|
||||
assertThat(consentStatus).isEqualTo(TtpConsentStatus.FAILED_TO_ASK);
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildRequestParameterCurrentPolicyStatesForPersonTest() {
|
||||
|
||||
String pid = "12345678";
|
||||
var result = GicsConsentService.buildRequestParameterCurrentPolicyStatesForPerson(gIcsConfigProperties,
|
||||
pid, Date.from(Instant.now()),gIcsConfigProperties.getGnomDeConsentDomainName());
|
||||
|
||||
assertThat(result.getParameter().size()).as("should contain 3 parameter resources").isEqualTo(3);
|
||||
|
||||
assertThat(((StringType)result.getParameter("domain").getValue()).getValue()).isEqualTo(gIcsConfigProperties.getGnomDeConsentDomainName());
|
||||
assertThat(((Identifier)result.getParameter("personIdentifier").getValue()).getValue()).isEqualTo(pid);
|
||||
}
|
||||
}
|
||||
|
@ -19,14 +19,22 @@
|
||||
|
||||
package dev.dnpm.etl.processor.services
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import de.ukw.ccc.bwhc.dto.Consent
|
||||
import de.ukw.ccc.bwhc.dto.Diagnosis
|
||||
import de.ukw.ccc.bwhc.dto.Icd10
|
||||
import de.ukw.ccc.bwhc.dto.MtbFile
|
||||
import dev.dnpm.etl.processor.config.JacksonConfig
|
||||
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import dev.pcvolkmer.mv64e.mtb.Mtb
|
||||
import dev.pcvolkmer.mv64e.mtb.MvhMetadata
|
||||
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
|
||||
|
||||
class TransformationServiceTest {
|
||||
|
||||
@ -35,7 +43,7 @@ class TransformationServiceTest {
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
this.service = TransformationService(
|
||||
ObjectMapper(), listOf(
|
||||
JacksonConfig().objectMapper(), listOf(
|
||||
Transformation.of("consent.status") from Consent.Status.ACTIVE to Consent.Status.REJECTED,
|
||||
Transformation.of("diagnoses[*].icd10.version") from "2013" to "2014",
|
||||
)
|
||||
@ -92,4 +100,79 @@ class TransformationServiceTest {
|
||||
assertThat(actual.consent.status).isEqualTo(Consent.Status.REJECTED)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldTransformConsentValues() {
|
||||
val mtbFile = MtbFile.builder().withDiagnoses(
|
||||
listOf(
|
||||
Diagnosis.builder().withId("1234").withIcd10(Icd10("F79.9").also {
|
||||
it.version = "2013"
|
||||
}).build(),
|
||||
Diagnosis.builder().withId("5678").withIcd10(Icd10("F79.8").also {
|
||||
it.version = "2019"
|
||||
}).build()
|
||||
)
|
||||
).build()
|
||||
|
||||
val actual = this.service.transform(mtbFile)
|
||||
|
||||
assertThat(actual).isNotNull
|
||||
assertThat(actual.diagnoses[0].icd10.code).isEqualTo("F79.9")
|
||||
assertThat(actual.diagnoses[0].icd10.version).isEqualTo("2014")
|
||||
assertThat(actual.diagnoses[1].icd10.code).isEqualTo("F79.8")
|
||||
assertThat(actual.diagnoses[1].icd10.version).isEqualTo("2019")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldTransformConsent() {
|
||||
val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build();
|
||||
|
||||
assertThat(mvhMetadata).isNotNull
|
||||
mvhMetadata.modelProjectConsent =
|
||||
ModelProjectConsent.builder().date(Date.from(Instant.now())).version("1").build()
|
||||
val consent1 = org.hl7.fhir.r4.model.Consent()
|
||||
consent1.id = "consent 1 id"
|
||||
consent1.patient.reference = "Patient/1234-pat1"
|
||||
|
||||
consent1.provision.setType(org.hl7.fhir.r4.model.Consent.ConsentProvisionType.fromCode("deny"))
|
||||
consent1.provision.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
|
||||
consent1.provision.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
|
||||
|
||||
|
||||
val addProvision1 = consent1.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 = consent1.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"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
mvhMetadata.researchConsents = mutableListOf()
|
||||
mvhMetadata.researchConsents.add(mapOf(consent1.id to consent1 as IBaseResource))
|
||||
|
||||
val mtbFile = Mtb.builder().metadata(mvhMetadata).build()
|
||||
|
||||
val transformed = service.transform(mtbFile)
|
||||
assertThat(transformed.metadata.modelProjectConsent.date).isNotNull
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user