diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt index a9ec3a1..231fffe 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/monitoring/RequestRepositoryTest.kt @@ -22,6 +22,7 @@ package dev.dnpm.etl.processor.monitoring import dev.dnpm.etl.processor.AbstractTestcontainerTest import dev.dnpm.etl.processor.Fingerprint import dev.dnpm.etl.processor.output.MtbFileSender +import dev.dnpm.etl.processor.randomRequestId import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -61,7 +62,7 @@ class RequestRepositoryTest : AbstractTestcontainerTest() { @Test fun shouldSaveRequest() { val request = Request( - RequestId.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt index 9314846..1aac5fd 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/services/RequestServiceIntegrationTest.kt @@ -26,6 +26,7 @@ import dev.dnpm.etl.processor.monitoring.RequestRepository import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.monitoring.RequestType import dev.dnpm.etl.processor.output.MtbFileSender +import dev.dnpm.etl.processor.randomRequestId import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -38,7 +39,6 @@ import org.springframework.test.context.junit.jupiter.SpringExtension import org.springframework.transaction.annotation.Transactional import org.testcontainers.junit.jupiter.Testcontainers import java.time.Instant -import java.util.* @Testcontainers @ExtendWith(SpringExtension::class) @@ -77,7 +77,7 @@ class RequestServiceIntegrationTest : AbstractTestcontainerTest() { this.requestRepository.saveAll( listOf( Request( - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -87,7 +87,7 @@ class RequestServiceIntegrationTest : AbstractTestcontainerTest() { ), // Should be ignored - wrong patient ID --> Request( - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678902", "P2", Fingerprint("0123456789abcdef2"), @@ -97,7 +97,7 @@ class RequestServiceIntegrationTest : AbstractTestcontainerTest() { ), // <-- Request( - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P2", Fingerprint("0123456789abcdee1"), diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt index d227dcd..a5d8771 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/HomeControllerTest.kt @@ -29,6 +29,7 @@ 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.randomRequestId import dev.dnpm.etl.processor.services.RequestService import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach @@ -36,6 +37,7 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers.anyString import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.any @@ -81,6 +83,13 @@ class HomeControllerTest { private lateinit var mockMvc: MockMvc private lateinit var webClient: WebClient + inline fun anyValueClass(): T { + val unboxedClass = T::class.java.declaredFields.first().type + return ArgumentMatchers.any(unboxedClass as Class) + ?: T::class.java.getDeclaredMethod("box-impl", unboxedClass) + .invoke(null, null) as T + } + @BeforeEach fun setup( @Autowired mockMvc: MockMvc, @@ -119,7 +128,7 @@ class HomeControllerTest { listOf( Request( 2L, - UUID.randomUUID().toString(), + randomRequestId(), "PSEUDO1", "PATIENT1", Fingerprint("ashdkasdh"), @@ -128,7 +137,7 @@ class HomeControllerTest { ), Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "PSEUDO1", "PATIENT1", Fingerprint("asdasdasd"), @@ -147,9 +156,9 @@ class HomeControllerTest { @Test @WithMockUser(username = "admin", roles = ["ADMIN"]) fun testShouldShowRequestDetails() { - val requestId = UUID.randomUUID().toString() + val requestId = randomRequestId() - whenever(requestService.findByUuid(any())).thenReturn( + whenever(requestService.findByUuid(anyValueClass())).thenReturn( Optional.of( Request( 2L, @@ -165,7 +174,7 @@ class HomeControllerTest { ) ) - val page = webClient.getPage("http://localhost/report/${requestId}") + val page = webClient.getPage("http://localhost/report/${requestId.value}") assertThat(page.querySelectorAll("tbody tr")).hasSize(1) assertThat(page.querySelectorAll("div.notification.info")).isEmpty() } @@ -178,7 +187,7 @@ class HomeControllerTest { listOf( Request( 2L, - UUID.randomUUID().toString(), + randomRequestId(), "PSEUDO1", "PATIENT1", Fingerprint("ashdkasdh"), @@ -187,7 +196,7 @@ class HomeControllerTest { ), Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "PSEUDO1", "PATIENT1", Fingerprint("asdasdasd"), @@ -229,14 +238,14 @@ class HomeControllerTest { @Test @WithMockUser(username = "admin", roles = ["ADMIN"]) fun testShouldThrowNotFoundExceptionForUnknownReport() { - val requestId = UUID.randomUUID().toString() + val requestId = randomRequestId() - whenever(requestService.findByUuid(any())).thenReturn( + whenever(requestService.findByUuid(anyValueClass())).thenReturn( Optional.empty() ) assertThrows { - webClient.getPage("http://localhost/report/${requestId}") + webClient.getPage("http://localhost/report/${requestId.value}") }.also { assertThat(it).hasRootCauseInstanceOf(NotFoundException::class.java) } diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt index 06c71e4..424a0e3 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsControllerTest.kt @@ -22,14 +22,12 @@ package dev.dnpm.etl.processor.web import com.gargoylesoftware.htmlunit.WebClient import dev.dnpm.etl.processor.config.AppConfiguration import dev.dnpm.etl.processor.config.AppSecurityConfiguration -import dev.dnpm.etl.processor.security.TokenService import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.junit.jupiter.MockitoExtension import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest -import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.TestPropertySource import org.springframework.test.context.junit.jupiter.SpringExtension diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt index 81a12d3..e66def7 100644 --- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt +++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/StatisticsRestControllerTest.kt @@ -26,6 +26,7 @@ import dev.dnpm.etl.processor.monitoring.CountedState 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.randomRequestId import dev.dnpm.etl.processor.services.RequestService import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.hasSize @@ -52,7 +53,6 @@ import reactor.core.publisher.Sinks import reactor.test.StepVerifier import java.time.Instant import java.time.temporal.ChronoUnit -import java.util.* @WebMvcTest(controllers = [StatisticsRestController::class]) @@ -187,7 +187,7 @@ class StatisticsRestControllerTest { listOf( Request( 1, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -197,7 +197,7 @@ class StatisticsRestControllerTest { ), Request( 2, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678902", "P2", Fingerprint("0123456789abcdef2"), @@ -207,7 +207,7 @@ class StatisticsRestControllerTest { ), Request( 3, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P2", Fingerprint("0123456789abcdee1"), @@ -217,7 +217,7 @@ class StatisticsRestControllerTest { ), Request( 4, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678902", "P2", Fingerprint("0123456789abcdef2"), @@ -227,7 +227,7 @@ class StatisticsRestControllerTest { ), Request( 5, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678902", "P2", Fingerprint("0123456789abcdef2"), 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 de901ce..b72b1fd 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/input/KafkaInputListener.kt @@ -22,6 +22,7 @@ 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.RequestId import dev.dnpm.etl.processor.services.RequestProcessor import org.apache.kafka.clients.consumer.ConsumerRecord import org.slf4j.LoggerFactory @@ -37,9 +38,9 @@ class KafkaInputListener( val mtbFile = objectMapper.readValue(data.value(), MtbFile::class.java) val firstRequestIdHeader = data.headers().headers("requestId")?.firstOrNull() val requestId = if (null != firstRequestIdHeader) { - String(firstRequestIdHeader.value()) + RequestId(String(firstRequestIdHeader.value())) } else { - "" + RequestId("") } if (mtbFile.consent.status == Consent.Status.ACTIVE) { diff --git a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt index 0ee07d6..9efae4c 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/monitoring/Request.kt @@ -20,6 +20,8 @@ package dev.dnpm.etl.processor.monitoring import dev.dnpm.etl.processor.Fingerprint +import dev.dnpm.etl.processor.randomRequestId +import dev.dnpm.etl.processor.RequestId import org.springframework.data.annotation.Id import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable @@ -32,12 +34,10 @@ import org.springframework.data.repository.PagingAndSortingRepository import java.time.Instant import java.util.* -typealias RequestId = UUID - @Table("request") data class Request( @Id val id: Long? = null, - val uuid: String = RequestId.randomUUID().toString(), + val uuid: RequestId = randomRequestId(), val patientId: String, val pid: String, @Column("fingerprint") @@ -48,7 +48,7 @@ data class Request( @Embedded.Nullable var report: Report? = null ) { constructor( - uuid: String, + uuid: RequestId, patientId: String, pid: String, fingerprint: Fingerprint, @@ -58,7 +58,7 @@ data class Request( this(null, uuid, patientId, pid, fingerprint, type, status, Instant.now()) constructor( - uuid: String, + uuid: RequestId, patientId: String, pid: String, fingerprint: Fingerprint, @@ -85,7 +85,7 @@ interface RequestRepository : CrudRepository, PagingAndSortingRep fun findAllByPatientIdOrderByProcessedAtDesc(patientId: String): List - fun findByUuidEquals(uuid: String): Optional + fun findByUuidEquals(uuid: RequestId): Optional fun findRequestByPatientId(patientId: String, pageable: Pageable): Page 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 fc5d617..7b777e8 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSender.kt @@ -22,6 +22,7 @@ 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.RequestId import dev.dnpm.etl.processor.config.KafkaProperties import dev.dnpm.etl.processor.monitoring.RequestStatus import org.slf4j.LoggerFactory @@ -101,5 +102,5 @@ class KafkaMtbFileSender( return "{\"pid\": \"${request.patientId}\"}" } - data class Data(val requestId: String, val content: MtbFile) + data class Data(val requestId: RequestId, val content: MtbFile) } \ 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 aca972b..2670f2e 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/output/MtbFileSender.kt @@ -20,6 +20,7 @@ package dev.dnpm.etl.processor.output import de.ukw.ccc.bwhc.dto.MtbFile +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.monitoring.RequestStatus import org.springframework.http.HttpStatusCode @@ -32,9 +33,9 @@ interface MtbFileSender { data class Response(val status: RequestStatus, val body: String = "") - data class MtbFileRequest(val requestId: String, val mtbFile: MtbFile) + data class MtbFileRequest(val requestId: RequestId, val mtbFile: MtbFile) - data class DeleteRequest(val requestId: String, val patientId: String) + data class DeleteRequest(val requestId: RequestId, val patientId: String) } 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 c36ddef..94598ae 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt @@ -22,6 +22,8 @@ package dev.dnpm.etl.processor.services import com.fasterxml.jackson.databind.ObjectMapper import de.ukw.ccc.bwhc.dto.MtbFile import dev.dnpm.etl.processor.Fingerprint +import dev.dnpm.etl.processor.randomRequestId +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.monitoring.Report import dev.dnpm.etl.processor.monitoring.Request @@ -50,10 +52,10 @@ class RequestProcessor( ) { fun processMtbFile(mtbFile: MtbFile) { - processMtbFile(mtbFile, UUID.randomUUID().toString()) + processMtbFile(mtbFile, randomRequestId()) } - fun processMtbFile(mtbFile: MtbFile, requestId: String) { + fun processMtbFile(mtbFile: MtbFile, requestId: RequestId) { val pid = mtbFile.patient.id mtbFile pseudonymizeWith pseudonymizeService @@ -109,10 +111,10 @@ class RequestProcessor( } fun processDeletion(patientId: String) { - processDeletion(patientId, UUID.randomUUID().toString()) + processDeletion(patientId, randomRequestId()) } - fun processDeletion(patientId: String, requestId: String) { + fun processDeletion(patientId: String, requestId: RequestId) { try { val patientPseudonym = pseudonymizeService.patientPseudonym(patientId) diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt index 38c6e30..a2e8de3 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestService.kt @@ -19,6 +19,7 @@ package dev.dnpm.etl.processor.services +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.monitoring.* import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable @@ -36,7 +37,7 @@ class RequestService( fun findAll(pageable: Pageable): Page = requestRepository.findAll(pageable) - fun findByUuid(uuid: String): Optional = + fun findByUuid(uuid: RequestId): Optional = requestRepository.findByUuidEquals(uuid) fun findRequestByPatientId(patientId: String, pageable: Pageable): Page = requestRepository.findRequestByPatientId(patientId, pageable) diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt index cabd26f..ecb2ec7 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/ResponseProcessor.kt @@ -19,6 +19,7 @@ package dev.dnpm.etl.processor.services +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.monitoring.Report import dev.dnpm.etl.processor.monitoring.RequestStatus import org.slf4j.LoggerFactory @@ -86,7 +87,7 @@ class ResponseProcessor( } data class ResponseEvent( - val requestUuid: String, + val requestUuid: RequestId, val timestamp: Instant, val status: RequestStatus, val body: Optional = Optional.empty() diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt index a29010f..12e824d 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessor.kt @@ -22,6 +22,7 @@ package dev.dnpm.etl.processor.services.kafka import com.fasterxml.jackson.annotation.JsonAlias import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.ObjectMapper +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.output.asRequestStatus import dev.dnpm.etl.processor.services.ResponseEvent @@ -47,7 +48,7 @@ class KafkaResponseProcessor( Optional.empty() }.ifPresentOrElse({ responseBody -> val event = ResponseEvent( - responseBody.requestId, + RequestId(responseBody.requestId), Instant.ofEpochMilli(data.timestamp()), responseBody.statusCode.asRequestStatus(), when (responseBody.statusCode.asRequestStatus()) { diff --git a/src/main/kotlin/dev/dnpm/etl/processor/types.kt b/src/main/kotlin/dev/dnpm/etl/processor/types.kt index c13714c..b41a550 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/types.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/types.kt @@ -1,5 +1,26 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2024 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 . + */ + package dev.dnpm.etl.processor +import java.util.* + class Fingerprint(val value: String) { override fun hashCode() = value.hashCode() @@ -9,3 +30,12 @@ class Fingerprint(val value: String) { fun empty() = Fingerprint("") } } + +@JvmInline +value class RequestId(val value: String) { + + fun isBlank() = value.isBlank() + +} + +fun randomRequestId() = RequestId(UUID.randomUUID().toString()) \ No newline at end of file diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt index 6f20640..ac003d3 100644 --- a/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt +++ b/src/main/kotlin/dev/dnpm/etl/processor/web/HomeController.kt @@ -20,8 +20,8 @@ package dev.dnpm.etl.processor.web import dev.dnpm.etl.processor.NotFoundException +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.monitoring.ReportService -import dev.dnpm.etl.processor.monitoring.RequestId import dev.dnpm.etl.processor.services.RequestService import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort @@ -65,7 +65,7 @@ class HomeController( @GetMapping(path = ["/report/{id}"]) fun report(@PathVariable id: RequestId, model: Model): String { - val request = requestService.findByUuid(id.toString()).orElse(null) ?: throw NotFoundException() + val request = requestService.findByUuid(id).orElse(null) ?: throw NotFoundException() model.addAttribute("request", request) model.addAttribute("issues", reportService.deserialize(request.report?.dataQualityReport)) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt new file mode 100644 index 0000000..55d6327 --- /dev/null +++ b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt @@ -0,0 +1,29 @@ +/* + * This file is part of ETL-Processor + * + * Copyright (c) 2024 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 . + */ + +package dev.dnpm.etl.processor + +import org.mockito.ArgumentMatchers + +inline fun anyValueClass(): T { + val unboxedClass = T::class.java.declaredFields.first().type + return ArgumentMatchers.any(unboxedClass as Class) + ?: T::class.java.getDeclaredMethod("box-impl", unboxedClass) + .invoke(null, null) as T +} \ No newline at end of file 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 1157644..58c78e4 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt @@ -23,6 +23,7 @@ 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.anyValueClass import dev.dnpm.etl.processor.services.RequestProcessor import org.apache.kafka.clients.consumer.ConsumerRecord import org.apache.kafka.common.header.internals.RecordHeader @@ -92,7 +93,7 @@ class KafkaInputListenerTest { ConsumerRecord("testtopic", 0, 0, -1L, TimestampType.NO_TIMESTAMP_TYPE, -1, -1, "", this.objectMapper.writeValueAsString(mtbFile), headers, Optional.empty()) ) - verify(requestProcessor, times(1)).processMtbFile(any(), anyString()) + verify(requestProcessor, times(1)).processMtbFile(any(), anyValueClass()) } @Test @@ -106,7 +107,7 @@ class KafkaInputListenerTest { kafkaInputListener.onMessage( ConsumerRecord("testtopic", 0, 0, -1L, TimestampType.NO_TIMESTAMP_TYPE, -1, -1, "", this.objectMapper.writeValueAsString(mtbFile), headers, Optional.empty()) ) - verify(requestProcessor, times(1)).processDeletion(anyString(), anyString()) + verify(requestProcessor, times(1)).processDeletion(anyString(), anyValueClass()) } } \ No newline at end of file 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 411c51e..b943f5f 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt @@ -21,6 +21,7 @@ package dev.dnpm.etl.processor.output import com.fasterxml.jackson.databind.ObjectMapper import de.ukw.ccc.bwhc.dto.* +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.config.KafkaProperties import dev.dnpm.etl.processor.monitoring.RequestStatus import org.assertj.core.api.Assertions.assertThat @@ -72,7 +73,7 @@ class KafkaMtbFileSenderTest { completedFuture(SendResult(null, null)) }.whenever(kafkaTemplate).send(anyString(), anyString(), anyString()) - val response = kafkaMtbFileSender.send(MtbFileSender.MtbFileRequest("TestID", mtbFile(Consent.Status.ACTIVE))) + val response = kafkaMtbFileSender.send(MtbFileSender.MtbFileRequest(TEST_REQUEST_ID, mtbFile(Consent.Status.ACTIVE))) assertThat(response.status).isEqualTo(testData.requestStatus) } @@ -86,7 +87,7 @@ class KafkaMtbFileSenderTest { completedFuture(SendResult(null, null)) }.whenever(kafkaTemplate).send(anyString(), anyString(), anyString()) - val response = kafkaMtbFileSender.send(MtbFileSender.DeleteRequest("TestID", "PID")) + val response = kafkaMtbFileSender.send(MtbFileSender.DeleteRequest(TEST_REQUEST_ID, "PID")) assertThat(response.status).isEqualTo(testData.requestStatus) } @@ -96,14 +97,14 @@ class KafkaMtbFileSenderTest { completedFuture(SendResult(null, null)) }.whenever(kafkaTemplate).send(anyString(), anyString(), anyString()) - kafkaMtbFileSender.send(MtbFileSender.MtbFileRequest("TestID", mtbFile(Consent.Status.ACTIVE))) + kafkaMtbFileSender.send(MtbFileSender.MtbFileRequest(TEST_REQUEST_ID, mtbFile(Consent.Status.ACTIVE))) val captor = argumentCaptor() verify(kafkaTemplate, times(1)).send(anyString(), captor.capture(), captor.capture()) assertThat(captor.firstValue).isNotNull assertThat(captor.firstValue).isEqualTo("{\"pid\": \"PID\"}") assertThat(captor.secondValue).isNotNull - assertThat(captor.secondValue).isEqualTo(objectMapper.writeValueAsString(kafkaRecordData("TestID", Consent.Status.ACTIVE))) + assertThat(captor.secondValue).isEqualTo(objectMapper.writeValueAsString(kafkaRecordData(TEST_REQUEST_ID, Consent.Status.ACTIVE))) } @Test @@ -112,14 +113,14 @@ class KafkaMtbFileSenderTest { completedFuture(SendResult(null, null)) }.whenever(kafkaTemplate).send(anyString(), anyString(), anyString()) - kafkaMtbFileSender.send(MtbFileSender.DeleteRequest("TestID", "PID")) + kafkaMtbFileSender.send(MtbFileSender.DeleteRequest(TEST_REQUEST_ID, "PID")) val captor = argumentCaptor() verify(kafkaTemplate, times(1)).send(anyString(), captor.capture(), captor.capture()) assertThat(captor.firstValue).isNotNull assertThat(captor.firstValue).isEqualTo("{\"pid\": \"PID\"}") assertThat(captor.secondValue).isNotNull - assertThat(captor.secondValue).isEqualTo(objectMapper.writeValueAsString(kafkaRecordData("TestID", Consent.Status.REJECTED))) + assertThat(captor.secondValue).isEqualTo(objectMapper.writeValueAsString(kafkaRecordData(TEST_REQUEST_ID, Consent.Status.REJECTED))) } @ParameterizedTest @@ -136,7 +137,7 @@ class KafkaMtbFileSenderTest { completedFuture(SendResult(null, null)) }.whenever(kafkaTemplate).send(anyString(), anyString(), anyString()) - kafkaMtbFileSender.send(MtbFileSender.MtbFileRequest("TestID", mtbFile(Consent.Status.ACTIVE))) + kafkaMtbFileSender.send(MtbFileSender.MtbFileRequest(TEST_REQUEST_ID, mtbFile(Consent.Status.ACTIVE))) val expectedCount = when (testData.exception) { // OK - No Retry @@ -162,7 +163,7 @@ class KafkaMtbFileSenderTest { completedFuture(SendResult(null, null)) }.whenever(kafkaTemplate).send(anyString(), anyString(), anyString()) - kafkaMtbFileSender.send(MtbFileSender.DeleteRequest("TestID", "PID")) + kafkaMtbFileSender.send(MtbFileSender.DeleteRequest(TEST_REQUEST_ID, "PID")) val expectedCount = when (testData.exception) { // OK - No Retry @@ -175,6 +176,8 @@ class KafkaMtbFileSenderTest { } companion object { + val TEST_REQUEST_ID = RequestId("TestId") + fun mtbFile(consentStatus: Consent.Status): MtbFile { return if (consentStatus == Consent.Status.ACTIVE) { MtbFile.builder() @@ -210,7 +213,7 @@ class KafkaMtbFileSenderTest { }.build() } - fun kafkaRecordData(requestId: String, consentStatus: Consent.Status): KafkaMtbFileSender.Data { + fun kafkaRecordData(requestId: RequestId, consentStatus: Consent.Status): KafkaMtbFileSender.Data { return KafkaMtbFileSender.Data(requestId, mtbFile(consentStatus)) } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSenderTest.kt index df19ddb..4f0eca8 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/RestMtbFileSenderTest.kt @@ -20,6 +20,7 @@ package dev.dnpm.etl.processor.output import de.ukw.ccc.bwhc.dto.* +import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.config.RestTargetProperties import dev.dnpm.etl.processor.monitoring.RequestStatus import org.assertj.core.api.Assertions.assertThat @@ -64,7 +65,7 @@ class RestMtbFileSenderTest { withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it) } - val response = restMtbFileSender.send(MtbFileSender.DeleteRequest("TestID", "PID")) + val response = restMtbFileSender.send(MtbFileSender.DeleteRequest(TEST_REQUEST_ID, "PID")) assertThat(response.status).isEqualTo(requestWithResponse.response.status) assertThat(response.body).isEqualTo(requestWithResponse.response.body) } @@ -79,7 +80,7 @@ class RestMtbFileSenderTest { withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it) } - val response = restMtbFileSender.send(MtbFileSender.MtbFileRequest("TestID", mtbFile)) + val response = restMtbFileSender.send(MtbFileSender.MtbFileRequest(TEST_REQUEST_ID, mtbFile)) assertThat(response.status).isEqualTo(requestWithResponse.response.status) assertThat(response.body).isEqualTo(requestWithResponse.response.body) } @@ -108,7 +109,7 @@ class RestMtbFileSenderTest { withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it) } - val response = restMtbFileSender.send(MtbFileSender.MtbFileRequest("TestID", mtbFile)) + val response = restMtbFileSender.send(MtbFileSender.MtbFileRequest(TEST_REQUEST_ID, mtbFile)) assertThat(response.status).isEqualTo(requestWithResponse.response.status) assertThat(response.body).isEqualTo(requestWithResponse.response.body) } @@ -137,7 +138,7 @@ class RestMtbFileSenderTest { withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it) } - val response = restMtbFileSender.send(MtbFileSender.DeleteRequest("TestID", "PID")) + val response = restMtbFileSender.send(MtbFileSender.DeleteRequest(TEST_REQUEST_ID, "PID")) assertThat(response.status).isEqualTo(requestWithResponse.response.status) assertThat(response.body).isEqualTo(requestWithResponse.response.body) } @@ -149,6 +150,8 @@ class RestMtbFileSenderTest { val response: MtbFileSender.Response ) + val TEST_REQUEST_ID = RequestId("TestId") + private val warningBody = """ { "patient_id": "PID", 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 c6f39b8..d7354be 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt @@ -22,8 +22,11 @@ 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.randomRequestId import dev.dnpm.etl.processor.config.AppConfigProperties -import dev.dnpm.etl.processor.monitoring.* +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.MtbFileSender import dev.dnpm.etl.processor.output.RestMtbFileSender import dev.dnpm.etl.processor.pseudonym.PseudonymizeService @@ -40,7 +43,6 @@ 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) @@ -88,7 +90,7 @@ class RequestProcessorTest { doAnswer { Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("zdlzv5s5ydmd4ktw2v5piohegc4jcyrm6j66bq6tv2uxuerndmga"), @@ -147,7 +149,7 @@ class RequestProcessorTest { doAnswer { Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("zdlzv5s5ydmd4ktw2v5piohegc4jcyrm6j66bq6tv2uxuerndmga"), @@ -206,7 +208,7 @@ class RequestProcessorTest { doAnswer { Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("different"), @@ -269,7 +271,7 @@ class RequestProcessorTest { doAnswer { Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("different"), diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt index 07e3ec3..93090ed 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt @@ -20,6 +20,7 @@ package dev.dnpm.etl.processor.services import dev.dnpm.etl.processor.Fingerprint +import dev.dnpm.etl.processor.randomRequestId import dev.dnpm.etl.processor.monitoring.Request import dev.dnpm.etl.processor.monitoring.RequestRepository import dev.dnpm.etl.processor.monitoring.RequestStatus @@ -33,7 +34,6 @@ import org.mockito.Mockito.* import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.whenever import java.time.Instant -import java.util.* @ExtendWith(MockitoExtension::class) class RequestServiceTest { @@ -44,7 +44,7 @@ class RequestServiceTest { private fun anyRequest() = any(Request::class.java) ?: Request( 0L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_dummy", "PX", Fingerprint("dummy"), @@ -66,7 +66,7 @@ class RequestServiceTest { val requests = listOf( Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -76,7 +76,7 @@ class RequestServiceTest { ), Request( 2L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdefd"), @@ -86,7 +86,7 @@ class RequestServiceTest { ), Request( 3L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -106,7 +106,7 @@ class RequestServiceTest { val requests = listOf( Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -116,7 +116,7 @@ class RequestServiceTest { ), Request( 2L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -126,7 +126,7 @@ class RequestServiceTest { ), Request( 3L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -146,7 +146,7 @@ class RequestServiceTest { val requests = listOf( Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), @@ -156,7 +156,7 @@ class RequestServiceTest { ), Request( 1L, - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678902", "P2", Fingerprint("0123456789abcdef2"), @@ -189,7 +189,7 @@ class RequestServiceTest { }.whenever(requestRepository).save(anyRequest()) val request = Request( - UUID.randomUUID().toString(), + randomRequestId(), "TEST_12345678901", "P1", Fingerprint("0123456789abcdef1"), diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt index 92d899a..7204d7c 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt @@ -20,6 +20,8 @@ package dev.dnpm.etl.processor.services import dev.dnpm.etl.processor.Fingerprint +import dev.dnpm.etl.processor.RequestId +import dev.dnpm.etl.processor.anyValueClass import dev.dnpm.etl.processor.monitoring.Request import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.monitoring.RequestType @@ -29,7 +31,6 @@ 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.MethodSource -import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.* @@ -47,7 +48,7 @@ class ResponseProcessorTest { private val testRequest = Request( 1L, - "TestID1234", + RequestId("TestID1234"), "PSEUDONYM-A", "1", Fingerprint("dummyfingerprint"), @@ -70,10 +71,10 @@ class ResponseProcessorTest { fun shouldNotSaveStatusForUnknownRequest() { doAnswer { Optional.empty() - }.whenever(requestService).findByUuid(anyString()) + }.whenever(requestService).findByUuid(anyValueClass()) val event = ResponseEvent( - "TestID1234", + RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), RequestStatus.SUCCESS ) @@ -87,17 +88,17 @@ class ResponseProcessorTest { fun shouldNotSaveStatusWithUnknownState() { doAnswer { Optional.of(testRequest) - }.whenever(requestService).findByUuid(anyString()) + }.whenever(requestService).findByUuid(anyValueClass()) val event = ResponseEvent( - "TestID1234", + RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), RequestStatus.UNKNOWN ) this.responseProcessor.handleResponseEvent(event) - verify(requestService, never()).save(any()) + verify(requestService, never()).save(any()) } @ParameterizedTest @@ -105,10 +106,10 @@ class ResponseProcessorTest { fun shouldSaveStatusForKnownRequest(requestStatus: RequestStatus) { doAnswer { Optional.of(testRequest) - }.whenever(requestService).findByUuid(anyString()) + }.whenever(requestService).findByUuid(anyValueClass()) val event = ResponseEvent( - "TestID1234", + RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), requestStatus )