mirror of
https://github.com/pcvolkmer/etl-processor.git
synced 2025-04-19 17:26:51 +00:00
feat: add new MTB endpoint path (#93)
This commit is contained in:
parent
033750eb10
commit
7ae34719fd
17
README.md
17
README.md
@ -24,6 +24,16 @@ Die Erkennung von Duplikaten ist normalerweise immer aktiv, kann jedoch über de
|
|||||||
|
|
||||||
Anfragen werden, wenn nicht als Duplikat behandelt, nach der Pseudonymisierung direkt an das bwHC-Backend gesendet.
|
Anfragen werden, wenn nicht als Duplikat behandelt, nach der Pseudonymisierung direkt an das bwHC-Backend gesendet.
|
||||||
|
|
||||||
|
Ein HTTP Request kann, angenommen die Installation erfolgte auf dem Host `dnpm.example.com` an nachfolgende URLs gesendet werden:
|
||||||
|
|
||||||
|
| HTTP-Request | URL | Consent-Status im Datensatz | Bemerkung |
|
||||||
|
|--------------|-----------------------------------------|-----------------------------|---------------------------------------------------------------------------------|
|
||||||
|
| POST | `https://dnpm.example.com/mtb` | ACTIVE | Die Anwendung verarbeitet den eingehenden Datensatz |
|
||||||
|
| POST | `https://dnpm.example.com/mtb` | REJECT | Die Anwendung sendet einen Lösch-Request für die im Datensatz angegebene Pat-ID |
|
||||||
|
| DELETE | `https://dnpm.example.com/mtb/12345678` | - | Die Anwendung sendet einen Lösch-Request für Pat-ID `12345678` |
|
||||||
|
|
||||||
|
Anstelle des Pfads `/mtb` kann auch, wie in Version 0.9 und älter üblich, `/mtbfile` verwendet werden.
|
||||||
|
|
||||||
### Datenübermittlung mit Apache Kafka
|
### Datenübermittlung mit Apache Kafka
|
||||||
|
|
||||||
Anfragen werden, wenn nicht als Duplikat behandelt, nach der Pseudonymisierung an Apache Kafka übergeben.
|
Anfragen werden, wenn nicht als Duplikat behandelt, nach der Pseudonymisierung an Apache Kafka übergeben.
|
||||||
@ -42,6 +52,9 @@ In Versionen des ETL-Processors **nach Version 0.10** werden die folgenden Konfi
|
|||||||
* `APP_KAFKA_TOPIC`: Nutzen Sie nun die Konfigurationsoption `APP_KAFKA_OUTPUT_TOPIC`
|
* `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`
|
* `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.
|
||||||
|
|
||||||
### Pseudonymisierung der Patienten-ID
|
### Pseudonymisierung der Patienten-ID
|
||||||
|
|
||||||
Wenn eine URI zu einer gPAS-Instanz (Version >= 2023.1.0) angegeben ist, wird diese verwendet.
|
Wenn eine URI zu einer gPAS-Instanz (Version >= 2023.1.0) angegeben ist, wird diese verwendet.
|
||||||
@ -161,7 +174,7 @@ zur Nutzung des MTB-File-Endpunkts eine HTTP-Basic-Authentifizierung voraussetze
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
In diesem Fall können den Endpunkt für das Onkostar-Plugin **[onkostar-plugin-dnpmexport](https://github.com/CCC-MF/onkostar-plugin-dnpmexport)** wie folgt konfigurieren:
|
In diesem Fall kann der Endpunkt für das Onkostar-Plugin **[onkostar-plugin-dnpmexport](https://github.com/CCC-MF/onkostar-plugin-dnpmexport)** wie folgt konfiguriert werden:
|
||||||
|
|
||||||
```
|
```
|
||||||
https://testonkostar:MTg1NTL...NGU4@etl.example.com/mtbfile
|
https://testonkostar:MTg1NTL...NGU4@etl.example.com/mtbfile
|
||||||
@ -266,7 +279,7 @@ Dieses Vorgehen empfiehlt sich, wenn Sie gespeicherte Records nachgelagert für
|
|||||||
|
|
||||||
### Antworten und Statusauswertung
|
### Antworten und Statusauswertung
|
||||||
|
|
||||||
Anfragen and bwHC-Backend aus Versionen bis 0.9.x wurden wie folgt behandelt:
|
Anfragen an das bwHC-Backend aus Versionen bis 0.9.x wurden wie folgt behandelt:
|
||||||
|
|
||||||
| HTTP-Response | Status |
|
| HTTP-Response | Status |
|
||||||
|----------------|-----------|
|
|----------------|-----------|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of ETL-Processor
|
* This file is part of ETL-Processor
|
||||||
*
|
*
|
||||||
* Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
|
* 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
|
* 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
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
@ -28,7 +28,7 @@ import org.springframework.http.ResponseEntity
|
|||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(path = ["mtbfile"])
|
@RequestMapping(path = ["mtbfile", "mtb"])
|
||||||
class MtbFileRestController(
|
class MtbFileRestController(
|
||||||
private val requestProcessor: RequestProcessor,
|
private val requestProcessor: RequestProcessor,
|
||||||
) {
|
) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of ETL-Processor
|
* This file is part of ETL-Processor
|
||||||
*
|
*
|
||||||
* Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
|
* 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
|
* 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
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import de.ukw.ccc.bwhc.dto.*
|
import de.ukw.ccc.bwhc.dto.*
|
||||||
import dev.dnpm.etl.processor.services.RequestProcessor
|
import dev.dnpm.etl.processor.services.RequestProcessor
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.extension.ExtendWith
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
@ -40,24 +41,122 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders
|
|||||||
@ExtendWith(MockitoExtension::class)
|
@ExtendWith(MockitoExtension::class)
|
||||||
class MtbFileRestControllerTest {
|
class MtbFileRestControllerTest {
|
||||||
|
|
||||||
private lateinit var mockMvc: MockMvc
|
|
||||||
|
|
||||||
private lateinit var requestProcessor: RequestProcessor
|
|
||||||
|
|
||||||
private val objectMapper = ObjectMapper()
|
private val objectMapper = ObjectMapper()
|
||||||
|
|
||||||
@BeforeEach
|
@Nested
|
||||||
fun setup(
|
inner class BwhcRequests {
|
||||||
@Mock requestProcessor: RequestProcessor
|
|
||||||
) {
|
private lateinit var mockMvc: MockMvc
|
||||||
this.requestProcessor = requestProcessor
|
|
||||||
val controller = MtbFileRestController(requestProcessor)
|
private lateinit var requestProcessor: RequestProcessor
|
||||||
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup(
|
||||||
|
@Mock requestProcessor: RequestProcessor
|
||||||
|
) {
|
||||||
|
this.requestProcessor = requestProcessor
|
||||||
|
val controller = MtbFileRestController(requestProcessor)
|
||||||
|
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldProcessPostRequest() {
|
||||||
|
mockMvc.post("/mtbfile") {
|
||||||
|
content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.ACTIVE))
|
||||||
|
contentType = MediaType.APPLICATION_JSON
|
||||||
|
}.andExpect {
|
||||||
|
status {
|
||||||
|
isAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(requestProcessor, times(1)).processMtbFile(any())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldProcessPostRequestWithRejectedConsent() {
|
||||||
|
mockMvc.post("/mtbfile") {
|
||||||
|
content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.REJECTED))
|
||||||
|
contentType = MediaType.APPLICATION_JSON
|
||||||
|
}.andExpect {
|
||||||
|
status {
|
||||||
|
isAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(requestProcessor, times(1)).processDeletion(anyValueClass())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldProcessDeleteRequest() {
|
||||||
|
mockMvc.delete("/mtbfile/TEST_12345678").andExpect {
|
||||||
|
status {
|
||||||
|
isAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(requestProcessor, times(1)).processDeletion(anyValueClass())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun shouldProcessMtbFilePostRequest() {
|
inner class BwhcRequestsWithAlias {
|
||||||
val mtbFile = MtbFile.builder()
|
|
||||||
|
private lateinit var mockMvc: MockMvc
|
||||||
|
|
||||||
|
private lateinit var requestProcessor: RequestProcessor
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup(
|
||||||
|
@Mock requestProcessor: RequestProcessor
|
||||||
|
) {
|
||||||
|
this.requestProcessor = requestProcessor
|
||||||
|
val controller = MtbFileRestController(requestProcessor)
|
||||||
|
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldProcessPostRequest() {
|
||||||
|
mockMvc.post("/mtb") {
|
||||||
|
content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.ACTIVE))
|
||||||
|
contentType = MediaType.APPLICATION_JSON
|
||||||
|
}.andExpect {
|
||||||
|
status {
|
||||||
|
isAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(requestProcessor, times(1)).processMtbFile(any())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldProcessPostRequestWithRejectedConsent() {
|
||||||
|
mockMvc.post("/mtb") {
|
||||||
|
content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.REJECTED))
|
||||||
|
contentType = MediaType.APPLICATION_JSON
|
||||||
|
}.andExpect {
|
||||||
|
status {
|
||||||
|
isAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(requestProcessor, times(1)).processDeletion(anyValueClass())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldProcessDeleteRequest() {
|
||||||
|
mockMvc.delete("/mtb/TEST_12345678").andExpect {
|
||||||
|
status {
|
||||||
|
isAccepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(requestProcessor, times(1)).processDeletion(anyValueClass())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun bwhcMtbFileContent(consentStatus: Consent.Status) = MtbFile.builder()
|
||||||
.withPatient(
|
.withPatient(
|
||||||
Patient.builder()
|
Patient.builder()
|
||||||
.withId("TEST_12345678")
|
.withId("TEST_12345678")
|
||||||
@ -68,7 +167,7 @@ class MtbFileRestControllerTest {
|
|||||||
.withConsent(
|
.withConsent(
|
||||||
Consent.builder()
|
Consent.builder()
|
||||||
.withId("1")
|
.withId("1")
|
||||||
.withStatus(Consent.Status.ACTIVE)
|
.withStatus(consentStatus)
|
||||||
.withPatient("TEST_12345678")
|
.withPatient("TEST_12345678")
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@ -80,66 +179,5 @@ class MtbFileRestControllerTest {
|
|||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
mockMvc.post("/mtbfile") {
|
|
||||||
content = objectMapper.writeValueAsString(mtbFile)
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
}.andExpect {
|
|
||||||
status {
|
|
||||||
isAccepted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(requestProcessor, times(1)).processMtbFile(any())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun shouldProcessMtbFilePostRequestWithRejectedConsent() {
|
|
||||||
val mtbFile = MtbFile.builder()
|
|
||||||
.withPatient(
|
|
||||||
Patient.builder()
|
|
||||||
.withId("TEST_12345678")
|
|
||||||
.withBirthDate("2000-08-08")
|
|
||||||
.withGender(Patient.Gender.MALE)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withConsent(
|
|
||||||
Consent.builder()
|
|
||||||
.withId("1")
|
|
||||||
.withStatus(Consent.Status.REJECTED)
|
|
||||||
.withPatient("TEST_12345678")
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withEpisode(
|
|
||||||
Episode.builder()
|
|
||||||
.withId("1")
|
|
||||||
.withPatient("TEST_12345678")
|
|
||||||
.withPeriod(PeriodStart("2023-08-08"))
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
mockMvc.post("/mtbfile") {
|
|
||||||
content = objectMapper.writeValueAsString(mtbFile)
|
|
||||||
contentType = MediaType.APPLICATION_JSON
|
|
||||||
}.andExpect {
|
|
||||||
status {
|
|
||||||
isAccepted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(requestProcessor, times(1)).processDeletion(anyValueClass())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun shouldProcessMtbFileDeleteRequest() {
|
|
||||||
mockMvc.delete("/mtbfile/TEST_12345678").andExpect {
|
|
||||||
status {
|
|
||||||
isAccepted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verify(requestProcessor, times(1)).processDeletion(anyValueClass())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user