summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2025-08-12 23:11:50 +0200
committerGitHub2025-08-12 23:11:50 +0200
commit2e881578937ee39bab3cacff9ee09328658341c2 (patch)
tree74e408d8c943ff0ba54df3ff6d4d3bb63a9d236a
parentbf898e5c25da311d90c0d30b0c5ebfd672781d92 (diff)
refactor: remove obsolete bwHC data model V1.0 (#129)
-rw-r--r--README.md46
-rw-r--r--build.gradle.kts2
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt45
-rw-r--r--src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt45
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt4
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt6
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt21
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt33
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt16
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt19
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt10
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSender.kt51
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt1
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt207
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt20
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt5
-rw-r--r--src/main/resources/application-dev.yml2
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt107
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt234
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt116
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSenderTest.kt313
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt106
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt170
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt35
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt4
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt191
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt73
27 files changed, 286 insertions, 1596 deletions
diff --git a/README.md b/README.md
index dd47973..24dba37 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# ETL-Processor for DNPM:DIP [![Run Tests](https://github.com/pcvolkmer/etl-processor/actions/workflows/test.yml/badge.svg)](https://github.com/pcvolkmer/etl-processor/actions/workflows/test.yml)
-Diese Anwendung versendet ein bwHC-MTB-File im bwHC-Datenmodell 1.0 an DNPM:DIP und pseudonymisiert
+Diese Anwendung versendet ein MTB-File im DNPM-Datenmodell 2.1 an DNPM:DIP und pseudonymisiert
die Patienten-ID.
## Einordnung innerhalb einer DNPM-ETL-Strecke
@@ -8,7 +8,7 @@ die Patienten-ID.
Diese Anwendung erlaubt das Entgegennehmen von HTTP/REST-Anfragen aus dem Onkostar-Plugin *
*[onkostar-plugin-dnpmexport](https://github.com/CCC-MF/onkostar-plugin-dnpmexport)**.
-Der Inhalt einer Anfrage, wenn ein bwHC-MTBFile, wird pseudonymisiert und auf Duplikate geprüft.
+Der Inhalt einer Anfrage, wenn ein MTB-File, wird pseudonymisiert und auf Duplikate geprüft.
Duplikate werden verworfen, Änderungen werden weitergeleitet.
Löschanfragen werden immer als Löschanfrage an DNPM:DIP weitergeleitet.
@@ -72,24 +72,9 @@ Siehe hierzu auch: https://github.com/CCC-MF/kafka-to-bwhc
## Konfiguration
-### 🔥 Wichtige Änderungen in Version 0.10
+### 🔥 Wichtige Änderungen in Version 0.11
-Ab Version 0.10 wird [DNPM:DIP](https://github.com/dnpm-dip) unterstützt und als Standardendpunkt
-verwendet.
-Soll noch das alte bwHC-Backend verwendet werden, so ist die Umgebungsvariable `APP_REST_IS_BWHC`
-auf `true` zu setzen.
-
-### 🔥 Breaking Changes nach Version 0.10
-
-In Versionen des ETL-Processors **nach Version 0.10** werden die folgenden Konfigurationsoptionen
-entfernt:
-
-* `APP_KAFKA_TOPIC`: Nutzen Sie nun die Konfigurationsoption `APP_KAFKA_OUTPUT_TOPIC`
-* `APP_KAFKA_RESPONSE_TOPIC`: Nutzen Sie nun die Konfigurationsoption
- `APP_KAFKA_OUTPUT_RESPONSE_TOPIC`
-
-Der Pfad zum Versenden von MTB-Daten ist nun offiziell `/mtb`.
-In Versionen **nach Version 0.10** wird die Unterstützung des Pfads `/mtbfile` entfernt.
+Ab Version 0.11 wird ausschließlich [DNPM:DIP](https://github.com/dnpm-dip) unterstützt.
### Pseudonymisierung der Patienten-ID
@@ -316,18 +301,15 @@ Werden sowohl REST als auch Kafka-Endpunkt konfiguriert, wird nur der REST-Endpu
#### REST
-Folgende Umgebungsvariablen müssen gesetzt sein, damit ein bwHC-MTB-File an DNPM:DIP gesendet wird:
+Folgende Umgebungsvariablen müssen gesetzt sein, damit ein MTB-File an DNPM:DIP gesendet wird:
-* `APP_REST_URI`: URI der zu benutzenden API der Backend-Instanz. Zum Beispiel:
- * `http://localhost:9000/bwhc/etl/api` für **bwHC Backend**
- * `http://localhost:9000/api` für **dnpm:dip**
+* `APP_REST_URI`: URI der zu benutzenden API der Backend-Instanz. Zum Beispiel `http://localhost:9000/api`
* `APP_REST_USERNAME`: Basic-Auth-Benutzername für den REST-Endpunkt
* `APP_REST_PASSWORD`: Basic-Auth-Passwort für den REST-Endpunkt
-* `APP_REST_IS_BWHC`: `true` für **bwHC Backend**, weglassen oder `false` für **dnpm:dip**
#### Kafka-Topics
-Folgende Umgebungsvariablen müssen gesetzt sein, damit ein bwHC-MTB-File an ein Kafka-Topic
+Folgende Umgebungsvariablen müssen gesetzt sein, damit ein MTB-File an ein Kafka-Topic
übermittelt wird:
* `APP_KAFKA_OUTPUT_TOPIC`: Zu verwendendes Topic zum Versenden von Anfragen.
@@ -402,19 +384,7 @@ verwenden möchten.
### Antworten und Statusauswertung
-Anfragen an das bwHC-Backend aus Versionen bis 0.9.x wurden wie folgt behandelt:
-
-| HTTP-Response | Status |
-|----------------|-----------|
-| `HTTP 200` | `SUCCESS` |
-| `HTTP 201` | `WARNING` |
-| `HTTP 400-...` | `ERROR` |
-
-Dies konnte dazu führen, dass zwar mit einem `HTTP 201` geantwortet wurde, aber dennoch in der
-Issue-Liste die
-Severity `error` aufgetaucht ist.
-
-Ab Version 0.10 wird die Issue-Liste der Antwort verwendet und die darion enthaltene höchste
+Seit Version 0.10 wird die Issue-Liste der Antwort verwendet und die darion enthaltene höchste
Severity-Stufe als Ergebnis verwendet.
| Höchste Severity | Status |
diff --git a/build.gradle.kts b/build.gradle.kts
index f451edf..d9f01be 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,7 +16,6 @@ group = "dev.dnpm"
version = "0.11.0-SNAPSHOT"
var versions = mapOf(
- "bwhc-dto-java" to "0.4.0",
"mtb-dto" to "0.1.0-SNAPSHOT",
"hapi-fhir" to "7.6.1",
"mockito-kotlin" to "5.4.0",
@@ -81,7 +80,6 @@ dependencies {
implementation("org.flywaydb:flyway-mysql")
implementation("commons-codec:commons-codec")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
- implementation("de.ukw.ccc:bwhc-dto-java:${versions["bwhc-dto-java"]}")
implementation("dev.pcvolkmer.mv64e:mtb-dto:${versions["mtb-dto"]}") { isChanging = true }
implementation("ca.uhn.hapi.fhir:hapi-fhir-base:${versions["hapi-fhir"]}")
implementation("ca.uhn.hapi.fhir:hapi-fhir-structures-r4:${versions["hapi-fhir"]}")
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt
index 7e48e62..130fea7 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/EtlProcessorApplicationTests.kt
@@ -20,11 +20,11 @@
package dev.dnpm.etl.processor
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.*
import dev.dnpm.etl.processor.monitoring.RequestRepository
import dev.dnpm.etl.processor.monitoring.RequestStatus
-import dev.dnpm.etl.processor.output.BwhcV1MtbFileRequest
+import dev.dnpm.etl.processor.output.DnpmV2MtbFileRequest
import dev.dnpm.etl.processor.output.MtbFileSender
+import dev.pcvolkmer.mv64e.mtb.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
@@ -69,7 +69,7 @@ class EtlProcessorApplicationTests : AbstractTestcontainerTest() {
properties = [
"app.pseudonymize.generator=buildin",
"app.consent.service=none",
- "app.transformations[0].path=diagnoses[*].icd10.version",
+ "app.transformations[0].path=diagnoses[*].code.version",
"app.transformations[0].from=2013",
"app.transformations[0].to=2014",
]
@@ -94,36 +94,21 @@ class EtlProcessorApplicationTests : AbstractTestcontainerTest() {
fun mtbFileIsTransformed() {
doAnswer {
MtbFileSender.Response(RequestStatus.SUCCESS)
- }.whenever(mtbFileSender).send(any<BwhcV1MtbFileRequest>())
+ }.whenever(mtbFileSender).send(any<DnpmV2MtbFileRequest>())
- val mtbFile = MtbFile.builder()
- .withPatient(
+ val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("TEST_12345678")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
+ .id("TEST_12345678")
.build()
)
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("TEST_12345678")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("TEST_12345678")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
- )
- .withDiagnoses(
+ .diagnoses(
listOf(
- Diagnosis.builder()
- .withId("1234")
- .withIcd10(Icd10.builder().withCode("F79.9").withVersion("2013").build())
- .build()
+ MtbDiagnosis.builder()
+ .id("1234")
+ .patient(Reference.builder().id("TEST_12345678").build())
+ .code(Coding.builder().code("F79.9").version("2013").build())
+ .build(),
)
)
.build()
@@ -137,10 +122,10 @@ class EtlProcessorApplicationTests : AbstractTestcontainerTest() {
}
}
- val captor = argumentCaptor<BwhcV1MtbFileRequest>()
+ val captor = argumentCaptor<DnpmV2MtbFileRequest>()
verify(mtbFileSender).send(captor.capture())
assertThat(captor.firstValue.content.diagnoses).hasSize(1).allMatch { diagnosis ->
- diagnosis.icd10.version == "2014"
+ diagnosis.code.version == "2014"
}
}
}
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
index 8aa8ba0..78bdc8f 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -20,7 +20,6 @@
package dev.dnpm.etl.processor.input
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.*
import dev.dnpm.etl.processor.anyValueClass
import dev.dnpm.etl.processor.config.AppSecurityConfiguration
import dev.dnpm.etl.processor.consent.ConsentByMtbFile
@@ -29,6 +28,7 @@ import dev.dnpm.etl.processor.consent.IGetConsent
import dev.dnpm.etl.processor.security.TokenRepository
import dev.dnpm.etl.processor.security.UserRoleRepository
import dev.dnpm.etl.processor.services.RequestProcessor
+import dev.pcvolkmer.mv64e.mtb.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
@@ -48,6 +48,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.delete
import org.springframework.test.web.servlet.post
+import java.time.Instant
+import java.util.*
@WebMvcTest(controllers = [MtbFileRestController::class])
@ExtendWith(value = [MockitoExtension::class, SpringExtension::class])
@@ -93,7 +95,7 @@ class MtbFileRestControllerTest {
status { isAccepted() }
}
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
@Test
@@ -106,7 +108,7 @@ class MtbFileRestControllerTest {
status { isAccepted() }
}
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
@Test
@@ -119,7 +121,7 @@ class MtbFileRestControllerTest {
status { isUnauthorized() }
}
- verify(requestProcessor, never()).processMtbFile(any<MtbFile>())
+ verify(requestProcessor, never()).processMtbFile(any<Mtb>())
}
@Test
@@ -132,7 +134,7 @@ class MtbFileRestControllerTest {
status { isForbidden() }
}
- verify(requestProcessor, never()).processMtbFile(any<MtbFile>())
+ verify(requestProcessor, never()).processMtbFile(any<Mtb>())
}
@Test
@@ -180,7 +182,7 @@ class MtbFileRestControllerTest {
status { isAccepted() }
}
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
@Test
@@ -193,33 +195,26 @@ class MtbFileRestControllerTest {
status { isAccepted() }
}
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
}
companion object {
- val mtbFile: MtbFile = MtbFile.builder()
- .withPatient(
+ val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("PID")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
+ .id("PID")
.build()
)
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("PID")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("PID")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("PID").build())
+ .period(PeriodDate.builder().start(Date.from(Instant.parse("2023-08-08T02:00:00.00Z"))).build())
+ .build()
+ )
)
.build()
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 207785e..1f3c650 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfigProperties.kt
@@ -24,7 +24,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties(AppConfigProperties.NAME)
data class AppConfigProperties(
- var bwhcUri: String?,
var transformations: List<TransformationProperties> = listOf(),
var maxRetryAttempts: Int = 3,
var duplicationDetection: Boolean = true,
@@ -128,8 +127,7 @@ data class GIcsConfigProperties(
data class RestTargetProperties(
val uri: String?,
val username: String?,
- val password: String?,
- val isBwhc: Boolean = false,
+ val password: String?
) {
companion object {
const val NAME = "app.rest"
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt
index 1a18924..62c25bc 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppRestConfiguration.kt
@@ -24,7 +24,6 @@ import dev.dnpm.etl.processor.monitoring.ConnectionCheckService
import dev.dnpm.etl.processor.monitoring.ReportService
import dev.dnpm.etl.processor.monitoring.RestConnectionCheckService
import dev.dnpm.etl.processor.output.MtbFileSender
-import dev.dnpm.etl.processor.output.RestBwhcMtbFileSender
import dev.dnpm.etl.processor.output.RestDipMtbFileSender
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
@@ -57,11 +56,6 @@ class AppRestConfiguration {
retryTemplate: RetryTemplate,
reportService: ReportService,
): MtbFileSender {
- if (restTargetProperties.isBwhc) {
- logger.info("Selected 'RestBwhcMtbFileSender'")
- return RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
- }
-
logger.info("Selected 'RestDipMtbFileSender'")
return RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt b/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt
index 415a68f..47615be 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt
@@ -20,13 +20,13 @@
package dev.dnpm.etl.processor.input
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.Consent
-import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.PatientId
import dev.dnpm.etl.processor.RequestId
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.RequestProcessor
+import dev.pcvolkmer.mv64e.mtb.ConsentProvision
+import dev.pcvolkmer.mv64e.mtb.Mtb
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.slf4j.LoggerFactory
import org.springframework.http.MediaType
@@ -40,7 +40,7 @@ class KafkaInputListener(
override fun onMessage(record: ConsumerRecord<String, String>) {
when (guessMimeType(record)) {
- MediaType.APPLICATION_JSON_VALUE -> handleBwhcMessage(record)
+ MediaType.APPLICATION_JSON_VALUE -> handleDnpmV2Message(record)
CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE -> handleDnpmV2Message(record)
else -> {
/* ignore other messages */
@@ -57,8 +57,11 @@ class KafkaInputListener(
return record.headers().headers("contentType")?.firstOrNull()?.value().contentToString()
}
- private fun handleBwhcMessage(record: ConsumerRecord<String, String>) {
- val mtbFile = objectMapper.readValue(record.value(), MtbFile::class.java)
+ private fun handleDnpmV2Message(record: ConsumerRecord<String, String>) {
+ // Do not handle DNPM-V2 for now
+ logger.warn("Ignoring MTB File in DNPM V2 format: Not implemented yet")
+
+ val mtbFile = objectMapper.readValue(record.value(), Mtb::class.java)
val patientId = PatientId(mtbFile.patient.id)
val firstRequestIdHeader = record.headers().headers("requestId")?.firstOrNull()
val requestId = if (null != firstRequestIdHeader) {
@@ -67,7 +70,8 @@ class KafkaInputListener(
RequestId("")
}
- if (mtbFile.consent.status == Consent.Status.ACTIVE) {
+ // TODO: Use MV Consent for now - needs to be replaced with proper consent evaluation
+ if (mtbFile.metadata.modelProjectConsent.provisions.filter { it.type == ConsentProvision.PERMIT }.isNotEmpty()) {
logger.debug("Accepted MTB File for processing")
if (requestId.isBlank()) {
requestProcessor.processMtbFile(mtbFile)
@@ -88,9 +92,4 @@ class KafkaInputListener(
}
}
- private fun handleDnpmV2Message(record: ConsumerRecord<String, String>) {
- // Do not handle DNPM-V2 for now
- logger.warn("Ignoring MTB File in DNPM V2 format: Not implemented yet")
- }
-
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
index ca64fc9..d00ad25 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt
@@ -19,8 +19,6 @@
package dev.dnpm.etl.processor.input
-import de.ukw.ccc.bwhc.dto.Consent
-import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.PatientId
import dev.dnpm.etl.processor.consent.IGetConsent
@@ -46,23 +44,7 @@ class MtbFileRestController(
return ResponseEntity.ok("Test")
}
- @PostMapping(consumes = [MediaType.APPLICATION_JSON_VALUE])
- fun mtbFile(@RequestBody mtbFile: MtbFile): ResponseEntity<Unit> {
- val consentStatusBooleanPair = checkConsentStatus(mtbFile)
- val ttpConsentStatus = consentStatusBooleanPair.first
- val isConsentOK = consentStatusBooleanPair.second
- if (isConsentOK) {
- logger.debug("Accepted MTB File (bwHC V1) for processing")
- requestProcessor.processMtbFile(mtbFile)
- } else {
- logger.debug("Accepted MTB File (bwHC V1) and process deletion")
- val patientId = PatientId(mtbFile.patient.id)
- requestProcessor.processDeletion(patientId, ttpConsentStatus)
- }
- return ResponseEntity.accepted().build()
- }
-
- @PostMapping(consumes = [CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE])
+ @PostMapping(consumes = [MediaType.APPLICATION_JSON_VALUE, CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE])
fun mtbFile(@RequestBody mtbFile: Mtb): ResponseEntity<Unit> {
logger.debug("Accepted MTB File (DNPM V2) for processing")
requestProcessor.processMtbFile(mtbFile)
@@ -76,17 +58,4 @@ class MtbFileRestController(
return ResponseEntity.accepted().build()
}
- private fun checkConsentStatus(mtbFile: MtbFile): Pair<TtpConsentStatus, Boolean> {
- var ttpConsentStatus = iGetConsent.getTtpBroadConsentStatus(mtbFile.patient.id)
-
- val isConsentOK = (ttpConsentStatus == TtpConsentStatus.UNKNOWN_CHECK_FILE && mtbFile.consent.status == Consent.Status.ACTIVE)
- || ttpConsentStatus == TtpConsentStatus.BROAD_CONSENT_GIVEN
-
- if (ttpConsentStatus == TtpConsentStatus.UNKNOWN_CHECK_FILE && mtbFile.consent.status == Consent.Status.REJECTED) {
- // in case ttp check is disabled - we propagate rejected status anyway
- ttpConsentStatus = TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED
- }
- return Pair(ttpConsentStatus, isConsentOK)
- }
-
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
index fe02b69..37cd5de 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckService.kt
@@ -128,15 +128,11 @@ class RestConnectionCheckService(
fun check() {
result = try {
val available = restTemplate.getForEntity(
- if (restTargetProperties.isBwhc) {
- UriComponentsBuilder.fromUriString(restTargetProperties.uri.toString()).path("").toUriString()
- } else {
- UriComponentsBuilder.fromUriString(restTargetProperties.uri.toString())
- .pathSegment("mtb")
- .pathSegment("kaplan-meier")
- .pathSegment("config")
- .toUriString()
- },
+ UriComponentsBuilder.fromUriString(restTargetProperties.uri.toString())
+ .pathSegment("mtb")
+ .pathSegment("kaplan-meier")
+ .pathSegment("config")
+ .toUriString(),
String::class.java
).statusCode == HttpStatus.OK
@@ -267,4 +263,4 @@ class GIcsConnectionCheckService(
override fun connectionAvailable(): ConnectionCheckResult.GIcsConnectionCheckResult {
return this.result
}
-} \ No newline at end of file
+}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt
index 1f2743e..8be0a1c 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt
@@ -20,11 +20,11 @@
package dev.dnpm.etl.processor.output
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.Consent
-import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.config.KafkaProperties
import dev.dnpm.etl.processor.monitoring.RequestStatus
+import dev.pcvolkmer.mv64e.mtb.Mtb
+import dev.pcvolkmer.mv64e.mtb.MvhMetadata
import org.apache.kafka.clients.producer.ProducerRecord
import org.slf4j.LoggerFactory
import org.springframework.http.MediaType
@@ -50,9 +50,6 @@ class KafkaMtbFileSender(
objectMapper.writeValueAsString(request)
)
when (request) {
- is BwhcV1MtbFileRequest -> record.headers()
- .add("contentType", MediaType.APPLICATION_JSON_VALUE.toByteArray())
-
is DnpmV2MtbFileRequest -> record.headers()
.add(
"contentType",
@@ -75,13 +72,8 @@ class KafkaMtbFileSender(
}
override fun send(request: DeleteRequest): MtbFileSender.Response {
- val dummyMtbFile = MtbFile.builder()
- .withConsent(
- Consent.builder()
- .withPatient(request.patientId.value)
- .withStatus(Consent.Status.REJECTED)
- .build()
- )
+ val dummyMtbFile = Mtb.builder()
+ .metadata(MvhMetadata())
.build()
return try {
@@ -92,7 +84,7 @@ class KafkaMtbFileSender(
key(request),
// Always use old BwhcV1FileRequest with Consent REJECT
objectMapper.writeValueAsString(
- BwhcV1MtbFileRequest(
+ DnpmV2MtbFileRequest(
request.requestId,
dummyMtbFile
)
@@ -119,7 +111,6 @@ class KafkaMtbFileSender(
private fun key(request: MtbRequest): String {
return when (request) {
- is BwhcV1MtbFileRequest -> "{\"pid\": \"${request.content.patient.id}\"}"
is DnpmV2MtbFileRequest -> "{\"pid\": \"${request.content.patient.id}\"}"
is DeleteRequest -> "{\"pid\": \"${request.patientId.value}\"}"
else -> throw IllegalArgumentException("Unsupported request type: ${request::class.simpleName}")
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt
index 9b500f0..7512200 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbRequest.kt
@@ -19,7 +19,6 @@
package dev.dnpm.etl.processor.output
-import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.PatientPseudonym
import dev.dnpm.etl.processor.RequestId
import dev.pcvolkmer.mv64e.mtb.Mtb
@@ -35,15 +34,6 @@ sealed interface MtbFileRequest<out T> : MtbRequest {
fun patientPseudonym(): PatientPseudonym
}
-data class BwhcV1MtbFileRequest(
- override val requestId: RequestId,
- override val content: MtbFile
-) : MtbFileRequest<MtbFile> {
- override fun patientPseudonym(): PatientPseudonym {
- return PatientPseudonym(content.patient.id)
- }
-}
-
data class DnpmV2MtbFileRequest(
override val requestId: RequestId,
override val content: Mtb
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSender.kt
deleted file mode 100644
index fbe6d0d..0000000
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSender.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of ETL-Processor
- *
- * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package dev.dnpm.etl.processor.output
-
-import dev.dnpm.etl.processor.PatientPseudonym
-import dev.dnpm.etl.processor.config.RestTargetProperties
-import dev.dnpm.etl.processor.monitoring.ReportService
-import org.springframework.retry.support.RetryTemplate
-import org.springframework.web.client.RestTemplate
-import org.springframework.web.util.UriComponentsBuilder
-
-class RestBwhcMtbFileSender(
- restTemplate: RestTemplate,
- private val restTargetProperties: RestTargetProperties,
- retryTemplate: RetryTemplate,
- reportService: ReportService,
-) : RestMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) {
-
- override fun sendUrl(): String {
- return UriComponentsBuilder
- .fromUriString(restTargetProperties.uri.toString())
- .pathSegment("MTBFile")
- .toUriString()
- }
-
- override fun deleteUrl(patientId: PatientPseudonym): String {
- return UriComponentsBuilder
- .fromUriString(restTargetProperties.uri.toString())
- .pathSegment("Patient")
- .pathSegment(patientId.value)
- .toUriString()
- }
-
-} \ No newline at end of file
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt
index 78222b2..ec6ff85 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSender.kt
@@ -108,7 +108,6 @@ abstract class RestMtbFileSender(
val password = restTargetProperties.password
val headers = HttpHeaders()
headers.contentType = when (request) {
- is BwhcV1MtbFileRequest -> MediaType.APPLICATION_JSON
is DnpmV2MtbFileRequest -> CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON
else -> MediaType.APPLICATION_JSON
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
index 50d5b20..28a7d3c 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/extensions.kt
@@ -19,7 +19,6 @@
package dev.dnpm.etl.processor.pseudonym
-import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.PatientId
import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
import dev.pcvolkmer.mv64e.mtb.Mtb
@@ -28,210 +27,6 @@ import org.apache.commons.codec.digest.DigestUtils
/** Replaces patient ID with generated patient pseudonym
*
- * @param pseudonymizeService The pseudonymizeService to be used
- * @return The MTB file containing patient pseudonymes
- */
-infix fun MtbFile.pseudonymizeWith(pseudonymizeService: PseudonymizeService) {
- val patientPseudonym = pseudonymizeService.patientPseudonym(PatientId(this.patient.id)).value
-
- this.episode?.patient = patientPseudonym
- this.carePlans?.forEach { it.patient = patientPseudonym }
- this.patient.id = patientPseudonym
- this.claims?.forEach { it.patient = patientPseudonym }
- this.consent?.patient = patientPseudonym
- this.claimResponses?.forEach { it.patient = patientPseudonym }
- this.diagnoses?.forEach { it.patient = patientPseudonym }
- this.ecogStatus?.forEach { it.patient = patientPseudonym }
- this.familyMemberDiagnoses?.forEach { it.patient = patientPseudonym }
- this.geneticCounsellingRequests?.forEach { it.patient = patientPseudonym }
- this.histologyReevaluationRequests?.forEach { it.patient = patientPseudonym }
- this.histologyReports?.forEach {
- it.patient = patientPseudonym
- it.tumorMorphology?.patient = patientPseudonym
- }
- this.lastGuidelineTherapies?.forEach { it.patient = patientPseudonym }
- this.molecularPathologyFindings?.forEach { it.patient = patientPseudonym }
- this.molecularTherapies?.forEach { molecularTherapy ->
- molecularTherapy.history.forEach {
- it.patient = patientPseudonym
- }
- }
- this.ngsReports?.forEach { it.patient = patientPseudonym }
- this.previousGuidelineTherapies?.forEach { it.patient = patientPseudonym }
- this.rebiopsyRequests?.forEach { it.patient = patientPseudonym }
- this.recommendations?.forEach { it.patient = patientPseudonym }
- this.responses?.forEach { it.patient = patientPseudonym }
- this.studyInclusionRequests?.forEach { it.patient = patientPseudonym }
- this.specimens?.forEach { it.patient = patientPseudonym }
-}
-
-/**
- * Creates new hash of content IDs with given prefix except for patient IDs
- *
- * @param pseudonymizeService The pseudonymizeService to be used
- * @return The MTB file containing rehashed content IDs
- */
-infix fun MtbFile.anonymizeContentWith(pseudonymizeService: PseudonymizeService) {
- val prefix = pseudonymizeService.prefix()
-
- fun anonymize(id: String): String {
- val hash = DigestUtils.sha256Hex("$prefix-$id").substring(0, 41).lowercase()
- return "$prefix$hash"
- }
-
- this.episode?.apply {
- id = id?.let {
- anonymize(it)
- }
- }
- this.carePlans?.onEach { carePlan ->
- carePlan?.apply {
- id = id?.let { anonymize(it) }
- diagnosis = diagnosis?.let { anonymize(it) }
- geneticCounsellingRequest = geneticCounsellingRequest?.let { anonymize(it) }
- rebiopsyRequests = rebiopsyRequests.map { it?.let { anonymize(it) } }
- recommendations = recommendations.map { it?.let { anonymize(it) } }
- studyInclusionRequests = studyInclusionRequests.map { it?.let { anonymize(it) } }
- }
- }
- this.claims?.onEach { claim ->
- claim?.apply {
- id = id?.let { anonymize(it) }
- therapy = therapy?.let { anonymize(it) }
- }
- }
- this.claimResponses?.onEach { claimResponse ->
- claimResponse?.apply {
- id = id?.let { anonymize(it) }
- claim = claim?.let { anonymize(it) }
- }
- }
- this.consent?.apply {
- id = id?.let { anonymize(it) }
- }
- this.diagnoses?.onEach { diagnosis ->
- diagnosis?.apply {
- id = id?.let { anonymize(it) }
- histologyResults = histologyResults?.map { it?.let { anonymize(it) } }
- }
- }
- this.ecogStatus?.onEach { ecogStatus ->
- ecogStatus?.apply {
- id = id?.let { anonymize(it) }
- }
- }
- this.familyMemberDiagnoses?.onEach { familyMemberDiagnosis ->
- familyMemberDiagnosis?.apply {
- id = id?.let { anonymize(it) }
- }
- }
- this.geneticCounsellingRequests?.onEach { geneticCounsellingRequest ->
- geneticCounsellingRequest?.apply {
- id = id?.let { anonymize(it) }
- }
- }
- this.histologyReevaluationRequests?.onEach { histologyReevaluationRequest ->
- histologyReevaluationRequest?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- }
- }
- this.histologyReports?.onEach { histologyReport ->
- histologyReport?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- tumorMorphology?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- }
- tumorCellContent?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- }
- }
- }
- this.lastGuidelineTherapies?.onEach { lastGuidelineTherapy ->
- lastGuidelineTherapy?.apply {
- id = id?.let { anonymize(it) }
- diagnosis = diagnosis?.let { anonymize(it) }
- }
- }
- this.molecularPathologyFindings?.onEach { molecularPathologyFinding ->
- molecularPathologyFinding?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- }
- }
- this.molecularTherapies?.onEach { molecularTherapy ->
- molecularTherapy?.apply {
- history?.onEach { history ->
- history?.apply {
- id = id?.let { anonymize(it) }
- basedOn = basedOn?.let { anonymize(it) }
- }
- }
- }
- }
- this.ngsReports?.onEach { ngsReport ->
- ngsReport?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- tumorCellContent?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- }
- simpleVariants?.onEach { simpleVariant ->
- simpleVariant?.apply {
- id = id?.let { anonymize(it) }
- }
- }
- }
- }
- this.previousGuidelineTherapies?.onEach { previousGuidelineTherapy ->
- previousGuidelineTherapy?.apply {
- id = id?.let { anonymize(it) }
- diagnosis = diagnosis?.let { anonymize(it) }
- medication.forEach { medication ->
- medication?.apply {
- id = id?.let { anonymize(it) }
- }
- }
- }
- }
- this.rebiopsyRequests?.onEach { rebiopsyRequest ->
- rebiopsyRequest?.apply {
- id = id?.let { anonymize(it) }
- specimen = specimen?.let { anonymize(it) }
- }
- }
- this.recommendations?.onEach { recommendation ->
- recommendation?.apply {
- id = id?.let { anonymize(it) }
- diagnosis = diagnosis?.let { anonymize(it) }
- ngsReport = ngsReport?.let { anonymize(it) }
- }
- }
- this.responses?.onEach { response ->
- response?.apply {
- id = id?.let { anonymize(it) }
- therapy = therapy?.let { anonymize(it) }
- }
- }
- this.studyInclusionRequests?.onEach { studyInclusionRequest ->
- studyInclusionRequest?.apply {
- id = id?.let { anonymize(it) }
- reason = reason?.let { anonymize(it) }
- }
- }
- this.specimens?.onEach { specimen ->
- specimen?.apply {
- id = id?.let { anonymize(it) }
- }
- }
-}
-
-/** Replaces patient ID with generated patient pseudonym
- *
* @since 0.11.0
*
* @param pseudonymizeService The pseudonymizeService to be used
@@ -353,4 +148,4 @@ fun Mtb.ensureMetaDataIsInitialized() {
infix fun Mtb.addGenomDeTan(pseudonymizeService: PseudonymizeService)
{
this.metadata.transferTan = pseudonymizeService.genomDeTan(PatientId(this.patient.id))
-} \ 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 f2e8390..07d8a8d 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
@@ -20,7 +20,6 @@
package dev.dnpm.etl.processor.services
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.MtbFile
import dev.dnpm.etl.processor.*
import dev.dnpm.etl.processor.config.AppConfigProperties
import dev.dnpm.etl.processor.consent.TtpConsentStatus
@@ -28,7 +27,10 @@ import dev.dnpm.etl.processor.monitoring.Report
import dev.dnpm.etl.processor.monitoring.Request
import dev.dnpm.etl.processor.monitoring.RequestStatus
import dev.dnpm.etl.processor.monitoring.RequestType
-import dev.dnpm.etl.processor.output.*
+import dev.dnpm.etl.processor.output.DeleteRequest
+import dev.dnpm.etl.processor.output.DnpmV2MtbFileRequest
+import dev.dnpm.etl.processor.output.MtbFileRequest
+import dev.dnpm.etl.processor.output.MtbFileSender
import dev.dnpm.etl.processor.pseudonym.PseudonymizeService
import dev.dnpm.etl.processor.pseudonym.addGenomDeTan
import dev.dnpm.etl.processor.pseudonym.anonymizeContentWith
@@ -42,7 +44,6 @@ import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
-import java.lang.RuntimeException
import java.time.Instant
import java.util.*
@@ -59,17 +60,6 @@ class RequestProcessor(
) {
private var logger: Logger = LoggerFactory.getLogger("RequestProcessor")
- fun processMtbFile(mtbFile: MtbFile) {
- processMtbFile(mtbFile, randomRequestId())
- }
-
- fun processMtbFile(mtbFile: MtbFile, requestId: RequestId) {
- val pid = PatientId(mtbFile.patient.id)
- mtbFile pseudonymizeWith pseudonymizeService
- mtbFile anonymizeContentWith pseudonymizeService
- val request = BwhcV1MtbFileRequest(requestId, transformationService.transform(mtbFile))
- saveAndSend(request, pid)
- }
fun processMtbFile(mtbFile: Mtb) {
processMtbFile(mtbFile, randomRequestId())
@@ -144,7 +134,6 @@ class RequestProcessor(
private fun <T> isDuplication(pseudonymizedMtbFileRequest: MtbFileRequest<T>): Boolean {
val patientPseudonym = when (pseudonymizedMtbFileRequest) {
- is BwhcV1MtbFileRequest -> PatientPseudonym(pseudonymizedMtbFileRequest.content.patient.id)
is DnpmV2MtbFileRequest -> PatientPseudonym(pseudonymizedMtbFileRequest.content.patient.id)
}
@@ -214,7 +203,6 @@ class RequestProcessor(
private fun <T> fingerprint(request: MtbFileRequest<T>): Fingerprint {
return when (request) {
- is BwhcV1MtbFileRequest -> fingerprint(objectMapper.writeValueAsString(request.content))
is DnpmV2MtbFileRequest -> fingerprint(objectMapper.writeValueAsString(request.content))
}
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt
index 9447a84..8f1081e 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/TransformationService.kt
@@ -22,14 +22,9 @@ package dev.dnpm.etl.processor.services
import com.fasterxml.jackson.databind.ObjectMapper
import com.jayway.jsonpath.JsonPath
import com.jayway.jsonpath.PathNotFoundException
-import de.ukw.ccc.bwhc.dto.MtbFile
import dev.pcvolkmer.mv64e.mtb.Mtb
class TransformationService(private val objectMapper: ObjectMapper, private val transformations: List<Transformation>) {
- fun transform(mtbFile: MtbFile): MtbFile {
- val json = transform(objectMapper.writeValueAsString(mtbFile))
- return objectMapper.readValue(json, MtbFile::class.java)
- }
fun transform(mtbFile: Mtb): Mtb {
val json = transform(objectMapper.writeValueAsString(mtbFile))
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 895f026..ed57ec8 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -20,7 +20,7 @@ spring:
app:
rest:
- uri: http://localhost:9000/bwhc/etl/api
+ uri: http://localhost/api
#kafka:
# topic: test
# response-topic: test_response
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
index fbcfb3f..1239cdf 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
@@ -20,12 +20,10 @@
package dev.dnpm.etl.processor.input
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.Consent
-import de.ukw.ccc.bwhc.dto.MtbFile
-import de.ukw.ccc.bwhc.dto.Patient
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.services.RequestProcessor
+import dev.pcvolkmer.mv64e.mtb.*
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.header.internals.RecordHeader
import org.apache.kafka.common.header.internals.RecordHeaders
@@ -57,9 +55,20 @@ class KafkaInputListenerTest {
@Test
fun shouldProcessMtbFileRequest() {
- val mtbFile = MtbFile.builder()
- .withPatient(Patient.builder().withId("DUMMY_12345678").build())
- .withConsent(Consent.builder().withStatus(Consent.Status.ACTIVE).build())
+ val mtbFile = Mtb.builder()
+ .patient(Patient.builder().id("DUMMY_12345678").build())
+ .metadata(
+ MvhMetadata
+ .builder()
+ .modelProjectConsent(
+ ModelProjectConsent
+ .builder()
+ .provisions(
+ listOf(Provision.builder().type(ConsentProvision.PERMIT).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
+ ).build()
+ )
+ .build()
+ )
.build()
kafkaInputListener.onMessage(
@@ -72,14 +81,25 @@ class KafkaInputListenerTest {
)
)
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>())
}
@Test
fun shouldProcessDeleteRequest() {
- val mtbFile = MtbFile.builder()
- .withPatient(Patient.builder().withId("DUMMY_12345678").build())
- .withConsent(Consent.builder().withStatus(Consent.Status.REJECTED).build())
+ val mtbFile = Mtb.builder()
+ .patient(Patient.builder().id("DUMMY_12345678").build())
+ .metadata(
+ MvhMetadata
+ .builder()
+ .modelProjectConsent(
+ ModelProjectConsent
+ .builder()
+ .provisions(
+ listOf(Provision.builder().type(ConsentProvision.DENY).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
+ ).build()
+ )
+ .build()
+ )
.build()
kafkaInputListener.onMessage(
@@ -100,9 +120,20 @@ class KafkaInputListenerTest {
@Test
fun shouldProcessMtbFileRequestWithExistingRequestId() {
- val mtbFile = MtbFile.builder()
- .withPatient(Patient.builder().withId("DUMMY_12345678").build())
- .withConsent(Consent.builder().withStatus(Consent.Status.ACTIVE).build())
+ val mtbFile = Mtb.builder()
+ .patient(Patient.builder().id("DUMMY_12345678").build())
+ .metadata(
+ MvhMetadata
+ .builder()
+ .modelProjectConsent(
+ ModelProjectConsent
+ .builder()
+ .provisions(
+ listOf(Provision.builder().type(ConsentProvision.PERMIT).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
+ ).build()
+ )
+ .build()
+ )
.build()
val headers = RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray())))
@@ -122,14 +153,25 @@ class KafkaInputListenerTest {
)
)
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>(), anyValueClass())
+ verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass())
}
@Test
fun shouldProcessDeleteRequestWithExistingRequestId() {
- val mtbFile = MtbFile.builder()
- .withPatient(Patient.builder().withId("DUMMY_12345678").build())
- .withConsent(Consent.builder().withStatus(Consent.Status.REJECTED).build())
+ val mtbFile = Mtb.builder()
+ .patient(Patient.builder().id("DUMMY_12345678").build())
+ .metadata(
+ MvhMetadata
+ .builder()
+ .modelProjectConsent(
+ ModelProjectConsent
+ .builder()
+ .provisions(
+ listOf(Provision.builder().type(ConsentProvision.DENY).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
+ ).build()
+ )
+ .build()
+ )
.build()
val headers = RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray())))
@@ -148,15 +190,29 @@ class KafkaInputListenerTest {
Optional.empty()
)
)
- verify(requestProcessor, times(1)).processDeletion(anyValueClass(), anyValueClass(), eq(
- TtpConsentStatus.UNKNOWN_CHECK_FILE))
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(), anyValueClass(), eq(
+ TtpConsentStatus.UNKNOWN_CHECK_FILE
+ )
+ )
}
@Test
fun shouldNotProcessDnpmV2Request() {
- val mtbFile = MtbFile.builder()
- .withPatient(Patient.builder().withId("DUMMY_12345678").build())
- .withConsent(Consent.builder().withStatus(Consent.Status.REJECTED).build())
+ val mtbFile = Mtb.builder()
+ .patient(Patient.builder().id("DUMMY_12345678").build())
+ .metadata(
+ MvhMetadata
+ .builder()
+ .modelProjectConsent(
+ ModelProjectConsent
+ .builder()
+ .provisions(
+ listOf(Provision.builder().type(ConsentProvision.DENY).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
+ ).build()
+ )
+ .build()
+ )
.build()
val headers = RecordHeaders(
@@ -180,8 +236,11 @@ class KafkaInputListenerTest {
Optional.empty()
)
)
- verify(requestProcessor, times(0)).processDeletion(anyValueClass(), anyValueClass(), eq(
- TtpConsentStatus.UNKNOWN_CHECK_FILE))
+ verify(requestProcessor, times(0)).processDeletion(
+ anyValueClass(), anyValueClass(), eq(
+ TtpConsentStatus.UNKNOWN_CHECK_FILE
+ )
+ )
}
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
index 7a91ed1..845f325 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -20,31 +20,21 @@
package dev.dnpm.etl.processor.input
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.*
-import de.ukw.ccc.bwhc.dto.Consent.Status
import dev.dnpm.etl.processor.CustomMediaType
-import dev.dnpm.etl.processor.consent.ConsentByMtbFile
import dev.dnpm.etl.processor.consent.GicsConsentService
-import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.RequestProcessor
import dev.pcvolkmer.mv64e.mtb.Mtb
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
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.ValueSource
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
-import org.mockito.kotlin.anyValueClass
-import org.mockito.kotlin.whenever
import org.springframework.core.io.ClassPathResource
-import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
-import org.springframework.test.web.servlet.delete
import org.springframework.test.web.servlet.post
import org.springframework.test.web.servlet.setup.MockMvcBuilders
@@ -54,219 +44,6 @@ class MtbFileRestControllerTest {
private val objectMapper = ObjectMapper()
@Nested
- inner class BwhcRequests {
-
- private lateinit var mockMvc: MockMvc
-
- private lateinit var requestProcessor: RequestProcessor
-
-
- @BeforeEach
- fun setup(
- @Mock requestProcessor: RequestProcessor
- ) {
- this.requestProcessor = requestProcessor
- val controller = MtbFileRestController(
- requestProcessor,
- ConsentByMtbFile()
- )
- this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
- }
-
- @Test
- fun shouldProcessPostRequest() {
- mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.ACTIVE))
- contentType = MediaType.APPLICATION_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
- }
-
- @Test
- fun shouldProcessPostRequestWithRejectedConsent() {
- mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.REJECTED))
- contentType = MediaType.APPLICATION_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(),
- org.mockito.kotlin.eq(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
- )
- }
-
- @Test
- fun shouldProcessDeleteRequest() {
- mockMvc.delete("/mtbfile/TEST_12345678").andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(),
- org.mockito.kotlin.eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
- )
- }
- }
-
- @Nested
- inner class BwhcRequestsCheckConsentViaTtp {
-
- private lateinit var mockMvc: MockMvc
-
- private lateinit var requestProcessor: RequestProcessor
-
- private lateinit var gicsConsentService: GicsConsentService
-
- @BeforeEach
- fun setup(
- @Mock requestProcessor: RequestProcessor,
- @Mock gicsConsentService: GicsConsentService
- ) {
- this.requestProcessor = requestProcessor
- val controller = MtbFileRestController(requestProcessor, gicsConsentService)
- this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
- this.gicsConsentService = gicsConsentService
- }
-
- @ParameterizedTest
- @ValueSource(strings = ["ACTIVE", "REJECTED"])
- fun shouldProcessPostRequest(status: String) {
-
- whenever(gicsConsentService.getTtpBroadConsentStatus(any())).thenReturn(TtpConsentStatus.BROAD_CONSENT_GIVEN)
-
- mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.valueOf(status)))
- contentType = MediaType.APPLICATION_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
- }
-
-
- @ParameterizedTest
- @ValueSource(strings = ["ACTIVE", "REJECTED"])
- fun shouldProcessPostRequestWithRejectedConsent(status: String) {
-
- whenever(gicsConsentService.getTtpBroadConsentStatus(any())).thenReturn(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
-
- mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.valueOf(status)))
- contentType = MediaType.APPLICATION_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- // consent status from ttp should override file consent value
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(),
- org.mockito.kotlin.eq(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
- )
- }
-
- @Test
- fun shouldProcessDeleteRequest() {
-
- mockMvc.delete("/mtbfile/TEST_12345678").andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(),
- org.mockito.kotlin.eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
- )
- verify(gicsConsentService, times(0)).getTtpBroadConsentStatus(any())
-
- }
- }
-
-
- @Nested
- inner class BwhcRequestsWithAlias {
-
- private lateinit var mockMvc: MockMvc
-
- private lateinit var requestProcessor: RequestProcessor
-
- @BeforeEach
- fun setup(
- @Mock requestProcessor: RequestProcessor
- ) {
- this.requestProcessor = requestProcessor
- val controller = MtbFileRestController(
- requestProcessor,
- ConsentByMtbFile()
- )
- this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
- }
-
- @Test
- fun shouldProcessPostRequest() {
- mockMvc.post("/mtb") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.ACTIVE))
- contentType = MediaType.APPLICATION_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
- }
-
- @Test
- fun shouldProcessPostRequestWithRejectedConsent() {
- mockMvc.post("/mtb") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.REJECTED))
- contentType = MediaType.APPLICATION_JSON
- }.andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(), org.mockito.kotlin.eq(
- TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED
- )
- )
- }
-
- @Test
- fun shouldProcessDeleteRequest() {
- mockMvc.delete("/mtb/TEST_12345678").andExpect {
- status {
- isAccepted()
- }
- }
-
- verify(requestProcessor, times(1)).processDeletion(
- anyValueClass(), org.mockito.kotlin.eq(
- TtpConsentStatus.UNKNOWN_CHECK_FILE
- )
- )
- }
- }
-
- @Nested
inner class RequestsForDnpmDataModel21 {
private lateinit var mockMvc: MockMvc
@@ -304,15 +81,4 @@ class MtbFileRestControllerTest {
}
}
-
- companion object {
- fun bwhcMtbFileContent(consentStatus: Status) = MtbFile.builder().withPatient(
- Patient.builder().withId("TEST_12345678").withBirthDate("2000-08-08").withGender(Patient.Gender.MALE)
- .build()
- ).withConsent(
- Consent.builder().withId("1").withStatus(consentStatus).withPatient("TEST_12345678").build()
- ).withEpisode(
- Episode.builder().withId("1").withPatient("TEST_12345678").withPeriod(PeriodStart("2023-08-08")).build()
- ).build()
- }
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt
index f1185ef..1e9c853 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt
@@ -20,8 +20,6 @@
package dev.dnpm.etl.processor.output
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.*
-import de.ukw.ccc.bwhc.dto.Patient
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.PatientPseudonym
import dev.dnpm.etl.processor.RequestId
@@ -39,7 +37,6 @@ import org.junit.jupiter.params.provider.MethodSource
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.*
-import org.springframework.http.MediaType
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.kafka.support.SendResult
import org.springframework.retry.policy.SimpleRetryPolicy
@@ -76,20 +73,6 @@ class KafkaMtbFileSenderTest {
@ParameterizedTest
@MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
- fun shouldSendMtbFileRequestAndReturnExpectedState(testData: TestData) {
- doAnswer {
- if (null != testData.exception) {
- throw testData.exception
- }
- completedFuture(SendResult<String, String>(null, null))
- }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>())
-
- val response = kafkaMtbFileSender.send(BwhcV1MtbFileRequest(TEST_REQUEST_ID, bwhcV1MtbFile(Consent.Status.ACTIVE)))
- assertThat(response.status).isEqualTo(testData.requestStatus)
- }
-
- @ParameterizedTest
- @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
fun shouldSendDeleteRequestAndReturnExpectedState(testData: TestData) {
doAnswer {
if (null != testData.exception) {
@@ -102,66 +85,6 @@ class KafkaMtbFileSenderTest {
assertThat(response.status).isEqualTo(testData.requestStatus)
}
- @Test
- fun shouldSendMtbFileRequestWithCorrectKeyAndHeaderAndBody() {
- doAnswer {
- completedFuture(SendResult<String, String>(null, null))
- }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>())
-
- kafkaMtbFileSender.send(BwhcV1MtbFileRequest(TEST_REQUEST_ID, bwhcV1MtbFile(Consent.Status.ACTIVE)))
-
- val captor = argumentCaptor<ProducerRecord<String, String>>()
- verify(kafkaTemplate, times(1)).send(captor.capture())
- assertThat(captor.firstValue.key()).isNotNull
- assertThat(captor.firstValue.key()).isEqualTo("{\"pid\": \"PID\"}")
- assertThat(captor.firstValue.headers().headers("contentType")).isNotNull
- assertThat(captor.firstValue.headers().headers("contentType")?.firstOrNull()?.value()).isEqualTo(MediaType.APPLICATION_JSON_VALUE.toByteArray())
- assertThat(captor.firstValue.value()).isNotNull
- assertThat(captor.firstValue.value()).isEqualTo(objectMapper.writeValueAsString(bwhcV1kafkaRecordData(TEST_REQUEST_ID, Consent.Status.ACTIVE)))
- }
-
- @Test
- fun shouldSendDeleteRequestWithCorrectKeyAndBody() {
- doAnswer {
- completedFuture(SendResult<String, String>(null, null))
- }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>())
-
- kafkaMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM))
-
- val captor = argumentCaptor<ProducerRecord<String, String>>()
- verify(kafkaTemplate, times(1)).send(captor.capture())
- assertThat(captor.firstValue.key()).isNotNull
- assertThat(captor.firstValue.key()).isEqualTo("{\"pid\": \"PID\"}")
- assertThat(captor.firstValue.value()).isNotNull
- assertThat(captor.firstValue.value()).isEqualTo(objectMapper.writeValueAsString(bwhcV1kafkaRecordData(TEST_REQUEST_ID, Consent.Status.REJECTED)))
- }
-
- @ParameterizedTest
- @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
- fun shouldRetryOnMtbFileKafkaSendError(testData: TestData) {
- val kafkaProperties = KafkaProperties("testtopic")
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build()
- this.kafkaMtbFileSender = KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.objectMapper)
-
- doAnswer {
- if (null != testData.exception) {
- throw testData.exception
- }
- completedFuture(SendResult<String, String>(null, null))
- }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>())
-
- kafkaMtbFileSender.send(BwhcV1MtbFileRequest(TEST_REQUEST_ID, bwhcV1MtbFile(Consent.Status.ACTIVE)))
-
- val expectedCount = when (testData.exception) {
- // OK - No Retry
- null -> times(1)
- // Request failed - Retry max 3 times
- else -> times(3)
- }
-
- verify(kafkaTemplate, expectedCount).send(any<ProducerRecord<String, String>>())
- }
-
@ParameterizedTest
@MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource")
fun shouldRetryOnDeleteKafkaSendError(testData: TestData) {
@@ -276,41 +199,6 @@ class KafkaMtbFileSenderTest {
val TEST_REQUEST_ID = RequestId("TestId")
val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
- fun bwhcV1MtbFile(consentStatus: Consent.Status): MtbFile {
- return if (consentStatus == Consent.Status.ACTIVE) {
- MtbFile.builder()
- .withPatient(
- Patient.builder()
- .withId("PID")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
- .build()
- )
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(consentStatus)
- .withPatient("PID")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("PID")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
- )
- } else {
- MtbFile.builder()
- .withConsent(
- Consent.builder()
- .withStatus(consentStatus)
- .withPatient("PID")
- .build()
- )
- }.build()
- }
-
fun dnpmV2MtbFile(): Mtb {
return Mtb().apply {
this.patient = dev.pcvolkmer.mv64e.mtb.Patient().apply {
@@ -334,10 +222,6 @@ class KafkaMtbFileSenderTest {
}
}
- fun bwhcV1kafkaRecordData(requestId: RequestId, consentStatus: Consent.Status): MtbRequest {
- return BwhcV1MtbFileRequest(requestId, bwhcV1MtbFile(consentStatus))
- }
-
fun dnmpV2kafkaRecordData(requestId: RequestId): MtbRequest {
return DnpmV2MtbFileRequest(requestId, dnpmV2MtbFile())
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSenderTest.kt
deleted file mode 100644
index ead2496..0000000
--- a/src/test/kotlin/dev/dnpm/etl/processor/output/RestBwhcMtbFileSenderTest.kt
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * This file is part of ETL-Processor
- *
- * Copyright (c) 2025 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package dev.dnpm.etl.processor.output
-
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import de.ukw.ccc.bwhc.dto.*
-import dev.dnpm.etl.processor.PatientPseudonym
-import dev.dnpm.etl.processor.RequestId
-import dev.dnpm.etl.processor.config.RestTargetProperties
-import dev.dnpm.etl.processor.monitoring.ReportService
-import dev.dnpm.etl.processor.monitoring.RequestStatus
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.params.ParameterizedTest
-import org.junit.jupiter.params.provider.MethodSource
-import org.springframework.http.HttpHeaders
-import org.springframework.http.HttpMethod
-import org.springframework.http.HttpStatus
-import org.springframework.http.MediaType
-import org.springframework.retry.policy.SimpleRetryPolicy
-import org.springframework.retry.support.RetryTemplateBuilder
-import org.springframework.test.web.client.ExpectedCount
-import org.springframework.test.web.client.MockRestServiceServer
-import org.springframework.test.web.client.match.MockRestRequestMatchers.*
-import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus
-import org.springframework.web.client.RestTemplate
-
-class RestBwhcMtbFileSenderTest {
-
- private lateinit var mockRestServiceServer: MockRestServiceServer
-
- private lateinit var restMtbFileSender: RestMtbFileSender
-
- private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
-
- @BeforeEach
- fun setup() {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/mtbfile", null, null)
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
-
- this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
-
- this.restMtbFileSender =
- RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
- }
-
- @ParameterizedTest
- @MethodSource("deleteRequestWithResponseSource")
- fun shouldReturnExpectedResponseForDelete(requestWithResponse: RequestWithResponse) {
- this.mockRestServiceServer
- .expect(method(HttpMethod.DELETE))
- .andExpect(requestTo("http://localhost:9000/mtbfile/Patient/${TEST_PATIENT_PSEUDONYM.value}"))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
-
- val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM))
- assertThat(response.status).isEqualTo(requestWithResponse.response.status)
- assertThat(response.body).isEqualTo(requestWithResponse.response.body)
- }
-
- @ParameterizedTest
- @MethodSource("mtbFileRequestWithResponseSource")
- fun shouldReturnExpectedResponseForMtbFilePost(requestWithResponse: RequestWithResponse) {
- this.mockRestServiceServer
- .expect(method(HttpMethod.POST))
- .andExpect(requestTo("http://localhost:9000/mtbfile/MTBFile"))
- .andExpect(header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
-
- val response = restMtbFileSender.send(BwhcV1MtbFileRequest(TEST_REQUEST_ID, mtbFile))
- assertThat(response.status).isEqualTo(requestWithResponse.response.status)
- assertThat(response.body).isEqualTo(requestWithResponse.response.body)
- }
-
- @ParameterizedTest
- @MethodSource("mtbFileRequestWithResponseSource")
- fun shouldRetryOnMtbFileHttpRequestError(requestWithResponse: RequestWithResponse) {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/mtbfile", null, null)
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build()
-
- this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
- this.restMtbFileSender =
- RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
-
- val expectedCount = when (requestWithResponse.httpStatus) {
- // OK - No Retry
- HttpStatus.OK, HttpStatus.CREATED -> ExpectedCount.max(1)
- // Request failed - Retry max 3 times
- else -> ExpectedCount.max(3)
- }
-
- this.mockRestServiceServer
- .expect(expectedCount, method(HttpMethod.POST))
- .andExpect(requestTo("http://localhost:9000/mtbfile/MTBFile"))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
-
- val response = restMtbFileSender.send(BwhcV1MtbFileRequest(TEST_REQUEST_ID, mtbFile))
- assertThat(response.status).isEqualTo(requestWithResponse.response.status)
- assertThat(response.body).isEqualTo(requestWithResponse.response.body)
- }
-
- @ParameterizedTest
- @MethodSource("deleteRequestWithResponseSource")
- fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/mtbfile", null, null)
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build()
-
- this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
- this.restMtbFileSender =
- RestBwhcMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
-
- val expectedCount = when (requestWithResponse.httpStatus) {
- // OK - No Retry
- HttpStatus.OK, HttpStatus.CREATED -> ExpectedCount.max(1)
- // Request failed - Retry max 3 times
- else -> ExpectedCount.max(3)
- }
-
- this.mockRestServiceServer
- .expect(expectedCount, method(HttpMethod.DELETE))
- .andExpect(requestTo("http://localhost:9000/mtbfile/Patient/${TEST_PATIENT_PSEUDONYM.value}"))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
-
- val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM))
- assertThat(response.status).isEqualTo(requestWithResponse.response.status)
- assertThat(response.body).isEqualTo(requestWithResponse.response.body)
- }
-
- companion object {
- data class RequestWithResponse(
- val httpStatus: HttpStatus,
- val body: String,
- val response: MtbFileSender.Response
- )
-
- val TEST_REQUEST_ID = RequestId("TestId")
- val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
-
- val mtbFile: MtbFile = MtbFile.builder()
- .withPatient(
- Patient.builder()
- .withId("PID")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
- .build()
- )
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("PID")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("PID")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
- )
- .build()
-
- private const val ERROR_RESPONSE_BODY = "Sonstiger Fehler bei der Übertragung"
-
- /**
- * Synthetic http responses with related request status
- * Also see: https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API
- */
- @JvmStatic
- fun mtbFileRequestWithResponseSource(): Set<RequestWithResponse> {
- return setOf(
- RequestWithResponse(
- HttpStatus.OK,
- responseBodyWithMaxSeverity(ReportService.Severity.INFO),
- MtbFileSender.Response(
- RequestStatus.SUCCESS,
- responseBodyWithMaxSeverity(ReportService.Severity.INFO)
- )
- ),
- RequestWithResponse(
- HttpStatus.CREATED,
- responseBodyWithMaxSeverity(ReportService.Severity.WARNING),
- MtbFileSender.Response(
- RequestStatus.WARNING,
- responseBodyWithMaxSeverity(ReportService.Severity.WARNING)
- )
- ),
- RequestWithResponse(
- HttpStatus.BAD_REQUEST,
- responseBodyWithMaxSeverity(ReportService.Severity.ERROR),
- MtbFileSender.Response(RequestStatus.ERROR, responseBodyWithMaxSeverity(ReportService.Severity.ERROR))
- ),
- RequestWithResponse(
- HttpStatus.UNPROCESSABLE_ENTITY,
- responseBodyWithMaxSeverity(ReportService.Severity.FATAL),
- MtbFileSender.Response(
- RequestStatus.ERROR,
- responseBodyWithMaxSeverity(ReportService.Severity.FATAL)
- )
- ),
- // Some more errors not mentioned in documentation
- RequestWithResponse(
- HttpStatus.NOT_FOUND,
- ERROR_RESPONSE_BODY,
- MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY)
- ),
- RequestWithResponse(
- HttpStatus.INTERNAL_SERVER_ERROR,
- ERROR_RESPONSE_BODY,
- MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY)
- )
- )
- }
-
- /**
- * Synthetic http responses with related request status
- * Also see: https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API
- */
- @JvmStatic
- fun deleteRequestWithResponseSource(): Set<RequestWithResponse> {
- return setOf(
- RequestWithResponse(HttpStatus.OK, "", MtbFileSender.Response(RequestStatus.SUCCESS)),
- // Some more errors not mentioned in documentation
- RequestWithResponse(
- HttpStatus.NOT_FOUND,
- "what????",
- MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY)
- ),
- RequestWithResponse(
- HttpStatus.INTERNAL_SERVER_ERROR,
- "what????",
- MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY)
- )
- )
- }
-
- fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String {
- return when (severity) {
- ReportService.Severity.INFO -> """
- {
- "patient": "PID",
- "issues": [
- { "severity": "info", "message": "Info Message" }
- ]
- }
- """
-
- ReportService.Severity.WARNING -> """
- {
- "patient": "PID",
- "issues": [
- { "severity": "info", "message": "Info Message" },
- { "severity": "warning", "message": "Warning Message" }
- ]
- }
- """
-
- ReportService.Severity.ERROR -> """
- {
- "patient": "PID",
- "issues": [
- { "severity": "info", "message": "Info Message" },
- { "severity": "warning", "message": "Warning Message" },
- { "severity": "error", "message": "Error Message" }
- ]
- }
- """
-
- ReportService.Severity.FATAL -> """
- {
- "patient": "PID",
- "issues": [
- { "severity": "info", "message": "Info Message" },
- { "severity": "warning", "message": "Warning Message" },
- { "severity": "error", "message": "Error Message" },
- { "severity": "fatal", "message": "Fatal Message" }
- ]
- }
- """
- }
- }
- }
-
-
-}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt
index 8395518..1b27a62 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt
@@ -21,8 +21,6 @@ package dev.dnpm.etl.processor.output
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
-import de.ukw.ccc.bwhc.dto.*
-import de.ukw.ccc.bwhc.dto.Patient
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.PatientPseudonym
import dev.dnpm.etl.processor.RequestId
@@ -55,78 +53,6 @@ import java.util.*
class RestDipMtbFileSenderTest {
@Nested
- inner class BwhcV1ContentRequest {
-
- private lateinit var mockRestServiceServer: MockRestServiceServer
-
- private lateinit var restMtbFileSender: RestMtbFileSender
-
- private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build()))
-
- @BeforeEach
- fun setup() {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null, false)
- val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
-
- this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
-
- this.restMtbFileSender =
- RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
- }
-
- @ParameterizedTest
- @MethodSource("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#mtbFileRequestWithResponseSource")
- fun shouldReturnExpectedResponseForMtbFilePost(requestWithResponse: RequestWithResponse) {
- this.mockRestServiceServer
- .expect(method(HttpMethod.POST))
- .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record"))
- .andExpect(header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
-
- val response = restMtbFileSender.send(BwhcV1MtbFileRequest(TEST_REQUEST_ID, bwhcV1mtbFile))
- assertThat(response.status).isEqualTo(requestWithResponse.response.status)
- assertThat(response.body).isEqualTo(requestWithResponse.response.body)
- }
-
- @ParameterizedTest
- @MethodSource("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#mtbFileRequestWithResponseSource")
- fun shouldRetryOnMtbFileHttpRequestError(requestWithResponse: RequestWithResponse) {
- val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null, false)
- val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties("http://localhost:9000"))
- retryTemplate.setBackOffPolicy(NoBackOffPolicy())
-
- this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
- this.restMtbFileSender =
- RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService)
-
- val expectedCount = when (requestWithResponse.httpStatus) {
- // OK - No Retry
- HttpStatus.OK, HttpStatus.CREATED, HttpStatus.UNPROCESSABLE_ENTITY, HttpStatus.BAD_REQUEST -> ExpectedCount.max(
- 1
- )
- // Request failed - Retry max 3 times
- else -> ExpectedCount.max(3)
- }
-
- this.mockRestServiceServer
- .expect(expectedCount, method(HttpMethod.POST))
- .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record"))
- .andRespond {
- withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it)
- }
-
- val response = restMtbFileSender.send(BwhcV1MtbFileRequest(TEST_REQUEST_ID, bwhcV1mtbFile))
- assertThat(response.status).isEqualTo(requestWithResponse.response.status)
- assertThat(response.body).isEqualTo(requestWithResponse.response.body)
- }
-
- }
-
- @Nested
inner class DnpmV2ContentRequest {
private lateinit var mockRestServiceServer: MockRestServiceServer
@@ -138,7 +64,7 @@ class RestDipMtbFileSenderTest {
@BeforeEach
fun setup() {
val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null, false)
+ val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
@@ -176,7 +102,7 @@ class RestDipMtbFileSenderTest {
@BeforeEach
fun setup() {
val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null, false)
+ val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build()
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
@@ -204,8 +130,8 @@ class RestDipMtbFileSenderTest {
@MethodSource("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource")
fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) {
val restTemplate = RestTemplate()
- val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null, false)
- val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties("http://localhost:9000"))
+ val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null)
+ val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties())
retryTemplate.setBackOffPolicy(NoBackOffPolicy())
this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate)
@@ -245,30 +171,6 @@ class RestDipMtbFileSenderTest {
val TEST_REQUEST_ID = RequestId("TestId")
val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID")
- val bwhcV1mtbFile: MtbFile = MtbFile.builder()
- .withPatient(
- Patient.builder()
- .withId("PID")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
- .build()
- )
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("PID")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("PID")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
- )
- .build()
-
fun dnpmV2MtbFile(): Mtb {
return Mtb().apply {
this.patient = dev.pcvolkmer.mv64e.mtb.Patient().apply {
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 b6baec9..5955263 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
@@ -21,8 +21,6 @@ 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.AppConfigProperties
import dev.dnpm.etl.processor.config.GIcsConfigProperties
import dev.dnpm.etl.processor.config.JacksonConfig
@@ -52,172 +50,6 @@ class ExtensionsTest {
}
@Nested
- inner class UsingBwhcDatamodel {
-
- val FAKE_MTB_FILE_PATH = "fake_MTBFile.json"
- val CLEAN_PATIENT_ID = "5dad2f0b-49c6-47d8-a952-7b9e9e0f7549"
-
-
- private fun fakeMtbFile(): MtbFile {
- val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream
- return getObjectMapper().readValue(mtbFile, MtbFile::class.java)
- }
-
- private fun MtbFile.serialized(): String {
- return getObjectMapper().writeValueAsString(this)
- }
-
- @Test
- fun shouldNotContainCleanPatientId(@Mock pseudonymizeService: PseudonymizeService) {
- doAnswer {
- it.arguments[0]
- "PSEUDO-ID"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- val mtbFile = fakeMtbFile()
-
- mtbFile.pseudonymizeWith(pseudonymizeService)
-
- assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID")
- assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID)
- }
-
- @Test
- fun shouldNotContainAnyUuidAfterRehashingOfIds(@Mock pseudonymizeService: PseudonymizeService) {
- doAnswer {
- it.arguments[0]
- "PSEUDO-ID"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- "TESTDOMAIN"
- }.whenever(pseudonymizeService).prefix()
-
- val mtbFile = fakeMtbFile()
-
- mtbFile.pseudonymizeWith(pseudonymizeService)
- mtbFile.anonymizeContentWith(pseudonymizeService)
-
- val pattern =
- "\"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\"".toRegex()
- .toPattern()
- val matcher = pattern.matcher(mtbFile.serialized())
-
- assertThrows<IllegalStateException> {
- matcher.find()
- matcher.group()
- }.also {
- assertThat(it.message).isEqualTo("No match found")
- }
-
- }
-
- @Test
- fun shouldRehashIdsWithPrefix(@Mock pseudonymizeService: PseudonymizeService) {
- doAnswer {
- it.arguments[0]
- "PSEUDO-ID"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- "TESTDOMAIN"
- }.whenever(pseudonymizeService).prefix()
-
- val mtbFile = MtbFile.builder()
- .withPatient(
- Patient.builder()
- .withId("1")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
- .build()
- )
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("1")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
- )
- .build()
-
- mtbFile.pseudonymizeWith(pseudonymizeService)
- mtbFile.anonymizeContentWith(pseudonymizeService)
-
-
- assertThat(mtbFile.episode.id)
- // TESTDOMAIN<sha256(TESTDOMAIN-1)[0-41]>
- .isEqualTo("TESTDOMAIN44e20a53bbbf9f3ae39626d05df7014dcd77d6098")
- }
-
- @Test
- fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) {
- doAnswer {
- it.arguments[0]
- "PSEUDO-ID"
- }.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
-
- doAnswer {
- "TESTDOMAIN"
- }.whenever(pseudonymizeService).prefix()
-
- val mtbFile = MtbFile.builder()
- .withPatient(
- Patient.builder()
- .withId("1")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
- .build()
- )
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("1")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
- )
- .withClaims(null)
- .withDiagnoses(null)
- .withCarePlans(null)
- .withClaimResponses(null)
- .withEcogStatus(null)
- .withFamilyMemberDiagnoses(null)
- .withGeneticCounsellingRequests(null)
- .withHistologyReevaluationRequests(null)
- .withHistologyReports(null)
- .withLastGuidelineTherapies(null)
- .withMolecularPathologyFindings(null)
- .withMolecularTherapies(null)
- .withNgsReports(null)
- .withPreviousGuidelineTherapies(null)
- .withRebiopsyRequests(null)
- .withRecommendations(null)
- .withResponses(null)
- .withStudyInclusionRequests(null)
- .withSpecimens(null)
- .build()
-
- mtbFile.pseudonymizeWith(pseudonymizeService)
- mtbFile.anonymizeContentWith(pseudonymizeService)
-
- assertThat(mtbFile.episode.id).isNotNull()
- }
- }
-
- @Nested
inner class UsingDnpmV2Datamodel {
val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json"
@@ -251,7 +83,7 @@ class ExtensionsTest {
private fun addConsentData(mtbFile: Mtb) {
val gIcsConfigProperties = GIcsConfigProperties("", "", "")
- val appConfigProperties = AppConfigProperties(null, emptyList())
+ val appConfigProperties = AppConfigProperties(emptyList())
val bundle = Bundle()
val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent()
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt
index 819454f..8ee19bc 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt
@@ -19,8 +19,8 @@
package dev.dnpm.etl.processor.pseudonym
-import de.ukw.ccc.bwhc.dto.*
import dev.dnpm.etl.processor.config.PseudonymizeConfigProperties
+import dev.pcvolkmer.mv64e.mtb.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -29,31 +29,26 @@ import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.whenever
+import java.time.Instant
+import java.util.*
@ExtendWith(MockitoExtension::class)
class PseudonymizeServiceTest {
- private val mtbFile = MtbFile.builder()
- .withPatient(
+ private val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("123")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
+ .id("123")
.build()
)
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("123")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("123").build())
+ .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build())
+ .build()
+ )
)
.build()
@@ -102,4 +97,4 @@ class PseudonymizeServiceTest {
assertThat(tans.add(tan)).`as`("never the same result!").isTrue
}
}
-} \ No newline at end of file
+}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt
index 38ce0b3..af93f7b 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt
@@ -51,7 +51,7 @@ class ConsentProcessorTest {
this.objectMapper = jacksonConfig.objectMapper()
this.fhirContext = JacksonConfig.fhirContext()
this.gicsConsentService = gicsConsentService
- this.appConfigProperties = AppConfigProperties(null, emptyList())
+ this.appConfigProperties = AppConfigProperties(emptyList())
this.consentProcessor =
ConsentProcessor(
appConfigProperties,
@@ -168,4 +168,4 @@ class ConsentProcessorTest {
.parseResource<Bundle>(Bundle::class.java, bundle)
}
-} \ No newline at end of file
+}
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 b36c696..0a42b9b 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
@@ -20,22 +20,21 @@
package dev.dnpm.etl.processor.services
import com.fasterxml.jackson.databind.ObjectMapper
-import de.ukw.ccc.bwhc.dto.*
import dev.dnpm.etl.processor.Fingerprint
import dev.dnpm.etl.processor.PatientId
import dev.dnpm.etl.processor.PatientPseudonym
import dev.dnpm.etl.processor.config.AppConfigProperties
-import dev.dnpm.etl.processor.consent.GicsConsentService
import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.monitoring.Request
import dev.dnpm.etl.processor.monitoring.RequestStatus
import dev.dnpm.etl.processor.monitoring.RequestType
-import dev.dnpm.etl.processor.output.BwhcV1MtbFileRequest
import dev.dnpm.etl.processor.output.DeleteRequest
+import dev.dnpm.etl.processor.output.DnpmV2MtbFileRequest
import dev.dnpm.etl.processor.output.MtbFileSender
import dev.dnpm.etl.processor.output.RestMtbFileSender
import dev.dnpm.etl.processor.pseudonym.PseudonymizeService
import dev.dnpm.etl.processor.randomRequestId
+import dev.pcvolkmer.mv64e.mtb.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@@ -49,6 +48,7 @@ import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.whenever
import org.springframework.context.ApplicationEventPublisher
import java.time.Instant
+import java.util.*
@ExtendWith(MockitoExtension::class)
@@ -77,7 +77,7 @@ class RequestProcessorTest {
this.sender = sender
this.requestService = requestService
this.applicationEventPublisher = applicationEventPublisher
- this.appConfigProperties = AppConfigProperties(null)
+ this.appConfigProperties = AppConfigProperties()
this.consentProcessor = consentProcessor
val objectMapper = ObjectMapper()
@@ -102,7 +102,7 @@ class RequestProcessorTest {
randomRequestId(),
PatientPseudonym("TEST_12345678901"),
PatientId("P1"),
- Fingerprint("zdlzv5s5ydmd4ktw2v5piohegc4jcyrm6j66bq6tv2uxuerndmga"),
+ Fingerprint("6vkiti5bk6ikwifpajpt7cygmd3dvw54d6lwfhzlynb3pqtzferq"),
RequestType.MTB_FILE,
RequestStatus.SUCCESS,
Instant.parse("2023-08-08T02:00:00Z")
@@ -119,29 +119,24 @@ class RequestProcessorTest {
doAnswer {
it.arguments[0]
- }.whenever(transformationService).transform(any<MtbFile>())
+ }.whenever(transformationService).transform(any<Mtb>())
- val mtbFile = MtbFile.builder()
- .withPatient(
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("1")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
+ .id("123")
.build()
)
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("1")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("123").build())
+ .period(PeriodDate.builder().start(Date.from(Instant.parse("2023-08-08T02:00:00.00Z"))).build())
+ .build()
+ )
)
.build()
@@ -161,7 +156,7 @@ class RequestProcessorTest {
randomRequestId(),
PatientPseudonym("TEST_12345678901"),
PatientId("P1"),
- Fingerprint("zdlzv5s5ydmd4ktw2v5piohegc4jcyrm6j66bq6tv2uxuerndmga"),
+ Fingerprint("4gcjwtjjtcczybsljxepdfpkaeusvd7g3vogfqpmphyffyzfx7dq"),
RequestType.MTB_FILE,
RequestStatus.SUCCESS,
Instant.parse("2023-08-08T02:00:00Z")
@@ -178,29 +173,24 @@ class RequestProcessorTest {
doAnswer {
it.arguments[0]
- }.whenever(transformationService).transform(any<MtbFile>())
+ }.whenever(transformationService).transform(any<Mtb>())
+
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
- val mtbFile = MtbFile.builder()
- .withPatient(
+ val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("1")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
+ .id("123")
.build()
)
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("1")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("123").build())
+ .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build())
+ .build()
+ )
)
.build()
@@ -233,7 +223,7 @@ class RequestProcessorTest {
doAnswer {
MtbFileSender.Response(status = RequestStatus.SUCCESS)
- }.whenever(sender).send(any<BwhcV1MtbFileRequest>())
+ }.whenever(sender).send(any<DnpmV2MtbFileRequest>())
doAnswer {
it.arguments[0] as String
@@ -241,29 +231,24 @@ class RequestProcessorTest {
doAnswer {
it.arguments[0]
- }.whenever(transformationService).transform(any<MtbFile>())
+ }.whenever(transformationService).transform(any<Mtb>())
+
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
- val mtbFile = MtbFile.builder()
- .withPatient(
+ val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("1")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
+ .id("123")
.build()
)
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
- .build()
- )
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("1")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("123").build())
+ .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build())
+ .build()
+ )
)
.build()
@@ -296,7 +281,7 @@ class RequestProcessorTest {
doAnswer {
MtbFileSender.Response(status = RequestStatus.ERROR)
- }.whenever(sender).send(any<BwhcV1MtbFileRequest>())
+ }.whenever(sender).send(any<DnpmV2MtbFileRequest>())
doAnswer {
it.arguments[0] as String
@@ -304,29 +289,36 @@ class RequestProcessorTest {
doAnswer {
it.arguments[0]
- }.whenever(transformationService).transform(any<MtbFile>())
+ }.whenever(transformationService).transform(any<Mtb>())
- val mtbFile = MtbFile.builder()
- .withPatient(
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("1")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
+ .id("123")
.build()
)
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
+ .metadata(
+ MvhMetadata
+ .builder()
+ .modelProjectConsent(
+ ModelProjectConsent
+ .builder()
+ .provisions(
+ listOf(Provision.builder().type(ConsentProvision.PERMIT).purpose(ModelProjectConsentPurpose.SEQUENCING).build())
+ ).build()
+ )
.build()
)
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("1")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("123").build())
+ .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build())
+ .build()
+ )
)
.build()
@@ -426,33 +418,28 @@ class RequestProcessorTest {
doAnswer {
it.arguments[0]
- }.whenever(transformationService).transform(any<MtbFile>())
+ }.whenever(transformationService).transform(any<Mtb>())
doAnswer {
MtbFileSender.Response(status = RequestStatus.SUCCESS)
- }.whenever(sender).send(any<BwhcV1MtbFileRequest>())
+ }.whenever(sender).send(any<DnpmV2MtbFileRequest>())
- val mtbFile = MtbFile.builder()
- .withPatient(
+ whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true)
+
+ val mtbFile = Mtb.builder()
+ .patient(
Patient.builder()
- .withId("1")
- .withBirthDate("2000-08-08")
- .withGender(Patient.Gender.MALE)
- .build()
- )
- .withConsent(
- Consent.builder()
- .withId("1")
- .withStatus(Consent.Status.ACTIVE)
- .withPatient("123")
+ .id("123")
.build()
)
- .withEpisode(
- Episode.builder()
- .withId("1")
- .withPatient("1")
- .withPeriod(PeriodStart("2023-08-08"))
- .build()
+ .episodesOfCare(
+ listOf(
+ MtbEpisodeOfCare.builder()
+ .id("1")
+ .patient(Reference.builder().id("123").build())
+ .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build())
+ .build()
+ )
)
.build()
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
index 113245a..4a2d2d3 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
@@ -19,20 +19,11 @@
package dev.dnpm.etl.processor.services
-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.ConsentProvision
-import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
-import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose
+import dev.pcvolkmer.mv64e.mtb.*
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 dev.pcvolkmer.mv64e.mtb.Provision
import org.hl7.fhir.instance.model.api.IBaseResource
import java.time.Instant
import java.util.Date
@@ -45,82 +36,59 @@ class TransformationServiceTest {
fun setup() {
this.service = TransformationService(
JacksonConfig().objectMapper(), listOf(
- Transformation.of("consent.status") from Consent.Status.ACTIVE to Consent.Status.REJECTED,
- Transformation.of("diagnoses[*].icd10.version") from "2013" to "2014",
+ Transformation.of("diagnoses[*].code.version") from "2013" to "2014",
)
)
}
@Test
fun shouldTransformMtbFile() {
- val mtbFile = MtbFile.builder().withDiagnoses(
+ val mtbFile = Mtb.builder().diagnoses(
listOf(
- Diagnosis.builder().withId("1234").withIcd10(Icd10("F79.9").also {
- it.version = "2013"
- }).build()
+ MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.9").version("2013").build()).build()
)
).build()
val actual = this.service.transform(mtbFile)
assertThat(actual).isNotNull
- assertThat(actual.diagnoses[0].icd10.version).isEqualTo("2014")
+ assertThat(actual.diagnoses[0].code.version).isEqualTo("2014")
}
@Test
fun shouldOnlyTransformGivenValues() {
- val mtbFile = MtbFile.builder().withDiagnoses(
+ val mtbFile = Mtb.builder().diagnoses(
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()
+ MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.9").version("2013").build()).build(),
+ MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.8").version("2019").build()).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 shouldTransformMtbFileWithConsentEnum() {
- val mtbFile = MtbFile.builder().withConsent(
- Consent("123", "456", Consent.Status.ACTIVE)
- ).build()
-
- val actual = this.service.transform(mtbFile)
-
- assertThat(actual.consent).isNotNull
- assertThat(actual.consent.status).isEqualTo(Consent.Status.REJECTED)
+ assertThat(actual.diagnoses[0].code.code).isEqualTo("F79.9")
+ assertThat(actual.diagnoses[0].code.version).isEqualTo("2014")
+ assertThat(actual.diagnoses[1].code.code).isEqualTo("F79.8")
+ assertThat(actual.diagnoses[1].code.version).isEqualTo("2019")
}
@Test
fun shouldTransformConsentValues() {
- val mtbFile = MtbFile.builder().withDiagnoses(
+ val mtbFile = Mtb.builder().diagnoses(
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()
+ MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.9").version("2013").build()).build(),
+ MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.8").version("2019").build()).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")
+ assertThat(actual.diagnoses[0].code.code).isEqualTo("F79.9")
+ assertThat(actual.diagnoses[0].code.version).isEqualTo("2014")
+ assertThat(actual.diagnoses[1].code.code).isEqualTo("F79.8")
+ assertThat(actual.diagnoses[1].code.version).isEqualTo("2019")
}
@Test
@@ -155,5 +123,4 @@ class TransformationServiceTest {
}
-
-} \ No newline at end of file
+}