diff options
Diffstat (limited to 'src/main/kotlin/dev/dnpm/etl/processor')
| -rw-r--r-- | src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt | 57 | ||||
| -rw-r--r-- | src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt | 410 |
2 files changed, 247 insertions, 220 deletions
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 f4ab194..523a0a8 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/input/MtbFileRestController.kt @@ -33,36 +33,37 @@ import org.springframework.web.bind.annotation.* @RestController @RequestMapping(path = ["mtbfile", "mtb", "api/mtbfile", "api/mtb"]) class MtbFileRestController( - private val requestProcessor: RequestProcessor, - private val consentEvaluator: ConsentEvaluator, + private val requestProcessor: RequestProcessor ) { - private val logger = LoggerFactory.getLogger(MtbFileRestController::class.java) + private val logger = LoggerFactory.getLogger(MtbFileRestController::class.java) - @GetMapping - fun info(): ResponseEntity<String> { - return ResponseEntity.ok("Test") - } + @GetMapping + fun info(): ResponseEntity<String> { + return ResponseEntity.ok("Test") + } - @PostMapping( - path = ["", "etl/patient-record"], - 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) - return ResponseEntity.accepted().build() - } + @PostMapping( + path = ["", "etl/patient-record"], + 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") + if (requestProcessor.processMtbFile(mtbFile)) { + return ResponseEntity.accepted().build() + } + return ResponseEntity.badRequest().build() + } - @DeleteMapping( - path = ["{patientId}", "etl/patient-record/{patientId}", "etl/patient/{patientId}"] - ) - fun deleteData(@PathVariable patientId: String): ResponseEntity<Unit> { - logger.debug("Accepted patient ID to process deletion") - requestProcessor.processDeletion(PatientId(patientId), TtpConsentStatus.UNKNOWN_CHECK_FILE) - return ResponseEntity.accepted().build() - } + @DeleteMapping( + path = ["{patientId}", "etl/patient-record/{patientId}", "etl/patient/{patientId}"] + ) + fun deleteData(@PathVariable patientId: String): ResponseEntity<Unit> { + logger.debug("Accepted patient ID to process deletion") + requestProcessor.processDeletion(PatientId(patientId), TtpConsentStatus.UNKNOWN_CHECK_FILE) + return ResponseEntity.accepted().build() + } } 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 fe1fd3b..7325265 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt @@ -56,225 +56,251 @@ class RequestProcessor( private val consentProcessor: ConsentProcessor?, ) { - private var logger: Logger = LoggerFactory.getLogger("RequestProcessor") + private var logger: Logger = LoggerFactory.getLogger("RequestProcessor") - fun processMtbFile(mtbFile: Mtb) { - processMtbFile(mtbFile, randomRequestId()) - } + fun processMtbFile(mtbFile: Mtb): Boolean { + return processMtbFile(mtbFile, randomRequestId()) + } + + fun processMtbFile(mtbFile: Mtb, requestId: RequestId): Boolean { + val isConsentOk = + consentProcessor != null && consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || + consentProcessor == null - fun processMtbFile(mtbFile: Mtb, requestId: RequestId) { - val isConsentOk = - consentProcessor != null && consentProcessor.consentGatedCheckAndTryEmbedding(mtbFile) || - consentProcessor == null + if (!isConsentOk) { + logger.warn("consent check failed but will be sent to DNPM:DIP!") + } - if (!isConsentOk) { - logger.warn("consent check failed but will be sent to DNPM:DIP!") + try { + mtbFile addGenomDeTan pseudonymizeService + mtbFile pseudonymizeWith pseudonymizeService + mtbFile anonymizeContentWith pseudonymizeService + val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile)) + saveAndSend(request) + } catch (e: Exception) { + logger.error("Error while processing MtbFile", e) + requestService.save( + Request( + null, + requestId, + PatientPseudonym("INVALID"), + emptyPatientId(), + fingerprint(""), + RequestType.MTB_FILE, + SubmissionType.UNKNOWN, + RequestStatus.ERROR, + Tan.empty(), + report = Report("Fehlerhafte Eingangsdaten. Keine Verarbeitung oder Weiterleitung."), + ) + ) + return false + } + return true } - mtbFile addGenomDeTan pseudonymizeService - mtbFile pseudonymizeWith pseudonymizeService - mtbFile anonymizeContentWith pseudonymizeService - val request = DnpmV2MtbFileRequest(requestId, transformationService.transform(mtbFile)) - saveAndSend(request) - } - - private fun <T> saveAndSend(request: MtbFileRequest<T>) { - var submissionType: SubmissionType = - when (request) { - is DnpmV2MtbFileRequest -> { - when (request.content.metadata?.type) { - MvhSubmissionType.TEST -> SubmissionType.TEST - MvhSubmissionType.INITIAL -> SubmissionType.INITIAL - MvhSubmissionType.ADDITION -> SubmissionType.ADDITION - MvhSubmissionType.CORRECTION -> SubmissionType.CORRECTION - MvhSubmissionType.FOLLOWUP -> SubmissionType.FOLLOWUP - else -> SubmissionType.UNKNOWN + private fun <T> saveAndSend(request: MtbFileRequest<T>) { + var submissionType: SubmissionType = + when (request) { + is DnpmV2MtbFileRequest -> { + when (request.content.metadata?.type) { + MvhSubmissionType.TEST -> SubmissionType.TEST + MvhSubmissionType.INITIAL -> SubmissionType.INITIAL + MvhSubmissionType.ADDITION -> SubmissionType.ADDITION + MvhSubmissionType.CORRECTION -> SubmissionType.CORRECTION + MvhSubmissionType.FOLLOWUP -> SubmissionType.FOLLOWUP + else -> SubmissionType.UNKNOWN + } + } } - } - } - if ( - appConfigProperties.postInitialSubmissionBlock && + if ( + appConfigProperties.postInitialSubmissionBlock && hasSuccessfullInitialSubmission(request.patientPseudonym()) && hasUnacceptedInitialSubmission(request.patientPseudonym()) - ) { - requestService.save( - Request( - request.requestId, - request.patientPseudonym(), - emptyPatientId(), - fingerprint(request), - RequestType.MTB_FILE, - submissionType, - RequestStatus.BLOCKED_INITIAL, - Tan(request.content.metadata?.transferTan.orEmpty()) - ) - ) - // Exit - no further processing - return - } + ) { + requestService.save( + Request( + request.requestId, + request.patientPseudonym(), + emptyPatientId(), + fingerprint(request), + RequestType.MTB_FILE, + submissionType, + RequestStatus.BLOCKED_INITIAL, + Tan(request.content.metadata?.transferTan.orEmpty()) + ) + ) + // Exit - no further processing + return + } - if ( - appConfigProperties.postInitialSubmissionBlock && + if ( + appConfigProperties.postInitialSubmissionBlock && hasSuccessfullInitialSubmission(request.patientPseudonym()) && !hasUnacceptedInitialSubmission(request.patientPseudonym()) - ) { - // Use "addition" after "intial" with "Meldebestaetigung" - request.content.metadata?.let { - logger.warn("Override submission type using 'addition' after first initial submission!") - it.type = MvhSubmissionType.ADDITION - submissionType = SubmissionType.ADDITION - } - } + ) { + // Use "addition" after "intial" with "Meldebestaetigung" + request.content.metadata?.let { + logger.warn("Override submission type using 'addition' after first initial submission!") + it.type = MvhSubmissionType.ADDITION + submissionType = SubmissionType.ADDITION + } + } - requestService.save( - Request( - request.requestId, - request.patientPseudonym(), - emptyPatientId(), - fingerprint(request), - RequestType.MTB_FILE, - submissionType, - RequestStatus.UNKNOWN, - Tan(request.content.metadata?.transferTan.orEmpty()), + requestService.save( + Request( + request.requestId, + request.patientPseudonym(), + emptyPatientId(), + fingerprint(request), + RequestType.MTB_FILE, + submissionType, + RequestStatus.UNKNOWN, + Tan(request.content.metadata?.transferTan.orEmpty()), + ) ) - ) - if (appConfigProperties.duplicationDetection && isDuplication(request)) { - applicationEventPublisher.publishEvent( - ResponseEvent(request.requestId, Instant.now(), RequestStatus.DUPLICATION) - ) - return - } + if (appConfigProperties.duplicationDetection && isDuplication(request)) { + applicationEventPublisher.publishEvent( + ResponseEvent(request.requestId, Instant.now(), RequestStatus.DUPLICATION) + ) + return + } - val responseStatus = sender.send(request) - - applicationEventPublisher.publishEvent( - ResponseEvent( - request.requestId, - Instant.now(), - responseStatus.status, - when (responseStatus.status) { - RequestStatus.ERROR, - RequestStatus.WARNING -> Optional.of(responseStatus.body) - else -> Optional.empty() - }, - ) - ) - } + val responseStatus = sender.send(request) - private fun hasSuccessfullInitialSubmission(patientPseudonym: PatientPseudonym): Boolean { - return this.requestService.allRequestsByPatientPseudonym(patientPseudonym).any { - it.submissionType == SubmissionType.INITIAL && - (it.status == RequestStatus.SUCCESS || it.status == RequestStatus.WARNING) + applicationEventPublisher.publishEvent( + ResponseEvent( + request.requestId, + Instant.now(), + responseStatus.status, + when (responseStatus.status) { + RequestStatus.ERROR, + RequestStatus.WARNING -> Optional.of(responseStatus.body) + + else -> Optional.empty() + }, + ) + ) } - } - private fun hasUnacceptedInitialSubmission(patientPseudonym: PatientPseudonym): Boolean { - return this.requestService.allRequestsByPatientPseudonym(patientPseudonym).any { - it.submissionType == SubmissionType.INITIAL && - !(it.submissionAccepted || it.status == RequestStatus.BLOCKED_INITIAL) + private fun hasSuccessfullInitialSubmission(patientPseudonym: PatientPseudonym): Boolean { + return this.requestService.allRequestsByPatientPseudonym(patientPseudonym).any { + it.submissionType == SubmissionType.INITIAL && + (it.status == RequestStatus.SUCCESS || it.status == RequestStatus.WARNING) + } } - } - private fun <T> isDuplication(pseudonymizedMtbFileRequest: MtbFileRequest<T>): Boolean { - val patientPseudonym = - when (pseudonymizedMtbFileRequest) { - is DnpmV2MtbFileRequest -> - PatientPseudonym(pseudonymizedMtbFileRequest.content.patient.id) + private fun hasUnacceptedInitialSubmission(patientPseudonym: PatientPseudonym): Boolean { + return this.requestService.allRequestsByPatientPseudonym(patientPseudonym).any { + it.submissionType == SubmissionType.INITIAL && + !(it.submissionAccepted || it.status == RequestStatus.BLOCKED_INITIAL) } + } - val lastMtbFileRequestForPatient = - requestService.lastMtbFileRequestForPatientPseudonym(patientPseudonym) - val isLastRequestDeletion = - requestService.isLastRequestWithKnownStatusDeletion(patientPseudonym) - - return null != lastMtbFileRequestForPatient && - !isLastRequestDeletion && - lastMtbFileRequestForPatient.fingerprint == fingerprint(pseudonymizedMtbFileRequest) - } - - fun processDeletion(patientId: PatientId, isConsented: TtpConsentStatus) { - processDeletion(patientId, randomRequestId(), isConsented) - } - - fun processDeletion(patientId: PatientId, requestId: RequestId, isConsented: TtpConsentStatus) { - try { - val patientPseudonym = pseudonymizeService.patientPseudonym(patientId) - - val requestStatus: RequestStatus = - when (isConsented) { - TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, - TtpConsentStatus.BROAD_CONSENT_MISSING, - TtpConsentStatus.BROAD_CONSENT_REJECTED -> RequestStatus.NO_CONSENT - TtpConsentStatus.FAILED_TO_ASK -> RequestStatus.ERROR - TtpConsentStatus.BROAD_CONSENT_GIVEN, - TtpConsentStatus.UNKNOWN_CHECK_FILE -> RequestStatus.UNKNOWN - TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, - TtpConsentStatus.GENOM_DE_CONSENT_MISSING, - TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED -> { - throw RuntimeException( - "processDelete should never deal with '" + - isConsented.name + - "' consent status. This is a bug and need to be fixed!" - ) + private fun <T> isDuplication(pseudonymizedMtbFileRequest: MtbFileRequest<T>): Boolean { + val patientPseudonym = + when (pseudonymizedMtbFileRequest) { + is DnpmV2MtbFileRequest -> + PatientPseudonym(pseudonymizedMtbFileRequest.content.patient.id) } - } - - requestService.save( - Request( - requestId, - patientPseudonym, - emptyPatientId(), - fingerprint(patientPseudonym.value), - RequestType.DELETE, - SubmissionType.UNKNOWN, - requestStatus, - Tan.empty() - ) - ) - - val responseStatus = sender.send(DeleteRequest(requestId, patientPseudonym)) - - applicationEventPublisher.publishEvent( - ResponseEvent( - requestId, - Instant.now(), - responseStatus.status, - when (responseStatus.status) { - RequestStatus.WARNING, - RequestStatus.ERROR -> Optional.of(responseStatus.body) - else -> Optional.empty() - }, - ) - ) - } catch (_: Exception) { - requestService.save( - Request( - uuid = requestId, - patientPseudonym = emptyPatientPseudonym(), - pid = patientId, - fingerprint = Fingerprint.empty(), - status = RequestStatus.ERROR, - type = RequestType.DELETE, - submissionType = SubmissionType.UNKNOWN, - report = Report("Fehler bei der Pseudonymisierung"), - tan = Tan.empty(), - ) - ) + + val lastMtbFileRequestForPatient = + requestService.lastMtbFileRequestForPatientPseudonym(patientPseudonym) + val isLastRequestDeletion = + requestService.isLastRequestWithKnownStatusDeletion(patientPseudonym) + + return null != lastMtbFileRequestForPatient && + !isLastRequestDeletion && + lastMtbFileRequestForPatient.fingerprint == fingerprint(pseudonymizedMtbFileRequest) } - } - private fun <T> fingerprint(request: MtbFileRequest<T>): Fingerprint { - return when (request) { - is DnpmV2MtbFileRequest -> fingerprint(objectMapper.writeValueAsString(request.content)) + fun processDeletion(patientId: PatientId, isConsented: TtpConsentStatus) { + processDeletion(patientId, randomRequestId(), isConsented) } - } - private fun fingerprint(s: String): Fingerprint { - return Fingerprint(Base32().encodeAsString(DigestUtils.sha256(s)) - .replace("=", "") - .lowercase()) - } + fun processDeletion(patientId: PatientId, requestId: RequestId, isConsented: TtpConsentStatus) { + try { + val patientPseudonym = pseudonymizeService.patientPseudonym(patientId) + + val requestStatus: RequestStatus = + when (isConsented) { + TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, + TtpConsentStatus.BROAD_CONSENT_MISSING, + TtpConsentStatus.BROAD_CONSENT_REJECTED -> RequestStatus.NO_CONSENT + + TtpConsentStatus.FAILED_TO_ASK -> RequestStatus.ERROR + TtpConsentStatus.BROAD_CONSENT_GIVEN, + TtpConsentStatus.UNKNOWN_CHECK_FILE -> RequestStatus.UNKNOWN + + TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, + TtpConsentStatus.GENOM_DE_CONSENT_MISSING, + TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED -> { + throw RuntimeException( + "processDelete should never deal with '" + + isConsented.name + + "' consent status. This is a bug and need to be fixed!" + ) + } + } + + requestService.save( + Request( + requestId, + patientPseudonym, + emptyPatientId(), + fingerprint(patientPseudonym.value), + RequestType.DELETE, + SubmissionType.UNKNOWN, + requestStatus, + Tan.empty() + ) + ) + + val responseStatus = sender.send(DeleteRequest(requestId, patientPseudonym)) + + applicationEventPublisher.publishEvent( + ResponseEvent( + requestId, + Instant.now(), + responseStatus.status, + when (responseStatus.status) { + RequestStatus.WARNING, + RequestStatus.ERROR -> Optional.of(responseStatus.body) + + else -> Optional.empty() + }, + ) + ) + } catch (_: Exception) { + requestService.save( + Request( + uuid = requestId, + patientPseudonym = emptyPatientPseudonym(), + pid = patientId, + fingerprint = Fingerprint.empty(), + status = RequestStatus.ERROR, + type = RequestType.DELETE, + submissionType = SubmissionType.UNKNOWN, + report = Report("Fehler bei der Pseudonymisierung"), + tan = Tan.empty(), + ) + ) + } + } + + private fun <T> fingerprint(request: MtbFileRequest<T>): Fingerprint { + return when (request) { + is DnpmV2MtbFileRequest -> fingerprint(objectMapper.writeValueAsString(request.content)) + } + } + + private fun fingerprint(s: String): Fingerprint { + return Fingerprint( + Base32().encodeAsString(DigestUtils.sha256(s)) + .replace("=", "") + .lowercase() + ) + } } |
