From 9af0e159b058586e8a2d20f7bb4c94510aeb2881 Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Mon, 31 Jul 2023 17:08:44 +0200 Subject: [PATCH] Enable patient data deletion --- .../etl/processor/monitoring/RequestStatus.kt | 4 +- .../etl/processor/output/MtbFileSender.kt | 8 ++- .../pseudonym/PseudonymizeService.kt | 6 +- .../etl/processor/web/MtbFileController.kt | 60 ++++++++++++++++++- 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt index 8c19e86..a62aa72 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/RequestStatus.kt @@ -24,5 +24,7 @@ enum class RequestStatus(val value: String) { WARNING("warning"), ERROR("error"), UNKNOWN("unknown"), - DUPLICATION("duplication") + DUPLICATION("duplication"), + DELETE_SUCCESS("delete_success"), + DELETE_ERROR("delete_error") } \ No newline at end of file diff --git a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt index 1fdc9e6..6914ba5 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt @@ -22,11 +22,15 @@ package dev.dnpm.etl.processor.output import de.ukw.ccc.bwhc.dto.MtbFile interface MtbFileSender { - fun send(request: Request): Response + fun send(request: MtbFileRequest): Response + + fun send(request: DeleteRequest): Response data class Response(val status: ResponseStatus, val reason: String = "") - data class Request(val requestId: String, val mtbFile: MtbFile) + data class MtbFileRequest(val requestId: String, val mtbFile: MtbFile) + + data class DeleteRequest(val requestId: String, val patientId: String) enum class ResponseStatus { SUCCESS, diff --git a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt index 364e296..1a79850 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeService.kt @@ -28,7 +28,7 @@ class PseudonymizeService( ) { fun pseudonymize(mtbFile: MtbFile): MtbFile { - val patientPseudonym = "${configProperties.prefix}_${generator.generate(mtbFile.patient.id)}" + val patientPseudonym = patientPseudonym(mtbFile.patient.id) mtbFile.episode.patient = patientPseudonym mtbFile.carePlans.forEach { it.patient = patientPseudonym } @@ -57,4 +57,8 @@ class PseudonymizeService( return mtbFile } + fun patientPseudonym(patientId: String): String { + return "${configProperties.prefix}_${generator.generate(patientId)}" + } + } \ No newline at end of file diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/MtbFileController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/MtbFileController.kt index af780bd..a720f20 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/MtbFileController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/MtbFileController.kt @@ -31,6 +31,7 @@ import org.apache.commons.codec.binary.Base32 import org.apache.commons.codec.digest.DigestUtils import org.slf4j.LoggerFactory import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController @@ -71,7 +72,7 @@ class MtbFileController( return ResponseEntity.noContent().build() } - val request = MtbFileSender.Request(UUID.randomUUID().toString(), pseudonymized) + val request = MtbFileSender.MtbFileRequest(UUID.randomUUID().toString(), pseudonymized) val responses = senders.map { val responseStatus = it.send(request) @@ -128,8 +129,63 @@ class MtbFileController( } } + @PostMapping(path = ["/mtbfile/{patientId}"]) + fun deleteData(@PathVariable patientId: String): ResponseEntity { + val requestId = UUID.randomUUID().toString() + + try { + val patientPseudonym = pseudonymizeService.patientPseudonym(patientId) + + val responses = senders.map { + it.send(MtbFileSender.DeleteRequest(requestId, patientPseudonym)) + } + + val requestStatus = if (responses.map { it.status }.contains(MtbFileSender.ResponseStatus.ERROR)) { + RequestStatus.DELETE_ERROR + } else if (responses.map { it.status }.contains(MtbFileSender.ResponseStatus.SUCCESS)) { + RequestStatus.DELETE_SUCCESS + } else { + RequestStatus.UNKNOWN + } + + requestRepository.save( + Request( + uuid = requestId, + patientId = patientPseudonym, + pid = patientId, + fingerprint = fingerprint(patientPseudonym), + status = requestStatus, + report = when (requestStatus) { + RequestStatus.DELETE_ERROR -> Report("Fehler bei der Datenübertragung oder Inhalt nicht verarbeitbar") + RequestStatus.UNKNOWN -> Report("Keine Informationen") + else -> null + } + ) + ) + + return ResponseEntity.unprocessableEntity().build() + } catch (e: Exception) { + requestRepository.save( + Request( + uuid = requestId, + patientId = "???", + pid = patientId, + fingerprint = "", + status = RequestStatus.DELETE_ERROR, + report = Report("Fehler bei der Pseudonymisierung") + ) + ) + + return ResponseEntity.noContent().build() + } + } + private fun fingerprint(mtbFile: MtbFile): String { - return Base32().encodeAsString(DigestUtils.sha256(objectMapper.writeValueAsString(mtbFile))) + return fingerprint(objectMapper.writeValueAsString(mtbFile)) + } + + private fun fingerprint(s: String): String { + return Base32().encodeAsString(DigestUtils.sha256(s)) .replace("=", "") .lowercase() }